diff --git a/contrib/mysql_fdw/Makefile b/contrib/mysql_fdw/Makefile index 1b3213adbb8446370ef3ca7591664a8d1c87777f..e43521352096d0ac8deb5fa0c9be6096d0399da8 100644 --- a/contrib/mysql_fdw/Makefile +++ b/contrib/mysql_fdw/Makefile @@ -48,6 +48,14 @@ define create_mysql_fdw_sources rm -rf $(MYSQL_FDW_MEGRED_SOURCES_DIR); \ mkdir $(MYSQL_FDW_MEGRED_SOURCES_DIR); \ tar xfzv $(MYSQL_FDW_DIR)/$(MYSQL_FDW_PACKAGE).tar.gz -C $(MYSQL_FDW_MEGRED_SOURCES_DIR) &> /dev/null; \ + for ((i=1;i<=99;i++)); \ + do \ + file_name="$(MYSQL_FDW_DIR)/$$i-mysql_fdw-2.5.3_patch.patch"; \ + if [ ! -f "$$file_name" ]; then \ + exit 0; \ + fi; \ + patch -p0 -d $(MYSQL_FDW_MEGRED_SOURCES_DIR)/$(MYSQL_FDW_PACKAGE) < $$file_name &> /dev/null; \ + done rename ".c" ".cpp" $(MYSQL_FDW_MEGRED_SOURCES_DIR)/$(MYSQL_FDW_PACKAGE)/*.c; \ - patch -d $(MYSQL_FDW_MEGRED_SOURCES_DIR)/$(MYSQL_FDW_PACKAGE) < $(MYSQL_FDW_DIR)/$(MYSQL_FDW_PATCH).patch &> /dev/null; + patch -p0 -d $(MYSQL_FDW_MEGRED_SOURCES_DIR)/$(MYSQL_FDW_PACKAGE) < $(MYSQL_FDW_DIR)/$(MYSQL_FDW_PATCH).patch &> /dev/null; endef diff --git a/third_party/dependency/mysql_fdw/1-mysql_fdw-2.5.3_patch.patch b/third_party/dependency/mysql_fdw/1-mysql_fdw-2.5.3_patch.patch new file mode 100644 index 0000000000000000000000000000000000000000..b311a1543a9ba2426caa1d5642b92fba3416bc26 --- /dev/null +++ b/third_party/dependency/mysql_fdw/1-mysql_fdw-2.5.3_patch.patch @@ -0,0 +1,250 @@ +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; diff --git a/third_party/dependency/mysql_fdw/2-mysql_fdw-2.5.3_patch.patch b/third_party/dependency/mysql_fdw/2-mysql_fdw-2.5.3_patch.patch new file mode 100644 index 0000000000000000000000000000000000000000..c05a50e565fa02cca9ed1c0df3e435ac685a383e --- /dev/null +++ b/third_party/dependency/mysql_fdw/2-mysql_fdw-2.5.3_patch.patch @@ -0,0 +1,190 @@ +diff --git expected/mysql_fdw.out expected/mysql_fdw.out +index 6048936..2d77913 100644 +--- expected/mysql_fdw.out ++++ expected/mysql_fdw.out +@@ -10,6 +10,9 @@ CREATE FOREIGN TABLE department(department_id int, department_name text) SERVER + 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'); + CREATE FOREIGN TABLE numbers(a int, b varchar(255)) SERVER mysql_svr OPTIONS (dbname 'testdb', table_name 'numbers'); ++CREATE FOREIGN TABLE fdw126_ft1(stu_id int, stu_name varchar(255)) SERVER mysql_svr OPTIONS (dbname 'testdb1', table_name 'student'); ++CREATE FOREIGN TABLE fdw126_ft2(stu_id int, stu_name varchar(255)) SERVER mysql_svr OPTIONS (table_name 'student'); ++CREATE FOREIGN TABLE fdw126_ft3(a int, b varchar(255)) SERVER mysql_svr OPTIONS (dbname 'testdb1', table_name 'numbers'); + SELECT * FROM department LIMIT 10; + department_id | department_name + ---------------+----------------- +@@ -396,6 +399,46 @@ SELECT * FROM numbers ORDER BY 1 LIMIT 1; + 1 | One + (1 row) + ++-- FDW-126: Insert/update/delete statement failing in mysql_fdw by picking ++-- wrong database name. ++-- Verify the INSERT/UPDATE/DELETE operations on another foreign table which ++-- resides in the another database in MySQL. The previous commands performs ++-- the operation on foreign table created for tables in testdb MySQL database. ++-- Below operations will be performed for foreign table created for table in ++-- testdb1 MySQL database. ++INSERT INTO fdw126_ft1 VALUES(1, 'One'); ++UPDATE fdw126_ft1 SET stu_name = 'one' WHERE stu_id = 1; ++DELETE FROM fdw126_ft1 WHERE stu_id = 1; ++-- Select on employee foreign table which is created for employee table from ++-- testdb MySQL database. This call is just to cross verify if everything is ++-- working correctly. ++SELECT * FROM employee ORDER BY 1 LIMIT 1; ++ emp_id | emp_name | emp_dept_id ++--------+----------+------------- ++ 1 | emp - 1 | 1 ++(1 row) ++ ++-- Insert into fdw126_ft2 table which does not have dbname specified while ++-- creating the foreign table, so it will consider the schema name of foreign ++-- table as database name and try to connect/lookup into that database. Will ++-- throw an error. ++INSERT INTO fdw126_ft2 VALUES(2, 'Two'); ++ERROR: failed to execute the MySQL query: ++Unknown database 'public' ++-- Check with the same table name from different database. fdw126_ft3 is ++-- pointing to the testdb1.numbers and not testdb.numbers table. ++-- INSERT/UPDATE/DELETE should be failing. SELECT will return no rows. ++INSERT INTO fdw126_ft3 VALUES(1, 'One'); ++ERROR: first column of remote table must be unique for INSERT/UPDATE/DELETE operation ++SELECT * FROM fdw126_ft3 ORDER BY 1 LIMIT 1; ++ a | b ++---+--- ++(0 rows) ++ ++UPDATE fdw126_ft3 SET b = 'one' WHERE a = 1; ++ERROR: first column of remote table must be unique for INSERT/UPDATE/DELETE operation ++DELETE FROM fdw126_ft3 WHERE a = 1; ++ERROR: first column of remote table must be unique for INSERT/UPDATE/DELETE operation + DELETE FROM employee; + DELETE FROM department; + DELETE FROM empdata; +@@ -406,6 +449,9 @@ DROP FOREIGN TABLE numbers; + DROP FOREIGN TABLE department; + DROP FOREIGN TABLE employee; + DROP FOREIGN TABLE empdata; ++DROP FOREIGN TABLE fdw126_ft1; ++DROP FOREIGN TABLE fdw126_ft2; ++DROP FOREIGN TABLE fdw126_ft3; + DROP USER MAPPING FOR postgres SERVER mysql_svr; + DROP SERVER mysql_svr; + DROP EXTENSION mysql_fdw CASCADE; +diff --git mysql_fdw.c mysql_fdw.c +index 2e520c2..eaba2e3 100644 +--- mysql_fdw.c ++++ mysql_fdw.c +@@ -964,7 +964,12 @@ mysql_is_column_unique(Oid foreigntableid) + /* Build the query */ + initStringInfo(&sql); + +- appendStringInfo(&sql, "EXPLAIN %s", options->svr_table); ++ /* ++ * Construct the query by prefixing the database name so that it can lookup ++ * in correct database. ++ */ ++ appendStringInfo(&sql, "EXPLAIN %s.%s", options->svr_database, ++ options->svr_table); + if (_mysql_query(conn, sql.data) != 0) + { + switch(_mysql_errno(conn)) +diff --git mysql_init.sh mysql_init.sh +index a970f19..bea095d 100644 +--- mysql_init.sh ++++ mysql_init.sh +@@ -4,4 +4,6 @@ mysql -h 127.0.0.1 -u foo -D testdb -e "CREATE TABLE department(department_id in + mysql -h 127.0.0.1 -u foo -D testdb -e "CREATE TABLE employee(emp_id int, emp_name text, emp_dept_id int, PRIMARY KEY (emp_id))" + mysql -h 127.0.0.1 -u foo -D testdb -e "CREATE TABLE empdata (emp_id int, emp_dat blob, PRIMARY KEY (emp_id))" + mysql -h 127.0.0.1 -u foo -D testdb -e "CREATE TABLE numbers (a int PRIMARY KEY, b varchar(255))" +- ++mysql -h 127.0.0.1 -u foo -D testdb -e "CREATE DATABASE testdb1" ++mysql -h 127.0.0.1 -u foo -D testdb1 -e "CREATE TABLE student (stu_id int PRIMARY KEY, stu_name text)" ++mysql -h 127.0.0.1 -u foo -D testdb1 -e "CREATE TABLE numbers (a int, b varchar(255))" +diff --git option.c option.c +index 880d984..574cb24 100644 +--- option.c ++++ option.c +@@ -254,8 +254,19 @@ mysql_get_options(Oid foreignoid) + if (!opt->svr_port) + opt->svr_port = MYSQL_PORT; + +- if (!opt->svr_table && f_table) +- opt->svr_table = get_rel_name(foreignoid); ++ /* ++ * When we don't have a table name or database name provided in the ++ * FOREIGN TABLE options, then use a foreign table name as the target table ++ * name and the namespace of the foreign table as a database name. ++ */ ++ if (f_table) ++ { ++ if (!opt->svr_table) ++ opt->svr_table = get_rel_name(foreignoid); ++ ++ if (!opt->svr_database) ++ opt->svr_database = get_namespace_name(get_rel_namespace(foreignoid)); ++ } + + return opt; + } +diff --git sql/mysql_fdw.sql sql/mysql_fdw.sql +index 4fd7ce3..b350c23 100644 +--- sql/mysql_fdw.sql ++++ sql/mysql_fdw.sql +@@ -12,6 +12,9 @@ CREATE FOREIGN TABLE department(department_id int, department_name text) SERVER + 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'); + CREATE FOREIGN TABLE numbers(a int, b varchar(255)) SERVER mysql_svr OPTIONS (dbname 'testdb', table_name 'numbers'); ++CREATE FOREIGN TABLE fdw126_ft1(stu_id int, stu_name varchar(255)) SERVER mysql_svr OPTIONS (dbname 'testdb1', table_name 'student'); ++CREATE FOREIGN TABLE fdw126_ft2(stu_id int, stu_name varchar(255)) SERVER mysql_svr OPTIONS (table_name 'student'); ++CREATE FOREIGN TABLE fdw126_ft3(a int, b varchar(255)) SERVER mysql_svr OPTIONS (dbname 'testdb1', table_name 'numbers'); + + SELECT * FROM department LIMIT 10; + SELECT * FROM employee LIMIT 10; +@@ -110,6 +113,38 @@ SELECT * FROM numbers ORDER BY 1 LIMIT 1; + 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; + ++ ++-- FDW-126: Insert/update/delete statement failing in mysql_fdw by picking ++-- wrong database name. ++ ++-- Verify the INSERT/UPDATE/DELETE operations on another foreign table which ++-- resides in the another database in MySQL. The previous commands performs ++-- the operation on foreign table created for tables in testdb MySQL database. ++-- Below operations will be performed for foreign table created for table in ++-- testdb1 MySQL database. ++INSERT INTO fdw126_ft1 VALUES(1, 'One'); ++UPDATE fdw126_ft1 SET stu_name = 'one' WHERE stu_id = 1; ++DELETE FROM fdw126_ft1 WHERE stu_id = 1; ++ ++-- Select on employee foreign table which is created for employee table from ++-- testdb MySQL database. This call is just to cross verify if everything is ++-- working correctly. ++SELECT * FROM employee ORDER BY 1 LIMIT 1; ++ ++-- Insert into fdw126_ft2 table which does not have dbname specified while ++-- creating the foreign table, so it will consider the schema name of foreign ++-- table as database name and try to connect/lookup into that database. Will ++-- throw an error. ++INSERT INTO fdw126_ft2 VALUES(2, 'Two'); ++ ++-- Check with the same table name from different database. fdw126_ft3 is ++-- pointing to the testdb1.numbers and not testdb.numbers table. ++-- INSERT/UPDATE/DELETE should be failing. SELECT will return no rows. ++INSERT INTO fdw126_ft3 VALUES(1, 'One'); ++SELECT * FROM fdw126_ft3 ORDER BY 1 LIMIT 1; ++UPDATE fdw126_ft3 SET b = 'one' WHERE a = 1; ++DELETE FROM fdw126_ft3 WHERE a = 1; ++ + DELETE FROM employee; + DELETE FROM department; + DELETE FROM empdata; +@@ -122,6 +157,9 @@ DROP FOREIGN TABLE numbers; + DROP FOREIGN TABLE department; + DROP FOREIGN TABLE employee; + DROP FOREIGN TABLE empdata; ++DROP FOREIGN TABLE fdw126_ft1; ++DROP FOREIGN TABLE fdw126_ft2; ++DROP FOREIGN TABLE fdw126_ft3; + DROP USER MAPPING FOR postgres SERVER mysql_svr; + DROP SERVER mysql_svr; + DROP EXTENSION mysql_fdw CASCADE; diff --git a/third_party/dependency/mysql_fdw/3-mysql_fdw-2.5.3_patch.patch b/third_party/dependency/mysql_fdw/3-mysql_fdw-2.5.3_patch.patch new file mode 100644 index 0000000000000000000000000000000000000000..b6e5b1e11ac9581fc65518c127c6971e08306d83 --- /dev/null +++ b/third_party/dependency/mysql_fdw/3-mysql_fdw-2.5.3_patch.patch @@ -0,0 +1,127 @@ +diff --git expected/mysql_fdw.out expected/mysql_fdw.out +index 2d77913..882674a 100644 +--- expected/mysql_fdw.out ++++ expected/mysql_fdw.out +@@ -13,6 +13,8 @@ CREATE FOREIGN TABLE numbers(a int, b varchar(255)) SERVER mysql_svr OPTIONS (db + CREATE FOREIGN TABLE fdw126_ft1(stu_id int, stu_name varchar(255)) SERVER mysql_svr OPTIONS (dbname 'testdb1', table_name 'student'); + CREATE FOREIGN TABLE fdw126_ft2(stu_id int, stu_name varchar(255)) SERVER mysql_svr OPTIONS (table_name 'student'); + CREATE FOREIGN TABLE fdw126_ft3(a int, b varchar(255)) SERVER mysql_svr OPTIONS (dbname 'testdb1', table_name 'numbers'); ++CREATE FOREIGN TABLE fdw126_ft4(a int, b varchar(255)) SERVER mysql_svr OPTIONS (dbname 'testdb1', table_name 'nosuchtable'); ++CREATE FOREIGN TABLE fdw126_ft5(a int, b varchar(255)) SERVER mysql_svr OPTIONS (dbname 'testdb2', table_name 'numbers'); + SELECT * FROM department LIMIT 10; + department_id | department_name + ---------------+----------------- +@@ -439,6 +441,14 @@ UPDATE fdw126_ft3 SET b = 'one' WHERE a = 1; + ERROR: first column of remote table must be unique for INSERT/UPDATE/DELETE operation + DELETE FROM fdw126_ft3 WHERE a = 1; + ERROR: first column of remote table must be unique for INSERT/UPDATE/DELETE operation ++-- Perform the ANALYZE on the foreign table which is not present on the remote ++-- side. Should not crash. ++-- The database is present but not the target table. ++ANALYZE fdw126_ft4; ++ERROR: relation testdb1.nosuchtable does not exist ++-- The database itself is not present. ++ANALYZE fdw126_ft5; ++ERROR: relation testdb2.numbers does not exist + DELETE FROM employee; + DELETE FROM department; + DELETE FROM empdata; +@@ -452,6 +462,8 @@ DROP FOREIGN TABLE empdata; + DROP FOREIGN TABLE fdw126_ft1; + DROP FOREIGN TABLE fdw126_ft2; + DROP FOREIGN TABLE fdw126_ft3; ++DROP FOREIGN TABLE fdw126_ft4; ++DROP FOREIGN TABLE fdw126_ft5; + DROP USER MAPPING FOR postgres SERVER mysql_svr; + DROP SERVER mysql_svr; + DROP EXTENSION mysql_fdw CASCADE; +diff --git mysql_fdw.c mysql_fdw.c +index eaba2e3..1c8c71f 100644 +--- mysql_fdw.c ++++ mysql_fdw.c +@@ -1209,7 +1209,6 @@ mysqlAnalyzeForeignTable(Relation relation, AcquireSampleRowsFunc *func, BlockNu + MYSQL_ROW row; + Oid foreignTableId = RelationGetRelid(relation); + mysql_opt *options; +- char *relname; + ForeignServer *server; + UserMapping *user; + ForeignTable *table; +@@ -1220,19 +1219,14 @@ mysqlAnalyzeForeignTable(Relation relation, AcquireSampleRowsFunc *func, BlockNu + + /* Fetch options */ + options = mysql_get_options(foreignTableId); ++ Assert(options->svr_database != NULL && options->svr_table != NULL); + + /* Connect to the server */ + conn = mysql_get_connection(server, user, options); + + /* Build the query */ + initStringInfo(&sql); +- +- /* If no table name specified, use the foreign table name */ +- relname = options->svr_table; +- if ( relname == NULL) +- relname = RelationGetRelationName(relation); +- +- mysql_deparse_analyze(&sql, options->svr_database, relname); ++ mysql_deparse_analyze(&sql, options->svr_database, options->svr_table); + + if (_mysql_query(conn, sql.data) != 0) + { +@@ -1264,6 +1258,18 @@ mysqlAnalyzeForeignTable(Relation relation, AcquireSampleRowsFunc *func, BlockNu + } + } + result = _mysql_store_result(conn); ++ ++ /* ++ * To get the table size in ANALYZE operation, we run a SELECT query by ++ * passing the database name and table name. So if the remote table is not ++ * present, then we end up getting zero rows. Throw an error in that case. ++ */ ++ if (_mysql_num_rows(result) == 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_FDW_TABLE_NOT_FOUND), ++ errmsg("relation %s.%s does not exist", options->svr_database, ++ options->svr_table))); ++ + if (result) + { + row = _mysql_fetch_row(result); +diff --git sql/mysql_fdw.sql sql/mysql_fdw.sql +index b350c23..776220a 100644 +--- sql/mysql_fdw.sql ++++ sql/mysql_fdw.sql +@@ -15,6 +15,8 @@ CREATE FOREIGN TABLE numbers(a int, b varchar(255)) SERVER mysql_svr OPTIONS (db + CREATE FOREIGN TABLE fdw126_ft1(stu_id int, stu_name varchar(255)) SERVER mysql_svr OPTIONS (dbname 'testdb1', table_name 'student'); + CREATE FOREIGN TABLE fdw126_ft2(stu_id int, stu_name varchar(255)) SERVER mysql_svr OPTIONS (table_name 'student'); + CREATE FOREIGN TABLE fdw126_ft3(a int, b varchar(255)) SERVER mysql_svr OPTIONS (dbname 'testdb1', table_name 'numbers'); ++CREATE FOREIGN TABLE fdw126_ft4(a int, b varchar(255)) SERVER mysql_svr OPTIONS (dbname 'testdb1', table_name 'nosuchtable'); ++CREATE FOREIGN TABLE fdw126_ft5(a int, b varchar(255)) SERVER mysql_svr OPTIONS (dbname 'testdb2', table_name 'numbers'); + + SELECT * FROM department LIMIT 10; + SELECT * FROM employee LIMIT 10; +@@ -145,6 +147,14 @@ SELECT * FROM fdw126_ft3 ORDER BY 1 LIMIT 1; + UPDATE fdw126_ft3 SET b = 'one' WHERE a = 1; + DELETE FROM fdw126_ft3 WHERE a = 1; + ++-- Perform the ANALYZE on the foreign table which is not present on the remote ++-- side. Should not crash. ++-- The database is present but not the target table. ++ANALYZE fdw126_ft4; ++-- The database itself is not present. ++ANALYZE fdw126_ft5; ++ ++ + DELETE FROM employee; + DELETE FROM department; + DELETE FROM empdata; +@@ -160,6 +170,8 @@ DROP FOREIGN TABLE empdata; + DROP FOREIGN TABLE fdw126_ft1; + DROP FOREIGN TABLE fdw126_ft2; + DROP FOREIGN TABLE fdw126_ft3; ++DROP FOREIGN TABLE fdw126_ft4; ++DROP FOREIGN TABLE fdw126_ft5; + DROP USER MAPPING FOR postgres SERVER mysql_svr; + DROP SERVER mysql_svr; + DROP EXTENSION mysql_fdw CASCADE; diff --git a/third_party/dependency/mysql_fdw/huawei_mysql_fdw-2.5.3_patch.patch b/third_party/dependency/mysql_fdw/huawei_mysql_fdw-2.5.3_patch.patch index c445da0654021ca2eaab3476f586ac8f03f55966..9fa8fb0de378b5d07ad2b4e099d61692b30c3030 100644 --- a/third_party/dependency/mysql_fdw/huawei_mysql_fdw-2.5.3_patch.patch +++ b/third_party/dependency/mysql_fdw/huawei_mysql_fdw-2.5.3_patch.patch @@ -1,7 +1,7 @@ -diff --git a/code/mysql_fdw-REL-2_5_3/Makefile b/code/mysql_fdw-REL-2_5_3/Makefile -index d5e7b36..157a6cf 100644 ---- a/code/mysql_fdw-REL-2_5_3/Makefile -+++ b/code/mysql_fdw-REL-2_5_3/Makefile +diff --git Makefile Makefile +index d5e7b36..4cd59e9 100644 +--- Makefile ++++ Makefile @@ -31,6 +31,8 @@ else MYSQL_LIB = mysqlclient endif @@ -37,25 +37,27 @@ index d5e7b36..157a6cf 100644 include $(top_builddir)/src/Makefile.global include $(top_srcdir)/contrib/contrib-global.mk endif -diff --git a/code/mysql_fdw-REL-2_5_3/connection.cpp b/code/mysql_fdw-REL-2_5_3/connection.cpp -index 6b18027..0a9e40f 100644 ---- a/code/mysql_fdw-REL-2_5_3/connection.cpp -+++ b/code/mysql_fdw-REL-2_5_3/connection.cpp -@@ -22,6 +22,7 @@ - #include "utils/hsearch.h" +diff --git connection.cpp connection.cpp +index a517a73..3fc2f20 100644 +--- connection.cpp ++++ connection.cpp +@@ -24,6 +24,7 @@ #include "utils/memutils.h" #include "utils/resowner.h" + #include "utils/syscache.h" +#include "storage/ipc.h" /* Length of host */ #define HOST_LEN 256 -@@ -48,8 +49,13 @@ typedef struct ConnCacheEntry +@@ -53,10 +54,15 @@ typedef struct ConnCacheEntry /* * Connection cache (initialized on first use) */ -static HTAB *ConnectionHash = NULL; +static THR_LOCAL HTAB *ConnectionHash = NULL; + static void mysql_inval_callback(Datum arg, int cacheid, uint32 hashvalue); + +static void +mysql_fdw_exit(int code, Datum arg) +{ @@ -64,7 +66,7 @@ index 6b18027..0a9e40f 100644 /* * mysql_get_connection: * Get a connection which can be used to execute queries on -@@ -73,10 +79,11 @@ mysql_get_connection(ForeignServer *server, UserMapping *user, mysql_opt *opt) +@@ -80,7 +86,7 @@ mysql_get_connection(ForeignServer *server, UserMapping *user, mysql_opt *opt) ctl.hash = tag_hash; /* allocate ConnectionHash in the cache context */ @@ -73,11 +75,15 @@ index 6b18027..0a9e40f 100644 ConnectionHash = hash_create("mysql_fdw connections", 8, &ctl, HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT); +@@ -93,6 +99,7 @@ mysql_get_connection(ForeignServer *server, UserMapping *user, mysql_opt *opt) + mysql_inval_callback, (Datum) 0); + CacheRegisterSyscacheCallback(USERMAPPINGOID, + mysql_inval_callback, (Datum) 0); + on_proc_exit(&mysql_fdw_exit, PointerGetDatum(NULL)); } /* Create hash key for the entry. Assume no pad bytes in key struct */ -@@ -86,7 +93,7 @@ mysql_get_connection(ForeignServer *server, UserMapping *user, mysql_opt *opt) +@@ -102,7 +109,7 @@ mysql_get_connection(ForeignServer *server, UserMapping *user, mysql_opt *opt) /* * Find or create cached entry for requested connection. */ @@ -86,7 +92,7 @@ index 6b18027..0a9e40f 100644 if (!found) { /* initialize new hashtable entry (key is already filled in) */ -@@ -137,6 +144,9 @@ mysql_cleanup_connection(void) +@@ -196,6 +203,9 @@ mysql_cleanup_connection(void) _mysql_close(entry->conn); entry->conn = NULL; } @@ -96,10 +102,10 @@ index 6b18027..0a9e40f 100644 } /* -diff --git a/code/mysql_fdw-REL-2_5_3/deparse.cpp b/code/mysql_fdw-REL-2_5_3/deparse.cpp +diff --git deparse.cpp deparse.cpp index a75c270..94b1799 100644 ---- a/code/mysql_fdw-REL-2_5_3/deparse.cpp -+++ b/code/mysql_fdw-REL-2_5_3/deparse.cpp +--- deparse.cpp ++++ deparse.cpp @@ -20,7 +20,7 @@ #include "pgtime.h" @@ -216,10 +222,10 @@ index a75c270..94b1799 100644 first = false; } appendStringInfoChar(buf, ']'); -diff --git a/code/mysql_fdw-REL-2_5_3/mysql_fdw.cpp b/code/mysql_fdw-REL-2_5_3/mysql_fdw.cpp -index f1e26c3..49243d1 100644 ---- a/code/mysql_fdw-REL-2_5_3/mysql_fdw.cpp -+++ b/code/mysql_fdw-REL-2_5_3/mysql_fdw.cpp +diff --git mysql_fdw.cpp mysql_fdw.cpp +index d518e2e..a56fae6 100644 +--- mysql_fdw.cpp ++++ mysql_fdw.cpp @@ -53,7 +53,7 @@ #include "utils/timestamp.h" #include "utils/formatting.h" @@ -448,7 +454,7 @@ index f1e26c3..49243d1 100644 festate->conn = conn; festate->cursor_exists = false; -@@ -1047,12 +1083,12 @@ mysqlGetForeignPaths(PlannerInfo *root,RelOptInfo *baserel,Oid foreigntableid) +@@ -1052,12 +1088,12 @@ mysqlGetForeignPaths(PlannerInfo *root,RelOptInfo *baserel,Oid foreigntableid) mysqlEstimateCosts(root, baserel, &startup_cost, &total_cost, foreigntableid); /* Create a ForeignPath node and add it as only possible path */ @@ -463,7 +469,7 @@ index f1e26c3..49243d1 100644 startup_cost, total_cost, NIL, /* no pathkeys */ -@@ -1155,7 +1191,7 @@ mysqlGetForeignPlan( +@@ -1160,7 +1196,7 @@ mysqlGetForeignPlan( mysql_append_where_clause(&sql, root, baserel, remote_conds, true, ¶ms_list); @@ -472,7 +478,7 @@ index f1e26c3..49243d1 100644 (root->parse->commandType == CMD_UPDATE || root->parse->commandType == CMD_DELETE)) { -@@ -1319,7 +1355,7 @@ mysqlPlanForeignModify(PlannerInfo *root, +@@ -1330,7 +1366,7 @@ mysqlPlanForeignModify(PlannerInfo *root, #if PG_VERSION_NUM >= 90500 Bitmapset *tmpset = bms_copy(rte->updatedCols); #else @@ -481,7 +487,7 @@ index f1e26c3..49243d1 100644 #endif AttrNumber col; -@@ -1633,7 +1669,7 @@ mysqlExecForeignUpdate(EState *estate, +@@ -1644,7 +1680,7 @@ mysqlExecForeignUpdate(EState *estate, n_params = list_length(fmstate->retrieved_attrs); mysql_bind_buffer = (MYSQL_BIND*) palloc0(sizeof(MYSQL_BIND) * n_params); @@ -490,7 +496,7 @@ index f1e26c3..49243d1 100644 /* Bind the values */ foreach(lc, fmstate->retrieved_attrs) -@@ -1822,7 +1858,7 @@ mysqlExecForeignDelete(EState *estate, +@@ -1833,7 +1869,7 @@ mysqlExecForeignDelete(EState *estate, static void mysqlEndForeignModify(EState *estate, ResultRelInfo *resultRelInfo) { @@ -499,10 +505,10 @@ index f1e26c3..49243d1 100644 if (festate && festate->stmt) { -diff --git a/code/mysql_fdw-REL-2_5_3/mysql_fdw.h b/code/mysql_fdw-REL-2_5_3/mysql_fdw.h -index 5b543cd..b2a7011 100644 ---- a/code/mysql_fdw-REL-2_5_3/mysql_fdw.h -+++ b/code/mysql_fdw-REL-2_5_3/mysql_fdw.h +diff --git mysql_fdw.h mysql_fdw.h +index 5b543cd..ea58af7 100644 +--- mysql_fdw.h ++++ mysql_fdw.h @@ -135,31 +135,31 @@ extern bool is_foreign_expr(PlannerInfo *root, Expr *expr); @@ -533,9 +539,9 @@ index 5b543cd..b2a7011 100644 -bool ((*_mysql_ssl_set)(MYSQL *mysql, const char *key, const char *cert, const char *ca, const char *capath, const char *cipher)); -MYSQL *((*_mysql_real_connect)(MYSQL *mysql, +extern int ((*_mysql_options)(MYSQL *mysql,enum mysql_option option, const void *arg)); ++extern int ((*_mysql_stmt_prepare)(MYSQL_STMT *stmt, const char *query, unsigned long length)); +extern int ((*_mysql_stmt_execute)(MYSQL_STMT *stmt)); +extern int ((*_mysql_stmt_fetch)(MYSQL_STMT *stmt)); -+extern int ((*_mysql_stmt_prepare)(MYSQL_STMT *stmt, const char *query, unsigned long length)); +extern int ((*_mysql_query)(MYSQL *mysql, const char *q)); +extern bool ((*_mysql_stmt_attr_set)(MYSQL_STMT *stmt, enum enum_stmt_attr_type attr_type, const void *attr)); +extern bool ((*_mysql_stmt_close)(MYSQL_STMT * stmt)); @@ -582,10 +588,10 @@ index 5b543cd..b2a7011 100644 /* option.c headers */ -diff --git a/code/mysql_fdw-REL-2_5_3/mysql_query.cpp b/code/mysql_fdw-REL-2_5_3/mysql_query.cpp +diff --git mysql_query.cpp mysql_query.cpp index 8c25f5c..6093a5a 100644 ---- a/code/mysql_fdw-REL-2_5_3/mysql_query.cpp -+++ b/code/mysql_fdw-REL-2_5_3/mysql_query.cpp +--- mysql_query.cpp ++++ mysql_query.cpp @@ -21,7 +21,7 @@ #include @@ -734,10 +740,10 @@ index 8c25f5c..6093a5a 100644 switch (pgtyp) { -diff --git a/code/mysql_fdw-REL-2_5_3/option.cpp b/code/mysql_fdw-REL-2_5_3/option.cpp -index 880d984..f3b77f7 100644 ---- a/code/mysql_fdw-REL-2_5_3/option.cpp -+++ b/code/mysql_fdw-REL-2_5_3/option.cpp +diff --git option.cpp option.cpp +index 574cb24..5c92c50 100644 +--- option.cpp ++++ option.cpp @@ -81,7 +81,7 @@ static struct MySQLFdwOption valid_options[] = { NULL, InvalidOid } };