]> granicus.if.org Git - postgresql/commitdiff
Avoid retrieving dummy NULL columns in postgres_fdw.
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 22 Mar 2013 04:31:11 +0000 (00:31 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 22 Mar 2013 04:31:11 +0000 (00:31 -0400)
This should provide some marginal overall savings, since it surely takes
many more cycles for the remote server to deal with the NULL columns than
it takes for postgres_fdw not to emit them.  But really the reason is to
keep the emitted queries from looking quite so silly ...

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

index 8d05b199343198fd474550623295be75704d548b..a03eec3c828763d04f56279905c972049ef1e0ab 100644 (file)
@@ -106,10 +106,12 @@ static void deparseTargetList(StringInfo buf,
                                  PlannerInfo *root,
                                  Index rtindex,
                                  Relation rel,
-                                 Bitmapset *attrs_used);
+                                 Bitmapset *attrs_used,
+                                 List **retrieved_attrs);
 static void deparseReturningList(StringInfo buf, PlannerInfo *root,
                                         Index rtindex, Relation rel,
-                                        List *returningList);
+                                        List *returningList,
+                                        List **retrieved_attrs);
 static void deparseColumnRef(StringInfo buf, int varno, int varattno,
                                 PlannerInfo *root);
 static void deparseRelation(StringInfo buf, Relation rel);
@@ -652,12 +654,16 @@ is_builtin(Oid oid)
  * Construct a simple SELECT statement that retrieves desired columns
  * of the specified foreign table, and append it to "buf".     The output
  * contains just "SELECT ... FROM tablename".
+ *
+ * We also create an integer List of the columns being retrieved, which is
+ * returned to *retrieved_attrs.
  */
 void
 deparseSelectSql(StringInfo buf,
                                 PlannerInfo *root,
                                 RelOptInfo *baserel,
-                                Bitmapset *attrs_used)
+                                Bitmapset *attrs_used,
+                                List **retrieved_attrs)
 {
        RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root);
        Relation        rel;
@@ -672,7 +678,8 @@ deparseSelectSql(StringInfo buf,
         * Construct SELECT list
         */
        appendStringInfoString(buf, "SELECT ");
-       deparseTargetList(buf, root, baserel->relid, rel, attrs_used);
+       deparseTargetList(buf, root, baserel->relid, rel, attrs_used,
+                                         retrieved_attrs);
 
        /*
         * Construct FROM clause
@@ -687,24 +694,24 @@ deparseSelectSql(StringInfo buf,
  * Emit a target list that retrieves the columns specified in attrs_used.
  * This is used for both SELECT and RETURNING targetlists.
  *
- * We list attributes in order of the foreign table's columns, but replace
- * any attributes that need not be fetched with NULL constants.  (We can't
- * just omit such attributes, or we'll lose track of which columns are
- * which at runtime.)  Note however that any dropped columns are ignored.
- * Also, if ctid needs to be retrieved, it's added at the end.
+ * The tlist text is appended to buf, and we also create an integer List
+ * of the columns being retrieved, which is returned to *retrieved_attrs.
  */
 static void
 deparseTargetList(StringInfo buf,
                                  PlannerInfo *root,
                                  Index rtindex,
                                  Relation rel,
-                                 Bitmapset *attrs_used)
+                                 Bitmapset *attrs_used,
+                                 List **retrieved_attrs)
 {
        TupleDesc       tupdesc = RelationGetDescr(rel);
        bool            have_wholerow;
        bool            first;
        int                     i;
 
+       *retrieved_attrs = NIL;
+
        /* If there's a whole-row reference, we'll need all the columns. */
        have_wholerow = bms_is_member(0 - FirstLowInvalidHeapAttributeNumber,
                                                                  attrs_used);
@@ -718,16 +725,18 @@ deparseTargetList(StringInfo buf,
                if (attr->attisdropped)
                        continue;
 
-               if (!first)
-                       appendStringInfoString(buf, ", ");
-               first = false;
-
                if (have_wholerow ||
                        bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
                                                  attrs_used))
+               {
+                       if (!first)
+                               appendStringInfoString(buf, ", ");
+                       first = false;
+
                        deparseColumnRef(buf, rtindex, i, root);
-               else
-                       appendStringInfoString(buf, "NULL");
+
+                       *retrieved_attrs = lappend_int(*retrieved_attrs, i);
+               }
        }
 
        /*
@@ -742,6 +751,9 @@ deparseTargetList(StringInfo buf,
                first = false;
 
                appendStringInfoString(buf, "ctid");
+
+               *retrieved_attrs = lappend_int(*retrieved_attrs,
+                                                                          SelfItemPointerAttributeNumber);
        }
 
        /* Don't generate bad syntax if no undropped columns */
@@ -809,11 +821,16 @@ appendWhereClause(StringInfo buf,
 
 /*
  * deparse remote INSERT statement
+ *
+ * The statement text is appended to buf, and we also create an integer List
+ * of the columns being retrieved by RETURNING (if any), which is returned
+ * to *retrieved_attrs.
  */
 void
 deparseInsertSql(StringInfo buf, PlannerInfo *root,
                                 Index rtindex, Relation rel,
-                                List *targetAttrs, List *returningList)
+                                List *targetAttrs, List *returningList,
+                                List **retrieved_attrs)
 {
        AttrNumber      pindex;
        bool            first;
@@ -858,16 +875,24 @@ deparseInsertSql(StringInfo buf, PlannerInfo *root,
                appendStringInfoString(buf, " DEFAULT VALUES");
 
        if (returningList)
-               deparseReturningList(buf, root, rtindex, rel, returningList);
+               deparseReturningList(buf, root, rtindex, rel, returningList,
+                                                        retrieved_attrs);
+       else
+               *retrieved_attrs = NIL;
 }
 
 /*
  * deparse remote UPDATE statement
+ *
+ * The statement text is appended to buf, and we also create an integer List
+ * of the columns being retrieved by RETURNING (if any), which is returned
+ * to *retrieved_attrs.
  */
 void
 deparseUpdateSql(StringInfo buf, PlannerInfo *root,
                                 Index rtindex, Relation rel,
-                                List *targetAttrs, List *returningList)
+                                List *targetAttrs, List *returningList,
+                                List **retrieved_attrs)
 {
        AttrNumber      pindex;
        bool            first;
@@ -894,23 +919,34 @@ deparseUpdateSql(StringInfo buf, PlannerInfo *root,
        appendStringInfoString(buf, " WHERE ctid = $1");
 
        if (returningList)
-               deparseReturningList(buf, root, rtindex, rel, returningList);
+               deparseReturningList(buf, root, rtindex, rel, returningList,
+                                                        retrieved_attrs);
+       else
+               *retrieved_attrs = NIL;
 }
 
 /*
  * deparse remote DELETE statement
+ *
+ * The statement text is appended to buf, and we also create an integer List
+ * of the columns being retrieved by RETURNING (if any), which is returned
+ * to *retrieved_attrs.
  */
 void
 deparseDeleteSql(StringInfo buf, PlannerInfo *root,
                                 Index rtindex, Relation rel,
-                                List *returningList)
+                                List *returningList,
+                                List **retrieved_attrs)
 {
        appendStringInfoString(buf, "DELETE FROM ");
        deparseRelation(buf, rel);
        appendStringInfoString(buf, " WHERE ctid = $1");
 
        if (returningList)
-               deparseReturningList(buf, root, rtindex, rel, returningList);
+               deparseReturningList(buf, root, rtindex, rel, returningList,
+                                                        retrieved_attrs);
+       else
+               *retrieved_attrs = NIL;
 }
 
 /*
@@ -919,7 +955,8 @@ deparseDeleteSql(StringInfo buf, PlannerInfo *root,
 static void
 deparseReturningList(StringInfo buf, PlannerInfo *root,
                                         Index rtindex, Relation rel,
-                                        List *returningList)
+                                        List *returningList,
+                                        List **retrieved_attrs)
 {
        Bitmapset  *attrs_used;
 
@@ -931,7 +968,8 @@ deparseReturningList(StringInfo buf, PlannerInfo *root,
                                   &attrs_used);
 
        appendStringInfoString(buf, " RETURNING ");
-       deparseTargetList(buf, root, rtindex, rel, attrs_used);
+       deparseTargetList(buf, root, rtindex, rel, attrs_used,
+                                         retrieved_attrs);
 }
 
 /*
@@ -959,10 +997,11 @@ deparseAnalyzeSizeSql(StringInfo buf, Relation rel)
 /*
  * Construct SELECT statement to acquire sample rows of given relation.
  *
- * Note: command is appended to whatever might be in buf already.
+ * SELECT command is appended to buf, and list of columns retrieved
+ * is returned to *retrieved_attrs.
  */
 void
