[Commits] af1f216: MDEV-19820 Wrong result with multiple single column index request
by IgorBabaev 25 Jun '19
by IgorBabaev 25 Jun '19
25 Jun '19
revision-id: af1f2161a1206354a2110a15663a8589d68c68a5 (mariadb-10.4.4-194-gaf1f216)
parent(s): 8b576616b442d061356bc5a2abd410f478e98ee7
author: Igor Babaev
committer: Igor Babaev
timestamp: 2019-06-25 08:06:27 -0700
message:
MDEV-19820 Wrong result with multiple single column index request
The bug occured when the optimizer decided to use a rowid filter built
by a range index scan to access an InnoDB table with generated clustered
index.
When a table is accessed by a secondary index Idx employing a rowid filter the
the value of pk contained in the found index tuple is checked against the
filter. A call of the handler function position is supposed to put the
pk value into the handler::ref buffer. However for generated clustered
primary keys it did not happened. The patch fixes this problem.
---
mysql-test/main/rowid_filter_innodb.result | 47 ++++++++++++++++++++++++++++++
mysql-test/main/rowid_filter_innodb.test | 30 +++++++++++++++++++
sql/sql_select.cc | 2 +-
storage/innobase/row/row0sel.cc | 22 +++++++++++---
4 files changed, 96 insertions(+), 5 deletions(-)
diff --git a/mysql-test/main/rowid_filter_innodb.result b/mysql-test/main/rowid_filter_innodb.result
index 54c7e03..d83239e 100644
--- a/mysql-test/main/rowid_filter_innodb.result
+++ b/mysql-test/main/rowid_filter_innodb.result
@@ -2162,4 +2162,51 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
Warnings:
Note 1003 select 1 AS `id`,`test`.`t2`.`y` AS `y`,`test`.`t2`.`x` AS `x` from `test`.`t1` join `test`.`t2` where `test`.`t2`.`y` = 2 and `test`.`t2`.`x` = 1
drop table t1, t2;
+#
+# MDEV-19820: use of rowid filter for innodb table without primary key
+#
+create table t1 (a int, b int, key (b), key (a)) engine=innodb;
+insert into t1
+select (rand(1)*1000)/10, (rand(1001)*1000)/50 from seq_1_to_1000;
+analyze table t1;
+Table Op Msg_type Msg_text
+test.t1 analyze status Engine-independent statistics collected
+test.t1 analyze status OK
+set @save_optimizer_switch= @@optimizer_switch;
+set optimizer_switch='rowid_filter=off';
+select count(*) from t1 where a in (22,83,11) and b=2;
+count(*)
+6
+explain extended select count(*) from t1 where a in (22,83,11) and b=2;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 SIMPLE t1 ref b,a b 5 const 59 55.93 Using where
+Warnings:
+Note 1003 select count(0) AS `count(*)` from `test`.`t1` where `test`.`t1`.`b` = 2 and `test`.`t1`.`a` in (22,83,11)
+select * from t1 where a in (22,83,11) and b=2;
+a b
+11 2
+11 2
+83 2
+11 2
+83 2
+22 2
+set optimizer_switch='rowid_filter=on';
+select count(*) from t1 where a in (22,83,11) and b=2;
+count(*)
+6
+explain extended select count(*) from t1 where a in (22,83,11) and b=2;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 SIMPLE t1 ref|filter b,a b|a 5|5 const 59 (3%) 55.93 Using where; Using rowid filter
+Warnings:
+Note 1003 select count(0) AS `count(*)` from `test`.`t1` where `test`.`t1`.`b` = 2 and `test`.`t1`.`a` in (22,83,11)
+select * from t1 where a in (22,83,11) and b=2;
+a b
+11 2
+11 2
+83 2
+11 2
+83 2
+22 2
+drop table t1;
+set optimizer_switch=@save_optimizer_switch;
SET SESSION STORAGE_ENGINE=DEFAULT;
diff --git a/mysql-test/main/rowid_filter_innodb.test b/mysql-test/main/rowid_filter_innodb.test
index 173ba15..240cd92 100644
--- a/mysql-test/main/rowid_filter_innodb.test
+++ b/mysql-test/main/rowid_filter_innodb.test
@@ -3,6 +3,7 @@
SET SESSION STORAGE_ENGINE='InnoDB';
--source rowid_filter.test
+--source include/have_sequence.inc
--echo #
--echo # MDEV-18755: possible RORI-plan and possible plan with range filter
@@ -65,4 +66,33 @@ eval explain extended $q;
drop table t1, t2;
+--echo #
+--echo # MDEV-19820: use of rowid filter for innodb table without primary key
+--echo #
+
+create table t1 (a int, b int, key (b), key (a)) engine=innodb;
+insert into t1
+select (rand(1)*1000)/10, (rand(1001)*1000)/50 from seq_1_to_1000;
+analyze table t1;
+
+let $q=
+select count(*) from t1 where a in (22,83,11) and b=2;
+let $q1=
+select * from t1 where a in (22,83,11) and b=2;
+
+set @save_optimizer_switch= @@optimizer_switch;
+
+set optimizer_switch='rowid_filter=off';
+eval $q;
+eval explain extended $q;
+eval $q1;
+
+set optimizer_switch='rowid_filter=on';
+eval $q;
+eval explain extended $q;
+eval $q1;
+
+drop table t1;
+set optimizer_switch=@save_optimizer_switch;
+
SET SESSION STORAGE_ENGINE=DEFAULT;
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 826026a..2382789 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -5382,7 +5382,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
impossible_range= records == 0 && s->table->reginfo.impossible_range;
if (join->thd->lex->sql_command == SQLCOM_SELECT &&
optimizer_flag(join->thd, OPTIMIZER_SWITCH_USE_ROWID_FILTER))
- s->table->init_cost_info_for_usable_range_rowid_filters(join->thd);
+ s->table->init_cost_info_for_usable_range_rowid_filters(join->thd);
}
if (!impossible_range)
{
diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc
index 659a824..f7eec2d 100644
--- a/storage/innobase/row/row0sel.cc
+++ b/storage/innobase/row/row0sel.cc
@@ -3902,10 +3902,24 @@ row_search_idx_cond_check(
switch (result) {
case ICP_MATCH:
- if (handler_rowid_filter_is_active(prebuilt->pk_filter)
- && !handler_rowid_filter_check(prebuilt->pk_filter)) {
- MONITOR_INC(MONITOR_ICP_MATCH);
- return(ICP_NO_MATCH);
+ if (handler_rowid_filter_is_active(prebuilt->pk_filter)) {
+ ut_ad(!prebuilt->index->is_primary());
+ if (prebuilt->clust_index_was_generated) {
+ ulint len;
+ dict_index_t* index = prebuilt->index;
+ const byte* data = rec_get_nth_field(
+ rec, offsets, index->n_fields - 1,
+ &len);
+ ut_ad(dict_index_get_nth_col(index,
+ index->n_fields - 1)
+ ->prtype == (DATA_ROW_ID | DATA_NOT_NULL));
+ ut_ad(len == DATA_ROW_ID_LEN);
+ memcpy(prebuilt->row_id, data, DATA_ROW_ID_LEN);
+ }
+ if (!handler_rowid_filter_check(prebuilt->pk_filter)) {
+ MONITOR_INC(MONITOR_ICP_MATCH);
+ return(ICP_NO_MATCH);
+ }
}
/* Convert the remaining fields to MySQL format.
If this is a secondary index record, we must defer
1
0
[Commits] 08e32aa6f89: MDEV-19708 RBR replication looses data silently ignoring important column attributes
by sachin.setiya@mariadb.com 25 Jun '19
by sachin.setiya@mariadb.com 25 Jun '19
25 Jun '19
revision-id: 08e32aa6f8993b7b89c81078a49554ba1cabcf31 (mariadb-10.4.4-171-g08e32aa6f89)
parent(s): 984d7100cdab91fb23d97c05e8b6329a90fe1583
author: Sachin
committer: Sachin
timestamp: 2019-06-25 17:38:28 +0530
message:
MDEV-19708 RBR replication looses data silently ignoring important column attributes
Cherry-pick the commits the mysql and some changes.
WL#4618 RBR: extended table metadata in the binary log
This patch extends Table Map Event. It appends some new fields for
more metadata. The new metadata includes:
- Signedness of Numberic Columns
- Character Set of Character Columns and Binary Columns
- Column Name
- String Value of SET Columns
- String Value of ENUM Columns
- Primary Key
- Geometry Type
Some of them are optional, the patch introduces a GLOBAL system
variable to control it. It is binlog_row_metadata.
- Scope: GLOBAL
- Dynamic: Yes
- Type: ENUM
- Values: {MINIMAL, FULL}
- Default: MINIMAL
Only Signedness, character set and geometry type are logged if it is MINIMAL.
Otherwise all of them are logged.
---
client/client_priv.h | 2 +
client/mysqlbinlog.cc | 6 +
mysql-test/include/grep_pattern.inc | 83 ++++
mysql-test/main/mysqlbinlog_row_compressed.result | 154 ++++----
mysql-test/main/mysqlbinlog_row_minimal.result | 168 ++++-----
mysql-test/main/mysqld--help.result | 6 +
.../binlog/include/print_optional_metadata.inc | 32 ++
.../r/binlog_table_map_optional_metadata.result | 401 ++++++++++++++++++++
.../t/binlog_table_map_optional_metadata.test | 348 +++++++++++++++++
.../sys_vars/r/binlog_row_metadata_basic.result | 85 +++++
.../sys_vars/t/binlog_row_metadata_basic.test | 124 ++++++
sql/log_event.cc | 335 +++++++++++++++-
sql/log_event.h | 327 ++++++++++++++++
sql/log_event_client.cc | 355 ++++++++++++++++-
sql/log_event_server.cc | 420 ++++++++++++++++++++-
sql/mysqld.cc | 1 +
sql/mysqld.h | 1 +
sql/sql_class.h | 5 +
sql/sys_vars.cc | 12 +
19 files changed, 2698 insertions(+), 167 deletions(-)
diff --git a/client/client_priv.h b/client/client_priv.h
index 5b6b31ccda9..8b8c217e207 100644
--- a/client/client_priv.h
+++ b/client/client_priv.h
@@ -101,6 +101,8 @@ enum options_client
OPT_SSL_CRL, OPT_SSL_CRLPATH,
OPT_PRINT_ROW_COUNT, OPT_PRINT_ROW_EVENT_POSITIONS,
OPT_SHUTDOWN_WAIT_FOR_SLAVES,
+ OPT_PRINT_TABLE_METADATA,
+ /* Add new option above this */
OPT_MAX_CLIENT_OPTION /* should be always the last */
};
diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc
index 090d743c61b..759cc2d66e7 100644
--- a/client/mysqlbinlog.cc
+++ b/client/mysqlbinlog.cc
@@ -143,6 +143,7 @@ static const char* dirname_for_local_load= 0;
static bool opt_skip_annotate_row_events= 0;
static my_bool opt_flashback;
+static bool opt_print_table_metadata;
#ifdef WHEN_FLASHBACK_REVIEW_READY
static my_bool opt_flashback_review;
static char *flashback_review_dbname, *flashback_review_tablename;
@@ -1094,6 +1095,7 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
print_event_info->hexdump_from= pos;
print_event_info->base64_output_mode= opt_base64_output_mode;
+ print_event_info->print_table_metadata= opt_print_table_metadata;
DBUG_PRINT("debug", ("event_type: %s", ev->get_type_str()));
@@ -1786,6 +1788,10 @@ Example: rewrite-db='from->to'.",
(uchar**) &opt_skip_annotate_row_events,
(uchar**) &opt_skip_annotate_row_events,
0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"print-table-metadata", OPT_PRINT_TABLE_METADATA,
+ "Print metadata stored in Table_map_log_event",
+ &opt_print_table_metadata, &opt_print_table_metadata, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
};
diff --git a/mysql-test/include/grep_pattern.inc b/mysql-test/include/grep_pattern.inc
new file mode 100644
index 00000000000..bdbca47fa9f
--- /dev/null
+++ b/mysql-test/include/grep_pattern.inc
@@ -0,0 +1,83 @@
+# ==== Purpose ====
+#
+# Grep for a pattern in a file in a portable manner.
+#
+# WARNING: Use include/assert_grep.inc instead, if at all possible.
+# That allows you to assert the property being tested in a much more
+# precise way. It also does not depend on the result file, so your
+# test becomes more readable/maintainable. It also produces better
+# debug output if the test fails.
+#
+# ==== Usage ====
+#
+# --let $grep_pattern= PERL_REGEX
+# --let $grep_file= FILENAME
+# [--let $grep_output= print_each | print_count | boolean]
+#
+# Parameters:
+#
+# $grep_pattern
+# The pattern to search for. This can be a perl regex.
+#
+# $grep_file
+# The file to search in.
+#
+# $grep_output
+# The format of the output. Can be one of:
+# - print_each: prints each line, and a count.
+# - print_count: print just a count.
+# - boolean: print only whether something was found or not.
+# If this is not specified, print_each is used.
+
+#--let $include_filename= grep_pattern.inc
+#--source include/begin_include_file.inc
+
+
+if ($grep_output)
+{
+ --let _GP_GREP_OUTPUT= $grep_output
+}
+if (!$grep_output)
+{
+ --let _GP_GREP_OUTPUT= print_each
+}
+--let _GP_GREP_PATTERN= $grep_pattern
+--let _GP_GREP_FILE= $grep_file
+
+--perl
+ use strict;
+ my $file= $ENV{'_GP_GREP_FILE'} or die "grep_file is not set";
+ my $pattern= $ENV{'_GP_GREP_PATTERN'} or die "grep_pattern is not set";
+ open(FILE, "$file") or die("Unable to open $file: $!\n");
+ my $count = 0;
+ my $output = $ENV{'_GP_GREP_OUTPUT'};
+ if ($output eq 'print_each') {
+ print "Matching lines are:\n";
+ }
+ while (<FILE>) {
+ my $line = $_;
+ if ($line =~ /$pattern/) {
+ if ($output eq 'print_each') {
+ print $line;
+ }
+ $count++;
+ if ($output eq 'boolean') {
+ last;
+ }
+ }
+ }
+ if ($count == 0 && $output eq 'print_each') {
+ print "None\n";
+ }
+ if ($output eq 'boolean') {
+ print $count ? "Pattern found.\n" : "Pattern not found.\n";
+ }
+ else {
+ print "Occurrences of '$pattern' in the input file: $count\n";
+ }
+ close(FILE) or die "Error closing $file: $!";
+EOF
+
+
+#--source include/end_include_file.inc
+
diff --git a/mysql-test/main/mysqlbinlog_row_compressed.result b/mysql-test/main/mysqlbinlog_row_compressed.result
index 8cf52f5b826..37b6b2ea7ee 100644
--- a/mysql-test/main/mysqlbinlog_row_compressed.result
+++ b/mysql-test/main/mysqlbinlog_row_compressed.result
@@ -59,9 +59,9 @@ BEGIN
# at 843
#<date> server id 1 end_log_pos 843 CRC32 XXX Annotate_rows:
#Q> INSERT INTO t1 VALUES (10, 1, 2, 3, 4, 5, 6, 7, "")
-#<date> server id 1 end_log_pos 899 CRC32 XXX Table_map: `test`.`t1` mapped to number num
-# at 899
-#<date> server id 1 end_log_pos 967 CRC32 XXX Write_compressed_rows: table id 32 flags: STMT_END_F
+#<date> server id 1 end_log_pos 905 CRC32 XXX Table_map: `test`.`t1` mapped to number num
+# at 905
+#<date> server id 1 end_log_pos 973 CRC32 XXX Write_compressed_rows: table id 32 flags: STMT_END_F
### INSERT INTO `test`.`t1`
### SET
### @1=10 /* INT meta=0 nullable=0 is_null=0 */
@@ -74,23 +74,23 @@ BEGIN
### @8=7 /* INT meta=0 nullable=1 is_null=0 */
### @9='' /* STRING(1) meta=65025 nullable=1 is_null=0 */
# Number of rows: 1
-# at 967
-#<date> server id 1 end_log_pos 1040 CRC32 XXX Query thread_id=5 exec_time=x error_code=0
+# at 973
+#<date> server id 1 end_log_pos 1046 CRC32 XXX Query thread_id=5 exec_time=x error_code=0
SET TIMESTAMP=X/*!*/;
COMMIT
/*!*/;
-# at 1040
-#<date> server id 1 end_log_pos 1082 CRC32 XXX GTID 0-1-4
+# at 1046
+#<date> server id 1 end_log_pos 1088 CRC32 XXX GTID 0-1-4
/*!100001 SET @@session.gtid_seq_no=4*//*!*/;
BEGIN
/*!*/;
-# at 1082
-# at 1158
-#<date> server id 1 end_log_pos 1158 CRC32 XXX Annotate_rows:
+# at 1088
+# at 1164
+#<date> server id 1 end_log_pos 1164 CRC32 XXX Annotate_rows:
#Q> INSERT INTO t1 VALUES (11, 1, 2, 3, 4, 5, 6, 7, NULL)
-#<date> server id 1 end_log_pos 1214 CRC32 XXX Table_map: `test`.`t1` mapped to number num
-# at 1214
-#<date> server id 1 end_log_pos 1281 CRC32 XXX Write_compressed_rows: table id 32 flags: STMT_END_F
+#<date> server id 1 end_log_pos 1226 CRC32 XXX Table_map: `test`.`t1` mapped to number num
+# at 1226
+#<date> server id 1 end_log_pos 1293 CRC32 XXX Write_compressed_rows: table id 32 flags: STMT_END_F
### INSERT INTO `test`.`t1`
### SET
### @1=11 /* INT meta=0 nullable=0 is_null=0 */
@@ -103,23 +103,23 @@ BEGIN
### @8=7 /* INT meta=0 nullable=1 is_null=0 */
### @9=NULL /* STRING(1) meta=65025 nullable=1 is_null=1 */
# Number of rows: 1
-# at 1281
-#<date> server id 1 end_log_pos 1354 CRC32 XXX Query thread_id=5 exec_time=x error_code=0
+# at 1293
+#<date> server id 1 end_log_pos 1366 CRC32 XXX Query thread_id=5 exec_time=x error_code=0
SET TIMESTAMP=X/*!*/;
COMMIT
/*!*/;
-# at 1354
-#<date> server id 1 end_log_pos 1396 CRC32 XXX GTID 0-1-5
+# at 1366
+#<date> server id 1 end_log_pos 1408 CRC32 XXX GTID 0-1-5
/*!100001 SET @@session.gtid_seq_no=5*//*!*/;
BEGIN
/*!*/;
-# at 1396
-# at 1474
-#<date> server id 1 end_log_pos 1474 CRC32 XXX Annotate_rows:
+# at 1408
+# at 1486
+#<date> server id 1 end_log_pos 1486 CRC32 XXX Annotate_rows:
#Q> INSERT INTO t1 VALUES (12, 1, 2, 3, NULL, 5, 6, 7, "A")
-#<date> server id 1 end_log_pos 1530 CRC32 XXX Table_map: `test`.`t1` mapped to number num
-# at 1530
-#<date> server id 1 end_log_pos 1596 CRC32 XXX Write_compressed_rows: table id 32 flags: STMT_END_F
+#<date> server id 1 end_log_pos 1548 CRC32 XXX Table_map: `test`.`t1` mapped to number num
+# at 1548
+#<date> server id 1 end_log_pos 1614 CRC32 XXX Write_compressed_rows: table id 32 flags: STMT_END_F
### INSERT INTO `test`.`t1`
### SET
### @1=12 /* INT meta=0 nullable=0 is_null=0 */
@@ -132,23 +132,23 @@ BEGIN
### @8=7 /* INT meta=0 nullable=1 is_null=0 */
### @9='A' /* STRING(1) meta=65025 nullable=1 is_null=0 */
# Number of rows: 1
-# at 1596
-#<date> server id 1 end_log_pos 1669 CRC32 XXX Query thread_id=5 exec_time=x error_code=0
+# at 1614
+#<date> server id 1 end_log_pos 1687 CRC32 XXX Query thread_id=5 exec_time=x error_code=0
SET TIMESTAMP=X/*!*/;
COMMIT
/*!*/;
-# at 1669
-#<date> server id 1 end_log_pos 1711 CRC32 XXX GTID 0-1-6
+# at 1687
+#<date> server id 1 end_log_pos 1729 CRC32 XXX GTID 0-1-6
/*!100001 SET @@session.gtid_seq_no=6*//*!*/;
BEGIN
/*!*/;
-# at 1711
-# at 1786
-#<date> server id 1 end_log_pos 1786 CRC32 XXX Annotate_rows:
+# at 1729
+# at 1804
+#<date> server id 1 end_log_pos 1804 CRC32 XXX Annotate_rows:
#Q> INSERT INTO t1 VALUES (13, 1, 2, 3, 0, 5, 6, 7, "A")
-#<date> server id 1 end_log_pos 1842 CRC32 XXX Table_map: `test`.`t1` mapped to number num
-# at 1842
-#<date> server id 1 end_log_pos 1909 CRC32 XXX Write_compressed_rows: table id 32 flags: STMT_END_F
+#<date> server id 1 end_log_pos 1866 CRC32 XXX Table_map: `test`.`t1` mapped to number num
+# at 1866
+#<date> server id 1 end_log_pos 1933 CRC32 XXX Write_compressed_rows: table id 32 flags: STMT_END_F
### INSERT INTO `test`.`t1`
### SET
### @1=13 /* INT meta=0 nullable=0 is_null=0 */
@@ -161,23 +161,23 @@ BEGIN
### @8=7 /* INT meta=0 nullable=1 is_null=0 */
### @9='A' /* STRING(1) meta=65025 nullable=1 is_null=0 */
# Number of rows: 1
-# at 1909
-#<date> server id 1 end_log_pos 1982 CRC32 XXX Query thread_id=5 exec_time=x error_code=0
+# at 1933
+#<date> server id 1 end_log_pos 2006 CRC32 XXX Query thread_id=5 exec_time=x error_code=0
SET TIMESTAMP=X/*!*/;
COMMIT
/*!*/;
-# at 1982
-#<date> server id 1 end_log_pos 2024 CRC32 XXX GTID 0-1-7
+# at 2006
+#<date> server id 1 end_log_pos 2048 CRC32 XXX GTID 0-1-7
/*!100001 SET @@session.gtid_seq_no=7*//*!*/;
BEGIN
/*!*/;
-# at 2024
-# at 2078
-#<date> server id 1 end_log_pos 2078 CRC32 XXX Annotate_rows:
+# at 2048
+# at 2102
+#<date> server id 1 end_log_pos 2102 CRC32 XXX Annotate_rows:
#Q> INSERT INTO t2 SELECT * FROM t1
-#<date> server id 1 end_log_pos 2134 CRC32 XXX Table_map: `test`.`t2` mapped to number num
-# at 2134
-#<date> server id 1 end_log_pos 2225 CRC32 XXX Write_compressed_rows: table id 33 flags: STMT_END_F
+#<date> server id 1 end_log_pos 2164 CRC32 XXX Table_map: `test`.`t2` mapped to number num
+# at 2164
+#<date> server id 1 end_log_pos 2255 CRC32 XXX Write_compressed_rows: table id 33 flags: STMT_END_F
### INSERT INTO `test`.`t2`
### SET
### @1=10 /* INT meta=0 nullable=0 is_null=0 */
@@ -223,23 +223,23 @@ BEGIN
### @8=7 /* INT meta=0 nullable=1 is_null=0 */
### @9='A' /* STRING(1) meta=65025 nullable=1 is_null=0 */
# Number of rows: 4
-# at 2225
-#<date> server id 1 end_log_pos 2298 CRC32 XXX Query thread_id=5 exec_time=x error_code=0
+# at 2255
+#<date> server id 1 end_log_pos 2328 CRC32 XXX Query thread_id=5 exec_time=x error_code=0
SET TIMESTAMP=X/*!*/;
COMMIT
/*!*/;
-# at 2298
-#<date> server id 1 end_log_pos 2340 CRC32 XXX GTID 0-1-8
+# at 2328
+#<date> server id 1 end_log_pos 2370 CRC32 XXX GTID 0-1-8
/*!100001 SET @@session.gtid_seq_no=8*//*!*/;
BEGIN
/*!*/;
-# at 2340
-# at 2406
-#<date> server id 1 end_log_pos 2406 CRC32 XXX Annotate_rows:
+# at 2370
+# at 2436
+#<date> server id 1 end_log_pos 2436 CRC32 XXX Annotate_rows:
#Q> UPDATE t2 SET f4=5 WHERE f4>0 or f4 is NULL
-#<date> server id 1 end_log_pos 2462 CRC32 XXX Table_map: `test`.`t2` mapped to number num
-# at 2462
-#<date> server id 1 end_log_pos 2561 CRC32 XXX Update_compressed_rows: table id 33 flags: STMT_END_F
+#<date> server id 1 end_log_pos 2498 CRC32 XXX Table_map: `test`.`t2` mapped to number num
+# at 2498
+#<date> server id 1 end_log_pos 2597 CRC32 XXX Update_compressed_rows: table id 33 flags: STMT_END_F
### UPDATE `test`.`t2`
### WHERE
### @1=10 /* INT meta=0 nullable=0 is_null=0 */
@@ -304,23 +304,23 @@ BEGIN
### @8=7 /* INT meta=0 nullable=1 is_null=0 */
### @9='A' /* STRING(1) meta=65025 nullable=1 is_null=0 */
# Number of rows: 3
-# at 2561
-#<date> server id 1 end_log_pos 2634 CRC32 XXX Query thread_id=5 exec_time=x error_code=0
+# at 2597
+#<date> server id 1 end_log_pos 2670 CRC32 XXX Query thread_id=5 exec_time=x error_code=0
SET TIMESTAMP=X/*!*/;
COMMIT
/*!*/;
-# at 2634
-#<date> server id 1 end_log_pos 2676 CRC32 XXX GTID 0-1-9
+# at 2670
+#<date> server id 1 end_log_pos 2712 CRC32 XXX GTID 0-1-9
/*!100001 SET @@session.gtid_seq_no=9*//*!*/;
BEGIN
/*!*/;
-# at 2676
-# at 2713
-#<date> server id 1 end_log_pos 2713 CRC32 XXX Annotate_rows:
+# at 2712
+# at 2749
+#<date> server id 1 end_log_pos 2749 CRC32 XXX Annotate_rows:
#Q> DELETE FROM t1
-#<date> server id 1 end_log_pos 2769 CRC32 XXX Table_map: `test`.`t1` mapped to number num
-# at 2769
-#<date> server id 1 end_log_pos 2861 CRC32 XXX Delete_compressed_rows: table id 32 flags: STMT_END_F
+#<date> server id 1 end_log_pos 2811 CRC32 XXX Table_map: `test`.`t1` mapped to number num
+# at 2811
+#<date> server id 1 end_log_pos 2903 CRC32 XXX Delete_compressed_rows: table id 32 flags: STMT_END_F
### DELETE FROM `test`.`t1`
### WHERE
### @1=10 /* INT meta=0 nullable=0 is_null=0 */
@@ -366,23 +366,23 @@ BEGIN
### @8=7 /* INT meta=0 nullable=1 is_null=0 */
### @9='A' /* STRING(1) meta=65025 nullable=1 is_null=0 */
# Number of rows: 4
-# at 2861
-#<date> server id 1 end_log_pos 2934 CRC32 XXX Query thread_id=5 exec_time=x error_code=0
+# at 2903
+#<date> server id 1 end_log_pos 2976 CRC32 XXX Query thread_id=5 exec_time=x error_code=0
SET TIMESTAMP=X/*!*/;
COMMIT
/*!*/;
-# at 2934
-#<date> server id 1 end_log_pos 2976 CRC32 XXX GTID 0-1-10
+# at 2976
+#<date> server id 1 end_log_pos 3018 CRC32 XXX GTID 0-1-10
/*!100001 SET @@session.gtid_seq_no=10*//*!*/;
BEGIN
/*!*/;
-# at 2976
-# at 3013
-#<date> server id 1 end_log_pos 3013 CRC32 XXX Annotate_rows:
+# at 3018
+# at 3055
+#<date> server id 1 end_log_pos 3055 CRC32 XXX Annotate_rows:
#Q> DELETE FROM t2
-#<date> server id 1 end_log_pos 3069 CRC32 XXX Table_map: `test`.`t2` mapped to number num
-# at 3069
-#<date> server id 1 end_log_pos 3154 CRC32 XXX Delete_compressed_rows: table id 33 flags: STMT_END_F
+#<date> server id 1 end_log_pos 3117 CRC32 XXX Table_map: `test`.`t2` mapped to number num
+# at 3117
+#<date> server id 1 end_log_pos 3202 CRC32 XXX Delete_compressed_rows: table id 33 flags: STMT_END_F
### DELETE FROM `test`.`t2`
### WHERE
### @1=10 /* INT meta=0 nullable=0 is_null=0 */
@@ -428,13 +428,13 @@ BEGIN
### @8=7 /* INT meta=0 nullable=1 is_null=0 */
### @9='A' /* STRING(1) meta=65025 nullable=1 is_null=0 */
# Number of rows: 4
-# at 3154
-#<date> server id 1 end_log_pos 3227 CRC32 XXX Query thread_id=5 exec_time=x error_code=0
+# at 3202
+#<date> server id 1 end_log_pos 3275 CRC32 XXX Query thread_id=5 exec_time=x error_code=0
SET TIMESTAMP=X/*!*/;
COMMIT
/*!*/;
-# at 3227
-#<date> server id 1 end_log_pos 3275 CRC32 XXX Rotate to master-bin.000002 pos: 4
+# at 3275
+#<date> server id 1 end_log_pos 3323 CRC32 XXX Rotate to master-bin.000002 pos: 4
DELIMITER ;
# End of log file
ROLLBACK /* added by mysqlbinlog */;
diff --git a/mysql-test/main/mysqlbinlog_row_minimal.result b/mysql-test/main/mysqlbinlog_row_minimal.result
index b3cee345428..edcefa505a2 100644
--- a/mysql-test/main/mysqlbinlog_row_minimal.result
+++ b/mysql-test/main/mysqlbinlog_row_minimal.result
@@ -57,9 +57,9 @@ BEGIN
# at 890
#<date> server id 1 end_log_pos 890 CRC32 XXX Annotate_rows:
#Q> INSERT INTO t1 VALUES (10, 1, 2, 3, 4, 5, 6, 7, "")
-#<date> server id 1 end_log_pos 946 CRC32 XXX Table_map: `test`.`t1` mapped to number num
-# at 946
-#<date> server id 1 end_log_pos 1015 CRC32 XXX Write_rows: table id 32 flags: STMT_END_F
+#<date> server id 1 end_log_pos 952 CRC32 XXX Table_map: `test`.`t1` mapped to number num
+# at 952
+#<date> server id 1 end_log_pos 1021 CRC32 XXX Write_rows: table id 32 flags: STMT_END_F
### INSERT INTO `test`.`t1`
### SET
### @1=10 /* INT meta=0 nullable=0 is_null=0 */
@@ -72,23 +72,23 @@ BEGIN
### @8=7 /* INT meta=0 nullable=1 is_null=0 */
### @9='' /* STRING(1) meta=65025 nullable=1 is_null=0 */
# Number of rows: 1
-# at 1015
-#<date> server id 1 end_log_pos 1088 CRC32 XXX Query thread_id=5 exec_time=x error_code=0
+# at 1021
+#<date> server id 1 end_log_pos 1094 CRC32 XXX Query thread_id=5 exec_time=x error_code=0
SET TIMESTAMP=X/*!*/;
COMMIT
/*!*/;
-# at 1088
-#<date> server id 1 end_log_pos 1130 CRC32 XXX GTID 0-1-4
+# at 1094
+#<date> server id 1 end_log_pos 1136 CRC32 XXX GTID 0-1-4
/*!100001 SET @@session.gtid_seq_no=4*//*!*/;
BEGIN
/*!*/;
-# at 1130
-# at 1206
-#<date> server id 1 end_log_pos 1206 CRC32 XXX Annotate_rows:
+# at 1136
+# at 1212
+#<date> server id 1 end_log_pos 1212 CRC32 XXX Annotate_rows:
#Q> INSERT INTO t1 VALUES (11, 1, 2, 3, 4, 5, 6, 7, NULL)
-#<date> server id 1 end_log_pos 1262 CRC32 XXX Table_map: `test`.`t1` mapped to number num
-# at 1262
-#<date> server id 1 end_log_pos 1330 CRC32 XXX Write_rows: table id 32 flags: STMT_END_F
+#<date> server id 1 end_log_pos 1274 CRC32 XXX Table_map: `test`.`t1` mapped to number num
+# at 1274
+#<date> server id 1 end_log_pos 1342 CRC32 XXX Write_rows: table id 32 flags: STMT_END_F
### INSERT INTO `test`.`t1`
### SET
### @1=11 /* INT meta=0 nullable=0 is_null=0 */
@@ -101,23 +101,23 @@ BEGIN
### @8=7 /* INT meta=0 nullable=1 is_null=0 */
### @9=NULL /* STRING(1) meta=65025 nullable=1 is_null=1 */
# Number of rows: 1
-# at 1330
-#<date> server id 1 end_log_pos 1403 CRC32 XXX Query thread_id=5 exec_time=x error_code=0
+# at 1342
+#<date> server id 1 end_log_pos 1415 CRC32 XXX Query thread_id=5 exec_time=x error_code=0
SET TIMESTAMP=X/*!*/;
COMMIT
/*!*/;
-# at 1403
-#<date> server id 1 end_log_pos 1445 CRC32 XXX GTID 0-1-5
+# at 1415
+#<date> server id 1 end_log_pos 1457 CRC32 XXX GTID 0-1-5
/*!100001 SET @@session.gtid_seq_no=5*//*!*/;
BEGIN
/*!*/;
-# at 1445
-# at 1523
-#<date> server id 1 end_log_pos 1523 CRC32 XXX Annotate_rows:
+# at 1457
+# at 1535
+#<date> server id 1 end_log_pos 1535 CRC32 XXX Annotate_rows:
#Q> INSERT INTO t1 VALUES (12, 1, 2, 3, NULL, 5, 6, 7, "A")
-#<date> server id 1 end_log_pos 1579 CRC32 XXX Table_map: `test`.`t1` mapped to number num
-# at 1579
-#<date> server id 1 end_log_pos 1646 CRC32 XXX Write_rows: table id 32 flags: STMT_END_F
+#<date> server id 1 end_log_pos 1597 CRC32 XXX Table_map: `test`.`t1` mapped to number num
+# at 1597
+#<date> server id 1 end_log_pos 1664 CRC32 XXX Write_rows: table id 32 flags: STMT_END_F
### INSERT INTO `test`.`t1`
### SET
### @1=12 /* INT meta=0 nullable=0 is_null=0 */
@@ -130,23 +130,23 @@ BEGIN
### @8=7 /* INT meta=0 nullable=1 is_null=0 */
### @9='A' /* STRING(1) meta=65025 nullable=1 is_null=0 */
# Number of rows: 1
-# at 1646
-#<date> server id 1 end_log_pos 1719 CRC32 XXX Query thread_id=5 exec_time=x error_code=0
+# at 1664
+#<date> server id 1 end_log_pos 1737 CRC32 XXX Query thread_id=5 exec_time=x error_code=0
SET TIMESTAMP=X/*!*/;
COMMIT
/*!*/;
-# at 1719
-#<date> server id 1 end_log_pos 1761 CRC32 XXX GTID 0-1-6
+# at 1737
+#<date> server id 1 end_log_pos 1779 CRC32 XXX GTID 0-1-6
/*!100001 SET @@session.gtid_seq_no=6*//*!*/;
BEGIN
/*!*/;
-# at 1761
-# at 1836
-#<date> server id 1 end_log_pos 1836 CRC32 XXX Annotate_rows:
+# at 1779
+# at 1854
+#<date> server id 1 end_log_pos 1854 CRC32 XXX Annotate_rows:
#Q> INSERT INTO t1 VALUES (13, 1, 2, 3, 0, 5, 6, 7, "A")
-#<date> server id 1 end_log_pos 1892 CRC32 XXX Table_map: `test`.`t1` mapped to number num
-# at 1892
-#<date> server id 1 end_log_pos 1962 CRC32 XXX Write_rows: table id 32 flags: STMT_END_F
+#<date> server id 1 end_log_pos 1916 CRC32 XXX Table_map: `test`.`t1` mapped to number num
+# at 1916
+#<date> server id 1 end_log_pos 1986 CRC32 XXX Write_rows: table id 32 flags: STMT_END_F
### INSERT INTO `test`.`t1`
### SET
### @1=13 /* INT meta=0 nullable=0 is_null=0 */
@@ -159,23 +159,23 @@ BEGIN
### @8=7 /* INT meta=0 nullable=1 is_null=0 */
### @9='A' /* STRING(1) meta=65025 nullable=1 is_null=0 */
# Number of rows: 1
-# at 1962
-#<date> server id 1 end_log_pos 2035 CRC32 XXX Query thread_id=5 exec_time=x error_code=0
+# at 1986
+#<date> server id 1 end_log_pos 2059 CRC32 XXX Query thread_id=5 exec_time=x error_code=0
SET TIMESTAMP=X/*!*/;
COMMIT
/*!*/;
-# at 2035
-#<date> server id 1 end_log_pos 2077 CRC32 XXX GTID 0-1-7
+# at 2059
+#<date> server id 1 end_log_pos 2101 CRC32 XXX GTID 0-1-7
/*!100001 SET @@session.gtid_seq_no=7*//*!*/;
BEGIN
/*!*/;
-# at 2077
-# at 2131
-#<date> server id 1 end_log_pos 2131 CRC32 XXX Annotate_rows:
+# at 2101
+# at 2155
+#<date> server id 1 end_log_pos 2155 CRC32 XXX Annotate_rows:
#Q> INSERT INTO t2 SELECT * FROM t1
-#<date> server id 1 end_log_pos 2187 CRC32 XXX Table_map: `test`.`t2` mapped to number num
-# at 2187
-#<date> server id 1 end_log_pos 2354 CRC32 XXX Write_rows: table id 33 flags: STMT_END_F
+#<date> server id 1 end_log_pos 2217 CRC32 XXX Table_map: `test`.`t2` mapped to number num
+# at 2217
+#<date> server id 1 end_log_pos 2384 CRC32 XXX Write_rows: table id 33 flags: STMT_END_F
### INSERT INTO `test`.`t2`
### SET
### @1=10 /* INT meta=0 nullable=0 is_null=0 */
@@ -221,23 +221,23 @@ BEGIN
### @8=7 /* INT meta=0 nullable=1 is_null=0 */
### @9='A' /* STRING(1) meta=65025 nullable=1 is_null=0 */
# Number of rows: 4
-# at 2354
-#<date> server id 1 end_log_pos 2427 CRC32 XXX Query thread_id=5 exec_time=x error_code=0
+# at 2384
+#<date> server id 1 end_log_pos 2457 CRC32 XXX Query thread_id=5 exec_time=x error_code=0
SET TIMESTAMP=X/*!*/;
COMMIT
/*!*/;
-# at 2427
-#<date> server id 1 end_log_pos 2469 CRC32 XXX GTID 0-1-8
+# at 2457
+#<date> server id 1 end_log_pos 2499 CRC32 XXX GTID 0-1-8
/*!100001 SET @@session.gtid_seq_no=8*//*!*/;
BEGIN
/*!*/;
-# at 2469
-# at 2535
-#<date> server id 1 end_log_pos 2535 CRC32 XXX Annotate_rows:
+# at 2499
+# at 2565
+#<date> server id 1 end_log_pos 2565 CRC32 XXX Annotate_rows:
#Q> UPDATE t2 SET f4=5 WHERE f4>0 or f4 is NULL
-#<date> server id 1 end_log_pos 2591 CRC32 XXX Table_map: `test`.`t2` mapped to number num
-# at 2591
-#<date> server id 1 end_log_pos 2657 CRC32 XXX Update_rows: table id 33 flags: STMT_END_F
+#<date> server id 1 end_log_pos 2627 CRC32 XXX Table_map: `test`.`t2` mapped to number num
+# at 2627
+#<date> server id 1 end_log_pos 2693 CRC32 XXX Update_rows: table id 33 flags: STMT_END_F
### UPDATE `test`.`t2`
### WHERE
### @1=10 /* INT meta=0 nullable=0 is_null=0 */
@@ -254,23 +254,23 @@ BEGIN
### SET
### @5=5 /* INT meta=0 nullable=1 is_null=0 */
# Number of rows: 3
-# at 2657
-#<date> server id 1 end_log_pos 2730 CRC32 XXX Query thread_id=5 exec_time=x error_code=0
+# at 2693
+#<date> server id 1 end_log_pos 2766 CRC32 XXX Query thread_id=5 exec_time=x error_code=0
SET TIMESTAMP=X/*!*/;
COMMIT
/*!*/;
-# at 2730
-#<date> server id 1 end_log_pos 2772 CRC32 XXX GTID 0-1-9
+# at 2766
+#<date> server id 1 end_log_pos 2808 CRC32 XXX GTID 0-1-9
/*!100001 SET @@session.gtid_seq_no=9*//*!*/;
BEGIN
/*!*/;
-# at 2772
-# at 2809
-#<date> server id 1 end_log_pos 2809 CRC32 XXX Annotate_rows:
+# at 2808
+# at 2845
+#<date> server id 1 end_log_pos 2845 CRC32 XXX Annotate_rows:
#Q> DELETE FROM t1
-#<date> server id 1 end_log_pos 2865 CRC32 XXX Table_map: `test`.`t1` mapped to number num
-# at 2865
-#<date> server id 1 end_log_pos 2919 CRC32 XXX Delete_rows: table id 32 flags: STMT_END_F
+#<date> server id 1 end_log_pos 2907 CRC32 XXX Table_map: `test`.`t1` mapped to number num
+# at 2907
+#<date> server id 1 end_log_pos 2961 CRC32 XXX Delete_rows: table id 32 flags: STMT_END_F
### DELETE FROM `test`.`t1`
### WHERE
### @1=10 /* INT meta=0 nullable=0 is_null=0 */
@@ -284,23 +284,23 @@ BEGIN
### WHERE
### @1=13 /* INT meta=0 nullable=0 is_null=0 */
# Number of rows: 4
-# at 2919
-#<date> server id 1 end_log_pos 2992 CRC32 XXX Query thread_id=5 exec_time=x error_code=0
+# at 2961
+#<date> server id 1 end_log_pos 3034 CRC32 XXX Query thread_id=5 exec_time=x error_code=0
SET TIMESTAMP=X/*!*/;
COMMIT
/*!*/;
-# at 2992
-#<date> server id 1 end_log_pos 3034 CRC32 XXX GTID 0-1-10
+# at 3034
+#<date> server id 1 end_log_pos 3076 CRC32 XXX GTID 0-1-10
/*!100001 SET @@session.gtid_seq_no=10*//*!*/;
BEGIN
/*!*/;
-# at 3034
-# at 3071
-#<date> server id 1 end_log_pos 3071 CRC32 XXX Annotate_rows:
+# at 3076
+# at 3113
+#<date> server id 1 end_log_pos 3113 CRC32 XXX Annotate_rows:
#Q> DELETE FROM t2
-#<date> server id 1 end_log_pos 3127 CRC32 XXX Table_map: `test`.`t2` mapped to number num
-# at 3127
-#<date> server id 1 end_log_pos 3181 CRC32 XXX Delete_rows: table id 33 flags: STMT_END_F
+#<date> server id 1 end_log_pos 3175 CRC32 XXX Table_map: `test`.`t2` mapped to number num
+# at 3175
+#<date> server id 1 end_log_pos 3229 CRC32 XXX Delete_rows: table id 33 flags: STMT_END_F
### DELETE FROM `test`.`t2`
### WHERE
### @1=10 /* INT meta=0 nullable=0 is_null=0 */
@@ -314,13 +314,13 @@ BEGIN
### WHERE
### @1=13 /* INT meta=0 nullable=0 is_null=0 */
# Number of rows: 4
-# at 3181
-#<date> server id 1 end_log_pos 3254 CRC32 XXX Query thread_id=5 exec_time=x error_code=0
+# at 3229
+#<date> server id 1 end_log_pos 3302 CRC32 XXX Query thread_id=5 exec_time=x error_code=0
SET TIMESTAMP=X/*!*/;
COMMIT
/*!*/;
-# at 3254
-#<date> server id 1 end_log_pos 3302 CRC32 XXX Rotate to master-bin.000002 pos: 4
+# at 3302
+#<date> server id 1 end_log_pos 3350 CRC32 XXX Rotate to master-bin.000002 pos: 4
DELIMITER ;
# End of log file
ROLLBACK /* added by mysqlbinlog */;
@@ -365,9 +365,9 @@ BEGIN
#Q> UPDATE t1 t1 INNER JOIN t2 t2 ON t1.ref_id = t2.id
#Q> SET t1.is_deleted = TRUE
#Q> WHERE t1.id =
-#<date> server id 1 end_log_pos 594 CRC32 XXX Table_map: `test`.`t1` mapped to number 35
-# at 594
-#<date> server id 1 end_log_pos 643 CRC32 XXX Update_rows: table id 35 flags: STMT_END_F
+#<date> server id 1 end_log_pos 597 CRC32 XXX Table_map: `test`.`t1` mapped to number 35
+# at 597
+#<date> server id 1 end_log_pos 646 CRC32 XXX Update_rows: table id 35 flags: STMT_END_F
### UPDATE `test`.`t1`
### WHERE
### @1=1 /* LONGINT meta=0 nullable=0 is_null=0 */
@@ -375,8 +375,8 @@ BEGIN
### @2=b'1' /* BIT(1) meta=1 nullable=1 is_null=0 */
### @3=X /* TIMESTAMP(0) meta=0 nullable=0 is_null=0 */
# Number of rows: 1
-# at 643
-#<date> server id 1 end_log_pos 725 CRC32 XXX Query thread_id=5 exec_time=x error_code=0
+# at 646
+#<date> server id 1 end_log_pos 728 CRC32 XXX Query thread_id=5 exec_time=x error_code=0
SET TIMESTAMP=X/*!*/;
SET @@session.pseudo_thread_id=5/*!*/;
SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1, @@session.check_constraint_checks=1/*!*/;
@@ -388,8 +388,8 @@ SET @@session.lc_time_names=0/*!*/;
SET @@session.collation_database=DEFAULT/*!*/;
COMMIT
/*!*/;
-# at 725
-#<date> server id 1 end_log_pos 773 CRC32 XXX Rotate to master-bin.000004 pos: 4
+# at 728
+#<date> server id 1 end_log_pos 776 CRC32 XXX Rotate to master-bin.000004 pos: 4
DELIMITER ;
# End of log file
ROLLBACK /* added by mysqlbinlog */;
diff --git a/mysql-test/main/mysqld--help.result b/mysql-test/main/mysqld--help.result
index 3eccd39eac2..ce14d3043c0 100644
--- a/mysql-test/main/mysqld--help.result
+++ b/mysql-test/main/mysqld--help.result
@@ -114,6 +114,11 @@ The following specify which files/extra groups are read (specified before remain
the table) is logged in the before image, and only
changed columns are logged in the after image. (Default:
FULL).
+ --binlog-row-metadata=name
+ Controls whether metadata is logged using FULL or MINIMAL
+ format. FULL causes all metadata to be logged; MINIMAL
+ means that only metadata actually required by slave is
+ logged. Default: MINIMAL.
--binlog-stmt-cache-size=#
The size of the statement cache for updates to
non-transactional engines for the binary log. If you
@@ -1427,6 +1432,7 @@ binlog-format MIXED
binlog-optimize-thread-scheduling TRUE
binlog-row-event-max-size 8192
binlog-row-image FULL
+binlog-row-metadata MINIMAL
binlog-stmt-cache-size 32768
bulk-insert-buffer-size 8388608
character-set-client-handshake TRUE
diff --git a/mysql-test/suite/binlog/include/print_optional_metadata.inc b/mysql-test/suite/binlog/include/print_optional_metadata.inc
new file mode 100644
index 00000000000..c91121297ee
--- /dev/null
+++ b/mysql-test/suite/binlog/include/print_optional_metadata.inc
@@ -0,0 +1,32 @@
+# Auxaliary file for printing optional metadata in table_map_log_event
+# Usage :
+# --let $binlog_file=
+# [--let $stop_position]
+# [--let $print_primary_key]
+# --source extra/binlog_tests/print_optional_metadata.inc
+
+--let $output_file= $MYSQLTEST_VARDIR/tmp/mysqlbinlog.output
+
+--let $_stop_position_opt=
+if ($stop_position)
+{
+ --let $_stop_position_opt=--stop-position=$stop_position
+}
+
+--exec $MYSQL_BINLOG -F --print-table-metadata $_stop_position_opt $binlog_file > $output_file
+
+
+--let $grep_pattern= # (?:Columns\(| {8})
+--let $grep_file= $output_file
+--source include/grep_pattern.inc
+
+if ($print_primary_key)
+{
+ --let $grep_pattern= # Primary Key
+ --source include/grep_pattern.inc
+}
+--remove_file $output_file
+--let $stop_position=
+--let $_stop_position_opt=
+
+
diff --git a/mysql-test/suite/binlog/r/binlog_table_map_optional_metadata.result b/mysql-test/suite/binlog/r/binlog_table_map_optional_metadata.result
new file mode 100644
index 00000000000..c478ae5a3f1
--- /dev/null
+++ b/mysql-test/suite/binlog/r/binlog_table_map_optional_metadata.result
@@ -0,0 +1,401 @@
+RESET MASTER;
+#
+# Temporal types can be printed correctly
+#
+CREATE TABLE t1(c_year YEAR, c_date DATE, c_time TIME, c_time_f TIME(3),
+c_datetime DATETIME, c_datetime_f DATETIME(3),
+c_timestamp TIMESTAMP, c_timestamp_f TIMESTAMP(3) DEFAULT "2017-1-1 10:10:10");
+INSERT INTO t1(c_year) VALUES(2017);
+Matching lines are:
+# Columns(YEAR,
+# DATE,
+# TIME,
+# TIME(3),
+# DATETIME,
+# DATETIME(3),
+# TIMESTAMP NOT NULL,
+# TIMESTAMP(3) NOT NULL)
+Occurrences of '# (?:Columns\(| {8})' in the input file: 8
+DROP TABLE t1;
+RESET MASTER;
+#
+# Json types can be printed correctly
+#
+CREATE TABLE t1 (c_json JSON, c_char CHAR(100));
+INSERT INTO t1(c_char) VALUES("abc");
+Matching lines are:
+# Columns(LONGTEXT CHARSET utf8mb4 COLLATE utf8mb4_bin CHARSET utf8mb4 COLLATE utf8mb4_bin,
+# CHAR(100) CHARSET latin1 COLLATE latin1_swedish_ci CHARSET latin1 COLLATE latin1_swedish_ci)
+Occurrences of '# (?:Columns\(| {8})' in the input file: 2
+DROP TABLE t1;
+RESET MASTER;
+#
+# Geometry types can be printed correctly
+#
+CREATE TABLE t1 (c_geo GEOMETRY, c_point POINT, c_linestring LINESTRING,
+c_polygon POLYGON, c_multi_point MULTIPOINT,
+c_multi_linestring MULTILINESTRING, c_multi_polygon MULTIPOLYGON,
+c_geometrycollection GEOMETRYCOLLECTION, c_char CHAR(100));
+INSERT INTO t1(c_point) VALUES(ST_PointFromText('POINT(10 10)'));
+Matching lines are:
+# Columns(GEOMETRY,
+# POINT,
+# LINESTRING,
+# POLYGON,
+# MULTIPOINT,
+# MULTILINESTRING,
+# MULTIPOLYGON,
+# GEOMETRYCOLLECTION,
+# CHAR(100) CHARSET latin1 COLLATE latin1_swedish_ci CHARSET latin1 COLLATE latin1_swedish_ci)
+Occurrences of '# (?:Columns\(| {8})' in the input file: 9
+RESET MASTER;
+SET GLOBAL binlog_row_metadata = FULL;
+INSERT INTO t1(c_point) VALUES(ST_PointFromText('POINT(10 10)'));
+Matching lines are:
+# Columns(`c_geo` GEOMETRY,
+# `c_point` POINT,
+# `c_linestring` LINESTRING,
+# `c_polygon` POLYGON,
+# `c_multi_point` MULTIPOINT,
+# `c_multi_linestring` MULTILINESTRING,
+# `c_multi_polygon` MULTIPOLYGON,
+# `c_geometrycollection` GEOMETRYCOLLECTION,
+# `c_char` CHAR(100) CHARSET latin1 COLLATE latin1_swedish_ci CHARSET latin1 COLLATE latin1_swedish_ci)
+Occurrences of '# (?:Columns\(| {8})' in the input file: 9
+DROP TABLE t1;
+RESET MASTER;
+#
+# Numeric types can be printed correctly
+#
+CREATE TABLE t1(c_bit BIT(10), c_bool BOOL, c_smallint SMALLINT,
+c_mediumint MEDIUMINT, c_int INT UNSIGNED, c_bigint BIGINT,
+c_float FLOAT UNSIGNED, c_double DOUBLE, c_decimal DECIMAL(10, 2));
+SET GLOBAL binlog_row_metadata = MINIMAL;
+INSERT INTO t1(c_bool) VALUES(1);
+# UNSIGNED flag should be printed
+Matching lines are:
+# Columns(BIT(10),
+# TINYINT,
+# SMALLINT,
+# MEDIUMINT,
+# INT UNSIGNED,
+# BIGINT,
+# FLOAT UNSIGNED,
+# DOUBLE,
+# DECIMAL(10,2))
+Occurrences of '# (?:Columns\(| {8})' in the input file: 9
+RESET MASTER;
+SET GLOBAL binlog_row_metadata = FULL;
+INSERT INTO t1(c_bool) VALUES(1);
+Matching lines are:
+# Columns(`c_bit` BIT(10),
+# `c_bool` TINYINT,
+# `c_smallint` SMALLINT,
+# `c_mediumint` MEDIUMINT,
+# `c_int` INT UNSIGNED,
+# `c_bigint` BIGINT,
+# `c_float` FLOAT UNSIGNED,
+# `c_double` DOUBLE,
+# `c_decimal` DECIMAL(10,2))
+Occurrences of '# (?:Columns\(| {8})' in the input file: 9
+DROP TABLE t1;
+RESET MASTER;
+#
+# Character types can be printed correctly
+#
+CREATE TABLE t1(c_char CHAR(10), c_varchar VARCHAR(500),
+c_tinytext TINYTEXT, c_text TEXT,
+c_mediumtext MEDIUMTEXT, c_longtext LONGTEXT CHARSET utf8);
+SET GLOBAL binlog_row_metadata = MINIMAL;
+INSERT INTO t1(c_char) VALUES("1");
+Matching lines are:
+# Columns(CHAR(10) CHARSET latin1 COLLATE latin1_swedish_ci CHARSET latin1 COLLATE latin1_swedish_ci,
+# VARCHAR(500) CHARSET latin1 COLLATE latin1_swedish_ci CHARSET latin1 COLLATE latin1_swedish_ci,
+# TINYTEXT CHARSET latin1 COLLATE latin1_swedish_ci CHARSET latin1 COLLATE latin1_swedish_ci,
+# TEXT CHARSET latin1 COLLATE latin1_swedish_ci CHARSET latin1 COLLATE latin1_swedish_ci,
+# MEDIUMTEXT CHARSET latin1 COLLATE latin1_swedish_ci CHARSET latin1 COLLATE latin1_swedish_ci,
+# LONGTEXT CHARSET utf8 COLLATE utf8_general_ci CHARSET utf8 COLLATE utf8_general_ci)
+Occurrences of '# (?:Columns\(| {8})' in the input file: 6
+RESET MASTER;
+SET GLOBAL binlog_row_metadata = FULL;
+INSERT INTO t1(c_char) VALUES("1");
+Matching lines are:
+# Columns(`c_char` CHAR(10) CHARSET latin1 COLLATE latin1_swedish_ci CHARSET latin1 COLLATE latin1_swedish_ci,
+# `c_varchar` VARCHAR(500) CHARSET latin1 COLLATE latin1_swedish_ci CHARSET latin1 COLLATE latin1_swedish_ci,
+# `c_tinytext` TINYTEXT CHARSET latin1 COLLATE latin1_swedish_ci CHARSET latin1 COLLATE latin1_swedish_ci,
+# `c_text` TEXT CHARSET latin1 COLLATE latin1_swedish_ci CHARSET latin1 COLLATE latin1_swedish_ci,
+# `c_mediumtext` MEDIUMTEXT CHARSET latin1 COLLATE latin1_swedish_ci CHARSET latin1 COLLATE latin1_swedish_ci,
+# `c_longtext` LONGTEXT CHARSET utf8 COLLATE utf8_general_ci CHARSET utf8 COLLATE utf8_general_ci)
+Occurrences of '# (?:Columns\(| {8})' in the input file: 6
+DROP TABLE t1;
+RESET MASTER;
+#
+# Column names with non-ascii characters and escape characters can be printed correctly
+#
+CREATE TABLE t1(`åäö表\a'``"` INT);
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `åäö表\a'``"` int(11) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+INSERT INTO t1 VALUES(1);
+Matching lines are:
+# Columns(`åäö表\\a\'`"` INT)
+Occurrences of '# (?:Columns\(| {8})' in the input file: 1
+DROP TABLE t1;
+RESET MASTER;
+#
+# Charsets can be printed correctly
+#
+CREATE TABLE t1(c_char_utf8 CHAR(10) CHARSET utf8,
+c_varchar_utf8 VARCHAR(10) CHARSET utf8,
+c_text_utf8 TEXT CHARSET utf8);
+INSERT INTO t1 VALUES("1", "2", "3");
+Matching lines are:
+# Columns(`c_char_utf8` CHAR(10) CHARSET utf8 COLLATE utf8_general_ci CHARSET utf8 COLLATE utf8_general_ci,
+# `c_varchar_utf8` VARCHAR(10) CHARSET utf8 COLLATE utf8_general_ci CHARSET utf8 COLLATE utf8_general_ci,
+# `c_text_utf8` TEXT CHARSET utf8 COLLATE utf8_general_ci CHARSET utf8 COLLATE utf8_general_ci)
+Occurrences of '# (?:Columns\(| {8})' in the input file: 3
+DROP TABLE t1;
+RESET MASTER;
+CREATE TABLE t1(c_utf8mb4_520 CHAR(10) CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci,
+c_utf8mb4_0900 VARCHAR(10) CHARSET utf8mb4 COLLATE utf8mb4_polish_ci,
+c_utf8mb4_def TEXT CHARSET utf8mb4);
+INSERT INTO t1 VALUES("1", "2", "3");
+Matching lines are:
+# Columns(`c_utf8mb4_520` CHAR(10) CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci,
+# `c_utf8mb4_0900` VARCHAR(10) CHARSET utf8mb4 COLLATE utf8mb4_polish_ci CHARSET utf8mb4 COLLATE utf8mb4_polish_ci,
+# `c_utf8mb4_def` TEXT CHARSET utf8mb4 COLLATE utf8mb4_general_ci CHARSET utf8mb4 COLLATE utf8mb4_general_ci)
+Occurrences of '# (?:Columns\(| {8})' in the input file: 3
+DROP TABLE t1;
+RESET MASTER;
+#
+# Blob and binary columns can be printed correctly
+#
+CREATE TABLE t1(c_binary BINARY(10), c_varbinary VARBINARY(10),
+c_tinyblob TINYBLOB, c_blob BLOB,
+c_mediumblob MEDIUMBLOB, c_longblob LONGBLOB);
+INSERT INTO t1 VALUES("1", "2", "3", "4", "5", "6");
+Matching lines are:
+# Columns(`c_binary` BINARY(10),
+# `c_varbinary` VARBINARY(10),
+# `c_tinyblob` TINYBLOB,
+# `c_blob` BLOB,
+# `c_mediumblob` MEDIUMBLOB,
+# `c_longblob` LONGBLOB)
+Occurrences of '# (?:Columns\(| {8})' in the input file: 6
+DROP TABLE t1;
+RESET MASTER;
+#
+# Verify that SET string values and character sets can be printed correctly
+#
+CREATE TABLE t1(
+c_set_1 SET("set1_v1_å", "set1_v2_ä", "set1_v3_ö"),
+c_set_2 SET("set2_v1_å", "set2_v2_ä", "set2_v3_ö") CHARACTER SET binary,
+c_set_3 SET("set3_v1_å", "set3_v2_ä", "set3_v3_ö") CHARACTER SET latin1,
+c_set_4 SET("set4_v1_å", "set4_v2_ä", "set4_v3_ö") CHARACTER SET swe7 COLLATE swe7_bin,
+c_set_5 SET("set5_v1_å", "set5_v2_ä", "set5_v3_ö") CHARACTER SET ucs2,
+c_set_6 SET("set6_v1_å", "set6_v2_ä", "set6_v3_ö") CHARACTER SET utf32);
+SET GLOBAL binlog_row_metadata = MINIMAL;
+INSERT INTO t1 VALUES("set1_v1_å", "set2_v2_ä", "set3_v3_ö", "set4_v1_å", "set5_v2_ä", "set6_v3_ö");
+Matching lines are:
+# Columns(SET,
+# SET,
+# SET,
+# SET,
+# SET,
+# SET)
+Occurrences of '# (?:Columns\(| {8})' in the input file: 6
+RESET MASTER;
+SET GLOBAL binlog_row_metadata = FULL;
+INSERT INTO t1 VALUES("set1_v1_å", "set2_v2_ä", "set3_v3_ö", "set4_v1_å", "set5_v2_ä", "set6_v3_ö");
+Matching lines are:
+# Columns(`c_set_1` SET('set1_v1_å','set1_v2_ä','set1_v3_ö') CHARSET latin1 COLLATE latin1_swedish_ci CHARSET latin1 COLLATE latin1_swedish_ci,
+# `c_set_2` SET('set2_v1_å','set2_v2_ä','set2_v3_ö') CHARSET binary COLLATE binary CHARSET binary COLLATE binary,
+# `c_set_3` SET('set3_v1_å','set3_v2_ä','set3_v3_ö') CHARSET latin1 COLLATE latin1_swedish_ci CHARSET latin1 COLLATE latin1_swedish_ci,
+# `c_set_4` SET('set4_v1_??','set4_v2_??','set4_v3_??') CHARSET swe7 COLLATE swe7_bin CHARSET swe7 COLLATE swe7_bin,
+# `c_set_5` SET('\0s\0e\0t\05\0_\0v\01\0_\0�\0�','\0s\0e\0t\05\0_\0v\02\0_\0�\0�','\0s\0e\0t\05\0_\0v\03\0_\0�\0�') CHARSET ucs2 COLLATE ucs2_general_ci CHARSET ucs2 COLLATE ucs2_general_ci,
+# `c_set_6` SET('\0\0\0s\0\0\0e\0\0\0t\0\0\06\0\0\0_\0\0\0v\0\0\01\0\0\0_\0\0\0�\0\0\0�','\0\0\0s\0\0\0e\0\0\0t\0\0\06\0\0\0_\0\0\0v\0\0\02\0\0\0_\0\0\0�\0\0\0�','\0\0\0s\0\0\0e\0\0\0t\0\0\06\0\0\0_\0\0\0v\0\0\03\0\0\0_\0\0\0�\0\0\0�') CHARSET utf32 COLLATE utf32_general_ci CHARSET utf32 COLLATE utf32_general_ci)
+Occurrences of '# (?:Columns\(| {8})' in the input file: 6
+DROP TABLE t1;
+RESET MASTER;
+#
+# Verify that ENUM string values and character sets can be printed correctly
+#
+CREATE TABLE t1(
+c_enum_1 ENUM("enum1_v1_å", "enum1_v2_ä", "enum1_v3_ö"),
+c_enum_2 ENUM("enum2_v1_å", "enum2_v2_ä", "enum2_v3_ö") CHARACTER SET binary,
+c_enum_3 ENUM("enum3_v1_å", "enum3_v2_ä", "enum3_v3_ö") CHARACTER SET latin1,
+c_enum_4 ENUM("enum4_v1_å", "enum4_v2_ä", "enum4_v3_ö") CHARACTER SET swe7 COLLATE swe7_bin,
+c_enum_5 ENUM("enum5_v1_å", "enum5_v2_ä", "enum5_v3_ö") CHARACTER SET ucs2,
+c_enum_6 ENUM("enum6_v1_å", "enum6_v2_ä", "enum6_v3_ö") CHARACTER SET utf32
+);
+SET GLOBAL binlog_row_metadata = MINIMAL;
+INSERT INTO t1 VALUES("enum1_v1_å", "enum2_v2_ä", "enum3_v3_ö", "enum4_v1_å", "enum5_v2_ä", "enum6_v3_ö");
+Matching lines are:
+# Columns(ENUM,
+# ENUM,
+# ENUM,
+# ENUM,
+# ENUM,
+# ENUM)
+Occurrences of '# (?:Columns\(| {8})' in the input file: 6
+RESET MASTER;
+SET GLOBAL binlog_row_metadata = FULL;
+INSERT INTO t1 VALUES("enum1_v1_å", "enum2_v2_ä", "enum3_v3_ö", "enum4_v1_å", "enum5_v2_ä", "enum6_v3_ö");
+Matching lines are:
+# Columns(`c_enum_1` ENUM('enum1_v1_å','enum1_v2_ä','enum1_v3_ö') CHARSET latin1 COLLATE latin1_swedish_ci CHARSET latin1 COLLATE latin1_swedish_ci,
+# `c_enum_2` ENUM('enum2_v1_å','enum2_v2_ä','enum2_v3_ö') CHARSET binary COLLATE binary CHARSET binary COLLATE binary,
+# `c_enum_3` ENUM('enum3_v1_å','enum3_v2_ä','enum3_v3_ö') CHARSET latin1 COLLATE latin1_swedish_ci CHARSET latin1 COLLATE latin1_swedish_ci,
+# `c_enum_4` ENUM('enum4_v1_??','enum4_v2_??','enum4_v3_??') CHARSET swe7 COLLATE swe7_bin CHARSET swe7 COLLATE swe7_bin,
+# `c_enum_5` ENUM('\0e\0n\0u\0m\05\0_\0v\01\0_\0�\0�','\0e\0n\0u\0m\05\0_\0v\02\0_\0�\0�','\0e\0n\0u\0m\05\0_\0v\03\0_\0�\0�') CHARSET ucs2 COLLATE ucs2_general_ci CHARSET ucs2 COLLATE ucs2_general_ci,
+# `c_enum_6` ENUM('\0\0\0e\0\0\0n\0\0\0u\0\0\0m\0\0\06\0\0\0_\0\0\0v\0\0\01\0\0\0_\0\0\0�\0\0\0�','\0\0\0e\0\0\0n\0\0\0u\0\0\0m\0\0\06\0\0\0_\0\0\0v\0\0\02\0\0\0_\0\0\0�\0\0\0�','\0\0\0e\0\0\0n\0\0\0u\0\0\0m\0\0\06\0\0\0_\0\0\0v\0\0\03\0\0\0_\0\0\0�\0\0\0�') CHARSET utf32 COLLATE utf32_general_ci CHARSET utf32 COLLATE utf32_general_ci)
+Occurrences of '# (?:Columns\(| {8})' in the input file: 6
+DROP TABLE t1;
+RESET MASTER;
+#
+# Verify that explicit NOT NULL can be printed correctly
+#
+CREATE TABLE t1(c_not_null1 INT NOT NULL, c_null1 INT, c_not_null2 INT NOT NULL,
+c_null2 INT);
+INSERT INTO t1 VALUES(1, 2, 3, 4);
+Matching lines are:
+# Columns(`c_not_null1` INT NOT NULL,
+# `c_null1` INT,
+# `c_not_null2` INT NOT NULL,
+# `c_null2` INT)
+Occurrences of '# (?:Columns\(| {8})' in the input file: 4
+DROP TABLE t1;
+RESET MASTER;
+#
+# Verify that primary key can be printed correctly
+#
+CREATE TABLE t1(c_key1 INT, c_key3 INT, c_not_key INT, c_key2 INT,
+PRIMARY KEY(c_key1, c_key2, c_key3));
+INSERT INTO t1 VALUES(1, 2, 3, 4);
+Matching lines are:
+# Columns(`c_key1` INT NOT NULL,
+# `c_key3` INT NOT NULL,
+# `c_not_key` INT,
+# `c_key2` INT NOT NULL)
+Occurrences of '# (?:Columns\(| {8})' in the input file: 4
+Matching lines are:
+# Primary Key(c_key1, c_key2, c_key3)
+Occurrences of '# Primary Key' in the input file: 1
+DROP TABLE t1;
+RESET MASTER;
+CREATE TABLE t1(c_key1 CHAR(100), c_key3 CHAR(100), c_not_key INT, c_key2 CHAR(10),
+PRIMARY KEY(c_key1(5), c_key2, c_key3(10)));
+INSERT INTO t1 VALUES("1", "2", 3, "4");
+Matching lines are:
+# Columns(`c_key1` CHAR(100) NOT NULL CHARSET latin1 COLLATE latin1_swedish_ci CHARSET latin1 COLLATE latin1_swedish_ci,
+# `c_key3` CHAR(100) NOT NULL CHARSET latin1 COLLATE latin1_swedish_ci CHARSET latin1 COLLATE latin1_swedish_ci,
+# `c_not_key` INT,
+# `c_key2` CHAR(10) NOT NULL CHARSET latin1 COLLATE latin1_swedish_ci CHARSET latin1 COLLATE latin1_swedish_ci)
+Occurrences of '# (?:Columns\(| {8})' in the input file: 4
+Matching lines are:
+# Primary Key(c_key1(5), c_key2, c_key3(10))
+Occurrences of '# Primary Key' in the input file: 1
+RESET MASTER;
+SET GLOBAL binlog_row_metadata = MINIMAL;
+INSERT INTO t1 VALUES("2", "2", 3, "4");
+Matching lines are:
+# Columns(CHAR(100) NOT NULL CHARSET latin1 COLLATE latin1_swedish_ci CHARSET latin1 COLLATE latin1_swedish_ci,
+# CHAR(100) NOT NULL CHARSET latin1 COLLATE latin1_swedish_ci CHARSET latin1 COLLATE latin1_swedish_ci,
+# INT,
+# CHAR(10) NOT NULL CHARSET latin1 COLLATE latin1_swedish_ci CHARSET latin1 COLLATE latin1_swedish_ci)
+Occurrences of '# (?:Columns\(| {8})' in the input file: 4
+Matching lines are:
+None
+Occurrences of '# Primary Key' in the input file: 0
+RESET MASTER;
+#
+# Coverage test: Print column index instead of column name if column name
+# is not binlogged.
+#
+SET GLOBAL binlog_row_metadata = FULL;
+SET SESSION debug_dbug = 'd, dont_log_column_name';
+INSERT INTO t1 VALUES("3", "2", 3, "4");
+Matching lines are:
+# Columns(`c_key1` CHAR(100) NOT NULL CHARSET latin1 COLLATE latin1_swedish_ci CHARSET latin1 COLLATE latin1_swedish_ci,
+# `c_key3` CHAR(100) NOT NULL CHARSET latin1 COLLATE latin1_swedish_ci CHARSET latin1 COLLATE latin1_swedish_ci,
+# `c_not_key` INT,
+# `c_key2` CHAR(10) NOT NULL CHARSET latin1 COLLATE latin1_swedish_ci CHARSET latin1 COLLATE latin1_swedish_ci)
+Occurrences of '# (?:Columns\(| {8})' in the input file: 4
+Matching lines are:
+# Primary Key(c_key1(5), c_key2, c_key3(10))
+Occurrences of '# Primary Key' in the input file: 1
+DROP TABLE t1;
+RESET MASTER;
+#
+# Coverage test: Inject an invalid column type
+#
+CREATE TABLE t1(c1 int, c2 BLOB);
+SET SESSION debug_dbug = 'd,inject_invalid_column_type';
+INSERT INTO t1 VALUES(1, "a");
+Matching lines are:
+# Columns(`c1` INT,
+# `c2` INVALID_TYPE(230))
+Occurrences of '# (?:Columns\(| {8})' in the input file: 2
+RESET MASTER;
+#
+# Coverage test: Inject an invalid BLOB metadata
+#
+SET SESSION debug_dbug = 'd,inject_invalid_blob_size';
+INSERT INTO t1 VALUES(2, "b");
+Matching lines are:
+# Columns(`c1` INT,
+# `c2` INVALID_BLOB(5))
+Occurrences of '# (?:Columns\(| {8})' in the input file: 2
+#
+# Coverage test: Inject an invalid Geometry type
+#
+DROP TABLE t1;
+CREATE TABLE t1(c_geometry GEOMETRY, c_point POINT, c_multilinestring MULTILINESTRING);
+RESET MASTER;
+SET SESSION debug_dbug = 'd,inject_invalid_geometry_type';
+INSERT INTO t1(c_point) VALUES(ST_PointFromText('POINT(10 10)'));
+Matching lines are:
+# Columns(`c_geometry` INVALID_GEOMETRY_TYPE(100),
+# `c_point` INVALID_GEOMETRY_TYPE(100),
+# `c_multilinestring` INVALID_GEOMETRY_TYPE(100))
+Occurrences of '# (?:Columns\(| {8})' in the input file: 3
+DROP TABLE t1;
+RESET MASTER;
+#
+# Comptibility Test: Verify mysqlbinlog can print OLD table_map_log_event
+# without any optional metadata
+#
+CREATE TABLE t1(c_int INT, c_tiny_int_unsigned TINYINT UNSIGNED,
+c_binary BINARY(10), c_text TEXT, c_point POINT);
+SET session debug_dbug='d,simulate_no_optional_metadata';
+INSERT INTO t1(c_int) VALUES(1);
+Matching lines are:
+# Columns(INT,
+# TINYINT,
+# BINARY(10),
+# BLOB,
+# GEOMETRY)
+Occurrences of '# (?:Columns\(| {8})' in the input file: 5
+DROP TABLE t1;
+RESET MASTER;
+#
+# Simulate error on initializing charset and primary key metadata
+#
+CREATE TABLE t1(c1 char(10) PRIMARY KEY);
+SET session debug_dbug='d,simulate_init_charset_field_error';
+INSERT INTO t1 VALUES("a");
+SET GLOBAL binlog_row_metadata = FULL;
+SET session debug_dbug='d,simulate_init_primary_key_field_error';
+INSERT INTO t1 VALUES("b");
+Matching lines are:
+# Columns(BINARY(10) NOT NULL)
+# Columns(BINARY(10) NOT NULL)
+Occurrences of '# (?:Columns\(| {8})' in the input file: 2
+Matching lines are:
+None
+Occurrences of '# Primary Key' in the input file: 0
+SET SESSION debug_dbug = '';
+SET GLOBAL binlog_row_metadata = MINIMAL;
+DROP TABLE t1;
+RESET MASTER;
diff --git a/mysql-test/suite/binlog/t/binlog_table_map_optional_metadata.test b/mysql-test/suite/binlog/t/binlog_table_map_optional_metadata.test
new file mode 100644
index 00000000000..be891759a7e
--- /dev/null
+++ b/mysql-test/suite/binlog/t/binlog_table_map_optional_metadata.test
@@ -0,0 +1,348 @@
+################################################################################
+# WL#4618 RBR: extended table metadata in the binary log
+#
+# Below metadata is logged into Table_map_log_event
+# - signedness of numeric columns
+# - charsets of character columns
+# - column names
+# - set/enum character sets and string values
+# - primary key
+#
+# The first two are always logged. The others are controlled by system
+# variable --binlog-row-metadata
+#
+# The test will verify if the metadata can be logged and printed by mysqlbinlog
+# correctly.
+# mysqlbinlog --print-table-metadata will print the extra metadata
+################################################################################
+--source include/have_debug.inc
+--source include/have_binlog_format_row.inc
+
+RESET MASTER;
+
+--let $MYSQLD_DATADIR= `select @@datadir`
+--let $binlog_file= $MYSQLD_DATADIR/master-bin.000001
+
+--echo #
+--echo # Temporal types can be printed correctly
+--echo #
+CREATE TABLE t1(c_year YEAR, c_date DATE, c_time TIME, c_time_f TIME(3),
+ c_datetime DATETIME, c_datetime_f DATETIME(3),
+ c_timestamp TIMESTAMP, c_timestamp_f TIMESTAMP(3) DEFAULT "2017-1-1 10:10:10");
+
+INSERT INTO t1(c_year) VALUES(2017);
+--source include/print_optional_metadata.inc
+
+DROP TABLE t1;
+RESET MASTER;
+
+--echo #
+--echo # Json types can be printed correctly
+--echo #
+CREATE TABLE t1 (c_json JSON, c_char CHAR(100));
+INSERT INTO t1(c_char) VALUES("abc");
+--source include/print_optional_metadata.inc
+
+DROP TABLE t1;
+RESET MASTER;
+
+--echo #
+--echo # Geometry types can be printed correctly
+--echo #
+CREATE TABLE t1 (c_geo GEOMETRY, c_point POINT, c_linestring LINESTRING,
+ c_polygon POLYGON, c_multi_point MULTIPOINT,
+ c_multi_linestring MULTILINESTRING, c_multi_polygon MULTIPOLYGON,
+ c_geometrycollection GEOMETRYCOLLECTION, c_char CHAR(100));
+
+INSERT INTO t1(c_point) VALUES(ST_PointFromText('POINT(10 10)'));
+--source include/print_optional_metadata.inc
+
+RESET MASTER;
+SET GLOBAL binlog_row_metadata = FULL;
+
+# geometry type is binlogged, the real geometry types are printed
+INSERT INTO t1(c_point) VALUES(ST_PointFromText('POINT(10 10)'));
+--source include/print_optional_metadata.inc
+
+DROP TABLE t1;
+RESET MASTER;
+
+--echo #
+--echo # Numeric types can be printed correctly
+--echo #
+CREATE TABLE t1(c_bit BIT(10), c_bool BOOL, c_smallint SMALLINT,
+ c_mediumint MEDIUMINT, c_int INT UNSIGNED, c_bigint BIGINT,
+ c_float FLOAT UNSIGNED, c_double DOUBLE, c_decimal DECIMAL(10, 2));
+
+SET GLOBAL binlog_row_metadata = MINIMAL;
+INSERT INTO t1(c_bool) VALUES(1);
+
+--echo # UNSIGNED flag should be printed
+--source include/print_optional_metadata.inc
+
+RESET MASTER;
+SET GLOBAL binlog_row_metadata = FULL;
+INSERT INTO t1(c_bool) VALUES(1);
+
+--source include/print_optional_metadata.inc
+
+DROP TABLE t1;
+RESET MASTER;
+
+--echo #
+--echo # Character types can be printed correctly
+--echo #
+CREATE TABLE t1(c_char CHAR(10), c_varchar VARCHAR(500),
+ c_tinytext TINYTEXT, c_text TEXT,
+ c_mediumtext MEDIUMTEXT, c_longtext LONGTEXT CHARSET utf8);
+
+SET GLOBAL binlog_row_metadata = MINIMAL;
+INSERT INTO t1(c_char) VALUES("1");
+
+# Charset set is printed with default charset
+--source include/print_optional_metadata.inc
+
+RESET MASTER;
+SET GLOBAL binlog_row_metadata = FULL;
+INSERT INTO t1(c_char) VALUES("1");
+
+--source include/print_optional_metadata.inc
+
+DROP TABLE t1;
+RESET MASTER;
+
+--echo #
+--echo # Column names with non-ascii characters and escape characters can be printed correctly
+--echo #
+CREATE TABLE t1(`åäö表\a'``"` INT);
+
+SHOW CREATE TABLE t1;
+
+INSERT INTO t1 VALUES(1);
+--source include/print_optional_metadata.inc
+DROP TABLE t1;
+RESET MASTER;
+
+--echo #
+--echo # Charsets can be printed correctly
+--echo #
+CREATE TABLE t1(c_char_utf8 CHAR(10) CHARSET utf8,
+ c_varchar_utf8 VARCHAR(10) CHARSET utf8,
+ c_text_utf8 TEXT CHARSET utf8);
+
+INSERT INTO t1 VALUES("1", "2", "3");
+
+# Charset set is printed with Default charset
+--source include/print_optional_metadata.inc
+
+DROP TABLE t1;
+RESET MASTER;
+
+# Test collation number less than 250 and collation number greater than 250
+CREATE TABLE t1(c_utf8mb4_520 CHAR(10) CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci,
+ c_utf8mb4_0900 VARCHAR(10) CHARSET utf8mb4 COLLATE utf8mb4_polish_ci,
+ c_utf8mb4_def TEXT CHARSET utf8mb4);
+
+INSERT INTO t1 VALUES("1", "2", "3");
+
+# Charset set is printed without default charset
+--source include/print_optional_metadata.inc
+
+DROP TABLE t1;
+RESET MASTER;
+
+--echo #
+--echo # Blob and binary columns can be printed correctly
+--echo #
+CREATE TABLE t1(c_binary BINARY(10), c_varbinary VARBINARY(10),
+ c_tinyblob TINYBLOB, c_blob BLOB,
+ c_mediumblob MEDIUMBLOB, c_longblob LONGBLOB);
+
+INSERT INTO t1 VALUES("1", "2", "3", "4", "5", "6");
+--source include/print_optional_metadata.inc
+
+DROP TABLE t1;
+RESET MASTER;
+
+--echo #
+--echo # Verify that SET string values and character sets can be printed correctly
+--echo #
+
+CREATE TABLE t1(
+ c_set_1 SET("set1_v1_å", "set1_v2_ä", "set1_v3_ö"),
+ c_set_2 SET("set2_v1_å", "set2_v2_ä", "set2_v3_ö") CHARACTER SET binary,
+ c_set_3 SET("set3_v1_å", "set3_v2_ä", "set3_v3_ö") CHARACTER SET latin1,
+ c_set_4 SET("set4_v1_å", "set4_v2_ä", "set4_v3_ö") CHARACTER SET swe7 COLLATE swe7_bin,
+ c_set_5 SET("set5_v1_å", "set5_v2_ä", "set5_v3_ö") CHARACTER SET ucs2,
+ c_set_6 SET("set6_v1_å", "set6_v2_ä", "set6_v3_ö") CHARACTER SET utf32);
+
+SET GLOBAL binlog_row_metadata = MINIMAL;
+INSERT INTO t1 VALUES("set1_v1_å", "set2_v2_ä", "set3_v3_ö", "set4_v1_å", "set5_v2_ä", "set6_v3_ö");
+--source include/print_optional_metadata.inc
+
+RESET MASTER;
+SET GLOBAL binlog_row_metadata = FULL;
+INSERT INTO t1 VALUES("set1_v1_å", "set2_v2_ä", "set3_v3_ö", "set4_v1_å", "set5_v2_ä", "set6_v3_ö");
+--source include/print_optional_metadata.inc
+
+DROP TABLE t1;
+RESET MASTER;
+
+--echo #
+--echo # Verify that ENUM string values and character sets can be printed correctly
+--echo #
+
+CREATE TABLE t1(
+ c_enum_1 ENUM("enum1_v1_å", "enum1_v2_ä", "enum1_v3_ö"),
+ c_enum_2 ENUM("enum2_v1_å", "enum2_v2_ä", "enum2_v3_ö") CHARACTER SET binary,
+ c_enum_3 ENUM("enum3_v1_å", "enum3_v2_ä", "enum3_v3_ö") CHARACTER SET latin1,
+ c_enum_4 ENUM("enum4_v1_å", "enum4_v2_ä", "enum4_v3_ö") CHARACTER SET swe7 COLLATE swe7_bin,
+ c_enum_5 ENUM("enum5_v1_å", "enum5_v2_ä", "enum5_v3_ö") CHARACTER SET ucs2,
+ c_enum_6 ENUM("enum6_v1_å", "enum6_v2_ä", "enum6_v3_ö") CHARACTER SET utf32
+ );
+
+SET GLOBAL binlog_row_metadata = MINIMAL;
+INSERT INTO t1 VALUES("enum1_v1_å", "enum2_v2_ä", "enum3_v3_ö", "enum4_v1_å", "enum5_v2_ä", "enum6_v3_ö");
+--source include/print_optional_metadata.inc
+
+RESET MASTER;
+SET GLOBAL binlog_row_metadata = FULL;
+INSERT INTO t1 VALUES("enum1_v1_å", "enum2_v2_ä", "enum3_v3_ö", "enum4_v1_å", "enum5_v2_ä", "enum6_v3_ö");
+--source include/print_optional_metadata.inc
+
+DROP TABLE t1;
+RESET MASTER;
+
+--echo #
+--echo # Verify that explicit NOT NULL can be printed correctly
+--echo #
+CREATE TABLE t1(c_not_null1 INT NOT NULL, c_null1 INT, c_not_null2 INT NOT NULL,
+ c_null2 INT);
+
+INSERT INTO t1 VALUES(1, 2, 3, 4);
+--source include/print_optional_metadata.inc
+
+DROP TABLE t1;
+RESET MASTER;
+
+--echo #
+--echo # Verify that primary key can be printed correctly
+--echo #
+CREATE TABLE t1(c_key1 INT, c_key3 INT, c_not_key INT, c_key2 INT,
+PRIMARY KEY(c_key1, c_key2, c_key3));
+
+INSERT INTO t1 VALUES(1, 2, 3, 4);
+--let $print_primary_key= 1
+--source include/print_optional_metadata.inc
+
+DROP TABLE t1;
+RESET MASTER;
+
+# Key has prefix
+CREATE TABLE t1(c_key1 CHAR(100), c_key3 CHAR(100), c_not_key INT, c_key2 CHAR(10),
+PRIMARY KEY(c_key1(5), c_key2, c_key3(10)));
+
+INSERT INTO t1 VALUES("1", "2", 3, "4");
+--source include/print_optional_metadata.inc
+
+RESET MASTER;
+# Primary key should not be printed
+SET GLOBAL binlog_row_metadata = MINIMAL;
+
+INSERT INTO t1 VALUES("2", "2", 3, "4");
+--source include/print_optional_metadata.inc
+
+RESET MASTER;
+--echo #
+--echo # Coverage test: Print column index instead of column name if column name
+--echo # is not binlogged.
+--echo #
+SET GLOBAL binlog_row_metadata = FULL;
+
+SET SESSION debug_dbug = 'd, dont_log_column_name';
+INSERT INTO t1 VALUES("3", "2", 3, "4");
+--source include/print_optional_metadata.inc
+
+--let $print_primary_key=
+DROP TABLE t1;
+RESET MASTER;
+
+--echo #
+--echo # Coverage test: Inject an invalid column type
+--echo #
+CREATE TABLE t1(c1 int, c2 BLOB);
+
+SET SESSION debug_dbug = 'd,inject_invalid_column_type';
+INSERT INTO t1 VALUES(1, "a");
+# It prints an error
+--source include/print_optional_metadata.inc
+
+RESET MASTER;
+
+--echo #
+--echo # Coverage test: Inject an invalid BLOB metadata
+--echo #
+--let $start_pos= query_get_value(SHOW MASTER STATUS, Position, 1)
+
+SET SESSION debug_dbug = 'd,inject_invalid_blob_size';
+INSERT INTO t1 VALUES(2, "b");
+
+# The invalid metadata will case assertion failure on Write_rows_log_event
+# So we need to stop mysqlbinlog before reading Write_rows_log_event.
+--let $stop_position= query_get_value(SHOW BINLOG EVENTS FROM $start_pos LIMIT 3, End_log_pos, 3)
+--source include/print_optional_metadata.inc
+
+--echo #
+--echo # Coverage test: Inject an invalid Geometry type
+--echo #
+DROP TABLE t1;
+CREATE TABLE t1(c_geometry GEOMETRY, c_point POINT, c_multilinestring MULTILINESTRING);
+RESET MASTER;
+--let $start_pos= query_get_value(SHOW MASTER STATUS, Position, 1)
+
+SET SESSION debug_dbug = 'd,inject_invalid_geometry_type';
+INSERT INTO t1(c_point) VALUES(ST_PointFromText('POINT(10 10)'));
+
+# The invalid metadata will case assertion failure on Write_rows_log_event
+# So we need to stop mysqlbinlog before reading Write_rows_log_event.
+--let $stop_position= query_get_value(SHOW BINLOG EVENTS FROM $start_pos LIMIT 3, End_log_pos, 3)
+--source include/print_optional_metadata.inc
+
+DROP TABLE t1;
+RESET MASTER;
+--echo #
+--echo # Comptibility Test: Verify mysqlbinlog can print OLD table_map_log_event
+--echo # without any optional metadata
+--echo #
+CREATE TABLE t1(c_int INT, c_tiny_int_unsigned TINYINT UNSIGNED,
+ c_binary BINARY(10), c_text TEXT, c_point POINT);
+
+SET session debug_dbug='d,simulate_no_optional_metadata';
+INSERT INTO t1(c_int) VALUES(1);
+# TINYINT will be printed without UNSIGNED flag,
+# CHAR will be printed as BINARY(10)
+# POINT will be printed as GEOMETRY
+--let $stop_position=
+--source include/print_optional_metadata.inc
+
+DROP TABLE t1;
+RESET MASTER;
+--echo #
+--echo # Simulate error on initializing charset and primary key metadata
+--echo #
+CREATE TABLE t1(c1 char(10) PRIMARY KEY);
+
+SET session debug_dbug='d,simulate_init_charset_field_error';
+INSERT INTO t1 VALUES("a");
+
+SET GLOBAL binlog_row_metadata = FULL;
+SET session debug_dbug='d,simulate_init_primary_key_field_error';
+INSERT INTO t1 VALUES("b");
+
+--let $print_primary_key= 1
+--source include/print_optional_metadata.inc
+
+SET SESSION debug_dbug = '';
+SET GLOBAL binlog_row_metadata = MINIMAL;
+DROP TABLE t1;
+RESET MASTER;
diff --git a/mysql-test/suite/sys_vars/r/binlog_row_metadata_basic.result b/mysql-test/suite/sys_vars/r/binlog_row_metadata_basic.result
new file mode 100644
index 00000000000..75971f2d2c6
--- /dev/null
+++ b/mysql-test/suite/sys_vars/r/binlog_row_metadata_basic.result
@@ -0,0 +1,85 @@
+SELECT @@GLOBAL.binlog_row_metadata;
+@@GLOBAL.binlog_row_metadata
+MINIMAL
+MINIMAL Expected
+SELECT @@SESSION.binlog_row_metadata;
+ERROR HY000: Variable 'binlog_row_metadata' is a GLOBAL variable
+'#---------------------BS_STVARS_002_01----------------------#'
+SET @start_value= @@global.binlog_row_metadata;
+SELECT COUNT(@@GLOBAL.binlog_row_metadata);
+COUNT(@@GLOBAL.binlog_row_metadata)
+1
+1 Expected
+'#---------------------BS_STVARS_002_02----------------------#'
+SET @@GLOBAL.binlog_row_metadata=0;
+SELECT @@GLOBAL.binlog_row_metadata;
+@@GLOBAL.binlog_row_metadata
+MINIMAL
+MINIMAL Expected
+SET @@GLOBAL.binlog_row_metadata=1;
+SELECT @@GLOBAL.binlog_row_metadata;
+@@GLOBAL.binlog_row_metadata
+FULL
+FULL Expected
+SET @@GLOBAL.binlog_row_metadata=MINIMAL;
+SELECT @@GLOBAL.binlog_row_metadata;
+@@GLOBAL.binlog_row_metadata
+MINIMAL
+MINIMAL Expected
+SET @@GLOBAL.binlog_row_metadata=FULL;
+SELECT @@GLOBAL.binlog_row_metadata;
+@@GLOBAL.binlog_row_metadata
+FULL
+FULL Expected
+SET @@GLOBAL.binlog_row_metadata='MINIMAL';
+SELECT @@GLOBAL.binlog_row_metadata;
+@@GLOBAL.binlog_row_metadata
+MINIMAL
+MINIMAL Expected
+SET @@GLOBAL.binlog_row_metadata='FULL';
+SELECT @@GLOBAL.binlog_row_metadata;
+@@GLOBAL.binlog_row_metadata
+FULL
+FULL Expected
+'#---------------------BS_STVARS_002_03----------------------#'
+SELECT @@GLOBAL.binlog_row_metadata = VARIABLE_VALUE
+FROM performance_schema.global_variables
+WHERE VARIABLE_NAME='binlog_row_metadata';
+@@GLOBAL.binlog_row_metadata = VARIABLE_VALUE
+1
+1 Expected
+SELECT COUNT(@@GLOBAL.binlog_row_metadata);
+COUNT(@@GLOBAL.binlog_row_metadata)
+1
+1 Expected
+SELECT COUNT(VARIABLE_VALUE)
+FROM performance_schema.global_variables
+WHERE VARIABLE_NAME='binlog_row_metadata';
+COUNT(VARIABLE_VALUE)
+1
+1 Expected
+'#---------------------BS_STVARS_002_04----------------------#'
+SELECT @@GLOBAL.binlog_row_metadata = VARIABLE_VALUE
+FROM performance_schema.session_variables
+WHERE VARIABLE_NAME LIKE 'binlog_row_metadata';
+@@GLOBAL.binlog_row_metadata = VARIABLE_VALUE
+1
+'#---------------------BS_STVARS_002_05----------------------#'
+SELECT COUNT(@@binlog_row_metadata);
+COUNT(@@binlog_row_metadata)
+1
+1 Expected
+SELECT COUNT(@@GLOBAL.binlog_row_metadata);
+COUNT(@@GLOBAL.binlog_row_metadata)
+1
+1 Expected
+'#---------------------BS_STVARS_002_06----------------------#'
+SET GLOBAL binlog_row_metadata = full1;
+ERROR 42000: Variable 'binlog_row_metadata' can't be set to the value of 'full1'
+SET GLOBAL binlog_row_metadata = "full1";
+ERROR 42000: Variable 'binlog_row_metadata' can't be set to the value of 'full1'
+SET GLOBAL binlog_row_metadata = 2;
+ERROR 42000: Variable 'binlog_row_metadata' can't be set to the value of '2'
+SET GLOBAL binlog_row_metadata = -1;
+ERROR 42000: Variable 'binlog_row_metadata' can't be set to the value of '-1'
+SET @@global.binlog_row_metadata= @start_value;
diff --git a/mysql-test/suite/sys_vars/t/binlog_row_metadata_basic.test b/mysql-test/suite/sys_vars/t/binlog_row_metadata_basic.test
new file mode 100644
index 00000000000..e66e8d4d31e
--- /dev/null
+++ b/mysql-test/suite/sys_vars/t/binlog_row_metadata_basic.test
@@ -0,0 +1,124 @@
+################## mysql-test\t\binlog_row_metadata_basic.test ################
+# #
+# Variable Name: binlog_row_metadata #
+# Scope: Global #
+# Data Type: enumeration #
+# #
+# Creation Date: 2017-01-23 #
+# Author : Libing Song #
+# #
+# #
+# Description:Test Cases of Dynamic System Variable binlog_row_metadata #
+# that checks the behavior of this variable in the following ways #
+# * Value Check #
+# * Scope Check #
+# #
+# Reference: #
+# http://dev.mysql.com/doc/refman/8.X/en/server-system-variables.html #
+# #
+###############################################################################
+
+
+SELECT @@GLOBAL.binlog_row_metadata;
+--echo MINIMAL Expected
+--error 1238
+SELECT @@SESSION.binlog_row_metadata;
+
+--echo '#---------------------BS_STVARS_002_01----------------------#'
+####################################################################
+# Displaying default value #
+####################################################################
+SET @start_value= @@global.binlog_row_metadata;
+
+SELECT COUNT(@@GLOBAL.binlog_row_metadata);
+--echo 1 Expected
+
+--echo '#---------------------BS_STVARS_002_02----------------------#'
+####################################################################
+# Check if Value can set #
+####################################################################
+SET @@GLOBAL.binlog_row_metadata=0;
+SELECT @@GLOBAL.binlog_row_metadata;
+--echo MINIMAL Expected
+
+SET @@GLOBAL.binlog_row_metadata=1;
+SELECT @@GLOBAL.binlog_row_metadata;
+--echo FULL Expected
+
+SET @@GLOBAL.binlog_row_metadata=MINIMAL;
+SELECT @@GLOBAL.binlog_row_metadata;
+--echo MINIMAL Expected
+
+SET @@GLOBAL.binlog_row_metadata=FULL;
+SELECT @@GLOBAL.binlog_row_metadata;
+--echo FULL Expected
+
+SET @@GLOBAL.binlog_row_metadata='MINIMAL';
+SELECT @@GLOBAL.binlog_row_metadata;
+--echo MINIMAL Expected
+
+SET @@GLOBAL.binlog_row_metadata='FULL';
+SELECT @@GLOBAL.binlog_row_metadata;
+--echo FULL Expected
+
+--echo '#---------------------BS_STVARS_002_03----------------------#'
+#################################################################
+# Check if the value in GLOBAL Table matches value in variable #
+#################################################################
+
+--disable_warnings
+SELECT @@GLOBAL.binlog_row_metadata = VARIABLE_VALUE
+FROM performance_schema.global_variables
+WHERE VARIABLE_NAME='binlog_row_metadata';
+--enable_warnings
+--echo 1 Expected
+
+SELECT COUNT(@@GLOBAL.binlog_row_metadata);
+--echo 1 Expected
+
+--disable_warnings
+SELECT COUNT(VARIABLE_VALUE)
+FROM performance_schema.global_variables
+WHERE VARIABLE_NAME='binlog_row_metadata';
+--enable_warnings
+--echo 1 Expected
+
+
+--echo '#---------------------BS_STVARS_002_04----------------------#'
+#################################################################
+# Check if the value in SESSION Table matches value in variable #
+#################################################################
+
+SELECT @@GLOBAL.binlog_row_metadata = VARIABLE_VALUE
+FROM performance_schema.session_variables
+WHERE VARIABLE_NAME LIKE 'binlog_row_metadata';
+
+--echo '#---------------------BS_STVARS_002_05----------------------#'
+################################################################################
+# Check if binlog_row_metadata can be accessed with and without @@ sign #
+################################################################################
+
+SELECT COUNT(@@binlog_row_metadata);
+--echo 1 Expected
+SELECT COUNT(@@GLOBAL.binlog_row_metadata);
+--echo 1 Expected
+
+--echo '#---------------------BS_STVARS_002_06----------------------#'
+################################################################################
+# Check if binlog_row_metadata can handle invalid values correctly #
+################################################################################
+--error 1231 # Cannot set the value
+SET GLOBAL binlog_row_metadata = full1;
+
+--error 1231
+SET GLOBAL binlog_row_metadata = "full1";
+
+--error 1231
+SET GLOBAL binlog_row_metadata = 2;
+
+--error 1231
+SET GLOBAL binlog_row_metadata = -1;
+
+SET @@global.binlog_row_metadata= @start_value;
+
+
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 351caa96446..6e5aa49f837 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -3428,7 +3428,8 @@ Table_map_log_event::Table_map_log_event(const char *buf, uint event_len,
m_colcnt(0), m_coltype(0),
m_memory(NULL), m_table_id(ULONGLONG_MAX), m_flags(0),
m_data_size(0), m_field_metadata(0), m_field_metadata_size(0),
- m_null_bits(0), m_meta_memory(NULL)
+ m_null_bits(0), m_meta_memory(NULL),
+ m_optional_metadata_len(0), m_optional_metadata(NULL)
{
unsigned int bytes_read= 0;
DBUG_ENTER("Table_map_log_event::Table_map_log_event(const char*,uint,...)");
@@ -3517,6 +3518,18 @@ Table_map_log_event::Table_map_log_event(const char *buf, uint event_len,
memcpy(m_field_metadata, ptr_after_colcnt, m_field_metadata_size);
ptr_after_colcnt= (uchar*)ptr_after_colcnt + m_field_metadata_size;
memcpy(m_null_bits, ptr_after_colcnt, num_null_bytes);
+ ptr_after_colcnt= (unsigned char*)ptr_after_colcnt + num_null_bytes;
+ }
+
+ bytes_read= (uint) (ptr_after_colcnt - (uchar *)buf);
+
+ /* After null_bits field, there are some new fields for extra metadata. */
+ if (bytes_read < event_len)
+ {
+ m_optional_metadata_len= event_len - bytes_read;
+ m_optional_metadata=
+ static_cast<unsigned char*>(my_malloc(m_optional_metadata_len, MYF(MY_WME)));
+ memcpy(m_optional_metadata, ptr_after_colcnt, m_optional_metadata_len);
}
}
@@ -3528,8 +3541,328 @@ Table_map_log_event::~Table_map_log_event()
{
my_free(m_meta_memory);
my_free(m_memory);
+ my_free(m_optional_metadata);
+ m_optional_metadata= NULL;
}
+/**
+ Parses SIGNEDNESS field.
+
+ @param[out] vec stores the signedness flags extracted from field.
+ @param[in] field SIGNEDNESS field in table_map_event.
+ @param[in] length length of the field
+ */
+static void parse_signedness(std::vector<bool> &vec,
+ unsigned char *field, unsigned int length)
+{
+ for (unsigned int i= 0; i < length; i++)
+ {
+ for (unsigned char c= 0x80; c != 0; c>>= 1)
+ vec.push_back(field[i] & c);
+ }
+ }
+
+/**
+ Parses DEFAULT_CHARSET field.
+
+ @param[out] default_charset stores collation numbers extracted from field.
+ @param[in] field DEFAULT_CHARSET field in table_map_event.
+ @param[in] length length of the field
+ */
+static void parse_default_charset(Table_map_log_event::Optional_metadata_fields::
+ Default_charset &default_charset,
+ unsigned char *field, unsigned int length)
+{
+ unsigned char* p= field;
+
+ default_charset.default_charset= net_field_length(&p);
+ while (p < field + length)
+ {
+ unsigned int col_index= net_field_length(&p);
+ unsigned int col_charset= net_field_length(&p);
+
+ default_charset.charset_pairs.push_back(std::make_pair(col_index,
+ col_charset));
+ }
+}
+
+/**
+ Parses COLUMN_CHARSET field.
+
+ @param[out] vec stores collation numbers extracted from field.
+ @param[in] field COLUMN_CHARSET field in table_map_event.
+ @param[in] length length of the field
+ */
+static void parse_column_charset(std::vector<unsigned int> &vec,
+ unsigned char *field, unsigned int length)
+{
+ unsigned char* p= field;
+
+ while (p < field + length)
+ vec.push_back(net_field_length(&p));
+}
+
+/**
+ Parses COLUMN_NAME field.
+
+ @param[out] vec stores column names extracted from field.
+ @param[in] field COLUMN_NAME field in table_map_event.
+ @param[in] length length of the field
+ */
+static void parse_column_name(std::vector<std::string> &vec,
+ unsigned char *field, unsigned int length)
+{
+ unsigned char* p= field;
+
+ while (p < field + length)
+ {
+ unsigned len= net_field_length(&p);
+ vec.push_back(std::string(reinterpret_cast<char *>(p), len));
+ p+= len;
+ }
+}
+
+/**
+ Parses SET_STR_VALUE/ENUM_STR_VALUE field.
+
+ @param[out] vec stores SET/ENUM column's string values extracted from
+ field. Each SET/ENUM column's string values are stored
+ into a string separate vector. All of them are stored
+ in 'vec'.
+ @param[in] field COLUMN_NAME field in table_map_event.
+ @param[in] length length of the field
+ */
+static void parse_set_str_value(std::vector<Table_map_log_event::
+ Optional_metadata_fields::str_vector> &vec,
+ unsigned char *field, unsigned int length)
+{
+ unsigned char* p= field;
+
+ while (p < field + length)
+ {
+ unsigned int count= net_field_length(&p);
+
+ vec.push_back(std::vector<std::string>());
+ for (unsigned int i= 0; i < count; i++)
+ {
+ unsigned len1= net_field_length(&p);
+ vec.back().push_back(std::string(reinterpret_cast<char *>(p), len1));
+ p+= len1;
+ }
+ }
+}
+
+/**
+ Parses GEOMETRY_TYPE field.
+
+ @param[out] vec stores geometry column's types extracted from field.
+ @param[in] field GEOMETRY_TYPE field in table_map_event.
+ @param[in] length length of the field
+ */
+static void parse_geometry_type(std::vector<unsigned int> &vec,
+ unsigned char *field, unsigned int length)
+{
+ unsigned char* p= field;
+
+ while (p < field + length)
+ vec.push_back(net_field_length(&p));
+}
+
+/**
+ Parses SIMPLE_PRIMARY_KEY field.
+
+ @param[out] vec stores primary key's column information extracted from
+ field. Each column has an index and a prefix which are
+ stored as a unit_pair. prefix is always 0 for
+ SIMPLE_PRIMARY_KEY field.
+ @param[in] field SIMPLE_PRIMARY_KEY field in table_map_event.
+ @param[in] length length of the field
+ */
+static void parse_simple_pk(std::vector<Table_map_log_event::
+ Optional_metadata_fields::uint_pair> &vec,
+ unsigned char *field, unsigned int length)
+{
+ unsigned char* p= field;
+
+ while (p < field + length)
+ vec.push_back(std::make_pair(net_field_length(&p), 0));
+}
+
+/**
+ Parses PRIMARY_KEY_WITH_PREFIX field.
+
+ @param[out] vec stores primary key's column information extracted from
+ field. Each column has an index and a prefix which are
+ stored as a unit_pair.
+ @param[in] field PRIMARY_KEY_WITH_PREFIX field in table_map_event.
+ @param[in] length length of the field
+ */
+
+static void parse_pk_with_prefix(std::vector<Table_map_log_event::
+ Optional_metadata_fields::uint_pair> &vec,
+ unsigned char *field, unsigned int length)
+{
+ unsigned char* p= field;
+
+ while (p < field + length)
+ {
+ unsigned int col_index= net_field_length(&p);
+ unsigned int col_prefix= net_field_length(&p);
+ vec.push_back(std::make_pair(col_index, col_prefix));
+ }
+}
+
+Table_map_log_event::Optional_metadata_fields::
+Optional_metadata_fields(unsigned char* optional_metadata,
+ unsigned int optional_metadata_len)
+{
+ unsigned char* field= optional_metadata;
+
+ if (optional_metadata == NULL)
+ return;
+
+ while (field < optional_metadata + optional_metadata_len)
+ {
+ unsigned int len;
+ Optional_metadata_field_type type=
+ static_cast<Optional_metadata_field_type>(field[0]);
+
+ // Get length and move field to the value.
+ field++;
+ len= net_field_length(&field);
+
+ switch(type)
+ {
+ case SIGNEDNESS:
+ parse_signedness(m_signedness, field, len);
+ break;
+ case DEFAULT_CHARSET:
+ parse_default_charset(m_default_charset, field, len);
+ break;
+ case COLUMN_CHARSET:
+ parse_column_charset(m_column_charset, field, len);
+ break;
+ case COLUMN_NAME:
+ parse_column_name(m_column_name, field, len);
+ break;
+ case SET_STR_VALUE:
+ parse_set_str_value(m_set_str_value, field, len);
+ break;
+ case ENUM_STR_VALUE:
+ parse_set_str_value(m_enum_str_value, field, len);
+ break;
+ case GEOMETRY_TYPE:
+ parse_geometry_type(m_geometry_type, field, len);
+ break;
+ case SIMPLE_PRIMARY_KEY:
+ parse_simple_pk(m_primary_key, field, len);
+ break;
+ case PRIMARY_KEY_WITH_PREFIX:
+ parse_pk_with_prefix(m_primary_key, field, len);
+ break;
+ case ENUM_AND_SET_DEFAULT_CHARSET:
+ parse_default_charset(m_enum_and_set_default_charset, field, len);
+ break;
+ case ENUM_AND_SET_COLUMN_CHARSET:
+ parse_column_charset(m_enum_and_set_column_charset, field, len);
+ break;
+ default:
+ DBUG_ASSERT(0);
+ }
+ // next field
+ field+= len;
+ }
+}
+#ifndef MYSQL_SERVER
+/**
+ Interface for iterator over charset columns.
+*/
+class Table_map_log_event::Charset_iterator
+{
+ public:
+ typedef Table_map_log_event::Optional_metadata_fields::Default_charset
+ Default_charset;
+ virtual const CHARSET_INFO *next()= 0;
+ virtual ~Charset_iterator(){};
+ /**
+ Factory method to create an instance of the appropriate subclass.
+ */
+ static std::unique_ptr<Charset_iterator> create_charset_iterator(
+ const Default_charset &default_charset,
+ const std::vector<uint> &column_charset);
+};
+
+/**
+ Implementation of charset iterator for the DEFAULT_CHARSET type.
+*/
+class Table_map_log_event::Default_charset_iterator : public Charset_iterator
+{
+ public:
+ Default_charset_iterator(const Default_charset &default_charset)
+ : m_iterator(default_charset.charset_pairs.begin()),
+ m_end(default_charset.charset_pairs.end()),
+ m_column_index(0),
+ m_default_charset_info(
+ get_charset(default_charset.default_charset, 0)) {}
+
+ const CHARSET_INFO *next() override {
+ const CHARSET_INFO *ret;
+ if (m_iterator != m_end && m_iterator->first == m_column_index) {
+ ret = get_charset(m_iterator->second, 0);
+ m_iterator++;
+ } else
+ ret = m_default_charset_info;
+ m_column_index++;
+ return ret;
+ }
+ ~Default_charset_iterator(){};
+
+ private:
+ std::vector<Optional_metadata_fields::uint_pair>::const_iterator m_iterator,
+ m_end;
+ uint m_column_index;
+ const CHARSET_INFO *m_default_charset_info;
+};
+//Table_map_log_event::Default_charset_iterator::~Default_charset_iterator(){int a=8;a++; a--;};
+/**
+ Implementation of charset iterator for the COLUMNT_CHARSET type.
+*/
+class Table_map_log_event::Column_charset_iterator : public Charset_iterator
+{
+ public:
+ Column_charset_iterator(const std::vector<uint> &column_charset)
+ : m_iterator(column_charset.begin()), m_end(column_charset.end()) {}
+
+ const CHARSET_INFO *next() override {
+ const CHARSET_INFO *ret = nullptr;
+ if (m_iterator != m_end) {
+ ret = get_charset(*m_iterator, 0);
+ m_iterator++;
+ }
+ return ret;
+ }
+
+ ~Column_charset_iterator(){};
+ private:
+ std::vector<uint>::const_iterator m_iterator;
+ std::vector<uint>::const_iterator m_end;
+};
+//Table_map_log_event::Column_charset_iterator::~Column_charset_iterator(){int a=8;a++; a--;};
+
+std::unique_ptr<Table_map_log_event::Charset_iterator>
+Table_map_log_event::Charset_iterator::create_charset_iterator(
+ const Default_charset &default_charset,
+ const std::vector<uint> &column_charset)
+{
+ if (!default_charset.empty())
+ return std::unique_ptr<Charset_iterator>(
+ new Default_charset_iterator(default_charset));
+ else
+ return std::unique_ptr<Charset_iterator>(
+ new Column_charset_iterator(column_charset));
+}
+#endif
+
/**************************************************************************
Write_rows_log_event member functions
diff --git a/sql/log_event.h b/sql/log_event.h
index 274182af841..dcfcf8a5622 100644
--- a/sql/log_event.h
+++ b/sql/log_event.h
@@ -35,6 +35,10 @@
#include <my_bitmap.h>
#include "rpl_constants.h"
+#include <vector>
+#include <string>
+#include <functional>
+#include <memory>
#ifdef MYSQL_CLIENT
#include "sql_const.h"
@@ -791,6 +795,43 @@ enum Int_event_type
INVALID_INT_EVENT = 0, LAST_INSERT_ID_EVENT = 1, INSERT_ID_EVENT = 2
};
+static inline bool is_numeric_type(uint type)
+{
+ switch (type)
+ {
+ case MYSQL_TYPE_TINY:
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_INT24:
+ case MYSQL_TYPE_LONG:
+ case MYSQL_TYPE_LONGLONG:
+ case MYSQL_TYPE_NEWDECIMAL:
+ case MYSQL_TYPE_FLOAT:
+ case MYSQL_TYPE_DOUBLE:
+ return true;
+ default:
+ return false;
+ }
+ return false;
+}
+
+static inline bool is_character_type(uint type)
+{
+ switch (type)
+ {
+ case MYSQL_TYPE_STRING:
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_VARCHAR:
+ case MYSQL_TYPE_BLOB:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static inline bool is_enum_or_set_type(uint type) {
+ return type == MYSQL_TYPE_ENUM || type == MYSQL_TYPE_SET;
+}
+
#ifdef MYSQL_SERVER
class String;
@@ -881,6 +922,7 @@ typedef struct st_print_event_info
statement for it.
*/
bool skip_replication;
+ bool print_table_metadata;
/*
These two caches are used by the row-based replication events to
@@ -4063,6 +4105,18 @@ class Annotate_rows_log_event: public Log_event
ninth is in the least significant bit of the second byte, and so
on. </td>
</tr>
+ <tr>
+ <td>optional metadata fields</td>
+ <td>optional metadata fields are stored in Type, Length, Value(TLV) format.
+ Type takes 1 byte. Length is a packed integer value. Values takes
+ Length bytes.
+ </td>
+ <td>There are some optional metadata defined. They are listed in the table
+ @ref Table_table_map_event_optional_metadata. Optional metadata fields
+ follow null_bits. Whether binlogging an optional metadata is decided by the
+ server. The order is not defined, so they can be binlogged in any order.
+ </td>
+ </tr>
</table>
@@ -4268,6 +4322,123 @@ class Annotate_rows_log_event: public Log_event
</tr>
</table>
+ The table below lists all optional metadata types, along with the numerical
+ identifier for it and the size and interpretation of meta-data used
+ to describe the type.
+
+ @anchor Table_table_map_event_optional_metadata
+ <table>
+ <caption>Table_map_event optional metadata types: numerical identifier and
+ metadata. Optional metadata fields are stored in TLV fields.
+ Format of values are described in this table. </caption>
+ <tr>
+ <th>Type</th>
+ <th>Description</th>
+ <th>Format</th>
+ </tr>
+ <tr>
+ <td>SIGNEDNESS</td>
+ <td>signedness of numeric colums. This is included for all values of
+ binlog_row_metadata.</td>
+ <td>For each numeric column, a bit indicates whether the numeric
+ colunm has unsigned flag. 1 means it is unsigned. The number of
+ bytes needed for this is int((column_count + 7) / 8). The order is
+ the same as the order of column_type field.</td>
+ </tr>
+ <tr>
+ <td>DEFAULT_CHARSET</td>
+ <td>Charsets of character columns. It has a default charset for
+ the case that most of character columns have same charset and the
+ most used charset is binlogged as default charset.Collation
+ numbers are binlogged for identifying charsets. They are stored in
+ packed length format. Either DEFAULT_CHARSET or COLUMN_CHARSET is
+ included for all values of binlog_row_metadata.</td>
+ <td>Default charset's collation is logged first. The charsets which are not
+ same to default charset are logged following default charset. They are
+ logged as column index and charset collation number pair sequence. The
+ column index is counted only in all character columns. The order is same to
+ the order of column_type
+ field. </td>
+ </tr>
+ <tr>
+ <td>COLUMN_CHARSET</td>
+ <td>Charsets of character columns. For the case that most of columns have
+ different charsets, this field is logged. It is never logged with
+ DEFAULT_CHARSET together. Either DEFAULT_CHARSET or COLUMN_CHARSET is
+ included for all values of binlog_row_metadata.</td>
+ <td>It is a collation number sequence for all character columns.</td>
+ </tr>
+ <tr>
+ <td>COLUMN_NAME</td>
+ <td>Names of columns. This is only included if
+ binlog_row_metadata=FULL.</td>
+ <td>A sequence of column names. For each column name, 1 byte for
+ the string length in bytes is followed by a string without null
+ terminator.</td>
+ </tr>
+ <tr>
+ <td>SET_STR_VALUE</td>
+ <td>The string values of SET columns. This is only included if
+ binlog_row_metadata=FULL.</td>
+ <td>For each SET column, a pack_length representing the value
+ count is followed by a sequence of length and string pairs. length
+ is the byte count in pack_length format. The string has no null
+ terminator.</td>
+ </tr>
+ <tr>
+ <td>ENUM_STR_VALUE</td>
+ <td>The string values is ENUM columns. This is only included
+ if binlog_row_metadata=FULL.</td>
+ <td>The format is the same as SET_STR_VALUE.</td>
+ </tr>
+ <tr>
+ <td>GEOMETRY_TYPE</td>
+ <td>The real type of geometry columns. This is only included
+ if binlog_row_metadata=FULL.</td>
+ <td>A sequence of real type of geometry columns are stored in pack_length
+ format. </td>
+ </tr>
+ <tr>
+ <td>SIMPLE_PRIMARY_KEY</td>
+ <td>The primary key without any prefix. This is only included
+ if binlog_row_metadata=FULL and there is a primary key where every
+ key part covers an entire column.</td>
+ <td>A sequence of column indexes. The indexes are stored in pack_length
+ format.</td>
+ </tr>
+ <tr>
+ <td>PRIMARY_KEY_WITH_PREFIX</td>
+ <td>The primary key with some prefix. It doesn't appear together with
+ SIMPLE_PRIMARY_KEY. This is only included if
+ binlog_row_metadata=FULL and there is a primary key where some key
+ part covers a prefix of the column.</td>
+ <td>A sequence of column index and prefix length pairs. Both
+ column index and prefix length are in pack_length format. Prefix length
+ 0 means that the whole column value is used.</td>
+ </tr>
+ <tr>
+ <td>ENUM_AND_SET_DEFAULT_CHARSET</td>
+ <td>Charsets of ENUM and SET columns. It has the same layout as
+ DEFAULT_CHARSET. If there are SET or ENUM columns and
+ binlog_row_metadata=FULL, exactly one of
+ ENUM_AND_SET_DEFAULT_CHARSET and ENUM_AND_SET_COLUMN_CHARSET
+ appears (the encoder chooses the representation that uses the
+ least amount of space). Otherwise, none of them appears.</td>
+ <td>The same format as for DEFAULT_CHARSET, except it counts ENUM
+ and SET columns rather than character columns.</td>
+ </tr>
+ <tr>
+ <td>ENUM_AND_SET_COLUMN_CHARSET</td>
+ <td>Charsets of ENUM and SET columns. It has the same layout as
+ COLUMN_CHARSET. If there are SET or ENUM columns and
+ binlog_row_metadata=FULL, exactly one of
+ ENUM_AND_SET_DEFAULT_CHARSET and ENUM_AND_SET_COLUMN_CHARSET
+ appears (the encoder chooses the representation that uses the
+ least amount of space). Otherwise, none of them appears.</td>
+ <td>The same format as for COLUMN_CHARSET, except it counts ENUM
+ and SET columns rather than character columns.</td>
+ </tr>
+ </table>
*/
class Table_map_log_event : public Log_event
{
@@ -4303,6 +4474,117 @@ class Table_map_log_event : public Log_event
};
typedef uint16 flag_set;
+ /**
+ DEFAULT_CHARSET and COLUMN_CHARSET don't appear together, and
+ ENUM_AND_SET_DEFAULT_CHARSET and ENUM_AND_SET_COLUMN_CHARSET don't
+ appear together. They are just alternative ways to pack character
+ set information. When binlogging, it logs character sets in the
+ way that occupies least storage.
+
+ SIMPLE_PRIMARY_KEY and PRIMARY_KEY_WITH_PREFIX don't appear together.
+ SIMPLE_PRIMARY_KEY is for the primary keys which only use whole values of
+ pk columns. PRIMARY_KEY_WITH_PREFIX is
+ for the primary keys which just use part value of pk columns.
+ */
+ enum Optional_metadata_field_type
+ {
+ SIGNEDNESS = 1, // UNSIGNED flag of numeric columns
+ DEFAULT_CHARSET, /* Character set of string columns, optimized to
+ minimize space when many columns have the
+ same charset. */
+ COLUMN_CHARSET, /* Character set of string columns, optimized to
+ minimize space when columns have many
+ different charsets. */
+ COLUMN_NAME,
+ SET_STR_VALUE, // String value of SET columns
+ ENUM_STR_VALUE, // String value of ENUM columns
+ GEOMETRY_TYPE, // Real type of geometry columns
+ SIMPLE_PRIMARY_KEY, // Primary key without prefix
+ PRIMARY_KEY_WITH_PREFIX, // Primary key with prefix
+ ENUM_AND_SET_DEFAULT_CHARSET, /* Character set of enum and set
+ columns, optimized to minimize
+ space when many columns have the
+ same charset. */
+ ENUM_AND_SET_COLUMN_CHARSET, /* Character set of enum and set
+ columns, optimized to minimize
+ space when many columns have the
+ same charset. */
+ };
+ /**
+ Metadata_fields organizes m_optional_metadata into a structured format which
+ is easy to access.
+ */
+ struct Optional_metadata_fields
+ {
+ typedef std::pair<unsigned int, unsigned int> uint_pair;
+ typedef std::vector<std::string> str_vector;
+
+ struct Default_charset
+ {
+ Default_charset() : default_charset(0) {}
+ bool empty() const { return default_charset == 0; }
+
+ // Default charset for the columns which are not in charset_pairs.
+ unsigned int default_charset;
+
+ /* The uint_pair means <column index, column charset number>. */
+ std::vector<uint_pair> charset_pairs;
+ };
+
+ // Contents of DEFAULT_CHARSET field is converted into Default_charset.
+ Default_charset m_default_charset;
+ // Contents of ENUM_AND_SET_DEFAULT_CHARSET are converted into
+ // Default_charset.
+ Default_charset m_enum_and_set_default_charset;
+ std::vector<bool> m_signedness;
+ // Character set number of every string column
+ std::vector<unsigned int> m_column_charset;
+ // Character set number of every ENUM or SET column.
+ std::vector<unsigned int> m_enum_and_set_column_charset;
+ std::vector<std::string> m_column_name;
+ // each str_vector stores values of one enum/set column
+ std::vector<str_vector> m_enum_str_value;
+ std::vector<str_vector> m_set_str_value;
+ std::vector<unsigned int> m_geometry_type;
+ /*
+ The uint_pair means <column index, prefix length>. Prefix length is 0 if
+ whole column value is used.
+ */
+ std::vector<uint_pair> m_primary_key;
+
+ /*
+ It parses m_optional_metadata and populates into above variables.
+
+ @param[in] optional_metadata points to the begin of optional metadata
+ fields in table_map_event.
+ @param[in] optional_metadata_len length of optional_metadata field.
+ */
+ Optional_metadata_fields(unsigned char* optional_metadata,
+ unsigned int optional_metadata_len);
+ };
+
+ /**
+ Print column metadata. Its format looks like:
+ # Columns(colume_name type, colume_name type, ...)
+ if colume_name field is not logged into table_map_log_event, then
+ only type is printed.
+
+ @@param[out] file the place where colume metadata is printed
+ @@param[in] The metadata extracted from optional metadata fields
+ */
+ void print_columns(IO_CACHE *file,
+ const Optional_metadata_fields &fields);
+ /**
+ Print primary information. Its format looks like:
+ # Primary Key(colume_name, column_name(prifix), ...)
+ if colume_name field is not logged into table_map_log_event, then
+ colume index is printed.
+
+ @@param[out] file the place where primary key is printed
+ @@param[in] The metadata extracted from optional metadata fields
+ */
+ void print_primary_key(IO_CACHE *file,
+ const Optional_metadata_fields &fields);
/* Special constants representing sets of flags */
enum
@@ -4369,6 +4651,49 @@ class Table_map_log_event : public Log_event
#ifdef MYSQL_SERVER
TABLE *m_table;
+
+ // Metadata fields buffer
+ StringBuffer<1024> m_metadata_buf;
+
+ /**
+ Capture the optional metadata fields which should be logged into
+ table_map_log_event and serialize them into m_metadata_buf.
+ */
+ void init_metadata_fields();
+ bool init_signedness_field();
+ /**
+ Capture and serialize character sets. Character sets for
+ character columns (TEXT etc) and character sets for ENUM and SET
+ columns are stored in different metadata fields. The reason is
+ that TEXT character sets are included even when
+ binlog_row_metadata=MINIMAL, whereas ENUM and SET character sets
+ are included only when binlog_row_metadata=FULL.
+
+ @param include_type Predicate to determine if a given Field object
+ is to be included in the metadata field.
+
+ @param default_charset_type Type code when storing in "default
+ charset" format. (See comment above Table_maps_log_event in
+ libbinlogevents/include/rows_event.h)
+
+ @param column_charset_type Type code when storing in "column
+ charset" format. (See comment above Table_maps_log_event in
+ libbinlogevents/include/rows_event.h)
+ */
+ bool init_charset_field(std::function<bool(const Field *)> include_type,
+ Optional_metadata_field_type default_charset_type,
+ Optional_metadata_field_type column_charset_type);
+ bool init_column_name_field();
+ bool init_set_str_value_field();
+ bool init_enum_str_value_field();
+ bool init_geometry_type_field();
+ bool init_primary_key_field();
+#endif
+
+#ifndef MYSQL_SERVER
+ class Charset_iterator;
+ class Default_charset_iterator;
+ class Column_charset_iterator;
#endif
char const *m_dbnam;
size_t m_dblen;
@@ -4390,6 +4715,8 @@ class Table_map_log_event : public Log_event
ulong m_field_metadata_size;
uchar *m_null_bits;
uchar *m_meta_memory;
+ unsigned int m_optional_metadata_len;
+ unsigned char *m_optional_metadata;
};
diff --git a/sql/log_event_client.cc b/sql/log_event_client.cc
index 57b217813e2..86c96f8274c 100644
--- a/sql/log_event_client.cc
+++ b/sql/log_event_client.cc
@@ -26,10 +26,11 @@
#endif
-static bool pretty_print_str(IO_CACHE* cache, const char* str, int len)
+static bool pretty_print_str(IO_CACHE* cache, const char* str,
+ size_t len, bool identifier)
{
const char* end = str + len;
- if (my_b_write_byte(cache, '\''))
+ if (my_b_write_byte(cache, identifier ? '`' : '\''))
goto err;
while (str < end)
@@ -52,12 +53,39 @@ static bool pretty_print_str(IO_CACHE* cache, const char* str, int len)
if (unlikely(error))
goto err;
}
- return my_b_write_byte(cache, '\'');
+ return my_b_write_byte(cache, identifier ? '`' : '\'');
err:
return 1;
}
+/**
+ Print src as an string enclosed with "'"
+
+ @param[out] cache IO_CACHE where the string will be printed.
+ @param[in] str the string will be printed.
+ @param[in] len length of the string.
+*/
+static inline bool pretty_print_str(IO_CACHE* cache, const char* str,
+ size_t len)
+{
+ return pretty_print_str(cache, str, len, false);
+}
+
+/**
+ Print src as an identifier enclosed with "`"
+
+ @param[out] cache IO_CACHE where the identifier will be printed.
+ @param[in] str the string will be printed.
+ @param[in] len length of the string.
+ */
+static inline bool pretty_print_identifier(IO_CACHE* cache, const char* str,
+ size_t len)
+{
+ return pretty_print_str(cache, str, len, true);
+}
+
+
/**
Prints a "session_var=value" string. Used by mysqlbinlog to print some SET
@@ -3110,6 +3138,15 @@ bool Table_map_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info)
}
if (!print_event_info->short_form || print_event_info->print_row_count)
{
+
+ if (print_event_info->print_table_metadata)
+ {
+ Optional_metadata_fields fields(m_optional_metadata,
+ m_optional_metadata_len);
+
+ print_columns(&print_event_info->head_cache, fields);
+ print_primary_key(&print_event_info->head_cache, fields);
+ }
bool do_print_encoded=
print_event_info->base64_output_mode != BASE64_OUTPUT_NEVER &&
print_event_info->base64_output_mode != BASE64_OUTPUT_DECODE_ROWS &&
@@ -3127,6 +3164,318 @@ bool Table_map_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info)
return 1;
}
+/**
+ return the string name of a type.
+
+ @param[in] type type of a column
+ @param[in|out] meta_ptr the meta_ptr of the column. If the type doesn't have
+ metadata, it will not change meta_ptr, otherwise
+ meta_ptr will be moved to the end of the column's
+ metadat.
+ @param[in] cs charset of the column if it is a character column.
+ @param[out] typestr buffer to storing the string name of the type
+ @param[in] typestr_length length of typestr
+ @param[in] geometry_type internal geometry_type
+ */
+static void get_type_name(uint type, unsigned char** meta_ptr,
+ const CHARSET_INFO *cs, char *typestr,
+ uint typestr_length, unsigned int geometry_type)
+{
+ switch (type) {
+ case MYSQL_TYPE_LONG:
+ my_snprintf(typestr, typestr_length, "%s", "INT");
+ break;
+ case MYSQL_TYPE_TINY:
+ my_snprintf(typestr, typestr_length, "TINYINT");
+ break;
+ case MYSQL_TYPE_SHORT:
+ my_snprintf(typestr, typestr_length, "SMALLINT");
+ break;
+ case MYSQL_TYPE_INT24:
+ my_snprintf(typestr, typestr_length, "MEDIUMINT");
+ break;
+ case MYSQL_TYPE_LONGLONG:
+ my_snprintf(typestr, typestr_length, "BIGINT");
+ break;
+ case MYSQL_TYPE_NEWDECIMAL:
+ my_snprintf(typestr, typestr_length, "DECIMAL(%d,%d)",
+ (*meta_ptr)[0], (*meta_ptr)[1]);
+ (*meta_ptr)+= 2;
+ break;
+ case MYSQL_TYPE_FLOAT:
+ my_snprintf(typestr, typestr_length, "FLOAT");
+ (*meta_ptr)++;
+ break;
+ case MYSQL_TYPE_DOUBLE:
+ my_snprintf(typestr, typestr_length, "DOUBLE");
+ (*meta_ptr)++;
+ break;
+ case MYSQL_TYPE_BIT:
+ my_snprintf(typestr, typestr_length, "BIT(%d)",
+ (((*meta_ptr)[0])) + (*meta_ptr)[1]*8);
+ (*meta_ptr)+= 2;
+ break;
+ case MYSQL_TYPE_TIMESTAMP2:
+ if (**meta_ptr != 0)
+ my_snprintf(typestr, typestr_length, "TIMESTAMP(%d)", **meta_ptr);
+ else
+ my_snprintf(typestr, typestr_length, "TIMESTAMP");
+ (*meta_ptr)++;
+ break;
+ case MYSQL_TYPE_DATETIME2:
+ if (**meta_ptr != 0)
+ my_snprintf(typestr, typestr_length, "DATETIME(%d)", **meta_ptr);
+ else
+ my_snprintf(typestr, typestr_length, "DATETIME");
+ (*meta_ptr)++;
+ break;
+ case MYSQL_TYPE_TIME2:
+ if (**meta_ptr != 0)
+ my_snprintf(typestr, typestr_length, "TIME(%d)", **meta_ptr);
+ else
+ my_snprintf(typestr, typestr_length, "TIME");
+ (*meta_ptr)++;
+ break;
+ case MYSQL_TYPE_NEWDATE:
+ case MYSQL_TYPE_DATE:
+ my_snprintf(typestr, typestr_length, "DATE");
+ break;
+ case MYSQL_TYPE_YEAR:
+ my_snprintf(typestr, typestr_length, "YEAR");
+ break;
+ case MYSQL_TYPE_ENUM:
+ my_snprintf(typestr, typestr_length, "ENUM");
+ (*meta_ptr)+= 2;
+ break;
+ case MYSQL_TYPE_SET:
+ my_snprintf(typestr, typestr_length, "SET");
+ (*meta_ptr)+= 2;
+ break;
+ case MYSQL_TYPE_BLOB:
+ {
+ bool is_text= (cs && cs->number != my_charset_bin.number);
+ const char *names[5][2] = {
+ {"INVALID_BLOB(%d)", "INVALID_TEXT(%d)"},
+ {"TINYBLOB", "TINYTEXT"},
+ {"BLOB", "TEXT"},
+ {"MEDIUMBLOB", "MEDIUMTEXT"},
+ {"LONGBLOB", "LONGTEXT"}
+ };
+ unsigned char size= **meta_ptr;
+
+ if (size == 0 || size > 4)
+ my_snprintf(typestr, typestr_length, names[0][is_text], size);
+ else
+ my_snprintf(typestr, typestr_length, names[**meta_ptr][is_text]);
+
+ (*meta_ptr)++;
+ }
+ break;
+ case MYSQL_TYPE_VARCHAR:
+ case MYSQL_TYPE_VAR_STRING:
+ if (cs && cs->number != my_charset_bin.number)
+ my_snprintf(typestr, typestr_length, "VARCHAR(%d)",
+ uint2korr(*meta_ptr)/cs->mbmaxlen);
+ else
+ my_snprintf(typestr, typestr_length, "VARBINARY(%d)",
+ uint2korr(*meta_ptr));
+
+ (*meta_ptr)+= 2;
+ break;
+ case MYSQL_TYPE_STRING:
+ {
+ uint byte0= (*meta_ptr)[0];
+ uint byte1= (*meta_ptr)[1];
+ uint len= (((byte0 & 0x30) ^ 0x30) << 4) | byte1;
+
+ if (cs && cs->number != my_charset_bin.number)
+ my_snprintf(typestr, typestr_length, "CHAR(%d)", len/cs->mbmaxlen);
+ else
+ my_snprintf(typestr, typestr_length, "BINARY(%d)", len);
+
+ (*meta_ptr)+= 2;
+ }
+ break;
+ case MYSQL_TYPE_JSON:
+ my_snprintf(typestr, typestr_length, "JSON");
+ (*meta_ptr)++;
+ break;
+ case MYSQL_TYPE_GEOMETRY:
+ {
+ const char* names[8] = {
+ "GEOMETRY", "POINT", "LINESTRING", "POLYGON", "MULTIPOINT",
+ "MULTILINESTRING", "MULTIPOLYGON", "GEOMETRYCOLLECTION"
+ };
+ if (geometry_type < 8)
+ my_snprintf(typestr, typestr_length, names[geometry_type]);
+ else
+ my_snprintf(typestr, typestr_length, "INVALID_GEOMETRY_TYPE(%u)",
+ geometry_type);
+ (*meta_ptr)++;
+ }
+ break;
+ default:
+ *typestr= 0;
+ break;
+ }
+}
+
+void Table_map_log_event::print_columns(IO_CACHE *file,
+ const Optional_metadata_fields &fields)
+{
+ unsigned char* field_metadata_ptr= m_field_metadata;
+ std::vector<bool>::const_iterator signedness_it= fields.m_signedness.begin();
+
+ std::unique_ptr<Charset_iterator> charset_it =
+ Charset_iterator::create_charset_iterator(fields.m_default_charset,
+ fields.m_column_charset);
+ std::unique_ptr<Charset_iterator> enum_and_set_charset_it =
+ Charset_iterator::create_charset_iterator(
+ fields.m_enum_and_set_default_charset,
+ fields.m_enum_and_set_column_charset);
+ std::vector<std::string>::const_iterator col_names_it=
+ fields.m_column_name.begin();
+ std::vector<Optional_metadata_fields::str_vector>::const_iterator
+ set_str_values_it= fields.m_set_str_value.begin();
+ std::vector<Optional_metadata_fields::str_vector>::const_iterator
+ enum_str_values_it= fields.m_enum_str_value.begin();
+ std::vector<unsigned int>::const_iterator geometry_type_it=
+ fields.m_geometry_type.begin();
+
+ uint geometry_type= 0;
+
+ my_b_printf(file, "# Columns(");
+
+ for (unsigned long i= 0; i < m_colcnt; i++)
+ {
+ uint real_type = m_coltype[i];
+ if (real_type == MYSQL_TYPE_STRING &&
+ (*field_metadata_ptr == MYSQL_TYPE_ENUM ||
+ *field_metadata_ptr == MYSQL_TYPE_SET))
+ real_type= *field_metadata_ptr;
+
+ // Get current column's collation id if it is a character, enum,
+ // or set column
+ const CHARSET_INFO *cs = NULL;
+ if (is_character_type(real_type))
+ cs = charset_it->next();
+ else if (is_enum_or_set_type(real_type))
+ cs = enum_and_set_charset_it->next();
+
+ // Print column name
+ if (col_names_it != fields.m_column_name.end())
+ {
+ pretty_print_identifier(file, col_names_it->c_str(), col_names_it->size());
+ my_b_printf(file, " ");
+ col_names_it++;
+ }
+
+
+ // update geometry_type for geometry columns
+ if (real_type == MYSQL_TYPE_GEOMETRY)
+ {
+ geometry_type= (geometry_type_it != fields.m_geometry_type.end()) ?
+ *geometry_type_it++ : 0;
+ }
+
+ // print column type
+ const uint TYPE_NAME_LEN = 100;
+ char type_name[TYPE_NAME_LEN];
+ get_type_name(real_type, &field_metadata_ptr, cs, type_name,
+ TYPE_NAME_LEN, geometry_type);
+
+ if (type_name[0] == '\0')
+ {
+ my_b_printf(file, "INVALID_TYPE(%d)", real_type);
+ continue;
+ }
+ my_b_printf(file, "%s", type_name);
+
+ // Print UNSIGNED for numeric column
+ if (is_numeric_type(real_type) &&
+ signedness_it != fields.m_signedness.end())
+ {
+ if (*signedness_it == true)
+ my_b_printf(file, " UNSIGNED");
+ signedness_it++;
+ }
+
+ // if the column is not marked as 'null', print 'not null'
+ if (!(m_null_bits[(i / 8)] & (1 << (i % 8))))
+ my_b_printf(file, " NOT NULL");
+
+ // Print string values of SET and ENUM column
+ const Optional_metadata_fields::str_vector *str_values= NULL;
+ if (real_type == MYSQL_TYPE_ENUM &&
+ enum_str_values_it != fields.m_enum_str_value.end())
+ {
+ str_values= &(*enum_str_values_it);
+ enum_str_values_it++;
+ }
+ else if (real_type == MYSQL_TYPE_SET &&
+ set_str_values_it != fields.m_set_str_value.end())
+ {
+ str_values= &(*set_str_values_it);
+ set_str_values_it++;
+ }
+
+ if (str_values != NULL)
+ {
+ const char *separator= "(";
+ for (Optional_metadata_fields::str_vector::const_iterator it=
+ str_values->begin(); it != str_values->end(); it++)
+ {
+ my_b_printf(file, "%s", separator);
+ pretty_print_str(file, it->c_str(), it->size());
+ separator= ",";
+ }
+ my_b_printf(file, ")");
+ }
+ // Print column character set, except in text columns with binary collation
+ if (cs != NULL &&
+ (is_enum_or_set_type(real_type) || cs->number != my_charset_bin.number))
+ my_b_printf(file, " CHARSET %s COLLATE %s", cs->csname, cs->name);
+
+ // Print column character set, except in text columns with binary collation
+ if (cs != NULL &&
+ (is_enum_or_set_type(real_type) || cs->number != my_charset_bin.number))
+ my_b_printf(file, " CHARSET %s COLLATE %s", cs->csname, cs->name);
+ if (i != m_colcnt - 1) my_b_printf(file, ",\n# ");
+ }
+ my_b_printf(file, ")");
+ my_b_printf(file, "\n");
+}
+
+void Table_map_log_event::print_primary_key
+ (IO_CACHE *file,const Optional_metadata_fields &fields)
+{
+ if (!fields.m_primary_key.empty())
+ {
+ my_b_printf(file, "# Primary Key(");
+
+ std::vector<Optional_metadata_fields::uint_pair>::const_iterator it=
+ fields.m_primary_key.begin();
+
+ for (; it != fields.m_primary_key.end(); it++)
+ {
+ if (it != fields.m_primary_key.begin())
+ my_b_printf(file, ", ");
+
+ // Print column name or column index
+ if (it->first >= fields.m_column_name.size())
+ my_b_printf(file, "%u", it->first);
+ else
+ my_b_printf(file, "%s", fields.m_column_name[it->first].c_str());
+
+ // Print prefix length
+ if (it->second != 0)
+ my_b_printf(file, "(%u)", it->second);
+ }
+
+ my_b_printf(file, ")\n");
+ }
+}
+
bool Write_rows_log_event::print(FILE *file, PRINT_EVENT_INFO* print_event_info)
{
diff --git a/sql/log_event_server.cc b/sql/log_event_server.cc
index 70b1c1538e9..1dd70ba7fea 100644
--- a/sql/log_event_server.cc
+++ b/sql/log_event_server.cc
@@ -5915,6 +5915,11 @@ int Table_map_log_event::save_field_metadata()
{
DBUG_PRINT("debug", ("field_type: %d", m_coltype[i]));
index+= m_table->s->field[i]->save_field_metadata(&m_field_metadata[index]);
+ DBUG_EXECUTE_IF("inject_invalid_blob_size",
+ {
+ if (m_coltype[i] == MYSQL_TYPE_BLOB)
+ m_field_metadata[index-1] = 5;
+ });
}
DBUG_RETURN(index);
}
@@ -5941,7 +5946,9 @@ Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulong tid,
m_field_metadata(0),
m_field_metadata_size(0),
m_null_bits(0),
- m_meta_memory(NULL)
+ m_meta_memory(NULL),
+ m_optional_metadata_len(0),
+ m_optional_metadata(NULL)
{
uchar cbuf[MAX_INT_WIDTH];
uchar *cbuf_end;
@@ -5975,6 +5982,7 @@ Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulong tid,
m_coltype= reinterpret_cast<uchar*>(m_memory);
for (unsigned int i= 0 ; i < m_table->s->fields ; ++i)
m_coltype[i]= m_table->field[i]->binlog_type();
+ DBUG_EXECUTE_IF("inject_invalid_column_type", m_coltype[1]= 230;);
}
/*
@@ -6013,6 +6021,9 @@ Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulong tid,
if (m_table->field[i]->maybe_null())
m_null_bits[(i / 8)]+= 1 << (i % 8);
+ init_metadata_fields();
+ m_data_size+= m_metadata_buf.length();
+
DBUG_VOID_RETURN;
}
@@ -6307,9 +6318,414 @@ bool Table_map_log_event::write_data_body()
write_data(m_coltype, m_colcnt) ||
write_data(mbuf, (size_t) (mbuf_end - mbuf)) ||
write_data(m_field_metadata, m_field_metadata_size),
- write_data(m_null_bits, (m_colcnt + 7) / 8);
+ write_data(m_null_bits, (m_colcnt + 7) / 8) ||
+ write_data((const uchar*) m_metadata_buf.ptr(),
+ m_metadata_buf.length());
}
+/**
+ stores an integer into packed format.
+
+ @param[out] str_buf a buffer where the packed integer will be stored.
+ @param[in] length the integer will be packed.
+ */
+static inline
+void store_compressed_length(String &str_buf, ulonglong length)
+{
+ // Store Type and packed length
+ uchar buf[4];
+ uchar *buf_ptr = net_store_length(buf, length);
+
+ str_buf.append(reinterpret_cast<char *>(buf), buf_ptr-buf);
+}
+
+/**
+ Write data into str_buf with Type|Length|Value(TLV) format.
+
+ @param[out] str_buf a buffer where the field is stored.
+ @param[in] type type of the field
+ @param[in] length length of the field value
+ @param[in] value value of the field
+*/
+static inline
+bool write_tlv_field(String &str_buf,
+ enum Table_map_log_event::Optional_metadata_field_type
+ type, uint length, const uchar *value)
+{
+ /* type is stored in one byte, so it should never bigger than 255. */
+ DBUG_ASSERT(static_cast<int>(type) <= 255);
+ str_buf.append((char) type);
+ store_compressed_length(str_buf, length);
+ return str_buf.append(reinterpret_cast<const char *>(value), length);
+}
+
+/**
+ Write data into str_buf with Type|Length|Value(TLV) format.
+
+ @param[out] str_buf a buffer where the field is stored.
+ @param[in] type type of the field
+ @param[in] value value of the field
+*/
+static inline
+bool write_tlv_field(String &str_buf,
+ enum Table_map_log_event::Optional_metadata_field_type
+ type, const String &value)
+{
+ return write_tlv_field(str_buf, type, value.length(),
+ reinterpret_cast<const uchar *>(value.ptr()));
+}
+
+
+#ifdef MYSQL_SERVER
+static inline bool is_numeric_field(const Field *field)
+{
+ return is_numeric_type(field->binlog_type());
+}
+
+static inline bool is_character_field(const Field *field)
+{
+ return is_character_type(field->real_type());
+}
+
+static inline bool is_enum_field(const Field *field)
+{
+ return field->real_type() == MYSQL_TYPE_ENUM;
+}
+
+static inline bool is_set_field(const Field *field)
+{
+ return field->real_type() == MYSQL_TYPE_SET;
+}
+
+static inline bool is_geometry_field(const Field *field)
+{
+ return field->real_type() == MYSQL_TYPE_GEOMETRY;
+}
+
+static inline bool is_enum_or_set_field(const Field *field) {
+ return is_enum_or_set_type(field->real_type());
+}
+
+
+void Table_map_log_event::init_metadata_fields()
+{
+ DBUG_ENTER("init_metadata_fields");
+ DBUG_EXECUTE_IF("simulate_no_optional_metadata", DBUG_VOID_RETURN;);
+
+ if (init_signedness_field() ||
+ init_charset_field(&is_character_field, DEFAULT_CHARSET,
+ COLUMN_CHARSET) ||
+ init_geometry_type_field())
+ {
+ m_metadata_buf.length(0);
+ DBUG_VOID_RETURN;
+ }
+
+ if (binlog_row_metadata == BINLOG_ROW_METADATA_FULL)
+ {
+ if (DBUG_EVALUATE_IF("dont_log_column_name", 0, init_column_name_field()) ||
+ init_charset_field(&is_enum_or_set_field, ENUM_AND_SET_DEFAULT_CHARSET,
+ ENUM_AND_SET_COLUMN_CHARSET) ||
+ init_set_str_value_field() ||
+ init_enum_str_value_field() ||
+ init_primary_key_field())
+ m_metadata_buf.length(0);
+ }
+ DBUG_VOID_RETURN;
+}
+
+bool Table_map_log_event::init_signedness_field()
+{
+ /* use it to store signed flags, each numeric column take a bit. */
+ StringBuffer<128> buf;
+ unsigned char flag= 0;
+ unsigned char mask= 0x80;
+
+ for (unsigned int i= 0 ; i < m_table->s->fields ; ++i)
+ {
+ if (is_numeric_field(m_table->field[i]))
+ {
+ Field_num *field= dynamic_cast<Field_num *>(m_table->field[i]);
+
+ if (field->unsigned_flag)
+ flag|= mask;
+
+ mask >>= 1;
+
+ // 8 fields are tested, store the result and clear the flag.
+ if (mask == 0)
+ {
+ buf.append(flag);
+ flag= 0;
+ mask= 0x80;
+ }
+ }
+ }
+
+ // Stores the signedness flags of last few columns
+ if (mask != 0x80)
+ buf.append(flag);
+
+ // The table has no numeric column, so don't log SIGNEDNESS field
+ if (buf.is_empty())
+ return false;
+
+ return write_tlv_field(m_metadata_buf, SIGNEDNESS, buf);
+}
+
+bool Table_map_log_event::init_charset_field(
+ std::function<bool(const Field *)> include_type,
+ Optional_metadata_field_type default_charset_type,
+ Optional_metadata_field_type column_charset_type)
+{
+ DBUG_EXECUTE_IF("simulate_init_charset_field_error", return true;);
+
+ std::map<uint, uint> collation_map;
+ // For counting characters columns
+ uint char_col_cnt= 0;
+
+ /* Find the collation number used by most fields */
+ for (unsigned int i= 0 ; i < m_table->s->fields ; ++i)
+ {
+ if (include_type(m_table->field[i]))
+ {
+ Field_str *field= dynamic_cast<Field_str *>(m_table->field[i]);
+
+ collation_map[field->charset()->number]++;
+ char_col_cnt++;
+ }
+ }
+
+ if (char_col_cnt == 0)
+ return false;
+
+ /* Find the most used collation */
+ uint most_used_collation= 0;
+ uint most_used_count= 0;
+ for (std::map<uint, uint>::iterator it= collation_map.begin();
+ it != collation_map.end(); it++)
+ {
+ if (it->second > most_used_count)
+ {
+ most_used_count= it->second;
+ most_used_collation= it->first;
+ }
+ }
+
+ /*
+ Comparing length of COLUMN_CHARSET field and COLUMN_CHARSET_WITH_DEFAULT
+ field to decide which field should be logged.
+
+ Length of COLUMN_CHARSET = character column count * collation id size.
+ Length of COLUMN_CHARSET_WITH_DEFAULT =
+ default collation_id size + count of columns not use default charset *
+ (column index size + collation id size)
+
+ Assume column index just uses 1 byte and collation number also uses 1 byte.
+ */
+ if (char_col_cnt * 1 < (1 + (char_col_cnt - most_used_count) * 2))
+ {
+ StringBuffer<512> buf;
+
+ /*
+ Stores character set information into COLUMN_CHARSET format,
+ character sets of all columns are stored one by one.
+ -----------------------------------------
+ | Charset number | .... |Charset number |
+ -----------------------------------------
+ */
+ for (unsigned int i= 0 ; i < m_table->s->fields ; ++i)
+ {
+ if (include_type(m_table->field[i]))
+ {
+ Field_str *field= dynamic_cast<Field_str *>(m_table->field[i]);
+
+ store_compressed_length(buf, field->charset()->number);
+ }
+ }
+ return write_tlv_field(m_metadata_buf, column_charset_type, buf);
+ }
+ else
+ {
+ StringBuffer<512> buf;
+ uint char_column_index= 0;
+ uint default_collation= most_used_collation;
+
+ /*
+ Stores character set information into DEFAULT_CHARSET format,
+ First stores the default character set, and then stores the character
+ sets different to default character with their column index one by one.
+ --------------------------------------------------------
+ | Default Charset | Col Index | Charset number | ... |
+ --------------------------------------------------------
+ */
+
+ // Store the default collation number
+ store_compressed_length(buf, default_collation);
+
+ for (unsigned int i= 0 ; i < m_table->s->fields ; ++i)
+ {
+ if (include_type(m_table->field[i]))
+ {
+ Field_str *field= dynamic_cast<Field_str *>(m_table->field[i]);
+
+ if (field->charset()->number != default_collation)
+ {
+ store_compressed_length(buf, char_column_index);
+ store_compressed_length(buf, field->charset()->number);
+ }
+ char_column_index++;
+ }
+ }
+ return write_tlv_field(m_metadata_buf, default_charset_type, buf);
+ }
+}
+
+bool Table_map_log_event::init_column_name_field()
+{
+ StringBuffer<2048> buf;
+
+ for (unsigned int i= 0 ; i < m_table->s->fields ; ++i)
+ {
+ size_t len= strlen(m_table->field[i]->field_name.str);
+
+ store_compressed_length(buf, len);
+ buf.append(m_table->field[i]->field_name.str, len);
+ }
+ return write_tlv_field(m_metadata_buf, COLUMN_NAME, buf);
+}
+
+bool Table_map_log_event::init_set_str_value_field()
+{
+ StringBuffer<1024> buf;
+
+ /*
+ SET string values are stored in the same format:
+ ----------------------------------------------
+ | Value number | value1 len | value 1| .... | // first SET column
+ ----------------------------------------------
+ | Value number | value1 len | value 1| .... | // second SET column
+ ----------------------------------------------
+ */
+ for (unsigned int i= 0 ; i < m_table->s->fields ; ++i)
+ {
+ if (is_set_field(m_table->field[i]))
+ {
+ TYPELIB *typelib= dynamic_cast<Field_set *>(m_table->field[i])->typelib;
+
+ store_compressed_length(buf, typelib->count);
+ for (unsigned int i= 0; i < typelib->count; i++)
+ {
+ store_compressed_length(buf, typelib->type_lengths[i]);
+ buf.append(typelib->type_names[i], typelib->type_lengths[i]);
+ }
+ }
+ }
+ if (buf.length() > 0)
+ return write_tlv_field(m_metadata_buf, SET_STR_VALUE, buf);
+ return false;
+}
+
+bool Table_map_log_event::init_enum_str_value_field()
+{
+ StringBuffer<1024> buf;
+
+ /* ENUM is same to SET columns, see comment in init_set_str_value_field */
+ for (unsigned int i= 0 ; i < m_table->s->fields ; ++i)
+ {
+ if (is_enum_field(m_table->field[i]))
+ {
+ TYPELIB *typelib= dynamic_cast<Field_enum *>(m_table->field[i])->typelib;
+
+ store_compressed_length(buf, typelib->count);
+ for (unsigned int i= 0; i < typelib->count; i++)
+ {
+ store_compressed_length(buf, typelib->type_lengths[i]);
+ buf.append(typelib->type_names[i], typelib->type_lengths[i]);
+ }
+ }
+ }
+
+ if (buf.length() > 0)
+ return write_tlv_field(m_metadata_buf, ENUM_STR_VALUE, buf);
+ return false;
+}
+
+bool Table_map_log_event::init_geometry_type_field()
+{
+ StringBuffer<256> buf;
+
+ /* Geometry type of geometry columns is stored one by one as packed length */
+ for (unsigned int i= 0 ; i < m_table->s->fields ; ++i)
+ {
+ if (is_geometry_field(m_table->field[i]))
+ {
+ int type= dynamic_cast<Field_geom *>(m_table->field[i])->geom_type;
+ DBUG_EXECUTE_IF("inject_invalid_geometry_type", type= 100;);
+ store_compressed_length(buf, type);
+ }
+ }
+
+ if (buf.length() > 0)
+ return write_tlv_field(m_metadata_buf, GEOMETRY_TYPE, buf);
+ return false;
+}
+
+bool Table_map_log_event::init_primary_key_field()
+{
+ DBUG_EXECUTE_IF("simulate_init_primary_key_field_error", return true;);
+
+ if (unlikely(m_table->s->primary_key == MAX_KEY))
+ return false;
+
+ // If any key column uses prefix like KEY(c1(10)) */
+ bool has_prefix= false;
+ KEY *pk= m_table->key_info + m_table->s->primary_key;
+
+ DBUG_ASSERT(pk->user_defined_key_parts > 0);
+
+ /* Check if any key column uses prefix */
+ for (uint i= 0; i < pk->user_defined_key_parts; i++)
+ {
+ KEY_PART_INFO *key_part= pk->key_part+i;
+ if (key_part->length != m_table->field[key_part->fieldnr-1]->key_length())
+ {
+ has_prefix= true;
+ break;
+ }
+ }
+
+ StringBuffer<128> buf;
+
+ if (!has_prefix)
+ {
+ /* Index of PK columns are stored one by one. */
+ for (uint i= 0; i < pk->user_defined_key_parts; i++)
+ {
+ KEY_PART_INFO *key_part= pk->key_part+i;
+ store_compressed_length(buf, key_part->fieldnr-1);
+ }
+ return write_tlv_field(m_metadata_buf, SIMPLE_PRIMARY_KEY, buf);
+ }
+ else
+ {
+ /* Index of PK columns are stored with a prefix length one by one. */
+ for (uint i= 0; i < pk->user_defined_key_parts; i++)
+ {
+ KEY_PART_INFO *key_part= pk->key_part+i;
+ size_t prefix= 0;
+
+ store_compressed_length(buf, key_part->fieldnr-1);
+
+ // Store character length but not octet length
+ if (key_part->length != m_table->field[key_part->fieldnr-1]->key_length())
+ prefix= key_part->length / key_part->field->charset()->mbmaxlen;
+ store_compressed_length(buf, prefix);
+ }
+ return write_tlv_field(m_metadata_buf, PRIMARY_KEY_WITH_PREFIX, buf);
+ }
+}
+#endif
#if defined(HAVE_REPLICATION)
/*
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index f3d36bfc15c..41c8ff38a1c 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -450,6 +450,7 @@ my_bool opt_noacl;
my_bool sp_automatic_privileges= 1;
ulong opt_binlog_rows_event_max_size;
+ulong binlog_row_metadata;
my_bool opt_master_verify_checksum= 0;
my_bool opt_slave_sql_verify_checksum= 1;
const char *binlog_format_names[]= {"MIXED", "STATEMENT", "ROW", NullS};
diff --git a/sql/mysqld.h b/sql/mysqld.h
index 505741b5458..693aee5e743 100644
--- a/sql/mysqld.h
+++ b/sql/mysqld.h
@@ -244,6 +244,7 @@ extern ulonglong max_binlog_cache_size, max_binlog_stmt_cache_size;
extern ulong max_binlog_size;
extern ulong slave_max_allowed_packet;
extern ulong opt_binlog_rows_event_max_size;
+extern ulong binlog_row_metadata;
extern ulong thread_cache_size;
extern ulong stored_program_cache_size;
extern ulong opt_slave_parallel_threads;
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 63f964e96ce..b254e206ca0 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -137,6 +137,11 @@ enum enum_binlog_row_image {
/** All columns in both before and after image. */
BINLOG_ROW_IMAGE_FULL= 2
};
+// Values for binlog_row_metadata sysvar
+enum enum_binlog_row_metadata {
+ BINLOG_ROW_METADATA_MINIMAL= 0,
+ BINLOG_ROW_METADATA_FULL= 1
+};
/* Bits for different SQL modes modes (including ANSI mode) */
diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
index 7bc294fcc99..d7adce57c76 100644
--- a/sql/sys_vars.cc
+++ b/sql/sys_vars.cc
@@ -6167,6 +6167,18 @@ static Sys_var_enum Sys_binlog_row_image(
SESSION_VAR(binlog_row_image), CMD_LINE(REQUIRED_ARG),
binlog_row_image_names, DEFAULT(BINLOG_ROW_IMAGE_FULL));
+static const char *binlog_row_metadata_names[]= {"MINIMAL", "FULL", NullS};
+static Sys_var_enum Sys_binlog_row_metadata(
+ "binlog_row_metadata",
+ "Controls whether metadata is logged using FULL or MINIMAL format. "
+ "FULL causes all metadata to be logged; MINIMAL means that only "
+ "metadata actually required by slave is logged. Default: MINIMAL.",
+ GLOBAL_VAR(binlog_row_metadata), CMD_LINE(REQUIRED_ARG),
+ binlog_row_metadata_names, DEFAULT(BINLOG_ROW_METADATA_MINIMAL),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(NULL),
+ ON_UPDATE(NULL));
+
+
static bool check_pseudo_slave_mode(sys_var *self, THD *thd, set_var *var)
{
longlong previous_val= thd->variables.pseudo_slave_mode;
1
0
[Commits] 5a2b9dda79b: MDEV-19855: Create "Sql_cmd_show_slave_status" class for "SHOW SLAVE STATUS" command.
by sujatha 25 Jun '19
by sujatha 25 Jun '19
25 Jun '19
revision-id: 5a2b9dda79bea019e6b6e86ab3a118f123f75e8c (mariadb-10.4.4-187-g5a2b9dda79b)
parent(s): e6297bbe376a687d3ab4771a5692a029f9507425
author: Sujatha
committer: Sujatha
timestamp: 2019-06-25 15:02:34 +0530
message:
MDEV-19855: Create "Sql_cmd_show_slave_status" class for "SHOW SLAVE STATUS" command.
Create "Sql_cmd_show_slave_status" class for "SHOW SLAVE STATUS" command.
---
sql/slave.cc | 34 ++++++++++++++++++++++++++++++++++
sql/sql_cmd.h | 20 ++++++++++++++++++++
sql/sql_parse.cc | 25 +------------------------
sql/sql_prepare.cc | 23 ++++++++++++++++-------
sql/sql_yacc.yy | 12 +++++++++---
sql/sql_yacc_ora.yy | 12 +++++++++---
6 files changed, 89 insertions(+), 37 deletions(-)
diff --git a/sql/slave.cc b/sql/slave.cc
index a3776c0a580..03777dc8ce8 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -1646,6 +1646,40 @@ const char *print_slave_db_safe(const char* db)
#endif /* HAVE_REPLICATION */
+bool Sql_cmd_show_slave_status::execute(THD *thd)
+{
+#ifndef HAVE_REPLICATION
+ my_ok(thd);
+ return false;
+#else
+ DBUG_ENTER("Sql_cmd_show_slave_status::execute");
+ bool res= true;
+
+ /* Accept one of two privileges */
+ if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL))
+ goto error;
+ if (is_show_all_slaves_stat())
+ {
+ mysql_mutex_lock(&LOCK_active_mi);
+ res= show_all_master_info(thd);
+ mysql_mutex_unlock(&LOCK_active_mi);
+ }
+ else
+ {
+ LEX_MASTER_INFO *lex_mi= &thd->lex->mi;
+ Master_info *mi;
+ if ((mi= get_master_info(&lex_mi->connection_name,
+ Sql_condition::WARN_LEVEL_ERROR)))
+ {
+ res= show_master_info(thd, mi, 0);
+ mi->release();
+ }
+ }
+error:
+ DBUG_RETURN(res);
+#endif
+}
+
int init_strvar_from_file(char *var, int max_size, IO_CACHE *f,
const char *default_val)
{
diff --git a/sql/sql_cmd.h b/sql/sql_cmd.h
index 7f1fd06aa46..1f8f2dcabc9 100644
--- a/sql/sql_cmd.h
+++ b/sql/sql_cmd.h
@@ -208,6 +208,26 @@ class Sql_cmd : public Sql_alloc
}
};
+class Sql_cmd_show_slave_status: public Sql_cmd
+{
+protected:
+ bool show_all_slaves_status;
+public:
+ Sql_cmd_show_slave_status()
+ :show_all_slaves_status(false)
+ {}
+
+ Sql_cmd_show_slave_status(bool status_all)
+ :show_all_slaves_status(status_all)
+ {}
+
+ enum_sql_command sql_command_code() const { return SQLCOM_SHOW_SLAVE_STAT; }
+
+ bool execute(THD *thd);
+ bool is_show_all_slaves_stat() { return show_all_slaves_status; }
+};
+
+
class Sql_cmd_create_table_like: public Sql_cmd,
public Storage_engine_name
{
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 54a41adbf68..16465c47a2f 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -4079,31 +4079,7 @@ mysql_execute_command(THD *thd)
mysql_mutex_unlock(&LOCK_active_mi);
break;
}
- case SQLCOM_SHOW_SLAVE_STAT:
- {
- /* Accept one of two privileges */
- if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL))
- goto error;
- if (lex->verbose)
- {
- mysql_mutex_lock(&LOCK_active_mi);
- res= show_all_master_info(thd);
- mysql_mutex_unlock(&LOCK_active_mi);
- }
- else
- {
- LEX_MASTER_INFO *lex_mi= &thd->lex->mi;
- Master_info *mi;
- if ((mi= get_master_info(&lex_mi->connection_name,
- Sql_condition::WARN_LEVEL_ERROR)))
- {
- res= show_master_info(thd, mi, 0);
- mi->release();
- }
- }
- break;
- }
case SQLCOM_SHOW_MASTER_STAT:
{
/* Accept one of two privileges */
@@ -6074,6 +6050,7 @@ mysql_execute_command(THD *thd)
DBUG_ASSERT(first_table == all_tables && first_table != 0);
/* fall through */
case SQLCOM_ALTER_SEQUENCE:
+ case SQLCOM_SHOW_SLAVE_STAT:
case SQLCOM_SIGNAL:
case SQLCOM_RESIGNAL:
case SQLCOM_GET_DIAGNOSTICS:
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 68c9edc9283..ab90e16d903 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -1933,14 +1933,15 @@ static int mysql_test_show_grants(Prepared_statement *stmt)
TRUE error, error message is set in THD
*/
-static int mysql_test_show_slave_status(Prepared_statement *stmt)
+static int mysql_test_show_slave_status(Prepared_statement *stmt,
+ bool show_all_slaves_stat)
{
DBUG_ENTER("mysql_test_show_slave_status");
THD *thd= stmt->thd;
List<Item> fields;
- show_master_info_get_fields(thd, &fields, thd->lex->verbose, 0);
-
+ show_master_info_get_fields(thd, &fields, show_all_slaves_stat, 0);
+
DBUG_RETURN(send_stmt_metadata(thd, stmt, &fields));
}
@@ -2393,12 +2394,20 @@ static bool check_prepared_statement(Prepared_statement *stmt)
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
#ifndef EMBEDDED_LIBRARY
case SQLCOM_SHOW_SLAVE_STAT:
- if ((res= mysql_test_show_slave_status(stmt)) == 2)
{
- /* Statement and field info has already been sent */
- DBUG_RETURN(FALSE);
+ DBUG_ASSERT(thd->lex->m_sql_cmd);
+ Sql_cmd_show_slave_status *cmd;
+ cmd= dynamic_cast<Sql_cmd_show_slave_status*>(thd->lex->m_sql_cmd);
+ DBUG_ASSERT(cmd);
+ if ((res= mysql_test_show_slave_status(stmt,
+ cmd->is_show_all_slaves_stat()))
+ == 2)
+ {
+ /* Statement and field info has already been sent */
+ DBUG_RETURN(FALSE);
+ }
+ break;
}
- break;
case SQLCOM_SHOW_MASTER_STAT:
if ((res= mysql_test_show_master_status(stmt)) == 2)
{
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index d50f9722d7e..49d6eec648e 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -14205,20 +14205,26 @@ show_param:
}
| ALL SLAVES STATUS_SYM
{
+ if (!(Lex->m_sql_cmd= new (thd->mem_root)
+ Sql_cmd_show_slave_status(true)))
+ MYSQL_YYABORT;
Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
- Lex->verbose= 1;
}
| SLAVE STATUS_SYM
{
LEX *lex= thd->lex;
lex->mi.connection_name= null_clex_str;
+ if (!(lex->m_sql_cmd= new (thd->mem_root)
+ Sql_cmd_show_slave_status()))
+ MYSQL_YYABORT;
lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
- lex->verbose= 0;
}
| SLAVE connection_name STATUS_SYM
{
+ if (!(Lex->m_sql_cmd= new (thd->mem_root)
+ Sql_cmd_show_slave_status()))
+ MYSQL_YYABORT;
Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
- Lex->verbose= 0;
}
| CREATE PROCEDURE_SYM sp_name
{
diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy
index 75c57111dc5..13b5bef5401 100644
--- a/sql/sql_yacc_ora.yy
+++ b/sql/sql_yacc_ora.yy
@@ -14329,19 +14329,25 @@ show_param:
| ALL SLAVES STATUS_SYM
{
Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
- Lex->verbose= 1;
+ if (!(Lex->m_sql_cmd= new (thd->mem_root)
+ Sql_cmd_show_slave_status(true)))
+ MYSQL_YYABORT;
}
| SLAVE STATUS_SYM
{
LEX *lex= thd->lex;
lex->mi.connection_name= null_clex_str;
lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
- lex->verbose= 0;
+ if (!(lex->m_sql_cmd= new (thd->mem_root)
+ Sql_cmd_show_slave_status()))
+ MYSQL_YYABORT;
}
| SLAVE connection_name STATUS_SYM
{
Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
- Lex->verbose= 0;
+ if (!(Lex->m_sql_cmd= new (thd->mem_root)
+ Sql_cmd_show_slave_status()))
+ MYSQL_YYABORT;
}
| CREATE PROCEDURE_SYM sp_name
{
1
0
[Commits] c7be3f5df70: MDEV-19855: Create "Sql_cmd_show_slave_status" class for "SHOW SLAVE STATUS" command.
by sujatha 25 Jun '19
by sujatha 25 Jun '19
25 Jun '19
revision-id: c7be3f5df70af13069f677957953db509506c728 (mariadb-10.4.4-187-gc7be3f5df70)
parent(s): e6297bbe376a687d3ab4771a5692a029f9507425
author: Sujatha
committer: Sujatha
timestamp: 2019-06-25 14:01:08 +0530
message:
MDEV-19855: Create "Sql_cmd_show_slave_status" class for "SHOW SLAVE STATUS" command.
Create "Sql_cmd_show_slave_status" class for "SHOW SLAVE STATUS" command.
---
sql/slave.cc | 37 +++++++++++++++++++++++++++++++++++++
sql/sql_cmd.h | 21 +++++++++++++++++++++
sql/sql_parse.cc | 26 +++++---------------------
sql/sql_prepare.cc | 23 ++++++++++++++++-------
sql/sql_yacc.yy | 9 ++++++---
sql/sql_yacc_ora.yy | 9 ++++++---
6 files changed, 91 insertions(+), 34 deletions(-)
diff --git a/sql/slave.cc b/sql/slave.cc
index a3776c0a580..e42b131ce4a 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -1646,6 +1646,43 @@ const char *print_slave_db_safe(const char* db)
#endif /* HAVE_REPLICATION */
+bool Sql_cmd_show_slave_status::execute(THD *thd)
+{
+#ifndef HAVE_REPLICATION
+ my_ok(thd);
+ return false;
+#else
+ DBUG_ENTER("Sql_cmd_show_slave_status::execute");
+ bool res= true;
+ Sql_cmd_show_slave_status *cmd;
+ cmd= dynamic_cast<Sql_cmd_show_slave_status*> (thd->lex->m_sql_cmd);
+ DBUG_ASSERT(cmd);
+
+ /* Accept one of two privileges */
+ if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL))
+ goto error;
+ if (cmd->is_show_all_slaves_stat())
+ {
+ mysql_mutex_lock(&LOCK_active_mi);
+ res= show_all_master_info(thd);
+ mysql_mutex_unlock(&LOCK_active_mi);
+ }
+ else
+ {
+ LEX_MASTER_INFO *lex_mi= &thd->lex->mi;
+ Master_info *mi;
+ if ((mi= get_master_info(&lex_mi->connection_name,
+ Sql_condition::WARN_LEVEL_ERROR)))
+ {
+ res= show_master_info(thd, mi, 0);
+ mi->release();
+ }
+ }
+error:
+ DBUG_RETURN(res);
+#endif
+}
+
int init_strvar_from_file(char *var, int max_size, IO_CACHE *f,
const char *default_val)
{
diff --git a/sql/sql_cmd.h b/sql/sql_cmd.h
index 7f1fd06aa46..2902da1c2e2 100644
--- a/sql/sql_cmd.h
+++ b/sql/sql_cmd.h
@@ -208,6 +208,27 @@ class Sql_cmd : public Sql_alloc
}
};
+class Sql_cmd_show_slave_status: public Sql_cmd
+{
+protected:
+ bool show_all_slaves_status;
+public:
+ Sql_cmd_show_slave_status()
+ {
+ show_all_slaves_status= 0;
+ }
+
+ Sql_cmd_show_slave_status(bool status_all)
+ :show_all_slaves_status(status_all)
+ {}
+
+ enum_sql_command sql_command_code() const { return SQLCOM_SHOW_SLAVE_STAT; }
+
+ bool execute(THD *thd);
+ bool is_show_all_slaves_stat() { return show_all_slaves_status; }
+};
+
+
class Sql_cmd_create_table_like: public Sql_cmd,
public Storage_engine_name
{
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 54a41adbf68..d0d222b0d80 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -4081,29 +4081,13 @@ mysql_execute_command(THD *thd)
}
case SQLCOM_SHOW_SLAVE_STAT:
{
- /* Accept one of two privileges */
- if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL))
- goto error;
-
- if (lex->verbose)
- {
- mysql_mutex_lock(&LOCK_active_mi);
- res= show_all_master_info(thd);
- mysql_mutex_unlock(&LOCK_active_mi);
- }
- else
- {
- LEX_MASTER_INFO *lex_mi= &thd->lex->mi;
- Master_info *mi;
- if ((mi= get_master_info(&lex_mi->connection_name,
- Sql_condition::WARN_LEVEL_ERROR)))
- {
- res= show_master_info(thd, mi, 0);
- mi->release();
- }
- }
+ DBUG_ASSERT(lex->m_sql_cmd != NULL);
+ res= lex->m_sql_cmd->execute(thd);
+ DBUG_PRINT("result", ("res: %d killed: %d is_error: %d",
+ res, thd->killed, thd->is_error()));
break;
}
+
case SQLCOM_SHOW_MASTER_STAT:
{
/* Accept one of two privileges */
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 68c9edc9283..ab90e16d903 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -1933,14 +1933,15 @@ static int mysql_test_show_grants(Prepared_statement *stmt)
TRUE error, error message is set in THD
*/
-static int mysql_test_show_slave_status(Prepared_statement *stmt)
+static int mysql_test_show_slave_status(Prepared_statement *stmt,
+ bool show_all_slaves_stat)
{
DBUG_ENTER("mysql_test_show_slave_status");
THD *thd= stmt->thd;
List<Item> fields;
- show_master_info_get_fields(thd, &fields, thd->lex->verbose, 0);
-
+ show_master_info_get_fields(thd, &fields, show_all_slaves_stat, 0);
+
DBUG_RETURN(send_stmt_metadata(thd, stmt, &fields));
}
@@ -2393,12 +2394,20 @@ static bool check_prepared_statement(Prepared_statement *stmt)
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
#ifndef EMBEDDED_LIBRARY
case SQLCOM_SHOW_SLAVE_STAT:
- if ((res= mysql_test_show_slave_status(stmt)) == 2)
{
- /* Statement and field info has already been sent */
- DBUG_RETURN(FALSE);
+ DBUG_ASSERT(thd->lex->m_sql_cmd);
+ Sql_cmd_show_slave_status *cmd;
+ cmd= dynamic_cast<Sql_cmd_show_slave_status*>(thd->lex->m_sql_cmd);
+ DBUG_ASSERT(cmd);
+ if ((res= mysql_test_show_slave_status(stmt,
+ cmd->is_show_all_slaves_stat()))
+ == 2)
+ {
+ /* Statement and field info has already been sent */
+ DBUG_RETURN(FALSE);
+ }
+ break;
}
- break;
case SQLCOM_SHOW_MASTER_STAT:
if ((res= mysql_test_show_master_status(stmt)) == 2)
{
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index d50f9722d7e..a749713d65b 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -14205,20 +14205,23 @@ show_param:
}
| ALL SLAVES STATUS_SYM
{
+ if (!(Lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_show_slave_status(1)))
+ MYSQL_YYABORT;
Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
- Lex->verbose= 1;
}
| SLAVE STATUS_SYM
{
LEX *lex= thd->lex;
lex->mi.connection_name= null_clex_str;
+ if (!(lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_show_slave_status()))
+ MYSQL_YYABORT;
lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
- lex->verbose= 0;
}
| SLAVE connection_name STATUS_SYM
{
+ if (!(Lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_show_slave_status()))
+ MYSQL_YYABORT;
Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
- Lex->verbose= 0;
}
| CREATE PROCEDURE_SYM sp_name
{
diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy
index 75c57111dc5..3143ca47e61 100644
--- a/sql/sql_yacc_ora.yy
+++ b/sql/sql_yacc_ora.yy
@@ -14329,19 +14329,22 @@ show_param:
| ALL SLAVES STATUS_SYM
{
Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
- Lex->verbose= 1;
+ if (!(Lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_show_slave_status(1)))
+ MYSQL_YYABORT;
}
| SLAVE STATUS_SYM
{
LEX *lex= thd->lex;
lex->mi.connection_name= null_clex_str;
lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
- lex->verbose= 0;
+ if (!(lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_show_slave_status()))
+ MYSQL_YYABORT;
}
| SLAVE connection_name STATUS_SYM
{
Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
- Lex->verbose= 0;
+ if (!(Lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_show_slave_status()))
+ MYSQL_YYABORT;
}
| CREATE PROCEDURE_SYM sp_name
{
1
0
[Commits] 53f82668d26: MDEV-18777: Rename or alias slave-related statements, options, status variables
by sujatha 25 Jun '19
by sujatha 25 Jun '19
25 Jun '19
revision-id: 53f82668d266a9033ee06dd9cd0ab91c24e30247 (mariadb-10.4.4-187-g53f82668d26)
parent(s): e6297bbe376a687d3ab4771a5692a029f9507425
author: Sujatha
committer: Sujatha
timestamp: 2019-06-24 19:35:14 +0530
message:
MDEV-18777: Rename or alias slave-related statements, options, status variables
Step1: Create "Sql_cmd_show_slave_status" class for "SHOW SLAVE STATUS" command.
---
sql/slave.cc | 37 +++++++++++++++++++++++++++++++++++++
sql/sql_cmd.h | 21 +++++++++++++++++++++
sql/sql_parse.cc | 26 +++++---------------------
sql/sql_prepare.cc | 23 ++++++++++++++++-------
sql/sql_yacc.yy | 9 ++++++---
sql/sql_yacc_ora.yy | 9 ++++++---
6 files changed, 91 insertions(+), 34 deletions(-)
diff --git a/sql/slave.cc b/sql/slave.cc
index a3776c0a580..e42b131ce4a 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -1646,6 +1646,43 @@ const char *print_slave_db_safe(const char* db)
#endif /* HAVE_REPLICATION */
+bool Sql_cmd_show_slave_status::execute(THD *thd)
+{
+#ifndef HAVE_REPLICATION
+ my_ok(thd);
+ return false;
+#else
+ DBUG_ENTER("Sql_cmd_show_slave_status::execute");
+ bool res= true;
+ Sql_cmd_show_slave_status *cmd;
+ cmd= dynamic_cast<Sql_cmd_show_slave_status*> (thd->lex->m_sql_cmd);
+ DBUG_ASSERT(cmd);
+
+ /* Accept one of two privileges */
+ if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL))
+ goto error;
+ if (cmd->is_show_all_slaves_stat())
+ {
+ mysql_mutex_lock(&LOCK_active_mi);
+ res= show_all_master_info(thd);
+ mysql_mutex_unlock(&LOCK_active_mi);
+ }
+ else
+ {
+ LEX_MASTER_INFO *lex_mi= &thd->lex->mi;
+ Master_info *mi;
+ if ((mi= get_master_info(&lex_mi->connection_name,
+ Sql_condition::WARN_LEVEL_ERROR)))
+ {
+ res= show_master_info(thd, mi, 0);
+ mi->release();
+ }
+ }
+error:
+ DBUG_RETURN(res);
+#endif
+}
+
int init_strvar_from_file(char *var, int max_size, IO_CACHE *f,
const char *default_val)
{
diff --git a/sql/sql_cmd.h b/sql/sql_cmd.h
index 7f1fd06aa46..2902da1c2e2 100644
--- a/sql/sql_cmd.h
+++ b/sql/sql_cmd.h
@@ -208,6 +208,27 @@ class Sql_cmd : public Sql_alloc
}
};
+class Sql_cmd_show_slave_status: public Sql_cmd
+{
+protected:
+ bool show_all_slaves_status;
+public:
+ Sql_cmd_show_slave_status()
+ {
+ show_all_slaves_status= 0;
+ }
+
+ Sql_cmd_show_slave_status(bool status_all)
+ :show_all_slaves_status(status_all)
+ {}
+
+ enum_sql_command sql_command_code() const { return SQLCOM_SHOW_SLAVE_STAT; }
+
+ bool execute(THD *thd);
+ bool is_show_all_slaves_stat() { return show_all_slaves_status; }
+};
+
+
class Sql_cmd_create_table_like: public Sql_cmd,
public Storage_engine_name
{
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 54a41adbf68..d0d222b0d80 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -4081,29 +4081,13 @@ mysql_execute_command(THD *thd)
}
case SQLCOM_SHOW_SLAVE_STAT:
{
- /* Accept one of two privileges */
- if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL))
- goto error;
-
- if (lex->verbose)
- {
- mysql_mutex_lock(&LOCK_active_mi);
- res= show_all_master_info(thd);
- mysql_mutex_unlock(&LOCK_active_mi);
- }
- else
- {
- LEX_MASTER_INFO *lex_mi= &thd->lex->mi;
- Master_info *mi;
- if ((mi= get_master_info(&lex_mi->connection_name,
- Sql_condition::WARN_LEVEL_ERROR)))
- {
- res= show_master_info(thd, mi, 0);
- mi->release();
- }
- }
+ DBUG_ASSERT(lex->m_sql_cmd != NULL);
+ res= lex->m_sql_cmd->execute(thd);
+ DBUG_PRINT("result", ("res: %d killed: %d is_error: %d",
+ res, thd->killed, thd->is_error()));
break;
}
+
case SQLCOM_SHOW_MASTER_STAT:
{
/* Accept one of two privileges */
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 68c9edc9283..ab90e16d903 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -1933,14 +1933,15 @@ static int mysql_test_show_grants(Prepared_statement *stmt)
TRUE error, error message is set in THD
*/
-static int mysql_test_show_slave_status(Prepared_statement *stmt)
+static int mysql_test_show_slave_status(Prepared_statement *stmt,
+ bool show_all_slaves_stat)
{
DBUG_ENTER("mysql_test_show_slave_status");
THD *thd= stmt->thd;
List<Item> fields;
- show_master_info_get_fields(thd, &fields, thd->lex->verbose, 0);
-
+ show_master_info_get_fields(thd, &fields, show_all_slaves_stat, 0);
+
DBUG_RETURN(send_stmt_metadata(thd, stmt, &fields));
}
@@ -2393,12 +2394,20 @@ static bool check_prepared_statement(Prepared_statement *stmt)
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
#ifndef EMBEDDED_LIBRARY
case SQLCOM_SHOW_SLAVE_STAT:
- if ((res= mysql_test_show_slave_status(stmt)) == 2)
{
- /* Statement and field info has already been sent */
- DBUG_RETURN(FALSE);
+ DBUG_ASSERT(thd->lex->m_sql_cmd);
+ Sql_cmd_show_slave_status *cmd;
+ cmd= dynamic_cast<Sql_cmd_show_slave_status*>(thd->lex->m_sql_cmd);
+ DBUG_ASSERT(cmd);
+ if ((res= mysql_test_show_slave_status(stmt,
+ cmd->is_show_all_slaves_stat()))
+ == 2)
+ {
+ /* Statement and field info has already been sent */
+ DBUG_RETURN(FALSE);
+ }
+ break;
}
- break;
case SQLCOM_SHOW_MASTER_STAT:
if ((res= mysql_test_show_master_status(stmt)) == 2)
{
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index d50f9722d7e..a749713d65b 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -14205,20 +14205,23 @@ show_param:
}
| ALL SLAVES STATUS_SYM
{
+ if (!(Lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_show_slave_status(1)))
+ MYSQL_YYABORT;
Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
- Lex->verbose= 1;
}
| SLAVE STATUS_SYM
{
LEX *lex= thd->lex;
lex->mi.connection_name= null_clex_str;
+ if (!(lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_show_slave_status()))
+ MYSQL_YYABORT;
lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
- lex->verbose= 0;
}
| SLAVE connection_name STATUS_SYM
{
+ if (!(Lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_show_slave_status()))
+ MYSQL_YYABORT;
Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
- Lex->verbose= 0;
}
| CREATE PROCEDURE_SYM sp_name
{
diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy
index 75c57111dc5..3143ca47e61 100644
--- a/sql/sql_yacc_ora.yy
+++ b/sql/sql_yacc_ora.yy
@@ -14329,19 +14329,22 @@ show_param:
| ALL SLAVES STATUS_SYM
{
Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
- Lex->verbose= 1;
+ if (!(Lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_show_slave_status(1)))
+ MYSQL_YYABORT;
}
| SLAVE STATUS_SYM
{
LEX *lex= thd->lex;
lex->mi.connection_name= null_clex_str;
lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
- lex->verbose= 0;
+ if (!(lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_show_slave_status()))
+ MYSQL_YYABORT;
}
| SLAVE connection_name STATUS_SYM
{
Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
- Lex->verbose= 0;
+ if (!(Lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_show_slave_status()))
+ MYSQL_YYABORT;
}
| CREATE PROCEDURE_SYM sp_name
{
1
0
[Commits] 61ad3ef: MDEV-19820 Wrong result with multiple single column index request
by IgorBabaev 25 Jun '19
by IgorBabaev 25 Jun '19
25 Jun '19
revision-id: 61ad3efea00303e2d0cd85628c76251b08e79531 (mariadb-10.4.4-194-g61ad3ef)
parent(s): 8b576616b442d061356bc5a2abd410f478e98ee7
author: Igor Babaev
committer: Igor Babaev
timestamp: 2019-06-23 10:26:42 -0700
message:
MDEV-19820 Wrong result with multiple single column index request
The bug occured when the optimizer decided to use a rowid filter built
by a range index scan to access an InnoDB table with generated clustered
index.
When a table is accessed by a secondary index Idx employing a rowid filter the
the value of pk contained in the found index tuple is checked against the
filter. A call of the handler function position is supposed to put the
pk value into the handler::ref buffer. However for generated clustered
primary keys it did not happened. The patch fixes this problem.
---
mysql-test/main/rowid_filter_innodb.result | 47 ++++++++++++++++++++++++++++++
mysql-test/main/rowid_filter_innodb.test | 30 +++++++++++++++++++
sql/sql_select.cc | 2 +-
storage/innobase/row/row0sel.cc | 21 ++++++++++---
4 files changed, 95 insertions(+), 5 deletions(-)
diff --git a/mysql-test/main/rowid_filter_innodb.result b/mysql-test/main/rowid_filter_innodb.result
index 54c7e03..d83239e 100644
--- a/mysql-test/main/rowid_filter_innodb.result
+++ b/mysql-test/main/rowid_filter_innodb.result
@@ -2162,4 +2162,51 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
Warnings:
Note 1003 select 1 AS `id`,`test`.`t2`.`y` AS `y`,`test`.`t2`.`x` AS `x` from `test`.`t1` join `test`.`t2` where `test`.`t2`.`y` = 2 and `test`.`t2`.`x` = 1
drop table t1, t2;
+#
+# MDEV-19820: use of rowid filter for innodb table without primary key
+#
+create table t1 (a int, b int, key (b), key (a)) engine=innodb;
+insert into t1
+select (rand(1)*1000)/10, (rand(1001)*1000)/50 from seq_1_to_1000;
+analyze table t1;
+Table Op Msg_type Msg_text
+test.t1 analyze status Engine-independent statistics collected
+test.t1 analyze status OK
+set @save_optimizer_switch= @@optimizer_switch;
+set optimizer_switch='rowid_filter=off';
+select count(*) from t1 where a in (22,83,11) and b=2;
+count(*)
+6
+explain extended select count(*) from t1 where a in (22,83,11) and b=2;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 SIMPLE t1 ref b,a b 5 const 59 55.93 Using where
+Warnings:
+Note 1003 select count(0) AS `count(*)` from `test`.`t1` where `test`.`t1`.`b` = 2 and `test`.`t1`.`a` in (22,83,11)
+select * from t1 where a in (22,83,11) and b=2;
+a b
+11 2
+11 2
+83 2
+11 2
+83 2
+22 2
+set optimizer_switch='rowid_filter=on';
+select count(*) from t1 where a in (22,83,11) and b=2;
+count(*)
+6
+explain extended select count(*) from t1 where a in (22,83,11) and b=2;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 SIMPLE t1 ref|filter b,a b|a 5|5 const 59 (3%) 55.93 Using where; Using rowid filter
+Warnings:
+Note 1003 select count(0) AS `count(*)` from `test`.`t1` where `test`.`t1`.`b` = 2 and `test`.`t1`.`a` in (22,83,11)
+select * from t1 where a in (22,83,11) and b=2;
+a b
+11 2
+11 2
+83 2
+11 2
+83 2
+22 2
+drop table t1;
+set optimizer_switch=@save_optimizer_switch;
SET SESSION STORAGE_ENGINE=DEFAULT;
diff --git a/mysql-test/main/rowid_filter_innodb.test b/mysql-test/main/rowid_filter_innodb.test
index 173ba15..240cd92 100644
--- a/mysql-test/main/rowid_filter_innodb.test
+++ b/mysql-test/main/rowid_filter_innodb.test
@@ -3,6 +3,7 @@
SET SESSION STORAGE_ENGINE='InnoDB';
--source rowid_filter.test
+--source include/have_sequence.inc
--echo #
--echo # MDEV-18755: possible RORI-plan and possible plan with range filter
@@ -65,4 +66,33 @@ eval explain extended $q;
drop table t1, t2;
+--echo #
+--echo # MDEV-19820: use of rowid filter for innodb table without primary key
+--echo #
+
+create table t1 (a int, b int, key (b), key (a)) engine=innodb;
+insert into t1
+select (rand(1)*1000)/10, (rand(1001)*1000)/50 from seq_1_to_1000;
+analyze table t1;
+
+let $q=
+select count(*) from t1 where a in (22,83,11) and b=2;
+let $q1=
+select * from t1 where a in (22,83,11) and b=2;
+
+set @save_optimizer_switch= @@optimizer_switch;
+
+set optimizer_switch='rowid_filter=off';
+eval $q;
+eval explain extended $q;
+eval $q1;
+
+set optimizer_switch='rowid_filter=on';
+eval $q;
+eval explain extended $q;
+eval $q1;
+
+drop table t1;
+set optimizer_switch=@save_optimizer_switch;
+
SET SESSION STORAGE_ENGINE=DEFAULT;
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 826026a..2382789 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -5382,7 +5382,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
impossible_range= records == 0 && s->table->reginfo.impossible_range;
if (join->thd->lex->sql_command == SQLCOM_SELECT &&
optimizer_flag(join->thd, OPTIMIZER_SWITCH_USE_ROWID_FILTER))
- s->table->init_cost_info_for_usable_range_rowid_filters(join->thd);
+ s->table->init_cost_info_for_usable_range_rowid_filters(join->thd);
}
if (!impossible_range)
{
diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc
index 659a824..d796b88 100644
--- a/storage/innobase/row/row0sel.cc
+++ b/storage/innobase/row/row0sel.cc
@@ -3902,10 +3902,23 @@ row_search_idx_cond_check(
switch (result) {
case ICP_MATCH:
- if (handler_rowid_filter_is_active(prebuilt->pk_filter)
- && !handler_rowid_filter_check(prebuilt->pk_filter)) {
- MONITOR_INC(MONITOR_ICP_MATCH);
- return(ICP_NO_MATCH);
+ if (handler_rowid_filter_is_active(prebuilt->pk_filter)) {
+ if (prebuilt->clust_index_was_generated) {
+ ulint len;
+ dict_index_t* index = prebuilt->index;
+ const byte* data = rec_get_nth_field(
+ rec, offsets, index->n_fields - 1,
+ &len);
+ ut_ad(dict_index_get_nth_col(index,
+ index->n_fields - 1)
+ ->prtype == (DATA_ROW_ID | DATA_NOT_NULL));
+ ut_ad(len == DATA_ROW_ID_LEN);
+ memcpy(prebuilt->row_id, data, DATA_ROW_ID_LEN);
+ }
+ if (!handler_rowid_filter_check(prebuilt->pk_filter)) {
+ MONITOR_INC(MONITOR_ICP_MATCH);
+ return(ICP_NO_MATCH);
+ }
}
/* Convert the remaining fields to MySQL format.
If this is a secondary index record, we must defer
2
1
[Commits] 17b1f29: MDEV-15777 Use inferred IS NOT NULL predicates in the range optimizer
by IgorBabaev 24 Jun '19
by IgorBabaev 24 Jun '19
24 Jun '19
revision-id: 17b1f29666d4105089fd192d0dcfae9b679245ac (mariadb-10.3.12-226-g17b1f29)
parent(s): 7060b0320d1479bb9476e0cbd4acc584e059e1ff
author: Igor Babaev
committer: Igor Babaev
timestamp: 2019-06-23 18:52:29 -0700
message:
MDEV-15777 Use inferred IS NOT NULL predicates in the range optimizer
This patch introduces the optimization that allows range optimizer to
consider index range scans that are built employing NOT NULL predicates
inferred from WHERE conditions and ON expressions.
The patch adds a new optimizer switch not_null_range_scan that is 'off'
by default in 10.3.
---
mysql-test/main/mysqld--help.result | 3 +-
mysql-test/main/range.result | 285 +++++++++++++++++++++
mysql-test/main/range.test | 182 +++++++++++++
mysql-test/main/range_mrr_icp.result | 285 +++++++++++++++++++++
.../suite/sys_vars/r/optimizer_switch_basic.result | 36 +--
.../sys_vars/r/sysvars_server_embedded.result | 8 +-
.../sys_vars/r/sysvars_server_notembedded.result | 8 +-
sql/item.cc | 10 +
sql/item.h | 43 ++++
sql/item_cmpfunc.cc | 161 +++++++++++-
sql/item_cmpfunc.h | 11 +
sql/item_func.cc | 19 ++
sql/item_func.h | 22 ++
sql/item_row.cc | 19 ++
sql/item_row.h | 1 +
sql/opt_range.cc | 10 +
sql/sql_priv.h | 1 +
sql/sql_select.cc | 285 +++++++++++++++++++++
sql/sql_select.h | 3 +-
sql/sys_vars.cc | 1 +
sql/table.cc | 2 +
sql/table.h | 7 +
.../mysql-test/tokudb/r/ext_key_1_innodb.result | 2 +-
.../mysql-test/tokudb/r/ext_key_1_tokudb.result | 2 +-
.../mysql-test/tokudb/r/ext_key_2_innodb.result | 2 +-
.../mysql-test/tokudb/r/ext_key_2_tokudb.result | 2 +-
26 files changed, 1377 insertions(+), 33 deletions(-)
diff --git a/mysql-test/main/mysqld--help.result b/mysql-test/main/mysqld--help.result
index 1c7e9cd..19b4319 100644
--- a/mysql-test/main/mysqld--help.result
+++ b/mysql-test/main/mysqld--help.result
@@ -678,7 +678,8 @@ The following specify which files/extra groups are read (specified before remain
join_cache_hashed, join_cache_bka,
optimize_join_buffer_size, table_elimination,
extended_keys, exists_to_in, orderby_uses_equalities,
- condition_pushdown_for_derived, split_materialized
+ condition_pushdown_for_derived, split_materialized,
+ not_null_range_scan
--optimizer-use-condition-selectivity=#
Controls selectivity of which conditions the optimizer
takes into account to calculate cardinality of a partial
diff --git a/mysql-test/main/range.result b/mysql-test/main/range.result
index 32e0cf2..807ca16 100644
--- a/mysql-test/main/range.result
+++ b/mysql-test/main/range.result
@@ -3024,3 +3024,288 @@ drop table t1;
#
# End of 10.2 tests
#
+#
+# MDEV-15777: Use inferred IS NOT NULL predicates in the range optimizer
+#
+set @save_optimizer_switch= @@optimizer_switch;
+set @@optimizer_switch='not_null_range_scan=on';
+create table ten(a int);
+insert into ten values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
+create table one_k(a int);
+insert into one_k select A.a + B.a* 10 + C.a * 100 from ten A, ten B, ten C;
+create table t1 (
+id int NOT NULL,
+subset_id int DEFAULT NULL,
+PRIMARY KEY (id),
+KEY t1_subset_id (subset_id));
+create table t2 (
+id int,
+col int NOT NULL,
+key (id)
+);
+insert into t1 select a,a from one_k limit 5;
+insert into t1 select a+5,NULL from one_k limit 995;
+insert into t2 select a,a from one_k;
+analyze table t1,t2;
+Table Op Msg_type Msg_text
+test.t1 analyze status OK
+test.t2 analyze status Table is already up to date
+# expected for t1: range access and rows = 4 (not 1000)
+explain SELECT * FROM t1,t2 WHERE t1.subset_id=t2.id;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range t1_subset_id t1_subset_id 5 NULL 4 Using index condition
+1 SIMPLE t2 ref id id 5 test.t1.subset_id 1
+SELECT * FROM t1,t2 WHERE t1.subset_id=t2.id;
+id subset_id id col
+0 0 0 0
+1 1 1 1
+2 2 2 2
+3 3 3 3
+4 4 4 4
+# with a subquery
+# expected the same plan as above
+explain SELECT * FROM t1 WHERE t1.subset_id IN (SELECT t2.id FROM t2);
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 range t1_subset_id t1_subset_id 5 NULL 4 Using index condition
+1 PRIMARY t2 ref id id 5 test.t1.subset_id 1 Using index; FirstMatch(t1)
+SELECT * FROM t1 WHERE t1.subset_id IN (SELECT t2.id FROM t2);
+id subset_id
+0 0
+1 1
+2 2
+3 3
+4 4
+# non-mergable subquery
+# expected for t1: range access and rows = 4 (not 1000)
+explain SELECT * FROM t1
+WHERE t1.subset_id IN (SELECT max(t2.id) FROM t2 group by t2.col);
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 range t1_subset_id t1_subset_id 5 NULL 4 Using index condition
+1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 4 test.t1.subset_id 1
+2 MATERIALIZED t2 ALL NULL NULL NULL NULL 1000 Using temporary
+SELECT * FROM t1
+WHERE t1.subset_id IN (SELECT max(t2.id) FROM t2 group by t2.col);
+id subset_id
+0 0
+1 1
+2 2
+3 3
+4 4
+create view v1 as SELECT t2.id FROM t2;
+create view v2 as SELECT t2.id FROM t2 group by t2.col;
+# with mergeable view
+# expected for t1: range access and rows = 4 (not 1000)
+explain SELECT * FROM t1, v1 where t1.subset_id=v1.id;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range t1_subset_id t1_subset_id 5 NULL 4 Using index condition
+1 SIMPLE t2 ref id id 5 test.t1.subset_id 1 Using index
+SELECT * FROM t1, v1 where t1.subset_id=v1.id;
+id subset_id id
+0 0 0
+1 1 1
+2 2 2
+3 3 3
+4 4 4
+# with non-mergeable view
+# expected for t1: range access and rows = 4 (not 1000)
+explain SELECT * FROM t1, v2 where t1.subset_id=v2.id;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 range t1_subset_id t1_subset_id 5 NULL 4 Using index condition
+1 PRIMARY <derived2> ref key0 key0 5 test.t1.subset_id 10
+2 DERIVED t2 ALL NULL NULL NULL NULL 1000 Using temporary; Using filesort
+SELECT * FROM t1, v2 where t1.subset_id=v2.id;
+id subset_id id
+0 0 0
+1 1 1
+2 2 2
+3 3 3
+4 4 4
+# expected for t2 and for t1: range access
+explain SELECT * FROM t2 LEFT JOIN t1 ON t1.subset_id != 5 WHERE t2.id in (0,2,4);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t2 range id id 5 NULL 3 Using index condition
+1 SIMPLE t1 range t1_subset_id t1_subset_id 5 NULL 4 Using where; Using join buffer (flat, BNL join)
+SELECT * FROM t2 LEFT JOIN t1 ON t1.subset_id != 5 WHERE t2.id in (0,2,4);
+id col id subset_id
+0 0 0 0
+2 2 0 0
+4 4 0 0
+0 0 1 1
+2 2 1 1
+4 4 1 1
+0 0 2 2
+2 2 2 2
+4 4 2 2
+0 0 3 3
+2 2 3 3
+4 4 3 3
+0 0 4 4
+2 2 4 4
+4 4 4 4
+# no range access expected for t1
+explain SELECT * FROM t1 LEFT JOIN t2 ON t1.subset_id=t2.id LIMIT 10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 1000
+1 SIMPLE t2 ref id id 5 test.t1.subset_id 1 Using where
+SELECT * FROM t1 LEFT JOIN t2 ON t1.subset_id=t2.id LIMIT 10;
+id subset_id id col
+0 0 0 0
+1 1 1 1
+2 2 2 2
+3 3 3 3
+4 4 4 4
+5 NULL NULL NULL
+6 NULL NULL NULL
+7 NULL NULL NULL
+8 NULL NULL NULL
+9 NULL NULL NULL
+# expected for t1: range access
+explain SELECT * FROM ten LEFT JOIN (t1,t2) ON ten.a=t2.col AND t1.subset_id=t2.id;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE ten ALL NULL NULL NULL NULL 10
+1 SIMPLE t1 range t1_subset_id t1_subset_id 5 NULL 4 Using where
+1 SIMPLE t2 ref id id 5 test.t1.subset_id 1 Using where
+SELECT * FROM ten LEFT JOIN (t1,t2) ON ten.a=t2.col AND t1.subset_id=t2.id;
+a id subset_id id col
+0 0 0 0 0
+1 1 1 1 1
+2 2 2 2 2
+3 3 3 3 3
+4 4 4 4 4
+5 NULL NULL NULL NULL
+6 NULL NULL NULL NULL
+7 NULL NULL NULL NULL
+8 NULL NULL NULL NULL
+9 NULL NULL NULL NULL
+# no range access expected for t1
+explain SELECT * FROM t1 LEFT JOIN (t2,ten) ON ten.a=t2.col AND t1.subset_id=t2.id
+LIMIT 10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 1000
+1 SIMPLE t2 ref id id 5 test.t1.subset_id 1 Using where
+1 SIMPLE ten ALL NULL NULL NULL NULL 10 Using where
+SELECT * FROM t1 LEFT JOIN (t2,ten) ON ten.a=t2.col AND t1.subset_id=t2.id
+LIMIT 10;
+id subset_id id col a
+0 0 0 0 0
+1 1 1 1 1
+2 2 2 2 2
+3 3 3 3 3
+4 4 4 4 4
+5 NULL NULL NULL NULL
+6 NULL NULL NULL NULL
+7 NULL NULL NULL NULL
+8 NULL NULL NULL NULL
+9 NULL NULL NULL NULL
+drop index id on t2;
+# expected for t1: range access
+explain SELECT * FROM t1,t2 WHERE t1.subset_id=t2.id;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range t1_subset_id t1_subset_id 5 NULL 4 Using index condition
+1 SIMPLE t2 ALL NULL NULL NULL NULL 1000 Using where; Using join buffer (flat, BNL join)
+SELECT * FROM t1,t2 WHERE t1.subset_id=t2.id;
+id subset_id id col
+0 0 0 0
+1 1 1 1
+2 2 2 2
+3 3 3 3
+4 4 4 4
+# expected impossible where after reading const tables
+explain SELECT * FROM t1,t2 WHERE t1.subset_id > t2.id AND t1.subset_id IS NULL;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables
+SELECT * FROM t1,t2 WHERE t1.subset_id > t2.id AND t1.subset_id IS NULL;
+id subset_id id col
+# expected impossible where after reading const tables
+explain SELECT * FROM t1,t2 WHERE t1.subset_id > t2.id AND t2.id IS NULL;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables
+SELECT * FROM t1,t2 WHERE t1.subset_id > t2.id AND t2.id IS NULL;
+id subset_id id col
+drop index t1_subset_id on t1;
+alter table t1 add column m int not null default 0;
+alter table t1 add index idx(m,subset_id);
+alter table t2 add index (id);
+update t1 set m = id mod 2;
+analyze table t1,t2;
+Table Op Msg_type Msg_text
+test.t1 analyze status OK
+test.t2 analyze status Table is already up to date
+# expected for t1: range access by idx (keylen=9)
+explain SELECT * FROM t1,t2 WHERE t1.subset_id=t2.id and t1.m=0 ;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range idx idx 9 NULL 4 Using index condition
+1 SIMPLE t2 ref id id 5 test.t1.subset_id 1
+SELECT * FROM t1,t2 WHERE t1.subset_id=t2.id and t1.m=0 ;
+id subset_id m id col
+0 0 0 0 0
+2 2 0 2 2
+4 4 0 4 4
+drop view v1,v2;
+drop table t1,t2;
+create table t1 (
+id int NOT NULL,
+subset_id int DEFAULT NULL,
+KEY key1(id, subset_id),
+KEY t1_subset_id (subset_id)
+);
+create table t2 (
+id int NOT NULL,
+col int NOT NULL,
+key (id)
+);
+insert into t1 select 1,a from one_k limit 5;
+insert into t1 select 1,NULL from one_k limit 495;
+insert into t2 select a,a from one_k;
+analyze table t1,t2;
+Table Op Msg_type Msg_text
+test.t1 analyze status OK
+test.t2 analyze status Table is already up to date
+# expected for t1 :range access by index key1
+# rows 4 instead of 500
+explain SELECT * FROM t1,t2 WHERE t1.id>=1 and t1.subset_id=t2.id;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range key1,t1_subset_id key1 9 NULL 4 Using where; Using index
+1 SIMPLE t2 ref id id 4 test.t1.subset_id 1
+SELECT * FROM t1,t2 WHERE t1.id>=1 and t1.subset_id=t2.id;
+id subset_id id col
+1 0 0 0
+1 1 1 1
+1 2 2 2
+1 3 3 3
+1 4 4 4
+drop table t1,t2;
+create table t1 (id int unsigned,col int, KEY key1(id));
+create table t2 (id int unsigned,col int DEFAULT NULL,key (id));
+insert into t1 select a,2 from one_k limit 50;
+insert into t1 select NULL,2 from one_k limit 450;
+insert into t2 select a,a from one_k;
+insert into t2 select a,a from one_k;
+analyze table t1,t2;
+Table Op Msg_type Msg_text
+test.t1 analyze status OK
+test.t2 analyze status OK
+# using key1 for range access on t1 and also using index for sorting,
+# no filesort, rows should be 75 not 500
+explain SELECT * FROM t1,t2 WHERE t1.id=t2.id AND t1.col=2 ORDER BY t2.id LIMIT 10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range key1 key1 5 NULL 75 Using index condition; Using where
+1 SIMPLE t2 ref id id 5 test.t1.id 2
+SELECT * FROM t1,t2 WHERE t1.id=t2.id AND t1.col=2 ORDER BY t2.id LIMIT 10;
+id col id col
+0 2 0 0
+0 2 0 0
+1 2 1 1
+1 2 1 1
+2 2 2 2
+2 2 2 2
+3 2 3 3
+3 2 3 3
+4 2 4 4
+4 2 4 4
+drop table t1,t2;
+drop table ten,one_k;
+set @@optimizer_switch= @save_optimizer_switch;
+#
+# End of 10.3 tests
+#
diff --git a/mysql-test/main/range.test b/mysql-test/main/range.test
index bd2299b..014916a 100644
--- a/mysql-test/main/range.test
+++ b/mysql-test/main/range.test
@@ -2053,3 +2053,185 @@ drop table t1;
--echo #
--echo # End of 10.2 tests
--echo #
+
+--echo #
+--echo # MDEV-15777: Use inferred IS NOT NULL predicates in the range optimizer
+--echo #
+
+set @save_optimizer_switch= @@optimizer_switch;
+set @@optimizer_switch='not_null_range_scan=on';
+create table ten(a int);
+insert into ten values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
+
+create table one_k(a int);
+insert into one_k select A.a + B.a* 10 + C.a * 100 from ten A, ten B, ten C;
+
+create table t1 (
+ id int NOT NULL,
+ subset_id int DEFAULT NULL,
+ PRIMARY KEY (id),
+ KEY t1_subset_id (subset_id));
+
+create table t2 (
+ id int,
+ col int NOT NULL,
+ key (id)
+);
+
+insert into t1 select a,a from one_k limit 5;
+insert into t1 select a+5,NULL from one_k limit 995;
+insert into t2 select a,a from one_k;
+
+analyze table t1,t2;
+
+let $q=
+SELECT * FROM t1,t2 WHERE t1.subset_id=t2.id;
+--echo # expected for t1: range access and rows = 4 (not 1000)
+eval explain $q;
+eval $q;
+
+let $q=
+SELECT * FROM t1 WHERE t1.subset_id IN (SELECT t2.id FROM t2);
+--echo # with a subquery
+--echo # expected the same plan as above
+eval explain $q;
+eval $q;
+
+let $q=
+SELECT * FROM t1
+ WHERE t1.subset_id IN (SELECT max(t2.id) FROM t2 group by t2.col);
+--echo # non-mergable subquery
+--echo # expected for t1: range access and rows = 4 (not 1000)
+eval explain $q;
+eval $q;
+
+create view v1 as SELECT t2.id FROM t2;
+create view v2 as SELECT t2.id FROM t2 group by t2.col;
+
+let $q=
+SELECT * FROM t1, v1 where t1.subset_id=v1.id;
+--echo # with mergeable view
+--echo # expected for t1: range access and rows = 4 (not 1000)
+eval explain $q;
+eval $q;
+
+let $q= SELECT * FROM t1, v2 where t1.subset_id=v2.id;
+--echo # with non-mergeable view
+--echo # expected for t1: range access and rows = 4 (not 1000)
+eval explain $q;
+eval $q;
+
+let $q=
+SELECT * FROM t2 LEFT JOIN t1 ON t1.subset_id != 5 WHERE t2.id in (0,2,4);
+--echo # expected for t2 and for t1: range access
+eval explain $q;
+eval $q;
+
+let $q=
+SELECT * FROM t1 LEFT JOIN t2 ON t1.subset_id=t2.id LIMIT 10;
+--echo # no range access expected for t1
+eval explain $q;
+eval $q;
+
+let $q=
+SELECT * FROM ten LEFT JOIN (t1,t2) ON ten.a=t2.col AND t1.subset_id=t2.id;
+--echo # expected for t1: range access
+eval explain $q;
+eval $q;
+
+let $q=
+SELECT * FROM t1 LEFT JOIN (t2,ten) ON ten.a=t2.col AND t1.subset_id=t2.id
+LIMIT 10;
+--echo # no range access expected for t1
+eval explain $q;
+eval $q;
+
+drop index id on t2;
+
+let $q=
+SELECT * FROM t1,t2 WHERE t1.subset_id=t2.id;
+--echo # expected for t1: range access
+eval explain $q;
+eval $q;
+
+let $q=
+SELECT * FROM t1,t2 WHERE t1.subset_id > t2.id AND t1.subset_id IS NULL;
+--echo # expected impossible where after reading const tables
+eval explain $q;
+eval $q;
+
+let $q=
+SELECT * FROM t1,t2 WHERE t1.subset_id > t2.id AND t2.id IS NULL;
+--echo # expected impossible where after reading const tables
+eval explain $q;
+eval $q;
+
+drop index t1_subset_id on t1;
+alter table t1 add column m int not null default 0;
+alter table t1 add index idx(m,subset_id);
+alter table t2 add index (id);
+update t1 set m = id mod 2;
+analyze table t1,t2;
+
+let $q=
+SELECT * FROM t1,t2 WHERE t1.subset_id=t2.id and t1.m=0 ;
+--echo # expected for t1: range access by idx (keylen=9)
+eval explain $q;
+eval $q;
+
+
+drop view v1,v2;
+drop table t1,t2;
+
+create table t1 (
+ id int NOT NULL,
+ subset_id int DEFAULT NULL,
+ KEY key1(id, subset_id),
+ KEY t1_subset_id (subset_id)
+);
+
+create table t2 (
+ id int NOT NULL,
+ col int NOT NULL,
+ key (id)
+);
+
+insert into t1 select 1,a from one_k limit 5;
+insert into t1 select 1,NULL from one_k limit 495;
+insert into t2 select a,a from one_k;
+
+analyze table t1,t2;
+
+let $q=
+SELECT * FROM t1,t2 WHERE t1.id>=1 and t1.subset_id=t2.id;
+--echo # expected for t1 :range access by index key1
+--echo # rows 4 instead of 500
+eval explain $q;
+eval $q;
+
+drop table t1,t2;
+
+create table t1 (id int unsigned,col int, KEY key1(id));
+create table t2 (id int unsigned,col int DEFAULT NULL,key (id));
+insert into t1 select a,2 from one_k limit 50;
+insert into t1 select NULL,2 from one_k limit 450;
+insert into t2 select a,a from one_k;
+insert into t2 select a,a from one_k;
+
+analyze table t1,t2;
+
+let $q=
+SELECT * FROM t1,t2 WHERE t1.id=t2.id AND t1.col=2 ORDER BY t2.id LIMIT 10;
+--echo # using key1 for range access on t1 and also using index for sorting,
+--echo # no filesort, rows should be 75 not 500
+eval explain $q;
+eval $q;
+
+drop table t1,t2;
+
+drop table ten,one_k;
+set @@optimizer_switch= @save_optimizer_switch;
+
+--echo #
+--echo # End of 10.3 tests
+--echo #
diff --git a/mysql-test/main/range_mrr_icp.result b/mysql-test/main/range_mrr_icp.result
index 6b5bf33..d484d51 100644
--- a/mysql-test/main/range_mrr_icp.result
+++ b/mysql-test/main/range_mrr_icp.result
@@ -3036,4 +3036,289 @@ drop table t1;
#
# End of 10.2 tests
#
+#
+# MDEV-15777: Use inferred IS NOT NULL predicates in the range optimizer
+#
+set @save_optimizer_switch= @@optimizer_switch;
+set @@optimizer_switch='not_null_range_scan=on';
+create table ten(a int);
+insert into ten values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
+create table one_k(a int);
+insert into one_k select A.a + B.a* 10 + C.a * 100 from ten A, ten B, ten C;
+create table t1 (
+id int NOT NULL,
+subset_id int DEFAULT NULL,
+PRIMARY KEY (id),
+KEY t1_subset_id (subset_id));
+create table t2 (
+id int,
+col int NOT NULL,
+key (id)
+);
+insert into t1 select a,a from one_k limit 5;
+insert into t1 select a+5,NULL from one_k limit 995;
+insert into t2 select a,a from one_k;
+analyze table t1,t2;
+Table Op Msg_type Msg_text
+test.t1 analyze status OK
+test.t2 analyze status Table is already up to date
+# expected for t1: range access and rows = 4 (not 1000)
+explain SELECT * FROM t1,t2 WHERE t1.subset_id=t2.id;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range t1_subset_id t1_subset_id 5 NULL 4 Using index condition; Rowid-ordered scan
+1 SIMPLE t2 ref id id 5 test.t1.subset_id 1
+SELECT * FROM t1,t2 WHERE t1.subset_id=t2.id;
+id subset_id id col
+0 0 0 0
+1 1 1 1
+2 2 2 2
+3 3 3 3
+4 4 4 4
+# with a subquery
+# expected the same plan as above
+explain SELECT * FROM t1 WHERE t1.subset_id IN (SELECT t2.id FROM t2);
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 range t1_subset_id t1_subset_id 5 NULL 4 Using index condition; Rowid-ordered scan
+1 PRIMARY t2 ref id id 5 test.t1.subset_id 1 Using index; FirstMatch(t1)
+SELECT * FROM t1 WHERE t1.subset_id IN (SELECT t2.id FROM t2);
+id subset_id
+0 0
+1 1
+2 2
+3 3
+4 4
+# non-mergable subquery
+# expected for t1: range access and rows = 4 (not 1000)
+explain SELECT * FROM t1
+WHERE t1.subset_id IN (SELECT max(t2.id) FROM t2 group by t2.col);
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 range t1_subset_id t1_subset_id 5 NULL 4 Using index condition; Rowid-ordered scan
+1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 4 test.t1.subset_id 1
+2 MATERIALIZED t2 ALL NULL NULL NULL NULL 1000 Using temporary
+SELECT * FROM t1
+WHERE t1.subset_id IN (SELECT max(t2.id) FROM t2 group by t2.col);
+id subset_id
+0 0
+1 1
+2 2
+3 3
+4 4
+create view v1 as SELECT t2.id FROM t2;
+create view v2 as SELECT t2.id FROM t2 group by t2.col;
+# with mergeable view
+# expected for t1: range access and rows = 4 (not 1000)
+explain SELECT * FROM t1, v1 where t1.subset_id=v1.id;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range t1_subset_id t1_subset_id 5 NULL 4 Using index condition; Rowid-ordered scan
+1 SIMPLE t2 ref id id 5 test.t1.subset_id 1 Using index
+SELECT * FROM t1, v1 where t1.subset_id=v1.id;
+id subset_id id
+0 0 0
+1 1 1
+2 2 2
+3 3 3
+4 4 4
+# with non-mergeable view
+# expected for t1: range access and rows = 4 (not 1000)
+explain SELECT * FROM t1, v2 where t1.subset_id=v2.id;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 range t1_subset_id t1_subset_id 5 NULL 4 Using index condition; Rowid-ordered scan
+1 PRIMARY <derived2> ref key0 key0 5 test.t1.subset_id 10
+2 DERIVED t2 ALL NULL NULL NULL NULL 1000 Using temporary; Using filesort
+SELECT * FROM t1, v2 where t1.subset_id=v2.id;
+id subset_id id
+0 0 0
+1 1 1
+2 2 2
+3 3 3
+4 4 4
+# expected for t2 and for t1: range access
+explain SELECT * FROM t2 LEFT JOIN t1 ON t1.subset_id != 5 WHERE t2.id in (0,2,4);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t2 range id id 5 NULL 3 Using index condition; Rowid-ordered scan
+1 SIMPLE t1 range t1_subset_id t1_subset_id 5 NULL 4 Using where; Rowid-ordered scan; Using join buffer (flat, BNL join)
+SELECT * FROM t2 LEFT JOIN t1 ON t1.subset_id != 5 WHERE t2.id in (0,2,4);
+id col id subset_id
+0 0 0 0
+2 2 0 0
+4 4 0 0
+0 0 1 1
+2 2 1 1
+4 4 1 1
+0 0 2 2
+2 2 2 2
+4 4 2 2
+0 0 3 3
+2 2 3 3
+4 4 3 3
+0 0 4 4
+2 2 4 4
+4 4 4 4
+# no range access expected for t1
+explain SELECT * FROM t1 LEFT JOIN t2 ON t1.subset_id=t2.id LIMIT 10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 1000
+1 SIMPLE t2 ref id id 5 test.t1.subset_id 1 Using where
+SELECT * FROM t1 LEFT JOIN t2 ON t1.subset_id=t2.id LIMIT 10;
+id subset_id id col
+0 0 0 0
+1 1 1 1
+2 2 2 2
+3 3 3 3
+4 4 4 4
+5 NULL NULL NULL
+6 NULL NULL NULL
+7 NULL NULL NULL
+8 NULL NULL NULL
+9 NULL NULL NULL
+# expected for t1: range access
+explain SELECT * FROM ten LEFT JOIN (t1,t2) ON ten.a=t2.col AND t1.subset_id=t2.id;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE ten ALL NULL NULL NULL NULL 10
+1 SIMPLE t1 range t1_subset_id t1_subset_id 5 NULL 4 Using where; Rowid-ordered scan
+1 SIMPLE t2 ref id id 5 test.t1.subset_id 1 Using where
+SELECT * FROM ten LEFT JOIN (t1,t2) ON ten.a=t2.col AND t1.subset_id=t2.id;
+a id subset_id id col
+0 0 0 0 0
+1 1 1 1 1
+2 2 2 2 2
+3 3 3 3 3
+4 4 4 4 4
+5 NULL NULL NULL NULL
+6 NULL NULL NULL NULL
+7 NULL NULL NULL NULL
+8 NULL NULL NULL NULL
+9 NULL NULL NULL NULL
+# no range access expected for t1
+explain SELECT * FROM t1 LEFT JOIN (t2,ten) ON ten.a=t2.col AND t1.subset_id=t2.id
+LIMIT 10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 1000
+1 SIMPLE t2 ref id id 5 test.t1.subset_id 1 Using where
+1 SIMPLE ten ALL NULL NULL NULL NULL 10 Using where
+SELECT * FROM t1 LEFT JOIN (t2,ten) ON ten.a=t2.col AND t1.subset_id=t2.id
+LIMIT 10;
+id subset_id id col a
+0 0 0 0 0
+1 1 1 1 1
+2 2 2 2 2
+3 3 3 3 3
+4 4 4 4 4
+5 NULL NULL NULL NULL
+6 NULL NULL NULL NULL
+7 NULL NULL NULL NULL
+8 NULL NULL NULL NULL
+9 NULL NULL NULL NULL
+drop index id on t2;
+# expected for t1: range access
+explain SELECT * FROM t1,t2 WHERE t1.subset_id=t2.id;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range t1_subset_id t1_subset_id 5 NULL 4 Using index condition; Rowid-ordered scan
+1 SIMPLE t2 ALL NULL NULL NULL NULL 1000 Using where; Using join buffer (flat, BNL join)
+SELECT * FROM t1,t2 WHERE t1.subset_id=t2.id;
+id subset_id id col
+0 0 0 0
+1 1 1 1
+2 2 2 2
+3 3 3 3
+4 4 4 4
+# expected impossible where after reading const tables
+explain SELECT * FROM t1,t2 WHERE t1.subset_id > t2.id AND t1.subset_id IS NULL;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables
+SELECT * FROM t1,t2 WHERE t1.subset_id > t2.id AND t1.subset_id IS NULL;
+id subset_id id col
+# expected impossible where after reading const tables
+explain SELECT * FROM t1,t2 WHERE t1.subset_id > t2.id AND t2.id IS NULL;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables
+SELECT * FROM t1,t2 WHERE t1.subset_id > t2.id AND t2.id IS NULL;
+id subset_id id col
+drop index t1_subset_id on t1;
+alter table t1 add column m int not null default 0;
+alter table t1 add index idx(m,subset_id);
+alter table t2 add index (id);
+update t1 set m = id mod 2;
+analyze table t1,t2;
+Table Op Msg_type Msg_text
+test.t1 analyze status OK
+test.t2 analyze status Table is already up to date
+# expected for t1: range access by idx (keylen=9)
+explain SELECT * FROM t1,t2 WHERE t1.subset_id=t2.id and t1.m=0 ;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range idx idx 9 NULL 4 Using index condition; Rowid-ordered scan
+1 SIMPLE t2 ref id id 5 test.t1.subset_id 1
+SELECT * FROM t1,t2 WHERE t1.subset_id=t2.id and t1.m=0 ;
+id subset_id m id col
+0 0 0 0 0
+2 2 0 2 2
+4 4 0 4 4
+drop view v1,v2;
+drop table t1,t2;
+create table t1 (
+id int NOT NULL,
+subset_id int DEFAULT NULL,
+KEY key1(id, subset_id),
+KEY t1_subset_id (subset_id)
+);
+create table t2 (
+id int NOT NULL,
+col int NOT NULL,
+key (id)
+);
+insert into t1 select 1,a from one_k limit 5;
+insert into t1 select 1,NULL from one_k limit 495;
+insert into t2 select a,a from one_k;
+analyze table t1,t2;
+Table Op Msg_type Msg_text
+test.t1 analyze status OK
+test.t2 analyze status Table is already up to date
+# expected for t1 :range access by index key1
+# rows 4 instead of 500
+explain SELECT * FROM t1,t2 WHERE t1.id>=1 and t1.subset_id=t2.id;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range key1,t1_subset_id key1 9 NULL 4 Using where; Using index
+1 SIMPLE t2 ref id id 4 test.t1.subset_id 1
+SELECT * FROM t1,t2 WHERE t1.id>=1 and t1.subset_id=t2.id;
+id subset_id id col
+1 0 0 0
+1 1 1 1
+1 2 2 2
+1 3 3 3
+1 4 4 4
+drop table t1,t2;
+create table t1 (id int unsigned,col int, KEY key1(id));
+create table t2 (id int unsigned,col int DEFAULT NULL,key (id));
+insert into t1 select a,2 from one_k limit 50;
+insert into t1 select NULL,2 from one_k limit 450;
+insert into t2 select a,a from one_k;
+insert into t2 select a,a from one_k;
+analyze table t1,t2;
+Table Op Msg_type Msg_text
+test.t1 analyze status OK
+test.t2 analyze status OK
+# using key1 for range access on t1 and also using index for sorting,
+# no filesort, rows should be 75 not 500
+explain SELECT * FROM t1,t2 WHERE t1.id=t2.id AND t1.col=2 ORDER BY t2.id LIMIT 10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range key1 key1 5 NULL 75 Using index condition; Using where
+1 SIMPLE t2 ref id id 5 test.t1.id 2
+SELECT * FROM t1,t2 WHERE t1.id=t2.id AND t1.col=2 ORDER BY t2.id LIMIT 10;
+id col id col
+0 2 0 0
+0 2 0 0
+1 2 1 1
+1 2 1 1
+2 2 2 2
+2 2 2 2
+3 2 3 3
+3 2 3 3
+4 2 4 4
+4 2 4 4
+drop table t1,t2;
+drop table ten,one_k;
+set @@optimizer_switch= @save_optimizer_switch;
+#
+# End of 10.3 tests
+#
set optimizer_switch=@mrr_icp_extra_tmp;
diff --git a/mysql-test/suite/sys_vars/r/optimizer_switch_basic.result b/mysql-test/suite/sys_vars/r/optimizer_switch_basic.result
index 87c8379..ba40512 100644
--- a/mysql-test/suite/sys_vars/r/optimizer_switch_basic.result
+++ b/mysql-test/suite/sys_vars/r/optimizer_switch_basic.result
@@ -1,63 +1,63 @@
SET @start_global_value = @@global.optimizer_switch;
SELECT @start_global_value;
@start_global_value
-index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on
+index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,not_null_range_scan=off
select @@global.optimizer_switch;
@@global.optimizer_switch
-index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on
+index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,not_null_range_scan=off
select @@session.optimizer_switch;
@@session.optimizer_switch
-index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on
+index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,not_null_range_scan=off
show global variables like 'optimizer_switch';
Variable_name Value
-optimizer_switch index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on
+optimizer_switch index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,not_null_range_scan=off
show session variables like 'optimizer_switch';
Variable_name Value
-optimizer_switch index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on
+optimizer_switch index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,not_null_range_scan=off
select * from information_schema.global_variables where variable_name='optimizer_switch';
VARIABLE_NAME VARIABLE_VALUE
-OPTIMIZER_SWITCH index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on
+OPTIMIZER_SWITCH index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,not_null_range_scan=off
select * from information_schema.session_variables where variable_name='optimizer_switch';
VARIABLE_NAME VARIABLE_VALUE
-OPTIMIZER_SWITCH index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on
+OPTIMIZER_SWITCH index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,not_null_range_scan=off
set global optimizer_switch=10;
set session optimizer_switch=5;
select @@global.optimizer_switch;
@@global.optimizer_switch
-index_merge=off,index_merge_union=on,index_merge_sort_union=off,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,materialization=off,in_to_exists=off,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=off,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=off
+index_merge=off,index_merge_union=on,index_merge_sort_union=off,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,materialization=off,in_to_exists=off,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=off,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=off,not_null_range_scan=off
select @@session.optimizer_switch;
@@session.optimizer_switch
-index_merge=on,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=off,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,materialization=off,in_to_exists=off,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=off,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=off
+index_merge=on,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=off,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,materialization=off,in_to_exists=off,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=off,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=off,not_null_range_scan=off
set global optimizer_switch="index_merge_sort_union=on";
set session optimizer_switch="index_merge=off";
select @@global.optimizer_switch;
@@global.optimizer_switch
-index_merge=off,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,materialization=off,in_to_exists=off,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=off,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=off
+index_merge=off,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,materialization=off,in_to_exists=off,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=off,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=off,not_null_range_scan=off
select @@session.optimizer_switch;
@@session.optimizer_switch
-index_merge=off,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=off,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,materialization=off,in_to_exists=off,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=off,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=off
+index_merge=off,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=off,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,materialization=off,in_to_exists=off,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=off,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=off,not_null_range_scan=off
show global variables like 'optimizer_switch';
Variable_name Value
-optimizer_switch index_merge=off,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,materialization=off,in_to_exists=off,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=off,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=off
+optimizer_switch index_merge=off,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,materialization=off,in_to_exists=off,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=off,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=off,not_null_range_scan=off
show session variables like 'optimizer_switch';
Variable_name Value
-optimizer_switch index_merge=off,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=off,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,materialization=off,in_to_exists=off,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=off,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=off
+optimizer_switch index_merge=off,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=off,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,materialization=off,in_to_exists=off,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=off,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=off,not_null_range_scan=off
select * from information_schema.global_variables where variable_name='optimizer_switch';
VARIABLE_NAME VARIABLE_VALUE
-OPTIMIZER_SWITCH index_merge=off,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,materialization=off,in_to_exists=off,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=off,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=off
+OPTIMIZER_SWITCH index_merge=off,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,materialization=off,in_to_exists=off,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=off,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=off,not_null_range_scan=off
select * from information_schema.session_variables where variable_name='optimizer_switch';
VARIABLE_NAME VARIABLE_VALUE
-OPTIMIZER_SWITCH index_merge=off,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=off,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,materialization=off,in_to_exists=off,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=off,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=off
+OPTIMIZER_SWITCH index_merge=off,index_merge_union=off,index_merge_sort_union=on,index_merge_intersection=off,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,materialization=off,in_to_exists=off,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=off,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=off,not_null_range_scan=off
set session optimizer_switch="default";
select @@session.optimizer_switch;
@@session.optimizer_switch
-index_merge=off,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,materialization=off,in_to_exists=off,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=off,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=off
+index_merge=off,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=off,derived_merge=off,derived_with_keys=off,firstmatch=off,loosescan=off,materialization=off,in_to_exists=off,semijoin=off,partial_match_rowid_merge=off,partial_match_table_scan=off,subquery_cache=off,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=off,semijoin_with_cache=off,join_cache_incremental=off,join_cache_hashed=off,join_cache_bka=off,optimize_join_buffer_size=off,table_elimination=off,extended_keys=off,exists_to_in=off,orderby_uses_equalities=off,condition_pushdown_for_derived=off,split_materialized=off,not_null_range_scan=off
set optimizer_switch = replace(@@optimizer_switch, '=off', '=on');
Warnings:
Warning 1681 'engine_condition_pushdown=on' is deprecated and will be removed in a future release
select @@optimizer_switch;
@@optimizer_switch
-index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=on,engine_condition_pushdown=on,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=on,mrr_sort_keys=on,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on
+index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=on,engine_condition_pushdown=on,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=on,mrr_cost_based=on,mrr_sort_keys=on,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=on,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,not_null_range_scan=on
set global optimizer_switch=1.1;
ERROR 42000: Incorrect argument type to variable 'optimizer_switch'
set global optimizer_switch=1e1;
@@ -69,4 +69,4 @@ ERROR 42000: Variable 'optimizer_switch' can't be set to the value of 'foobar'
SET @@global.optimizer_switch = @start_global_value;
SELECT @@global.optimizer_switch;
@@global.optimizer_switch
-index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on
+index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,not_null_range_scan=off
diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result
index 89e5fef..714b55c 100644
--- a/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result
+++ b/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result
@@ -2715,17 +2715,17 @@ ENUM_VALUE_LIST NULL
READ_ONLY NO
COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME OPTIMIZER_SWITCH
-SESSION_VALUE index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on
-GLOBAL_VALUE index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on
+SESSION_VALUE index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,not_null_range_scan=off
+GLOBAL_VALUE index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,not_null_range_scan=off
GLOBAL_VALUE_ORIGIN COMPILE-TIME
-DEFAULT_VALUE index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on
+DEFAULT_VALUE index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,not_null_range_scan=off
VARIABLE_SCOPE SESSION
VARIABLE_TYPE FLAGSET
VARIABLE_COMMENT Fine-tune the optimizer behavior
NUMERIC_MIN_VALUE NULL
NUMERIC_MAX_VALUE NULL
NUMERIC_BLOCK_SIZE NULL
-ENUM_VALUE_LIST index_merge,index_merge_union,index_merge_sort_union,index_merge_intersection,index_merge_sort_intersection,engine_condition_pushdown,index_condition_pushdown,derived_merge,derived_with_keys,firstmatch,loosescan,materialization,in_to_exists,semijoin,partial_match_rowid_merge,partial_match_table_scan,subquery_cache,mrr,mrr_cost_based,mrr_sort_keys,outer_join_with_cache,semijoin_with_cache,join_cache_incremental,join_cache_hashed,join_cache_bka,optimize_join_buffer_size,table_elimination,extended_keys,exists_to_in,orderby_uses_equalities,condition_pushdown_for_derived,split_materialized,default
+ENUM_VALUE_LIST index_merge,index_merge_union,index_merge_sort_union,index_merge_intersection,index_merge_sort_intersection,engine_condition_pushdown,index_condition_pushdown,derived_merge,derived_with_keys,firstmatch,loosescan,materialization,in_to_exists,semijoin,partial_match_rowid_merge,partial_match_table_scan,subquery_cache,mrr,mrr_cost_based,mrr_sort_keys,outer_join_with_cache,semijoin_with_cache,join_cache_incremental,join_cache_hashed,join_cache_bka,optimize_join_buffer_size,table_elimination,extended_keys,exists_to_in,orderby_uses_equalities,condition_pushdown_for_derived,split_materialized,not_null_range_scan,default
READ_ONLY NO
COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME OPTIMIZER_USE_CONDITION_SELECTIVITY
diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result
index ca875e7..dd9d9eb 100644
--- a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result
+++ b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result
@@ -2925,17 +2925,17 @@ ENUM_VALUE_LIST NULL
READ_ONLY NO
COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME OPTIMIZER_SWITCH
-SESSION_VALUE index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on
-GLOBAL_VALUE index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on
+SESSION_VALUE index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,not_null_range_scan=off
+GLOBAL_VALUE index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,not_null_range_scan=off
GLOBAL_VALUE_ORIGIN COMPILE-TIME
-DEFAULT_VALUE index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on
+DEFAULT_VALUE index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,not_null_range_scan=off
VARIABLE_SCOPE SESSION
VARIABLE_TYPE FLAGSET
VARIABLE_COMMENT Fine-tune the optimizer behavior
NUMERIC_MIN_VALUE NULL
NUMERIC_MAX_VALUE NULL
NUMERIC_BLOCK_SIZE NULL
-ENUM_VALUE_LIST index_merge,index_merge_union,index_merge_sort_union,index_merge_intersection,index_merge_sort_intersection,engine_condition_pushdown,index_condition_pushdown,derived_merge,derived_with_keys,firstmatch,loosescan,materialization,in_to_exists,semijoin,partial_match_rowid_merge,partial_match_table_scan,subquery_cache,mrr,mrr_cost_based,mrr_sort_keys,outer_join_with_cache,semijoin_with_cache,join_cache_incremental,join_cache_hashed,join_cache_bka,optimize_join_buffer_size,table_elimination,extended_keys,exists_to_in,orderby_uses_equalities,condition_pushdown_for_derived,split_materialized,default
+ENUM_VALUE_LIST index_merge,index_merge_union,index_merge_sort_union,index_merge_intersection,index_merge_sort_intersection,engine_condition_pushdown,index_condition_pushdown,derived_merge,derived_with_keys,firstmatch,loosescan,materialization,in_to_exists,semijoin,partial_match_rowid_merge,partial_match_table_scan,subquery_cache,mrr,mrr_cost_based,mrr_sort_keys,outer_join_with_cache,semijoin_with_cache,join_cache_incremental,join_cache_hashed,join_cache_bka,optimize_join_buffer_size,table_elimination,extended_keys,exists_to_in,orderby_uses_equalities,condition_pushdown_for_derived,split_materialized,not_null_range_scan,default
READ_ONLY NO
COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME OPTIMIZER_USE_CONDITION_SELECTIVITY
diff --git a/sql/item.cc b/sql/item.cc
index 3584230..9c95611 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -3556,6 +3556,16 @@ table_map Item_field::all_used_tables() const
}
+bool Item_field::find_not_null_fields(table_map allowed)
+{
+ if (field->table->const_table)
+ return false;
+ if (!get_depended_from() && field->real_maybe_null())
+ bitmap_set_bit(&field->table->tmp_set, field->field_index);
+ return false;
+}
+
+
/*
@Note thd->fatal_error can be set in case of OOM
*/
diff --git a/sql/item.h b/sql/item.h
index 2adc111..c2dbdb1 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -1818,6 +1818,44 @@ class Item: public Value_source,
virtual bool set_fields_as_dependent_processor(void *arg) { return 0; }
/*============== End of Item processor list ======================*/
+ /*
+ Given a condition P from the WHERE clause or from an ON expression of
+ the processed SELECT S and a set of join tables from S marked in the
+ parameter 'allowed'={T} a call of P->find_not_null_fields({T}) has to
+ find the set fields {F} of the tables from 'allowed' such that:
+ - each field from {F} is declared as nullable
+ - each record of table t from {T} that contains NULL as the value for at
+ at least one field from {F} can be ignored when building the result set
+ for S
+ It is assumed here that the condition P is conjunctive and all its column
+ references belong to T.
+
+ Examples:
+ CREATE TABLE t1 (a int, b int);
+ CREATE TABLE t2 (a int, b int);
+
+ SELECT * FROM t1,t2 WHERE t1.a=t2.a and t1.b > 5;
+ A call of find_not_null_fields() for the whole WHERE condition and {t1,t2}
+ should find {t1.a,t1.b,t2.a}
+
+ SELECT * FROM t1 LEFT JOIN ON (t1.a=t2.a and t2.a > t2.b);
+ A call of find_not_null_fields() for the ON expression and {t2}
+ should find {t2.a,t2.b}
+
+ The function returns TRUE if it succeeds to prove that all records of
+ a table from {T} can be ignored. Otherwise it always returns FALSE.
+
+ Example:
+ SELECT * FROM t1,t2 WHERE t1.a=t2.a AND t2.a IS NULL;
+ A call of find_not_null_fields() for the WHERE condition and {t1,t2}
+ will return TRUE.
+
+ It is assumed that the implementation of this virtual function saves
+ the info on the found set of fields in the structures associates with
+ tables from {T}.
+ */
+ virtual bool find_not_null_fields(table_map allowed) { return false; }
+
virtual Item *get_copy(THD *thd)=0;
bool cache_const_expr_analyzer(uchar **arg);
@@ -3079,6 +3117,7 @@ class Item_field :public Item_ident,
bool is_result_field() { return false; }
void save_in_result_field(bool no_conversions);
Item *get_tmp_table_item(THD *thd);
+ bool find_not_null_fields(table_map allowed);
bool collect_item_field_processor(void * arg);
bool add_field_to_set_processor(void * arg);
bool find_item_in_field_list_processor(void *arg);
@@ -4858,6 +4897,10 @@ class Item_ref :public Item_ident
{
return depended_from ? 0 : (*ref)->not_null_tables();
}
+ bool find_not_null_fields(table_map allowed)
+ {
+ return depended_from ? false : (*ref)->find_not_null_fields(allowed);
+ }
void save_in_result_field(bool no_conversions)
{
(*ref)->save_in_field(result_field, no_conversions);
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 49e0a94..c4b43a7 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -1192,6 +1192,15 @@ bool Item_in_optimizer::eval_not_null_tables(void *opt_arg)
}
+bool Item_in_optimizer::find_not_null_fields(table_map allowed)
+{
+ if (!(~allowed & used_tables()) && is_top_level_item())
+ {
+ return args[0]->find_not_null_fields(allowed);
+ }
+ return false;
+}
+
void Item_in_optimizer::print(String *str, enum_query_type query_type)
{
restore_first_argument();
@@ -2035,7 +2044,17 @@ bool Item_func_between::eval_not_null_tables(void *opt_arg)
(args[1]->not_null_tables() &
args[2]->not_null_tables()));
return 0;
-}
+}
+
+
+bool Item_func_between::find_not_null_fields(table_map allowed)
+{
+ if (negated || !is_top_level_item() || (~allowed & used_tables()))
+ return false;
+ return args[0]->find_not_null_fields(allowed) ||
+ args[1]->find_not_null_fields(allowed) ||
+ args[3]->find_not_null_fields(allowed);
+}
bool Item_func_between::count_sargable_conds(void *arg)
@@ -4151,6 +4170,15 @@ Item_func_in::eval_not_null_tables(void *opt_arg)
}
+bool
+Item_func_in::find_not_null_fields(table_map allowed)
+{
+ if (negated || !is_top_level_item() || (~allowed & used_tables()))
+ return 0;
+ return args[0]->find_not_null_fields(allowed);
+}
+
+
void Item_func_in::fix_after_pullout(st_select_lex *new_parent, Item **ref,
bool merge)
{
@@ -4705,6 +4733,82 @@ Item_cond::eval_not_null_tables(void *opt_arg)
}
+/**
+ @note
+ This implementation of the virtual function find_not_null_fields()
+ infers null-rejectedness if fields from tables marked in 'allowed' from
+ this condition.
+ Currently only top level AND conjuncts that are not disjunctions are used
+ for the inference. Usage of any top level and-or formula with l OR levels
+ would require a stack of bitmaps for fields of the height h=2*l+1 So we
+ would have to allocate h-1 additional field bitmaps for each table marked
+ in 'allowed'.
+*/
+
+bool
+Item_cond::find_not_null_fields(table_map allowed)
+{
+ Item *item;
+ bool is_and_cond= functype() == Item_func::COND_AND_FUNC;
+ if (!is_and_cond)
+ {
+ /* Now only fields of top AND level conjuncts are taken into account */
+ return false;
+ }
+ uint isnull_func_cnt= 0;
+ List_iterator<Item> li(list);
+ while ((item=li++))
+ {
+ bool is_mult_eq= item->type() == Item::FUNC_ITEM &&
+ ((Item_func *) item)->functype() == Item_func::MULT_EQUAL_FUNC;
+ if (is_mult_eq)
+ {
+ if (!item->find_not_null_fields(allowed))
+ continue;
+ }
+
+ if (~allowed & item->used_tables())
+ continue;
+
+ /* It is assumed that all constant conjuncts are already eliminated */
+
+ /*
+ First infer null-rejectedness of fields from all conjuncts but
+ IS NULL predicates
+ */
+ bool isnull_func= item->type() == Item::FUNC_ITEM &&
+ ((Item_func *) item)->functype() == Item_func::ISNULL_FUNC;
+ if (isnull_func)
+ {
+ isnull_func_cnt++;
+ continue;
+ }
+ if (!item->find_not_null_fields(allowed))
+ continue;
+ }
+
+ /* Now try no get contradictions using IS NULL conjuncts */
+ if (isnull_func_cnt)
+ {
+ li.rewind();
+ while ((item=li++) && isnull_func_cnt)
+ {
+ if (~allowed & item->used_tables())
+ continue;
+
+ bool isnull_func= item->type() == Item::FUNC_ITEM &&
+ ((Item_func *) item)->functype() == Item_func::ISNULL_FUNC;
+ if (isnull_func)
+ {
+ if (item->find_not_null_fields(allowed))
+ return true;
+ isnull_func_cnt--;
+ }
+ }
+ }
+ return false;
+}
+
void Item_cond::fix_after_pullout(st_select_lex *new_parent, Item **ref,
bool merge)
{
@@ -5148,6 +5252,19 @@ longlong Item_func_isnull::val_int()
}
+bool Item_func_isnull::find_not_null_fields(table_map allowed)
+{
+ if (!(~allowed & used_tables()) &&
+ args[0]->real_item()->type() == Item::FIELD_ITEM)
+ {
+ Field *field= ((Item_field *)(args[0]->real_item()))->field;
+ if (bitmap_is_set(&field->table->tmp_set, field->field_index))
+ return true;
+ }
+ return false;
+}
+
+
void Item_func_isnull::print(String *str, enum_query_type query_type)
{
if (const_item() && !args[0]->maybe_null &&
@@ -6734,6 +6851,48 @@ void Item_equal::update_used_tables()
}
+/**
+ @note
+ This multiple equality can contains elements belonging not to tables {T}
+ marked in 'allowed' . So we can ascertain null-rejectedness of field f
+ belonging to table t from {T} only if one of the following equality
+ predicate can be extracted from this multiple equality:
+ - f=const
+ - f=f' where f' is a field of some table from {T}
+*/
+
+bool Item_equal::find_not_null_fields(table_map allowed)
+{
+ if (!(allowed & used_tables()))
+ return false;
+ bool checked= false;
+ Item_equal_fields_iterator it(*this);
+ Item *item;
+ while ((item= it++))
+ {
+ if (~allowed & item->used_tables())
+ continue;
+ if ((with_const || checked) && !item->find_not_null_fields(allowed))
+ continue;
+ Item_equal_fields_iterator it1(*this);
+ Item *item1;
+ while ((item1= it1++) && item1 != item)
+ {
+ if (~allowed & item1->used_tables())
+ continue;
+ if (!item->find_not_null_fields(allowed) &&
+ !item1->find_not_null_fields(allowed))
+ {
+ checked= true;
+ break;
+ }
+ }
+ }
+ return false;
+}
+
+
+
bool Item_equal::count_sargable_conds(void *arg)
{
SELECT_LEX *sel= (SELECT_LEX *) arg;
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index 7d99cbd..8f01e4f 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -284,6 +284,7 @@ class Item_func_isnottrue : public Item_func_truth
Item_func_truth(thd, a, true, false) {}
~Item_func_isnottrue() {}
virtual const char* func_name() const { return "isnottrue"; }
+ bool find_not_null_fields(table_map allowed) { return false; }
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_isnottrue>(thd, this); }
};
@@ -315,6 +316,7 @@ class Item_func_isnotfalse : public Item_func_truth
Item_func_truth(thd, a, false, false) {}
~Item_func_isnotfalse() {}
virtual const char* func_name() const { return "isnotfalse"; }
+ bool find_not_null_fields(table_map allowed) { return false; }
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_isnotfalse>(thd, this); }
};
@@ -376,6 +378,7 @@ class Item_in_optimizer: public Item_bool_func
virtual void get_cache_parameters(List<Item> ¶meters);
bool is_top_level_item();
bool eval_not_null_tables(void *opt_arg);
+ bool find_not_null_fields(table_map allowed);
void fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge);
bool invisible_mode();
void reset_cache() { cache= NULL; }
@@ -571,6 +574,7 @@ class Item_func_xor :public Item_bool_func
void print(String *str, enum_query_type query_type)
{ Item_func::print_op(str, query_type); }
longlong val_int();
+ bool find_not_null_fields(table_map allowed) { return false; }
Item *neg_transformer(THD *thd);
Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond)
{
@@ -592,6 +596,7 @@ class Item_func_not :public Item_bool_func
longlong val_int();
enum Functype functype() const { return NOT_FUNC; }
const char *func_name() const { return "not"; }
+ bool find_not_null_fields(table_map allowed) { return false; }
enum precedence precedence() const { return BANG_PRECEDENCE; }
Item *neg_transformer(THD *thd);
bool fix_fields(THD *, Item **);
@@ -736,6 +741,7 @@ class Item_func_equal :public Item_bool_rowready_func2
longlong val_int();
bool fix_length_and_dec();
table_map not_null_tables() const { return 0; }
+ bool find_not_null_fields(table_map allowed) { return false; }
enum Functype functype() const { return EQUAL_FUNC; }
enum Functype rev_functype() const { return EQUAL_FUNC; }
cond_result eq_cmp_result() const { return COND_TRUE; }
@@ -912,6 +918,7 @@ class Item_func_between :public Item_func_opt_neg
bool fix_length_and_dec_numeric(THD *);
virtual void print(String *str, enum_query_type query_type);
bool eval_not_null_tables(void *opt_arg);
+ bool find_not_null_fields(table_map allowed);
void fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge);
bool count_sargable_conds(void *arg);
void add_key_fields(JOIN *join, KEY_FIELD **key_fields,
@@ -2396,6 +2403,7 @@ class Item_func_in :public Item_func_opt_neg,
const char *func_name() const { return "in"; }
enum precedence precedence() const { return CMP_PRECEDENCE; }
bool eval_not_null_tables(void *opt_arg);
+ bool find_not_null_fields(table_map allowed);
void fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge);
bool count_sargable_conds(void *arg);
Item *get_copy(THD *thd)
@@ -2533,6 +2541,7 @@ class Item_func_isnull :public Item_func_null_predicate
COND *remove_eq_conds(THD *thd, Item::cond_result *cond_value,
bool top_level);
table_map not_null_tables() const { return 0; }
+ bool find_not_null_fields(table_map allowed);
Item *neg_transformer(THD *thd);
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_isnull>(thd, this); }
@@ -2953,6 +2962,7 @@ class Item_cond :public Item_bool_func
Item *compile(THD *thd, Item_analyzer analyzer, uchar **arg_p,
Item_transformer transformer, uchar *arg_t);
bool eval_not_null_tables(void *opt_arg);
+ bool find_not_null_fields(table_map allowed);
Item *build_clone(THD *thd);
bool excl_dep_on_table(table_map tab_map);
bool excl_dep_on_grouping_fields(st_select_lex *sel);
@@ -3119,6 +3129,7 @@ class Item_equal: public Item_bool_func
eval_item= NULL;
}
void update_used_tables();
+ bool find_not_null_fields(table_map allowed);
COND *build_equal_items(THD *thd, COND_EQUAL *inherited,
bool link_item_fields,
COND_EQUAL **cond_equal_ref);
diff --git a/sql/item_func.cc b/sql/item_func.cc
index a030d2f..41bbd2e 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -403,6 +403,25 @@ Item_func::eval_not_null_tables(void *opt_arg)
}
+bool
+Item_func::find_not_null_fields(table_map allowed)
+{
+ if (~allowed & used_tables())
+ return false;
+
+ Item **arg,**arg_end;
+ if (arg_count)
+ {
+ for (arg=args, arg_end=args+arg_count; arg != arg_end ; arg++)
+ {
+ if (!(*arg)->find_not_null_fields(allowed))
+ continue;
+ }
+ }
+ return false;
+}
+
+
void Item_func::fix_after_pullout(st_select_lex *new_parent, Item **ref,
bool merge)
{
diff --git a/sql/item_func.h b/sql/item_func.h
index 6345dd4..325a796 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -210,6 +210,7 @@ class Item_func :public Item_func_or_sum
void traverse_cond(Cond_traverser traverser,
void * arg, traverse_order order);
bool eval_not_null_tables(void *opt_arg);
+ bool find_not_null_fields(table_map allowed);
// bool is_expensive_processor(void *arg);
// virtual bool is_expensive() { return 0; }
inline void raise_numeric_overflow(const char *type_name)
@@ -671,6 +672,7 @@ class Item_func_case_expression: public Item_func_hybrid_field_type
Item_func_case_expression(THD *thd, List<Item> &list):
Item_func_hybrid_field_type(thd, list)
{ }
+ bool find_not_null_fields(table_map allowed) { return false; }
};
@@ -1759,6 +1761,10 @@ class Item_func_coercibility :public Item_long_func
not_null_tables_cache= 0;
return false;
}
+ bool find_not_null_fields(table_map allowed)
+ {
+ return false;
+ }
Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond)
{ return this; }
bool const_item() const { return true; }
@@ -2119,6 +2125,10 @@ class Item_udf_func :public Item_func
not_null_tables_cache= 0;
return 0;
}
+ bool find_not_null_fields(table_map allowed)
+ {
+ return false;
+ }
bool is_expensive() { return 1; }
virtual void print(String *str, enum_query_type query_type);
bool check_vcol_func_processor(void *arg)
@@ -2711,6 +2721,10 @@ class Item_func_match :public Item_real_func
not_null_tables_cache= 0;
return 0;
}
+ bool find_not_null_fields(table_map allowed)
+ {
+ return false;
+ }
bool fix_fields(THD *thd, Item **ref);
bool eq(const Item *, bool binary_cmp) const;
/* The following should be safe, even if we compare doubles */
@@ -3000,6 +3014,10 @@ class Item_func_sp :public Item_func,
not_null_tables_cache= 0;
return 0;
}
+ bool find_not_null_fields(table_map allowed)
+ {
+ return false;
+ }
};
@@ -3103,6 +3121,10 @@ class Item_func_last_value :public Item_func
not_null_tables_cache= 0;
return 0;
}
+ bool find_not_null_fields(table_map allowed)
+ {
+ return false;
+ }
bool const_item() const { return 0; }
void evaluate_sideeffects();
void update_used_tables()
diff --git a/sql/item_row.cc b/sql/item_row.cc
index 70a9fe5..0b55ae5 100644
--- a/sql/item_row.cc
+++ b/sql/item_row.cc
@@ -87,6 +87,25 @@ Item_row::eval_not_null_tables(void *opt_arg)
}
+bool
+Item_row::find_not_null_fields(table_map allowed)
+{
+ if (~allowed & used_tables())
+ return false;
+
+ Item **arg,**arg_end;
+ if (arg_count)
+ {
+ for (arg= args, arg_end= args + arg_count; arg != arg_end ; arg++)
+ {
+ if (!(*arg)->find_not_null_fields(allowed))
+ continue;
+ }
+ }
+ return false;
+}
+
+
void Item_row::cleanup()
{
DBUG_ENTER("Item_row::cleanup");
diff --git a/sql/item_row.h b/sql/item_row.h
index 0d6a6db..0efa29f 100644
--- a/sql/item_row.h
+++ b/sql/item_row.h
@@ -110,6 +110,7 @@ class Item_row: public Item,
}
Item *transform(THD *thd, Item_transformer transformer, uchar *arg);
bool eval_not_null_tables(void *opt_arg);
+ bool find_not_null_fields(table_map allowed);
uint cols() const { return arg_count; }
Item* element_index(uint i) { return args[i]; }
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index ec7b3db..91103a5 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -2399,6 +2399,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
{
uint idx;
double scan_time;
+ Item *notnull_cond= NULL;
DBUG_ENTER("SQL_SELECT::test_quick_select");
DBUG_PRINT("enter",("keys_to_use: %lu prev_tables: %lu const_tables: %lu",
(ulong) keys_to_use.to_ulonglong(), (ulong) prev_tables,
@@ -2412,6 +2413,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
if (keys_to_use.is_clear_all() || head->is_filled_at_execution())
DBUG_RETURN(0);
records= head->stat_records();
+ notnull_cond= head->notnull_cond;
if (!records)
records++; /* purecov: inspected */
scan_time= (double) records / TIME_FOR_COMPARE + 1;
@@ -2419,7 +2421,10 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
if (head->force_index)
scan_time= read_time= DBL_MAX;
if (limit < records)
+ {
read_time= (double) records + scan_time + 1; // Force to use index
+ notnull_cond= NULL;
+ }
possible_keys.clear_all();
@@ -2431,6 +2436,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
uchar buff[STACK_BUFF_ALLOC];
MEM_ROOT alloc;
SEL_TREE *tree= NULL;
+ SEL_TREE *notnull_cond_tree= NULL;
KEY_PART *key_parts;
KEY *key_info;
PARAM param;
@@ -2539,6 +2545,9 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
TRP_GROUP_MIN_MAX *group_trp;
double best_read_time= read_time;
+ if (notnull_cond)
+ notnull_cond_tree= notnull_cond->get_mm_tree(¶m, ¬null_cond);
+
if (cond)
{
if ((tree= cond->get_mm_tree(¶m, &cond)))
@@ -2557,6 +2566,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
tree= NULL;
}
}
+ tree= tree_and(¶m, tree, notnull_cond_tree);
/*
Try to construct a QUICK_GROUP_MIN_MAX_SELECT.
diff --git a/sql/sql_priv.h b/sql/sql_priv.h
index 2f37e11..ec7b2cc 100644
--- a/sql/sql_priv.h
+++ b/sql/sql_priv.h
@@ -222,6 +222,7 @@
#define OPTIMIZER_SWITCH_ORDERBY_EQ_PROP (1ULL << 29)
#define OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_DERIVED (1ULL << 30)
#define OPTIMIZER_SWITCH_SPLIT_MATERIALIZED (1ULL << 31)
+#define OPTIMIZER_SWITCH_NOT_NULL_RANGE_SCAN (1ULL << 32)
#define OPTIMIZER_SWITCH_DEFAULT (OPTIMIZER_SWITCH_INDEX_MERGE | \
OPTIMIZER_SWITCH_INDEX_MERGE_UNION | \
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 0fbc4ba..fe2c54c 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -295,6 +295,14 @@ void set_postjoin_aggr_write_func(JOIN_TAB *tab);
static Item **get_sargable_cond(JOIN *join, TABLE *table);
+static
+bool build_notnull_conds_for_range_scans(JOIN *join, COND *cond,
+ table_map allowed);
+static
+void build_notnull_conds_for_inner_nest_of_outer_join(JOIN *join,
+ TABLE_LIST *nest_tbl);
+
+
#ifndef DBUG_OFF
/*
@@ -4907,6 +4915,9 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
}
}
+ join->join_tab= stat;
+ join->make_notnull_conds_for_range_scans();
+
/* Calc how many (possible) matched records in each table */
for (s=stat ; s < stat_end ; s++)
@@ -27572,6 +27583,280 @@ Item *remove_pushed_top_conjuncts(THD *thd, Item *cond)
return cond;
}
+
+/**
+ @brief
+ Construct not null conditions for provingly not nullable fields
+
+ @details
+ For each non-constant joined table the function creates a conjunction
+ of IS NOT NULL predicates containing a predicate for each field used
+ in the WHERE clause or an OR expression such that
+ - is declared as nullable
+ - for which it can proved be that it is null-rejected
+ - is a part of some index.
+ This conjunction could be anded with either the WHERE condition or with
+ an ON expression and the modified join query would produce the same
+ result set as the original one.
+ If a conjunction of IS NOT NULL predicates is constructed for an inner
+ table of an outer join OJ that is not an inner table of embedded outer
+ joins then it is to be anded with the ON expression of OJ.
+ The constructed conjunctions of IS NOT NULL predicates are attached
+ to the corresponding tables. They used for range analysis complementary
+ to other sargable range conditions.
+
+ @note
+ Let f be a field of the joined table t. In the context of the upper
+ paragraph field f is called null-rejected if any the following holds:
+
+ - t is a table of a top inner join and a conjunctive formula that rejects
+ rows with null values for f can be extracted from the WHERE condition
+
+ - t is an outer table of a top outer join operation and a conjunctive
+ formula over the outer tables of the outer join that rejects rows with
+ null values for can be extracted from the WHERE condition
+
+ - t is an outer table of a non-top outer join operation and a conjunctive
+ formula over the outer tables of the outer join that rejects rows with
+ null values for f can be extracted from the ON expression of the
+ embedding outer join
+
+ - the joined table is an inner table of a outer join operation and
+ a conjunctive formula over inner tables of the outer join that rejects
+ rows with null values for f can be extracted from the ON expression of
+ the outer join operation.
+
+ It is assumed above that all inner join nests have been eliminated and
+ that all possible conversions of outer joins into inner joins have been
+ already done.
+*/
+
+void JOIN::make_notnull_conds_for_range_scans()
+{
+ DBUG_ENTER("JOIN::make_notnull_conds_for_range_scans");
+
+
+ if (impossible_where ||
+ !optimizer_flag(thd, OPTIMIZER_SWITCH_NOT_NULL_RANGE_SCAN))
+ {
+ /* Complementary range analysis is not needed */
+ DBUG_VOID_RETURN;
+ }
+
+ if (conds && build_notnull_conds_for_range_scans(this, conds,
+ conds->used_tables()))
+ {
+ Item *false_cond= new (thd->mem_root) Item_int(thd, (longlong) 0, 1);
+ if (false_cond)
+ {
+ /*
+ Found a IS NULL conjunctive predicate for a null-rejected field
+ in the WHERE clause
+ */
+ conds= false_cond;
+ cond_equal= 0;
+ impossible_where= true;
+ }
+ DBUG_VOID_RETURN;
+ }
+
+ List_iterator<TABLE_LIST> li(*join_list);
+ TABLE_LIST *tbl;
+ while ((tbl= li++))
+ {
+ if (tbl->on_expr)
+ {
+ if (tbl->nested_join)
+ {
+ build_notnull_conds_for_inner_nest_of_outer_join(this, tbl);
+ }
+ else if (build_notnull_conds_for_range_scans(this, tbl->on_expr,
+ tbl->table->map))
+ {
+ /*
+ Found a IS NULL conjunctive predicate for a null-rejected field
+ of the inner table of an outer join with ON expression tbl->on_expr
+ */
+ Item *false_cond= new (thd->mem_root) Item_int(thd, (longlong) 0, 1);
+ if (false_cond)
+ tbl->on_expr= false_cond;
+ }
+ }
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ @brief
+ Build not null conditions for range scans of given join tables
+
+ @param join the join for whose tables not null conditions are to be built
+ @param cond the condition from which not null predicates are to be inferred
+ @param allowed the bit map of join tables to be taken into account
+
+ @details
+ For each join table t from the 'allowed' set of tables the function finds
+ all fields whose null-rejectedness can be inferred from null-rejectedness
+ of the condition cond. For each found field f from table t such that it
+ participates at least in one index on table t a NOT NULL predicate is
+ constructed and a conjunction of all such predicates is attached to t.
+ If when looking for null-rejecting fields of t it is discovered one of its
+ fields has to be null-rejected and there is IS NULL conjunctive top level
+ predicate for this field then the function immediately returns true.
+ The function uses the bitmap TABLE::tmp_set to mark found null-rejected
+ fields of tale t.
+
+ @note
+ Currently only top level conjuncts without disjunctive sub-formulas are
+ are taken into account when looking for null-rejected fields.
+
+ @retval
+ true if a contradiction is inferred
+ false otherwise
+*/
+
+static
+bool build_notnull_conds_for_range_scans(JOIN *join, Item *cond,
+ table_map allowed)
+{
+ THD *thd= join->thd;
+
+ DBUG_ENTER("build_notnull_conds_for_range_scans");
+
+ for (JOIN_TAB *s= join->join_tab + join->const_tables ;
+ s < join->join_tab + join->table_count ; s++)
+ {
+ /* Clear all needed bitmaps to mark found fields */
+ if (allowed & s->table->map)
+ bitmap_clear_all(&s->table->tmp_set);
+ }
+
+ /*
+ Find all null-rejected fields assuming that cond is null-rejected and
+ only formulas over tables from 'allowed' are to be taken into account
+ */
+ if (cond->find_not_null_fields(allowed))
+ DBUG_RETURN(true);
+
+ /*
+ For each table t from 'allowed' build a conjunction of NOT NULL predicates
+ constructed for all found fields if they are included in some indexes.
+ If the construction of the conjunction succeeds attach the formula to
+ t->table->notnull_cond. The condition will be used to look for complementary
+ range scans.
+ */
+ for (JOIN_TAB *s= join->join_tab + join->const_tables ;
+ s < join->join_tab + join->table_count ; s++)
+ {
+ TABLE *tab= s->table;
+ List<Item> notnull_list;
+ Item *notnull_cond= 0;
+
+ if (!(allowed & tab->map))
+ continue;
+
+ for (Field** field_ptr= tab->field; *field_ptr; field_ptr++)
+ {
+ Field *field= *field_ptr;
+ if (field->part_of_key.is_clear_all())
+ continue;
+ if (!bitmap_is_set(&tab->tmp_set, field->field_index))
+ continue;
+ Item_field *field_item= new (thd->mem_root) Item_field(thd, field);
+ if (!field_item)
+ continue;
+ Item *isnotnull_item=
+ new (thd->mem_root) Item_func_isnotnull(thd, field_item);
+ if (!isnotnull_item)
+ continue;
+ if (notnull_list.push_back(isnotnull_item, thd->mem_root))
+ continue;
+ s->const_keys.merge(field->part_of_key);
+ }
+
+ switch (notnull_list.elements) {
+ case 0:
+ break;
+ case 1:
+ notnull_cond= notnull_list.head();
+ break;
+ default:
+ notnull_cond=
+ new (thd->mem_root) Item_cond_and(thd, notnull_list);
+ }
+ if (notnull_cond && !notnull_cond->fix_fields(thd, 0))
+ {
+ tab->notnull_cond= notnull_cond;
+ }
+ }
+ DBUG_RETURN(false);
+}
+
+
+/**
+ @brief
+ Build not null conditions for inner nest tables of an outer join
+
+ @param join the join for whose table nest not null conditions are to be built
+ @param nest_tbl the nest of the inner tables of an outer join
+
+ @details
+ The function assumes that nest_tbl is the nest of the inner tables of an
+ outer join and so an ON expression for this outer join is attached to
+ nest_tbl.
+ The function selects the tables of the nest_tbl that are not inner tables of
+ embedded outer joins and then it calls build_notnull_conds_for_range_scans()
+ for nest_tbl->on_expr and the bitmap for the selected tables. This call
+ finds all fields belonging to the selected tables whose null-rejectedness
+ can be inferred from the null-rejectedness of nest_tbl->on_expr. After this
+ the function recursively finds all null_rejected fields for the remaining
+ tables from the nest of nest_tbl.
+*/
+
+static
+void build_notnull_conds_for_inner_nest_of_outer_join(JOIN *join,
+ TABLE_LIST *nest_tbl)
+{
+ TABLE_LIST *tbl;
+ table_map used_tables= 0;
+ THD *thd= join->thd;
+ List_iterator<TABLE_LIST> li(nest_tbl->nested_join->join_list);
+
+ while ((tbl= li++))
+ {
+ if (!tbl->on_expr)
+ used_tables|= tbl->table->map;
+ }
+ if (used_tables &&
+ build_notnull_conds_for_range_scans(join, nest_tbl->on_expr, used_tables))
+ {
+ Item *false_cond= new (thd->mem_root) Item_int(thd, (longlong) 0, 1);
+ if (false_cond)
+ nest_tbl->on_expr= false_cond;
+ }
+
+ li.rewind();
+ while ((tbl= li++))
+ {
+ if (tbl->on_expr)
+ {
+ if (tbl->nested_join)
+ {
+ build_notnull_conds_for_inner_nest_of_outer_join(join, tbl);
+ }
+ else if (build_notnull_conds_for_range_scans(join, tbl->on_expr,
+ tbl->table->map))
+ {
+ Item *false_cond= new (thd->mem_root) Item_int(thd, (longlong) 0, 1);
+ if (false_cond)
+ tbl->on_expr= false_cond;
+ }
+ }
+ }
+}
+
+
/**
@} (end of group Query_Optimizer)
*/
diff --git a/sql/sql_select.h b/sql/sql_select.h
index dd823a7..2b40a41 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -1749,6 +1749,7 @@ class JOIN :public Sql_alloc
void add_keyuses_for_splitting();
bool inject_best_splitting_cond(table_map remaining_tables);
bool fix_all_splittings_in_plan();
+ void make_notnull_conds_for_range_scans();
bool transform_in_predicates_into_in_subq(THD *thd);
private:
@@ -2343,7 +2344,7 @@ Item_equal *find_item_equal(COND_EQUAL *cond_equal, Field *field,
extern bool test_if_ref(Item *,
Item_field *left_item,Item *right_item);
-inline bool optimizer_flag(THD *thd, uint flag)
+inline bool optimizer_flag(THD *thd, ulonglong flag)
{
return (thd->variables.optimizer_switch & flag);
}
diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
index b50f697..58b6da5 100644
--- a/sql/sys_vars.cc
+++ b/sql/sys_vars.cc
@@ -2544,6 +2544,7 @@ export const char *optimizer_switch_names[]=
"orderby_uses_equalities",
"condition_pushdown_for_derived",
"split_materialized",
+ "not_null_range_scan",
"default",
NullS
};
diff --git a/sql/table.cc b/sql/table.cc
index 4805017..34fb6d5 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -4658,6 +4658,8 @@ void TABLE::init(THD *thd, TABLE_LIST *tl)
(*f_ptr)->cond_selectivity= 1.0;
}
+ notnull_cond= 0;
+
DBUG_ASSERT(!file->keyread_enabled());
restore_record(this, s->default_values);
diff --git a/sql/table.h b/sql/table.h
index fa6cb70..47096aa 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -1344,6 +1344,13 @@ struct TABLE
SplM_opt_info *spl_opt_info;
key_map keys_usable_for_splitting;
+ /*
+ Conjunction of the predicates of the form IS NOT NULL(f) where f refers to
+ a column of this TABLE such that they can be inferred from the condition
+ of the WHERE clause or from some ON expression of the processed select
+ and can be useful for range optimizer.
+ */
+ Item *notnull_cond;
inline void reset() { bzero((void*)this, sizeof(*this)); }
void init(THD *thd, TABLE_LIST *tl);
diff --git a/storage/tokudb/mysql-test/tokudb/r/ext_key_1_innodb.result b/storage/tokudb/mysql-test/tokudb/r/ext_key_1_innodb.result
index a80e166..5d471d2 100644
--- a/storage/tokudb/mysql-test/tokudb/r/ext_key_1_innodb.result
+++ b/storage/tokudb/mysql-test/tokudb/r/ext_key_1_innodb.result
@@ -1,7 +1,7 @@
drop table if exists t;
select @@optimizer_switch;
@@optimizer_switch
-index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on
+index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,not_null_range_scan=off
create table t (id int not null, x int not null, y int not null, primary key(id), key(x)) engine=innodb;
insert into t values (0,0,0),(1,1,1),(2,2,2),(3,2,3),(4,2,4);
explain select x,id from t force index (x) where x=0 and id=0;
diff --git a/storage/tokudb/mysql-test/tokudb/r/ext_key_1_tokudb.result b/storage/tokudb/mysql-test/tokudb/r/ext_key_1_tokudb.result
index 96d6814..a3c518a 100644
--- a/storage/tokudb/mysql-test/tokudb/r/ext_key_1_tokudb.result
+++ b/storage/tokudb/mysql-test/tokudb/r/ext_key_1_tokudb.result
@@ -1,7 +1,7 @@
drop table if exists t;
select @@optimizer_switch;
@@optimizer_switch
-index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on
+index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,not_null_range_scan=off
create table t (id int not null, x int not null, y int not null, primary key(id), key(x)) engine=tokudb;
insert into t values (0,0,0),(1,1,1),(2,2,2),(3,2,3),(4,2,4);
explain select x,id from t force index (x) where x=0 and id=0;
diff --git a/storage/tokudb/mysql-test/tokudb/r/ext_key_2_innodb.result b/storage/tokudb/mysql-test/tokudb/r/ext_key_2_innodb.result
index 43737c7..21dadb8 100644
--- a/storage/tokudb/mysql-test/tokudb/r/ext_key_2_innodb.result
+++ b/storage/tokudb/mysql-test/tokudb/r/ext_key_2_innodb.result
@@ -1,7 +1,7 @@
drop table if exists t;
select @@optimizer_switch;
@@optimizer_switch
-index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on
+index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,not_null_range_scan=off
create table t (a int not null, b int not null, c int not null, d int not null, primary key(a,b), key(c,a)) engine=innodb;
insert into t values (0,0,0,0),(0,1,0,1);
explain select c,a,b from t where c=0 and a=0 and b=1;
diff --git a/storage/tokudb/mysql-test/tokudb/r/ext_key_2_tokudb.result b/storage/tokudb/mysql-test/tokudb/r/ext_key_2_tokudb.result
index 1dcb1ee..41c3f2b 100644
--- a/storage/tokudb/mysql-test/tokudb/r/ext_key_2_tokudb.result
+++ b/storage/tokudb/mysql-test/tokudb/r/ext_key_2_tokudb.result
@@ -1,7 +1,7 @@
drop table if exists t;
select @@optimizer_switch;
@@optimizer_switch
-index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on
+index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on,extended_keys=on,exists_to_in=on,orderby_uses_equalities=on,condition_pushdown_for_derived=on,split_materialized=on,not_null_range_scan=off
create table t (a int not null, b int not null, c int not null, d int not null, primary key(a,b), key(c,a)) engine=tokudb;
insert into t values (0,0,0,0),(0,1,0,1);
explain select c,a,b from t where c=0 and a=0 and b=1;
1
0
[Commits] 28a2ba7: MDEV-19778 Wrong Result on Left Outer Join with Subquery right on true
by IgorBabaev 21 Jun '19
by IgorBabaev 21 Jun '19
21 Jun '19
revision-id: 28a2ba77430690a4889eba26203c4c4bc0e4a1bf (mariadb-5.5.64-21-g28a2ba7)
parent(s): 167da05f554dbe27d16373f6f0b02408ee76dc94
author: Igor Babaev
committer: Igor Babaev
timestamp: 2019-06-20 18:50:20 -0700
message:
MDEV-19778 Wrong Result on Left Outer Join with Subquery right on true
and WHERE filter afterwards
This patch complements the patch fixing the bug MDEV-6892. The latter
properly handled queries that used mergeable views returning constant
columns as inner tables of outer joins and whose where clause contained
predicates referring to these columns if the predicates of happened not
to be equality predicates. Otherwise the server still could return wrong
result sets for such queries. Besides the fix for MDEV-6892 prevented
some possible conversions of outer joins to inner joins for such queries.
This patch corrected the function check_simple_equality() to handle
properly conjunctive equalities of the where clause that refer to the
constant columns of mergeable views used as inner tables of an outer join.
The patch also changed the code of Item_direct_view_ref::not_null_tables().
This change allowed to take into account predicates containing references
to constant columns of mergeable views when converting outer joins into
inner joins.
---
mysql-test/r/derived.result | 9 ++++++
mysql-test/r/derived_view.result | 57 +++++++++++++++++++++++++++++++++++++
mysql-test/r/func_group.result | 2 ++
mysql-test/r/subselect_cache.result | 2 ++
mysql-test/t/derived.test | 4 +++
mysql-test/t/derived_view.test | 34 ++++++++++++++++++++++
sql/item.cc | 13 +++++----
sql/item.h | 1 +
sql/item_func.cc | 1 +
sql/sql_select.cc | 9 +++++-
10 files changed, 126 insertions(+), 6 deletions(-)
diff --git a/mysql-test/r/derived.result b/mysql-test/r/derived.result
index 2c0d009..9714963 100644
--- a/mysql-test/r/derived.result
+++ b/mysql-test/r/derived.result
@@ -578,6 +578,15 @@ select x.id, message from (select id from t1) x left join
(select id, 1 as message from t2) y on x.id=y.id
where coalesce(message,0) <> 0;
id message
+explain extended
+select x.id, message from (select id from t1) x left join
+(select id, 1 as message from t2) y on x.id=y.id
+where message <> 0;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00
+1 SIMPLE t2 ALL NULL NULL NULL NULL 3 100.00 Using where; Using join buffer (flat, BNL join)
+Warnings:
+Note 1003 select `test`.`t1`.`id` AS `id`,1 AS `message` from `test`.`t1` join `test`.`t2` where (`test`.`t2`.`id` = `test`.`t1`.`id`)
drop table t1,t2;
#
# MDEV-7827: Assertion `!table || (!table->read_set ||
diff --git a/mysql-test/r/derived_view.result b/mysql-test/r/derived_view.result
index f8cf919..412786c 100644
--- a/mysql-test/r/derived_view.result
+++ b/mysql-test/r/derived_view.result
@@ -3041,3 +3041,60 @@ id select_type table type possible_keys key key_len ref rows Extra
7 DERIVED p9 ALL NULL NULL NULL NULL 550 Using where; Using join buffer (incremental, BNL join)
7 DERIVED p10 ALL NULL NULL NULL NULL 550 Using where; Using join buffer (incremental, BNL join)
DROP TABLE t1, t2;
+#
+# MDEV-19778: equality condition for mergeable view returning constants
+# in its columns and used as inner table of outer join
+#
+create table t1 (pk int, a int);
+insert into t1 values (1,7), (2,3), (3,2), (4,3);
+create table t2 (b int);
+insert into t2 values (5), (1), (NULL), (3);
+create table t3 (c int);
+insert into t3 values (1), (8);
+create view v1 as
+select 3 as d, t2.b from t2;
+select * from t1 left join v1 on t1.pk <= 2 where t1.a=v1.d;
+pk a d b
+2 3 3 5
+2 3 3 1
+2 3 3 NULL
+2 3 3 3
+explain extended select * from t1 left join v1 on t1.pk <= 2 where t1.a=v1.d;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 4 100.00 Using where
+1 SIMPLE t2 ALL NULL NULL NULL NULL 4 100.00 Using join buffer (flat, BNL join)
+Warnings:
+Note 1003 select `test`.`t1`.`pk` AS `pk`,`test`.`t1`.`a` AS `a`,3 AS `d`,`test`.`t2`.`b` AS `b` from `test`.`t1` join `test`.`t2` where ((`test`.`t1`.`a` = 3) and (`test`.`t1`.`pk` <= 2))
+select * from t1 left join (select 3 as d, t2.b from t2) dt on t1.pk <= 2
+where t1.a=dt.d;
+pk a d b
+2 3 3 5
+2 3 3 1
+2 3 3 NULL
+2 3 3 3
+explain extended select * from t1 left join (select 3 as d, t2.b from t2) dt on t1.pk <= 2
+where t1.a=dt.d;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 4 100.00 Using where
+1 SIMPLE t2 ALL NULL NULL NULL NULL 4 100.00 Using join buffer (flat, BNL join)
+Warnings:
+Note 1003 select `test`.`t1`.`pk` AS `pk`,`test`.`t1`.`a` AS `a`,3 AS `d`,`test`.`t2`.`b` AS `b` from `test`.`t1` join `test`.`t2` where ((`test`.`t1`.`a` = 3) and (`test`.`t1`.`pk` <= 2))
+select * from t1 left join (v1,t3) on t1.pk <= 2 where t1.a=v1.d;
+pk a d b c
+2 3 3 5 1
+2 3 3 5 8
+2 3 3 1 1
+2 3 3 1 8
+2 3 3 NULL 1
+2 3 3 NULL 8
+2 3 3 3 1
+2 3 3 3 8
+explain extended select * from t1 left join (v1,t3) on t1.pk <= 2 where t1.a=v1.d;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 SIMPLE t3 ALL NULL NULL NULL NULL 2 100.00
+1 SIMPLE t1 ALL NULL NULL NULL NULL 4 100.00 Using where; Using join buffer (flat, BNL join)
+1 SIMPLE t2 ALL NULL NULL NULL NULL 4 100.00 Using join buffer (incremental, BNL join)
+Warnings:
+Note 1003 select `test`.`t1`.`pk` AS `pk`,`test`.`t1`.`a` AS `a`,3 AS `d`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`c` AS `c` from `test`.`t1` join `test`.`t2` join `test`.`t3` where ((`test`.`t1`.`a` = 3) and (`test`.`t1`.`pk` <= 2))
+drop view v1;
+drop table t1,t2,t3;
diff --git a/mysql-test/r/func_group.result b/mysql-test/r/func_group.result
index 38fae2f0..c1de5d9 100644
--- a/mysql-test/r/func_group.result
+++ b/mysql-test/r/func_group.result
@@ -1981,6 +1981,7 @@ NULL
Warnings:
Warning 1292 Truncated incorrect INTEGER value: 'j'
Warning 1292 Truncated incorrect INTEGER value: 'j'
+Warning 1292 Truncated incorrect INTEGER value: 'j'
EXPLAIN
SELECT MIN(t2.pk)
@@ -1995,6 +1996,7 @@ id select_type table type possible_keys key key_len ref rows Extra
Warnings:
Warning 1292 Truncated incorrect INTEGER value: 'j'
Warning 1292 Truncated incorrect INTEGER value: 'j'
+Warning 1292 Truncated incorrect INTEGER value: 'j'
#
# 2) Test that subquery materialization is setup for query with
diff --git a/mysql-test/r/subselect_cache.result b/mysql-test/r/subselect_cache.result
index 95f9359..0bdef7a 100644
--- a/mysql-test/r/subselect_cache.result
+++ b/mysql-test/r/subselect_cache.result
@@ -3130,6 +3130,7 @@ WHERE table1 .`col_varchar_key` ) field10
Warnings:
Warning 1292 Truncated incorrect DOUBLE value: 'f'
Warning 1292 Truncated incorrect DOUBLE value: 'f'
+Warning 1292 Truncated incorrect DOUBLE value: 'f'
SET @@optimizer_switch = 'subquery_cache=on';
/* cache is on */ SELECT COUNT( DISTINCT table2 .`col_int_key` ) , (
SELECT SUBQUERY2_t1 .`col_int_key`
@@ -3146,6 +3147,7 @@ WHERE table1 .`col_varchar_key` ) field10
Warnings:
Warning 1292 Truncated incorrect DOUBLE value: 'f'
Warning 1292 Truncated incorrect DOUBLE value: 'f'
+Warning 1292 Truncated incorrect DOUBLE value: 'f'
drop table t1,t2,t3,t4;
set @@optimizer_switch= default;
#launchpad BUG#611625
diff --git a/mysql-test/t/derived.test b/mysql-test/t/derived.test
index 62b2c43..38ab28f 100644
--- a/mysql-test/t/derived.test
+++ b/mysql-test/t/derived.test
@@ -502,6 +502,10 @@ insert into t2 values(4),(5),(6);
select x.id, message from (select id from t1) x left join
(select id, 1 as message from t2) y on x.id=y.id
where coalesce(message,0) <> 0;
+explain extended
+select x.id, message from (select id from t1) x left join
+(select id, 1 as message from t2) y on x.id=y.id
+where message <> 0;
drop table t1,t2;
--echo #
diff --git a/mysql-test/t/derived_view.test b/mysql-test/t/derived_view.test
index 61c4278..34aaa27 100644
--- a/mysql-test/t/derived_view.test
+++ b/mysql-test/t/derived_view.test
@@ -2194,3 +2194,37 @@ JOIN
) gp_20 ON gp_20.id=t2.id ;
DROP TABLE t1, t2;
+
+--echo #
+--echo # MDEV-19778: equality condition for mergeable view returning constants
+--echo # in its columns and used as inner table of outer join
+--echo #
+
+create table t1 (pk int, a int);
+insert into t1 values (1,7), (2,3), (3,2), (4,3);
+create table t2 (b int);
+insert into t2 values (5), (1), (NULL), (3);
+create table t3 (c int);
+insert into t3 values (1), (8);
+
+create view v1 as
+select 3 as d, t2.b from t2;
+
+let $q=
+select * from t1 left join v1 on t1.pk <= 2 where t1.a=v1.d;
+eval $q;
+eval explain extended $q;
+
+let $q=
+select * from t1 left join (select 3 as d, t2.b from t2) dt on t1.pk <= 2
+ where t1.a=dt.d;
+eval $q;
+eval explain extended $q;
+
+let $q=
+select * from t1 left join (v1,t3) on t1.pk <= 2 where t1.a=v1.d;
+eval $q;
+eval explain extended $q;
+
+drop view v1;
+drop table t1,t2,t3;
diff --git a/sql/item.cc b/sql/item.cc
index 719bdcf..ffd899e 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -9814,11 +9814,14 @@ table_map Item_direct_view_ref::used_tables() const
table_map Item_direct_view_ref::not_null_tables() const
{
- return get_depended_from() ?
- 0 :
- ((view->is_merged_derived() || view->merged || !view->table) ?
- (*ref)->not_null_tables() :
- view->table->map);
+ if (get_depended_from())
+ return 0;
+ if (!( view->merged || !view->table))
+ return view->table->map;
+ TABLE *tab= get_null_ref_table();
+ if (tab == NO_NULL_TABLE || (*ref)->used_tables())
+ return (*ref)->not_null_tables();
+ return get_null_ref_table()->map;
}
/*
diff --git a/sql/item.h b/sql/item.h
index f6c3c20..6dc9997 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -3382,6 +3382,7 @@ class Item_direct_view_ref :public Item_direct_ref
void update_used_tables();
table_map not_null_tables() const;
bool const_item() const { return used_tables() == 0; }
+ TABLE *get_null_ref_table() const { return null_ref_table; }
bool walk(Item_processor processor, bool walk_subquery, uchar *arg)
{
return (*ref)->walk(processor, walk_subquery, arg) ||
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 22d7e8c..dad4b89 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -226,6 +226,7 @@ Item_func::fix_fields(THD *thd, Item **ref)
with_field= with_field || item->with_field;
used_tables_cache|= item->used_tables();
const_item_cache&= item->const_item();
+ not_null_tables_cache|= item->not_null_tables();
with_subselect|= item->has_subquery();
}
}
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 0d94ba2..3d78000 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -1030,7 +1030,6 @@ JOIN::optimize()
DBUG_RETURN(1); /* purecov: inspected */
/* dump_TABLE_LIST_graph(select_lex, select_lex->leaf_tables); */
select_lex->update_used_tables();
-
}
eval_select_list_used_tables();
@@ -1092,6 +1091,8 @@ JOIN::optimize()
sel->where= conds;
+ select_lex->update_used_tables();
+
if (arena)
thd->restore_active_arena(arena, &backup);
}
@@ -11763,6 +11764,9 @@ static bool check_simple_equality(Item *left_item, Item *right_item,
{
if (((Item_ref*)left_item)->get_depended_from())
return FALSE;
+ if (((Item_direct_view_ref*)left_item)->get_null_ref_table() !=
+ NO_NULL_TABLE && !left_item->real_item()->used_tables())
+ return FALSE;
left_item= left_item->real_item();
}
if (right_item->type() == Item::REF_ITEM &&
@@ -11770,6 +11774,9 @@ static bool check_simple_equality(Item *left_item, Item *right_item,
{
if (((Item_ref*)right_item)->get_depended_from())
return FALSE;
+ if (((Item_direct_view_ref*)right_item)->get_null_ref_table() !=
+ NO_NULL_TABLE && !right_item->real_item()->used_tables())
+ return FALSE;
right_item= right_item->real_item();
}
if (left_item->type() == Item::FIELD_ITEM &&
2
1
[Commits] 7345c0de26a: Fix tests: some build hosts have ramdisk in /run/shm instead of /dev/shm
by Sergei Petrunia 20 Jun '19
by Sergei Petrunia 20 Jun '19
20 Jun '19
revision-id: 7345c0de26a8e9a88c88a5b5502beb3250b9429e (mariadb-10.2.25-17-g7345c0de26a)
parent(s): 622ecfc7c6ab4adb3720687dfae623824b1454a5
author: Sergei Petrunia
committer: Sergei Petrunia
timestamp: 2019-06-20 21:34:15 +0300
message:
Fix tests: some build hosts have ramdisk in /run/shm instead of /dev/shm
---
storage/rocksdb/mysql-test/rocksdb/include/have_direct_io.inc | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/storage/rocksdb/mysql-test/rocksdb/include/have_direct_io.inc b/storage/rocksdb/mysql-test/rocksdb/include/have_direct_io.inc
index 49509af5bd2..d9b4b46b25a 100644
--- a/storage/rocksdb/mysql-test/rocksdb/include/have_direct_io.inc
+++ b/storage/rocksdb/mysql-test/rocksdb/include/have_direct_io.inc
@@ -8,7 +8,8 @@ use Cwd 'abs_path';
open(FILE, ">", "$ENV{MYSQL_TMP_DIR}/data_in_shm.inc") or die;
my $real_path= abs_path($ENV{'MYSQLTEST_VARDIR'});
-my $in_shm= index($real_path, "/dev/shm") != -1;
+my $in_shm= (index($real_path, "/dev/shm") != -1) ||
+ (index($real_path, "/run/shm") != -1);
print FILE "let \$DATA_IN_SHM= $in_shm;\n";
close FILE;
EOF
@@ -18,5 +19,5 @@ EOF
if ($DATA_IN_SHM)
{
- --skip DATADIR is in /dev/shm, possibly due to --mem
+ --skip DATADIR is in /{dev|run}/shm, possibly due to --mem
}
1
0
revision-id: 622ecfc7c6ab4adb3720687dfae623824b1454a5 (mariadb-10.2.25-16-g622ecfc7c6a)
parent(s): a2e9e3fbd485181a4db679a163167b9a833e67ad
author: Sergei Petrunia
committer: Sergei Petrunia
timestamp: 2019-06-20 19:35:36 +0300
message:
Update test results
---
storage/rocksdb/mysql-test/rocksdb/r/rocksdb.result | 2 --
1 file changed, 2 deletions(-)
diff --git a/storage/rocksdb/mysql-test/rocksdb/r/rocksdb.result b/storage/rocksdb/mysql-test/rocksdb/r/rocksdb.result
index 8b09a70d288..6cc4cc7a1dc 100644
--- a/storage/rocksdb/mysql-test/rocksdb/r/rocksdb.result
+++ b/storage/rocksdb/mysql-test/rocksdb/r/rocksdb.result
@@ -968,8 +968,6 @@ rocksdb_persistent_cache_size_mb 0
rocksdb_pin_l0_filter_and_index_blocks_in_cache ON
rocksdb_print_snapshot_conflict_queries OFF
rocksdb_rate_limiter_bytes_per_sec 0
-rocksdb_read_free_rpl OFF
-rocksdb_read_free_rpl_tables .*
rocksdb_records_in_range 50
rocksdb_remove_mariabackup_checkpoint OFF
rocksdb_reset_stats OFF
1
0