Skip to content

Commit fbe52f3

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 fbe52f3

5 files changed

Lines changed: 134 additions & 17 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_FOREIGN_DATA_SOURCE_DOESNT_EXIST
2121
select * from fed;
2222
drop table fed;
2323

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

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: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3091,47 +3091,65 @@ int ha_federatedx::info(uint flag)
30913091
DBUG_ENTER("ha_federatedx::info");
30923092

30933093
error_code= ER_QUERY_ON_FOREIGN_DATA_SOURCE;
3094-
3094+
30953095
// external_lock may not have been called so txn may not be set
30963096
tmp_txn= get_txn(thd);
30973097

30983098
/* we want not to show table status if not needed to do so */
30993099
if (flag & (HA_STATUS_VARIABLE | HA_STATUS_CONST | HA_STATUS_AUTO))
31003100
{
3101-
if (!*(iop= &io) && (error_code= tmp_txn->acquire(share, thd, TRUE, (iop= &tmp_io))))
3101+
if (!*(iop= &io) && (error_code= tmp_txn->acquire(share, thd, TRUE,
3102+
(iop= &tmp_io))))
31023103
goto fail;
31033104
}
31043105

31053106
if (flag & (HA_STATUS_VARIABLE | HA_STATUS_CONST))
31063107
{
3107-
/*
3108-
size of IO operations (This is based on a good guess, no high science
3109-
involved)
3110-
*/
31113108
if (flag & HA_STATUS_CONST)
31123109
stats.block_size= 4096;
31133110

31143111
if ((*iop)->table_metadata(&stats, share->table_name,
31153112
(uint)share->table_name_length, flag))
3113+
{
3114+
error_code= (uint)(*iop)->error_code(); /* capture NOW before it clears */
31163115
goto error;
3116+
}
31173117
}
31183118

31193119
if (flag & HA_STATUS_AUTO)
31203120
stats.auto_increment_value= (*iop)->last_insert_id();
31213121

3122-
/*
3123-
If ::info created it's own transaction, close it. This happens in case
3124-
of show table status;
3125-
*/
31263122
tmp_txn->release(&tmp_io);
3127-
31283123
DBUG_RETURN(0);
31293124

31303125
error:
31313126
if (iop && *iop)
31323127
{
3133-
my_printf_error((*iop)->error_code(), "Received error: %d : %s", MYF(0),
3134-
(*iop)->error_code(), (*iop)->error_str());
3128+
uint remote_err= error_code; /* already captured before goto */
3129+
if ((flag & HA_STATUS_VARIABLE) &&
3130+
(remote_err == 1146 || /* ER_NO_SUCH_TABLE */
3131+
remote_err == 2002 || /* CR_CONNECTION_ERROR */
3132+
remote_err == 2003 || /* CR_CONN_HOST_ERROR */
3133+
remote_err == 2005 || /* CR_UNKNOWN_HOST */
3134+
remote_err == 2006 || /* CR_SERVER_GONE_ERROR */
3135+
remote_err == 2013)) /* CR_SERVER_LOST */
3136+
{
3137+
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
3138+
ER_QUERY_ON_FOREIGN_DATA_SOURCE,
3139+
"FederatedX: Table '%s' is inaccessible: "
3140+
"%d : %s",
3141+
share->table_name,
3142+
remote_err,
3143+
(*iop)->error_str());
3144+
error_code= 0;
3145+
}
3146+
else
3147+
{
3148+
my_printf_error(remote_err,
3149+
"Received error: %d : %s", MYF(0),
3150+
remote_err, (*iop)->error_str());
3151+
error_code= remote_err;
3152+
}
31353153
}
31363154
else if (remote_error_number != -1 /* error already reported */)
31373155
{

0 commit comments

Comments
 (0)