Commit 5f296f3a authored by Oleg Smirnov's avatar Oleg Smirnov

MDEV-29640 FederatedX does not properly handle pushdown in case of difference...

MDEV-29640  FederatedX does not properly handle pushdown in case of difference in local and remote table names

FederatedX table may refer to a table with a different name on the
remote server:
  test> CREATE TABLE t2 (...) ENGINE="FEDERATEDX"
  CONNECTION="mysql://user:pass@192.168.1.111:9308/federatedx/t1";
  test> select * from t2 where ...;
This could cause an issue with federated_pushdown=1, because FederatedX
pushes the query (or derived table's) text to the remote server. The remote
server will try to read from table t2 (while it should read from t1).

Solution: do not allow pushing down queries with tables that have different
db_name.table name on the local and remote server.

This patch also fixes:
MDEV-29863 Server crashes in federatedx_txn::acquire after select from the
FederatedX table with partitions

Solution: disallow pushdown when partitioned FederatedX tables are used.
parent 58cd0bd5
......@@ -420,6 +420,55 @@ 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 federated.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 federated.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 federated.t1_local;
COUNT(DISTINCT a)
70000
#
# MDEV-29863 Server crashes in federatedx_txn::acquire after select from
# the Federated table with partitions and federated_pushdown=1
# in case of difference in local and remote table names
#
connection slave;
CREATE TABLE federated.t3 (a INT);
INSERT INTO federated.t3 VALUES (1),(2),(3);
CREATE TABLE federated.t4 (a INT);
connection master;
CREATE SERVER fedlink FOREIGN DATA WRAPPER mysql
OPTIONS (USER 'root', HOST '127.0.0.1', DATABASE 'federated',
PORT SLAVE_PORT);
CREATE TABLE federated.t3 (a INT)
ENGINE=FEDERATED
CONNECTION='mysql://root@127.0.0.1:$SLAVE_MYPORT/federated/t3'
PARTITION BY list (a)
(PARTITION p1 VALUES IN (1) CONNECTION='fedlink/t3',
PARTITION p2 VALUES IN (2) CONNECTION='fedlink/t4');
EXPLAIN SELECT * FROM federated.t3;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t3 ALL NULL NULL NULL NULL 3
SELECT * FROM federated.t3;
a
1
2
3
set global federated_pushdown=0;
connection master;
DROP TABLE IF EXISTS federated.t1;
......
--source have_federatedx.inc
--source include/federated.inc
--source include/no_valgrind_without_big.inc
--source include/have_partition.inc
connection default;
......@@ -266,6 +267,53 @@ 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 federated.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 federated.t1_local;
SELECT COUNT(DISTINCT a) FROM federated.t1_local;
--echo #
--echo # MDEV-29863 Server crashes in federatedx_txn::acquire after select from
--echo # the Federated table with partitions and federated_pushdown=1
--echo # in case of difference in local and remote table names
--echo #
connection slave;
CREATE TABLE federated.t3 (a INT);
INSERT INTO federated.t3 VALUES (1),(2),(3);
CREATE TABLE federated.t4 (a INT);
connection master;
--replace_result $SLAVE_MYPORT SLAVE_PORT
eval CREATE SERVER fedlink FOREIGN DATA WRAPPER mysql
OPTIONS (USER 'root', HOST '127.0.0.1', DATABASE 'federated',
PORT $SLAVE_MYPORT);
CREATE TABLE federated.t3 (a INT)
ENGINE=FEDERATED
CONNECTION='mysql://root@127.0.0.1:$SLAVE_MYPORT/federated/t3'
PARTITION BY list (a)
(PARTITION p1 VALUES IN (1) CONNECTION='fedlink/t3',
PARTITION p2 VALUES IN (2) CONNECTION='fedlink/t4');
EXPLAIN SELECT * FROM federated.t3;
SELECT * FROM federated.t3;
set global federated_pushdown=0;
source include/federated_cleanup.inc;
......
......@@ -35,6 +35,64 @@
*/
/*
Check if table and database names are equal on local and remote servers
SYNOPSIS
local_and_remote_names_match()
tbl_share Pointer to current table TABLE_SHARE structure
fshare Pointer to current table FEDERATEDX_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(const TABLE_SHARE *tbl_share,
const FEDERATEDX_SHARE *fshare)
{
if (lower_case_table_names)
{
if (strcasecmp(fshare->database, tbl_share->db.str) != 0)
return true;
}
else
{
if (strncmp(fshare->database, tbl_share->db.str, tbl_share->db.length) != 0)
return true;
}
return my_strnncoll(system_charset_info, (uchar *) fshare->table_name,
strlen(fshare->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)
{
......@@ -42,7 +100,6 @@ create_federatedx_derived_handler(THD* thd, TABLE_LIST *derived)
return 0;
ha_federatedx_derived_handler* handler = NULL;
handlerton *ht= 0;
SELECT_LEX_UNIT *unit= derived->derived;
......@@ -54,9 +111,16 @@ create_federatedx_derived_handler(THD* thd, TABLE_LIST *derived)
{
if (!tbl->table)
return 0;
if (!ht)
ht= tbl->table->file->partition_ht();
else if (ht != tbl->table->file->partition_ht())
/*
We intentionally don't support partitioned federatedx tables here, so
use file->ht and not file->partition_ht().
*/
if (tbl->table->file->ht != federatedx_hton)
return 0;
const FEDERATEDX_SHARE *fshare=
((ha_federatedx*)tbl->table->file)->get_federatedx_share();
if (local_and_remote_names_mismatch(tbl->table->s, fshare))
return 0;
}
}
......@@ -170,15 +234,22 @@ create_federatedx_select_handler(THD* thd, SELECT_LEX *sel)
return 0;
ha_federatedx_select_handler* handler = NULL;
handlerton *ht= 0;
for (TABLE_LIST *tbl= thd->lex->query_tables; tbl; tbl= tbl->next_global)
{
if (!tbl->table)
return 0;
if (!ht)
ht= tbl->table->file->partition_ht();
else if (ht != tbl->table->file->partition_ht())
/*
We intentionally don't support partitioned federatedx tables here, so
use file->ht and not file->partition_ht().
*/
if (tbl->table->file->ht != federatedx_hton)
return 0;
const FEDERATEDX_SHARE *fshare=
((ha_federatedx*)tbl->table->file)->get_federatedx_share();
if (local_and_remote_names_mismatch(tbl->table->s, fshare))
return 0;
}
......
......@@ -609,7 +609,7 @@ int get_connection(MEM_ROOT *mem_root, FEDERATEDX_SHARE *share)
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
......
......@@ -463,6 +463,7 @@ class ha_federatedx: public handler
int reset(void);
int free_result(void);
const FEDERATEDX_SHARE *get_federatedx_share() const { return share; }
friend class ha_federatedx_derived_handler;
friend class ha_federatedx_select_handler;
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment