diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c index f005e0f26fb8309b7ccf21522ccdc908a72bc92e..1f4b2449be7e7cf18b62f529e7c845e5e1dcef59 100644 --- a/contrib/postgres_fdw/deparse.c +++ b/contrib/postgres_fdw/deparse.c @@ -331,12 +331,10 @@ foreign_expr_walker(Node *node, foreign_expr_cxt *context) * function or type defined in the information_schema. * * Our constraints for dealing with types are tighter than they are for - * functions or operators: we want to accept only types that are in pg_catalog - * (else format_type might incorrectly fail to schema-qualify their names), - * and we want to be sure that the remote server will use the same OID as - * we do (since we must transmit a numeric type OID when passing a value of - * the type as a query parameter). Both of these are reasons to reject - * objects created post-bootstrap. + * functions or operators: we want to accept only types that are in pg_catalog, + * else format_type might incorrectly fail to schema-qualify their names. + * (This could be fixed with some changes to format_type, but for now there's + * no need.) Thus we must exclude information_schema types. * * XXX there is a problem with this, which is that the set of built-in * objects expands over time. Something that is built-in to us might not @@ -794,12 +792,20 @@ deparseConst(StringInfo buf, Const *node, PlannerInfo *root) * We don't need to renumber the parameter ID, because the executor functions * in postgres_fdw.c preserve the numbering of PARAM_EXTERN Params. * (This might change soon.) + * + * Note: we label the Param's type explicitly rather than relying on + * transmitting a numeric type OID in PQexecParams(). This allows us to + * avoid assuming that types have the same OIDs on the remote side as they + * do locally --- they need only have the same names. */ static void deparseParam(StringInfo buf, Param *node, PlannerInfo *root) { Assert(node->paramkind == PARAM_EXTERN); appendStringInfo(buf, "$%d", node->paramid); + appendStringInfo(buf, "::%s", + format_type_with_typemod(node->paramtype, + node->paramtypmod)); } /* diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index b81a30867388a8b1614599560f4b8fe1fd6a0752..9a2c8e83cc4489b9e5aee6b3182fd87834fc621d 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -558,11 +558,11 @@ EXPLAIN (VERBOSE, COSTS false) EXECUTE st4(1); -- once we try it enough times, should switch to generic plan EXPLAIN (VERBOSE, COSTS false) EXECUTE st4(1); - QUERY PLAN ----------------------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------------------- Foreign Scan on public.ft1 t1 Output: c1, c2, c3, c4, c5, c6, c7, c8 - Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" = $1)) + Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" = $1::integer)) (3 rows) -- value of $1 should not be sent to remote @@ -613,12 +613,12 @@ EXPLAIN (VERBOSE, COSTS false) EXECUTE st5('foo', 1); (4 rows) EXPLAIN (VERBOSE, COSTS false) EXECUTE st5('foo', 1); - QUERY PLAN ----------------------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------------------- Foreign Scan on public.ft1 t1 Output: c1, c2, c3, c4, c5, c6, c7, c8 Filter: (t1.c8 = $1) - Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" = $2)) + Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" = $2::integer)) (4 rows) EXECUTE st5('foo', 1); diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 0aef00b738dfeeede00e50aef31c538e6312bda2..a3256179f2bee9db0446dbc7c3125177fe0fd3b4 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -901,11 +901,24 @@ create_cursor(ForeignScanState *node) if (!OidIsValid(prm->ptype) && params->paramFetch != NULL) params->paramFetch(params, paramno); + /* + * Force the remote server to infer a type for this parameter. + * Since we explicitly cast every parameter (see deparse.c), the + * "inference" is trivial and will produce the desired result. + * This allows us to avoid assuming that the remote server has the + * same OIDs we do for the parameters' types. + * + * We'd not need to pass a type array to PQexecParams at all, + * except that there may be unused holes in the array, which + * will have to be filled with something or the remote server will + * complain. We arbitrarily set them to INT4OID earlier. + */ + types[paramno - 1] = InvalidOid; + /* * Get string representation of each parameter value by invoking * type-specific output function, unless the value is null. */ - types[paramno - 1] = prm->ptype; if (prm->isnull) values[paramno - 1] = NULL; else