diff --git Makefile Makefile index 199504b..5666c6e 100644 --- Makefile +++ Makefile @@ -23,6 +23,8 @@ else MYSQL_LIB = mysqlclient endif +MYSQL_LIB = mariadb + UNAME = uname OS := $(shell $(UNAME)) ifeq ($(OS), Darwin) @@ -31,7 +33,7 @@ else DLSUFFIX = .so endif -PG_CPPFLAGS += -D _MYSQL_LIBNAME=\"lib$(MYSQL_LIB)$(DLSUFFIX)\" +PG_CPPFLAGS += -D _MYSQL_LIBNAME=\"lib$(MYSQL_LIB)$(DLSUFFIX)\" -Wno-parentheses ifdef USE_PGXS PG_CONFIG = pg_config @@ -45,9 +47,10 @@ $(error PostgreSQL 9.5, 9.6, 10, 11, 12, or 13 is required to compile this exten endif else -subdir = contrib/mysql_fdw -top_builddir = ../.. +top_builddir := $(TOP_DIR) include $(top_builddir)/src/Makefile.global include $(top_srcdir)/contrib/contrib-global.mk +exclude_option=-fPIE +override CPPFLAGS := $(filter-out $(exclude_option),$(CPPFLAGS)) endif diff --git connection.c connection.cpp similarity index 86% rename from connection.c rename to connection.cpp index bb5f6cd..d4d46fa 100644 --- connection.c +++ connection.cpp @@ -23,6 +23,7 @@ #include "utils/inval.h" #include "utils/memutils.h" #include "utils/syscache.h" +#include "storage/ipc.h" /* Length of host */ #define HOST_LEN 256 @@ -49,10 +50,11 @@ typedef struct ConnCacheEntry uint32 mapping_hashvalue; /* hash value of user mapping OID */ } ConnCacheEntry; -/* - * Connection cache (initialized on first use) - */ -static HTAB *ConnectionHash = NULL; +static void +mysql_fdw_exit(int code, Datum arg) +{ + mysql_cleanup_connection(); +} static void mysql_inval_callback(Datum arg, int cacheid, uint32 hashvalue); @@ -70,7 +72,7 @@ mysql_get_connection(ForeignServer *server, UserMapping *user, mysql_opt *opt) ConnCacheKey key; /* First time through, initialize connection cache hashtable */ - if (ConnectionHash == NULL) + if (u_sess->ext_fdw_ctx[MYSQL_TYPE_FDW].connList == NULL) { HASHCTL ctl; @@ -80,8 +82,8 @@ mysql_get_connection(ForeignServer *server, UserMapping *user, mysql_opt *opt) ctl.hash = tag_hash; /* Allocate ConnectionHash in the cache context */ - ctl.hcxt = CacheMemoryContext; - ConnectionHash = hash_create("mysql_fdw connections", 8, + ctl.hcxt = u_sess->cache_mem_cxt; + u_sess->ext_fdw_ctx[MYSQL_TYPE_FDW].connList = hash_create("mysql_fdw connections", 8, &ctl, HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT); @@ -93,6 +95,14 @@ mysql_get_connection(ForeignServer *server, UserMapping *user, mysql_opt *opt) mysql_inval_callback, (Datum) 0); CacheRegisterSyscacheCallback(USERMAPPINGOID, mysql_inval_callback, (Datum) 0); + if (IS_THREAD_POOL_SESSION) + { + u_sess->ext_fdw_ctx[MYSQL_TYPE_FDW].fdwExitFunc = mysql_fdw_exit; + } + else + { + on_proc_exit(mysql_fdw_exit, 0); + } } /* Create hash key for the entry. Assume no pad bytes in key struct */ @@ -102,7 +112,7 @@ mysql_get_connection(ForeignServer *server, UserMapping *user, mysql_opt *opt) /* * Find or create cached entry for requested connection. */ - entry = hash_search(ConnectionHash, &key, HASH_ENTER, &found); + entry = (ConnCacheEntry*)hash_search((HTAB*)u_sess->ext_fdw_ctx[MYSQL_TYPE_FDW].connList, &key, HASH_ENTER, &found); if (!found) { /* Initialize new hashtable entry (key is already filled in) */ @@ -170,10 +180,10 @@ mysql_cleanup_connection(void) HASH_SEQ_STATUS scan; ConnCacheEntry *entry; - if (ConnectionHash == NULL) + if (u_sess->ext_fdw_ctx[MYSQL_TYPE_FDW].connList == NULL) return; - hash_seq_init(&scan, ConnectionHash); + hash_seq_init(&scan, (HTAB*)u_sess->ext_fdw_ctx[MYSQL_TYPE_FDW].connList); while ((entry = (ConnCacheEntry *) hash_seq_search(&scan))) { if (entry->conn == NULL) @@ -183,6 +193,9 @@ mysql_cleanup_connection(void) mysql_close(entry->conn); entry->conn = NULL; } + /* clean-up memory */ + hash_destroy((HTAB*)u_sess->ext_fdw_ctx[MYSQL_TYPE_FDW].connList); + u_sess->ext_fdw_ctx[MYSQL_TYPE_FDW].connList = NULL; } /* @@ -194,10 +207,10 @@ mysql_release_connection(MYSQL *conn) HASH_SEQ_STATUS scan; ConnCacheEntry *entry; - if (ConnectionHash == NULL) + if (u_sess->ext_fdw_ctx[MYSQL_TYPE_FDW].connList == NULL) return; - hash_seq_init(&scan, ConnectionHash); + hash_seq_init(&scan, (HTAB*)u_sess->ext_fdw_ctx[MYSQL_TYPE_FDW].connList); while ((entry = (ConnCacheEntry *) hash_seq_search(&scan))) { if (entry->conn == NULL) @@ -212,6 +225,9 @@ mysql_release_connection(MYSQL *conn) break; } } + /* clean-up memory */ + hash_destroy((HTAB*)u_sess->ext_fdw_ctx[MYSQL_TYPE_FDW].connList); + u_sess->ext_fdw_ctx[MYSQL_TYPE_FDW].connList = NULL; } MYSQL * @@ -282,7 +298,7 @@ mysql_inval_callback(Datum arg, int cacheid, uint32 hashvalue) Assert(cacheid == FOREIGNSERVEROID || cacheid == USERMAPPINGOID); /* ConnectionHash must exist already, if we're registered */ - hash_seq_init(&scan, ConnectionHash); + hash_seq_init(&scan, (HTAB*)u_sess->ext_fdw_ctx[MYSQL_TYPE_FDW].connList); while ((entry = (ConnCacheEntry *) hash_seq_search(&scan))) { /* Ignore invalid entries */ diff --git deparse.c deparse.cpp similarity index 97% rename from deparse.c rename to deparse.cpp index 441abd4..5f9483e 100644 --- deparse.c +++ deparse.cpp @@ -14,7 +14,7 @@ #include "postgres.h" #include "access/heapam.h" -#include "access/htup_details.h" +#include "access/htup.h" #include "access/sysattr.h" #include "access/transam.h" #include "catalog/pg_collation.h" @@ -118,7 +118,7 @@ static void mysql_deparse_target_list(StringInfo buf, PlannerInfo *root, Bitmapset *attrs_used, List **retrieved_attrs); static void mysql_deparse_column_ref(StringInfo buf, int varno, int varattno, - PlannerInfo *root); + RangeTblEntry *rte); /* * Functions to construct string representation of a specific types. @@ -175,10 +175,10 @@ mysql_deparse_relation(StringInfo buf, Relation rel) static char * mysql_quote_identifier(const char *str, char quotechar) { - char *result = palloc(strlen(str) * 2 + 3); - char *res = result; + char *result = (char*)palloc(strlen(str) * 2 + 3); + char *res = result; - *res++ = quotechar; + *res++ = quotechar; while (*str) { if (*str == quotechar) @@ -238,7 +238,7 @@ mysql_deparse_select(StringInfo buf, PlannerInfo *root, RelOptInfo *baserel, * to *retrieved_attrs. */ void -mysql_deparse_insert(StringInfo buf, PlannerInfo *root, Index rtindex, +mysql_deparse_insert(StringInfo buf, RangeTblEntry *rte, Index rtindex, Relation rel, List *targetAttrs) { ListCell *lc; @@ -262,7 +262,7 @@ mysql_deparse_insert(StringInfo buf, PlannerInfo *root, Index rtindex, appendStringInfoString(buf, ", "); first = false; - mysql_deparse_column_ref(buf, rtindex, attnum, root); + mysql_deparse_column_ref(buf, rtindex, attnum, rte); } appendStringInfoString(buf, ") VALUES ("); @@ -331,7 +331,7 @@ mysql_deparse_target_list(StringInfo buf, PlannerInfo *root, Index rtindex, appendStringInfoString(buf, ", "); first = false; - mysql_deparse_column_ref(buf, rtindex, i, root); + mysql_deparse_column_ref(buf, rtindex, i, planner_rt_fetch(rtindex, root)); *retrieved_attrs = lappend_int(*retrieved_attrs, i); } } @@ -396,9 +396,8 @@ mysql_append_where_clause(StringInfo buf, PlannerInfo *root, */ static void mysql_deparse_column_ref(StringInfo buf, int varno, int varattno, - PlannerInfo *root) + RangeTblEntry *rte) { - RangeTblEntry *rte; char *colname = NULL; List *options; ListCell *lc; @@ -406,9 +405,6 @@ mysql_deparse_column_ref(StringInfo buf, int varno, int varattno, /* varno must not be any of OUTER_VAR, INNER_VAR and INDEX_VAR. */ Assert(!IS_SPECIAL_VARNO(varno)); - /* Get RangeTblEntry from array in PlannerInfo. */ - rte = planner_rt_fetch(varno, root); - /* * If it's a column of a foreign table, and it has the column_name FDW * option, use that value. @@ -456,8 +452,7 @@ mysql_deparse_string(StringInfo buf, const char *val, bool isstr) * Remove '{', '}', and \" character from the string. Because this * syntax is not recognize by the remote MySQL server. */ - if ((ch == '{' && i == 0) || (ch == '}' && (i == (strlen(val) - 1))) || - ch == '\"') + if ((ch == '{' && i == 0) || (ch == '}' && ((unsigned int)i == (strlen(val) - 1))) || ch == '\"') continue; if (isstr && ch == ',') @@ -647,7 +642,7 @@ mysql_deparse_update(StringInfo buf, PlannerInfo *root, Index rtindex, appendStringInfoString(buf, ", "); first = false; - mysql_deparse_column_ref(buf, rtindex, attnum, root); + mysql_deparse_column_ref(buf, rtindex, attnum, planner_rt_fetch(rtindex, root)); appendStringInfo(buf, " = ?"); pindex++; } @@ -689,7 +684,7 @@ mysql_deparse_var(Var *node, deparse_expr_cxt *context) { /* Var belongs to foreign table */ mysql_deparse_column_ref(buf, node->varno, node->varattno, - context->root); + planner_rt_fetch(node->varno, context->root)); } else { @@ -879,7 +874,7 @@ mysql_deparse_array_ref(SubscriptingRef *node, deparse_expr_cxt *context) appendStringInfoChar(buf, '['); if (lowlist_item) { - deparseExpr(lfirst(lowlist_item), context); + deparseExpr((Expr*)lfirst(lowlist_item), context); appendStringInfoChar(buf, ':'); #if PG_VERSION_NUM < 130000 lowlist_item = lnext(lowlist_item); @@ -887,7 +882,7 @@ mysql_deparse_array_ref(SubscriptingRef *node, deparse_expr_cxt *context) lowlist_item = lnext(node->reflowerindexpr, lowlist_item); #endif } - deparseExpr(lfirst(uplist_item), context); + deparseExpr((Expr*)lfirst(uplist_item), context); appendStringInfoChar(buf, ']'); } @@ -983,7 +978,7 @@ mysql_deparse_op_expr(OpExpr *node, deparse_expr_cxt *context) if (oprkind == 'r' || oprkind == 'b') { arg = list_head(node->args); - deparseExpr(lfirst(arg), context); + deparseExpr((Expr*)lfirst(arg), context); appendStringInfoChar(buf, ' '); } @@ -995,7 +990,7 @@ mysql_deparse_op_expr(OpExpr *node, deparse_expr_cxt *context) { arg = list_tail(node->args); appendStringInfoChar(buf, ' '); - deparseExpr(lfirst(arg), context); + deparseExpr((Expr*)lfirst(arg), context); } appendStringInfoChar(buf, ')'); @@ -1056,9 +1051,9 @@ mysql_deparse_distinct_expr(DistinctExpr *node, deparse_expr_cxt *context) Assert(list_length(node->args) == 2); appendStringInfoChar(buf, '('); - deparseExpr(linitial(node->args), context); + deparseExpr((Expr*)linitial(node->args), context); appendStringInfoString(buf, " IS DISTINCT FROM "); - deparseExpr(lsecond(node->args), context); + deparseExpr((Expr*)lsecond(node->args), context); appendStringInfoChar(buf, ')'); } @@ -1090,7 +1085,7 @@ mysql_deparse_scalar_array_op_expr(ScalarArrayOpExpr *node, Assert(list_length(node->args) == 2); /* Deparse left operand. */ - arg1 = linitial(node->args); + arg1 = (Expr*)linitial(node->args); deparseExpr(arg1, context); appendStringInfoChar(buf, ' '); @@ -1102,7 +1097,7 @@ mysql_deparse_scalar_array_op_expr(ScalarArrayOpExpr *node, appendStringInfo(buf, " IN ("); /* Deparse right operand. */ - arg2 = lsecond(node->args); + arg2 = (Expr*)lsecond(node->args); switch (nodeTag((Node *) arg2)) { case T_Const: @@ -1122,7 +1117,7 @@ mysql_deparse_scalar_array_op_expr(ScalarArrayOpExpr *node, switch (c->consttype) { case INT4ARRAYOID: - case OIDARRAYOID: + // case OIDARRAYOID: mysql_deparse_string(buf, extval, false); break; default: @@ -1174,7 +1169,7 @@ mysql_deparse_bool_expr(BoolExpr *node, deparse_expr_cxt *context) case NOT_EXPR: appendStringInfoChar(buf, '('); appendStringInfoString(buf, "NOT "); - deparseExpr(linitial(node->args), context); + deparseExpr((Expr*)linitial(node->args), context); appendStringInfoChar(buf, ')'); return; } @@ -1223,7 +1218,7 @@ mysql_deparse_array_expr(ArrayExpr *node, deparse_expr_cxt *context) { if (!first) appendStringInfoString(buf, ", "); - deparseExpr(lfirst(lc), context); + deparseExpr((Expr*)lfirst(lc), context); first = false; } appendStringInfoChar(buf, ']'); diff --git expected/connection_validation.out expected/connection_validation.out index edeadfd..9372640 100644 --- expected/connection_validation.out +++ expected/connection_validation.out @@ -1,10 +1,11 @@ \set MYSQL_HOST '\'localhost\'' \set MYSQL_PORT '\'3306\'' \set MYSQL_USER_NAME '\'edb\'' -\set MYSQL_PASS '\'edb\'' +\set MYSQL_PASS '\'Mysql@123!@#\'' -- Before running this file User must create database mysql_fdw_regress on --- MySQL with all permission for 'edb' user with 'edb' password and ran --- mysql_init.sh file to create tables. +-- MySQL with all permission for 'edb' user with 'Mysql@123!@#' password and +-- ran mysql_init.sh file to create tables. +create database contrib_regression; \c contrib_regression CREATE EXTENSION IF NOT EXISTS mysql_fdw; CREATE SERVER mysql_svr FOREIGN DATA WRAPPER mysql_fdw @@ -28,7 +29,7 @@ SELECT * FROM f_mysql_test ORDER BY 1, 2; -- details and fail as the host address is not correct. ALTER SERVER mysql_svr OPTIONS (SET host 'localhos'); SELECT * FROM f_mysql_test ORDER BY 1, 2; -ERROR: failed to connect to MySQL: Unknown MySQL server host 'localhos' (2) +ERROR: failed to connect to MySQL: Unknown MySQL server host 'localhos' (-2) -- Set the correct host-name, next operation should succeed. ALTER SERVER mysql_svr OPTIONS (SET host :MYSQL_HOST); SELECT * FROM f_mysql_test ORDER BY 1, 2; diff --git expected/dml.out expected/dml.out index fbd5cf4..4d62ce1 100644 --- expected/dml.out +++ expected/dml.out @@ -1,10 +1,10 @@ \set MYSQL_HOST '\'localhost\'' \set MYSQL_PORT '\'3306\'' \set MYSQL_USER_NAME '\'edb\'' -\set MYSQL_PASS '\'edb\'' +\set MYSQL_PASS '\'Mysql@123!@#\'' -- Before running this file User must create database mysql_fdw_regress on --- MySQL with all permission for 'edb' user with 'edb' password and ran --- mysql_init.sh file to create tables. +-- MySQL with all permission for 'edb' user with 'Mysql@123!@#' password and +-- ran mysql_init.sh file to create tables. \c contrib_regression CREATE EXTENSION IF NOT EXISTS mysql_fdw; CREATE SERVER mysql_svr FOREIGN DATA WRAPPER mysql_fdw @@ -113,9 +113,7 @@ WARNING: skipping "f_empdata" --- cannot vacuum non-tables or special system ta VACUUM FREEZE f_empdata; WARNING: skipping "f_empdata" --- cannot vacuum non-tables or special system tables ANALYZE f_empdata; -WARNING: skipping "f_empdata" --- cannot analyze this foreign table ANALYZE f_empdata(emp_id); -WARNING: skipping "f_empdata" --- cannot analyze this foreign table VACUUM ANALYZE f_empdata; WARNING: skipping "f_empdata" --- cannot vacuum non-tables or special system tables -- Verify the before update trigger which modifies the column value which is not @@ -126,26 +124,28 @@ BEGIN RETURN NEW; END $$ language plpgsql; +---Currently,triggers can only be created on general row-store tables. CREATE TRIGGER before_row_update_trig BEFORE UPDATE ON fdw126_ft1 FOR EACH ROW EXECUTE PROCEDURE before_row_update_func(); +ERROR: "fdw126_ft1" is not a table or view INSERT INTO fdw126_ft1 VALUES(1, 'One', 101); EXPLAIN (verbose, costs off) UPDATE fdw126_ft1 SET stu_dept = 201 WHERE stu_id = 1; - QUERY PLAN -------------------------------------------------------------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------- Update on public.fdw126_ft1 -> Foreign Scan on public.fdw126_ft1 - Output: stu_id, stu_name, 201, stu_id, fdw126_ft1.* + Output: stu_id, stu_name, 201, stu_id Local server startup cost: 10 - Remote query: SELECT `stu_id`, `stu_name`, `stu_dept` FROM `mysql_fdw_regress1`.`student` WHERE ((`stu_id` = 1)) FOR UPDATE + Remote query: SELECT `stu_id`, `stu_name` FROM `mysql_fdw_regress1`.`student` WHERE ((`stu_id` = 1)) FOR UPDATE (5 rows) UPDATE fdw126_ft1 SET stu_dept = 201 WHERE stu_id = 1; SELECT * FROM fdw126_ft1 ORDER BY stu_id; - stu_id | stu_name | stu_dept ---------+----------------------+---------- - 1 | One trigger updated! | 201 + stu_id | stu_name | stu_dept +--------+----------+---------- + 1 | One | 201 (1 row) -- Throw an error when target list has row identifier column. @@ -161,7 +161,6 @@ BEGIN END $$ language plpgsql; UPDATE fdw126_ft1 SET stu_dept = 301 WHERE stu_id = 1; -ERROR: row identifier column update is not supported -- Verify the before update trigger which modifies the column value which is -- not part of update statement. CREATE OR REPLACE FUNCTION before_row_update_func() RETURNS TRIGGER AS $$ @@ -170,26 +169,28 @@ BEGIN RETURN NEW; END $$ language plpgsql; +---Currently,triggers can only be created on general row-store tables. CREATE TRIGGER before_row_update_trig1 BEFORE UPDATE ON fdw193_ft1 FOR EACH ROW EXECUTE PROCEDURE before_row_update_func(); +ERROR: "fdw193_ft1" is not a table or view INSERT INTO fdw193_ft1 VALUES('aa', 'One', 101); EXPLAIN (verbose, costs off) UPDATE fdw193_ft1 SET stu_dept = 201 WHERE stu_id = 'aa'; - QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------ + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------- Update on public.fdw193_ft1 -> Foreign Scan on public.fdw193_ft1 - Output: stu_id, stu_name, 201, stu_id, fdw193_ft1.* + Output: stu_id, stu_name, 201, stu_id Local server startup cost: 10 - Remote query: SELECT `stu_id`, `stu_name`, `stu_dept` FROM `mysql_fdw_regress1`.`student1` WHERE ((`stu_id` = 'aa')) FOR UPDATE + Remote query: SELECT `stu_id`, `stu_name` FROM `mysql_fdw_regress1`.`student1` WHERE ((`stu_id` = 'aa')) FOR UPDATE (5 rows) UPDATE fdw193_ft1 SET stu_dept = 201 WHERE stu_id = 'aa'; SELECT * FROM fdw193_ft1 ORDER BY stu_id; - stu_id | stu_name | stu_dept ---------+----------------------+---------- - aa | One trigger updated! | 201 + stu_id | stu_name | stu_dept +--------+----------+---------- + aa | One | 201 (1 row) -- Throw an error when before row update trigger modify the row identifier @@ -202,7 +203,6 @@ BEGIN END $$ language plpgsql; UPDATE fdw193_ft1 SET stu_dept = 301 WHERE stu_id = 'aa'; -ERROR: row identifier column update is not supported -- Verify the NULL assignment scenario. CREATE OR REPLACE FUNCTION before_row_update_func() RETURNS TRIGGER AS $$ BEGIN @@ -212,7 +212,6 @@ BEGIN END $$ language plpgsql; UPDATE fdw193_ft1 SET stu_dept = 401 WHERE stu_id = 'aa'; -ERROR: row identifier column update is not supported -- Cleanup DELETE FROM fdw126_ft1; DELETE FROM f_empdata; diff --git expected/pushdown.out expected/pushdown.out index 9a72763..d2b2492 100644 --- expected/pushdown.out +++ expected/pushdown.out @@ -1,10 +1,10 @@ \set MYSQL_HOST '\'localhost\'' \set MYSQL_PORT '\'3306\'' \set MYSQL_USER_NAME '\'edb\'' -\set MYSQL_PASS '\'edb\'' +\set MYSQL_PASS '\'Mysql@123!@#\'' -- Before running this file User must create database mysql_fdw_regress on --- mysql with all permission for 'edb' user with 'edb' password and ran --- mysql_init.sh file to create tables. +-- mysql with all permission for 'edb' user with 'Mysql@123!@#' password and +-- ran mysql_init.sh file to create tables. \c contrib_regression CREATE EXTENSION IF NOT EXISTS mysql_fdw; CREATE SERVER mysql_svr FOREIGN DATA WRAPPER mysql_fdw diff --git expected/select.out expected/select.out index 94d4278..4be6e33 100644 --- expected/select.out +++ expected/select.out @@ -1,10 +1,10 @@ \set MYSQL_HOST '\'localhost\'' \set MYSQL_PORT '\'3306\'' \set MYSQL_USER_NAME '\'edb\'' -\set MYSQL_PASS '\'edb\'' +\set MYSQL_PASS '\'Mysql@123!@#\'' -- Before running this file User must create database mysql_fdw_regress on --- MySQL with all permission for 'edb' user with 'edb' password and ran --- mysql_init.sh file to create tables. +-- MySQL with all permission for 'edb' user with 'Mysql@123!@#' password and +-- ran mysql_init.sh file to create tables. \c contrib_regression CREATE EXTENSION IF NOT EXISTS mysql_fdw; CREATE SERVER mysql_svr FOREIGN DATA WRAPPER mysql_fdw @@ -538,14 +538,23 @@ END $$ LANGUAGE plpgsql; SELECT test_param_where(); NOTICE: Found number One +CONTEXT: referenced column: test_param_where NOTICE: Found number Two +CONTEXT: referenced column: test_param_where NOTICE: Found number Three +CONTEXT: referenced column: test_param_where NOTICE: Found number Four +CONTEXT: referenced column: test_param_where NOTICE: Found number Five +CONTEXT: referenced column: test_param_where NOTICE: Found number Six +CONTEXT: referenced column: test_param_where NOTICE: Found number Seven +CONTEXT: referenced column: test_param_where NOTICE: Found number Eight +CONTEXT: referenced column: test_param_where NOTICE: Found number Nine +CONTEXT: referenced column: test_param_where test_param_where ------------------ @@ -898,104 +907,43 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 40 | HR | | | | (15 rows) +-- LATERAL is not supported now -- FDW-206: LEFT JOIN LATERAL case should not crash EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM f_mysql_test t1 LEFT JOIN LATERAL ( SELECT t2.a, t1.a AS t1_a FROM f_mysql_test t2) t3 ON t1.a = t3.a ORDER BY 1; - QUERY PLAN ------------------------------------------------------------------------------------------------- - Sort - Output: t1.a, t1.b, t2.a, (t1.a) - Sort Key: t1.a - -> Nested Loop Left Join - Output: t1.a, t1.b, t2.a, (t1.a) - -> Foreign Scan on public.f_mysql_test t1 - Output: t1.a, t1.b - Local server startup cost: 10 - Remote query: SELECT `a`, `b` FROM `mysql_fdw_regress`.`mysql_test` - -> Foreign Scan on public.f_mysql_test t2 - Output: t2.a, t1.a - Local server startup cost: 10 - Remote query: SELECT `a` FROM `mysql_fdw_regress`.`mysql_test` WHERE ((? = `a`)) -(13 rows) - +ERROR: syntax error at or near "SELECT" +LINE 3: SELECT t2.a, t1.a AS t1_a FROM f_mysql_test t2) t3 ON t1.a... + ^ SELECT * FROM f_mysql_test t1 LEFT JOIN LATERAL ( SELECT t2.a, t1.a AS t1_a FROM f_mysql_test t2) t3 ON t1.a = t3.a ORDER BY 1; - a | b | a | t1_a ----+---+---+------ - 1 | 1 | 1 | 1 -(1 row) - +ERROR: syntax error at or near "SELECT" +LINE 2: SELECT t2.a, t1.a AS t1_a FROM f_mysql_test t2) t3 ON t1.a... + ^ SELECT t1.c1, t3.c1, t3.t1_c8 FROM f_test_tbl1 t1 INNER JOIN LATERAL ( SELECT t2.c1, t1.c8 AS t1_c8 FROM f_test_tbl2 t2) t3 ON t3.c1 = t3.t1_c8 ORDER BY 1, 2, 3; - c1 | c1 | t1_c8 -------+----+------- - 100 | 20 | 20 - 200 | 30 | 30 - 300 | 30 | 30 - 400 | 20 | 20 - 500 | 30 | 30 - 600 | 30 | 30 - 700 | 10 | 10 - 800 | 20 | 20 - 900 | 10 | 10 - 1000 | 30 | 30 - 1100 | 20 | 20 - 1200 | 30 | 30 - 1300 | 20 | 20 - 1400 | 10 | 10 -(14 rows) - +ERROR: syntax error at or near "SELECT" +LINE 2: SELECT t2.c1, t1.c8 AS t1_c8 FROM f_test_tbl2 t2) t3 ON t3... + ^ SELECT t1.c1, t3.c1, t3.t1_c8 FROM l_test_tbl1 t1 LEFT JOIN LATERAL ( SELECT t2.c1, t1.c8 AS t1_c8 FROM f_test_tbl2 t2) t3 ON t3.c1 = t3.t1_c8 ORDER BY 1, 2, 3; - c1 | c1 | t1_c8 -------+----+------- - 100 | 20 | 20 - 200 | 30 | 30 - 300 | 30 | 30 - 400 | 20 | 20 - 500 | 30 | 30 - 600 | 30 | 30 - 700 | 10 | 10 - 800 | 20 | 20 - 900 | 10 | 10 - 1000 | 30 | 30 - 1100 | 20 | 20 - 1200 | 30 | 30 - 1300 | 20 | 20 - 1400 | 10 | 10 -(14 rows) - +ERROR: syntax error at or near "SELECT" +LINE 2: SELECT t2.c1, t1.c8 AS t1_c8 FROM f_test_tbl2 t2) t3 ON t3... + ^ SELECT *, (SELECT r FROM (SELECT c1 AS c1) x, LATERAL (SELECT c1 AS r) y) FROM f_test_tbl1 ORDER BY 1, 2, 3; - c1 | c2 | c3 | c4 | c5 | c6 | c7 | c8 | r -------+-------+----------+------+------------+------------+------+----+------ - 100 | EMP1 | ADMIN | 1300 | 1980-12-17 | 800.23000 | | 20 | 100 - 200 | EMP2 | SALESMAN | 600 | 1981-02-20 | 1600.00000 | 300 | 30 | 200 - 300 | EMP3 | SALESMAN | 600 | 1981-02-22 | 1250.00000 | 500 | 30 | 300 - 400 | EMP4 | MANAGER | 900 | 1981-04-02 | 2975.12000 | | 20 | 400 - 500 | EMP5 | SALESMAN | 600 | 1981-09-28 | 1250.00000 | 1400 | 30 | 500 - 600 | EMP6 | MANAGER | 900 | 1981-05-01 | 2850.00000 | | 30 | 600 - 700 | EMP7 | MANAGER | 900 | 1981-06-09 | 2450.45000 | | 10 | 700 - 800 | EMP8 | FINANCE | 400 | 1987-04-19 | 3000.00000 | | 20 | 800 - 900 | EMP9 | HEAD | | 1981-11-17 | 5000.00000 | | 10 | 900 - 1000 | EMP10 | SALESMAN | 600 | 1980-09-08 | 1500.00000 | 0 | 30 | 1000 - 1100 | EMP11 | ADMIN | 800 | 1987-05-23 | 1100.00000 | | 20 | 1100 - 1200 | EMP12 | ADMIN | 600 | 1981-12-03 | 950.00000 | | 30 | 1200 - 1300 | EMP13 | FINANCE | 400 | 1981-12-03 | 3000.00000 | | 20 | 1300 - 1400 | EMP14 | ADMIN | 700 | 1982-01-23 | 1300.00000 | | 10 | 1400 -(14 rows) - +ERROR: syntax error at or near "SELECT" +LINE 1: ...T *, (SELECT r FROM (SELECT c1 AS c1) x, LATERAL (SELECT c1 ... + ^ -- LATERAL JOIN with RIGHT should throw error SELECT t1.c1, t3.c1, t3.t1_c8 FROM f_test_tbl1 t1 RIGHT JOIN LATERAL ( SELECT t2.c1, t1.c8 AS t1_c8 FROM f_test_tbl2 t2) t3 ON t3.c1 = t3.t1_c8 ORDER BY 1, 2, 3; -ERROR: invalid reference to FROM-clause entry for table "t1" +ERROR: syntax error at or near "SELECT" LINE 2: SELECT t2.c1, t1.c8 AS t1_c8 FROM f_test_tbl2 t2) t3 ON t3... - ^ -DETAIL: The combining JOIN type must be INNER or LEFT for a LATERAL reference. + ^ -- FDW-207: NATURAL JOIN should give correct output SELECT t1.c1, t2.c1, t3.c1 FROM f_test_tbl1 t1 NATURAL JOIN f_test_tbl1 t2 NATURAL JOIN f_test_tbl1 t3 @@ -1061,21 +1009,14 @@ SELECT * FROM f_enum_t1 WHERE id = 4; DROP FOREIGN TABLE f_enum_t1; DROP TYPE size_t; -- Create the type with extra enum values. -CREATE TYPE size_t AS enum('small', 'medium', 'large', 'largest', ''); +CREATE TYPE size_t AS enum('small', 'medium', 'large', 'largest', 'blank'); CREATE FOREIGN TABLE f_enum_t1(id int, size size_t) SERVER mysql_svr OPTIONS (dbname 'mysql_fdw_regress', table_name 'enum_t1'); -- If we insert the enum value which is not present on MySQL side then it -- inserts empty string in ANSI_QUOTES sql_mode, so verify that. INSERT INTO f_enum_t1 VALUES (4, 'largest'); SELECT * from f_enum_t1; - id | size -----+-------- - 1 | small - 2 | medium - 3 | medium - 4 | -(4 rows) - +ERROR: invalid input value for enum size_t: "" DELETE FROM f_enum_t1 WHERE size = ''; -- Postgres should throw an error as the value which we are inserting for enum -- column is not present in enum on Postgres side, no matter whether it is @@ -1084,15 +1025,11 @@ INSERT INTO f_enum_t1 VALUES (4, 'big'); ERROR: invalid input value for enum size_t: "big" LINE 1: INSERT INTO f_enum_t1 VALUES (4, 'big'); ^ +CONTEXT: referenced column: size -- FDW-155: Enum data type can be handled correctly in select statements on -- foreign table. SELECT * FROM f_enum_t1 WHERE size = 'medium' ORDER BY id; - id | size -----+-------- - 2 | medium - 3 | medium -(2 rows) - +ERROR: invalid input value for enum size_t: "" -- Remote aggregate in combination with a local Param (for the output -- of an initplan) EXPLAIN (VERBOSE, COSTS OFF) @@ -1104,7 +1041,7 @@ SELECT EXISTS(SELECT 1 FROM pg_enum), sum(id) from f_enum_t1; InitPlan 1 (returns $0) -> Seq Scan on pg_catalog.pg_enum -> Foreign Scan on public.f_enum_t1 - Output: f_enum_t1.id, f_enum_t1.size + Output: f_enum_t1.id Local server startup cost: 10 Remote query: SELECT `id` FROM `mysql_fdw_regress`.`enum_t1` (8 rows) @@ -1112,9 +1049,10 @@ SELECT EXISTS(SELECT 1 FROM pg_enum), sum(id) from f_enum_t1; SELECT EXISTS(SELECT 1 FROM pg_enum), sum(id) from f_enum_t1; exists | sum --------+----- - t | 6 + t | 10 (1 row) +-- IMPORT FOREIGN SCHEMA command is not supported now. -- Check with the IMPORT FOREIGN SCHEMA command. Also, check ENUM types with -- the IMPORT FOREIGN SCHEMA command. If the enum name is the same for multiple -- tables, then it should handle correctly by prefixing the table name. @@ -1122,41 +1060,27 @@ CREATE TYPE enum_t1_size_t AS enum('small', 'medium', 'large'); CREATE TYPE enum_t2_size_t AS enum('S', 'M', 'L'); IMPORT FOREIGN SCHEMA mysql_fdw_regress LIMIT TO (enum_t1, enum_t2) FROM SERVER mysql_svr INTO public; -NOTICE: error while generating the table definition -HINT: If you encounter an error, you may need to execute the following first: -DO $$BEGIN IF NOT EXISTS (SELECT 1 FROM pg_catalog.pg_type WHERE typname = 'enum_t1_size_t') THEN CREATE TYPE enum_t1_size_t AS enum('small','medium','large'); END IF; END$$; - -NOTICE: error while generating the table definition -HINT: If you encounter an error, you may need to execute the following first: -DO $$BEGIN IF NOT EXISTS (SELECT 1 FROM pg_catalog.pg_type WHERE typname = 'enum_t2_size_t') THEN CREATE TYPE enum_t2_size_t AS enum('S','M','L'); END IF; END$$; - +ERROR: syntax error at or near "IMPORT" +LINE 1: IMPORT FOREIGN SCHEMA mysql_fdw_regress LIMIT TO (enum_t1, e... + ^ SELECT attrelid::regclass, atttypid::regtype FROM pg_attribute WHERE (attrelid = 'enum_t1'::regclass OR attrelid = 'enum_t2'::regclass) AND attnum > 1 ORDER BY 1; - attrelid | atttypid -----------+---------------- - enum_t1 | enum_t1_size_t - enum_t2 | enum_t2_size_t -(2 rows) - +ERROR: relation "enum_t1" does not exist +LINE 2: WHERE (attrelid = 'enum_t1'::regclass OR attrelid = 'enum_... + ^ SELECT * FROM enum_t1 ORDER BY id; - id | size -----+-------- - 1 | small - 2 | medium - 3 | medium -(3 rows) - +ERROR: relation "enum_t1" does not exist on datanode1 +LINE 1: SELECT * FROM enum_t1 ORDER BY id; + ^ SELECT * FROM enum_t2 ORDER BY id; - id | size -----+------ - 10 | S - 20 | M - 30 | M -(3 rows) - +ERROR: relation "enum_t2" does not exist on datanode1 +LINE 1: SELECT * FROM enum_t2 ORDER BY id; + ^ DROP FOREIGN TABLE enum_t1; +ERROR: foreign table "enum_t1" does not exist DROP FOREIGN TABLE enum_t2; +ERROR: foreign table "enum_t2" does not exist -- Parameterized queries should work correctly. EXPLAIN (VERBOSE, COSTS OFF) SELECT c1, c2 FROM f_test_tbl1 @@ -1243,15 +1167,15 @@ SELECT c1, c2 FROM f_test_tbl1 WHERE c8 = ( Output: f_test_tbl1.c1, f_test_tbl1.c2 Sort Key: f_test_tbl1.c1 InitPlan 2 (returns $1) - -> Foreign Scan on public.f_test_tbl2 f_test_tbl2_1 - Output: f_test_tbl2_1.c1 + -> Foreign Scan on public.f_test_tbl2 + Output: public.f_test_tbl2.c1 Local server startup cost: 10 Remote query: SELECT `c1` FROM `mysql_fdw_regress`.`test_tbl2` WHERE ((`c1` = ?)) InitPlan 1 (returns $0) -> Aggregate - Output: (min(f_test_tbl2.c1) + 1) + Output: (min(public.f_test_tbl2.c1) + 1) -> Foreign Scan on public.f_test_tbl2 - Output: f_test_tbl2.c1, f_test_tbl2.c2, f_test_tbl2.c3 + Output: public.f_test_tbl2.c1 Local server startup cost: 10 Remote query: SELECT `c1` FROM `mysql_fdw_regress`.`test_tbl2` -> Foreign Scan on public.f_test_tbl1 diff --git expected/server_options.out expected/server_options.out index ef2b3ad..4405fe1 100644 --- expected/server_options.out +++ expected/server_options.out @@ -1,10 +1,10 @@ \set MYSQL_HOST '\'localhost\'' \set MYSQL_PORT '\'3306\'' \set MYSQL_USER_NAME '\'edb\'' -\set MYSQL_PASS '\'edb\'' +\set MYSQL_PASS '\'Mysql@123!@#\'' -- Before running this file User must create database mysql_fdw_regress on --- MySQL with all permission for 'edb' user with 'edb' password and ran --- mysql_init.sh file to create tables. +-- MySQL with all permission for 'edb' user with 'Mysql@123!@#' password and +-- ran mysql_init.sh file to create tables. \c contrib_regression CREATE EXTENSION IF NOT EXISTS mysql_fdw; CREATE SERVER mysql_svr FOREIGN DATA WRAPPER mysql_fdw @@ -16,9 +16,9 @@ SELECT e.fdwname as "Extension", srvname AS "Server", s.srvoptions AS "Server_Op FROM pg_foreign_data_wrapper e LEFT JOIN pg_foreign_server s ON e.oid = s.srvfdw LEFT JOIN pg_user_mapping u ON s.oid = u.umserver WHERE e.fdwname = 'mysql_fdw' ORDER BY 1, 2, 3, 4; - Extension | Server | Server_Options | User_Mapping_Options ------------+-----------+----------------------------+----------------------------- - mysql_fdw | mysql_svr | {host=localhost,port=3306} | {username=edb,password=edb} + Extension | Server | Server_Options | User_Mapping_Options +-----------+-----------+----------------------------+-------------------------------------- + mysql_fdw | mysql_svr | {host=localhost,port=3306} | {username=edb,password=Mysql@123!@#} (1 row) -- Create foreign table and perform basic SQL operations diff --git mysql_fdw.c mysql_fdw.cpp similarity index 88% rename from mysql_fdw.c rename to mysql_fdw.cpp index 1b83c8e..76d2a2c 100644 --- mysql_fdw.c +++ mysql_fdw.cpp @@ -26,7 +26,7 @@ #include #include -#include "access/htup_details.h" +#include "access/htup.h" #include "access/sysattr.h" #include "access/reloptions.h" #if PG_VERSION_NUM >= 120000 @@ -121,7 +121,8 @@ typedef struct MySQLFdwRelationInfo } MySQLFdwRelationInfo; extern PGDLLEXPORT void _PG_init(void); -extern Datum mysql_fdw_handler(PG_FUNCTION_ARGS); +extern "C" Datum mysql_fdw_handler(PG_FUNCTION_ARGS); +extern "C" Datum mysql_fdw_version(PG_FUNCTION_ARGS); PG_FUNCTION_INFO_V1(mysql_fdw_handler); PG_FUNCTION_INFO_V1(mysql_fdw_version); @@ -199,7 +200,6 @@ static void mysqlEndForeignInsert(EState *estate, * Helper functions */ bool mysql_load_library(void); -static void mysql_fdw_exit(int code, Datum arg); static bool mysql_is_column_unique(Oid foreigntableid); static void prepare_query_params(PlanState *node, @@ -218,6 +218,7 @@ static void process_query_params(ExprContext *econtext, Oid *param_types); static void bind_stmt_params_and_exec(ForeignScanState *node); +static MySQLFdwExecState* getFdwState(EState* estate, ResultRelInfo* resultRelInfo); void *mysql_dll_handle = NULL; static int wait_timeout = WAIT_TIMEOUT; @@ -267,36 +268,36 @@ mysql_load_library(void) if (mysql_dll_handle == NULL) return false; - _mysql_stmt_bind_param = dlsym(mysql_dll_handle, "mysql_stmt_bind_param"); - _mysql_stmt_bind_result = dlsym(mysql_dll_handle, "mysql_stmt_bind_result"); - _mysql_stmt_init = dlsym(mysql_dll_handle, "mysql_stmt_init"); - _mysql_stmt_prepare = dlsym(mysql_dll_handle, "mysql_stmt_prepare"); - _mysql_stmt_execute = dlsym(mysql_dll_handle, "mysql_stmt_execute"); - _mysql_stmt_fetch = dlsym(mysql_dll_handle, "mysql_stmt_fetch"); - _mysql_query = dlsym(mysql_dll_handle, "mysql_query"); - _mysql_stmt_result_metadata = dlsym(mysql_dll_handle, "mysql_stmt_result_metadata"); - _mysql_stmt_store_result = dlsym(mysql_dll_handle, "mysql_stmt_store_result"); - _mysql_fetch_row = dlsym(mysql_dll_handle, "mysql_fetch_row"); - _mysql_fetch_field = dlsym(mysql_dll_handle, "mysql_fetch_field"); - _mysql_fetch_fields = dlsym(mysql_dll_handle, "mysql_fetch_fields"); - _mysql_stmt_close = dlsym(mysql_dll_handle, "mysql_stmt_close"); - _mysql_stmt_reset = dlsym(mysql_dll_handle, "mysql_stmt_reset"); - _mysql_free_result = dlsym(mysql_dll_handle, "mysql_free_result"); - _mysql_error = dlsym(mysql_dll_handle, "mysql_error"); - _mysql_options = dlsym(mysql_dll_handle, "mysql_options"); - _mysql_ssl_set = dlsym(mysql_dll_handle, "mysql_ssl_set"); - _mysql_real_connect = dlsym(mysql_dll_handle, "mysql_real_connect"); - _mysql_close = dlsym(mysql_dll_handle, "mysql_close"); - _mysql_init = dlsym(mysql_dll_handle, "mysql_init"); - _mysql_stmt_attr_set = dlsym(mysql_dll_handle, "mysql_stmt_attr_set"); - _mysql_store_result = dlsym(mysql_dll_handle, "mysql_store_result"); - _mysql_stmt_errno = dlsym(mysql_dll_handle, "mysql_stmt_errno"); - _mysql_errno = dlsym(mysql_dll_handle, "mysql_errno"); - _mysql_num_fields = dlsym(mysql_dll_handle, "mysql_num_fields"); - _mysql_num_rows = dlsym(mysql_dll_handle, "mysql_num_rows"); - _mysql_get_host_info = dlsym(mysql_dll_handle, "mysql_get_host_info"); - _mysql_get_server_info = dlsym(mysql_dll_handle, "mysql_get_server_info"); - _mysql_get_proto_info = dlsym(mysql_dll_handle, "mysql_get_proto_info"); + _mysql_stmt_bind_param = (bool (*)(st_mysql_stmt*, st_mysql_bind*))dlsym(mysql_dll_handle, "mysql_stmt_bind_param"); + _mysql_stmt_bind_result = (bool (*)(st_mysql_stmt*, st_mysql_bind*))dlsym(mysql_dll_handle, "mysql_stmt_bind_result"); + _mysql_stmt_init = (st_mysql_stmt* (*)(st_mysql*))dlsym(mysql_dll_handle, "mysql_stmt_init"); + _mysql_stmt_prepare = (int (*)(st_mysql_stmt*, const char*, long unsigned int))dlsym(mysql_dll_handle, "mysql_stmt_prepare"); + _mysql_stmt_execute = (int (*)(st_mysql_stmt*))dlsym(mysql_dll_handle, "mysql_stmt_execute"); + _mysql_stmt_fetch = (int (*)(st_mysql_stmt*))dlsym(mysql_dll_handle, "mysql_stmt_fetch"); + _mysql_query = (int (*)(st_mysql*, const char*))dlsym(mysql_dll_handle, "mysql_query"); + _mysql_stmt_result_metadata = (st_mysql_res* (*)(st_mysql_stmt*))dlsym(mysql_dll_handle, "mysql_stmt_result_metadata"); + _mysql_stmt_store_result = (int (*)(st_mysql*))dlsym(mysql_dll_handle, "mysql_stmt_store_result"); + _mysql_fetch_row = (char** (*)(st_mysql_res*))dlsym(mysql_dll_handle, "mysql_fetch_row"); + _mysql_fetch_field = (st_mysql_field* (*)(st_mysql_res*))dlsym(mysql_dll_handle, "mysql_fetch_field"); + _mysql_fetch_fields = (st_mysql_field* (*)(st_mysql_res*))dlsym(mysql_dll_handle, "mysql_fetch_fields"); + _mysql_stmt_close = (bool (*)(st_mysql_stmt*))dlsym(mysql_dll_handle, "mysql_stmt_close"); + _mysql_stmt_reset = (bool (*)(st_mysql_stmt*))dlsym(mysql_dll_handle, "mysql_stmt_reset"); + _mysql_free_result = (bool (*)(st_mysql_res*))dlsym(mysql_dll_handle, "mysql_free_result"); + _mysql_error = (const char* (*)(st_mysql*))dlsym(mysql_dll_handle, "mysql_error"); + _mysql_options = (int (*)(st_mysql*, mysql_option, const void*))dlsym(mysql_dll_handle, "mysql_options"); + _mysql_ssl_set = (bool (*)(st_mysql*, const char*, const char*, const char*, const char*, const char*))dlsym(mysql_dll_handle, "mysql_ssl_set"); + _mysql_real_connect = (st_mysql* (*)(st_mysql*, const char*, const char*, const char*, const char*, unsigned int, const char*, long unsigned int))dlsym(mysql_dll_handle, "mysql_real_connect"); + _mysql_close = (void (*)(st_mysql*))dlsym(mysql_dll_handle, "mysql_close"); + _mysql_init = (st_mysql* (*)(st_mysql*))dlsym(mysql_dll_handle, "mysql_init"); + _mysql_stmt_attr_set = (bool (*)(st_mysql_stmt*, enum_stmt_attr_type, const void*))dlsym(mysql_dll_handle, "mysql_stmt_attr_set"); + _mysql_store_result = (st_mysql_res* (*)(st_mysql*))dlsym(mysql_dll_handle, "mysql_store_result"); + _mysql_stmt_errno = (unsigned int (*)(st_mysql_stmt*))dlsym(mysql_dll_handle, "mysql_stmt_errno"); + _mysql_errno = (unsigned int (*)(st_mysql*))dlsym(mysql_dll_handle, "mysql_errno"); + _mysql_num_fields = (unsigned int (*)(st_mysql_res*))dlsym(mysql_dll_handle, "mysql_num_fields"); + _mysql_num_rows = (unsigned int (*)(st_mysql_res*))dlsym(mysql_dll_handle, "mysql_num_rows"); + _mysql_get_host_info = (const char* (*)(st_mysql*))dlsym(mysql_dll_handle, "mysql_get_host_info"); + _mysql_get_server_info = (const char* (*)(st_mysql*))dlsym(mysql_dll_handle, "mysql_get_server_info"); + _mysql_get_proto_info = (int (*)(st_mysql*))dlsym(mysql_dll_handle, "mysql_get_proto_info"); if (_mysql_stmt_bind_param == NULL || _mysql_stmt_bind_result == NULL || @@ -346,45 +347,42 @@ _PG_init(void) errmsg("failed to load the mysql query: \n%s", dlerror()), errhint("Export LD_LIBRARY_PATH to locate the library."))); - DefineCustomIntVariable("mysql_fdw.wait_timeout", - "Server-side wait_timeout", - "Set the maximum wait_timeout" - "use to set the MySQL session timeout", - &wait_timeout, - WAIT_TIMEOUT, - 0, - INT_MAX, - PGC_USERSET, - 0, - NULL, - NULL, - NULL); - - DefineCustomIntVariable("mysql_fdw.interactive_timeout", - "Server-side interactive timeout", - "Set the maximum interactive timeout" - "use to set the MySQL session timeout", - &interactive_timeout, - INTERACTIVE_TIMEOUT, - 0, - INT_MAX, - PGC_USERSET, - 0, - NULL, - NULL, - NULL); - - on_proc_exit(&mysql_fdw_exit, PointerGetDatum(NULL)); + if (GetConfigOption("mysql_fdw.wait_timeout", true, true) == NULL) { + DefineCustomIntVariable("mysql_fdw.wait_timeout", + "Server-side wait_timeout", + "Set the maximum wait_timeout" + "use to set the MySQL session timeout", + &wait_timeout, + WAIT_TIMEOUT, + 0, + INT_MAX, + PGC_USERSET, + 0, + NULL, + NULL, + NULL); + } + + if (GetConfigOption("mysql_fdw.interactive_timeout", true, true) == NULL) { + DefineCustomIntVariable("mysql_fdw.interactive_timeout", + "Server-side interactive timeout", + "Set the maximum interactive timeout" + "use to set the MySQL session timeout", + &interactive_timeout, + INTERACTIVE_TIMEOUT, + 0, + INT_MAX, + PGC_USERSET, + 0, + NULL, + NULL, + NULL); + } } -/* - * mysql_fdw_exit - * Exit callback function. - */ -static void -mysql_fdw_exit(int code, Datum arg) +static int mysqlGetFdwType() { - mysql_cleanup_connection(); + return MYSQL_ORC; } /* @@ -418,7 +416,7 @@ mysql_fdw_handler(PG_FUNCTION_ARGS) fdwroutine->ExplainForeignScan = mysqlExplainForeignScan; /* Support functions for ANALYZE */ - fdwroutine->AnalyzeForeignTable = mysqlAnalyzeForeignTable; + fdwroutine->AnalyzeForeignTable = (bool (*)(Relation relation, AcquireSampleRowsFunc* func, BlockNumber* totalpages, void* additionalData, bool estimate_table_rownum))mysqlAnalyzeForeignTable; /* Support functions for IMPORT FOREIGN SCHEMA */ #if PG_VERSION_NUM >= 90500 @@ -431,6 +429,8 @@ mysql_fdw_handler(PG_FUNCTION_ARGS) fdwroutine->EndForeignInsert = mysqlEndForeignInsert; #endif + fdwroutine->GetFdwType = mysqlGetFdwType; + PG_RETURN_POINTER(fdwroutine); } @@ -490,7 +490,7 @@ mysqlBeginForeignScan(ForeignScanState *node, int eflags) /* Stash away the state info we have already */ festate->query = strVal(list_nth(fsplan->fdw_private, 0)); - festate->retrieved_attrs = list_nth(fsplan->fdw_private, 1); + festate->retrieved_attrs = (List*)list_nth(fsplan->fdw_private, 1); festate->conn = conn; festate->query_executed = false; @@ -966,12 +966,12 @@ mysqlGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, foreigntableid); /* Create a ForeignPath node and add it as only possible path */ - add_path(baserel, (Path *) + add_path(root, baserel, (Path *) create_foreignscan_path(root, baserel, #if PG_VERSION_NUM >= 90600 NULL, /* default pathtarget */ -#endif baserel->rows, +#endif startup_cost, total_cost, NIL, /* no pathkeys */ @@ -1074,7 +1074,7 @@ mysqlGetForeignPlan(PlannerInfo *root, RelOptInfo *foreignrel, mysql_append_where_clause(&sql, root, foreignrel, remote_conds, true, ¶ms_list); - if (foreignrel->relid == root->parse->resultRelation && + if (foreignrel->relid == (unsigned int)root->parse->resultRelation && (root->parse->commandType == CMD_UPDATE || root->parse->commandType == CMD_DELETE)) { @@ -1258,7 +1258,7 @@ mysqlPlanForeignModify(PlannerInfo *root, switch (operation) { case CMD_INSERT: - mysql_deparse_insert(&sql, root, resultRelation, rel, targetAttrs); + mysql_deparse_insert(&sql, planner_rt_fetch(resultRelation, root), resultRelation, rel, targetAttrs); break; case CMD_UPDATE: mysql_deparse_update(&sql, root, resultRelation, rel, targetAttrs, @@ -1404,6 +1404,12 @@ mysqlExecForeignInsert(EState *estate, bool *isnull; fmstate = (MySQLFdwExecState *) resultRelInfo->ri_FdwState; + if (fmstate == NULL) + { + fmstate = getFdwState(estate, resultRelInfo); + resultRelInfo->ri_FdwState = fmstate; + } + n_params = list_length(fmstate->retrieved_attrs); oldcontext = MemoryContextSwitchTo(fmstate->temp_cxt); @@ -1419,7 +1425,7 @@ mysqlExecForeignInsert(EState *estate, Oid type = TupleDescAttr(slot->tts_tupleDescriptor, attnum)->atttypid; Datum value; - value = slot_getattr(slot, attnum + 1, &isnull[attnum]); + value = heap_slot_getattr(slot, attnum + 1, &isnull[attnum]); mysql_bind_sql_var(type, attnum, value, mysql_bind_buffer, &isnull[attnum]); @@ -1483,7 +1489,7 @@ mysqlExecForeignUpdate(EState *estate, } type = TupleDescAttr(slot->tts_tupleDescriptor, attnum - 1)->atttypid; - value = slot_getattr(slot, attnum, (bool *) (&isnull[bindnum])); + value = heap_slot_getattr(slot, attnum, (bool *) (&isnull[bindnum])); mysql_bind_sql_var(type, bindnum, value, mysql_bind_buffer, &isnull[bindnum]); @@ -1497,7 +1503,7 @@ mysqlExecForeignUpdate(EState *estate, if (!found_row_id_col) elog(ERROR, "missing row identifier column value in UPDATE"); - new_value = slot_getattr(slot, 1, &is_null); + new_value = heap_slot_getattr(slot, 1, &is_null); /* * Get the row identifier column value that was passed up as a resjunk @@ -1652,7 +1658,7 @@ mysqlExecForeignDelete(EState *estate, static void mysqlEndForeignModify(EState *estate, ResultRelInfo *resultRelInfo) { - MySQLFdwExecState *festate = resultRelInfo->ri_FdwState; + MySQLFdwExecState *festate = (MySQLFdwExecState*)resultRelInfo->ri_FdwState; if (festate && festate->stmt) { @@ -1886,6 +1892,93 @@ mysqlImportForeignSchema(ImportForeignSchemaStmt *stmt, Oid serverOid) } #endif +static MySQLFdwExecState* getFdwState(EState* estate, ResultRelInfo* resultRelInfo) +{ + Relation rel = resultRelInfo->ri_RelationDesc; + RangeTblEntry* rte = rt_fetch(resultRelInfo->ri_RangeTableIndex, estate->es_range_table); + Oid userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + Oid foreignTableId = RelationGetRelid(rel); + + if (!mysql_is_column_unique(foreignTableId)) + elog(ERROR, "first column of remote table must be unique for COPY operation"); + + ForeignTable* table = GetForeignTable(foreignTableId); + ForeignServer* server = GetForeignServer(table->serverid); + UserMapping* user = GetUserMapping(userid, server->serverid); + StringInfoData sql; + + MySQLFdwExecState* fmstate = (MySQLFdwExecState *)palloc0(sizeof(MySQLFdwExecState)); + fmstate->rel = rel; + fmstate->mysqlFdwOptions = mysql_get_options(foreignTableId); + fmstate->conn = mysql_get_connection(server, user, fmstate->mysqlFdwOptions); + fmstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt, "mysql_fdw temporary data", +#if PG_VERSION_NUM >= 110000 + ALLOCSET_DEFAULT_SIZES); +#else + ALLOCSET_SMALL_MINSIZE, + ALLOCSET_SMALL_INITSIZE, + ALLOCSET_SMALL_MAXSIZE); +#endif + + TupleDesc tupdesc = RelationGetDescr(rel); + int attnum; + List* targetAttrs = NULL; + + for (attnum = 1; attnum <= tupdesc->natts; attnum++) + { + Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1); + + if (!attr->attisdropped) + targetAttrs = lappend_int(targetAttrs, attnum); + } + + initStringInfo(&sql); + mysql_deparse_insert(&sql, rte, resultRelInfo->ri_RangeTableIndex, rel, targetAttrs); + fmstate->retrieved_attrs = targetAttrs; + fmstate->query = sql.data; + fmstate->stmt = _mysql_stmt_init(fmstate->conn); + if (!fmstate->stmt) + { + char *err = pstrdup(_mysql_error(fmstate->conn)); + ereport(ERROR, (errcode(ERRCODE_FDW_UNABLE_TO_CREATE_EXECUTION), + errmsg("failed to initialize the MySQL query: \n%s", err))); + } + + /* Prepare mysql statment */ + if (_mysql_stmt_prepare(fmstate->stmt, fmstate->query, strlen(fmstate->query)) != 0) + { + switch(_mysql_stmt_errno(fmstate->stmt)) + { + case CR_NO_ERROR: + break; + + case CR_OUT_OF_MEMORY: + case CR_SERVER_GONE_ERROR: + case CR_SERVER_LOST: + { + char *err = pstrdup(_mysql_error(fmstate->conn)); + mysql_release_connection(fmstate->conn); + ereport(ERROR, + (errcode(ERRCODE_FDW_UNABLE_TO_CREATE_EXECUTION), + errmsg("failed to prepare the MySQL query: \n%s", err))); + } + break; + case CR_COMMANDS_OUT_OF_SYNC: + case CR_UNKNOWN_ERROR: + default: + { + char *err = pstrdup(_mysql_error(fmstate->conn)); + ereport(ERROR, + (errcode(ERRCODE_FDW_UNABLE_TO_CREATE_EXECUTION), + errmsg("failed to prepare the MySQL query: \n%s", err))); + } + break; + } + } + + return fmstate; +} + #if PG_VERSION_NUM >= 110000 /* * mysqlBeginForeignInsert @@ -2137,7 +2230,7 @@ getUpdateTargetAttrs(RangeTblEntry *rte) #if PG_VERSION_NUM >= 90500 Bitmapset *tmpset = bms_copy(rte->updatedCols); #else - Bitmapset *tmpset = bms_copy(rte->modifiedCols); + Bitmapset *tmpset = bms_copy(rte->updatedCols); #endif AttrNumber col; diff --git mysql_fdw.h mysql_fdw.h index 22a48d3..997d2b3 100644 --- mysql_fdw.h +++ mysql_fdw.h @@ -215,7 +215,7 @@ extern mysql_opt *mysql_get_options(Oid foreigntableid); extern void mysql_deparse_select(StringInfo buf, PlannerInfo *root, RelOptInfo *baserel, Bitmapset *attrs_used, char *svr_table, List **retrieved_attrs); -extern void mysql_deparse_insert(StringInfo buf, PlannerInfo *root, +extern void mysql_deparse_insert(StringInfo buf, RangeTblEntry *rte, Index rtindex, Relation rel, List *targetAttrs); extern void mysql_deparse_update(StringInfo buf, PlannerInfo *root, @@ -238,8 +238,8 @@ MYSQL *mysql_connect(mysql_opt *opt); void mysql_cleanup_connection(void); void mysql_release_connection(MYSQL *conn); -#if PG_VERSION_NUM < 110000 /* TupleDescAttr is defined from PG version 11 */ -#define TupleDescAttr(tupdesc, i) ((tupdesc)->attrs[(i)]) -#endif +//#if PG_VERSION_NUM < 110000 /* TupleDescAttr is defined from PG version 11 */ +//#define TupleDescAttr(tupdesc, i) ((tupdesc)->attrs[(i)]) +//#endif #endif /* MYSQL_FDW_H */ diff --git mysql_init.sh mysql_init.sh index 852b175..febc20f 100644 --- mysql_init.sh +++ mysql_init.sh @@ -1,11 +1,11 @@ #!/bin/sh -export MYSQL_PWD="edb" +export MYSQL_PWD="Mysql@123!@#" MYSQL_HOST="localhost" MYSQL_PORT="3306" MYSQL_USER_NAME="edb" # Below commands must be run first time to create mysql_fdw_regress and mysql_fdw_regress1 databases -# used in regression tests with edb user and edb password. +# used in regression tests with edb user and Mysql@123!@# password. # --connect to mysql with root user # mysql -u root -p @@ -17,7 +17,7 @@ MYSQL_USER_NAME="edb" # SET GLOBAL validate_password.mixed_case_count = 0; # SET GLOBAL validate_password.number_count = 0; # SET GLOBAL validate_password.special_char_count = 0; -# CREATE USER 'edb'@'localhost' IDENTIFIED BY 'edb'; +# CREATE USER 'edb'@'localhost' IDENTIFIED WITH mysql_native_password BY 'Mysql@123!@#'; # GRANT ALL PRIVILEGES ON mysql_fdw_regress.* TO 'edb'@'localhost'; # GRANT ALL PRIVILEGES ON mysql_fdw_regress1.* TO 'edb'@'localhost'; diff --git mysql_query.c mysql_query.cpp similarity index 93% rename from mysql_query.c rename to mysql_query.cpp index 34bae80..eec6a6f 100644 --- mysql_query.c +++ mysql_query.cpp @@ -24,7 +24,7 @@ #include #include -#include "access/htup_details.h" +#include "access/htup.h" #include "catalog/pg_type.h" #include "mysql_query.h" #if PG_VERSION_NUM < 120000 @@ -48,7 +48,7 @@ x->minute = y.tm_min; \ x->second = y.tm_sec; \ } while(0); -static int32 mysql_from_pgtyp(Oid type); +static enum enum_field_types mysql_from_pgtyp(Oid type); static int dec_bin(int number); static int bin_dec(int binarynumber); @@ -121,7 +121,7 @@ mysql_convert_to_pg(Oid pgtyp, int pgtypmod, mysql_column *column) * mysql_from_pgtyp: * Give MySQL data type for PG type */ -static int32 +static enum enum_field_types mysql_from_pgtyp(Oid type) { switch (type) @@ -165,6 +165,7 @@ mysql_from_pgtyp(Oid type) errhint("Constant value data type: %u", type))); break; } + return MAX_NO_FIELD_TYPES; } /* @@ -204,7 +205,7 @@ mysql_bind_sql_var(Oid type, int attnum, Datum value, MYSQL_BIND *binds, case INT2OID: { int16 dat = DatumGetInt16(value); - int16 *bufptr = palloc(sizeof(int16)); + int16 *bufptr = (int16*)palloc0(sizeof(int16)); memcpy(bufptr, (char *) &dat, sizeof(int16)); @@ -214,7 +215,7 @@ mysql_bind_sql_var(Oid type, int attnum, Datum value, MYSQL_BIND *binds, case INT4OID: { int32 dat = DatumGetInt32(value); - int32 *bufptr = palloc(sizeof(int32)); + int32 *bufptr = (int32*)palloc0(sizeof(int32)); memcpy(bufptr, (char *) &dat, sizeof(int32)); @@ -224,7 +225,7 @@ mysql_bind_sql_var(Oid type, int attnum, Datum value, MYSQL_BIND *binds, case INT8OID: { int64 dat = DatumGetInt64(value); - int64 *bufptr = palloc(sizeof(int64)); + int64 *bufptr = (int64*)palloc0(sizeof(int64)); memcpy(bufptr, (char *) &dat, sizeof(int64)); @@ -234,7 +235,7 @@ mysql_bind_sql_var(Oid type, int attnum, Datum value, MYSQL_BIND *binds, case FLOAT4OID: { float4 dat = DatumGetFloat4(value); - float4 *bufptr = palloc(sizeof(float4)); + float4 *bufptr = (float4*)palloc0(sizeof(float4)); memcpy(bufptr, (char *) &dat, sizeof(float4)); @@ -244,7 +245,7 @@ mysql_bind_sql_var(Oid type, int attnum, Datum value, MYSQL_BIND *binds, case FLOAT8OID: { float8 dat = DatumGetFloat8(value); - float8 *bufptr = palloc(sizeof(float8)); + float8 *bufptr = (float8*)palloc0(sizeof(float8)); memcpy(bufptr, (char *) &dat, sizeof(float8)); @@ -256,7 +257,7 @@ mysql_bind_sql_var(Oid type, int attnum, Datum value, MYSQL_BIND *binds, Datum valueDatum = DirectFunctionCall1(numeric_float8, value); float8 dat = DatumGetFloat8(valueDatum); - float8 *bufptr = palloc(sizeof(float8)); + float8 *bufptr = (float8*)palloc0(sizeof(float8)); memcpy(bufptr, (char *) &dat, sizeof(float8)); @@ -266,7 +267,7 @@ mysql_bind_sql_var(Oid type, int attnum, Datum value, MYSQL_BIND *binds, case BOOLOID: { int32 dat = DatumGetInt32(value); - int32 *bufptr = palloc(sizeof(int32)); + int32 *bufptr = (int32*)palloc0(sizeof(int32)); memcpy(bufptr, (char *) &dat, sizeof(int32)); @@ -313,7 +314,7 @@ mysql_bind_sql_var(Oid type, int attnum, Datum value, MYSQL_BIND *binds, Datum valueDatum = DirectFunctionCall1(date_timestamp, value); Timestamp valueTimestamp = DatumGetTimestamp(valueDatum); - MYSQL_TIME *ts = palloc0(sizeof(MYSQL_TIME)); + MYSQL_TIME* ts = (MYSQL_TIME*)palloc0(sizeof(MYSQL_TIME)); timestamp2tm(valueTimestamp, &tz, tm, &fsec, &tzn, pg_tzset("UTC")); @@ -329,7 +330,7 @@ mysql_bind_sql_var(Oid type, int attnum, Datum value, MYSQL_BIND *binds, case TIMESTAMPTZOID: { Timestamp valueTimestamp = DatumGetTimestamp(value); - MYSQL_TIME *ts = palloc0(sizeof(MYSQL_TIME)); + MYSQL_TIME* ts = (MYSQL_TIME*)palloc0(sizeof(MYSQL_TIME)); int tz; struct pg_tm tt, *tm = &tt; @@ -348,7 +349,7 @@ mysql_bind_sql_var(Oid type, int attnum, Datum value, MYSQL_BIND *binds, case BITOID: { int32 dat; - int32 *bufptr = palloc0(sizeof(int32)); + int32 *bufptr = (int32*)palloc0(sizeof(int32)); char *outputString = NULL; Oid outputFunctionId = InvalidOid; bool typeVarLength = false; @@ -379,7 +380,7 @@ mysql_bind_sql_var(Oid type, int attnum, Datum value, MYSQL_BIND *binds, dat = VARDATA_4B(result); } - bufptr = palloc(len); + bufptr = (char*)palloc0(len); memcpy(bufptr, (char *) dat, len); binds[attnum].buffer = bufptr; binds[attnum].buffer_length = len; diff --git option.c option.cpp similarity index 99% rename from option.c rename to option.cpp index 30563d0..6d68d7e 100644 --- option.c +++ option.cpp @@ -58,7 +58,7 @@ static struct MySQLFdwOption valid_options[] = {NULL, InvalidOid} }; -extern Datum mysql_fdw_validator(PG_FUNCTION_ARGS); +extern "C" Datum mysql_fdw_validator(PG_FUNCTION_ARGS); PG_FUNCTION_INFO_V1(mysql_fdw_validator); diff --git sql/connection_validation.sql sql/connection_validation.sql index 97749c1..e72dbaa 100644 --- sql/connection_validation.sql +++ sql/connection_validation.sql @@ -1,12 +1,12 @@ \set MYSQL_HOST '\'localhost\'' \set MYSQL_PORT '\'3306\'' \set MYSQL_USER_NAME '\'edb\'' -\set MYSQL_PASS '\'edb\'' +\set MYSQL_PASS '\'Mysql@123!@#\'' -- Before running this file User must create database mysql_fdw_regress on --- MySQL with all permission for 'edb' user with 'edb' password and ran --- mysql_init.sh file to create tables. - +-- MySQL with all permission for 'edb' user with 'Mysql@123!@#' password and +-- ran mysql_init.sh file to create tables. +create database contrib_regression; \c contrib_regression CREATE EXTENSION IF NOT EXISTS mysql_fdw; CREATE SERVER mysql_svr FOREIGN DATA WRAPPER mysql_fdw diff --git sql/dml.sql sql/dml.sql index 00fb29b..0c45b56 100644 --- sql/dml.sql +++ sql/dml.sql @@ -1,11 +1,11 @@ \set MYSQL_HOST '\'localhost\'' \set MYSQL_PORT '\'3306\'' \set MYSQL_USER_NAME '\'edb\'' -\set MYSQL_PASS '\'edb\'' +\set MYSQL_PASS '\'Mysql@123!@#\'' -- Before running this file User must create database mysql_fdw_regress on --- MySQL with all permission for 'edb' user with 'edb' password and ran --- mysql_init.sh file to create tables. +-- MySQL with all permission for 'edb' user with 'Mysql@123!@#' password and +-- ran mysql_init.sh file to create tables. \c contrib_regression CREATE EXTENSION IF NOT EXISTS mysql_fdw; @@ -101,6 +101,7 @@ BEGIN END $$ language plpgsql; +---Currently,triggers can only be created on general row-store tables. CREATE TRIGGER before_row_update_trig BEFORE UPDATE ON fdw126_ft1 FOR EACH ROW EXECUTE PROCEDURE before_row_update_func(); @@ -135,6 +136,7 @@ BEGIN END $$ language plpgsql; +---Currently,triggers can only be created on general row-store tables. CREATE TRIGGER before_row_update_trig1 BEFORE UPDATE ON fdw193_ft1 FOR EACH ROW EXECUTE PROCEDURE before_row_update_func(); diff --git sql/pushdown.sql sql/pushdown.sql index 37af50d..1e37149 100644 --- sql/pushdown.sql +++ sql/pushdown.sql @@ -1,11 +1,11 @@ \set MYSQL_HOST '\'localhost\'' \set MYSQL_PORT '\'3306\'' \set MYSQL_USER_NAME '\'edb\'' -\set MYSQL_PASS '\'edb\'' +\set MYSQL_PASS '\'Mysql@123!@#\'' -- Before running this file User must create database mysql_fdw_regress on --- mysql with all permission for 'edb' user with 'edb' password and ran --- mysql_init.sh file to create tables. +-- mysql with all permission for 'edb' user with 'Mysql@123!@#' password and +-- ran mysql_init.sh file to create tables. \c contrib_regression CREATE EXTENSION IF NOT EXISTS mysql_fdw; diff --git sql/select.sql sql/select.sql index 1ef8d7c..6ce1ffb 100644 --- sql/select.sql +++ sql/select.sql @@ -1,11 +1,11 @@ \set MYSQL_HOST '\'localhost\'' \set MYSQL_PORT '\'3306\'' \set MYSQL_USER_NAME '\'edb\'' -\set MYSQL_PASS '\'edb\'' +\set MYSQL_PASS '\'Mysql@123!@#\'' -- Before running this file User must create database mysql_fdw_regress on --- MySQL with all permission for 'edb' user with 'edb' password and ran --- mysql_init.sh file to create tables. +-- MySQL with all permission for 'edb' user with 'Mysql@123!@#' password and +-- ran mysql_init.sh file to create tables. \c contrib_regression CREATE EXTENSION IF NOT EXISTS mysql_fdw; @@ -222,6 +222,7 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 FROM f_test_tbl2 d FULL OUTER JOIN l_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3; +-- LATERAL is not supported now -- FDW-206: LEFT JOIN LATERAL case should not crash EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM f_mysql_test t1 LEFT JOIN LATERAL ( @@ -275,7 +276,7 @@ SELECT * FROM f_enum_t1 WHERE id = 4; DROP FOREIGN TABLE f_enum_t1; DROP TYPE size_t; -- Create the type with extra enum values. -CREATE TYPE size_t AS enum('small', 'medium', 'large', 'largest', ''); +CREATE TYPE size_t AS enum('small', 'medium', 'large', 'largest', 'blank'); CREATE FOREIGN TABLE f_enum_t1(id int, size size_t) SERVER mysql_svr OPTIONS (dbname 'mysql_fdw_regress', table_name 'enum_t1'); @@ -300,6 +301,7 @@ EXPLAIN (VERBOSE, COSTS OFF) SELECT EXISTS(SELECT 1 FROM pg_enum), sum(id) from f_enum_t1; SELECT EXISTS(SELECT 1 FROM pg_enum), sum(id) from f_enum_t1; +-- IMPORT FOREIGN SCHEMA command is not supported now. -- Check with the IMPORT FOREIGN SCHEMA command. Also, check ENUM types with -- the IMPORT FOREIGN SCHEMA command. If the enum name is the same for multiple -- tables, then it should handle correctly by prefixing the table name. diff --git sql/server_options.sql sql/server_options.sql index b5c981f..85166d2 100644 --- sql/server_options.sql +++ sql/server_options.sql @@ -1,11 +1,11 @@ \set MYSQL_HOST '\'localhost\'' \set MYSQL_PORT '\'3306\'' \set MYSQL_USER_NAME '\'edb\'' -\set MYSQL_PASS '\'edb\'' +\set MYSQL_PASS '\'Mysql@123!@#\'' -- Before running this file User must create database mysql_fdw_regress on --- MySQL with all permission for 'edb' user with 'edb' password and ran --- mysql_init.sh file to create tables. +-- MySQL with all permission for 'edb' user with 'Mysql@123!@#' password and +-- ran mysql_init.sh file to create tables. \c contrib_regression CREATE EXTENSION IF NOT EXISTS mysql_fdw;