pgsql-v9.3-writable-fdw-poc.v6.patch

application/octet-stream

Filename: pgsql-v9.3-writable-fdw-poc.v6.patch
Type: application/octet-stream
Part: 0
Message: Re: [v9.3] writable foreign tables

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: unified
Series: patch v9
File+
contrib/postgres_fdw/connection.c 56 14
contrib/postgres_fdw/connection.h 6 2
contrib/postgres_fdw/deparse.c 124 2
contrib/postgres_fdw/expected/postgres_fdw.out 1055 0
contrib/postgres_fdw/postgres_fdw.c 395 13
contrib/postgres_fdw/postgres_fdw.h 5 1
contrib/postgres_fdw/sql/postgres_fdw.sql 13 0
doc/src/sgml/fdwhandler.sgml 151 1
src/backend/executor/execMain.c 30 4
src/backend/executor/nodeForeignscan.c 135 1
src/backend/executor/nodeModifyTable.c 126 24
src/backend/foreign/foreign.c 68 0
src/backend/nodes/copyfuncs.c 2 0
src/backend/nodes/outfuncs.c 2 0
src/backend/optimizer/plan/createplan.c 47 1
src/backend/optimizer/plan/initsplan.c 5 5
src/backend/optimizer/plan/planmain.c 4 1
src/backend/optimizer/plan/planner.c 5 3
src/backend/optimizer/prep/prepunion.c 2 1
src/backend/optimizer/util/plancat.c 26 1
src/backend/optimizer/util/relnode.c 4 3
src/backend/rewrite/rewriteHandler.c 45 10
src/include/foreign/fdwapi.h 31 0
src/include/foreign/foreign.h 6 0
src/include/nodes/execnodes.h 10 3
src/include/nodes/plannodes.h 2 0
src/include/optimizer/pathnode.h 1 1
src/include/optimizer/plancat.h 1 1
src/include/optimizer/planmain.h 4 2
 contrib/postgres_fdw/connection.c              |   70 +-
 contrib/postgres_fdw/connection.h              |    8 +-
 contrib/postgres_fdw/deparse.c                 |  126 ++-
 contrib/postgres_fdw/expected/postgres_fdw.out | 1055 ++++++++++++++++++++++++
 contrib/postgres_fdw/postgres_fdw.c            |  408 ++++++++-
 contrib/postgres_fdw/postgres_fdw.h            |    6 +-
 contrib/postgres_fdw/sql/postgres_fdw.sql      |   13 +
 doc/src/sgml/fdwhandler.sgml                   |  152 +++-
 src/backend/executor/execMain.c                |   34 +-
 src/backend/executor/nodeForeignscan.c         |  136 ++-
 src/backend/executor/nodeModifyTable.c         |  150 +++-
 src/backend/foreign/foreign.c                  |   68 ++
 src/backend/nodes/copyfuncs.c                  |    2 +
 src/backend/nodes/outfuncs.c                   |    2 +
 src/backend/optimizer/plan/createplan.c        |   48 +-
 src/backend/optimizer/plan/initsplan.c         |   10 +-
 src/backend/optimizer/plan/planmain.c          |    5 +-
 src/backend/optimizer/plan/planner.c           |    8 +-
 src/backend/optimizer/prep/prepunion.c         |    3 +-
 src/backend/optimizer/util/plancat.c           |   27 +-
 src/backend/optimizer/util/relnode.c           |    7 +-
 src/backend/rewrite/rewriteHandler.c           |   55 +-
 src/include/foreign/fdwapi.h                   |   31 +
 src/include/foreign/foreign.h                  |    6 +
 src/include/nodes/execnodes.h                  |   13 +-
 src/include/nodes/plannodes.h                  |    2 +
 src/include/optimizer/pathnode.h               |    2 +-
 src/include/optimizer/plancat.h                |    2 +-
 src/include/optimizer/planmain.h               |    6 +-
 29 files changed, 2361 insertions(+), 94 deletions(-)

diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index eab8b87..9ca7fbf 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -43,7 +43,7 @@ typedef struct ConnCacheEntry
 	Oid				serverid;	/* oid of foreign server */
 	Oid				userid;		/* oid of local user */
 
-	bool			use_tx;		/* true when using remote transaction */
+	int				conntx;		/* one of PGSQL_FDW_CONNTX_* */
 	int				refs;		/* reference counter */
 	PGconn		   *conn;		/* foreign server connection */
 } ConnCacheEntry;
