diff options
author | Oleg Smirnov <olernov@gmail.com> | 2022-10-23 20:48:43 +0700 |
---|---|---|
committer | Oleg Smirnov <olernov@gmail.com> | 2022-10-24 12:14:33 +0700 |
commit | e78a948b9b27d9e6a8d1967cc12ff1be8396bfb8 (patch) | |
tree | 47b34657cf290f5e35f07b2c8d5beb4ed2f268fe | |
parent | 291872ec82fc2a19aedf8dcf13838ecd34101caa (diff) | |
download | mariadb-git-bb-10.4-MDEV-29640.tar.gz |
MDEV-29640 FederatedX does not properly handle pushdown in case of difference in local and remote table namesbb-10.4-MDEV-29640
FederatedX table on the local server may refer to a table having
another name on the remote server. The remote table may even reside
in a different database. For example:
-- Remote server
CREATE TABLE t1 (id int(32));
-- Local server
CREATE TABLE t2 ENGINE="FEDERATEDX"
CONNECTION="mysql://joe:joespass@192.168.1.111:9308/federatedx/t1";
It's not a problem while the federated_pushdown is disabled 'cause
the CONNECTION strings are being parsed for every table during
the execution, so the table names are translated from local to remote.
But in case of the federated_pushdown the whole query is pushed down
to the engine without any translation, so the remote server may try
to select data from a nonexistent table (for example, query
"SELECT * FROM t2" will try to retrieve data from nonexistent "t2").
Solution: do not allow pushing down queries with tables having
different names on local and remote names.
-rw-r--r-- | mysql-test/suite/federated/federatedx_create_handlers.result | 23 | ||||
-rw-r--r-- | mysql-test/suite/federated/federatedx_create_handlers.test | 22 | ||||
-rw-r--r-- | storage/federatedx/federatedx_pushdown.cc | 68 | ||||
-rw-r--r-- | storage/federatedx/ha_federatedx.cc | 6 |
4 files changed, 116 insertions, 3 deletions
diff --git a/mysql-test/suite/federated/federatedx_create_handlers.result b/mysql-test/suite/federated/federatedx_create_handlers.result index b115cc73b87..de1f42f0c9c 100644 --- a/mysql-test/suite/federated/federatedx_create_handlers.result +++ b/mysql-test/suite/federated/federatedx_create_handlers.result @@ -420,6 +420,29 @@ SELECT * FROM (SELECT * FROM federated.t1 LIMIT 70000) dt; SELECT COUNT(DISTINCT a) FROM federated.t2; COUNT(DISTINCT a) 70000 +# +# MDEV-29640 FederatedX does not properly handle pushdown +# in case of difference in local and remote table names +# +connection master; +# Use tables from the previous test. Make sure pushdown works: +EXPLAIN SELECT COUNT(DISTINCT a) FROM federated.t2; +id select_type table type possible_keys key key_len ref rows Extra +1 PUSHED SELECT NULL NULL NULL NULL NULL NULL NULL NULL +SELECT COUNT(DISTINCT a) FROM federated.t2; +COUNT(DISTINCT a) +70000 +# Link remote table `federated.t1` with the local table named `t1_local` +CREATE TABLE t1_local ENGINE="FEDERATED" +CONNECTION='mysql://root@127.0.0.1:SLAVE_PORT/federated/t1'; +# No pushdown here due to table names mismatch, retrieve data as usual: +EXPLAIN SELECT COUNT(DISTINCT a) FROM t1_local; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1_local ALL NULL NULL NULL NULL 70000 +SELECT COUNT(DISTINCT a) FROM t1_local; +COUNT(DISTINCT a) +70000 +DROP TABLE t1_local; set global federated_pushdown=0; connection master; DROP TABLE IF EXISTS federated.t1; diff --git a/mysql-test/suite/federated/federatedx_create_handlers.test b/mysql-test/suite/federated/federatedx_create_handlers.test index 8863a057b47..ea2bc2f74f2 100644 --- a/mysql-test/suite/federated/federatedx_create_handlers.test +++ b/mysql-test/suite/federated/federatedx_create_handlers.test @@ -266,6 +266,28 @@ INSERT INTO federated.t2 SELECT * FROM (SELECT * FROM federated.t1 LIMIT 70000) dt; SELECT COUNT(DISTINCT a) FROM federated.t2; + +--echo # +--echo # MDEV-29640 FederatedX does not properly handle pushdown +--echo # in case of difference in local and remote table names +--echo # +connection master; +--echo # Use tables from the previous test. Make sure pushdown works: +EXPLAIN SELECT COUNT(DISTINCT a) FROM federated.t2; +SELECT COUNT(DISTINCT a) FROM federated.t2; + +--echo # Link remote table `federated.t1` with the local table named `t1_local` +--replace_result $SLAVE_MYPORT SLAVE_PORT +eval +CREATE TABLE t1_local ENGINE="FEDERATED" +CONNECTION='mysql://root@127.0.0.1:$SLAVE_MYPORT/federated/t1'; + +--echo # No pushdown here due to table names mismatch, retrieve data as usual: +EXPLAIN SELECT COUNT(DISTINCT a) FROM t1_local; +SELECT COUNT(DISTINCT a) FROM t1_local; + +DROP TABLE t1_local; + set global federated_pushdown=0; source include/federated_cleanup.inc; diff --git a/storage/federatedx/federatedx_pushdown.cc b/storage/federatedx/federatedx_pushdown.cc index 15b0b0d3d4e..c26fbffa4c1 100644 --- a/storage/federatedx/federatedx_pushdown.cc +++ b/storage/federatedx/federatedx_pushdown.cc @@ -35,6 +35,68 @@ */ +/* + Check if table and database names are equal on local and remote servers + + SYNOPSIS + local_and_remote_names_match() + thd Thread descriptor + tbl_share Pointer to current table TABLE_SHARE structure + + DESCRIPTION + FederatedX table on the local server may refer to a table having another + name on the remote server. The remote table may even reside in a different + database. For example: + + -- Remote server + CREATE TABLE t1 (id int(32)); + + -- Local server + CREATE TABLE t2 ENGINE="FEDERATEDX" + CONNECTION="mysql://joe:joespass@192.168.1.111:9308/federatedx/t1"; + + It's not a problem while the federated_pushdown is disabled 'cause + the CONNECTION strings are being parsed for every table during + the execution, so the table names are translated from local to remote. + But in case of the federated_pushdown the whole query is pushed down + to the engine without any translation, so the remote server may try + to select data from a nonexistent table (for example, query + "SELECT * FROM t2" will try to retrieve data from nonexistent "t2"). + + This function checks whether there is a mismatch between local and remote + table/database names + + RETURN VALUE + false names are equal + true names are not equal + +*/ +bool local_and_remote_names_mismatch(THD *thd, const TABLE_SHARE *tbl_share) +{ + FEDERATEDX_SHARE tmp_share; + bzero(&tmp_share, sizeof(tmp_share)); + if (parse_url(thd->mem_root, &tmp_share, tbl_share, 0)) + return true; + + if (lower_case_table_names) + { + if (strcasecmp(tmp_share.database, tbl_share->db.str) != 0) + return true; + } + else + { + if (strncmp(tmp_share.database, tbl_share->db.str, tbl_share->db.length) != + 0) + return true; + } + + return my_strnncoll(system_charset_info, (uchar *) tmp_share.table_name, + strlen(tmp_share.table_name), + (uchar *) tbl_share->table_name.str, + tbl_share->table_name.length) != 0; +} + + static derived_handler* create_federatedx_derived_handler(THD* thd, TABLE_LIST *derived) { @@ -58,6 +120,9 @@ create_federatedx_derived_handler(THD* thd, TABLE_LIST *derived) ht= tbl->table->file->partition_ht(); else if (ht != tbl->table->file->partition_ht()) return 0; + if (ht == federatedx_hton && + local_and_remote_names_mismatch(thd, tbl->table->s)) + return 0; } } @@ -180,6 +245,9 @@ create_federatedx_select_handler(THD* thd, SELECT_LEX *sel) ht= tbl->table->file->partition_ht(); else if (ht != tbl->table->file->partition_ht()) return 0; + if (ht == federatedx_hton && + local_and_remote_names_mismatch(thd, tbl->table->s)) + return 0; } /* diff --git a/storage/federatedx/ha_federatedx.cc b/storage/federatedx/ha_federatedx.cc index 085422e6016..2eda3786d9d 100644 --- a/storage/federatedx/ha_federatedx.cc +++ b/storage/federatedx/ha_federatedx.cc @@ -528,7 +528,7 @@ err: } -static int parse_url_error(FEDERATEDX_SHARE *share, TABLE_SHARE *table_s, +static int parse_url_error(FEDERATEDX_SHARE *share, const TABLE_SHARE *table_s, int error_num) { char buf[FEDERATEDX_QUERY_BUFFER_SIZE]; @@ -609,7 +609,7 @@ error: parse_url() mem_root MEM_ROOT pointer for memory allocation share pointer to FEDERATEDX share - table pointer to current TABLE class + table_s pointer to current TABLE_SHARE class table_create_flag determines what error to throw DESCRIPTION @@ -658,7 +658,7 @@ error: */ static int parse_url(MEM_ROOT *mem_root, FEDERATEDX_SHARE *share, - TABLE_SHARE *table_s, uint table_create_flag) + const TABLE_SHARE *table_s, uint table_create_flag) { uint error_num= (table_create_flag ? ER_FOREIGN_DATA_STRING_INVALID_CANT_CREATE : |