-deparseAnalyzeSql(StringInfo buf, Relation rel)
+deparseAnalyzeSql(StringInfo buf, Relation rel, List **retrieved_attrs)
 {
        Oid                     relid = RelationGetRelid(rel);
        TupleDesc       tupdesc = RelationGetDescr(rel);
@@ -972,6 +1011,8 @@ deparseAnalyzeSql(StringInfo buf, Relation rel)
        ListCell   *lc;
        bool            first = true;
 
+       *retrieved_attrs = NIL;
+
        appendStringInfoString(buf, "SELECT ");
        for (i = 0; i < tupdesc->natts; i++)
        {
@@ -999,6 +1040,8 @@ deparseAnalyzeSql(StringInfo buf, Relation rel)
                }
 
                appendStringInfoString(buf, quote_identifier(colname));
+
+               *retrieved_attrs = lappend_int(*retrieved_attrs, i + 1);
        }
 
        /* Don't generate bad syntax for zero-column relation. */
index 3909ef8b586b0cf7f2d79093ea94e9dab26b3ab3..cb007cd58d6acc1e3e333058b835f2e323f05e69 100644 (file)
@@ -473,16 +473,16 @@ SELECT * FROM ft2 a, ft2 b WHERE a.c1 = 47 AND b.c1 = a.c2;
 -- simple join
 PREPARE st1(int, int) AS SELECT t1.c3, t2.c3 FROM ft1 t1, ft2 t2 WHERE t1.c1 = $1 AND t2.c1 = $2;
 EXPLAIN (VERBOSE, COSTS false) EXECUTE st1(1, 2);
-                                                  QUERY PLAN                                                  
---------------------------------------------------------------------------------------------------------------
+                             QUERY PLAN                             
+--------------------------------------------------------------------
  Nested Loop
    Output: t1.c3, t2.c3
    ->  Foreign Scan on public.ft1 t1
          Output: t1.c3
-         Remote SQL: SELECT NULL, NULL, c3, NULL, NULL, NULL, NULL, NULL FROM "S 1"."T 1" WHERE (("C 1" = 1))
+         Remote SQL: SELECT c3 FROM "S 1"."T 1" WHERE (("C 1" = 1))
    ->  Foreign Scan on public.ft2 t2
          Output: t2.c3
-         Remote SQL: SELECT NULL, NULL, c3, NULL, NULL, NULL, NULL, NULL FROM "S 1"."T 1" WHERE (("C 1" = 2))
+         Remote SQL: SELECT c3 FROM "S 1"."T 1" WHERE (("C 1" = 2))
 (8 rows)
 
 EXECUTE st1(1, 1);