@@ -65,6 +65,8 @@ cleanup_connection(ResourceReleasePhase phase,
 static PGconn *connect_pg_server(ForeignServer *server, UserMapping *user);
 static void begin_remote_tx(PGconn *conn);
 static void abort_remote_tx(PGconn *conn);
+static void commit_remote_tx(PGconn *conn);
+static void deallocate_remote_prepare(PGconn *conn);
 
 /*
  * Get a PGconn which can be used to execute foreign query on the remote
@@ -80,7 +82,7 @@ static void abort_remote_tx(PGconn *conn);
  * FDW object to invalidate already established connections.
  */
 PGconn *
-GetConnection(ForeignServer *server, UserMapping *user, bool use_tx)
+GetConnection(ForeignServer *server, UserMapping *user, int conntx)
 {
 	bool			found;
 	ConnCacheEntry *entry;
@@ -126,7 +128,7 @@ GetConnection(ForeignServer *server, UserMapping *user, bool use_tx)
 	entry = hash_search(ConnectionHash, &key, HASH_ENTER, &found);
 	if (!found)
 	{
-		entry->use_tx = false;
+		entry->conntx = PGSQL_FDW_CONNTX_NONE;
 		entry->refs = 0;
 		entry->conn = NULL;
 	}
@@ -162,7 +164,7 @@ GetConnection(ForeignServer *server, UserMapping *user, bool use_tx)
 		{
 			/* Clear connection cache entry on error case. */
 			PQfinish(entry->conn);
-			entry->use_tx = false;
+			entry->conntx = PGSQL_FDW_CONNTX_NONE;
 			entry->refs = 0;
 			entry->conn = NULL;
 			PG_RE_THROW();
@@ -182,10 +184,11 @@ GetConnection(ForeignServer *server, UserMapping *user, bool use_tx)
 	 * are in.  We need to remember whether this connection uses remote
 	 * transaction to abort it when this connection is released completely.
 	 */
-	if (use_tx && !entry->use_tx)
+	if (conntx > entry->conntx)
 	{
-		begin_remote_tx(entry->conn);
-		entry->use_tx = use_tx;
+		if (entry->conntx == PGSQL_FDW_CONNTX_NONE)
+			begin_remote_tx(entry->conn);
+		entry->conntx = conntx;
 	}
 
 	return entry->conn;
@@ -355,12 +358,45 @@ abort_remote_tx(PGconn *conn)
 	PQclear(res);
 }
 
+static void
+commit_remote_tx(PGconn *conn)
+{
+	PGresult	   *res;
+
+	elog(DEBUG3, "committing remote transaction");
+
+	res = PQexec(conn, "COMMIT TRANSACTION");
+	if (PQresultStatus(res) != PGRES_COMMAND_OK)
+	{
+		PQclear(res);
+		elog(ERROR, "could not commit transaction: %s", PQerrorMessage(conn));
+	}
+	PQclear(res);
+}
+
+static void
+deallocate_remote_prepare(PGconn *conn)
+{
+	PGresult	   *res;
+
+	elog(DEBUG3, "deallocating remote prepares");
+
+	res = PQexec(conn, "DEALLOCATE PREPARE ALL");
+	if (PQresultStatus(res) != PGRES_COMMAND_OK)
+	{
+		PQclear(res);
+		elog(ERROR, "could not deallocate prepared statement: %s",
+			 PQerrorMessage(conn));
+	}
+	PQclear(res);
+}
+
 /*
  * Mark the connection as "unused", and close it if the caller was the last
  * user of the connection.
  */
 void
-ReleaseConnection(PGconn *conn)
+ReleaseConnection(PGconn *conn, bool is_abort)
 {
 	HASH_SEQ_STATUS		scan;
 	ConnCacheEntry	   *entry;
@@ -412,7 +448,7 @@ ReleaseConnection(PGconn *conn)
 			 PQtransactionStatus(conn) == PQTRANS_INERROR ? "INERROR" :
 			 "UNKNOWN");
 		PQfinish(conn);
-		entry->use_tx = false;
+		entry->conntx = PGSQL_FDW_CONNTX_NONE;
 		entry->refs = 0;
 		entry->conn = NULL;
 		return;
@@ -430,10 +466,16 @@ ReleaseConnection(PGconn *conn)
 	 * If this connection uses remote transaction and there is no user other
 	 * than the caller, abort the remote transaction and forget about it.
 	 */
-	if (entry->use_tx && entry->refs == 0)
+	if (entry->conntx > PGSQL_FDW_CONNTX_NONE && entry->refs == 0)
 	{
-		abort_remote_tx(conn);
-		entry->use_tx = false;
+		if (entry->conntx > PGSQL_FDW_CONNTX_READ_ONLY)
+			deallocate_remote_prepare(conn);
+		if (is_abort || entry->conntx == PGSQL_FDW_CONNTX_READ_ONLY)
+			abort_remote_tx(conn);
+		else
+			commit_remote_tx(conn);
+
+		entry->conntx = PGSQL_FDW_CONNTX_NONE;
 	}
 }
 
@@ -485,7 +527,7 @@ cleanup_connection(ResourceReleasePhase phase,
 		elog(DEBUG3, "discard postgres_fdw connection %p due to resowner cleanup",
 			 entry->conn);
 		PQfinish(entry->conn);
-		entry->use_tx = false;
+		entry->conntx = PGSQL_FDW_CONNTX_NONE;
 		entry->refs = 0;
 		entry->conn = NULL;
 	}
@@ -597,7 +639,7 @@ postgres_fdw_disconnect(PG_FUNCTION_ARGS)
 
 	/* Discard cached connection, and clear reference counter. */
 	PQfinish(entry->conn);
-	entry->use_tx = false;
+	entry->conntx = PGSQL_FDW_CONNTX_NONE;
 	entry->refs = 0;
 	entry->conn = NULL;
 
diff --git a/contrib/postgres_fdw/connection.h b/contrib/postgres_fdw/connection.h
index 4c9d850..f97cc8a 100644
--- a/contrib/postgres_fdw/connection.h
+++ b/contrib/postgres_fdw/connection.h
@@ -16,10 +16,14 @@
 #include "foreign/foreign.h"
 #include "libpq-fe.h"
 
+#define PGSQL_FDW_CONNTX_NONE			0
+#define PGSQL_FDW_CONNTX_READ_ONLY		1
+#define PGSQL_FDW_CONNTX_READ_WRITE		2
+
 /*
  * Connection management
  */
-PGconn *GetConnection(ForeignServer *server, UserMapping *user, bool use_tx);
-void ReleaseConnection(PGconn *conn);
+PGconn *GetConnection(ForeignServer *server, UserMapping *user, int conntx);
+void ReleaseConnection(PGconn *conn, bool is_abort);
 
 #endif /* CONNECTION_H */
diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index 69e6a3e..9e09429 100644
--- a/contrib/postgres_fdw/deparse.c
+++ b/contrib/postgres_fdw/deparse.c
@@ -12,6 +12,7 @@
  */
 #include "postgres.h"
 
+#include "access/heapam.h"
 #include "access/htup_details.h"
 #include "access/transam.h"
 #include "catalog/pg_class.h"
@@ -86,9 +87,11 @@ void
 deparseSimpleSql(StringInfo buf,
 				 PlannerInfo *root,
 				 RelOptInfo *baserel,
-				 List *local_conds)
+				 List *local_conds,
+				 AttrNumber anum_rowid)
 {
 	RangeTblEntry *rte;
+	Relation	rel;
 	ListCell   *lc;
 	StringInfoData	foreign_relname;
 	bool		first;
@@ -125,6 +128,24 @@ deparseSimpleSql(StringInfo buf,
 	}
 
 	/*
+	 * XXX - When this foreign table is target relation and RETURNING
+	 * clause reference some column, we have to mark these columns as
+	 * in-use. It is needed to support DELETE command, because INSERT
+	 * and UPDATE implicitly add references to all the regular columns
+	 * on baserel->reltargetlist.
+	 */
+	if (root->parse->resultRelation == baserel->relid &&
+		root->parse->returningList)
+	{
+		List   *attrs;
+
+		attrs = pull_var_clause((Node *) root->parse->returningList,
+								PVC_RECURSE_AGGREGATES,
+                                PVC_RECURSE_PLACEHOLDERS);
+		attr_used = list_union(attr_used, attrs);
+	}
+
+	/*
 	 * deparse SELECT clause
 	 *
 	 * List attributes which are in either target list or local restriction.
@@ -136,9 +157,10 @@ deparseSimpleSql(StringInfo buf,
 	 */
 	appendStringInfo(buf, "SELECT ");
 	rte = root->simple_rte_array[baserel->relid];
+	rel = heap_open(rte->relid, NoLock);
 	attr_used = list_union(attr_used, baserel->reltargetlist);
 	first = true;
-	for (attr = 1; attr <= baserel->max_attr; attr++)
+	for (attr = 1; attr <= RelationGetNumberOfAttributes(rel); attr++)
 	{
 		Var		   *var = NULL;
 		ListCell   *lc;
@@ -167,6 +189,10 @@ deparseSimpleSql(StringInfo buf,
 		else
 			appendStringInfo(buf, "NULL");
 	}
+	if (anum_rowid != InvalidAttrNumber)
+		appendStringInfo(buf, "%sctid", (first ? "" : ","));
+
+	heap_close(rel, NoLock);
 	appendStringInfoChar(buf, ' ');
 
 	/*
@@ -283,6 +309,102 @@ deparseAnalyzeSql(StringInfo buf, Relation rel)
 }
 
 /*
+ * deparse remote INSERT statement
+ */
+void
+deparseInsertSql(StringInfo buf, PlannerInfo *root, Index rtindex)
+{
+	RangeTblEntry  *rte = root->simple_rte_array[rtindex];
+	Relation		frel = heap_open(rte->relid, NoLock);
+	int				i, j, nattrs = RelationGetNumberOfAttributes(frel);
+	bool			is_first = true;
+
+	appendStringInfo(buf, "INSERT INTO ");
+	deparseRelation(buf, rte);
+	appendStringInfo(buf, "(");
+
+	for (i=0; i < nattrs; i++)
+	{
+		Form_pg_attribute attr = RelationGetDescr(frel)->attrs[i];
+		Var		var;
+
+		if (attr->attisdropped)
+			continue;
+
+		if (!is_first)
+			appendStringInfo(buf, ",");
+
+		var.varno = rtindex;
+		var.varattno = attr->attnum;
+		deparseVar(buf, &var, root);
+		is_first = false;
+	}
+	appendStringInfo(buf, ") VALUES (");
+
+	for (i=0, j=1; i < nattrs; i++)
+	{
+		Form_pg_attribute attr = RelationGetDescr(frel)->attrs[i];
+
+		if (attr->attisdropped)
+			continue;
+
+		appendStringInfo(buf, "%s$%d", (j == 1 ? "" : ","), j);
+		j++;
+	}
+	appendStringInfo(buf, ")");
+	heap_close(frel, NoLock);
+}
+
+/*
+ * deparse remote UPDATE statement
+ */
+void
+deparseUpdateSql(StringInfo buf, PlannerInfo *root, Index rtindex)
+{
+	RangeTblEntry  *rte = root->simple_rte_array[rtindex];
+	Relation		frel = heap_open(rte->relid, NoLock);
+	int				i, j, nattrs = RelationGetNumberOfAttributes(frel);
+	bool			is_first = true;
+
+	appendStringInfo(buf, "UPDATE ");
+	deparseRelation(buf, rte);
+	appendStringInfo(buf, " SET ");
+
+	for (i=0, j=2; i < nattrs; i++)
+	{
+		Form_pg_attribute attr = RelationGetDescr(frel)->attrs[i];
+		Var		var;
+
+		if (attr->attisdropped)
+			continue;
+
+		if (!is_first)
+			appendStringInfo(buf, ",");
+
+		var.varno = rtindex;
+		var.varattno = attr->attnum;
+		deparseVar(buf, &var, root);
+		appendStringInfo(buf, "=$%d", j++);
+		is_first = false;
+	}
+	appendStringInfo(buf, " WHERE ctid=$1");
+	heap_close(frel, NoLock);
+}
+
+/*
+ * deparse remote DELETE statement
+ */
+void
+deparseDeleteSql(StringInfo buf, PlannerInfo *root, Index rtindex)
+{
+	RangeTblEntry  *rte = root->simple_rte_array[rtindex];
+
+	appendStringInfo(buf, "DELETE FROM ");
+	deparseRelation(buf, rte);
+	appendStringInfo(buf, " WHERE ctid = $1");
+}
+
+/*
  * Deparse given expression into buf.  Actual string operation is delegated to
  * node-type-specific functions.
  *
diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index f81c727..db2c725 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -736,6 +736,1061 @@ SELECT srvname FROM postgres_fdw_connections;
 (0 rows)
 
 -- ===================================================================
+-- test for writable foreign table stuff (PoC stage now)
+-- ===================================================================
+INSERT INTO ft2 (c1,c2,c3) (SELECT c1+1000,c2+100, c3 || c3 FROM ft2 LIMIT 20);
+INSERT INTO ft2 (c1,c2,c3) VALUES (1101,201,'aaa'), (1102,202,'bbb'),(1103,203,'ccc') RETURNING *;
+  c1  | c2  | c3  | c4 | c5 | c6 | c7 | c8 
+------+-----+-----+----+----+----+----+----
+ 1101 | 201 | aaa |    |    |    |    | 
+ 1102 | 202 | bbb |    |    |    |    | 
+ 1103 | 203 | ccc |    |    |    |    | 
+(3 rows)
+
+INSERT INTO ft2 (c1,c2,c3) VALUES (1104,204,'ddd'), (1105,205,'eee');
+UPDATE ft2 SET c2 = c2 + 300, c3 = c3 || '_update3' WHERE c1 % 10 = 3;
+UPDATE ft2 SET c2 = c2 + 400, c3 = c3 || '_update7' WHERE c1 % 10 = 7 RETURNING *;
+  c1  | c2  |         c3         |              c4              |            c5            | c6 |     c7     | c8  
+------+-----+--------------------+------------------------------+--------------------------+----+------------+-----
+    7 | 407 | 00007_update7      | Thu Jan 08 00:00:00 1970 PST | Thu Jan 08 00:00:00 1970 | 7  | 7          | foo
+   17 | 407 | 00017_update7      | Sun Jan 18 00:00:00 1970 PST | Sun Jan 18 00:00:00 1970 | 7  | 7          | foo
+   27 | 407 | 00027_update7      | Wed Jan 28 00:00:00 1970 PST | Wed Jan 28 00:00:00 1970 | 7  | 7          | foo
+   37 | 407 | 00037_update7      | Sat Feb 07 00:00:00 1970 PST | Sat Feb 07 00:00:00 1970 | 7  | 7          | foo
+   47 | 407 | 00047_update7      | Tue Feb 17 00:00:00 1970 PST | Tue Feb 17 00:00:00 1970 | 7  | 7          | foo
+   57 | 407 | 00057_update7      | Fri Feb 27 00:00:00 1970 PST | Fri Feb 27 00:00:00 1970 | 7  | 7          | foo
+   67 | 407 | 00067_update7      | Mon Mar 09 00:00:00 1970 PST | Mon Mar 09 00:00:00 1970 | 7  | 7          | foo
+   77 | 407 | 00077_update7      | Thu Mar 19 00:00:00 1970 PST | Thu Mar 19 00:00:00 1970 | 7  | 7          | foo
+   87 | 407 | 00087_update7      | Sun Mar 29 00:00:00 1970 PST | Sun Mar 29 00:00:00 1970 | 7  | 7          | foo
+   97 | 407 | 00097_update7      | Wed Apr 08 00:00:00 1970 PST | Wed Apr 08 00:00:00 1970 | 7  | 7          | foo
+  107 | 407 | 00107_update7      | Thu Jan 08 00:00:00 1970 PST | Thu Jan 08 00:00:00 1970 | 7  | 7          | foo
+  117 | 407 | 00117_update7      | Sun Jan 18 00:00:00 1970 PST | Sun Jan 18 00:00:00 1970 | 7  | 7          | foo
+  127 | 407 | 00127_update7      | Wed Jan 28 00:00:00 1970 PST | Wed Jan 28 00:00:00 1970 | 7  | 7          | foo
+  137 | 407 | 00137_update7      | Sat Feb 07 00:00:00 1970 PST | Sat Feb 07 00:00:00 1970 | 7  | 7          | foo
+  147 | 407 | 00147_update7      | Tue Feb 17 00:00:00 1970 PST | Tue Feb 17 00:00:00 1970 | 7  | 7          | foo
+  157 | 407 | 00157_update7      | Fri Feb 27 00:00:00 1970 PST | Fri Feb 27 00:00:00 1970 | 7  | 7          | foo
+  167 | 407 | 00167_update7      | Mon Mar 09 00:00:00 1970 PST | Mon Mar 09 00:00:00 1970 | 7  | 7          | foo
+  177 | 407 | 00177_update7      | Thu Mar 19 00:00:00 1970 PST | Thu Mar 19 00:00:00 1970 | 7  | 7          | foo
+  187 | 407 | 00187_update7      | Sun Mar 29 00:00:00 1970 PST | Sun Mar 29 00:00:00 1970 | 7  | 7          | foo
+  197 | 407 | 00197_update7      | Wed Apr 08 00:00:00 1970 PST | Wed Apr 08 00:00:00 1970 | 7  | 7          | foo
+  207 | 407 | 00207_update7      | Thu Jan 08 00:00:00 1970 PST | Thu Jan 08 00:00:00 1970 | 7  | 7          | foo
+  217 | 407 | 00217_update7      | Sun Jan 18 00:00:00 1970 PST | Sun Jan 18 00:00:00 1970 | 7  | 7          | foo
+  227 | 407 | 00227_update7      | Wed Jan 28 00:00:00 1970 PST | Wed Jan 28 00:00:00 1970 | 7  | 7          | foo
+  237 | 407 | 00237_update7      | Sat Feb 07 00:00:00 1970 PST | Sat Feb 07 00:00:00 1970 | 7  | 7          | foo
+  247 | 407 | 00247_update7      | Tue Feb 17 00:00:00 1970 PST | Tue Feb 17 00:00:00 1970 | 7  | 7          | foo
+  257 | 407 | 00257_update7      | Fri Feb 27 00:00:00 1970 PST | Fri Feb 27 00:00:00 1970 | 7  | 7          | foo
+  267 | 407 | 00267_update7      | Mon Mar 09 00:00:00 1970 PST | Mon Mar 09 00:00:00 1970 | 7  | 7          | foo
+  277 | 407 | 00277_update7      | Thu Mar 19 00:00:00 1970 PST | Thu Mar 19 00:00:00 1970 | 7  | 7          | foo
+  287 | 407 | 00287_update7      | Sun Mar 29 00:00:00 1970 PST | Sun Mar 29 00:00:00 1970 | 7  | 7          | foo
+  297 | 407 | 00297_update7      | Wed Apr 08 00:00:00 1970 PST | Wed Apr 08 00:00:00 1970 | 7  | 7          | foo
+  307 | 407 | 00307_update7      | Thu Jan 08 00:00:00 1970 PST | Thu Jan 08 00:00:00 1970 | 7  | 7          | foo
+  317 | 407 | 00317_update7      | Sun Jan 18 00:00:00 1970 PST | Sun Jan 18 00:00:00 1970 | 7  | 7          | foo
+  327 | 407 | 00327_update7      | Wed Jan 28 00:00:00 1970 PST | Wed Jan 28 00:00:00 1970 | 7  | 7          | foo
+  337 | 407 | 00337_update7      | Sat Feb 07 00:00:00 1970 PST | Sat Feb 07 00:00:00 1970 | 7  | 7          | foo
+  347 | 407 | 00347_update7      | Tue Feb 17 00:00:00 1970 PST | Tue Feb 17 00:00:00 1970 | 7  | 7          | foo
+  357 | 407 | 00357_update7      | Fri Feb 27 00:00:00 1970 PST | Fri Feb 27 00:00:00 1970 | 7  | 7          | foo
+  367 | 407 | 00367_update7      | Mon Mar 09 00:00:00 1970 PST | Mon Mar 09 00:00:00 1970 | 7  | 7          | foo
+  377 | 407 | 00377_update7      | Thu Mar 19 00:00:00 1970 PST | Thu Mar 19 00:00:00 1970 | 7  | 7          | foo
+  387 | 407 | 00387_update7      | Sun Mar 29 00:00:00 1970 PST | Sun Mar 29 00:00:00 1970 | 7  | 7          | foo
+  397 | 407 | 00397_update7      | Wed Apr 08 00:00:00 1970 PST | Wed Apr 08 00:00:00 1970 | 7  | 7          | foo
+  407 | 407 | 00407_update7      | Thu Jan 08 00:00:00 1970 PST | Thu Jan 08 00:00:00 1970 | 7  | 7          | foo
+  417 | 407 | 00417_update7      | Sun Jan 18 00:00:00 1970 PST | Sun Jan 18 00:00:00 1970 | 7  | 7          | foo
+  427 | 407 | 00427_update7      | Wed Jan 28 00:00:00 1970 PST | Wed Jan 28 00:00:00 1970 | 7  | 7          | foo
+  437 | 407 | 00437_update7      | Sat Feb 07 00:00:00 1970 PST | Sat Feb 07 00:00:00 1970 | 7  | 7          | foo
+  447 | 407 | 00447_update7      | Tue Feb 17 00:00:00 1970 PST | Tue Feb 17 00:00:00 1970 | 7  | 7          | foo
+  457 | 407 | 00457_update7      | Fri Feb 27 00:00:00 1970 PST | Fri Feb 27 00:00:00 1970 | 7  | 7          | foo
+  467 | 407 | 00467_update7      | Mon Mar 09 00:00:00 1970 PST | Mon Mar 09 00:00:00 1970 | 7  | 7          | foo
+  477 | 407 | 00477_update7      | Thu Mar 19 00:00:00 1970 PST | Thu Mar 19 00:00:00 1970 | 7  | 7          | foo
+  487 | 407 | 00487_update7      | Sun Mar 29 00:00:00 1970 PST | Sun Mar 29 00:00:00 1970 | 7  | 7          | foo
+  497 | 407 | 00497_update7      | Wed Apr 08 00:00:00 1970 PST | Wed Apr 08 00:00:00 1970 | 7  | 7          | foo
+  507 | 407 | 00507_update7      | Thu Jan 08 00:00:00 1970 PST | Thu Jan 08 00:00:00 1970 | 7  | 7          | foo
+  517 | 407 | 00517_update7      | Sun Jan 18 00:00:00 1970 PST | Sun Jan 18 00:00:00 1970 | 7  | 7          | foo
+  527 | 407 | 00527_update7      | Wed Jan 28 00:00:00 1970 PST | Wed Jan 28 00:00:00 1970 | 7  | 7          | foo
+  537 | 407 | 00537_update7      | Sat Feb 07 00:00:00 1970 PST | Sat Feb 07 00:00:00 1970 | 7  | 7          | foo
+  547 | 407 | 00547_update7      | Tue Feb 17 00:00:00 1970 PST | Tue Feb 17 00:00:00 1970 | 7  | 7          | foo
+  557 | 407 | 00557_update7      | Fri Feb 27 00:00:00 1970 PST | Fri Feb 27 00:00:00 1970 | 7  | 7          | foo
+  567 | 407 | 00567_update7      | Mon Mar 09 00:00:00 1970 PST | Mon Mar 09 00:00:00 1970 | 7  | 7          | foo
+  577 | 407 | 00577_update7      | Thu Mar 19 00:00:00 1970 PST | Thu Mar 19 00:00:00 1970 | 7  | 7          | foo
+  587 | 407 | 00587_update7      | Sun Mar 29 00:00:00 1970 PST | Sun Mar 29 00:00:00 1970 | 7  | 7          | foo
+  597 | 407 | 00597_update7      | Wed Apr 08 00:00:00 1970 PST | Wed Apr 08 00:00:00 1970 | 7  | 7          | foo
+  607 | 407 | 00607_update7      | Thu Jan 08 00:00:00 1970 PST | Thu Jan 08 00:00:00 1970 | 7  | 7          | foo
+  617 | 407 | 00617_update7      | Sun Jan 18 00:00:00 1970 PST | Sun Jan 18 00:00:00 1970 | 7  | 7          | foo
+  627 | 407 | 00627_update7      | Wed Jan 28 00:00:00 1970 PST | Wed Jan 28 00:00:00 1970 | 7  | 7          | foo
+  637 | 407 | 00637_update7      | Sat Feb 07 00:00:00 1970 PST | Sat Feb 07 00:00:00 1970 | 7  | 7          | foo
+  647 | 407 | 00647_update7      | Tue Feb 17 00:00:00 1970 PST | Tue Feb 17 00:00:00 1970 | 7  | 7          | foo
+  657 | 407 | 00657_update7      | Fri Feb 27 00:00:00 1970 PST | Fri Feb 27 00:00:00 1970 | 7  | 7          | foo
+  667 | 407 | 00667_update7      | Mon Mar 09 00:00:00 1970 PST | Mon Mar 09 00:00:00 1970 | 7  | 7          | foo
+  677 | 407 | 00677_update7      | Thu Mar 19 00:00:00 1970 PST | Thu Mar 19 00:00:00 1970 | 7  | 7          | foo
+  687 | 407 | 00687_update7      | Sun Mar 29 00:00:00 1970 PST | Sun Mar 29 00:00:00 1970 | 7  | 7          | foo
+  697 | 407 | 00697_update7      | Wed Apr 08 00:00:00 1970 PST | Wed Apr 08 00:00:00 1970 | 7  | 7          | foo
+  707 | 407 | 00707_update7      | Thu Jan 08 00:00:00 1970 PST | Thu Jan 08 00:00:00 1970 | 7  | 7          | foo
+  717 | 407 | 00717_update7      | Sun Jan 18 00:00:00 1970 PST | Sun Jan 18 00:00:00 1970 | 7  | 7          | foo
+  727 | 407 | 00727_update7      | Wed Jan 28 00:00:00 1970 PST | Wed Jan 28 00:00:00 1970 | 7  | 7          | foo
+  737 | 407 | 00737_update7      | Sat Feb 07 00:00:00 1970 PST | Sat Feb 07 00:00:00 1970 | 7  | 7          | foo
+  747 | 407 | 00747_update7      | Tue Feb 17 00:00:00 1970 PST | Tue Feb 17 00:00:00 1970 | 7  | 7          | foo
+  757 | 407 | 00757_update7      | Fri Feb 27 00:00:00 1970 PST | Fri Feb 27 00:00:00 1970 | 7  | 7          | foo
+  767 | 407 | 00767_update7      | Mon Mar 09 00:00:00 1970 PST | Mon Mar 09 00:00:00 1970 | 7  | 7          | foo
+  777 | 407 | 00777_update7      | Thu Mar 19 00:00:00 1970 PST | Thu Mar 19 00:00:00 1970 | 7  | 7          | foo
+  787 | 407 | 00787_update7      | Sun Mar 29 00:00:00 1970 PST | Sun Mar 29 00:00:00 1970 | 7  | 7          | foo
+  797 | 407 | 00797_update7      | Wed Apr 08 00:00:00 1970 PST | Wed Apr 08 00:00:00 1970 | 7  | 7          | foo
+  807 | 407 | 00807_update7      | Thu Jan 08 00:00:00 1970 PST | Thu Jan 08 00:00:00 1970 | 7  | 7          | foo
+  817 | 407 | 00817_update7      | Sun Jan 18 00:00:00 1970 PST | Sun Jan 18 00:00:00 1970 | 7  | 7          | foo
+  827 | 407 | 00827_update7      | Wed Jan 28 00:00:00 1970 PST | Wed Jan 28 00:00:00 1970 | 7  | 7          | foo
+  837 | 407 | 00837_update7      | Sat Feb 07 00:00:00 1970 PST | Sat Feb 07 00:00:00 1970 | 7  | 7          | foo
+  847 | 407 | 00847_update7      | Tue Feb 17 00:00:00 1970 PST | Tue Feb 17 00:00:00 1970 | 7  | 7          | foo
+  857 | 407 | 00857_update7      | Fri Feb 27 00:00:00 1970 PST | Fri Feb 27 00:00:00 1970 | 7  | 7          | foo
+  867 | 407 | 00867_update7      | Mon Mar 09 00:00:00 1970 PST | Mon Mar 09 00:00:00 1970 | 7  | 7          | foo
+  877 | 407 | 00877_update7      | Thu Mar 19 00:00:00 1970 PST | Thu Mar 19 00:00:00 1970 | 7  | 7          | foo
+  887 | 407 | 00887_update7      | Sun Mar 29 00:00:00 1970 PST | Sun Mar 29 00:00:00 1970 | 7  | 7          | foo
+  897 | 407 | 00897_update7      | Wed Apr 08 00:00:00 1970 PST | Wed Apr 08 00:00:00 1970 | 7  | 7          | foo
+  907 | 407 | 00907_update7      | Thu Jan 08 00:00:00 1970 PST | Thu Jan 08 00:00:00 1970 | 7  | 7          | foo
+  917 | 407 | 00917_update7      | Sun Jan 18 00:00:00 1970 PST | Sun Jan 18 00:00:00 1970 | 7  | 7          | foo
+  927 | 407 | 00927_update7      | Wed Jan 28 00:00:00 1970 PST | Wed Jan 28 00:00:00 1970 | 7  | 7          | foo
+  937 | 407 | 00937_update7      | Sat Feb 07 00:00:00 1970 PST | Sat Feb 07 00:00:00 1970 | 7  | 7          | foo
+  947 | 407 | 00947_update7      | Tue Feb 17 00:00:00 1970 PST | Tue Feb 17 00:00:00 1970 | 7  | 7          | foo
+  957 | 407 | 00957_update7      | Fri Feb 27 00:00:00 1970 PST | Fri Feb 27 00:00:00 1970 | 7  | 7          | foo
+  967 | 407 | 00967_update7      | Mon Mar 09 00:00:00 1970 PST | Mon Mar 09 00:00:00 1970 | 7  | 7          | foo
+  977 | 407 | 00977_update7      | Thu Mar 19 00:00:00 1970 PST | Thu Mar 19 00:00:00 1970 | 7  | 7          | foo
+  987 | 407 | 00987_update7      | Sun Mar 29 00:00:00 1970 PST | Sun Mar 29 00:00:00 1970 | 7  | 7          | foo
+  997 | 407 | 00997_update7      | Wed Apr 08 00:00:00 1970 PST | Wed Apr 08 00:00:00 1970 | 7  | 7          | foo
+ 1007 | 507 | 0000700007_update7 |                              |                          |    |            | 
+ 1017 | 507 | 0001700017_update7 |                              |                          |    |            | 
+(102 rows)
+
+UPDATE ft2 SET c2 = ft2.c2 + 500, c3 = ft2.c3 || '_update9' 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 |                              |                          |    |            | 
+ 1015 | 105 | 0001500015 |                              |                          |    |            | 
+ 1105 | 205 | eee        |                              |                          |    |            | 
+(103 rows)
+
+DELETE FROM ft2 USING ft1 WHERE ft1.c1 = ft2.c2 AND ft1.c1 % 10 = 2;
+SELECT c1,c2,c3,c4 FROM ft2 ORDER BY c1;
+  c1  | c2  |         c3         |              c4              
+------+-----+--------------------+------------------------------
+    1 |   1 | 00001              | Fri Jan 02 00:00:00 1970 PST
+    3 | 303 | 00003_update3      | Sun Jan 04 00:00:00 1970 PST
+    4 |   4 | 00004              | Mon Jan 05 00:00:00 1970 PST
+    6 |   6 | 00006              | Wed Jan 07 00:00:00 1970 PST
+    7 | 407 | 00007_update7      | Thu Jan 08 00:00:00 1970 PST
+    8 |   8 | 00008              | Fri Jan 09 00:00:00 1970 PST
+    9 | 509 | 00009_update9      | Sat Jan 10 00:00:00 1970 PST
+   10 |   0 | 00010              | Sun Jan 11 00:00:00 1970 PST
+   11 |   1 | 00011              | Mon Jan 12 00:00:00 1970 PST
+   13 | 303 | 00013_update3      | Wed Jan 14 00:00:00 1970 PST
+   14 |   4 | 00014              | Thu Jan 15 00:00:00 1970 PST
+   16 |   6 | 00016              | Sat Jan 17 00:00:00 1970 PST
+   17 | 407 | 00017_update7      | Sun Jan 18 00:00:00 1970 PST
+   18 |   8 | 00018              | Mon Jan 19 00:00:00 1970 PST
+   19 | 509 | 00019_update9      | Tue Jan 20 00:00:00 1970 PST
+   20 |   0 | 00020              | Wed Jan 21 00:00:00 1970 PST
+   21 |   1 | 00021              | Thu Jan 22 00:00:00 1970 PST
+   23 | 303 | 00023_update3      | Sat Jan 24 00:00:00 1970 PST
+   24 |   4 | 00024              | Sun Jan 25 00:00:00 1970 PST
+   26 |   6 | 00026              | Tue Jan 27 00:00:00 1970 PST
+   27 | 407 | 00027_update7      | Wed Jan 28 00:00:00 1970 PST
+   28 |   8 | 00028              | Thu Jan 29 00:00:00 1970 PST
+   29 | 509 | 00029_update9      | Fri Jan 30 00:00:00 1970 PST
+   30 |   0 | 00030              | Sat Jan 31 00:00:00 1970 PST
+   31 |   1 | 00031              | Sun Feb 01 00:00:00 1970 PST
+   33 | 303 | 00033_update3      | Tue Feb 03 00:00:00 1970 PST
+   34 |   4 | 00034              | Wed Feb 04 00:00:00 1970 PST
+   36 |   6 | 00036              | Fri Feb 06 00:00:00 1970 PST
+   37 | 407 | 00037_update7      | Sat Feb 07 00:00:00 1970 PST
+   38 |   8 | 00038              | Sun Feb 08 00:00:00 1970 PST
+   39 | 509 | 00039_update9      | Mon Feb 09 00:00:00 1970 PST
+   40 |   0 | 00040              | Tue Feb 10 00:00:00 1970 PST
+   41 |   1 | 00041              | Wed Feb 11 00:00:00 1970 PST
+   43 | 303 | 00043_update3      | Fri Feb 13 00:00:00 1970 PST
+   44 |   4 | 00044              | Sat Feb 14 00:00:00 1970 PST
+   46 |   6 | 00046              | Mon Feb 16 00:00:00 1970 PST
+   47 | 407 | 00047_update7      | Tue Feb 17 00:00:00 1970 PST
+   48 |   8 | 00048              | Wed Feb 18 00:00:00 1970 PST
+   49 | 509 | 00049_update9      | Thu Feb 19 00:00:00 1970 PST
+   50 |   0 | 00050              | Fri Feb 20 00:00:00 1970 PST
+   51 |   1 | 00051              | Sat Feb 21 00:00:00 1970 PST
+   53 | 303 | 00053_update3      | Mon Feb 23 00:00:00 1970 PST
+   54 |   4 | 00054              | Tue Feb 24 00:00:00 1970 PST
+   56 |   6 | 00056              | Thu Feb 26 00:00:00 1970 PST
+   57 | 407 | 00057_update7      | Fri Feb 27 00:00:00 1970 PST
+   58 |   8 | 00058              | Sat Feb 28 00:00:00 1970 PST
+   59 | 509 | 00059_update9      | Sun Mar 01 00:00:00 1970 PST
+   60 |   0 | 00060              | Mon Mar 02 00:00:00 1970 PST
+   61 |   1 | 00061              | Tue Mar 03 00:00:00 1970 PST
+   63 | 303 | 00063_update3      | Thu Mar 05 00:00:00 1970 PST
+   64 |   4 | 00064              | Fri Mar 06 00:00:00 1970 PST
+   66 |   6 | 00066              | Sun Mar 08 00:00:00 1970 PST
+   67 | 407 | 00067_update7      | Mon Mar 09 00:00:00 1970 PST
+   68 |   8 | 00068              | Tue Mar 10 00:00:00 1970 PST
+   69 | 509 | 00069_update9      | Wed Mar 11 00:00:00 1970 PST
+   70 |   0 | 00070              | Thu Mar 12 00:00:00 1970 PST
+   71 |   1 | 00071              | Fri Mar 13 00:00:00 1970 PST
+   73 | 303 | 00073_update3      | Sun Mar 15 00:00:00 1970 PST
+   74 |   4 | 00074              | Mon Mar 16 00:00:00 1970 PST
+   76 |   6 | 00076              | Wed Mar 18 00:00:00 1970 PST
+   77 | 407 | 00077_update7      | Thu Mar 19 00:00:00 1970 PST
+   78 |   8 | 00078              | Fri Mar 20 00:00:00 1970 PST
+   79 | 509 | 00079_update9      | Sat Mar 21 00:00:00 1970 PST
+   80 |   0 | 00080              | Sun Mar 22 00:00:00 1970 PST
+   81 |   1 | 00081              | Mon Mar 23 00:00:00 1970 PST
+   83 | 303 | 00083_update3      | Wed Mar 25 00:00:00 1970 PST
+   84 |   4 | 00084              | Thu Mar 26 00:00:00 1970 PST
+   86 |   6 | 00086              | Sat Mar 28 00:00:00 1970 PST
+   87 | 407 | 00087_update7      | Sun Mar 29 00:00:00 1970 PST
+   88 |   8 | 00088              | Mon Mar 30 00:00:00 1970 PST
+   89 | 509 | 00089_update9      | Tue Mar 31 00:00:00 1970 PST
+   90 |   0 | 00090              | Wed Apr 01 00:00:00 1970 PST
+   91 |   1 | 00091              | Thu Apr 02 00:00:00 1970 PST
+   93 | 303 | 00093_update3      | Sat Apr 04 00:00:00 1970 PST
+   94 |   4 | 00094              | Sun Apr 05 00:00:00 1970 PST
+   96 |   6 | 00096              | Tue Apr 07 00:00:00 1970 PST
+   97 | 407 | 00097_update7      | Wed Apr 08 00:00:00 1970 PST
+   98 |   8 | 00098              | Thu Apr 09 00:00:00 1970 PST
+   99 | 509 | 00099_update9      | Fri Apr 10 00:00:00 1970 PST
+  100 |   0 | 00100              | Thu Jan 01 00:00:00 1970 PST
+  101 |   1 | 00101              | Fri Jan 02 00:00:00 1970 PST
+  103 | 303 | 00103_update3      | Sun Jan 04 00:00:00 1970 PST
+  104 |   4 | 00104              | Mon Jan 05 00:00:00 1970 PST
+  106 |   6 | 00106              | Wed Jan 07 00:00:00 1970 PST
+  107 | 407 | 00107_update7      | Thu Jan 08 00:00:00 1970 PST
+  108 |   8 | 00108              | Fri Jan 09 00:00:00 1970 PST
+  109 | 509 | 00109_update9      | Sat Jan 10 00:00:00 1970 PST
+  110 |   0 | 00110              | Sun Jan 11 00:00:00 1970 PST
+  111 |   1 | 00111              | Mon Jan 12 00:00:00 1970 PST
+  113 | 303 | 00113_update3      | Wed Jan 14 00:00:00 1970 PST
+  114 |   4 | 00114              | Thu Jan 15 00:00:00 1970 PST
+  116 |   6 | 00116              | Sat Jan 17 00:00:00 1970 PST
+  117 | 407 | 00117_update7      | Sun Jan 18 00:00:00 1970 PST
+  118 |   8 | 00118              | Mon Jan 19 00:00:00 1970 PST
+  119 | 509 | 00119_update9      | Tue Jan 20 00:00:00 1970 PST
+  120 |   0 | 00120              | Wed Jan 21 00:00:00 1970 PST
+  121 |   1 | 00121              | Thu Jan 22 00:00:00 1970 PST
+  123 | 303 | 00123_update3      | Sat Jan 24 00:00:00 1970 PST
+  124 |   4 | 00124              | Sun Jan 25 00:00:00 1970 PST
+  126 |   6 | 00126              | Tue Jan 27 00:00:00 1970 PST
+  127 | 407 | 00127_update7      | Wed Jan 28 00:00:00 1970 PST
+  128 |   8 | 00128              | Thu Jan 29 00:00:00 1970 PST
+  129 | 509 | 00129_update9      | Fri Jan 30 00:00:00 1970 PST
+  130 |   0 | 00130              | Sat Jan 31 00:00:00 1970 PST
+  131 |   1 | 00131              | Sun Feb 01 00:00:00 1970 PST
+  133 | 303 | 00133_update3      | Tue Feb 03 00:00:00 1970 PST
+  134 |   4 | 00134              | Wed Feb 04 00:00:00 1970 PST
+  136 |   6 | 00136              | Fri Feb 06 00:00:00 1970 PST
+  137 | 407 | 00137_update7      | Sat Feb 07 00:00:00 1970 PST
+  138 |   8 | 00138              | Sun Feb 08 00:00:00 1970 PST
+  139 | 509 | 00139_update9      | Mon Feb 09 00:00:00 1970 PST
+  140 |   0 | 00140              | Tue Feb 10 00:00:00 1970 PST
+  141 |   1 | 00141              | Wed Feb 11 00:00:00 1970 PST
+  143 | 303 | 00143_update3      | Fri Feb 13 00:00:00 1970 PST
+  144 |   4 | 00144              | Sat Feb 14 00:00:00 1970 PST
+  146 |   6 | 00146              | Mon Feb 16 00:00:00 1970 PST
+  147 | 407 | 00147_update7      | Tue Feb 17 00:00:00 1970 PST
+  148 |   8 | 00148              | Wed Feb 18 00:00:00 1970 PST
+  149 | 509 | 00149_update9      | Thu Feb 19 00:00:00 1970 PST
+  150 |   0 | 00150              | Fri Feb 20 00:00:00 1970 PST
+  151 |   1 | 00151              | Sat Feb 21 00:00:00 1970 PST
+  153 | 303 | 00153_update3      | Mon Feb 23 00:00:00 1970 PST
+  154 |   4 | 00154              | Tue Feb 24 00:00:00 1970 PST
+  156 |   6 | 00156              | Thu Feb 26 00:00:00 1970 PST
+  157 | 407 | 00157_update7      | Fri Feb 27 00:00:00 1970 PST
+  158 |   8 | 00158              | Sat Feb 28 00:00:00 1970 PST
+  159 | 509 | 00159_update9      | Sun Mar 01 00:00:00 1970 PST
+  160 |   0 | 00160              | Mon Mar 02 00:00:00 1970 PST
+  161 |   1 | 00161              | Tue Mar 03 00:00:00 1970 PST
+  163 | 303 | 00163_update3      | Thu Mar 05 00:00:00 1970 PST
+  164 |   4 | 00164              | Fri Mar 06 00:00:00 1970 PST
+  166 |   6 | 00166              | Sun Mar 08 00:00:00 1970 PST
+  167 | 407 | 00167_update7      | Mon Mar 09 00:00:00 1970 PST
+  168 |   8 | 00168              | Tue Mar 10 00:00:00 1970 PST
+  169 | 509 | 00169_update9      | Wed Mar 11 00:00:00 1970 PST
+  170 |   0 | 00170              | Thu Mar 12 00:00:00 1970 PST
+  171 |   1 | 00171              | Fri Mar 13 00:00:00 1970 PST
+  173 | 303 | 00173_update3      | Sun Mar 15 00:00:00 1970 PST
+  174 |   4 | 00174              | Mon Mar 16 00:00:00 1970 PST
+  176 |   6 | 00176              | Wed Mar 18 00:00:00 1970 PST
+  177 | 407 | 00177_update7      | Thu Mar 19 00:00:00 1970 PST
+  178 |   8 | 00178              | Fri Mar 20 00:00:00 1970 PST
+  179 | 509 | 00179_update9      | Sat Mar 21 00:00:00 1970 PST
+  180 |   0 | 00180              | Sun Mar 22 00:00:00 1970 PST
+  181 |   1 | 00181              | Mon Mar 23 00:00:00 1970 PST
+  183 | 303 | 00183_update3      | Wed Mar 25 00:00:00 1970 PST
+  184 |   4 | 00184              | Thu Mar 26 00:00:00 1970 PST
+  186 |   6 | 00186              | Sat Mar 28 00:00:00 1970 PST
+  187 | 407 | 00187_update7      | Sun Mar 29 00:00:00 1970 PST
+  188 |   8 | 00188              | Mon Mar 30 00:00:00 1970 PST
+  189 | 509 | 00189_update9      | Tue Mar 31 00:00:00 1970 PST
+  190 |   0 | 00190              | Wed Apr 01 00:00:00 1970 PST
+  191 |   1 | 00191              | Thu Apr 02 00:00:00 1970 PST
+  193 | 303 | 00193_update3      | Sat Apr 04 00:00:00 1970 PST
+  194 |   4 | 00194              | Sun Apr 05 00:00:00 1970 PST
+  196 |   6 | 00196              | Tue Apr 07 00:00:00 1970 PST
+  197 | 407 | 00197_update7      | Wed Apr 08 00:00:00 1970 PST
+  198 |   8 | 00198              | Thu Apr 09 00:00:00 1970 PST
+  199 | 509 | 00199_update9      | Fri Apr 10 00:00:00 1970 PST
+  200 |   0 | 00200              | Thu Jan 01 00:00:00 1970 PST
+  201 |   1 | 00201              | Fri Jan 02 00:00:00 1970 PST
+  203 | 303 | 00203_update3      | Sun Jan 04 00:00:00 1970 PST
+  204 |   4 | 00204              | Mon Jan 05 00:00:00 1970 PST
+  206 |   6 | 00206              | Wed Jan 07 00:00:00 1970 PST
+  207 | 407 | 00207_update7      | Thu Jan 08 00:00:00 1970 PST
+  208 |   8 | 00208              | Fri Jan 09 00:00:00 1970 PST
+  209 | 509 | 00209_update9      | Sat Jan 10 00:00:00 1970 PST
+  210 |   0 | 00210              | Sun Jan 11 00:00:00 1970 PST
+  211 |   1 | 00211              | Mon Jan 12 00:00:00 1970 PST
+  213 | 303 | 00213_update3      | Wed Jan 14 00:00:00 1970 PST
+  214 |   4 | 00214              | Thu Jan 15 00:00:00 1970 PST
+  216 |   6 | 00216              | Sat Jan 17 00:00:00 1970 PST
+  217 | 407 | 00217_update7      | Sun Jan 18 00:00:00 1970 PST
+  218 |   8 | 00218              | Mon Jan 19 00:00:00 1970 PST
+  219 | 509 | 00219_update9      | Tue Jan 20 00:00:00 1970 PST
+  220 |   0 | 00220              | Wed Jan 21 00:00:00 1970 PST
+  221 |   1 | 00221              | Thu Jan 22 00:00:00 1970 PST
+  223 | 303 | 00223_update3      | Sat Jan 24 00:00:00 1970 PST
+  224 |   4 | 00224              | Sun Jan 25 00:00:00 1970 PST
+  226 |   6 | 00226              | Tue Jan 27 00:00:00 1970 PST
+  227 | 407 | 00227_update7      | Wed Jan 28 00:00:00 1970 PST
+  228 |   8 | 00228              | Thu Jan 29 00:00:00 1970 PST
+  229 | 509 | 00229_update9      | Fri Jan 30 00:00:00 1970 PST
+  230 |   0 | 00230              | Sat Jan 31 00:00:00 1970 PST
+  231 |   1 | 00231              | Sun Feb 01 00:00:00 1970 PST
+  233 | 303 | 00233_update3      | Tue Feb 03 00:00:00 1970 PST
+  234 |   4 | 00234              | Wed Feb 04 00:00:00 1970 PST
+  236 |   6 | 00236              | Fri Feb 06 00:00:00 1970 PST
+  237 | 407 | 00237_update7      | Sat Feb 07 00:00:00 1970 PST
+  238 |   8 | 00238              | Sun Feb 08 00:00:00 1970 PST
+  239 | 509 | 00239_update9      | Mon Feb 09 00:00:00 1970 PST
+  240 |   0 | 00240              | Tue Feb 10 00:00:00 1970 PST
+  241 |   1 | 00241              | Wed Feb 11 00:00:00 1970 PST
+  243 | 303 | 00243_update3      | Fri Feb 13 00:00:00 1970 PST
+  244 |   4 | 00244              | Sat Feb 14 00:00:00 1970 PST
+  246 |   6 | 00246              | Mon Feb 16 00:00:00 1970 PST
+  247 | 407 | 00247_update7      | Tue Feb 17 00:00:00 1970 PST
+  248 |   8 | 00248              | Wed Feb 18 00:00:00 1970 PST
+  249 | 509 | 00249_update9      | Thu Feb 19 00:00:00 1970 PST
+  250 |   0 | 00250              | Fri Feb 20 00:00:00 1970 PST
+  251 |   1 | 00251              | Sat Feb 21 00:00:00 1970 PST
+  253 | 303 | 00253_update3      | Mon Feb 23 00:00:00 1970 PST
+  254 |   4 | 00254              | Tue Feb 24 00:00:00 1970 PST
+  256 |   6 | 00256              | Thu Feb 26 00:00:00 1970 PST
+  257 | 407 | 00257_update7      | Fri Feb 27 00:00:00 1970 PST
+  258 |   8 | 00258              | Sat Feb 28 00:00:00 1970 PST
+  259 | 509 | 00259_update9      | Sun Mar 01 00:00:00 1970 PST
+  260 |   0 | 00260              | Mon Mar 02 00:00:00 1970 PST
+  261 |   1 | 00261              | Tue Mar 03 00:00:00 1970 PST
+  263 | 303 | 00263_update3      | Thu Mar 05 00:00:00 1970 PST
+  264 |   4 | 00264              | Fri Mar 06 00:00:00 1970 PST
+  266 |   6 | 00266              | Sun Mar 08 00:00:00 1970 PST
+  267 | 407 | 00267_update7      | Mon Mar 09 00:00:00 1970 PST
+  268 |   8 | 00268              | Tue Mar 10 00:00:00 1970 PST
+  269 | 509 | 00269_update9      | Wed Mar 11 00:00:00 1970 PST
+  270 |   0 | 00270              | Thu Mar 12 00:00:00 1970 PST
+  271 |   1 | 00271              | Fri Mar 13 00:00:00 1970 PST
+  273 | 303 | 00273_update3      | Sun Mar 15 00:00:00 1970 PST
+  274 |   4 | 00274              | Mon Mar 16 00:00:00 1970 PST
+  276 |   6 | 00276              | Wed Mar 18 00:00:00 1970 PST
+  277 | 407 | 00277_update7      | Thu Mar 19 00:00:00 1970 PST
+  278 |   8 | 00278              | Fri Mar 20 00:00:00 1970 PST
+  279 | 509 | 00279_update9      | Sat Mar 21 00:00:00 1970 PST
+  280 |   0 | 00280              | Sun Mar 22 00:00:00 1970 PST
+  281 |   1 | 00281              | Mon Mar 23 00:00:00 1970 PST
+  283 | 303 | 00283_update3      | Wed Mar 25 00:00:00 1970 PST
+  284 |   4 | 00284              | Thu Mar 26 00:00:00 1970 PST
+  286 |   6 | 00286              | Sat Mar 28 00:00:00 1970 PST
+  287 | 407 | 00287_update7      | Sun Mar 29 00:00:00 1970 PST
+  288 |   8 | 00288              | Mon Mar 30 00:00:00 1970 PST
+  289 | 509 | 00289_update9      | Tue Mar 31 00:00:00 1970 PST
+  290 |   0 | 00290              | Wed Apr 01 00:00:00 1970 PST
+  291 |   1 | 00291              | Thu Apr 02 00:00:00 1970 PST
+  293 | 303 | 00293_update3      | Sat Apr 04 00:00:00 1970 PST
+  294 |   4 | 00294              | Sun Apr 05 00:00:00 1970 PST
+  296 |   6 | 00296              | Tue Apr 07 00:00:00 1970 PST
+  297 | 407 | 00297_update7      | Wed Apr 08 00:00:00 1970 PST
+  298 |   8 | 00298              | Thu Apr 09 00:00:00 1970 PST
+  299 | 509 | 00299_update9      | Fri Apr 10 00:00:00 1970 PST
+  300 |   0 | 00300              | Thu Jan 01 00:00:00 1970 PST
+  301 |   1 | 00301              | Fri Jan 02 00:00:00 1970 PST
+  303 | 303 | 00303_update3      | Sun Jan 04 00:00:00 1970 PST
+  304 |   4 | 00304              | Mon Jan 05 00:00:00 1970 PST
+  306 |   6 | 00306              | Wed Jan 07 00:00:00 1970 PST
+  307 | 407 | 00307_update7      | Thu Jan 08 00:00:00 1970 PST
+  308 |   8 | 00308              | Fri Jan 09 00:00:00 1970 PST
+  309 | 509 | 00309_update9      | Sat Jan 10 00:00:00 1970 PST
+  310 |   0 | 00310              | Sun Jan 11 00:00:00 1970 PST
+  311 |   1 | 00311              | Mon Jan 12 00:00:00 1970 PST
+  313 | 303 | 00313_update3      | Wed Jan 14 00:00:00 1970 PST
+  314 |   4 | 00314              | Thu Jan 15 00:00:00 1970 PST
+  316 |   6 | 00316              | Sat Jan 17 00:00:00 1970 PST
+  317 | 407 | 00317_update7      | Sun Jan 18 00:00:00 1970 PST
+  318 |   8 | 00318              | Mon Jan 19 00:00:00 1970 PST
+  319 | 509 | 00319_update9      | Tue Jan 20 00:00:00 1970 PST
+  320 |   0 | 00320              | Wed Jan 21 00:00:00 1970 PST
+  321 |   1 | 00321              | Thu Jan 22 00:00:00 1970 PST
+  323 | 303 | 00323_update3      | Sat Jan 24 00:00:00 1970 PST
+  324 |   4 | 00324              | Sun Jan 25 00:00:00 1970 PST
+  326 |   6 | 00326              | Tue Jan 27 00:00:00 1970 PST
+  327 | 407 | 00327_update7      | Wed Jan 28 00:00:00 1970 PST
+  328 |   8 | 00328              | Thu Jan 29 00:00:00 1970 PST
+  329 | 509 | 00329_update9      | Fri Jan 30 00:00:00 1970 PST
+  330 |   0 | 00330              | Sat Jan 31 00:00:00 1970 PST
+  331 |   1 | 00331              | Sun Feb 01 00:00:00 1970 PST
+  333 | 303 | 00333_update3      | Tue Feb 03 00:00:00 1970 PST
+  334 |   4 | 00334              | Wed Feb 04 00:00:00 1970 PST
+  336 |   6 | 00336              | Fri Feb 06 00:00:00 1970 PST
+  337 | 407 | 00337_update7      | Sat Feb 07 00:00:00 1970 PST
+  338 |   8 | 00338              | Sun Feb 08 00:00:00 1970 PST
+  339 | 509 | 00339_update9      | Mon Feb 09 00:00:00 1970 PST
+  340 |   0 | 00340              | Tue Feb 10 00:00:00 1970 PST
+  341 |   1 | 00341              | Wed Feb 11 00:00:00 1970 PST
+  343 | 303 | 00343_update3      | Fri Feb 13 00:00:00 1970 PST
+  344 |   4 | 00344              | Sat Feb 14 00:00:00 1970 PST
+  346 |   6 | 00346              | Mon Feb 16 00:00:00 1970 PST
+  347 | 407 | 00347_update7      | Tue Feb 17 00:00:00 1970 PST
+  348 |   8 | 00348              | Wed Feb 18 00:00:00 1970 PST
+  349 | 509 | 00349_update9      | Thu Feb 19 00:00:00 1970 PST
+  350 |   0 | 00350              | Fri Feb 20 00:00:00 1970 PST
+  351 |   1 | 00351              | Sat Feb 21 00:00:00 1970 PST
+  353 | 303 | 00353_update3      | Mon Feb 23 00:00:00 1970 PST
+  354 |   4 | 00354              | Tue Feb 24 00:00:00 1970 PST
+  356 |   6 | 00356              | Thu Feb 26 00:00:00 1970 PST
+  357 | 407 | 00357_update7      | Fri Feb 27 00:00:00 1970 PST
+  358 |   8 | 00358              | Sat Feb 28 00:00:00 1970 PST
+  359 | 509 | 00359_update9      | Sun Mar 01 00:00:00 1970 PST
+  360 |   0 | 00360              | Mon Mar 02 00:00:00 1970 PST
+  361 |   1 | 00361              | Tue Mar 03 00:00:00 1970 PST
+  363 | 303 | 00363_update3      | Thu Mar 05 00:00:00 1970 PST
+  364 |   4 | 00364              | Fri Mar 06 00:00:00 1970 PST
+  366 |   6 | 00366              | Sun Mar 08 00:00:00 1970 PST
+  367 | 407 | 00367_update7      | Mon Mar 09 00:00:00 1970 PST
+  368 |   8 | 00368              | Tue Mar 10 00:00:00 1970 PST
+  369 | 509 | 00369_update9      | Wed Mar 11 00:00:00 1970 PST
+  370 |   0 | 00370              | Thu Mar 12 00:00:00 1970 PST
+  371 |   1 | 00371              | Fri Mar 13 00:00:00 1970 PST
+  373 | 303 | 00373_update3      | Sun Mar 15 00:00:00 1970 PST
+  374 |   4 | 00374              | Mon Mar 16 00:00:00 1970 PST
+  376 |   6 | 00376              | Wed Mar 18 00:00:00 1970 PST
+  377 | 407 | 00377_update7      | Thu Mar 19 00:00:00 1970 PST
+  378 |   8 | 00378              | Fri Mar 20 00:00:00 1970 PST
+  379 | 509 | 00379_update9      | Sat Mar 21 00:00:00 1970 PST
+  380 |   0 | 00380              | Sun Mar 22 00:00:00 1970 PST
+  381 |   1 | 00381              | Mon Mar 23 00:00:00 1970 PST
+  383 | 303 | 00383_update3      | Wed Mar 25 00:00:00 1970 PST
+  384 |   4 | 00384              | Thu Mar 26 00:00:00 1970 PST
+  386 |   6 | 00386              | Sat Mar 28 00:00:00 1970 PST
+  387 | 407 | 00387_update7      | Sun Mar 29 00:00:00 1970 PST
+  388 |   8 | 00388              | Mon Mar 30 00:00:00 1970 PST
+  389 | 509 | 00389_update9      | Tue Mar 31 00:00:00 1970 PST
+  390 |   0 | 00390              | Wed Apr 01 00:00:00 1970 PST
+  391 |   1 | 00391              | Thu Apr 02 00:00:00 1970 PST
+  393 | 303 | 00393_update3      | Sat Apr 04 00:00:00 1970 PST
+  394 |   4 | 00394              | Sun Apr 05 00:00:00 1970 PST
+  396 |   6 | 00396              | Tue Apr 07 00:00:00 1970 PST
+  397 | 407 | 00397_update7      | Wed Apr 08 00:00:00 1970 PST
+  398 |   8 | 00398              | Thu Apr 09 00:00:00 1970 PST
+  399 | 509 | 00399_update9      | Fri Apr 10 00:00:00 1970 PST
+  400 |   0 | 00400              | Thu Jan 01 00:00:00 1970 PST
+  401 |   1 | 00401              | Fri Jan 02 00:00:00 1970 PST
+  403 | 303 | 00403_update3      | Sun Jan 04 00:00:00 1970 PST
+  404 |   4 | 00404              | Mon Jan 05 00:00:00 1970 PST
+  406 |   6 | 00406              | Wed Jan 07 00:00:00 1970 PST
+  407 | 407 | 00407_update7      | Thu Jan 08 00:00:00 1970 PST
+  408 |   8 | 00408              | Fri Jan 09 00:00:00 1970 PST
+  409 | 509 | 00409_update9      | Sat Jan 10 00:00:00 1970 PST
+  410 |   0 | 00410              | Sun Jan 11 00:00:00 1970 PST
+  411 |   1 | 00411              | Mon Jan 12 00:00:00 1970 PST
+  413 | 303 | 00413_update3      | Wed Jan 14 00:00:00 1970 PST
+  414 |   4 | 00414              | Thu Jan 15 00:00:00 1970 PST
+  416 |   6 | 00416              | Sat Jan 17 00:00:00 1970 PST
+  417 | 407 | 00417_update7      | Sun Jan 18 00:00:00 1970 PST
+  418 |   8 | 00418              | Mon Jan 19 00:00:00 1970 PST
+  419 | 509 | 00419_update9      | Tue Jan 20 00:00:00 1970 PST
+  420 |   0 | 00420              | Wed Jan 21 00:00:00 1970 PST
+  421 |   1 | 00421              | Thu Jan 22 00:00:00 1970 PST
+  423 | 303 | 00423_update3      | Sat Jan 24 00:00:00 1970 PST
+  424 |   4 | 00424              | Sun Jan 25 00:00:00 1970 PST
+  426 |   6 | 00426              | Tue Jan 27 00:00:00 1970 PST
+  427 | 407 | 00427_update7      | Wed Jan 28 00:00:00 1970 PST
+  428 |   8 | 00428              | Thu Jan 29 00:00:00 1970 PST
+  429 | 509 | 00429_update9      | Fri Jan 30 00:00:00 1970 PST
+  430 |   0 | 00430              | Sat Jan 31 00:00:00 1970 PST
+  431 |   1 | 00431              | Sun Feb 01 00:00:00 1970 PST
+  433 | 303 | 00433_update3      | Tue Feb 03 00:00:00 1970 PST
+  434 |   4 | 00434              | Wed Feb 04 00:00:00 1970 PST
+  436 |   6 | 00436              | Fri Feb 06 00:00:00 1970 PST
+  437 | 407 | 00437_update7      | Sat Feb 07 00:00:00 1970 PST
+  438 |   8 | 00438              | Sun Feb 08 00:00:00 1970 PST
+  439 | 509 | 00439_update9      | Mon Feb 09 00:00:00 1970 PST
+  440 |   0 | 00440              | Tue Feb 10 00:00:00 1970 PST
+  441 |   1 | 00441              | Wed Feb 11 00:00:00 1970 PST
+  443 | 303 | 00443_update3      | Fri Feb 13 00:00:00 1970 PST
+  444 |   4 | 00444              | Sat Feb 14 00:00:00 1970 PST
+  446 |   6 | 00446              | Mon Feb 16 00:00:00 1970 PST
+  447 | 407 | 00447_update7      | Tue Feb 17 00:00:00 1970 PST
+  448 |   8 | 00448              | Wed Feb 18 00:00:00 1970 PST
+  449 | 509 | 00449_update9      | Thu Feb 19 00:00:00 1970 PST
+  450 |   0 | 00450              | Fri Feb 20 00:00:00 1970 PST
+  451 |   1 | 00451              | Sat Feb 21 00:00:00 1970 PST
+  453 | 303 | 00453_update3      | Mon Feb 23 00:00:00 1970 PST
+  454 |   4 | 00454              | Tue Feb 24 00:00:00 1970 PST
+  456 |   6 | 00456              | Thu Feb 26 00:00:00 1970 PST
+  457 | 407 | 00457_update7      | Fri Feb 27 00:00:00 1970 PST
+  458 |   8 | 00458              | Sat Feb 28 00:00:00 1970 PST
+  459 | 509 | 00459_update9      | Sun Mar 01 00:00:00 1970 PST
+  460 |   0 | 00460              | Mon Mar 02 00:00:00 1970 PST
+  461 |   1 | 00461              | Tue Mar 03 00:00:00 1970 PST
+  463 | 303 | 00463_update3      | Thu Mar 05 00:00:00 1970 PST
+  464 |   4 | 00464              | Fri Mar 06 00:00:00 1970 PST
+  466 |   6 | 00466              | Sun Mar 08 00:00:00 1970 PST
+  467 | 407 | 00467_update7      | Mon Mar 09 00:00:00 1970 PST
+  468 |   8 | 00468              | Tue Mar 10 00:00:00 1970 PST
+  469 | 509 | 00469_update9      | Wed Mar 11 00:00:00 1970 PST
+  470 |   0 | 00470              | Thu Mar 12 00:00:00 1970 PST
+  471 |   1 | 00471              | Fri Mar 13 00:00:00 1970 PST
+  473 | 303 | 00473_update3      | Sun Mar 15 00:00:00 1970 PST
+  474 |   4 | 00474              | Mon Mar 16 00:00:00 1970 PST
+  476 |   6 | 00476              | Wed Mar 18 00:00:00 1970 PST
+  477 | 407 | 00477_update7      | Thu Mar 19 00:00:00 1970 PST
+  478 |   8 | 00478              | Fri Mar 20 00:00:00 1970 PST
+  479 | 509 | 00479_update9      | Sat Mar 21 00:00:00 1970 PST
+  480 |   0 | 00480              | Sun Mar 22 00:00:00 1970 PST
+  481 |   1 | 00481              | Mon Mar 23 00:00:00 1970 PST
+  483 | 303 | 00483_update3      | Wed Mar 25 00:00:00 1970 PST
+  484 |   4 | 00484              | Thu Mar 26 00:00:00 1970 PST
+  486 |   6 | 00486              | Sat Mar 28 00:00:00 1970 PST
+  487 | 407 | 00487_update7      | Sun Mar 29 00:00:00 1970 PST
+  488 |   8 | 00488              | Mon Mar 30 00:00:00 1970 PST
+  489 | 509 | 00489_update9      | Tue Mar 31 00:00:00 1970 PST
+  490 |   0 | 00490              | Wed Apr 01 00:00:00 1970 PST
+  491 |   1 | 00491              | Thu Apr 02 00:00:00 1970 PST
+  493 | 303 | 00493_update3      | Sat Apr 04 00:00:00 1970 PST
+  494 |   4 | 00494              | Sun Apr 05 00:00:00 1970 PST
+  496 |   6 | 00496              | Tue Apr 07 00:00:00 1970 PST
+  497 | 407 | 00497_update7      | Wed Apr 08 00:00:00 1970 PST
+  498 |   8 | 00498              | Thu Apr 09 00:00:00 1970 PST
+  499 | 509 | 00499_update9      | Fri Apr 10 00:00:00 1970 PST
+  500 |   0 | 00500              | Thu Jan 01 00:00:00 1970 PST
+  501 |   1 | 00501              | Fri Jan 02 00:00:00 1970 PST
+  503 | 303 | 00503_update3      | Sun Jan 04 00:00:00 1970 PST
+  504 |   4 | 00504              | Mon Jan 05 00:00:00 1970 PST
+  506 |   6 | 00506              | Wed Jan 07 00:00:00 1970 PST
+  507 | 407 | 00507_update7      | Thu Jan 08 00:00:00 1970 PST
+  508 |   8 | 00508              | Fri Jan 09 00:00:00 1970 PST
+  509 | 509 | 00509_update9      | Sat Jan 10 00:00:00 1970 PST
+  510 |   0 | 00510              | Sun Jan 11 00:00:00 1970 PST
+  511 |   1 | 00511              | Mon Jan 12 00:00:00 1970 PST
+  513 | 303 | 00513_update3      | Wed Jan 14 00:00:00 1970 PST
+  514 |   4 | 00514              | Thu Jan 15 00:00:00 1970 PST
+  516 |   6 | 00516              | Sat Jan 17 00:00:00 1970 PST
+  517 | 407 | 00517_update7      | Sun Jan 18 00:00:00 1970 PST
+  518 |   8 | 00518              | Mon Jan 19 00:00:00 1970 PST
+  519 | 509 | 00519_update9      | Tue Jan 20 00:00:00 1970 PST
+  520 |   0 | 00520              | Wed Jan 21 00:00:00 1970 PST
+  521 |   1 | 00521              | Thu Jan 22 00:00:00 1970 PST
+  523 | 303 | 00523_update3      | Sat Jan 24 00:00:00 1970 PST
+  524 |   4 | 00524              | Sun Jan 25 00:00:00 1970 PST
+  526 |   6 | 00526              | Tue Jan 27 00:00:00 1970 PST
+  527 | 407 | 00527_update7      | Wed Jan 28 00:00:00 1970 PST
+  528 |   8 | 00528              | Thu Jan 29 00:00:00 1970 PST
+  529 | 509 | 00529_update9      | Fri Jan 30 00:00:00 1970 PST
+  530 |   0 | 00530              | Sat Jan 31 00:00:00 1970 PST
+  531 |   1 | 00531              | Sun Feb 01 00:00:00 1970 PST
+  533 | 303 | 00533_update3      | Tue Feb 03 00:00:00 1970 PST
+  534 |   4 | 00534              | Wed Feb 04 00:00:00 1970 PST
+  536 |   6 | 00536              | Fri Feb 06 00:00:00 1970 PST
+  537 | 407 | 00537_update7      | Sat Feb 07 00:00:00 1970 PST
+  538 |   8 | 00538              | Sun Feb 08 00:00:00 1970 PST
+  539 | 509 | 00539_update9      | Mon Feb 09 00:00:00 1970 PST
+  540 |   0 | 00540              | Tue Feb 10 00:00:00 1970 PST
+  541 |   1 | 00541              | Wed Feb 11 00:00:00 1970 PST
+  543 | 303 | 00543_update3      | Fri Feb 13 00:00:00 1970 PST
+  544 |   4 | 00544              | Sat Feb 14 00:00:00 1970 PST
+  546 |   6 | 00546              | Mon Feb 16 00:00:00 1970 PST
+  547 | 407 | 00547_update7      | Tue Feb 17 00:00:00 1970 PST
+  548 |   8 | 00548              | Wed Feb 18 00:00:00 1970 PST
+  549 | 509 | 00549_update9      | Thu Feb 19 00:00:00 1970 PST
+  550 |   0 | 00550              | Fri Feb 20 00:00:00 1970 PST
+  551 |   1 | 00551              | Sat Feb 21 00:00:00 1970 PST
+  553 | 303 | 00553_update3      | Mon Feb 23 00:00:00 1970 PST
+  554 |   4 | 00554              | Tue Feb 24 00:00:00 1970 PST
+  556 |   6 | 00556              | Thu Feb 26 00:00:00 1970 PST
+  557 | 407 | 00557_update7      | Fri Feb 27 00:00:00 1970 PST
+  558 |   8 | 00558              | Sat Feb 28 00:00:00 1970 PST
+  559 | 509 | 00559_update9      | Sun Mar 01 00:00:00 1970 PST
+  560 |   0 | 00560              | Mon Mar 02 00:00:00 1970 PST
+  561 |   1 | 00561              | Tue Mar 03 00:00:00 1970 PST
+  563 | 303 | 00563_update3      | Thu Mar 05 00:00:00 1970 PST
+  564 |   4 | 00564              | Fri Mar 06 00:00:00 1970 PST
+  566 |   6 | 00566              | Sun Mar 08 00:00:00 1970 PST
+  567 | 407 | 00567_update7      | Mon Mar 09 00:00:00 1970 PST
+  568 |   8 | 00568              | Tue Mar 10 00:00:00 1970 PST
+  569 | 509 | 00569_update9      | Wed Mar 11 00:00:00 1970 PST
+  570 |   0 | 00570              | Thu Mar 12 00:00:00 1970 PST
+  571 |   1 | 00571              | Fri Mar 13 00:00:00 1970 PST
+  573 | 303 | 00573_update3      | Sun Mar 15 00:00:00 1970 PST
+  574 |   4 | 00574              | Mon Mar 16 00:00:00 1970 PST
+  576 |   6 | 00576              | Wed Mar 18 00:00:00 1970 PST
+  577 | 407 | 00577_update7      | Thu Mar 19 00:00:00 1970 PST
+  578 |   8 | 00578              | Fri Mar 20 00:00:00 1970 PST
+  579 | 509 | 00579_update9      | Sat Mar 21 00:00:00 1970 PST
+  580 |   0 | 00580              | Sun Mar 22 00:00:00 1970 PST
+  581 |   1 | 00581              | Mon Mar 23 00:00:00 1970 PST
+  583 | 303 | 00583_update3      | Wed Mar 25 00:00:00 1970 PST
+  584 |   4 | 00584              | Thu Mar 26 00:00:00 1970 PST
+  586 |   6 | 00586              | Sat Mar 28 00:00:00 1970 PST
+  587 | 407 | 00587_update7      | Sun Mar 29 00:00:00 1970 PST
+  588 |   8 | 00588              | Mon Mar 30 00:00:00 1970 PST
+  589 | 509 | 00589_update9      | Tue Mar 31 00:00:00 1970 PST
+  590 |   0 | 00590              | Wed Apr 01 00:00:00 1970 PST
+  591 |   1 | 00591              | Thu Apr 02 00:00:00 1970 PST
+  593 | 303 | 00593_update3      | Sat Apr 04 00:00:00 1970 PST
+  594 |   4 | 00594              | Sun Apr 05 00:00:00 1970 PST
+  596 |   6 | 00596              | Tue Apr 07 00:00:00 1970 PST
+  597 | 407 | 00597_update7      | Wed Apr 08 00:00:00 1970 PST
+  598 |   8 | 00598              | Thu Apr 09 00:00:00 1970 PST
+  599 | 509 | 00599_update9      | Fri Apr 10 00:00:00 1970 PST
+  600 |   0 | 00600              | Thu Jan 01 00:00:00 1970 PST
+  601 |   1 | 00601              | Fri Jan 02 00:00:00 1970 PST
+  603 | 303 | 00603_update3      | Sun Jan 04 00:00:00 1970 PST
+  604 |   4 | 00604              | Mon Jan 05 00:00:00 1970 PST
+  606 |   6 | 00606              | Wed Jan 07 00:00:00 1970 PST
+  607 | 407 | 00607_update7      | Thu Jan 08 00:00:00 1970 PST
+  608 |   8 | 00608              | Fri Jan 09 00:00:00 1970 PST
+  609 | 509 | 00609_update9      | Sat Jan 10 00:00:00 1970 PST
+  610 |   0 | 00610              | Sun Jan 11 00:00:00 1970 PST
+  611 |   1 | 00611              | Mon Jan 12 00:00:00 1970 PST
+  613 | 303 | 00613_update3      | Wed Jan 14 00:00:00 1970 PST
+  614 |   4 | 00614              | Thu Jan 15 00:00:00 1970 PST
+  616 |   6 | 00616              | Sat Jan 17 00:00:00 1970 PST
+  617 | 407 | 00617_update7      | Sun Jan 18 00:00:00 1970 PST
+  618 |   8 | 00618              | Mon Jan 19 00:00:00 1970 PST
+  619 | 509 | 00619_update9      | Tue Jan 20 00:00:00 1970 PST
+  620 |   0 | 00620              | Wed Jan 21 00:00:00 1970 PST
+  621 |   1 | 00621              | Thu Jan 22 00:00:00 1970 PST
+  623 | 303 | 00623_update3      | Sat Jan 24 00:00:00 1970 PST
+  624 |   4 | 00624              | Sun Jan 25 00:00:00 1970 PST
+  626 |   6 | 00626              | Tue Jan 27 00:00:00 1970 PST
+  627 | 407 | 00627_update7      | Wed Jan 28 00:00:00 1970 PST
+  628 |   8 | 00628              | Thu Jan 29 00:00:00 1970 PST
+  629 | 509 | 00629_update9      | Fri Jan 30 00:00:00 1970 PST
+  630 |   0 | 00630              | Sat Jan 31 00:00:00 1970 PST
+  631 |   1 | 00631              | Sun Feb 01 00:00:00 1970 PST
+  633 | 303 | 00633_update3      | Tue Feb 03 00:00:00 1970 PST
+  634 |   4 | 00634              | Wed Feb 04 00:00:00 1970 PST
+  636 |   6 | 00636              | Fri Feb 06 00:00:00 1970 PST
+  637 | 407 | 00637_update7      | Sat Feb 07 00:00:00 1970 PST
+  638 |   8 | 00638              | Sun Feb 08 00:00:00 1970 PST
+  639 | 509 | 00639_update9      | Mon Feb 09 00:00:00 1970 PST
+  640 |   0 | 00640              | Tue Feb 10 00:00:00 1970 PST
+  641 |   1 | 00641              | Wed Feb 11 00:00:00 1970 PST
+  643 | 303 | 00643_update3      | Fri Feb 13 00:00:00 1970 PST
+  644 |   4 | 00644              | Sat Feb 14 00:00:00 1970 PST
+  646 |   6 | 00646              | Mon Feb 16 00:00:00 1970 PST
+  647 | 407 | 00647_update7      | Tue Feb 17 00:00:00 1970 PST
+  648 |   8 | 00648              | Wed Feb 18 00:00:00 1970 PST
+  649 | 509 | 00649_update9      | Thu Feb 19 00:00:00 1970 PST
+  650 |   0 | 00650              | Fri Feb 20 00:00:00 1970 PST
+  651 |   1 | 00651              | Sat Feb 21 00:00:00 1970 PST
+  653 | 303 | 00653_update3      | Mon Feb 23 00:00:00 1970 PST
+  654 |   4 | 00654              | Tue Feb 24 00:00:00 1970 PST
+  656 |   6 | 00656              | Thu Feb 26 00:00:00 1970 PST
+  657 | 407 | 00657_update7      | Fri Feb 27 00:00:00 1970 PST
+  658 |   8 | 00658              | Sat Feb 28 00:00:00 1970 PST
+  659 | 509 | 00659_update9      | Sun Mar 01 00:00:00 1970 PST
+  660 |   0 | 00660              | Mon Mar 02 00:00:00 1970 PST
+  661 |   1 | 00661              | Tue Mar 03 00:00:00 1970 PST
+  663 | 303 | 00663_update3      | Thu Mar 05 00:00:00 1970 PST
+  664 |   4 | 00664              | Fri Mar 06 00:00:00 1970 PST
+  666 |   6 | 00666              | Sun Mar 08 00:00:00 1970 PST
+  667 | 407 | 00667_update7      | Mon Mar 09 00:00:00 1970 PST
+  668 |   8 | 00668              | Tue Mar 10 00:00:00 1970 PST
+  669 | 509 | 00669_update9      | Wed Mar 11 00:00:00 1970 PST
+  670 |   0 | 00670              | Thu Mar 12 00:00:00 1970 PST
+  671 |   1 | 00671              | Fri Mar 13 00:00:00 1970 PST
+  673 | 303 | 00673_update3      | Sun Mar 15 00:00:00 1970 PST
+  674 |   4 | 00674              | Mon Mar 16 00:00:00 1970 PST
+  676 |   6 | 00676              | Wed Mar 18 00:00:00 1970 PST
+  677 | 407 | 00677_update7      | Thu Mar 19 00:00:00 1970 PST
+  678 |   8 | 00678              | Fri Mar 20 00:00:00 1970 PST
+  679 | 509 | 00679_update9      | Sat Mar 21 00:00:00 1970 PST
+  680 |   0 | 00680              | Sun Mar 22 00:00:00 1970 PST
+  681 |   1 | 00681              | Mon Mar 23 00:00:00 1970 PST
+  683 | 303 | 00683_update3      | Wed Mar 25 00:00:00 1970 PST
+  684 |   4 | 00684              | Thu Mar 26 00:00:00 1970 PST
+  686 |   6 | 00686              | Sat Mar 28 00:00:00 1970 PST
+  687 | 407 | 00687_update7      | Sun Mar 29 00:00:00 1970 PST
+  688 |   8 | 00688              | Mon Mar 30 00:00:00 1970 PST
+  689 | 509 | 00689_update9      | Tue Mar 31 00:00:00 1970 PST
+  690 |   0 | 00690              | Wed Apr 01 00:00:00 1970 PST
+  691 |   1 | 00691              | Thu Apr 02 00:00:00 1970 PST
+  693 | 303 | 00693_update3      | Sat Apr 04 00:00:00 1970 PST
+  694 |   4 | 00694              | Sun Apr 05 00:00:00 1970 PST
+  696 |   6 | 00696              | Tue Apr 07 00:00:00 1970 PST
+  697 | 407 | 00697_update7      | Wed Apr 08 00:00:00 1970 PST
+  698 |   8 | 00698              | Thu Apr 09 00:00:00 1970 PST
+  699 | 509 | 00699_update9      | Fri Apr 10 00:00:00 1970 PST
+  700 |   0 | 00700              | Thu Jan 01 00:00:00 1970 PST
+  701 |   1 | 00701              | Fri Jan 02 00:00:00 1970 PST
+  703 | 303 | 00703_update3      | Sun Jan 04 00:00:00 1970 PST
+  704 |   4 | 00704              | Mon Jan 05 00:00:00 1970 PST
+  706 |   6 | 00706              | Wed Jan 07 00:00:00 1970 PST
+  707 | 407 | 00707_update7      | Thu Jan 08 00:00:00 1970 PST
+  708 |   8 | 00708              | Fri Jan 09 00:00:00 1970 PST
+  709 | 509 | 00709_update9      | Sat Jan 10 00:00:00 1970 PST
+  710 |   0 | 00710              | Sun Jan 11 00:00:00 1970 PST
+  711 |   1 | 00711              | Mon Jan 12 00:00:00 1970 PST
+  713 | 303 | 00713_update3      | Wed Jan 14 00:00:00 1970 PST
+  714 |   4 | 00714              | Thu Jan 15 00:00:00 1970 PST
+  716 |   6 | 00716              | Sat Jan 17 00:00:00 1970 PST
+  717 | 407 | 00717_update7      | Sun Jan 18 00:00:00 1970 PST
+  718 |   8 | 00718              | Mon Jan 19 00:00:00 1970 PST
+  719 | 509 | 00719_update9      | Tue Jan 20 00:00:00 1970 PST
+  720 |   0 | 00720              | Wed Jan 21 00:00:00 1970 PST
+  721 |   1 | 00721              | Thu Jan 22 00:00:00 1970 PST
+  723 | 303 | 00723_update3      | Sat Jan 24 00:00:00 1970 PST
+  724 |   4 | 00724              | Sun Jan 25 00:00:00 1970 PST
+  726 |   6 | 00726              | Tue Jan 27 00:00:00 1970 PST
+  727 | 407 | 00727_update7      | Wed Jan 28 00:00:00 1970 PST
+  728 |   8 | 00728              | Thu Jan 29 00:00:00 1970 PST
+  729 | 509 | 00729_update9      | Fri Jan 30 00:00:00 1970 PST
+  730 |   0 | 00730              | Sat Jan 31 00:00:00 1970 PST
+  731 |   1 | 00731              | Sun Feb 01 00:00:00 1970 PST
+  733 | 303 | 00733_update3      | Tue Feb 03 00:00:00 1970 PST
+  734 |   4 | 00734              | Wed Feb 04 00:00:00 1970 PST
+  736 |   6 | 00736              | Fri Feb 06 00:00:00 1970 PST
+  737 | 407 | 00737_update7      | Sat Feb 07 00:00:00 1970 PST
+  738 |   8 | 00738              | Sun Feb 08 00:00:00 1970 PST
+  739 | 509 | 00739_update9      | Mon Feb 09 00:00:00 1970 PST
+  740 |   0 | 00740              | Tue Feb 10 00:00:00 1970 PST
+  741 |   1 | 00741              | Wed Feb 11 00:00:00 1970 PST
+  743 | 303 | 00743_update3      | Fri Feb 13 00:00:00 1970 PST
+  744 |   4 | 00744              | Sat Feb 14 00:00:00 1970 PST
+  746 |   6 | 00746              | Mon Feb 16 00:00:00 1970 PST
+  747 | 407 | 00747_update7      | Tue Feb 17 00:00:00 1970 PST
+  748 |   8 | 00748              | Wed Feb 18 00:00:00 1970 PST
+  749 | 509 | 00749_update9      | Thu Feb 19 00:00:00 1970 PST
+  750 |   0 | 00750              | Fri Feb 20 00:00:00 1970 PST
+  751 |   1 | 00751              | Sat Feb 21 00:00:00 1970 PST
+  753 | 303 | 00753_update3      | Mon Feb 23 00:00:00 1970 PST
+  754 |   4 | 00754              | Tue Feb 24 00:00:00 1970 PST
+  756 |   6 | 00756              | Thu Feb 26 00:00:00 1970 PST
+  757 | 407 | 00757_update7      | Fri Feb 27 00:00:00 1970 PST
+  758 |   8 | 00758              | Sat Feb 28 00:00:00 1970 PST
+  759 | 509 | 00759_update9      | Sun Mar 01 00:00:00 1970 PST
+  760 |   0 | 00760              | Mon Mar 02 00:00:00 1970 PST
+  761 |   1 | 00761              | Tue Mar 03 00:00:00 1970 PST
+  763 | 303 | 00763_update3      | Thu Mar 05 00:00:00 1970 PST
+  764 |   4 | 00764              | Fri Mar 06 00:00:00 1970 PST
+  766 |   6 | 00766              | Sun Mar 08 00:00:00 1970 PST
+  767 | 407 | 00767_update7      | Mon Mar 09 00:00:00 1970 PST
+  768 |   8 | 00768              | Tue Mar 10 00:00:00 1970 PST
+  769 | 509 | 00769_update9      | Wed Mar 11 00:00:00 1970 PST
+  770 |   0 | 00770              | Thu Mar 12 00:00:00 1970 PST
+  771 |   1 | 00771              | Fri Mar 13 00:00:00 1970 PST
+  773 | 303 | 00773_update3      | Sun Mar 15 00:00:00 1970 PST
+  774 |   4 | 00774              | Mon Mar 16 00:00:00 1970 PST
+  776 |   6 | 00776              | Wed Mar 18 00:00:00 1970 PST
+  777 | 407 | 00777_update7      | Thu Mar 19 00:00:00 1970 PST
+  778 |   8 | 00778              | Fri Mar 20 00:00:00 1970 PST
+  779 | 509 | 00779_update9      | Sat Mar 21 00:00:00 1970 PST
+  780 |   0 | 00780              | Sun Mar 22 00:00:00 1970 PST
+  781 |   1 | 00781              | Mon Mar 23 00:00:00 1970 PST
+  783 | 303 | 00783_update3      | Wed Mar 25 00:00:00 1970 PST
+  784 |   4 | 00784              | Thu Mar 26 00:00:00 1970 PST
+  786 |   6 | 00786              | Sat Mar 28 00:00:00 1970 PST
+  787 | 407 | 00787_update7      | Sun Mar 29 00:00:00 1970 PST
+  788 |   8 | 00788              | Mon Mar 30 00:00:00 1970 PST
+  789 | 509 | 00789_update9      | Tue Mar 31 00:00:00 1970 PST
+  790 |   0 | 00790              | Wed Apr 01 00:00:00 1970 PST
+  791 |   1 | 00791              | Thu Apr 02 00:00:00 1970 PST
+  793 | 303 | 00793_update3      | Sat Apr 04 00:00:00 1970 PST
+  794 |   4 | 00794              | Sun Apr 05 00:00:00 1970 PST
+  796 |   6 | 00796              | Tue Apr 07 00:00:00 1970 PST
+  797 | 407 | 00797_update7      | Wed Apr 08 00:00:00 1970 PST
+  798 |   8 | 00798              | Thu Apr 09 00:00:00 1970 PST
+  799 | 509 | 00799_update9      | Fri Apr 10 00:00:00 1970 PST
+  800 |   0 | 00800              | Thu Jan 01 00:00:00 1970 PST
+  801 |   1 | 00801              | Fri Jan 02 00:00:00 1970 PST
+  803 | 303 | 00803_update3      | Sun Jan 04 00:00:00 1970 PST
+  804 |   4 | 00804              | Mon Jan 05 00:00:00 1970 PST
+  806 |   6 | 00806              | Wed Jan 07 00:00:00 1970 PST
+  807 | 407 | 00807_update7      | Thu Jan 08 00:00:00 1970 PST
+  808 |   8 | 00808              | Fri Jan 09 00:00:00 1970 PST
+  809 | 509 | 00809_update9      | Sat Jan 10 00:00:00 1970 PST
+  810 |   0 | 00810              | Sun Jan 11 00:00:00 1970 PST
+  811 |   1 | 00811              | Mon Jan 12 00:00:00 1970 PST
+  813 | 303 | 00813_update3      | Wed Jan 14 00:00:00 1970 PST
+  814 |   4 | 00814              | Thu Jan 15 00:00:00 1970 PST
+  816 |   6 | 00816              | Sat Jan 17 00:00:00 1970 PST
+  817 | 407 | 00817_update7      | Sun Jan 18 00:00:00 1970 PST
+  818 |   8 | 00818              | Mon Jan 19 00:00:00 1970 PST
+  819 | 509 | 00819_update9      | Tue Jan 20 00:00:00 1970 PST
+  820 |   0 | 00820              | Wed Jan 21 00:00:00 1970 PST
+  821 |   1 | 00821              | Thu Jan 22 00:00:00 1970 PST
+  823 | 303 | 00823_update3      | Sat Jan 24 00:00:00 1970 PST
+  824 |   4 | 00824              | Sun Jan 25 00:00:00 1970 PST
+  826 |   6 | 00826              | Tue Jan 27 00:00:00 1970 PST
+  827 | 407 | 00827_update7      | Wed Jan 28 00:00:00 1970 PST
+  828 |   8 | 00828              | Thu Jan 29 00:00:00 1970 PST
+  829 | 509 | 00829_update9      | Fri Jan 30 00:00:00 1970 PST
+  830 |   0 | 00830              | Sat Jan 31 00:00:00 1970 PST
+  831 |   1 | 00831              | Sun Feb 01 00:00:00 1970 PST
+  833 | 303 | 00833_update3      | Tue Feb 03 00:00:00 1970 PST
+  834 |   4 | 00834              | Wed Feb 04 00:00:00 1970 PST
+  836 |   6 | 00836              | Fri Feb 06 00:00:00 1970 PST
+  837 | 407 | 00837_update7      | Sat Feb 07 00:00:00 1970 PST
+  838 |   8 | 00838              | Sun Feb 08 00:00:00 1970 PST
+  839 | 509 | 00839_update9      | Mon Feb 09 00:00:00 1970 PST
+  840 |   0 | 00840              | Tue Feb 10 00:00:00 1970 PST
+  841 |   1 | 00841              | Wed Feb 11 00:00:00 1970 PST
+  843 | 303 | 00843_update3      | Fri Feb 13 00:00:00 1970 PST
+  844 |   4 | 00844              | Sat Feb 14 00:00:00 1970 PST
+  846 |   6 | 00846              | Mon Feb 16 00:00:00 1970 PST
+  847 | 407 | 00847_update7      | Tue Feb 17 00:00:00 1970 PST
+  848 |   8 | 00848              | Wed Feb 18 00:00:00 1970 PST
+  849 | 509 | 00849_update9      | Thu Feb 19 00:00:00 1970 PST
+  850 |   0 | 00850              | Fri Feb 20 00:00:00 1970 PST
+  851 |   1 | 00851              | Sat Feb 21 00:00:00 1970 PST
+  853 | 303 | 00853_update3      | Mon Feb 23 00:00:00 1970 PST
+  854 |   4 | 00854              | Tue Feb 24 00:00:00 1970 PST
+  856 |   6 | 00856              | Thu Feb 26 00:00:00 1970 PST
+  857 | 407 | 00857_update7      | Fri Feb 27 00:00:00 1970 PST
+  858 |   8 | 00858              | Sat Feb 28 00:00:00 1970 PST
+  859 | 509 | 00859_update9      | Sun Mar 01 00:00:00 1970 PST
+  860 |   0 | 00860              | Mon Mar 02 00:00:00 1970 PST
+  861 |   1 | 00861              | Tue Mar 03 00:00:00 1970 PST
+  863 | 303 | 00863_update3      | Thu Mar 05 00:00:00 1970 PST
+  864 |   4 | 00864              | Fri Mar 06 00:00:00 1970 PST
+  866 |   6 | 00866              | Sun Mar 08 00:00:00 1970 PST
+  867 | 407 | 00867_update7      | Mon Mar 09 00:00:00 1970 PST
+  868 |   8 | 00868              | Tue Mar 10 00:00:00 1970 PST
+  869 | 509 | 00869_update9      | Wed Mar 11 00:00:00 1970 PST
+  870 |   0 | 00870              | Thu Mar 12 00:00:00 1970 PST
+  871 |   1 | 00871              | Fri Mar 13 00:00:00 1970 PST
+  873 | 303 | 00873_update3      | Sun Mar 15 00:00:00 1970 PST
+  874 |   4 | 00874              | Mon Mar 16 00:00:00 1970 PST
+  876 |   6 | 00876              | Wed Mar 18 00:00:00 1970 PST
+  877 | 407 | 00877_update7      | Thu Mar 19 00:00:00 1970 PST
+  878 |   8 | 00878              | Fri Mar 20 00:00:00 1970 PST
+  879 | 509 | 00879_update9      | Sat Mar 21 00:00:00 1970 PST
+  880 |   0 | 00880              | Sun Mar 22 00:00:00 1970 PST
+  881 |   1 | 00881              | Mon Mar 23 00:00:00 1970 PST
+  883 | 303 | 00883_update3      | Wed Mar 25 00:00:00 1970 PST
+  884 |   4 | 00884              | Thu Mar 26 00:00:00 1970 PST
+  886 |   6 | 00886              | Sat Mar 28 00:00:00 1970 PST
+  887 | 407 | 00887_update7      | Sun Mar 29 00:00:00 1970 PST
+  888 |   8 | 00888              | Mon Mar 30 00:00:00 1970 PST
+  889 | 509 | 00889_update9      | Tue Mar 31 00:00:00 1970 PST
+  890 |   0 | 00890              | Wed Apr 01 00:00:00 1970 PST
+  891 |   1 | 00891              | Thu Apr 02 00:00:00 1970 PST
+  893 | 303 | 00893_update3      | Sat Apr 04 00:00:00 1970 PST
+  894 |   4 | 00894              | Sun Apr 05 00:00:00 1970 PST
+  896 |   6 | 00896              | Tue Apr 07 00:00:00 1970 PST
+  897 | 407 | 00897_update7      | Wed Apr 08 00:00:00 1970 PST
+  898 |   8 | 00898              | Thu Apr 09 00:00:00 1970 PST
+  899 | 509 | 00899_update9      | Fri Apr 10 00:00:00 1970 PST
+  900 |   0 | 00900              | Thu Jan 01 00:00:00 1970 PST
+  901 |   1 | 00901              | Fri Jan 02 00:00:00 1970 PST
+  903 | 303 | 00903_update3      | Sun Jan 04 00:00:00 1970 PST
+  904 |   4 | 00904              | Mon Jan 05 00:00:00 1970 PST
+  906 |   6 | 00906              | Wed Jan 07 00:00:00 1970 PST
+  907 | 407 | 00907_update7      | Thu Jan 08 00:00:00 1970 PST
+  908 |   8 | 00908              | Fri Jan 09 00:00:00 1970 PST
+  909 | 509 | 00909_update9      | Sat Jan 10 00:00:00 1970 PST
+  910 |   0 | 00910              | Sun Jan 11 00:00:00 1970 PST
+  911 |   1 | 00911              | Mon Jan 12 00:00:00 1970 PST
+  913 | 303 | 00913_update3      | Wed Jan 14 00:00:00 1970 PST
+  914 |   4 | 00914              | Thu Jan 15 00:00:00 1970 PST
+  916 |   6 | 00916              | Sat Jan 17 00:00:00 1970 PST
+  917 | 407 | 00917_update7      | Sun Jan 18 00:00:00 1970 PST
+  918 |   8 | 00918              | Mon Jan 19 00:00:00 1970 PST
+  919 | 509 | 00919_update9      | Tue Jan 20 00:00:00 1970 PST
+  920 |   0 | 00920              | Wed Jan 21 00:00:00 1970 PST
+  921 |   1 | 00921              | Thu Jan 22 00:00:00 1970 PST
+  923 | 303 | 00923_update3      | Sat Jan 24 00:00:00 1970 PST
+  924 |   4 | 00924              | Sun Jan 25 00:00:00 1970 PST
+  926 |   6 | 00926              | Tue Jan 27 00:00:00 1970 PST
+  927 | 407 | 00927_update7      | Wed Jan 28 00:00:00 1970 PST
+  928 |   8 | 00928              | Thu Jan 29 00:00:00 1970 PST
+  929 | 509 | 00929_update9      | Fri Jan 30 00:00:00 1970 PST
+  930 |   0 | 00930              | Sat Jan 31 00:00:00 1970 PST
+  931 |   1 | 00931              | Sun Feb 01 00:00:00 1970 PST
+  933 | 303 | 00933_update3      | Tue Feb 03 00:00:00 1970 PST
+  934 |   4 | 00934              | Wed Feb 04 00:00:00 1970 PST
+  936 |   6 | 00936              | Fri Feb 06 00:00:00 1970 PST
+  937 | 407 | 00937_update7      | Sat Feb 07 00:00:00 1970 PST
+  938 |   8 | 00938              | Sun Feb 08 00:00:00 1970 PST
+  939 | 509 | 00939_update9      | Mon Feb 09 00:00:00 1970 PST
+  940 |   0 | 00940              | Tue Feb 10 00:00:00 1970 PST
+  941 |   1 | 00941              | Wed Feb 11 00:00:00 1970 PST
+  943 | 303 | 00943_update3      | Fri Feb 13 00:00:00 1970 PST
+  944 |   4 | 00944              | Sat Feb 14 00:00:00 1970 PST
+  946 |   6 | 00946              | Mon Feb 16 00:00:00 1970 PST
+  947 | 407 | 00947_update7      | Tue Feb 17 00:00:00 1970 PST
+  948 |   8 | 00948              | Wed Feb 18 00:00:00 1970 PST
+  949 | 509 | 00949_update9      | Thu Feb 19 00:00:00 1970 PST
+  950 |   0 | 00950              | Fri Feb 20 00:00:00 1970 PST
+  951 |   1 | 00951              | Sat Feb 21 00:00:00 1970 PST
+  953 | 303 | 00953_update3      | Mon Feb 23 00:00:00 1970 PST
+  954 |   4 | 00954              | Tue Feb 24 00:00:00 1970 PST
+  956 |   6 | 00956              | Thu Feb 26 00:00:00 1970 PST
+  957 | 407 | 00957_update7      | Fri Feb 27 00:00:00 1970 PST
+  958 |   8 | 00958              | Sat Feb 28 00:00:00 1970 PST
+  959 | 509 | 00959_update9      | Sun Mar 01 00:00:00 1970 PST
+  960 |   0 | 00960              | Mon Mar 02 00:00:00 1970 PST
+  961 |   1 | 00961              | Tue Mar 03 00:00:00 1970 PST
+  963 | 303 | 00963_update3      | Thu Mar 05 00:00:00 1970 PST
+  964 |   4 | 00964              | Fri Mar 06 00:00:00 1970 PST
+  966 |   6 | 00966              | Sun Mar 08 00:00:00 1970 PST
+  967 | 407 | 00967_update7      | Mon Mar 09 00:00:00 1970 PST
+  968 |   8 | 00968              | Tue Mar 10 00:00:00 1970 PST
+  969 | 509 | 00969_update9      | Wed Mar 11 00:00:00 1970 PST
+  970 |   0 | 00970              | Thu Mar 12 00:00:00 1970 PST
+  971 |   1 | 00971              | Fri Mar 13 00:00:00 1970 PST
+  973 | 303 | 00973_update3      | Sun Mar 15 00:00:00 1970 PST
+  974 |   4 | 00974              | Mon Mar 16 00:00:00 1970 PST
+  976 |   6 | 00976              | Wed Mar 18 00:00:00 1970 PST
+  977 | 407 | 00977_update7      | Thu Mar 19 00:00:00 1970 PST
+  978 |   8 | 00978              | Fri Mar 20 00:00:00 1970 PST
+  979 | 509 | 00979_update9      | Sat Mar 21 00:00:00 1970 PST
+  980 |   0 | 00980              | Sun Mar 22 00:00:00 1970 PST
+  981 |   1 | 00981              | Mon Mar 23 00:00:00 1970 PST
+  983 | 303 | 00983_update3      | Wed Mar 25 00:00:00 1970 PST
+  984 |   4 | 00984              | Thu Mar 26 00:00:00 1970 PST
+  986 |   6 | 00986              | Sat Mar 28 00:00:00 1970 PST
+  987 | 407 | 00987_update7      | Sun Mar 29 00:00:00 1970 PST
+  988 |   8 | 00988              | Mon Mar 30 00:00:00 1970 PST
+  989 | 509 | 00989_update9      | Tue Mar 31 00:00:00 1970 PST
+  990 |   0 | 00990              | Wed Apr 01 00:00:00 1970 PST
+  991 |   1 | 00991              | Thu Apr 02 00:00:00 1970 PST
+  993 | 303 | 00993_update3      | Sat Apr 04 00:00:00 1970 PST
+  994 |   4 | 00994              | Sun Apr 05 00:00:00 1970 PST
+  996 |   6 | 00996              | Tue Apr 07 00:00:00 1970 PST
+  997 | 407 | 00997_update7      | Wed Apr 08 00:00:00 1970 PST
+  998 |   8 | 00998              | Thu Apr 09 00:00:00 1970 PST
+  999 | 509 | 00999_update9      | Fri Apr 10 00:00:00 1970 PST
+ 1000 |   0 | 01000              | Thu Jan 01 00:00:00 1970 PST
+ 1001 | 101 | 0000100001         | 
+ 1003 | 403 | 0000300003_update3 | 
+ 1004 | 104 | 0000400004         | 
+ 1006 | 106 | 0000600006         | 
+ 1007 | 507 | 0000700007_update7 | 
+ 1008 | 108 | 0000800008         | 
+ 1009 | 609 | 0000900009_update9 | 
+ 1010 | 100 | 0001000010         | 
+ 1011 | 101 | 0001100011         | 
+ 1013 | 403 | 0001300013_update3 | 
+ 1014 | 104 | 0001400014         | 
+ 1016 | 106 | 0001600016         | 
+ 1017 | 507 | 0001700017_update7 | 
+ 1018 | 108 | 0001800018         | 
+ 1019 | 609 | 0001900019_update9 | 
+ 1020 | 100 | 0002000020         | 
+ 1101 | 201 | aaa                | 
+ 1103 | 503 | ccc_update3        | 
+ 1104 | 204 | ddd                | 
+(819 rows)
+
+-- ===================================================================
 -- cleanup
 -- ===================================================================
 DROP OPERATOR === (int, int) CASCADE;
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 6b870ab..637f31c 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -59,6 +59,7 @@ typedef struct PostgresFdwPlanState {
 	List		   *param_conds;
 	List		   *local_conds;
 	int				width;			/* obtained by remote EXPLAIN */
+	AttrNumber		anum_rowid;
 
 	/* Cached catalog information. */
 	ForeignTable   *table;
@@ -150,6 +151,20 @@ typedef struct PostgresAnalyzeState
 } PostgresAnalyzeState;
 
 /*
+ * Describes a state of modify request for a foreign table
+ */
+typedef struct PostgresFdwModifyState
+{
+	PGconn	   *conn;
+	char	   *query;
+	char	   *p_name;
+	int			p_nums;
+	Oid		   *p_types;
+	FmgrInfo   *p_flinfo;
+	MemoryContext	es_query_cxt;
+} PostgresFdwModifyState;
+
+/*
  * SQL functions
  */
 extern Datum postgres_fdw_handler(PG_FUNCTION_ARGS);
@@ -158,6 +173,11 @@ PG_FUNCTION_INFO_V1(postgres_fdw_handler);
 /*
  * FDW callback routines
  */
+static AttrNumber postgresGetForeignRelWidth(PlannerInfo *root,
+											 RelOptInfo *baserel,
+											 Relation foreignrel,
+											 bool inhparent,
+											 List *targetList);
 static void postgresGetForeignRelSize(PlannerInfo *root,
 									  RelOptInfo *baserel,
 									  Oid foreigntableid);
@@ -179,6 +199,23 @@ static void postgresEndForeignScan(ForeignScanState *node);
 static bool postgresAnalyzeForeignTable(Relation relation,
 										AcquireSampleRowsFunc *func,
 										BlockNumber *totalpages);
+static List *postgresPlanForeignModify(PlannerInfo *root,
+									   ModifyTable *plan,
+									   Index resultRelation,
+									   Plan *subplan);
+static void postgresBeginForeignModify(ModifyTableState *mtstate,
+									   ResultRelInfo *resultRelInfo,
+									   List *fdw_private,
+									   Plan *subplan,
+									   int eflags);
+static int postgresExecForeignInsert(ResultRelInfo *resultRelInfo,
+									 HeapTuple tuple);
+static int postgresExecForeignDelete(ResultRelInfo *resultRelInfo,
+									 Datum rowid);
+static int postgresExecForeignUpdate(ResultRelInfo *resultRelInfo,
+									 Datum rowid,
+									 HeapTuple tuple);
+static void postgresEndForeignModify(ResultRelInfo *resultRelInfo);
 
 /*
  * Helper functions
@@ -231,6 +268,7 @@ postgres_fdw_handler(PG_FUNCTION_ARGS)
 	FdwRoutine	*routine = makeNode(FdwRoutine);
 
 	/* Required handler functions. */
+	routine->GetForeignRelWidth = postgresGetForeignRelWidth;
 	routine->GetForeignRelSize = postgresGetForeignRelSize;
 	routine->GetForeignPaths = postgresGetForeignPaths;
 	routine->GetForeignPlan = postgresGetForeignPlan;
@@ -239,6 +277,12 @@ postgres_fdw_handler(PG_FUNCTION_ARGS)
 	routine->IterateForeignScan = postgresIterateForeignScan;
 	routine->ReScanForeignScan = postgresReScanForeignScan;
 	routine->EndForeignScan = postgresEndForeignScan;
+	routine->PlanForeignModify = postgresPlanForeignModify;
+	routine->BeginForeignModify = postgresBeginForeignModify;
+	routine->ExecForeignInsert = postgresExecForeignInsert;
+	routine->ExecForeignDelete = postgresExecForeignDelete;
+	routine->ExecForeignUpdate = postgresExecForeignUpdate;
+	routine->EndForeignModify = postgresEndForeignModify;
 
 	/* Optional handler functions. */
 	routine->AnalyzeForeignTable = postgresAnalyzeForeignTable;
@@ -247,6 +291,29 @@ postgres_fdw_handler(PG_FUNCTION_ARGS)
 }
 
 /*
+ * postgresGetForeignRelWidth
+ *		Informs how many columns (including pseudo ones) are needed.
+ */
+static AttrNumber
+postgresGetForeignRelWidth(PlannerInfo *root,
+						   RelOptInfo *baserel,
+						   Relation foreignrel,
+						   bool inhparent,
+						   List *targetList)
+{
+	PostgresFdwPlanState *fpstate = palloc0(sizeof(PostgresFdwPlanState));
+
+	baserel->fdw_private = fpstate;
+
+	/* does rowid pseudo-column is required? */
+	fpstate->anum_rowid = get_pseudo_rowid_column(baserel, targetList);
+	if (fpstate->anum_rowid != InvalidAttrNumber)
+		return fpstate->anum_rowid;
+
+	return RelationGetNumberOfAttributes(foreignrel);
+}
+
+/*
  * postgresGetForeignRelSize
  *		Estimate # of rows and width of the result of the scan
  *
@@ -283,7 +350,7 @@ postgresGetForeignRelSize(PlannerInfo *root,
 	 * We use PostgresFdwPlanState to pass various information to subsequent
 	 * functions.
 	 */
