forked from huawei/openGauss-server
251 lines
9.3 KiB
Diff
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;
|