@@ -500,8 +500,8 @@ EXECUTE st1(101, 101);
 -- subquery using stable function (can't be sent to remote)
 PREPARE st2(int) AS SELECT * FROM ft1 t1 WHERE t1.c1 < $2 AND t1.c3 IN (SELECT c3 FROM ft2 t2 WHERE c1 > $1 AND date(c4) = '1970-01-17'::date) ORDER BY c1;
 EXPLAIN (VERBOSE, COSTS false) EXECUTE st2(10, 20);
-                                                       QUERY PLAN                                                        
--------------------------------------------------------------------------------------------------------------------------
+                                                QUERY PLAN                                                
+----------------------------------------------------------------------------------------------------------
  Sort
    Output: t1.c1, t1.c2, t1.c3, t1.c4, t1.c5, t1.c6, t1.c7, t1.c8
    Sort Key: t1.c1
@@ -516,7 +516,7 @@ EXPLAIN (VERBOSE, COSTS false) EXECUTE st2(10, 20);
                ->  Foreign Scan on public.ft2 t2
                      Output: t2.c3
                      Filter: (date(t2.c4) = '01-17-1970'::date)
-                     Remote SQL: SELECT NULL, NULL, c3, c4, NULL, NULL, NULL, NULL FROM "S 1"."T 1" WHERE (("C 1" > 10))
+                     Remote SQL: SELECT c3, c4 FROM "S 1"."T 1" WHERE (("C 1" > 10))
 (15 rows)
 
 EXECUTE st2(10, 20);
@@ -534,8 +534,8 @@ EXECUTE st2(101, 121);
 -- subquery using immutable function (can be sent to remote)
 PREPARE st3(int) AS SELECT * FROM ft1 t1 WHERE t1.c1 < $2 AND t1.c3 IN (SELECT c3 FROM ft2 t2 WHERE c1 > $1 AND date(c5) = '1970-01-17'::date) ORDER BY c1;
 EXPLAIN (VERBOSE, COSTS false) EXECUTE st3(10, 20);
-                                                                           QUERY PLAN                                                                            
------------------------------------------------------------------------------------------------------------------------------------------------------------------
+                                                      QUERY PLAN                                                       
+-----------------------------------------------------------------------------------------------------------------------
  Sort
    Output: t1.c1, t1.c2, t1.c3, t1.c4, t1.c5, t1.c6, t1.c7, t1.c8
    Sort Key: t1.c1
@@ -549,7 +549,7 @@ EXPLAIN (VERBOSE, COSTS false) EXECUTE st3(10, 20);
                Output: t2.c3
                ->  Foreign Scan on public.ft2 t2
                      Output: t2.c3
-                     Remote SQL: SELECT NULL, NULL, c3, NULL, NULL, NULL, NULL, NULL FROM "S 1"."T 1" WHERE (("C 1" > 10)) AND ((date(c5) = '1970-01-17'::date))
+                     Remote SQL: SELECT c3 FROM "S 1"."T 1" WHERE (("C 1" > 10)) AND ((date(c5) = '1970-01-17'::date))
 (14 rows)
 
 EXECUTE st3(10, 20);
@@ -834,7 +834,7 @@ INSERT INTO ft2 (c1,c2,c3) SELECT c1+1000,c2+100, c3 || c3 FROM ft2 LIMIT 20;
                Output: ((ft2_1.c1 + 1000)), ((ft2_1.c2 + 100)), ((ft2_1.c3 || ft2_1.c3))
                ->  Foreign Scan on public.ft2 ft2_1
                      Output: (ft2_1.c1 + 1000), (ft2_1.c2 + 100), (ft2_1.c3 || ft2_1.c3)
-                     Remote SQL: SELECT "C 1", c2, c3, NULL, NULL, NULL, NULL, NULL FROM "S 1"."T 1"
+                     Remote SQL: SELECT "C 1", c2, c3 FROM "S 1"."T 1"
 (9 rows)
 
 INSERT INTO ft2 (c1,c2,c3) SELECT c1+1000,c2+100, c3 || c3 FROM ft2 LIMIT 20;
@@ -968,7 +968,7 @@ UPDATE ft2 SET c2 = ft2.c2 + 500, c3 = ft2.c3 || '_update9', c7 = DEFAULT
          Hash Cond: (ft2.c2 = ft1.c1)
          ->  Foreign Scan on public.ft2
                Output: ft2.c1, ft2.c2, ft2.c3, ft2.c4, ft2.c5, ft2.c6, ft2.c8, ft2.ctid
-               Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, NULL, c8, ctid FROM "S 1"."T 1" FOR UPDATE
+               Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c8, ctid FROM "S 1"."T 1" FOR UPDATE
          ->  Hash
                Output: ft1.*, ft1.c1
                ->  Foreign Scan on public.ft1
@@ -978,112 +978,124 @@ UPDATE ft2 SET c2 = ft2.c2 + 500, c3 = ft2.c3 || '_update9', c7 = DEFAULT
 
 UPDATE ft2 SET c2 = ft2.c2 + 500, c3 = ft2.c3 || '_update9', c7 = DEFAULT
   FROM ft1 WHERE ft1.c1 = ft2.c2 AND ft1.c1 % 10 = 9;
-DELETE FROM ft2 WHERE c1 % 10 = 5 RETURNING *;
-  c1  | c2  |     c3     |              c4              |            c5            | c6 |     c7     | c8  
-------+-----+------------+------------------------------+--------------------------+----+------------+-----
-    5 |   5 | 00005      | Tue Jan 06 00:00:00 1970 PST | Tue Jan 06 00:00:00 1970 | 5  | 5          | foo
-   15 |   5 | 00015      | Fri Jan 16 00:00:00 1970 PST | Fri Jan 16 00:00:00 1970 | 5  | 5          | foo
-   25 |   5 | 00025      | Mon Jan 26 00:00:00 1970 PST | Mon Jan 26 00:00:00 1970 | 5  | 5          | foo
-   35 |   5 | 00035      | Thu Feb 05 00:00:00 1970 PST | Thu Feb 05 00:00:00 1970 | 5  | 5          | foo
-   45 |   5 | 00045      | Sun Feb 15 00:00:00 1970 PST | Sun Feb 15 00:00:00 1970 | 5  | 5          | foo
-   55 |   5 | 00055      | Wed Feb 25 00:00:00 1970 PST | Wed Feb 25 00:00:00 1970 | 5  | 5          | foo
-   65 |   5 | 00065      | Sat Mar 07 00:00:00 1970 PST | Sat Mar 07 00:00:00 1970 | 5  | 5          | foo
-   75 |   5 | 00075      | Tue Mar 17 00:00:00 1970 PST | Tue Mar 17 00:00:00 1970 | 5  | 5          | foo
-   85 |   5 | 00085      | Fri Mar 27 00:00:00 1970 PST | Fri Mar 27 00:00:00 1970 | 5  | 5          | foo
-   95 |   5 | 00095      | Mon Apr 06 00:00:00 1970 PST | Mon Apr 06 00:00:00 1970 | 5  | 5          | foo
-  105 |   5 | 00105      | Tue Jan 06 00:00:00 1970 PST | Tue Jan 06 00:00:00 1970 | 5  | 5          | foo
-  115 |   5 | 00115      | Fri Jan 16 00:00:00 1970 PST | Fri Jan 16 00:00:00 1970 | 5  | 5          | foo
-  125 |   5 | 00125      | Mon Jan 26 00:00:00 1970 PST | Mon Jan 26 00:00:00 1970 | 5  | 5          | foo
-  135 |   5 | 00135      | Thu Feb 05 00:00:00 1970 PST | Thu Feb 05 00:00:00 1970 | 5  | 5          | foo
-  145 |   5 | 00145      | Sun Feb 15 00:00:00 1970 PST | Sun Feb 15 00:00:00 1970 | 5  | 5          | foo
-  155 |   5 | 00155      | Wed Feb 25 00:00:00 1970 PST | Wed Feb 25 00:00:00 1970 | 5  | 5          | foo
-  165 |   5 | 00165      | Sat Mar 07 00:00:00 1970 PST | Sat Mar 07 00:00:00 1970 | 5  | 5          | foo
-  175 |   5 | 00175      | Tue Mar 17 00:00:00 1970 PST | Tue Mar 17 00:00:00 1970 | 5  | 5          | foo
-  185 |   5 | 00185      | Fri Mar 27 00:00:00 1970 PST | Fri Mar 27 00:00:00 1970 | 5  | 5          | foo
-  195 |   5 | 00195      | Mon Apr 06 00:00:00 1970 PST | Mon Apr 06 00:00:00 1970 | 5  | 5          | foo
-  205 |   5 | 00205      | Tue Jan 06 00:00:00 1970 PST | Tue Jan 06 00:00:00 1970 | 5  | 5          | foo
-  215 |   5 | 00215      | Fri Jan 16 00:00:00 1970 PST | Fri Jan 16 00:00:00 1970 | 5  | 5          | foo
-  225 |   5 | 00225      | Mon Jan 26 00:00:00 1970 PST | Mon Jan 26 00:00:00 1970 | 5  | 5          | foo
-  235 |   5 | 00235      | Thu Feb 05 00:00:00 1970 PST | Thu Feb 05 00:00:00 1970 | 5  | 5          | foo
-  245 |   5 | 00245      | Sun Feb 15 00:00:00 1970 PST | Sun Feb 15 00:00:00 1970 | 5  | 5          | foo
-  255 |   5 | 00255      | Wed Feb 25 00:00:00 1970 PST | Wed Feb 25 00:00:00 1970 | 5  | 5          | foo
-  265 |   5 | 00265      | Sat Mar 07 00:00:00 1970 PST | Sat Mar 07 00:00:00 1970 | 5  | 5          | foo
-  275 |   5 | 00275      | Tue Mar 17 00:00:00 1970 PST | Tue Mar 17 00:00:00 1970 | 5  | 5          | foo
-  285 |   5 | 00285      | Fri Mar 27 00:00:00 1970 PST | Fri Mar 27 00:00:00 1970 | 5  | 5          | foo
-  295 |   5 | 00295      | Mon Apr 06 00:00:00 1970 PST | Mon Apr 06 00:00:00 1970 | 5  | 5          | foo
-  305 |   5 | 00305      | Tue Jan 06 00:00:00 1970 PST | Tue Jan 06 00:00:00 1970 | 5  | 5          | foo
-  315 |   5 | 00315      | Fri Jan 16 00:00:00 1970 PST | Fri Jan 16 00:00:00 1970 | 5  | 5          | foo
-  325 |   5 | 00325      | Mon Jan 26 00:00:00 1970 PST | Mon Jan 26 00:00:00 1970 | 5  | 5          | foo
-  335 |   5 | 00335      | Thu Feb 05 00:00:00 1970 PST | Thu Feb 05 00:00:00 1970 | 5  | 5          | foo
-  345 |   5 | 00345      | Sun Feb 15 00:00:00 1970 PST | Sun Feb 15 00:00:00 1970 | 5  | 5          | foo
-  355 |   5 | 00355      | Wed Feb 25 00:00:00 1970 PST | Wed Feb 25 00:00:00 1970 | 5  | 5          | foo
-  365 |   5 | 00365      | Sat Mar 07 00:00:00 1970 PST | Sat Mar 07 00:00:00 1970 | 5  | 5          | foo
-  375 |   5 | 00375      | Tue Mar 17 00:00:00 1970 PST | Tue Mar 17 00:00:00 1970 | 5  | 5          | foo
-  385 |   5 | 00385      | Fri Mar 27 00:00:00 1970 PST | Fri Mar 27 00:00:00 1970 | 5  | 5          | foo
-  395 |   5 | 00395      | Mon Apr 06 00:00:00 1970 PST | Mon Apr 06 00:00:00 1970 | 5  | 5          | foo
-  405 |   5 | 00405      | Tue Jan 06 00:00:00 1970 PST | Tue Jan 06 00:00:00 1970 | 5  | 5          | foo
-  415 |   5 | 00415      | Fri Jan 16 00:00:00 1970 PST | Fri Jan 16 00:00:00 1970 | 5  | 5          | foo
-  425 |   5 | 00425      | Mon Jan 26 00:00:00 1970 PST | Mon Jan 26 00:00:00 1970 | 5  | 5          | foo
-  435 |   5 | 00435      | Thu Feb 05 00:00:00 1970 PST | Thu Feb 05 00:00:00 1970 | 5  | 5          | foo
-  445 |   5 | 00445      | Sun Feb 15 00:00:00 1970 PST | Sun Feb 15 00:00:00 1970 | 5  | 5          | foo
-  455 |   5 | 00455      | Wed Feb 25 00:00:00 1970 PST | Wed Feb 25 00:00:00 1970 | 5  | 5          | foo
-  465 |   5 | 00465      | Sat Mar 07 00:00:00 1970 PST | Sat Mar 07 00:00:00 1970 | 5  | 5          | foo
-  475 |   5 | 00475      | Tue Mar 17 00:00:00 1970 PST | Tue Mar 17 00:00:00 1970 | 5  | 5          | foo
-  485 |   5 | 00485      | Fri Mar 27 00:00:00 1970 PST | Fri Mar 27 00:00:00 1970 | 5  | 5          | foo
-  495 |   5 | 00495      | Mon Apr 06 00:00:00 1970 PST | Mon Apr 06 00:00:00 1970 | 5  | 5          | foo
-  505 |   5 | 00505      | Tue Jan 06 00:00:00 1970 PST | Tue Jan 06 00:00:00 1970 | 5  | 5          | foo
-  515 |   5 | 00515      | Fri Jan 16 00:00:00 1970 PST | Fri Jan 16 00:00:00 1970 | 5  | 5          | foo
-  525 |   5 | 00525      | Mon Jan 26 00:00:00 1970 PST | Mon Jan 26 00:00:00 1970 | 5  | 5          | foo
-  535 |   5 | 00535      | Thu Feb 05 00:00:00 1970 PST | Thu Feb 05 00:00:00 1970 | 5  | 5          | foo
-  545 |   5 | 00545      | Sun Feb 15 00:00:00 1970 PST | Sun Feb 15 00:00:00 1970 | 5  | 5          | foo
-  555 |   5 | 00555      | Wed Feb 25 00:00:00 1970 PST | Wed Feb 25 00:00:00 1970 | 5  | 5          | foo
-  565 |   5 | 00565      | Sat Mar 07 00:00:00 1970 PST | Sat Mar 07 00:00:00 1970 | 5  | 5          | foo
-  575 |   5 | 00575      | Tue Mar 17 00:00:00 1970 PST | Tue Mar 17 00:00:00 1970 | 5  | 5          | foo
-  585 |   5 | 00585      | Fri Mar 27 00:00:00 1970 PST | Fri Mar 27 00:00:00 1970 | 5  | 5          | foo
-  595 |   5 | 00595      | Mon Apr 06 00:00:00 1970 PST | Mon Apr 06 00:00:00 1970 | 5  | 5          | foo
-  605 |   5 | 00605      | Tue Jan 06 00:00:00 1970 PST | Tue Jan 06 00:00:00 1970 | 5  | 5          | foo
-  615 |   5 | 00615      | Fri Jan 16 00:00:00 1970 PST | Fri Jan 16 00:00:00 1970 | 5  | 5          | foo
-  625 |   5 | 00625      | Mon Jan 26 00:00:00 1970 PST | Mon Jan 26 00:00:00 1970 | 5  | 5          | foo
-  635 |   5 | 00635      | Thu Feb 05 00:00:00 1970 PST | Thu Feb 05 00:00:00 1970 | 5  | 5          | foo
-  645 |   5 | 00645      | Sun Feb 15 00:00:00 1970 PST | Sun Feb 15 00:00:00 1970 | 5  | 5          | foo
-  655 |   5 | 00655      | Wed Feb 25 00:00:00 1970 PST | Wed Feb 25 00:00:00 1970 | 5  | 5          | foo
-  665 |   5 | 00665      | Sat Mar 07 00:00:00 1970 PST | Sat Mar 07 00:00:00 1970 | 5  | 5          | foo
-  675 |   5 | 00675      | Tue Mar 17 00:00:00 1970 PST | Tue Mar 17 00:00:00 1970 | 5  | 5          | foo
-  685 |   5 | 00685      | Fri Mar 27 00:00:00 1970 PST | Fri Mar 27 00:00:00 1970 | 5  | 5          | foo
-  695 |   5 | 00695      | Mon Apr 06 00:00:00 1970 PST | Mon Apr 06 00:00:00 1970 | 5  | 5          | foo
-  705 |   5 | 00705      | Tue Jan 06 00:00:00 1970 PST | Tue Jan 06 00:00:00 1970 | 5  | 5          | foo
-  715 |   5 | 00715      | Fri Jan 16 00:00:00 1970 PST | Fri Jan 16 00:00:00 1970 | 5  | 5          | foo
-  725 |   5 | 00725      | Mon Jan 26 00:00:00 1970 PST | Mon Jan 26 00:00:00 1970 | 5  | 5          | foo
-  735 |   5 | 00735      | Thu Feb 05 00:00:00 1970 PST | Thu Feb 05 00:00:00 1970 | 5  | 5          | foo
-  745 |   5 | 00745      | Sun Feb 15 00:00:00 1970 PST | Sun Feb 15 00:00:00 1970 | 5  | 5          | foo
-  755 |   5 | 00755      | Wed Feb 25 00:00:00 1970 PST | Wed Feb 25 00:00:00 1970 | 5  | 5          | foo
-  765 |   5 | 00765      | Sat Mar 07 00:00:00 1970 PST | Sat Mar 07 00:00:00 1970 | 5  | 5          | foo
-  775 |   5 | 00775      | Tue Mar 17 00:00:00 1970 PST | Tue Mar 17 00:00:00 1970 | 5  | 5          | foo
-  785 |   5 | 00785      | Fri Mar 27 00:00:00 1970 PST | Fri Mar 27 00:00:00 1970 | 5  | 5          | foo
-  795 |   5 | 00795      | Mon Apr 06 00:00:00 1970 PST | Mon Apr 06 00:00:00 1970 | 5  | 5          | foo
-  805 |   5 | 00805      | Tue Jan 06 00:00:00 1970 PST | Tue Jan 06 00:00:00 1970 | 5  | 5          | foo
-  815 |   5 | 00815      | Fri Jan 16 00:00:00 1970 PST | Fri Jan 16 00:00:00 1970 | 5  | 5          | foo
-  825 |   5 | 00825      | Mon Jan 26 00:00:00 1970 PST | Mon Jan 26 00:00:00 1970 | 5  | 5          | foo
-  835 |   5 | 00835      | Thu Feb 05 00:00:00 1970 PST | Thu Feb 05 00:00:00 1970 | 5  | 5          | foo
-  845 |   5 | 00845      | Sun Feb 15 00:00:00 1970 PST | Sun Feb 15 00:00:00 1970 | 5  | 5          | foo
-  855 |   5 | 00855      | Wed Feb 25 00:00:00 1970 PST | Wed Feb 25 00:00:00 1970 | 5  | 5          | foo
-  865 |   5 | 00865      | Sat Mar 07 00:00:00 1970 PST | Sat Mar 07 00:00:00 1970 | 5  | 5          | foo
-  875 |   5 | 00875      | Tue Mar 17 00:00:00 1970 PST | Tue Mar 17 00:00:00 1970 | 5  | 5          | foo
-  885 |   5 | 00885      | Fri Mar 27 00:00:00 1970 PST | Fri Mar 27 00:00:00 1970 | 5  | 5          | foo
-  895 |   5 | 00895      | Mon Apr 06 00:00:00 1970 PST | Mon Apr 06 00:00:00 1970 | 5  | 5          | foo
-  905 |   5 | 00905      | Tue Jan 06 00:00:00 1970 PST | Tue Jan 06 00:00:00 1970 | 5  | 5          | foo
-  915 |   5 | 00915      | Fri Jan 16 00:00:00 1970 PST | Fri Jan 16 00:00:00 1970 | 5  | 5          | foo
-  925 |   5 | 00925      | Mon Jan 26 00:00:00 1970 PST | Mon Jan 26 00:00:00 1970 | 5  | 5          | foo
-  935 |   5 | 00935      | Thu Feb 05 00:00:00 1970 PST | Thu Feb 05 00:00:00 1970 | 5  | 5          | foo
-  945 |   5 | 00945      | Sun Feb 15 00:00:00 1970 PST | Sun Feb 15 00:00:00 1970 | 5  | 5          | foo
-  955 |   5 | 00955      | Wed Feb 25 00:00:00 1970 PST | Wed Feb 25 00:00:00 1970 | 5  | 5          | foo
-  965 |   5 | 00965      | Sat Mar 07 00:00:00 1970 PST | Sat Mar 07 00:00:00 1970 | 5  | 5          | foo
-  975 |   5 | 00975      | Tue Mar 17 00:00:00 1970 PST | Tue Mar 17 00:00:00 1970 | 5  | 5          | foo
-  985 |   5 | 00985      | Fri Mar 27 00:00:00 1970 PST | Fri Mar 27 00:00:00 1970 | 5  | 5          | foo
-  995 |   5 | 00995      | Mon Apr 06 00:00:00 1970 PST | Mon Apr 06 00:00:00 1970 | 5  | 5          | foo
- 1005 | 105 | 0000500005 |                              |                          |    | ft2        | 
- 1015 | 105 | 0001500015 |                              |                          |    | ft2        | 
- 1105 | 205 | eee        |                              |                          |    | ft2        | 
+EXPLAIN (verbose, costs off)
+  DELETE FROM ft2 WHERE c1 % 10 = 5 RETURNING c1, c4;
+                                       QUERY PLAN                                       
+----------------------------------------------------------------------------------------
+ Delete on public.ft2
+   Output: c1, c4
+   Remote SQL: DELETE FROM "S 1"."T 1" WHERE ctid = $1 RETURNING "C 1", c4
+   ->  Foreign Scan on public.ft2
+         Output: ctid
+         Remote SQL: SELECT ctid FROM "S 1"."T 1" WHERE ((("C 1" % 10) = 5)) FOR UPDATE
+(6 rows)
+
+DELETE FROM ft2 WHERE c1 % 10 = 5 RETURNING c1, c4;
+  c1  |              c4              
+------+------------------------------
+    5 | Tue Jan 06 00:00:00 1970 PST
+   15 | Fri Jan 16 00:00:00 1970 PST
+   25 | Mon Jan 26 00:00:00 1970 PST
+   35 | Thu Feb 05 00:00:00 1970 PST
+   45 | Sun Feb 15 00:00:00 1970 PST
+   55 | Wed Feb 25 00:00:00 1970 PST
+   65 | Sat Mar 07 00:00:00 1970 PST
+   75 | Tue Mar 17 00:00:00 1970 PST
+   85 | Fri Mar 27 00:00:00 1970 PST
+   95 | Mon Apr 06 00:00:00 1970 PST
+  105 | Tue Jan 06 00:00:00 1970 PST
+  115 | Fri Jan 16 00:00:00 1970 PST
+  125 | Mon Jan 26 00:00:00 1970 PST
+  135 | Thu Feb 05 00:00:00 1970 PST
+  145 | Sun Feb 15 00:00:00 1970 PST
+  155 | Wed Feb 25 00:00:00 1970 PST
+  165 | Sat Mar 07 00:00:00 1970 PST
+  175 | Tue Mar 17 00:00:00 1970 PST
+  185 | Fri Mar 27 00:00:00 1970 PST
+  195 | Mon Apr 06 00:00:00 1970 PST
+  205 | Tue Jan 06 00:00:00 1970 PST
+  215 | Fri Jan 16 00:00:00 1970 PST
+  225 | Mon Jan 26 00:00:00 1970 PST
+  235 | Thu Feb 05 00:00:00 1970 PST
+  245 | Sun Feb 15 00:00:00 1970 PST
+  255 | Wed Feb 25 00:00:00 1970 PST
+  265 | Sat Mar 07 00:00:00 1970 PST
+  275 | Tue Mar 17 00:00:00 1970 PST
+  285 | Fri Mar 27 00:00:00 1970 PST
+  295 | Mon Apr 06 00:00:00 1970 PST
+  305 | Tue Jan 06 00:00:00 1970 PST
+  315 | Fri Jan 16 00:00:00 1970 PST
+  325 | Mon Jan 26 00:00:00 1970 PST
+  335 | Thu Feb 05 00:00:00 1970 PST
+  345 | Sun Feb 15 00:00:00 1970 PST
+  355 | Wed Feb 25 00:00:00 1970 PST
+  365 | Sat Mar 07 00:00:00 1970 PST
+  375 | Tue Mar 17 00:00:00 1970 PST
+  385 | Fri Mar 27 00:00:00 1970 PST
+  395 | Mon Apr 06 00:00:00 1970 PST
+  405 | Tue Jan 06 00:00:00 1970 PST
+  415 | Fri Jan 16 00:00:00 1970 PST
+  425 | Mon Jan 26 00:00:00 1970 PST
+  435 | Thu Feb 05 00:00:00 1970 PST
+  445 | Sun Feb 15 00:00:00 1970 PST
+  455 | Wed Feb 25 00:00:00 1970 PST
+  465 | Sat Mar 07 00:00:00 1970 PST
+  475 | Tue Mar 17 00:00:00 1970 PST
+  485 | Fri Mar 27 00:00:00 1970 PST
+  495 | Mon Apr 06 00:00:00 1970 PST
+  505 | Tue Jan 06 00:00:00 1970 PST
+  515 | Fri Jan 16 00:00:00 1970 PST
+  525 | Mon Jan 26 00:00:00 1970 PST
+  535 | Thu Feb 05 00:00:00 1970 PST
+  545 | Sun Feb 15 00:00:00 1970 PST
+  555 | Wed Feb 25 00:00:00 1970 PST
+  565 | Sat Mar 07 00:00:00 1970 PST
+  575 | Tue Mar 17 00:00:00 1970 PST
+  585 | Fri Mar 27 00:00:00 1970 PST
+  595 | Mon Apr 06 00:00:00 1970 PST
+  605 | Tue Jan 06 00:00:00 1970 PST
+  615 | Fri Jan 16 00:00:00 1970 PST
+  625 | Mon Jan 26 00:00:00 1970 PST
+  635 | Thu Feb 05 00:00:00 1970 PST
+  645 | Sun Feb 15 00:00:00 1970 PST
+  655 | Wed Feb 25 00:00:00 1970 PST
+  665 | Sat Mar 07 00:00:00 1970 PST
+  675 | Tue Mar 17 00:00:00 1970 PST
+  685 | Fri Mar 27 00:00:00 1970 PST
+  695 | Mon Apr 06 00:00:00 1970 PST
+  705 | Tue Jan 06 00:00:00 1970 PST
+  715 | Fri Jan 16 00:00:00 1970 PST
+  725 | Mon Jan 26 00:00:00 1970 PST
+  735 | Thu Feb 05 00:00:00 1970 PST
+  745 | Sun Feb 15 00:00:00 1970 PST
+  755 | Wed Feb 25 00:00:00 1970 PST
+  765 | Sat Mar 07 00:00:00 1970 PST
+  775 | Tue Mar 17 00:00:00 1970 PST
+  785 | Fri Mar 27 00:00:00 1970 PST
+  795 | Mon Apr 06 00:00:00 1970 PST
+  805 | Tue Jan 06 00:00:00 1970 PST
+  815 | Fri Jan 16 00:00:00 1970 PST
+  825 | Mon Jan 26 00:00:00 1970 PST
+  835 | Thu Feb 05 00:00:00 1970 PST
+  845 | Sun Feb 15 00:00:00 1970 PST
+  855 | Wed Feb 25 00:00:00 1970 PST
+  865 | Sat Mar 07 00:00:00 1970 PST
+  875 | Tue Mar 17 00:00:00 1970 PST
+  885 | Fri Mar 27 00:00:00 1970 PST
+  895 | Mon Apr 06 00:00:00 1970 PST
+  905 | Tue Jan 06 00:00:00 1970 PST
+  915 | Fri Jan 16 00:00:00 1970 PST
+  925 | Mon Jan 26 00:00:00 1970 PST
+  935 | Thu Feb 05 00:00:00 1970 PST
+  945 | Sun Feb 15 00:00:00 1970 PST
+  955 | Wed Feb 25 00:00:00 1970 PST
+  965 | Sat Mar 07 00:00:00 1970 PST
+  975 | Tue Mar 17 00:00:00 1970 PST
+  985 | Fri Mar 27 00:00:00 1970 PST
+  995 | Mon Apr 06 00:00:00 1970 PST
+ 1005 | 
+ 1015 | 
+ 1105 | 
 (103 rows)
 
 EXPLAIN (verbose, costs off)
@@ -1097,7 +1109,7 @@ DELETE FROM ft2 USING ft1 WHERE ft1.c1 = ft2.c2 AND ft1.c1 % 10 = 2;
          Hash Cond: (ft2.c2 = ft1.c1)
          ->  Foreign Scan on public.ft2
                Output: ft2.ctid, ft2.c2
-               Remote SQL: SELECT NULL, c2, NULL, NULL, NULL, NULL, NULL, NULL, ctid FROM "S 1"."T 1" FOR UPDATE
+               Remote SQL: SELECT c2, ctid FROM "S 1"."T 1" FOR UPDATE
          ->  Hash
                Output: ft1.*, ft1.c1
                ->  Foreign Scan on public.ft1
index 982a8d9a61fac86ef983dcb1f6ea382a0dc88991..687b87b8604bb03efc22c7ed5a79db5d9f3e97a6 100644 (file)
@@ -84,9 +84,10 @@ typedef struct PgFdwRelationInfo
  * Indexes of FDW-private information stored in fdw_private lists.
  *
  * We store various information in ForeignScan.fdw_private to pass it from
- * planner to executor.  Currently there is just:
+ * planner to executor.  Currently we store:
  *
  * 1) SELECT statement text to be sent to the remote server