-	fpstate = palloc0(sizeof(PostgresFdwPlanState));
+	fpstate = baserel->fdw_private;
 	initStringInfo(&fpstate->sql);
 	sql = &fpstate->sql;
 
@@ -320,10 +387,9 @@ postgresGetForeignRelSize(PlannerInfo *root,
 	 */
 	classifyConditions(root, baserel, &remote_conds, &param_conds,
 					   &local_conds);
-	deparseSimpleSql(sql, root, baserel, local_conds);
+	deparseSimpleSql(sql, root, baserel, local_conds, fpstate->anum_rowid);
 	if (list_length(remote_conds) > 0)
 		appendWhereClause(sql, true, remote_conds, root);
-	elog(DEBUG3, "Query SQL: %s", sql->data);
 
 	/*
 	 * If the table or the server is configured to use remote EXPLAIN, connect
@@ -337,10 +403,10 @@ postgresGetForeignRelSize(PlannerInfo *root,
 		PGconn		   *conn;
 
 		user = GetUserMapping(GetOuterUserId(), server->serverid);
-		conn = GetConnection(server, user, false);
+		conn = GetConnection(server, user, PGSQL_FDW_CONNTX_NONE);
 		get_remote_estimate(sql->data, conn, &rows, &width,
 							&startup_cost, &total_cost);
-		ReleaseConnection(conn);
+		ReleaseConnection(conn, false);
 
 		/*
 		 * Estimate selectivity of conditions which are not used in remote
@@ -391,7 +457,6 @@ postgresGetForeignRelSize(PlannerInfo *root,
 	fpstate->width = width;
 	fpstate->table = table;
 	fpstate->server = server;
-	baserel->fdw_private = (void *) fpstate;
 }
 
 /*
@@ -592,7 +657,7 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags)
 	table = GetForeignTable(relid);
 	server = GetForeignServer(table->serverid);
 	user = GetUserMapping(GetOuterUserId(), server->serverid);
-	conn = GetConnection(server, user, true);
+	conn = GetConnection(server, user, PGSQL_FDW_CONNTX_READ_ONLY);
 	festate->conn = conn;
 
 	/* Result will be filled in first Iterate call. */
@@ -724,7 +789,7 @@ postgresEndForeignScan(ForeignScanState *node)
 	 * end of the scan to make the lifespan of remote transaction same as the
 	 * local query.
 	 */
-	ReleaseConnection(festate->conn);
+	ReleaseConnection(festate->conn, false);
 	festate->conn = NULL;
 
 	/* Discard fetch results */
@@ -790,7 +855,7 @@ get_remote_estimate(const char *sql, PGconn *conn,
 		PQclear(res);
 
 		/* Release connection and let connection manager cleanup. */
-		ReleaseConnection(conn);
+		ReleaseConnection(conn, true);
 
 		PG_RE_THROW();
 	}
@@ -947,7 +1012,7 @@ execute_query(ForeignScanState *node)
 		PQclear(res);
 
 		/* Release connection and let connection manager cleanup. */
-		ReleaseConnection(conn);
+		ReleaseConnection(conn, true);
 
 		/* propagate error */
 		PG_RE_THROW();
@@ -1105,6 +1170,8 @@ postgres_fdw_error_callback(void *arg)
 
 	relname = get_rel_name(errpos->relid);
 	colname = get_attname(errpos->relid, errpos->cur_attno);
+	if (!colname)
+		colname = "pseudo-column";
 	errcontext("column %s of foreign table %s",
 			   quote_identifier(colname), quote_identifier(relname));
 }
