Skip to content

Commit ca3fa4a

Browse files
committed
MDEV-39196: SELECT from information schema fails when FederatedX loses underlying table
When a remote table is unavailable, FederatedX was passing a hard error back to the SQL layer, causing INFORMATION_SCHEMA queries to abort entirely. This patch intercepts the remote error in ha_federatedx::info, downgrades it to a warning using push_warning_printf, and includes the local table name in the warning message so the user knows which table is inaccessible. Signed-off-by: Anway Durge <124391429+itzanway@users.noreply.github.com>
1 parent 56ad23b commit ca3fa4a

6 files changed

Lines changed: 147 additions & 6 deletions

File tree

mysql-test/suite/federated/federated.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ create table t1 (a int);
1717
--replace_result $MASTER_MYPORT MASTER_PORT
1818
eval create table fed (a int) engine=Federated CONNECTION='mysql://root@127.0.0.1:$MASTER_MYPORT/test/t1';
1919
drop table t1;
20-
--error 1146,1431
20+
--error ER_NO_SUCH_TABLE,ER_GET_ERRMSG,ER_FOREIGN_DATA_SOURCE_DOESNT_EXIST
2121
select * from fed;
2222
drop table fed;
2323

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
--plugin-load-add=$HA_FEDERATEDX_SO --loose-federated
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
connect master,127.0.0.1,root,,test,$MASTER_MYPORT,;
2+
connect slave,127.0.0.1,root,,test,$SLAVE_MYPORT,;
3+
connection master;
4+
CREATE DATABASE federated;
5+
connection slave;
6+
CREATE DATABASE federated;
7+
#
8+
# MDEV-39196: INFORMATION_SCHEMA query must succeed with a
9+
# warning when a FederatedX remote table is unreachable.
10+
#
11+
connection slave;
12+
USE federated;
13+
CREATE TABLE t1 (
14+
id INT NOT NULL,
15+
name VARCHAR(64)
16+
) ENGINE=MyISAM;
17+
INSERT INTO t1 VALUES (1, 'foo');
18+
connection master;
19+
USE federated;
20+
CREATE TABLE t1 (
21+
id INT NOT NULL,
22+
name VARCHAR(64)
23+
) ENGINE=FEDERATED
24+
CONNECTION='mysql://root@127.0.0.1:SLAVE_PORT/federated/t1';
25+
# Verify the federated table works before dropping remote table.
26+
SELECT * FROM federated.t1;
27+
id name
28+
1 foo
29+
# Drop the remote table to simulate unreachable/missing table.
30+
connection slave;
31+
USE federated;
32+
DROP TABLE t1;
33+
connection master;
34+
# INFORMATION_SCHEMA query must succeed and issue a warning.
35+
SELECT TABLE_NAME, TABLE_ROWS
36+
FROM information_schema.TABLES
37+
WHERE TABLE_SCHEMA = 'federated'
38+
AND TABLE_NAME = 't1';
39+
TABLE_NAME TABLE_ROWS
40+
t1 1
41+
Warnings:
42+
Warning 1430 FederatedX: Table 't1' is inaccessible: 1146 : Remote table does not exist
43+
# Warning must be present.
44+
SHOW WARNINGS;
45+
Level Code Message
46+
Warning 1430 FederatedX: Table 't1' is inaccessible: 1146 : Remote table does not exist
47+
# Cleanup.
48+
connection master;
49+
DROP TABLE IF EXISTS federated.t1;
50+
connection master;
51+
DROP TABLE IF EXISTS federated.t1;
52+
DROP DATABASE IF EXISTS federated;
53+
connection slave;
54+
DROP TABLE IF EXISTS federated.t1;
55+
DROP DATABASE IF EXISTS federated;
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
--source include/not_embedded.inc
2+
--source have_federatedx.inc
3+
--source include/federated.inc
4+
5+
--echo #
6+
--echo # MDEV-39196: INFORMATION_SCHEMA query must succeed with a
7+
--echo # warning when a FederatedX remote table is unreachable.
8+
--echo #
9+
10+
connection slave;
11+
USE federated;
12+
CREATE TABLE t1 (
13+
id INT NOT NULL,
14+
name VARCHAR(64)
15+
) ENGINE=MyISAM;
16+
INSERT INTO t1 VALUES (1, 'foo');
17+
18+
connection master;
19+
USE federated;
20+
--replace_result $SLAVE_MYPORT SLAVE_PORT
21+
eval CREATE TABLE t1 (
22+
id INT NOT NULL,
23+
name VARCHAR(64)
24+
) ENGINE=FEDERATED
25+
CONNECTION='mysql://root@127.0.0.1:$SLAVE_MYPORT/federated/t1';
26+
27+
--echo # Verify the federated table works before dropping remote table.
28+
SELECT * FROM federated.t1;
29+
30+
--echo # Drop the remote table to simulate unreachable/missing table.
31+
connection slave;
32+
USE federated;
33+
DROP TABLE t1;
34+
35+
connection master;
36+
--echo # INFORMATION_SCHEMA query must succeed and issue a warning.
37+
SELECT TABLE_NAME, TABLE_ROWS
38+
FROM information_schema.TABLES
39+
WHERE TABLE_SCHEMA = 'federated'
40+
AND TABLE_NAME = 't1';
41+
42+
--echo # Warning must be present.
43+
SHOW WARNINGS;
44+
45+
--echo # Cleanup.
46+
connection master;
47+
DROP TABLE IF EXISTS federated.t1;
48+
49+
--source include/federated_cleanup.inc

