openGauss-server/third_party/dependency/mysql_fdw/1-mysql_fdw-2.5.3_patch.patch

251 lines
9.3 KiB
Diff

diff --git connection.c connection.c
index cb9b90d..0fdd890 100644
--- connection.c
+++ connection.c
@@ -23,8 +23,10 @@
#include "mpg_wchar.h"
#include "miscadmin.h"
#include "utils/hsearch.h"
+#include "utils/inval.h"
#include "utils/memutils.h"
#include "utils/resowner.h"
+#include "utils/syscache.h"
/* Length of host */
#define HOST_LEN 256
@@ -46,6 +48,9 @@ typedef struct ConnCacheEntry
{
ConnCacheKey key; /* hash key (must be first) */
MYSQL *conn; /* connection to foreign server, or NULL */
+ bool invalidated; /* true if reconnect is pending */
+ uint32 server_hashvalue; /* hash value of foreign server OID */
+ uint32 mapping_hashvalue; /* hash value of user mapping OID */
} ConnCacheEntry;
/*
@@ -53,6 +58,8 @@ typedef struct ConnCacheEntry
*/
static HTAB *ConnectionHash = NULL;
+static void mysql_inval_callback(Datum arg, int cacheid, uint32 hashvalue);
+
/*
* mysql_get_connection:
* Get a connection which can be used to execute queries on
@@ -80,6 +87,15 @@ mysql_get_connection(ForeignServer *server, UserMapping *user, mysql_opt *opt)
ConnectionHash = hash_create("mysql_fdw connections", 8,
&ctl,
HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
+
+ /*
+ * Register some callback functions that manage connection cleanup.
+ * This should be done just once in each backend.
+ */
+ CacheRegisterSyscacheCallback(FOREIGNSERVEROID,
+ mysql_inval_callback, (Datum) 0);
+ CacheRegisterSyscacheCallback(USERMAPPINGOID,
+ mysql_inval_callback, (Datum) 0);
}
/* Create hash key for the entry. Assume no pad bytes in key struct */
@@ -95,8 +111,22 @@ mysql_get_connection(ForeignServer *server, UserMapping *user, mysql_opt *opt)
/* initialize new hashtable entry (key is already filled in) */
entry->conn = NULL;
}
+
+ /* If an existing entry has invalid connection then release it */
+ if (entry->conn != NULL && entry->invalidated)
+ {
+ elog(DEBUG3, "disconnecting mysql_fdw connection %p for option changes to take effect",
+ entry->conn);
+ _mysql_close(entry->conn);
+ entry->conn = NULL;
+ }
+
if (entry->conn == NULL)
{
+#if PG_VERSION_NUM < 90600
+ Oid umoid;
+#endif
+
entry->conn = mysql_connect(
opt->svr_address,
opt->svr_username,
@@ -113,6 +143,35 @@ mysql_get_connection(ForeignServer *server, UserMapping *user, mysql_opt *opt)
);
elog(DEBUG3, "new mysql_fdw connection %p for server \"%s\"",
entry->conn, server->servername);
+
+ /*
+ * Once the connection is established, then set the connection
+ * invalidation flag to false, also set the server and user mapping
+ * hash values.
+ */
+ entry->invalidated = false;
+ entry->server_hashvalue =
+ GetSysCacheHashValue1(FOREIGNSERVEROID,
+ ObjectIdGetDatum(server->serverid));
+#if PG_VERSION_NUM >= 90600
+ entry->mapping_hashvalue =
+ GetSysCacheHashValue1(USERMAPPINGOID,
+ ObjectIdGetDatum(user->umid));
+#else
+ /* Pre-9.6, UserMapping doesn't store its OID, so look it up again */
+ umoid = GetSysCacheOid2(USERMAPPINGUSERSERVER,
+ ObjectIdGetDatum(user->userid),
+ ObjectIdGetDatum(user->serverid));
+ if (!OidIsValid(umoid))
+ {
+ /* Not found for the specific user -- try PUBLIC */
+ umoid = GetSysCacheOid2(USERMAPPINGUSERSERVER,
+ ObjectIdGetDatum(InvalidOid),
+ ObjectIdGetDatum(user->serverid));
+ }
+ entry->mapping_hashvalue =
+ GetSysCacheHashValue1(USERMAPPINGOID, ObjectIdGetDatum(umoid));
+#endif
}
return entry->conn;
}
@@ -233,3 +292,36 @@ mysql_connect(
return conn;
}
+
+/*
+ * Connection invalidation callback function for mysql.
+ *
+ * After a change to a pg_foreign_server or pg_user_mapping catalog entry,
+ * mark connections depending on that entry as needing to be remade. This
+ * implementation is similar as pgfdw_inval_callback.
+ */
+static void
+mysql_inval_callback(Datum arg, int cacheid, uint32 hashvalue)
+{
+ HASH_SEQ_STATUS scan;
+ ConnCacheEntry *entry;
+
+ Assert(cacheid == FOREIGNSERVEROID || cacheid == USERMAPPINGOID);
+
+ /* ConnectionHash must exist already, if we're registered */
+ hash_seq_init(&scan, ConnectionHash);
+ while ((entry = (ConnCacheEntry *) hash_seq_search(&scan)))
+ {
+ /* Ignore invalid entries */
+ if (entry->conn == NULL)
+ continue;
+
+ /* hashvalue == 0 means a cache reset, must clear all state */
+ if (hashvalue == 0 ||
+ (cacheid == FOREIGNSERVEROID &&
+ entry->server_hashvalue == hashvalue) ||
+ (cacheid == USERMAPPINGOID &&
+ entry->mapping_hashvalue == hashvalue))
+ entry->invalidated = true;
+ }
+}
diff --git expected/mysql_fdw.out expected/mysql_fdw.out
index aca67e4..6048936 100644
--- expected/mysql_fdw.out
+++ expected/mysql_fdw.out
@@ -1,7 +1,11 @@
+\set MYSQL_HOST '\'localhost\''
+\set MYSQL_PORT '\'3306\''
+\set MYSQL_USER_NAME '\'foo\''
+\set MYSQL_PASS '\'bar\''
\c postgres postgres
CREATE EXTENSION mysql_fdw;
-CREATE SERVER mysql_svr FOREIGN DATA WRAPPER mysql_fdw;
-CREATE USER MAPPING FOR postgres SERVER mysql_svr OPTIONS(username 'foo', password 'bar');
+CREATE SERVER mysql_svr FOREIGN DATA WRAPPER mysql_fdw OPTIONS (host :MYSQL_HOST, port :MYSQL_PORT);;
+CREATE USER MAPPING FOR postgres SERVER mysql_svr OPTIONS(username :MYSQL_USER_NAME, password :MYSQL_PASS);
CREATE FOREIGN TABLE department(department_id int, department_name text) SERVER mysql_svr OPTIONS(dbname 'testdb', table_name 'department');
CREATE FOREIGN TABLE employee(emp_id int, emp_name text, emp_dept_id int) SERVER mysql_svr OPTIONS(dbname 'testdb', table_name 'employee');
CREATE FOREIGN TABLE empdata(emp_id int, emp_dat bytea) SERVER mysql_svr OPTIONS(dbname 'testdb', table_name 'empdata');
@@ -362,6 +366,36 @@ SELECT test_param_where2(1, 'One');
1
(1 row)
+-- FDW-121: After a change to a pg_foreign_server or pg_user_mapping catalog
+-- entry, existing connection should be invalidated and should make new
+-- connection using the updated connection details.
+-- Alter SERVER option.
+-- Set wrong host, subsequent operation on this server should use updated
+-- details and fail as the host address is not correct.
+ALTER SERVER mysql_svr OPTIONS (SET host 'localhos');
+SELECT * FROM numbers ORDER BY 1 LIMIT 1;
+ERROR: failed to connect to MySQL: Unknown MySQL server host 'localhos' (2)
+-- Set the correct hostname, next operation should succeed.
+ALTER SERVER mysql_svr OPTIONS (SET host :MYSQL_HOST);
+SELECT * FROM numbers ORDER BY 1 LIMIT 1;
+ a | b
+---+-----
+ 1 | One
+(1 row)
+
+-- Alter USER MAPPING option.
+-- Set wrong username and password, next operation should fail.
+ALTER USER MAPPING FOR postgres SERVER mysql_svr OPTIONS(SET username 'foo1', SET password 'bar1');
+SELECT * FROM numbers ORDER BY 1 LIMIT 1;
+ERROR: failed to connect to MySQL: Access denied for user 'foo1'@'localhost' (using password: YES)
+-- Set correct username and password, next operation should succeed.
+ALTER USER MAPPING FOR postgres SERVER mysql_svr OPTIONS(SET username :MYSQL_USER_NAME, SET password :MYSQL_PASS);
+SELECT * FROM numbers ORDER BY 1 LIMIT 1;
+ a | b
+---+-----
+ 1 | One
+(1 row)
+
DELETE FROM employee;
DELETE FROM department;
DELETE FROM empdata;
diff --git sql/mysql_fdw.sql sql/mysql_fdw.sql
index 78efca8..4fd7ce3 100644
--- sql/mysql_fdw.sql
+++ sql/mysql_fdw.sql
@@ -1,7 +1,12 @@
+\set MYSQL_HOST '\'localhost\''
+\set MYSQL_PORT '\'3306\''
+\set MYSQL_USER_NAME '\'foo\''
+\set MYSQL_PASS '\'bar\''
+
\c postgres postgres
CREATE EXTENSION mysql_fdw;
-CREATE SERVER mysql_svr FOREIGN DATA WRAPPER mysql_fdw;
-CREATE USER MAPPING FOR postgres SERVER mysql_svr OPTIONS(username 'foo', password 'bar');
+CREATE SERVER mysql_svr FOREIGN DATA WRAPPER mysql_fdw OPTIONS (host :MYSQL_HOST, port :MYSQL_PORT);;
+CREATE USER MAPPING FOR postgres SERVER mysql_svr OPTIONS(username :MYSQL_USER_NAME, password :MYSQL_PASS);
CREATE FOREIGN TABLE department(department_id int, department_name text) SERVER mysql_svr OPTIONS(dbname 'testdb', table_name 'department');
CREATE FOREIGN TABLE employee(emp_id int, emp_name text, emp_dept_id int) SERVER mysql_svr OPTIONS(dbname 'testdb', table_name 'employee');
@@ -82,6 +87,29 @@ create or replace function test_param_where2(integer, text) returns integer as '
SELECT test_param_where2(1, 'One');
+-- FDW-121: After a change to a pg_foreign_server or pg_user_mapping catalog
+-- entry, existing connection should be invalidated and should make new
+-- connection using the updated connection details.
+
+-- Alter SERVER option.
+-- Set wrong host, subsequent operation on this server should use updated
+-- details and fail as the host address is not correct.
+ALTER SERVER mysql_svr OPTIONS (SET host 'localhos');
+SELECT * FROM numbers ORDER BY 1 LIMIT 1;
+
+-- Set the correct hostname, next operation should succeed.
+ALTER SERVER mysql_svr OPTIONS (SET host :MYSQL_HOST);
+SELECT * FROM numbers ORDER BY 1 LIMIT 1;
+
+-- Alter USER MAPPING option.
+-- Set wrong username and password, next operation should fail.
+ALTER USER MAPPING FOR postgres SERVER mysql_svr OPTIONS(SET username 'foo1', SET password 'bar1');
+SELECT * FROM numbers ORDER BY 1 LIMIT 1;
+
+-- Set correct username and password, next operation should succeed.
+ALTER USER MAPPING FOR postgres SERVER mysql_svr OPTIONS(SET username :MYSQL_USER_NAME, SET password :MYSQL_PASS);
+SELECT * FROM numbers ORDER BY 1 LIMIT 1;
+
DELETE FROM employee;
DELETE FROM department;
DELETE FROM empdata;