@@ -1172,7 +1239,7 @@ postgresAcquireSampleRowsFunc(Relation relation, int elevel,
 	table = GetForeignTable(relation->rd_id);
 	server = GetForeignServer(table->serverid);
 	user = GetUserMapping(GetOuterUserId(), server->serverid);
-	conn = GetConnection(server, user, true);
+	conn = GetConnection(server, user, PGSQL_FDW_CONNTX_READ_ONLY);
 
 	/*
 	 * Acquire sample rows from the result set.
@@ -1239,13 +1306,13 @@ postgresAcquireSampleRowsFunc(Relation relation, int elevel,
 		PQclear(res);
 
 		/* Release connection and let connection manager cleanup. */
-		ReleaseConnection(conn);
+		ReleaseConnection(conn, true);
 
 		PG_RE_THROW();
 	}
 	PG_END_TRY();
 
-	ReleaseConnection(conn);
+	ReleaseConnection(conn, false);
 
 	/* We assume that we have no dead tuple. */
 	*totaldeadrows = 0.0;
@@ -1429,3 +1496,318 @@ analyze_row_processor(PGresult *res, PostgresAnalyzeState *astate, bool first)
 
 	return;
 }
+
+static List *
+postgresPlanForeignModify(PlannerInfo *root,
+						  ModifyTable *plan,
+						  Index resultRelaion,
+						  Plan *subplan)
+{
+	CmdType			operation = plan->operation;
+	StringInfoData	sql;
+
+	initStringInfo(&sql);
+
+	/*
+	 * XXX - In case of UPDATE or DELETE commands are quite "simple",
+	 * we will be able to execute raw UPDATE or DELETE statement at
+	 * the stage of scan, instead of combination SELECT ... FOR UPDATE
+	 * and either of UPDATE or DELETE commands.
+	 * It should be an idea of optimization in the future version.
+	 *
+	 * XXX - FOR UPDATE should be appended on the remote query of scan
+	 * stage to avoid unexpected concurrent update on the target rows.
+	 */
+	if (operation == CMD_UPDATE || operation == CMD_DELETE)
+	{
+		ForeignScan	   *fscan;
+		Value		   *select_sql;
+
+		fscan = lookup_foreign_scan_plan(subplan, resultRelaion);
+		if (!fscan)
+			elog(ERROR, "no underlying scan plan found in subplan tree");
+
+		select_sql = list_nth(fscan->fdw_private,
+							  FdwPrivateSelectSql);
+		appendStringInfo(&sql, "%s FOR UPDATE", strVal(select_sql));
+		strVal(select_sql) = pstrdup(sql.data);
+
+		resetStringInfo(&sql);
+	}
+
+	switch (operation)
+	{
+		case CMD_INSERT:
+			deparseInsertSql(&sql, root, resultRelaion);
+			elog(DEBUG3, "Remote INSERT query: %s", sql.data);
+			break;
+		case CMD_UPDATE:
+			deparseUpdateSql(&sql, root, resultRelaion);
+			elog(DEBUG3, "Remote UPDATE query: %s", sql.data);
+			break;
+		case CMD_DELETE:
+			deparseDeleteSql(&sql, root, resultRelaion);
+			elog(DEBUG3, "Remote DELETE query: %s", sql.data);
+			break;
+		default:
+			elog(ERROR, "unexpected operation: %d", (int) operation);
+	}
+	return list_make1(makeString(sql.data));
+}
+
+static void
+postgresBeginForeignModify(ModifyTableState *mtstate,
+						   ResultRelInfo *resultRelInfo,
+						   List *fdw_private,
+						   Plan *subplan,
+						   int eflags)
+{
+	PostgresFdwModifyState *fmstate;
+	CmdType			operation = mtstate->operation;
+	Relation		frel = resultRelInfo->ri_RelationDesc;
+	AttrNumber		n_params = RelationGetNumberOfAttributes(frel) + 1;
+	ForeignTable   *ftable;
+	ForeignServer  *fserver;
+	UserMapping	   *fuser;
+	Oid				out_func_oid;
+	bool			isvarlena;
+
+	/*
+	 * Construct PostgresFdwExecutionState
+	 */
+	fmstate = palloc0(sizeof(PostgresFdwExecutionState));
+
+	ftable = GetForeignTable(RelationGetRelid(frel));
+	fserver = GetForeignServer(ftable->serverid);
+	fuser = GetUserMapping(GetOuterUserId(), fserver->serverid);
+
+	fmstate->query = strVal(linitial(fdw_private));
+	fmstate->conn = GetConnection(fserver, fuser,
+								  PGSQL_FDW_CONNTX_READ_WRITE);
+	fmstate->p_name = NULL;
+	fmstate->p_types = palloc0(sizeof(Oid) * n_params);
+	fmstate->p_flinfo = palloc0(sizeof(FmgrInfo) * n_params);
+
+	/* 1st parameter should be ctid on UPDATE or DELETE */
+	if (operation == CMD_UPDATE || operation == CMD_DELETE)
+	{
+		fmstate->p_types[fmstate->p_nums] = TIDOID;
+		getTypeOutputInfo(TIDOID, &out_func_oid, &isvarlena);
+		fmgr_info(out_func_oid, &fmstate->p_flinfo[fmstate->p_nums]);
+		fmstate->p_nums++;
+	}
+	/* following parameters should be regular columns */
+	if (operation == CMD_UPDATE || operation == CMD_INSERT)
+	{
+		AttrNumber	index;
+
+		for (index=0; index < RelationGetNumberOfAttributes(frel); index++)
+		{
+			Form_pg_attribute	attr = RelationGetDescr(frel)->attrs[index];
+
+			if (attr->attisdropped)
+				continue;
+
+			fmstate->p_types[fmstate->p_nums] = attr->atttypid;
+			getTypeOutputInfo(attr->atttypid, &out_func_oid, &isvarlena);
+			fmgr_info(out_func_oid, &fmstate->p_flinfo[fmstate->p_nums]);
+			fmstate->p_nums++;
+		}
+	}
+	Assert(fmstate->p_nums <= n_params);
+	fmstate->es_query_cxt = mtstate->ps.state->es_query_cxt;
+	resultRelInfo->ri_fdw_state = fmstate;
+}
+
+static void
+prepare_foreign_modify(PostgresFdwModifyState *fmstate)
+{
+	static int	prep_id = 1;
+	char		prep_name[NAMEDATALEN];
+	PGresult   *res;
+
+	snprintf(prep_name, sizeof(prep_name),
+			 "pgsql_fdw_prep_%08x", prep_id++);
+
+	res = PQprepare(fmstate->conn,
+					prep_name,
+					fmstate->query,
+					fmstate->p_nums,
+					fmstate->p_types);
+	if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
+	{
+		PQclear(res);
+		elog(ERROR, "could not prepare statement (%s): %s",
+			 fmstate->query, PQerrorMessage(fmstate->conn));
+	}
+	PQclear(res);
+
+	fmstate->p_name = MemoryContextStrdup(fmstate->es_query_cxt, prep_name);
+}
+
+static int
+setup_exec_prepared(ResultRelInfo *resultRelInfo,
+					const char *rowid, HeapTuple tuple,
+					const char *p_values[], int p_lengths[])
+{
+	PostgresFdwModifyState *fmstate = resultRelInfo->ri_fdw_state;
+	Relation	frel = resultRelInfo->ri_RelationDesc;
+	TupleDesc	tupdesc = RelationGetDescr(frel);
+	int			i, p_idx = 0;
+
+	/* 1st parameter should be ctid */
+	if (rowid)
+	{
+		p_values[p_idx] = rowid;
+		p_lengths[p_idx] = strlen(rowid) + 1;
+		p_idx++;
+	}
+
+	/* following parameters are as TupleDesc */
+	if (HeapTupleIsValid(tuple))
+	{
+		for (i = 0; i < tupdesc->natts; i++)
+		{
+			Form_pg_attribute	attr = tupdesc->attrs[i];
+			Datum		value;
+			bool		isnull;
+
+			if (attr->attisdropped)
+				continue;
+
+			value = heap_getattr(tuple, attr->attnum, tupdesc, &isnull);
+			if (isnull)
+			{
+				p_values[p_idx] = NULL;
+				p_lengths[p_idx] = 0;
+			}
+			else
+			{
+				p_values[p_idx] =
+					OutputFunctionCall(&fmstate->p_flinfo[p_idx], value);
+				p_lengths[p_idx] = strlen(p_values[p_idx]) + 1;
+			}
+			p_idx++;
+		}
+	}
+	return p_idx;
+}
+
+static int
+postgresExecForeignInsert(ResultRelInfo *resultRelInfo,
+						  HeapTuple tuple)
+{
+	PostgresFdwModifyState *fmstate = resultRelInfo->ri_fdw_state;
+	const char	  **p_values  = alloca(sizeof(char *) * fmstate->p_nums);
+	int			   *p_lengths = alloca(sizeof(int) * fmstate->p_nums);
+	AttrNumber		nattrs;
+	PGresult	   *res;
+	int				n_rows;
+
+	if (!fmstate->p_name)
+		prepare_foreign_modify(fmstate);
+
+	nattrs = setup_exec_prepared(resultRelInfo,
+								 NULL, tuple,
+								 p_values, p_lengths);
+	Assert(fmstate->p_nums == nattrs);
+
+	res = PQexecPrepared(fmstate->conn,
+						 fmstate->p_name,
+						 nattrs,
+						 p_values,
+						 p_lengths,
+						 NULL, 0);
+	if (!res ||  PQresultStatus(res) != PGRES_COMMAND_OK)
+	{
+		PQclear(res);
+		elog(ERROR, "could not execute prepared statement (%s): %s",
+			 fmstate->query, PQerrorMessage(fmstate->conn));
+    }
+	n_rows = atoi(PQcmdTuples(res));
+    PQclear(res);
+
+	return n_rows;
+}
+
+static int
+postgresExecForeignDelete(ResultRelInfo *resultRelInfo, Datum rowid)
+{
+	PostgresFdwModifyState *fmstate = resultRelInfo->ri_fdw_state;
+	const char	   *p_values[1];
+	int				p_lengths[1];
+	AttrNumber		nattrs;
+	PGresult	   *res;
+	int				n_rows;
+
+	if (!fmstate->p_name)
+		prepare_foreign_modify(fmstate);
+
+	nattrs = setup_exec_prepared(resultRelInfo,
+								 DatumGetCString(rowid), NULL,
+								 p_values, p_lengths);
+	Assert(fmstate->p_nums == nattrs);
+
+	res = PQexecPrepared(fmstate->conn,
+						 fmstate->p_name,
+						 nattrs,
+						 p_values,
+						 p_lengths,
+						 NULL, 0);
+	if (!res ||  PQresultStatus(res) != PGRES_COMMAND_OK)
+	{
+		PQclear(res);
+		elog(ERROR, "could not execute prepared statement (%s): %s",
+			 fmstate->query, PQerrorMessage(fmstate->conn));
+    }
+	n_rows = atoi(PQcmdTuples(res));
+    PQclear(res);
+
+	return n_rows;
+}
+
+static int
+postgresExecForeignUpdate(ResultRelInfo *resultRelInfo,
+						  Datum rowid, HeapTuple tuple)
+{
+	PostgresFdwModifyState *fmstate = resultRelInfo->ri_fdw_state;
+	const char	  **p_values  = alloca(sizeof(char *) * (fmstate->p_nums + 1));
+	int			   *p_lengths = alloca(sizeof(int) * (fmstate->p_nums + 1));
+	AttrNumber		nattrs;
+	PGresult	   *res;
+	int				n_rows;
+
+	if (!fmstate->p_name)
+		prepare_foreign_modify(fmstate);
+
+	nattrs = setup_exec_prepared(resultRelInfo,
+								 DatumGetCString(rowid), tuple,
+								 p_values, p_lengths);
+	Assert(fmstate->p_nums == nattrs);
+
+	res = PQexecPrepared(fmstate->conn,
+						 fmstate->p_name,
+						 nattrs,
+						 p_values,
+						 p_lengths,
+						 NULL, 0);
+	if (!res ||  PQresultStatus(res) != PGRES_COMMAND_OK)
+	{
+		PQclear(res);
+		elog(ERROR, "could not execute prepared statement (%s): %s",
+			 fmstate->query, PQerrorMessage(fmstate->conn));
+    }
+	n_rows = atoi(PQcmdTuples(res));
+    PQclear(res);
+
+	return n_rows;
+}
+
+static void
+postgresEndForeignModify(ResultRelInfo *resultRelInfo)
+{
+	PostgresFdwModifyState *fmstate = resultRelInfo->ri_fdw_state;
+
+	ReleaseConnection(fmstate->conn, false);
+	fmstate->conn = NULL;
+}
diff --git a/contrib/postgres_fdw/postgres_fdw.h b/contrib/postgres_fdw/postgres_fdw.h
index b5cefb8..3756a0d 100644
--- a/contrib/postgres_fdw/postgres_fdw.h
+++ b/contrib/postgres_fdw/postgres_fdw.h
@@ -30,7 +30,8 @@ int GetFetchCountOption(ForeignTable *table, ForeignServer *server);
 void deparseSimpleSql(StringInfo buf,
 					  PlannerInfo *root,
 					  RelOptInfo *baserel,
-					  List *local_conds);
+					  List *local_conds,
+					  AttrNumber anum_rowid);
 void appendWhereClause(StringInfo buf,
 					   bool has_where,
 					   List *exprs,
@@ -41,5 +42,8 @@ void classifyConditions(PlannerInfo *root,
 						List **param_conds,
 						List **local_conds);
 void deparseAnalyzeSql(StringInfo buf, Relation rel);
+void deparseInsertSql(StringInfo buf, PlannerInfo *root, Index rtindex);
+void deparseUpdateSql(StringInfo buf, PlannerInfo *root, Index rtindex);
+void deparseDeleteSql(StringInfo buf, PlannerInfo *root, Index rtindex);
 
 #endif /* POSTGRESQL_FDW_H */
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index 7845e70..70b09b4 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -300,6 +300,19 @@ ERROR OUT;          -- ERROR
 SELECT srvname FROM postgres_fdw_connections;
 
 -- ===================================================================
+-- test for writable foreign table stuff (PoC stage now)
+-- ===================================================================
+INSERT INTO ft2 (c1,c2,c3) (SELECT c1+1000,c2+100, c3 || c3 FROM ft2 LIMIT 20);
+INSERT INTO ft2 (c1,c2,c3) VALUES (1101,201,'aaa'), (1102,202,'bbb'),(1103,203,'ccc') RETURNING *;
+INSERT INTO ft2 (c1,c2,c3) VALUES (1104,204,'ddd'), (1105,205,'eee');
+UPDATE ft2 SET c2 = c2 + 300, c3 = c3 || '_update3' WHERE c1 % 10 = 3;
+UPDATE ft2 SET c2 = c2 + 400, c3 = c3 || '_update7' WHERE c1 % 10 = 7 RETURNING *;
+UPDATE ft2 SET c2 = ft2.c2 + 500, c3 = ft2.c3 || '_update9' FROM ft1 WHERE ft1.c1 = ft2.c2 AND ft1.c1 % 10 = 9;
+DELETE FROM ft2 WHERE c1 % 10 = 5 RETURNING *;
+DELETE FROM ft2 USING ft1 WHERE ft1.c1 = ft2.c2 AND ft1.c1 % 10 = 2;
+SELECT c1,c2,c3,c4 FROM ft2 ORDER BY c1;
+
+-- ===================================================================
 -- cleanup
 -- ===================================================================
 DROP OPERATOR === (int, int) CASCADE;
diff --git a/doc/src/sgml/fdwhandler.sgml b/doc/src/sgml/fdwhandler.sgml
index 2d604ed..a2ec974 100644
--- a/doc/src/sgml/fdwhandler.sgml
+++ b/doc/src/sgml/fdwhandler.sgml
@@ -89,6 +89,46 @@
 
     <para>
 <programlisting>
+AttrNumber
+GetForeignRelWidth(PlannerInfo *root,
+                   RelOptInfo *baserel,
+                   Relation foreignrel,
+                   bool inhparent,
+                   List *targetList);
+</programlisting>
+     Obtain width of the result set to be fetched on foreign table scan.
+     This is an optional handler, and called at the beginning of planning
+     for a query involving a foreign table; during construction of
+     <literal>RelOptInfo</>.
+     <literal>root</> is the planner's global information about the query;
+     <literal>baserel</> is the planner's information about this table under
+     construction; and <literal>foreignrel</> is <literal>Relation</>
+     descriptor of the foreign table.
+     <literal>inhparent</> is a boolean to show whether the relation is
+     inheritance parent, even though foreign table does not support table
+     inheritance right now. <literal>targetList</> is list of
+     <literal>TargetEntry</> to be returned from the (sub-)query being
+     currently focused on.
+    </para>
+
+    <para>
+     The result value of this function shall be assigned on the
+     <literal>baserel-&gt;max_attr</>; that means expected number of
+     columns being fetched on the foreign table scan.
+     It should not be smaller than number of regular columns in definition
+     of this foreign table. All we can do is expansion of this value to
+     acquire slots for some additional attributes; being called pseudo-
+     columns. A typical usage of pseudo-column is to move an identifier of
+     a particular remote row to be updated or deleted from scanning stage
+     to modifying stage when foreign table performs as result relation of
+     the query. In addition, it also allows off-load burden of complex
+     calculation into foreign computing resources, and replace it with
+     just a reference of its calculated result on the remote side, instead
+     of local calculation.
+   </para>
+
+    <para>
+<programlisting>
 void
 GetForeignRelSize (PlannerInfo *root,
                    RelOptInfo *baserel,
@@ -96,7 +136,8 @@ GetForeignRelSize (PlannerInfo *root,
 </programlisting>
 
      Obtain relation size estimates for a foreign table.  This is called
-     at the beginning of planning for a query involving a foreign table.
+     at the beginning of planning for a query involving a foreign table,
+     unless <literal>FixupGetForeignRelWidth</> is not configured.
      <literal>root</> is the planner's global information about the query;
      <literal>baserel</> is the planner's information about this table; and
      <literal>foreigntableid</> is the <structname>pg_class</> OID of the
@@ -315,6 +356,115 @@ AcquireSampleRowsFunc (Relation relation, int elevel,
     </para>
 
     <para>
+     If FDW driver supports writable foreign tables, it must implement both
+     of <literal>BeginForeignModify</> and <literal>EndForeignModify</> at
+     least. In addition, some or all of <literal>ExecForeignInsert</>,
+     <literal>ExecForeignUpdate</> and <literal>ExecForeignDelete</> need
+     to be implement according to capability of FDW.
+    </para>     
+
+    <para>
+<programlisting>
+List *
+PlanForeignModify(PlannerInfo *root,
+                  ModifyTable *plan,
+                  Index resultRelation,
+                  Plan *subplan);
+</programlisting>
+     It allows FDW drivers to construct its own private stuff relevant to
+     the foreign table to be modified. This private stuff should have
+     form of <literal>List *</> data, then it shall be delivered as
+     3rd argument of <literal>BeginForeignModify</> on execution stage.
+    </para>
+    <para>
+     <literal>root</> is the planner's global information about the query.
+     <literal>plan</> is the master plan to modify result relation according
+     to its command type. Please also pay attention <literal>ModifyTable</>
+     may have multiple result relations in the future revision, even though
+     we don't support inheritance feature on foreign tables.
+     <literal>resultRelation</> is an index towards result relation in the
+     range table entries, and <literal>subplan</> is the relevant scan plan;
+     that should be <literal>ForeignScan</> at <literal>UPDATE</> or
+     <literal>DELETE</>, so the driver can access private datum of scan stage
+     using this argument.
+    </para>
+
+    <para>
+<programlisting>
+void
+BeginForeignModify (ModifyTableState *mtstate,
+                    ResultRelInfo *resultRelInfo,
+                    List *fdw_private,
+                    Plan *subplan,
+                    int eflags);
+</programlisting>
+     It is invoked at beginning of foreign table modification, durting
+     execurot startup. This routine should perform any initialization
+     stuffs needed prior to actual table modifications, but not start
+     modifying the actual tuples. (That should be done for each call of
+     <function>ExecForeignInsert</>, <function>ExecForeignUpdate</> or
+     <function>ExecForeignDelete</>.)
+    </para>
+    <para>
+     The <structfield>ri_fdw_state</> field of <structname>ResultRelInfo</>
+     is reserved to store any private stuff relevant to this foreign table,
+     so it is assumed this routine handles any initialization stuff and
+     save its per-scan state on this field.
+     Please also pay attention <literal>ModifyTableState</> may have multiple
+     result relations in the future revision, even though we don't support
+     inheritance feature on foreign tables. <literal>resultRelInfo</> is
+     master information connected to this foreign table.
+     <literal>fdw_private</> is a private stuff constructed at
+     <literal>PlanForeignModify</>, and <literal>subplan</> is the relevant
+     scan plan of this table modification.
+    </para>
+
+    <para>
+<programlisting>
+int
+ExecForeignInsert (ResultRelInfo *resultRelInfo,
+                   HeapTuple tuple);
+</programlisting>
+     Insert the given tuple into backing storage behalf on the foreign table.
+     The supplied tuple is already formed according to the definition of
+     relation, thus, all the pseudo-columns are already filtered out.
+     It can return number of rows being inserted, usually 1.
+    </para>
+
+    <para>
+<programlisting>
+int
+ExecForeignDelete (ResultRelInfo *resultRelInfo,
+                   Datum rowid);
+</programlisting>
+     Delete the tuple being identified with <literal>rowid</> in the backing
+     storage behalf on the foreign table.
+     It can return number of rows being deleted, usually 1.
+    </para>
+
+    <para>
+<programlisting>
+int
+ExecForeignUpdate (ResultRelInfo *resultRelInfo,
+                   Datum rowid,
+                   HeapTuple tuple);
+</programlisting>
+     Update the tuple being identified with <literal>rowid</> in the backing
+     storage behalf on the foreign table, by the given tuple.
+     It can return number of rows being updated, usually 1.
+    </para>
+
+    <para>
+<programlisting>
+void
+EndForeignModify (ResultRelInfo *resultRelInfo);
+</programlisting>
+     End the modification and release resources. It is normally not important
+     to release palloc'd memory, but for example open files and connections
+     to remote servers should be cleaned up.
+    </para>
+
+    <para>
      The <structname>FdwRoutine</> struct type is declared in
      <filename>src/include/foreign/fdwapi.h</>, which see for additional
      details.
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index dbd3755..ccc1f3a 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -44,6 +44,7 @@
 #include "catalog/namespace.h"
 #include "commands/trigger.h"
 #include "executor/execdebug.h"
+#include "foreign/fdwapi.h"
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
 #include "optimizer/clauses.h"
@@ -934,6 +935,7 @@ void
 CheckValidResultRel(Relation resultRel, CmdType operation)
 {
 	TriggerDesc *trigDesc = resultRel->trigdesc;
+	FdwRoutine	*fdwroutine;
 
 	switch (resultRel->rd_rel->relkind)
 	{
@@ -985,10 +987,34 @@ CheckValidResultRel(Relation resultRel, CmdType operation)
 			}
 			break;
 		case RELKIND_FOREIGN_TABLE:
-			ereport(ERROR,
-					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-					 errmsg("cannot change foreign table \"%s\"",
-							RelationGetRelationName(resultRel))));
+			fdwroutine = GetFdwRoutineByRelId(RelationGetRelid(resultRel));
+			switch (operation)
+			{
+				case CMD_INSERT:
+					if (!fdwroutine || !fdwroutine->ExecForeignInsert)
+						ereport(ERROR,
+							(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+							 errmsg("cannot insert into foreign table \"%s\"",
+									RelationGetRelationName(resultRel))));
+					break;
+				case CMD_UPDATE:
+					if (!fdwroutine || !fdwroutine->ExecForeignUpdate)
+						ereport(ERROR,
+							(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+							 errmsg("cannot update foreign table \"%s\"",
+									RelationGetRelationName(resultRel))));
+					break;
+				case CMD_DELETE:
+					if (!fdwroutine || !fdwroutine->ExecForeignDelete)
+						ereport(ERROR,
+							(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+							 errmsg("cannot delete from foreign table \"%s\"",
+									RelationGetRelationName(resultRel))));
+					break;
+				default:
+					elog(ERROR, "unrecognized CmdType: %d", (int) operation);
+					break;
+			}
 			break;
 		default:
 			ereport(ERROR,
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index 9204859..1ee626f 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -25,6 +25,7 @@
 #include "executor/executor.h"
 #include "executor/nodeForeignscan.h"
 #include "foreign/fdwapi.h"
+#include "nodes/nodeFuncs.h"
 #include "utils/rel.h"
 
 static TupleTableSlot *ForeignNext(ForeignScanState *node);
@@ -93,6 +94,133 @@ ExecForeignScan(ForeignScanState *node)
 					(ExecScanRecheckMtd) ForeignRecheck);
 }
 
+/*
+ * pseudo_column_walker
+ *
+ * helper routine of GetPseudoTupleDesc. It pulls Var nodes that reference
+ * pseudo columns from targetlis of the relation
+ */
+typedef struct
+{
+	Relation	relation;
+	Index		varno;
+	List	   *pcolumns;
+	AttrNumber	max_attno;
+} pseudo_column_walker_context;
+
+static bool
+pseudo_column_walker(Node *node, pseudo_column_walker_context *context)
+{
+	if (node == NULL)
+		return false;
+	if (IsA(node, Var))
+	{
+		Var		   *var = (Var *) node;
+		ListCell   *cell;
+
+		if (var->varno == context->varno && var->varlevelsup == 0 &&
+			var->varattno > RelationGetNumberOfAttributes(context->relation))
+		{
+			foreach (cell, context->pcolumns)
+			{
+				Var	   *temp = lfirst(cell);
+
+				if (temp->varattno == var->varattno)
+				{
+					if (!equal(var, temp))
+						elog(ERROR, "asymmetric pseudo column appeared");
+					break;
+				}
+			}
+			if (!cell)
+			{
+				context->pcolumns = lappend(context->pcolumns, var);
+				if (var->varattno > context->max_attno)
+					context->max_attno = var->varattno;
+			}
+		}
+		return false;
+	}
+
+	/* Should not find an unplanned subquery */
+	Assert(!IsA(node, Query));
+
+	return expression_tree_walker(node, pseudo_column_walker,
+								  (void *)context);
+}
+
+/*
+ * GetPseudoTupleDesc
+ *
+ * It generates TupleDesc structure including pseudo-columns if required.
+ */
+static TupleDesc
+GetPseudoTupleDesc(ForeignScan *node, Relation relation)
+{
+	pseudo_column_walker_context context;
+	List	   *target_list = node->scan.plan.targetlist;
+	TupleDesc	tupdesc;
+	AttrNumber	attno;
+	ListCell   *cell;
+	ListCell   *prev;
+	bool		hasoid;
+
+	context.relation = relation;
+	context.varno = node->scan.scanrelid;
+	context.pcolumns = NIL;
+	context.max_attno = -1;
+
+	pseudo_column_walker((Node *)target_list, (void *)&context);
+	Assert(context.max_attno > RelationGetNumberOfAttributes(relation));
+
+	hasoid = RelationGetForm(relation)->relhasoids;
+	tupdesc = CreateTemplateTupleDesc(context.max_attno, hasoid);
+
+	for (attno = 1; attno <= context.max_attno; attno++)
+	{
+		/* case of regular columns */
+		if (attno <= RelationGetNumberOfAttributes(relation))
+		{
+			memcpy(tupdesc->attrs[attno - 1],
+				   RelationGetDescr(relation)->attrs[attno - 1],
+				   ATTRIBUTE_FIXED_PART_SIZE);
+			continue;
+		}
+
+		/* case of pseudo columns */
+		prev = NULL;
+		foreach (cell, context.pcolumns)
+		{
+			Var	   *var = lfirst(cell);
+
+			if (var->varattno == attno)
+			{
+				char		namebuf[NAMEDATALEN];
+
+				snprintf(namebuf, sizeof(namebuf),
+						 "pseudo_column_%d", attno);
+
+				TupleDescInitEntry(tupdesc,
+								   attno,
+								   namebuf,
+								   var->vartype,
+								   var->vartypmod,
+								   0);
+				TupleDescInitEntryCollation(tupdesc,
+											attno,
+											var->varcollid);
+				context.pcolumns
+					= list_delete_cell(context.pcolumns, cell, prev);
+				break;
+			}
+			prev = cell;
+		}
+		if (!cell)
+			elog(ERROR, "pseudo column %d of %s not in target list",
+				 attno, RelationGetRelationName(relation));
+	}
+	return tupdesc;
+}
 
 /* ----------------------------------------------------------------
  *		ExecInitForeignScan
@@ -103,6 +231,7 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
 {
 	ForeignScanState *scanstate;
 	Relation	currentRelation;
+	TupleDesc	tupdesc;
 	FdwRoutine *fdwroutine;
 
 	/* check for unsupported flags */
@@ -149,7 +278,12 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
 	/*
 	 * get the scan type from the relation descriptor.
 	 */
-	ExecAssignScanType(&scanstate->ss, RelationGetDescr(currentRelation));
+	if (node->fsPseudoCol)
+		tupdesc = GetPseudoTupleDesc(node, currentRelation);
+	else
+		tupdesc = RelationGetDescr(currentRelation);
+
+	ExecAssignScanType(&scanstate->ss, tupdesc);
 
 	/*
 	 * Initialize result tuple type and projection info.
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index d31015c..bf9421e 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -42,6 +42,7 @@
 #include "commands/trigger.h"
 #include "executor/executor.h"
 #include "executor/nodeModifyTable.h"
+#include "foreign/fdwapi.h"
 #include "miscadmin.h"
 #include "nodes/nodeFuncs.h"
 #include "storage/bufmgr.h"
@@ -170,6 +171,7 @@ ExecInsert(TupleTableSlot *slot,
 	Relation	resultRelationDesc;
 	Oid			newId;
 	List	   *recheckIndexes = NIL;
+	int			num_rows = 1;
 
 	/*
 	 * get the heap tuple out of the tuple table slot, making sure we have a
@@ -225,6 +227,14 @@ ExecInsert(TupleTableSlot *slot,
 
 		newId = InvalidOid;
 	}
+	else if (resultRelInfo->ri_fdwroutine)
+	{
+		FdwRoutine *fdwroutine = resultRelInfo->ri_fdwroutine;
+
+		num_rows = fdwroutine->ExecForeignInsert(resultRelInfo, tuple);
+
+		newId = InvalidOid;
+	}
 	else
 	{
 		/*
@@ -252,7 +262,7 @@ ExecInsert(TupleTableSlot *slot,
 
 	if (canSetTag)
 	{
-		(estate->es_processed)++;
+		(estate->es_processed) += num_rows;
 		estate->es_lastoid = newId;
 		setLastTid(&(tuple->t_self));
 	}
@@ -285,7 +295,7 @@ ExecInsert(TupleTableSlot *slot,
  * ----------------------------------------------------------------
  */
 static TupleTableSlot *
-ExecDelete(ItemPointer tupleid,
+ExecDelete(Datum rowid,
 		   HeapTupleHeader oldtuple,
 		   TupleTableSlot *planSlot,
 		   EPQState *epqstate,
@@ -296,6 +306,7 @@ ExecDelete(ItemPointer tupleid,
 	Relation	resultRelationDesc;
 	HTSU_Result result;
 	HeapUpdateFailureData hufd;
+	int			num_rows = 1;
 
 	/*
 	 * get information on the (current) result relation
@@ -310,7 +321,7 @@ ExecDelete(ItemPointer tupleid,
 		bool		dodelete;
 
 		dodelete = ExecBRDeleteTriggers(estate, epqstate, resultRelInfo,
-										tupleid);
+										(ItemPointer)DatumGetPointer(rowid));
 
 		if (!dodelete)			/* "do nothing" */
 			return NULL;
@@ -334,8 +345,16 @@ ExecDelete(ItemPointer tupleid,
 		if (!dodelete)			/* "do nothing" */
 			return NULL;
 	}
+	else if (resultRelInfo->ri_fdwroutine)
+	{
+		FdwRoutine *fdwroutine = resultRelInfo->ri_fdwroutine;
+
+		num_rows = fdwroutine->ExecForeignDelete(resultRelInfo, rowid);
+	}
 	else
 	{
+		ItemPointer	tupleid = (ItemPointer) DatumGetPointer(rowid);
+
 		/*
 		 * delete the tuple
 		 *
@@ -430,10 +449,11 @@ ldelete:;
 	}
 
 	if (canSetTag)
-		(estate->es_processed)++;
+		(estate->es_processed) += num_rows;
 
 	/* AFTER ROW DELETE Triggers */
-	ExecARDeleteTriggers(estate, resultRelInfo, tupleid);
+	ExecARDeleteTriggers(estate, resultRelInfo,
+						 (ItemPointer)DatumGetPointer(rowid));
 
 	/* Process RETURNING if present */
 	if (resultRelInfo->ri_projectReturning)
@@ -457,7 +477,8 @@ ldelete:;
 		}
 		else
 		{
-			deltuple.t_self = *tupleid;
+			ItemPointerCopy((ItemPointer)DatumGetPointer(rowid),
+							&deltuple.t_self);
 			if (!heap_fetch(resultRelationDesc, SnapshotAny,
 							&deltuple, &delbuffer, false, NULL))
 				elog(ERROR, "failed to fetch deleted tuple for DELETE RETURNING");
@@ -499,7 +520,7 @@ ldelete:;
  * ----------------------------------------------------------------
  */
 static TupleTableSlot *
-ExecUpdate(ItemPointer tupleid,
+ExecUpdate(Datum rowid,
 		   HeapTupleHeader oldtuple,
 		   TupleTableSlot *slot,
 		   TupleTableSlot *planSlot,
@@ -513,6 +534,7 @@ ExecUpdate(ItemPointer tupleid,
 	HTSU_Result result;
 	HeapUpdateFailureData hufd;
 	List	   *recheckIndexes = NIL;
+	int			num_rows = 1;
 
 	/*
 	 * abort the operation if not running transactions
@@ -537,7 +559,7 @@ ExecUpdate(ItemPointer tupleid,
 		resultRelInfo->ri_TrigDesc->trig_update_before_row)
 	{
 		slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
-									tupleid, slot);
+									(ItemPointer)DatumGetPointer(rowid), slot);
 
 		if (slot == NULL)		/* "do nothing" */
 			return NULL;
@@ -567,8 +589,16 @@ ExecUpdate(ItemPointer tupleid,
 		/* trigger might have changed tuple */
 		tuple = ExecMaterializeSlot(slot);
 	}
+	else if (resultRelInfo->ri_fdwroutine)
+	{
+		FdwRoutine *fdwroutine = resultRelInfo->ri_fdwroutine;
+
+		num_rows = fdwroutine->ExecForeignUpdate(resultRelInfo, rowid, tuple);
+	}
 	else
 	{
+		ItemPointer	tupleid = (ItemPointer) DatumGetPointer(rowid);
+
 		/*
 		 * Check the constraints of the tuple
 		 *
@@ -687,11 +717,12 @@ lreplace:;
 	}
 
 	if (canSetTag)
-		(estate->es_processed)++;
+		(estate->es_processed) += num_rows;
 
 	/* AFTER ROW UPDATE Triggers */
-	ExecARUpdateTriggers(estate, resultRelInfo, tupleid, tuple,
-						 recheckIndexes);
+	ExecARUpdateTriggers(estate, resultRelInfo,
+						 (ItemPointer) DatumGetPointer(rowid),
+						 tuple, recheckIndexes);
 
 	list_free(recheckIndexes);
 
@@ -771,6 +802,7 @@ ExecModifyTable(ModifyTableState *node)
 	TupleTableSlot *planSlot;
 	ItemPointer tupleid = NULL;
 	ItemPointerData tuple_ctid;
+	Datum		rowid = 0;
 	HeapTupleHeader oldtuple = NULL;
 
 	/*
@@ -859,17 +891,19 @@ ExecModifyTable(ModifyTableState *node)
 		if (junkfilter != NULL)
 		{
 			/*
-			 * extract the 'ctid' or 'wholerow' junk attribute.
+			 * extract the 'ctid', 'rowid' or 'wholerow' junk attribute.
 			 */
 			if (operation == CMD_UPDATE || operation == CMD_DELETE)
 			{
+				char		relkind;
 				Datum		datum;
 				bool		isNull;
 
-				if (resultRelInfo->ri_RelationDesc->rd_rel->relkind == RELKIND_RELATION)
+				relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
+				if (relkind == RELKIND_RELATION)
 				{
 					datum = ExecGetJunkAttribute(slot,
-												 junkfilter->jf_junkAttNo,
+												 junkfilter->jf_junkRowidNo,
 												 &isNull);
 					/* shouldn't ever get a null result... */
 					if (isNull)
@@ -877,13 +911,33 @@ ExecModifyTable(ModifyTableState *node)
 
 					tupleid = (ItemPointer) DatumGetPointer(datum);
 					tuple_ctid = *tupleid;		/* be sure we don't free
-												 * ctid!! */
-					tupleid = &tuple_ctid;
+												 * ctid ! */
+					rowid = PointerGetDatum(&tuple_ctid);
+				}
+				else if (relkind == RELKIND_FOREIGN_TABLE)
+				{
+					datum = ExecGetJunkAttribute(slot,
+												 junkfilter->jf_junkRowidNo,
+												 &isNull);
+					/* shouldn't ever get a null result... */
+					if (isNull)
+						elog(ERROR, "rowid is NULL");
+
+					rowid = datum;
+
+					datum = ExecGetJunkAttribute(slot,
+												 junkfilter->jf_junkRecordNo,
+												 &isNull);
+					/* shouldn't ever get a null result... */
+					if (isNull)
+						elog(ERROR, "wholerow is NULL");
+
+					oldtuple = DatumGetHeapTupleHeader(datum);
 				}
 				else
 				{
 					datum = ExecGetJunkAttribute(slot,
-												 junkfilter->jf_junkAttNo,
+												 junkfilter->jf_junkRecordNo,
 												 &isNull);
 					/* shouldn't ever get a null result... */
 					if (isNull)
@@ -906,11 +960,11 @@ ExecModifyTable(ModifyTableState *node)
 				slot = ExecInsert(slot, planSlot, estate, node->canSetTag);
 				break;
 			case CMD_UPDATE:
-				slot = ExecUpdate(tupleid, oldtuple, slot, planSlot,
+				slot = ExecUpdate(rowid, oldtuple, slot, planSlot,
 								&node->mt_epqstate, estate, node->canSetTag);
 				break;
 			case CMD_DELETE:
-				slot = ExecDelete(tupleid, oldtuple, planSlot,
+				slot = ExecDelete(rowid, oldtuple, planSlot,
 								&node->mt_epqstate, estate, node->canSetTag);
 				break;
 			default:
@@ -997,6 +1051,8 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	i = 0;
 	foreach(l, node->plans)
 	{
+		char	relkind;
+
 		subplan = (Plan *) lfirst(l);
 
 		/*
@@ -1022,6 +1078,24 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		estate->es_result_relation_info = resultRelInfo;
 		mtstate->mt_plans[i] = ExecInitNode(subplan, estate, eflags);
 
+		/*
+		 * Also tells FDW extensions to init the plan for this result rel
+		 */
+		relkind = RelationGetForm(resultRelInfo->ri_RelationDesc)->relkind;
+		if (relkind == RELKIND_FOREIGN_TABLE)
+		{
+			Oid		relid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
+			FdwRoutine *fdwroutine = GetFdwRoutineByRelId(relid);
+			List   *fdwprivate = list_nth(node->fdwPrivList, i);
+
+			Assert(fdwroutine != NULL);
+			resultRelInfo->ri_fdwroutine = fdwroutine;
+			resultRelInfo->ri_fdw_state = NULL;
+
+			if (fdwroutine->BeginForeignModify)
+				fdwroutine->BeginForeignModify(mtstate, resultRelInfo,
+											   fdwprivate, subplan, eflags);
+		}
 		resultRelInfo++;
 		i++;
 	}
@@ -1163,6 +1237,8 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 			for (i = 0; i < nplans; i++)
 			{
 				JunkFilter *j;
+				char		relkind =
+				    RelationGetForm(resultRelInfo->ri_RelationDesc)->relkind;
 
 				subplan = mtstate->mt_plans[i]->plan;
 				if (operation == CMD_INSERT || operation == CMD_UPDATE)
@@ -1176,16 +1252,27 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 				if (operation == CMD_UPDATE || operation == CMD_DELETE)
 				{
 					/* For UPDATE/DELETE, find the appropriate junk attr now */
-					if (resultRelInfo->ri_RelationDesc->rd_rel->relkind == RELKIND_RELATION)
+					if (relkind == RELKIND_RELATION)
 					{
-						j->jf_junkAttNo = ExecFindJunkAttribute(j, "ctid");
-						if (!AttributeNumberIsValid(j->jf_junkAttNo))
+						j->jf_junkRowidNo = ExecFindJunkAttribute(j, "ctid");
+						if (!AttributeNumberIsValid(j->jf_junkRowidNo))
 							elog(ERROR, "could not find junk ctid column");
 					}
+					else if (relkind == RELKIND_FOREIGN_TABLE)
+					{
+						j->jf_junkRowidNo = ExecFindJunkAttribute(j, "rowid");
+						if (!AttributeNumberIsValid(j->jf_junkRowidNo))
+							elog(ERROR, "could not find junk rowid column");
+						j->jf_junkRecordNo
+							= ExecFindJunkAttribute(j, "record");
+						if (!AttributeNumberIsValid(j->jf_junkRecordNo))
+							elog(ERROR, "could not find junk wholerow column");
+					}
 					else
 					{
-						j->jf_junkAttNo = ExecFindJunkAttribute(j, "wholerow");
-						if (!AttributeNumberIsValid(j->jf_junkAttNo))
+						j->jf_junkRecordNo
+							= ExecFindJunkAttribute(j, "wholerow");
+						if (!AttributeNumberIsValid(j->jf_junkRecordNo))
 							elog(ERROR, "could not find junk wholerow column");
 					}
 				}
@@ -1239,6 +1326,21 @@ ExecEndModifyTable(ModifyTableState *node)
 {
 	int			i;
 
+	/* Let the FDW shut dowm */
+	for (i=0; i < node->ps.state->es_num_result_relations; i++)
+	{
+		ResultRelInfo  *resultRelInfo
+			= &node->ps.state->es_result_relations[i];
+
+		if (resultRelInfo->ri_fdwroutine)
+		{
+			FdwRoutine *fdwroutine = resultRelInfo->ri_fdwroutine;
+
+			if (fdwroutine->EndForeignModify)
+				fdwroutine->EndForeignModify(resultRelInfo);
+		}
+	}
+
 	/*
 	 * Free the exprcontext
 	 */
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index d8845aa..04fd9a9 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -571,3 +571,71 @@ get_foreign_server_oid(const char *servername, bool missing_ok)
 				 errmsg("server \"%s\" does not exist", servername)));
 	return oid;
 }
+
+/*
+ * get_pseudo_rowid_column
+ *
+ * It picks up an attribute number to be used for pseudo rowid columns,
+ * if exists. It should be injected at rewriteHandler.c if supplied query
+ * is UPDATE or DELETE command. Elsewhere, it returns InvalidAttrNumber.
+ */
+AttrNumber
+get_pseudo_rowid_column(RelOptInfo *baserel, List *targetList)
+{
+	ListCell   *cell;
+
+	foreach (cell, targetList)
+	{
+		TargetEntry *tle = lfirst(cell);
+
+		if (tle->resjunk &&
+			tle->resname && strcmp(tle->resname, "rowid") == 0)
+		{
+			Var	   *var;
+
+			if (!IsA(tle->expr, Var))
+				elog(ERROR, "unexpected node on junk rowid entry: %d",
+					 (int) nodeTag(tle->expr));
+
+			var = (Var *) tle->expr;
+			if (baserel->relid == var->varno)
+				return var->varattno;
+		}
+	}
+	return InvalidAttrNumber;
+}
+
+/*
+ * lookup_foreign_scan_plan
+ *
+ * It looks up ForeignScan plan node with the supplied range-table id.
+ * Its typical usage is PlanForeignModify gets underlying scan plan on
+ * UPDATE or DELETE commands.
+ */
+ForeignScan *
+lookup_foreign_scan_plan(Plan *subplan, Index rtindex)
+{
+	if (!subplan)
+		return NULL;
+
+	else if (IsA(subplan, ForeignScan))
+	{
+		ForeignScan	   *fscan = (ForeignScan *) subplan;
+
+		if (fscan->scan.scanrelid == rtindex)
+			return fscan;
+	}
+	else
+	{
+		ForeignScan    *fscan;
+
+		fscan = lookup_foreign_scan_plan(subplan->lefttree, rtindex);
+		if (fscan != NULL)
+			return fscan;
+
+		fscan = lookup_foreign_scan_plan(subplan->righttree, rtindex);
+		if (fscan != NULL)
+			return fscan;
+	}
+	return NULL;
+}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 9387ee9..5b1e8ac 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -181,6 +181,7 @@ _copyModifyTable(const ModifyTable *from)
 	COPY_NODE_FIELD(returningLists);
 	COPY_NODE_FIELD(rowMarks);
 	COPY_SCALAR_FIELD(epqParam);
+	COPY_NODE_FIELD(fdwPrivList);
 
 	return newnode;
 }
@@ -594,6 +595,7 @@ _copyForeignScan(const ForeignScan *from)
 	COPY_NODE_FIELD(fdw_exprs);
 	COPY_NODE_FIELD(fdw_private);
 	COPY_SCALAR_FIELD(fsSystemCol);
+	COPY_SCALAR_FIELD(fsPseudoCol);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 35c6287..c32ebd3 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -335,6 +335,7 @@ _outModifyTable(StringInfo str, const ModifyTable *node)
 	WRITE_NODE_FIELD(returningLists);
 	WRITE_NODE_FIELD(rowMarks);
 	WRITE_INT_FIELD(epqParam);
+	WRITE_NODE_FIELD(fdwPrivList);
 }
 
 static void
@@ -562,6 +563,7 @@ _outForeignScan(StringInfo str, const ForeignScan *node)
 	WRITE_NODE_FIELD(fdw_exprs);
 	WRITE_NODE_FIELD(fdw_private);
 	WRITE_BOOL_FIELD(fsSystemCol);
+	WRITE_BOOL_FIELD(fsPseudoCol);
 }
 
 static void
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 030f420..e9edb87 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -39,6 +39,7 @@
 #include "parser/parse_clause.h"
 #include "parser/parsetree.h"
 #include "utils/lsyscache.h"
+#include "utils/rel.h"
 
 
 static Plan *create_plan_recurse(PlannerInfo *root, Path *best_path);
@@ -1943,6 +1944,8 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
 	RelOptInfo *rel = best_path->path.parent;
 	Index		scan_relid = rel->relid;
 	RangeTblEntry *rte;
+	Relation	relation;
+	AttrNumber	num_attrs;
 	int			i;
 
 	/* it should be a base rel... */
@@ -2001,6 +2004,22 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
 		}
 	}
 
+	/*
+	 * Also, detect whether any pseudo columns are requested from rel.
+	 */
+	relation = heap_open(rte->relid, NoLock);
+	scan_plan->fsPseudoCol = false;
+	num_attrs = RelationGetNumberOfAttributes(relation);
+	for (i = num_attrs + 1; i <= rel->max_attr; i++)
+	{
+		if (!bms_is_empty(rel->attr_needed[i - rel->min_attr]))
+		{
+			scan_plan->fsPseudoCol = true;
+			break;
+		}
+	}
+	heap_close(relation, NoLock);
+
 	return scan_plan;
 }
 
@@ -4695,7 +4714,8 @@ make_result(PlannerInfo *root,
  * to make it look better sometime.
  */
 ModifyTable *
-make_modifytable(CmdType operation, bool canSetTag,
+make_modifytable(PlannerInfo *root,
+				 CmdType operation, bool canSetTag,
 				 List *resultRelations,
 				 List *subplans, List *returningLists,
 				 List *rowMarks, int epqParam)
@@ -4704,6 +4724,8 @@ make_modifytable(CmdType operation, bool canSetTag,
 	Plan	   *plan = &node->plan;
 	double		total_size;
 	ListCell   *subnode;
+	ListCell   *resultRel;
+	List	   *fdw_priv_list = NIL;
 
 	Assert(list_length(resultRelations) == list_length(subplans));
 	Assert(returningLists == NIL ||
@@ -4746,6 +4768,30 @@ make_modifytable(CmdType operation, bool canSetTag,
 	node->rowMarks = rowMarks;
 	node->epqParam = epqParam;
 
+	/*
+	 * Allows FDW driver to construct its private plan if result relation
+	 * is foreign table.
+	 */
+	forboth (resultRel, resultRelations, subnode, subplans)
+	{
+		RangeTblEntry  *rte = rt_fetch(lfirst_int(resultRel),
+									   root->parse->rtable);
+		List		   *fdw_private = NIL;
+		char			relkind = get_rel_relkind(rte->relid);
+
+		if (relkind == RELKIND_FOREIGN_TABLE)
+		{
+			FdwRoutine *fdwroutine = GetFdwRoutineByRelId(rte->relid);
+
+			if (fdwroutine && fdwroutine->PlanForeignModify)
+				fdw_private = fdwroutine->PlanForeignModify(root, node,
+													lfirst_int(resultRel),
+													(Plan *) lfirst(subnode));
+		}
+		fdw_priv_list = lappend(fdw_priv_list, fdw_private);
+	}
+	node->fdwPrivList = fdw_priv_list;
+
 	return node;
 }
 
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index bd719b5..57ba192 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -85,7 +85,7 @@ static void check_hashjoinable(RestrictInfo *restrictinfo);
  * "other rel" RelOptInfos for the members of any appendrels we find here.)
  */
 void
-add_base_rels_to_query(PlannerInfo *root, Node *jtnode)
+add_base_rels_to_query(PlannerInfo *root, List *tlist, Node *jtnode)
 {
 	if (jtnode == NULL)
 		return;
@@ -93,7 +93,7 @@ add_base_rels_to_query(PlannerInfo *root, Node *jtnode)
 	{
 		int			varno = ((RangeTblRef *) jtnode)->rtindex;
 
-		(void) build_simple_rel(root, varno, RELOPT_BASEREL);
+		(void) build_simple_rel(root, varno, tlist, RELOPT_BASEREL);
 	}
 	else if (IsA(jtnode, FromExpr))
 	{
@@ -101,14 +101,14 @@ add_base_rels_to_query(PlannerInfo *root, Node *jtnode)
 		ListCell   *l;
 
 		foreach(l, f->fromlist)
-			add_base_rels_to_query(root, lfirst(l));
+			add_base_rels_to_query(root, tlist, lfirst(l));
 	}
 	else if (IsA(jtnode, JoinExpr))
 	{
 		JoinExpr   *j = (JoinExpr *) jtnode;
 
-		add_base_rels_to_query(root, j->larg);
-		add_base_rels_to_query(root, j->rarg);
+		add_base_rels_to_query(root, tlist, j->larg);
+		add_base_rels_to_query(root, tlist, j->rarg);
 	}
 	else
 		elog(ERROR, "unrecognized node type: %d",
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c
index c2488a4..4442444 100644
--- a/src/backend/optimizer/plan/planmain.c
+++ b/src/backend/optimizer/plan/planmain.c
@@ -112,6 +112,9 @@ query_planner(PlannerInfo *root, List *tlist,
 	 */
 	if (parse->jointree->fromlist == NIL)
 	{
+		/* Make a flattened version of the rangetable for faster access */
+		setup_simple_rel_arrays(root);
+
 		/* We need a trivial path result */
 		*cheapest_path = (Path *)
 			create_result_path((List *) parse->jointree->quals);
@@ -163,7 +166,7 @@ query_planner(PlannerInfo *root, List *tlist,
 	 * rangetable may contain RTEs for rels not actively part of the query,
 	 * for example views.  We don't want to make RelOptInfos for them.
 	 */
-	add_base_rels_to_query(root, (Node *) parse->jointree);
+	add_base_rels_to_query(root, tlist, (Node *) parse->jointree);
 
 	/*
 	 * Examine the targetlist and join tree, adding entries to baserel
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index b61005f..2d72244 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -571,7 +571,8 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 			else
 				rowMarks = root->rowMarks;
 
-			plan = (Plan *) make_modifytable(parse->commandType,
+			plan = (Plan *) make_modifytable(root,
+											 parse->commandType,
 											 parse->canSetTag,
 									   list_make1_int(parse->resultRelation),
 											 list_make1(plan),
@@ -964,7 +965,8 @@ inheritance_planner(PlannerInfo *root)
 		rowMarks = root->rowMarks;
 
 	/* And last, tack on a ModifyTable node to do the UPDATE/DELETE work */
-	return (Plan *) make_modifytable(parse->commandType,
+	return (Plan *) make_modifytable(root,
+									 parse->commandType,
 									 parse->canSetTag,
 									 resultRelations,
 									 subplans,
@@ -3385,7 +3387,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid)
 	setup_simple_rel_arrays(root);
 
 	/* Build RelOptInfo */
-	rel = build_simple_rel(root, 1, RELOPT_BASEREL);
+	rel = build_simple_rel(root, 1, NIL, RELOPT_BASEREL);
 
 	/* Locate IndexOptInfo for the target index */
 	indexInfo = NULL;
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index b91e9f4..61bc447 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -236,7 +236,8 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,
 		 * used for anything here, but it carries the subroot data structures
 		 * forward to setrefs.c processing.
 		 */
-		rel = build_simple_rel(root, rtr->rtindex, RELOPT_BASEREL);
+		rel = build_simple_rel(root, rtr->rtindex, refnames_tlist,
+							   RELOPT_BASEREL);
 
 		/* plan_params should not be in use in current query level */
 		Assert(root->plan_params == NIL);
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 04d5028..b16cde3 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -25,6 +25,7 @@
 #include "access/xlog.h"
 #include "catalog/catalog.h"
 #include "catalog/heap.h"
+#include "foreign/fdwapi.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
 #include "optimizer/clauses.h"
@@ -80,7 +81,7 @@ static List *build_index_tlist(PlannerInfo *root, IndexOptInfo *index,
  */
 void
 get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
-				  RelOptInfo *rel)
+				  List *tlist, RelOptInfo *rel)
 {
 	Index		varno = rel->relid;
 	Relation	relation;
@@ -104,6 +105,30 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 	rel->max_attr = RelationGetNumberOfAttributes(relation);
 	rel->reltablespace = RelationGetForm(relation)->reltablespace;
 
+	/*
+	 * Adjust width of attr_needed slot in case when FDW extension wants
+	 * or needs to return pseudo-columns also, not only columns in its
+	 * table definition.
+	 * GetForeignRelWidth, an optional FDW handler, enables FDW extension
+	 * to save properties of pseudo-column on its private field.
+	 * When foreign-table is the target of UPDATE/DELETE, query-rewritter
+	 * injects "rowid" pseudo-column to track remote row to be modified,
+	 * so FDW has to track which varattno shall perform as "rowid".
+	 */
+	if (RelationGetForm(relation)->relkind == RELKIND_FOREIGN_TABLE)
+	{
+		FdwRoutine *fdwroutine = GetFdwRoutineByRelId(relationObjectId);
+
+		if (fdwroutine->GetForeignRelWidth)
+		{
+			rel->max_attr = fdwroutine->GetForeignRelWidth(root, rel,
+														   relation,
+														   inhparent,
+														   tlist);
+			Assert(rel->max_attr >= RelationGetNumberOfAttributes(relation));
+		}
+	}
+
 	Assert(rel->max_attr >= rel->min_attr);
 	rel->attr_needed = (Relids *)
 		palloc0((rel->max_attr - rel->min_attr + 1) * sizeof(Relids));
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index f724714..84b674c 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -80,7 +80,8 @@ setup_simple_rel_arrays(PlannerInfo *root)
  *	  Construct a new RelOptInfo for a base relation or 'other' relation.
  */
 RelOptInfo *
-build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
+build_simple_rel(PlannerInfo *root, int relid, List *tlist,
+				 RelOptKind reloptkind)
 {
 	RelOptInfo *rel;
 	RangeTblEntry *rte;
@@ -133,7 +134,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
 	{
 		case RTE_RELATION:
 			/* Table --- retrieve statistics from the system catalogs */
-			get_relation_info(root, rte->relid, rte->inh, rel);
+			get_relation_info(root, rte->relid, rte->inh, tlist, rel);
 			break;
 		case RTE_SUBQUERY:
 		case RTE_FUNCTION:
@@ -180,7 +181,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
 			if (appinfo->parent_relid != relid)
 				continue;
 
-			(void) build_simple_rel(root, appinfo->child_relid,
+			(void) build_simple_rel(root, appinfo->child_relid, tlist,
 									RELOPT_OTHER_MEMBER_REL);
 		}
 	}
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index b785c26..6cd6241 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -1165,7 +1165,10 @@ rewriteTargetListUD(Query *parsetree, RangeTblEntry *target_rte,
 					Relation target_relation)
 {
 	Var		   *var;
-	const char *attrname;
+	List	   *varList;
+	List	   *attNameList;
+	ListCell   *cell1;
+	ListCell   *cell2;
 	TargetEntry *tle;
 
 	if (target_relation->rd_rel->relkind == RELKIND_RELATION)
@@ -1179,8 +1182,35 @@ rewriteTargetListUD(Query *parsetree, RangeTblEntry *target_rte,
 					  -1,
 					  InvalidOid,
 					  0);
+		varList = list_make1(var);
+		attNameList = list_make1("ctid");
+	}
+	else if (target_relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+	{
+		/*
+		 * Emit Rowid so that executor can find the row to update or delete.
+		 */
+		var = makeVar(parsetree->resultRelation,
+					  RelationGetNumberOfAttributes(target_relation) + 1,
+					  CSTRINGOID,
+					  -2,
+					  InvalidOid,
+					  0);
+		varList = list_make1(var);
+
+		/*
+		 * Emit generic record Var so that executor will have the "old" view
+		 * row to pass the RETURNING clause (or upcoming triggers).
+		 */
+		var = makeVar(parsetree->resultRelation,
+					  InvalidAttrNumber,
+					  RECORDOID,
+					  -1,
+					  InvalidOid,
+					  0);
+		varList = lappend(varList, var);
 
-		attrname = "ctid";
+		attNameList = list_make2("rowid", "record");
 	}
 	else
 	{
@@ -1192,16 +1222,21 @@ rewriteTargetListUD(Query *parsetree, RangeTblEntry *target_rte,
 							  parsetree->resultRelation,
 							  0,
 							  false);
-
-		attrname = "wholerow";
+		varList = list_make1(var);
+		attNameList = list_make1("wholerow");
 	}
 
-	tle = makeTargetEntry((Expr *) var,
-						  list_length(parsetree->targetList) + 1,
-						  pstrdup(attrname),
-						  true);
-
-	parsetree->targetList = lappend(parsetree->targetList, tle);
+	/*
+	 * Append them to targetList
+	 */
+	forboth (cell1, varList, cell2, attNameList)
+	{
+		tle = makeTargetEntry((Expr *)lfirst(cell1),
+							  list_length(parsetree->targetList) + 1,
+							  pstrdup((const char *)lfirst(cell2)),
+							  true);
+		parsetree->targetList = lappend(parsetree->targetList, tle);
+	}
 }
 
 
diff --git a/src/include/foreign/fdwapi.h b/src/include/foreign/fdwapi.h
index 721cd25..153932a 100644
--- a/src/include/foreign/fdwapi.h
+++ b/src/include/foreign/fdwapi.h
@@ -14,6 +14,7 @@
 
 #include "nodes/execnodes.h"
 #include "nodes/relation.h"
+#include "utils/rel.h"
 
 /* To avoid including explain.h here, reference ExplainState thus: */
 struct ExplainState;
@@ -22,6 +23,11 @@ struct ExplainState;
 /*
  * Callback function signatures --- see fdwhandler.sgml for more info.
  */
+typedef AttrNumber (*GetForeignRelWidth_function) (PlannerInfo *root,
+												   RelOptInfo *baserel,
+												   Relation foreignrel,
+												   bool inhparent,
+												   List *targetList);
 
 typedef void (*GetForeignRelSize_function) (PlannerInfo *root,
 														RelOptInfo *baserel,
@@ -58,6 +64,24 @@ typedef int (*AcquireSampleRowsFunc) (Relation relation, int elevel,
 typedef bool (*AnalyzeForeignTable_function) (Relation relation,
 												 AcquireSampleRowsFunc *func,
 													BlockNumber *totalpages);
+typedef List *(*PlanForeignModify_function) (PlannerInfo *root,
+											 ModifyTable *plan,
+											 Index resultRelation,
+											 Plan *subplan);
+
+typedef void (*BeginForeignModify_function) (ModifyTableState *mtstate,
+											 ResultRelInfo *resultRelInfo,
+											 List *fdw_private,
+											 Plan *subplan,
+											 int eflags);
+typedef int	(*ExecForeignInsert_function) (ResultRelInfo *resultRelInfo,
+										   HeapTuple tuple);
+typedef int	(*ExecForeignDelete_function) (ResultRelInfo *resultRelInfo,
+										   Datum rowid);
+typedef int	(*ExecForeignUpdate_function) (ResultRelInfo *resultRelInfo,
+										   Datum rowid,
+										   HeapTuple tuple);
+typedef void (*EndForeignModify_function) (ResultRelInfo *resultRelInfo);
 
 /*
  * FdwRoutine is the struct returned by a foreign-data wrapper's handler
@@ -90,6 +114,13 @@ typedef struct FdwRoutine
 	 * not provided.
 	 */
 	AnalyzeForeignTable_function AnalyzeForeignTable;
+	GetForeignRelWidth_function GetForeignRelWidth;
+	PlanForeignModify_function PlanForeignModify;
+	BeginForeignModify_function	BeginForeignModify;
+	ExecForeignInsert_function ExecForeignInsert;
+	ExecForeignDelete_function ExecForeignDelete;
+	ExecForeignUpdate_function ExecForeignUpdate;
+	EndForeignModify_function EndForeignModify;
 } FdwRoutine;
 
 
diff --git a/src/include/foreign/foreign.h b/src/include/foreign/foreign.h
index f8aa99e..b63000a 100644
--- a/src/include/foreign/foreign.h
+++ b/src/include/foreign/foreign.h
@@ -14,6 +14,8 @@
 #define FOREIGN_H
 
 #include "nodes/parsenodes.h"
+#include "nodes/plannodes.h"
+#include "nodes/relation.h"
 
 
 /* Helper for obtaining username for user mapping */
@@ -81,4 +83,8 @@ extern List *GetForeignColumnOptions(Oid relid, AttrNumber attnum);
 extern Oid	get_foreign_data_wrapper_oid(const char *fdwname, bool missing_ok);
 extern Oid	get_foreign_server_oid(const char *servername, bool missing_ok);
 
+extern AttrNumber get_pseudo_rowid_column(RelOptInfo *baserel,
+										  List *targetList);
+extern ForeignScan *lookup_foreign_scan_plan(Plan *subplan,
+											 Index rtindex);
 #endif   /* FOREIGN_H */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index d4911bd..4c079b7 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -268,9 +268,11 @@ typedef struct ProjectionInfo
  *						attribute numbers of the "original" tuple and the
  *						attribute numbers of the "clean" tuple.
  *	  resultSlot:		tuple slot used to hold cleaned tuple.
- *	  junkAttNo:		not used by junkfilter code.  Can be used by caller
- *						to remember the attno of a specific junk attribute
+ *	  jf_junkRowidNo:	not used by junkfilter code.  Can be used by caller
+ *						to remember the attno of to track a particular tuple
+ *						being updated or deleted.
  *						(execMain.c stores the "ctid" attno here).
+ *	  jf_junkRecordNo:	Also, the attno of whole-row reference.
  * ----------------
  */
 typedef struct JunkFilter
@@ -280,7 +282,8 @@ typedef struct JunkFilter
 	TupleDesc	jf_cleanTupType;
 	AttrNumber *jf_cleanMap;
 	TupleTableSlot *jf_resultSlot;
-	AttrNumber	jf_junkAttNo;
+	AttrNumber	jf_junkRowidNo;
+	AttrNumber	jf_junkRecordNo;
 } JunkFilter;
 
 /* ----------------
@@ -303,6 +306,8 @@ typedef struct JunkFilter
  *		ConstraintExprs			array of constraint-checking expr states
  *		junkFilter				for removing junk attributes from tuples
  *		projectReturning		for computing a RETURNING list
+ *		fdwroutine				FDW callbacks if foreign table
+ *		fdw_state				opaque state of FDW module, or NULL
  * ----------------
  */
 typedef struct ResultRelInfo
@@ -320,6 +325,8 @@ typedef struct ResultRelInfo
 	List	  **ri_ConstraintExprs;
 	JunkFilter *ri_junkFilter;
 	ProjectionInfo *ri_projectReturning;
+	struct FdwRoutine  *ri_fdwroutine;
+	void	   *ri_fdw_state;
 } ResultRelInfo;
 
 /* ----------------
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index fb9a863..93481aa 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -175,6 +175,7 @@ typedef struct ModifyTable
 	List	   *returningLists; /* per-target-table RETURNING tlists */
 	List	   *rowMarks;		/* PlanRowMarks (non-locking only) */
 	int			epqParam;		/* ID of Param for EvalPlanQual re-eval */
+	List	   *fdwPrivList;	/* private fields for foreign tables */
 } ModifyTable;
 
 /* ----------------
@@ -478,6 +479,7 @@ typedef struct ForeignScan
 	List	   *fdw_exprs;		/* expressions that FDW may evaluate */
 	List	   *fdw_private;	/* private data for FDW */
 	bool		fsSystemCol;	/* true if any "system column" is needed */
+	bool		fsPseudoCol;	/* true if any "pseudo column" is needed */
 } ForeignScan;
 
 
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index de889fb..adfc93d 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -133,7 +133,7 @@ extern Path *reparameterize_path(PlannerInfo *root, Path *path,
  */
 extern void setup_simple_rel_arrays(PlannerInfo *root);
 extern RelOptInfo *build_simple_rel(PlannerInfo *root, int relid,
-				 RelOptKind reloptkind);
+			   List *tlist, RelOptKind reloptkind);
 extern RelOptInfo *find_base_rel(PlannerInfo *root, int relid);
 extern RelOptInfo *find_join_rel(PlannerInfo *root, Relids relids);
 extern RelOptInfo *build_join_rel(PlannerInfo *root,
diff --git a/src/include/optimizer/plancat.h b/src/include/optimizer/plancat.h
index e0d04db..c5fce41 100644
--- a/src/include/optimizer/plancat.h
+++ b/src/include/optimizer/plancat.h
@@ -26,7 +26,7 @@ extern PGDLLIMPORT get_relation_info_hook_type get_relation_info_hook;
 
 
 extern void get_relation_info(PlannerInfo *root, Oid relationObjectId,
-				  bool inhparent, RelOptInfo *rel);
+				  bool inhparent, List *tlist, RelOptInfo *rel);
 
 extern void estimate_rel_size(Relation rel, int32 *attr_widths,
 				  BlockNumber *pages, double *tuples, double *allvisfrac);
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index 0fe696c..746a603 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -79,7 +79,8 @@ extern SetOp *make_setop(SetOpCmd cmd, SetOpStrategy strategy, Plan *lefttree,
 		   long numGroups, double outputRows);
 extern Result *make_result(PlannerInfo *root, List *tlist,
 			Node *resconstantqual, Plan *subplan);
-extern ModifyTable *make_modifytable(CmdType operation, bool canSetTag,
+extern ModifyTable *make_modifytable(PlannerInfo *root,
+				 CmdType operation, bool canSetTag,
 				 List *resultRelations, List *subplans, List *returningLists,
 				 List *rowMarks, int epqParam);
 extern bool is_projection_capable_plan(Plan *plan);
@@ -90,7 +91,8 @@ extern bool is_projection_capable_plan(Plan *plan);
 extern int	from_collapse_limit;
 extern int	join_collapse_limit;
 
-extern void add_base_rels_to_query(PlannerInfo *root, Node *jtnode);
+extern void add_base_rels_to_query(PlannerInfo *root, List *tlist,
+								   Node *jtnode);
 extern void build_base_rel_tlists(PlannerInfo *root, List *final_tlist);
 extern void add_vars_to_targetlist(PlannerInfo *root, List *vars,
 					   Relids where_needed, bool create_new_ph);