+ * 2) Integer list of attribute numbers retrieved by the SELECT
  *
  * These items are indexed with the enum FdwScanPrivateIndex, so an item
  * can be fetched with list_nth().     For example, to get the SELECT statement:
@@ -95,7 +96,9 @@ typedef struct PgFdwRelationInfo
 enum FdwScanPrivateIndex
 {
        /* SQL statement to execute remotely (as a String node) */
-       FdwScanPrivateSelectSql
+       FdwScanPrivateSelectSql,
+       /* Integer list of attribute numbers retrieved by the SELECT */
+       FdwScanPrivateRetrievedAttrs
 };
 
 /*
@@ -106,6 +109,7 @@ enum FdwScanPrivateIndex
  * 2) Integer list of target attribute numbers for INSERT/UPDATE
  *       (NIL for a DELETE)
  * 3) Boolean flag showing if there's a RETURNING clause
+ * 4) Integer list of attribute numbers retrieved by RETURNING, if any
  */
 enum FdwModifyPrivateIndex
 {
@@ -114,7 +118,9 @@ enum FdwModifyPrivateIndex
        /* Integer list of target attribute numbers for INSERT/UPDATE */
        FdwModifyPrivateTargetAttnums,
        /* has-returning flag (as an integer Value node) */
-       FdwModifyPrivateHasReturning
+       FdwModifyPrivateHasReturning,
+       /* Integer list of attribute numbers retrieved by RETURNING */
+       FdwModifyPrivateRetrievedAttrs
 };
 
 /*
@@ -125,7 +131,9 @@ typedef struct PgFdwScanState
        Relation        rel;                    /* relcache entry for the foreign table */
        AttInMetadata *attinmeta;       /* attribute datatype conversion metadata */
 
