Get rid of postgres_fdw's assumption that remote type OIDs match ours.
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 22 Feb 2013 11:36:09 +0000 (06:36 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 22 Feb 2013 11:36:54 +0000 (06:36 -0500)
The only place we depended on that was in sending numeric type OIDs in
PQexecParams; but we can replace that usage with explicitly casting
each Param symbol in the query string, so that the types are specified
to the remote by name not OID.  This makes no immediate difference but
will be essential if we ever hope to support use of non-builtin types.

contrib/postgres_fdw/deparse.c
contrib/postgres_fdw/expected/postgres_fdw.out
contrib/postgres_fdw/postgres_fdw.c

index f005e0f26fb8309b7ccf21522ccdc908a72bc92e..1f4b2449be7e7cf18b62f529e7c845e5e1dcef59 100644 (file)
@@ -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));
 }
 
 /*
index b81a30867388a8b1614599560f4b8fe1fd6a0752..9a2c8e83cc4489b9e5aee6b3182fd87834fc621d 100644 (file)
@@ -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);
index 0aef00b738dfeeede00e50aef31c538e6312bda2..a3256179f2bee9db0446dbc7c3125177fe0fd3b4 100644 (file)
@@ -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