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);
* 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;
* 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
* 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);
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);
+ }
}
/*
first = false;
appendStringInfoString(buf, "ctid");
+
+ *retrieved_attrs = lappend_int(*retrieved_attrs,
+ SelfItemPointerAttributeNumber);
}
/* Don't generate bad syntax if no undropped columns */
/*
* 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;
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;
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;
}
/*
static void
deparseReturningList(StringInfo buf, PlannerInfo *root,
Index rtindex, Relation rel,
- List *returningList)
+ List *returningList,
+ List **retrieved_attrs)
{
Bitmapset *attrs_used;
&attrs_used);
appendStringInfoString(buf, " RETURNING ");
- deparseTargetList(buf, root, rtindex, rel, attrs_used);
+ deparseTargetList(buf, root, rtindex, rel, attrs_used,
+ retrieved_attrs);
}
/*
/*
* 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);
ListCell *lc;
bool first = true;
+ *retrieved_attrs = NIL;
+
appendStringInfoString(buf, "SELECT ");
for (i = 0; i < tupdesc->natts; i++)
{
}
appendStringInfoString(buf, quote_identifier(colname));
+
+ *retrieved_attrs = lappend_int(*retrieved_attrs, i + 1);
}
/* Don't generate bad syntax for zero-column relation. */
-- 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);
-- 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
-> 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);
-- 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
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);
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;
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
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)
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
* 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:
enum FdwScanPrivateIndex
{
/* SQL statement to execute remotely (as a String node) */
- FdwScanPrivateSelectSql
+ FdwScanPrivateSelectSql,
+ /* Integer list of attribute numbers retrieved by the SELECT */
+ FdwScanPrivateRetrievedAttrs
};
/*
* 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
{
/* 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
};
/*
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 */
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 */
{
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 */
int row,
Relation rel,
AttInMetadata *attinmeta,
+ List *retrieved_attrs,
MemoryContext temp_context);
static void conversion_error_callback(void *arg);
List *remote_conds = NIL;
List *local_exprs = NIL;
List *params_list = NIL;
+ List *retrieved_attrs;
StringInfoData sql;
ListCell *lc;
* 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, ¶ms_list);
* 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
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,
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. */
StringInfoData sql;
List *targetAttrs = NIL;
List *returningList = NIL;
+ List *retrieved_attrs = NIL;
initStringInfo(&sql);
{
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);
* 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);
}
/*
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,
if (fpinfo->use_remote_estimate)
{
StringInfoData sql;
+ List *retrieved_attrs;
PGconn *conn;
/*
*/
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);
int numParams = fsstate->numParams;
const char **values = fsstate->param_values;
PGconn *conn = fsstate->conn;
- char *sql;
StringInfoData buf;
PGresult *res;
}
/* 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
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 */
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);
make_tuple_from_result_row(res, i,
fsstate->rel,
fsstate->attinmeta,
+ fsstate->retrieved_attrs,
fsstate->temp_cxt);
}
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);
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();
astate->rows[pos] = make_tuple_from_result_row(res, row,
astate->rel,
astate->attinmeta,
+ astate->retrieved_attrs,
astate->temp_cxt);
MemoryContextSwitchTo(oldcontext);
* 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));
*/
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.
/*
* 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");
/*
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,
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 */
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;