-       List       *fdw_private;        /* FDW-private information from planner */
+       /* extracted fdw_private data */
+       char       *query;                      /* text of SELECT command */
+       List       *retrieved_attrs; /* list of retrieved attribute numbers */
 
        /* for remote query execution */
        PGconn     *conn;                       /* connection for the scan */
@@ -166,6 +174,7 @@ typedef struct PgFdwModifyState
        char       *query;                      /* text of INSERT/UPDATE/DELETE command */
        List       *target_attrs;       /* list of target attribute numbers */
        bool            has_returning;  /* is there a RETURNING clause? */
+       List       *retrieved_attrs; /* attr numbers retrieved by RETURNING */
 
        /* info about parameters for prepared statement */
        AttrNumber      ctidAttno;              /* attnum of input resjunk ctid column */
@@ -183,6 +192,7 @@ typedef struct PgFdwAnalyzeState
 {
        Relation        rel;                    /* relcache entry for the foreign table */
        AttInMetadata *attinmeta;       /* attribute datatype conversion metadata */
+       List       *retrieved_attrs; /* attr numbers retrieved by query */
 
        /* collected sample rows */
        HeapTuple  *rows;                       /* array of size targrows */
@@ -314,6 +324,7 @@ static HeapTuple make_tuple_from_result_row(PGresult *res,
                                                   int row,
                                                   Relation rel,
                                                   AttInMetadata *attinmeta,
+                                                  List *retrieved_attrs,
                                                   MemoryContext temp_context);
 static void conversion_error_callback(void *arg);
 
@@ -728,6 +739,7 @@ postgresGetForeignPlan(PlannerInfo *root,
        List       *remote_conds = NIL;
        List       *local_exprs = NIL;
        List       *params_list = NIL;
+       List       *retrieved_attrs;
        StringInfoData sql;
        ListCell   *lc;
 
@@ -777,7 +789,8 @@ postgresGetForeignPlan(PlannerInfo *root,
         * expressions to be sent as parameters.
         */
        initStringInfo(&sql);
-       deparseSelectSql(&sql, root, baserel, fpinfo->attrs_used);
+       deparseSelectSql(&sql, root, baserel, fpinfo->attrs_used,
+                                        &retrieved_attrs);
        if (remote_conds)
                appendWhereClause(&sql, root, baserel, remote_conds,
                                                  true, &params_list);
@@ -829,7 +842,8 @@ postgresGetForeignPlan(PlannerInfo *root,
         * Build the fdw_private list that will be available to the executor.
         * Items in the list must match enum FdwScanPrivateIndex, above.
         */
-       fdw_private = list_make1(makeString(sql.data));
+       fdw_private = list_make2(makeString(sql.data),
+                                                        retrieved_attrs);
 
        /*
         * Create the ForeignScan node from target list, local filtering
@@ -901,7 +915,10 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags)
        fsstate->cursor_exists = false;
 
        /* Get private info created by planner functions. */
-       fsstate->fdw_private = fsplan->fdw_private;
+       fsstate->query = strVal(list_nth(fsplan->fdw_private,
+                                                                        FdwScanPrivateSelectSql));
+       fsstate->retrieved_attrs = (List *) list_nth(fsplan->fdw_private,
+                                                                                                FdwScanPrivateRetrievedAttrs);
 
        /* Create contexts for batches of tuples and per-tuple temp workspace. */
        fsstate->batch_cxt = AllocSetContextCreate(estate->es_query_cxt,
@@ -915,7 +932,7 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags)
                                                                                          ALLOCSET_SMALL_INITSIZE,
                                                                                          ALLOCSET_SMALL_MAXSIZE);
 
-       /* Get info we'll need for data conversion. */
+       /* Get info we'll need for input data conversion. */
        fsstate->attinmeta = TupleDescGetAttInMetadata(RelationGetDescr(fsstate->rel));
 
        /* Prepare for output conversion of parameters used in remote query. */
@@ -1138,6 +1155,7 @@ postgresPlanForeignModify(PlannerInfo *root,
        StringInfoData sql;
        List       *targetAttrs = NIL;
        List       *returningList = NIL;
+       List       *retrieved_attrs = NIL;
 
        initStringInfo(&sql);
 
@@ -1194,15 +1212,18 @@ postgresPlanForeignModify(PlannerInfo *root,
        {
                case CMD_INSERT:
                        deparseInsertSql(&sql, root, resultRelation, rel,
-                                                        targetAttrs, returningList);
+                                                        targetAttrs, returningList,
+                                                        &retrieved_attrs);
                        break;
                case CMD_UPDATE:
                        deparseUpdateSql(&sql, root, resultRelation, rel,
-                                                        targetAttrs, returningList);
+                                                        targetAttrs, returningList,
+                                                        &retrieved_attrs);
                        break;
                case CMD_DELETE:
                        deparseDeleteSql(&sql, root, resultRelation, rel,
-                                                        returningList);
+                                                        returningList,
+                                                        &retrieved_attrs);
                        break;
                default:
                        elog(ERROR, "unexpected operation: %d", (int) operation);
@@ -1215,9 +1236,10 @@ postgresPlanForeignModify(PlannerInfo *root,
         * Build the fdw_private list that will be available to the executor.
         * Items in the list must match enum FdwModifyPrivateIndex, above.
         */
-       return list_make3(makeString(sql.data),
+       return list_make4(makeString(sql.data),
                                          targetAttrs,
-                                         makeInteger((returningList != NIL)));
+                                         makeInteger((returningList != NIL)),
+                                         retrieved_attrs);
 }
 
 /*
@@ -1279,6 +1301,8 @@ postgresBeginForeignModify(ModifyTableState *mtstate,
                                                                                          FdwModifyPrivateTargetAttnums);
        fmstate->has_returning = intVal(list_nth(fdw_private,
                                                                                         FdwModifyPrivateHasReturning));
+       fmstate->retrieved_attrs = (List *) list_nth(fdw_private,
+                                                                                                FdwModifyPrivateRetrievedAttrs);
 
        /* Create context for per-tuple temp workspace. */
        fmstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt,
@@ -1641,6 +1665,7 @@ estimate_path_cost_size(PlannerInfo *root,
        if (fpinfo->use_remote_estimate)
        {
                StringInfoData sql;
+               List       *retrieved_attrs;
                PGconn     *conn;
 
                /*
@@ -1650,7 +1675,8 @@ estimate_path_cost_size(PlannerInfo *root,
                 */
                initStringInfo(&sql);
                appendStringInfoString(&sql, "EXPLAIN ");
-               deparseSelectSql(&sql, root, baserel, fpinfo->attrs_used);
+               deparseSelectSql(&sql, root, baserel, fpinfo->attrs_used,
+                                                &retrieved_attrs);
                if (fpinfo->remote_conds)
                        appendWhereClause(&sql, root, baserel, fpinfo->remote_conds,
                                                          true, NULL);
@@ -1819,7 +1845,6 @@ create_cursor(ForeignScanState *node)
        int                     numParams = fsstate->numParams;
        const char **values = fsstate->param_values;
        PGconn     *conn = fsstate->conn;
-       char       *sql;
        StringInfoData buf;
        PGresult   *res;
 
@@ -1867,10 +1892,9 @@ create_cursor(ForeignScanState *node)
        }
 
        /* Construct the DECLARE CURSOR command */
-       sql = strVal(list_nth(fsstate->fdw_private, FdwScanPrivateSelectSql));
        initStringInfo(&buf);
        appendStringInfo(&buf, "DECLARE c%u CURSOR FOR\n%s",
-                                        fsstate->cursor_number, sql);
+                                        fsstate->cursor_number, fsstate->query);
 
        /*
         * Notice that we pass NULL for paramTypes, thus forcing the remote server
@@ -1885,7 +1909,7 @@ create_cursor(ForeignScanState *node)
        res = PQexecParams(conn, buf.data, numParams, NULL, values,
                                           NULL, NULL, 0);
        if (PQresultStatus(res) != PGRES_COMMAND_OK)
-               pgfdw_report_error(ERROR, res, true, sql);
+               pgfdw_report_error(ERROR, res, true, fsstate->query);
        PQclear(res);
 
        /* Mark the cursor as created, and show no tuples have been retrieved */
@@ -1936,9 +1960,7 @@ fetch_more_data(ForeignScanState *node)
                res = PQexec(conn, sql);
                /* On error, report the original query, not the FETCH. */
                if (PQresultStatus(res) != PGRES_TUPLES_OK)
-                       pgfdw_report_error(ERROR, res, false,
-                                                          strVal(list_nth(fsstate->fdw_private,
-                                                                                          FdwScanPrivateSelectSql)));
+                       pgfdw_report_error(ERROR, res, false, fsstate->query);
 
                /* Convert the data into HeapTuples */
                numrows = PQntuples(res);
@@ -1952,6 +1974,7 @@ fetch_more_data(ForeignScanState *node)
                                make_tuple_from_result_row(res, i,
                                                                                   fsstate->rel,
                                                                                   fsstate->attinmeta,
+                                                                                  fsstate->retrieved_attrs,
                                                                                   fsstate->temp_cxt);
                }
 
@@ -2170,6 +2193,7 @@ store_returning_result(PgFdwModifyState *fmstate,
                newtup = make_tuple_from_result_row(res, 0,
                                                                                        fmstate->rel,
                                                                                        fmstate->attinmeta,
+                                                                                       fmstate->retrieved_attrs,
                                                                                        fmstate->temp_cxt);
                /* tuple will be deleted when it is cleared from the slot */
                ExecStoreTuple(newtup, slot, InvalidBuffer, true);
@@ -2316,7 +2340,7 @@ postgresAcquireSampleRowsFunc(Relation relation, int elevel,
        cursor_number = GetCursorNumber(conn);
        initStringInfo(&sql);
        appendStringInfo(&sql, "DECLARE c%u CURSOR FOR ", cursor_number);
-       deparseAnalyzeSql(&sql, relation);
+       deparseAnalyzeSql(&sql, relation, &astate.retrieved_attrs);
 
        /* In what follows, do not risk leaking any PGresults. */
        PG_TRY();
@@ -2461,6 +2485,7 @@ analyze_row_processor(PGresult *res, int row, PgFdwAnalyzeState *astate)
                astate->rows[pos] = make_tuple_from_result_row(res, row,
                                                                                                           astate->rel,
                                                                                                           astate->attinmeta,
+                                                                                                          astate->retrieved_attrs,
                                                                                                           astate->temp_cxt);
 
                MemoryContextSwitchTo(oldcontext);
@@ -2471,26 +2496,27 @@ analyze_row_processor(PGresult *res, int row, PgFdwAnalyzeState *astate)
  * Create a tuple from the specified row of the PGresult.
  *
  * rel is the local representation of the foreign table, attinmeta is
- * conversion data for the rel's tupdesc, and temp_context is a working
- * context that can be reset after each tuple.
+ * conversion data for the rel's tupdesc, and retrieved_attrs is an
+ * integer list of the table column numbers present in the PGresult.
+ * temp_context is a working context that can be reset after each tuple.
  */
 static HeapTuple
 make_tuple_from_result_row(PGresult *res,
                                                   int row,
                                                   Relation rel,
                                                   AttInMetadata *attinmeta,
+                                                  List *retrieved_attrs,
                                                   MemoryContext temp_context)
 {
        HeapTuple       tuple;
        TupleDesc       tupdesc = RelationGetDescr(rel);
-       Form_pg_attribute *attrs = tupdesc->attrs;
        Datum      *values;
        bool       *nulls;
        ItemPointer ctid = NULL;
        ConversionLocation errpos;
        ErrorContextCallback errcallback;
        MemoryContext oldcontext;
-       int                     i;
+       ListCell   *lc;
        int                     j;
 
        Assert(row < PQntuples(res));
@@ -2502,8 +2528,10 @@ make_tuple_from_result_row(PGresult *res,
         */
        oldcontext = MemoryContextSwitchTo(temp_context);
 
-       values = (Datum *) palloc(tupdesc->natts * sizeof(Datum));
+       values = (Datum *) palloc0(tupdesc->natts * sizeof(Datum));
        nulls = (bool *) palloc(tupdesc->natts * sizeof(bool));
+       /* Initialize to nulls for any columns not present in result */
+       memset(nulls, true, tupdesc->natts * sizeof(bool));
 
        /*
         * Set up and install callback to report where conversion error occurs.
@@ -2517,63 +2545,56 @@ make_tuple_from_result_row(PGresult *res,
 
        /*
         * i indexes columns in the relation, j indexes columns in the PGresult.
-        * We assume dropped columns are not represented in the PGresult.
         */
-       for (i = 0, j = 0; i < tupdesc->natts; i++)
+       j = 0;
+       foreach(lc, retrieved_attrs)
        {
+               int                     i = lfirst_int(lc);
                char       *valstr;
 
-               /* skip dropped columns. */
-               if (attrs[i]->attisdropped)
-               {
-                       values[i] = (Datum) 0;
-                       nulls[i] = true;
-                       continue;
-               }
-
-               /* convert value to internal representation */
+               /* fetch next column's textual value */
                if (PQgetisnull(res, row, j))
-               {
                        valstr = NULL;
-                       nulls[i] = true;
-               }
                else
-               {
                        valstr = PQgetvalue(res, row, j);
-                       nulls[i] = false;
-               }
-
-               /* Note: apply the input function even to nulls, to support domains */
-               errpos.cur_attno = i + 1;
-               values[i] = InputFunctionCall(&attinmeta->attinfuncs[i],
-                                                                         valstr,
-                                                                         attinmeta->attioparams[i],
-                                                                         attinmeta->atttypmods[i]);
-               errpos.cur_attno = 0;
 
-               j++;
-       }
+               /* convert value to internal representation */
+               if (i > 0)
+               {
+                       /* ordinary column */
+                       Assert(i <= tupdesc->natts);
+                       nulls[i - 1] = (valstr == NULL);
+                       /* Apply the input function even to nulls, to support domains */
+                       errpos.cur_attno = i;
+                       values[i - 1] = InputFunctionCall(&attinmeta->attinfuncs[i - 1],
+                                                                                         valstr,
+                                                                                         attinmeta->attioparams[i - 1],
+                                                                                         attinmeta->atttypmods[i - 1]);
+                       errpos.cur_attno = 0;
+               }
+               else if (i == SelfItemPointerAttributeNumber)
+               {
+                       /* ctid --- note we ignore any other system column in result */
+                       if (valstr != NULL)
+                       {
+                               Datum           datum;
 
-       /*
-        * Convert ctid if present.  XXX we could stand to have a cleaner way of
-        * detecting whether ctid is included in the result.
-        */
-       if (j < PQnfields(res))
-       {
-               char       *valstr;
-               Datum           datum;
+                               datum = DirectFunctionCall1(tidin, CStringGetDatum(valstr));
+                               ctid = (ItemPointer) DatumGetPointer(datum);
+                       }
+               }
 
-               valstr = PQgetvalue(res, row, j);
-               datum = DirectFunctionCall1(tidin, CStringGetDatum(valstr));
-               ctid = (ItemPointer) DatumGetPointer(datum);
                j++;
        }
 
        /* Uninstall error context callback. */
        error_context_stack = errcallback.previous;
 
-       /* check result and tuple descriptor have the same number of columns */
-       if (j != PQnfields(res))
+       /*
+        * Check we got the expected number of columns.  Note: j == 0 and
+        * PQnfields == 1 is expected, since deparse emits a NULL if no columns.
+        */
+       if (j > 0 && j != PQnfields(res))
                elog(ERROR, "remote query result does not match the foreign table");
 
        /*
index 78a57ea0575a7b64c98e1efc6543d92da9d7daaf..2939d2b61da5ee417ee1c26ebc3c1c7aa90e8853 100644 (file)
@@ -49,7 +49,8 @@ extern bool is_foreign_expr(PlannerInfo *root,
 extern void deparseSelectSql(StringInfo buf,
                                 PlannerInfo *root,
                                 RelOptInfo *baserel,
-                                Bitmapset *attrs_used);
+                                Bitmapset *attrs_used,
+                                List **retrieved_attrs);
 extern void appendWhereClause(StringInfo buf,
                                  PlannerInfo *root,
                                  RelOptInfo *baserel,
@@ -58,14 +59,18 @@ extern void appendWhereClause(StringInfo buf,
                                  List **params);
 extern void deparseInsertSql(StringInfo buf, PlannerInfo *root,
                                 Index rtindex, Relation rel,
-                                List *targetAttrs, List *returningList);
+                                List *targetAttrs, List *returningList,
+                                List **retrieved_attrs);
 extern void deparseUpdateSql(StringInfo buf, PlannerInfo *root,
                                 Index rtindex, Relation rel,
-                                List *targetAttrs, List *returningList);
+                                List *targetAttrs, List *returningList,
+                                List **retrieved_attrs);
 extern void deparseDeleteSql(StringInfo buf, PlannerInfo *root,
                                 Index rtindex, Relation rel,
-                                List *returningList);
+                                List *returningList,
+                                List **retrieved_attrs);
 extern void deparseAnalyzeSizeSql(StringInfo buf, Relation rel);
-extern void deparseAnalyzeSql(StringInfo buf, Relation rel);
+extern void deparseAnalyzeSql(StringInfo buf, Relation rel,
+                                                         List **retrieved_attrs);
 
 #endif   /* POSTGRES_FDW_H */
index 1d5989e8dd0319346899181c642100d6e5922412..670d769a80d76deea062a3f66c847a5591b177dc 100644 (file)
@@ -311,7 +311,9 @@ UPDATE ft2 SET c2 = ft2.c2 + 500, c3 = ft2.c3 || '_update9', c7 = DEFAULT
   FROM ft1 WHERE ft1.c1 = ft2.c2 AND ft1.c1 % 10 = 9;
 UPDATE ft2 SET c2 = ft2.c2 + 500, c3 = ft2.c3 || '_update9', c7 = DEFAULT
   FROM ft1 WHERE ft1.c1 = ft2.c2 AND ft1.c1 % 10 = 9;
-DELETE FROM ft2 WHERE c1 % 10 = 5 RETURNING *;
+EXPLAIN (verbose, costs off)
+  DELETE FROM ft2 WHERE c1 % 10 = 5 RETURNING c1, c4;
+DELETE FROM ft2 WHERE c1 % 10 = 5 RETURNING c1, c4;
 EXPLAIN (verbose, costs off)
 DELETE FROM ft2 USING ft1 WHERE ft1.c1 = ft2.c2 AND ft1.c1 % 10 = 2;
 DELETE FROM ft2 USING ft1 WHERE ft1.c1 = ft2.c2 AND ft1.c1 % 10 = 2;