storage/federatedx/federatedx_io_mysql.cc

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ class federatedx_io_mysql :public federatedx_io
6868
DYNAMIC_ARRAY savepoints;
6969
bool requested_autocommit;
7070
bool actual_autocommit;
71+
int stored_error_code;
72+
char stored_error_msg[MYSQL_ERRMSG_SIZE];
7173

7274
int actual_query(const char *buffer, size_t length);
7375
bool test_all_restrict() const;
@@ -134,12 +136,14 @@ federatedx_io *instantiate_io_mysql(MEM_ROOT *server_root,
134136

135137
federatedx_io_mysql::federatedx_io_mysql(FEDERATEDX_SERVER *aserver)
136138
: federatedx_io(aserver),
137-
requested_autocommit(TRUE), actual_autocommit(TRUE)
139+
requested_autocommit(TRUE), actual_autocommit(TRUE),
140+
stored_error_code(0)
138141
{
139142
DBUG_ENTER("federatedx_io_mysql::federatedx_io_mysql");
140143

141144
bzero(&mysql, sizeof(MYSQL));
142145
bzero(&savepoints, sizeof(DYNAMIC_ARRAY));
146+
bzero(stored_error_msg, sizeof(stored_error_msg));
143147

144148
my_init_dynamic_array(PSI_INSTRUMENT_ME, &savepoints, sizeof(SAVEPT), 16, 16, MYF(0));
145149

@@ -483,12 +487,16 @@ my_ulonglong federatedx_io_mysql::last_insert_id() const
483487

484488
int federatedx_io_mysql::error_code()
485489
{
490+
if (stored_error_code)
491+
return stored_error_code;
486492
return mysql_errno(&mysql);
487493
}
488494

489495

490496
const char *federatedx_io_mysql::error_str()
491497
{
498+
if (stored_error_code)
499+
return stored_error_msg;
492500
return mysql_error(&mysql);
493501
}
494502

@@ -583,7 +591,11 @@ bool federatedx_io_mysql::table_metadata(ha_statistics *stats,
583591
goto error;
584592

585593
if (!(row= fetch_row(result)))
594+
{
595+
stored_error_code= 0;
596+
bzero(stored_error_msg, sizeof(stored_error_msg));
586597
goto error;
598+
}
587599

588600
/*
589601
deleted is set in ha_federatedx::info
@@ -618,8 +630,13 @@ bool federatedx_io_mysql::table_metadata(ha_statistics *stats,
618630
error:
619631
if (!mysql_errno(&mysql))
620632
{
621-
mysql.net.last_errno= ER_NO_SUCH_TABLE;
622-
strmake_buf(mysql.net.last_error, "Remote table does not exist");
633+
stored_error_code= ER_NO_SUCH_TABLE;
634+
strmake_buf(stored_error_msg, "Remote table does not exist");
635+
}
636+
else
637+
{
638+
stored_error_code= mysql_errno(&mysql);
639+
strmake_buf(stored_error_msg, mysql_error(&mysql));
623640
}
624641
free_result(result);
625642
return 1;

storage/federatedx/ha_federatedx.cc

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3113,7 +3113,10 @@ int ha_federatedx::info(uint flag)
31133113

31143114
if ((*iop)->table_metadata(&stats, share->table_name,
31153115
(uint)share->table_name_length, flag))
3116+
{
3117+
error_code= (uint)(*iop)->error_code(); /* capture NOW before it clears */
31163118
goto error;
3119+
}
31173120
}
31183121

31193122
if (flag & HA_STATUS_AUTO)
@@ -3130,8 +3133,24 @@ int ha_federatedx::info(uint flag)
31303133
error:
31313134
if (iop && *iop)
31323135
{
3133-
my_printf_error((*iop)->error_code(), "Received error: %d : %s", MYF(0),
3134-
(*iop)->error_code(), (*iop)->error_str());
3136+
if (flag & HA_STATUS_VARIABLE)
3137+
{
3138+
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
3139+
ER_QUERY_ON_FOREIGN_DATA_SOURCE,
3140+
"FederatedX: Table '%s' is inaccessible: "
3141+
"%d : %s",
3142+
share->table_name,
3143+
(*iop)->error_code(),
3144+
(*iop)->error_str());
3145+
error_code= 0;
3146+
}
3147+
else
3148+
{
3149+
my_printf_error((*iop)->error_code(),
3150+
": %d : %s", MYF(0),
3151+
(*iop)->error_code(), (*iop)->error_str());
3152+
error_code= (*iop)->error_code();
3153+
}
31353154
}
31363155
else if (remote_error_number != -1 /* error already reported */)
31373156
{

0 commit comments

Comments
 (0)