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

application/octet-stream

Filename: pgsql-v9.3-writable-fdw-poc.v7.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 0 0
contrib/postgres_fdw/connection.h 0 0
contrib/postgres_fdw/deparse.c 0 0
contrib/postgres_fdw/expected/postgres_fdw.out 0 0
contrib/postgres_fdw/postgres_fdw.c 0 0
contrib/postgres_fdw/postgres_fdw.h 0 0
contrib/postgres_fdw/sql/postgres_fdw.sql 0 0
doc/src/sgml/ddl.sgml 0 0
doc/src/sgml/fdwhandler.sgml 0 0
src/backend/executor/execMain.c 0 0
src/backend/executor/nodeForeignscan.c 0 0
src/backend/executor/nodeModifyTable.c 0 0
src/backend/foreign/foreign.c 0 0
src/backend/nodes/copyfuncs.c 0 0
src/backend/nodes/outfuncs.c 0 0
src/backend/optimizer/plan/createplan.c 0 0
src/backend/optimizer/plan/initsplan.c 0 0
src/backend/optimizer/plan/planmain.c 0 0
src/backend/optimizer/plan/planner.c 0 0
src/backend/optimizer/prep/prepunion.c 0 0
src/backend/optimizer/util/plancat.c 0 0
src/backend/optimizer/util/relnode.c 0 0
src/backend/rewrite/rewriteHandler.c 0 0
src/include/foreign/fdwapi.h 0 0
src/include/foreign/foreign.h 0 0
src/include/nodes/execnodes.h 0 0
src/include/nodes/plannodes.h 0 0
src/include/optimizer/pathnode.h 0 0
src/include/optimizer/plancat.h 0 0
src/include/optimizer/planmain.h 0 0
diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
new file mode 100644
index eab8b87..9ca7fbf
*** a/contrib/postgres_fdw/connection.c
--- b/contrib/postgres_fdw/connection.c
*************** typedef struct ConnCacheEntry
*** 43,49 ****
  	Oid				serverid;	/* oid of foreign server */
  	Oid				userid;		/* oid of local user */
  
! 	bool			use_tx;		/* true when using remote transaction */
  	int				refs;		/* reference counter */
  	PGconn		   *conn;		/* foreign server connection */
  } ConnCacheEntry;
--- 43,49 ----
  	Oid				serverid;	/* oid of foreign server */
  	Oid				userid;		/* oid of local user */
  
! 	int				conntx;		/* one of PGSQL_FDW_CONNTX_* */
  	int				refs;		/* reference counter */
  	PGconn		   *conn;		/* foreign server connection */
  } ConnCacheEntry;
*************** cleanup_connection(ResourceReleasePhase 
*** 65,70 ****
--- 65,72 ----
  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
*************** static void abort_remote_tx(PGconn *conn
*** 80,86 ****
   * FDW object to invalidate already established connections.
   */
  PGconn *
! GetConnection(ForeignServer *server, UserMapping *user, bool use_tx)
  {
  	bool			found;
  	ConnCacheEntry *entry;
--- 82,88 ----
   * FDW object to invalidate already established connections.
   */
  PGconn *
! GetConnection(ForeignServer *server, UserMapping *user, int conntx)
  {
  	bool			found;
  	ConnCacheEntry *entry;
*************** GetConnection(ForeignServer *server, Use
*** 126,132 ****
  	entry = hash_search(ConnectionHash, &key, HASH_ENTER, &found);
  	if (!found)
  	{
! 		entry->use_tx = false;
  		entry->refs = 0;
  		entry->conn = NULL;
  	}
--- 128,134 ----
  	entry = hash_search(ConnectionHash, &key, HASH_ENTER, &found);
  	if (!found)
  	{
! 		entry->conntx = PGSQL_FDW_CONNTX_NONE;
  		entry->refs = 0;
  		entry->conn = NULL;
  	}
*************** GetConnection(ForeignServer *server, Use
*** 162,168 ****
  		{
  			/* Clear connection cache entry on error case. */
  			PQfinish(entry->conn);
! 			entry->use_tx = false;
  			entry->refs = 0;
  			entry->conn = NULL;
  			PG_RE_THROW();
--- 164,170 ----
  		{
  			/* Clear connection cache entry on error case. */
  			PQfinish(entry->conn);
! 			entry->conntx = PGSQL_FDW_CONNTX_NONE;
  			entry->refs = 0;
  			entry->conn = NULL;
  			PG_RE_THROW();
*************** GetConnection(ForeignServer *server, Use
*** 182,191 ****
  	 * 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)
  	{
! 		begin_remote_tx(entry->conn);
! 		entry->use_tx = use_tx;
  	}
  
  	return entry->conn;
--- 184,194 ----
  	 * are in.  We need to remember whether this connection uses remote
  	 * transaction to abort it when this connection is released completely.
  	 */
! 	if (conntx > entry->conntx)
  	{
! 		if (entry->conntx == PGSQL_FDW_CONNTX_NONE)
! 			begin_remote_tx(entry->conn);
! 		entry->conntx = conntx;
  	}
  
  	return entry->conn;
*************** abort_remote_tx(PGconn *conn)
*** 355,366 ****
  	PQclear(res);
  }
  
  /*
   * Mark the connection as "unused", and close it if the caller was the last
   * user of the connection.
   */
  void
! ReleaseConnection(PGconn *conn)
  {
  	HASH_SEQ_STATUS		scan;
  	ConnCacheEntry	   *entry;
--- 358,402 ----
  	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, bool is_abort)
  {
  	HASH_SEQ_STATUS		scan;
  	ConnCacheEntry	   *entry;
*************** ReleaseConnection(PGconn *conn)
*** 412,418 ****
  			 PQtransactionStatus(conn) == PQTRANS_INERROR ? "INERROR" :
  			 "UNKNOWN");
  		PQfinish(conn);
! 		entry->use_tx = false;
  		entry->refs = 0;
  		entry->conn = NULL;
  		return;
--- 448,454 ----
  			 PQtransactionStatus(conn) == PQTRANS_INERROR ? "INERROR" :
  			 "UNKNOWN");
  		PQfinish(conn);
! 		entry->conntx = PGSQL_FDW_CONNTX_NONE;
  		entry->refs = 0;
  		entry->conn = NULL;
  		return;
*************** ReleaseConnection(PGconn *conn)
*** 430,439 ****
  	 * 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)
  	{
! 		abort_remote_tx(conn);
! 		entry->use_tx = false;
  	}
  }
  
--- 466,481 ----
  	 * 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->conntx > PGSQL_FDW_CONNTX_NONE && entry->refs == 0)
  	{
! 		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;
  	}
  }
  
*************** cleanup_connection(ResourceReleasePhase 
*** 485,491 ****
  		elog(DEBUG3, "discard postgres_fdw connection %p due to resowner cleanup",
  			 entry->conn);
  		PQfinish(entry->conn);
! 		entry->use_tx = false;
  		entry->refs = 0;
  		entry->conn = NULL;
  	}
--- 527,533 ----
  		elog(DEBUG3, "discard postgres_fdw connection %p due to resowner cleanup",
  			 entry->conn);
  		PQfinish(entry->conn);
! 		entry->conntx = PGSQL_FDW_CONNTX_NONE;
  		entry->refs = 0;
  		entry->conn = NULL;
  	}
*************** postgres_fdw_disconnect(PG_FUNCTION_ARGS
*** 597,603 ****
  
  	/* Discard cached connection, and clear reference counter. */
  	PQfinish(entry->conn);
! 	entry->use_tx = false;
  	entry->refs = 0;
  	entry->conn = NULL;
  
--- 639,645 ----
  
  	/* Discard cached connection, and clear reference counter. */
  	PQfinish(entry->conn);
! 	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
new file mode 100644
index 4c9d850..f97cc8a
*** a/contrib/postgres_fdw/connection.h
--- b/contrib/postgres_fdw/connection.h
***************
*** 16,25 ****
  #include "foreign/foreign.h"
  #include "libpq-fe.h"
  
  /*
   * Connection management
   */
! PGconn *GetConnection(ForeignServer *server, UserMapping *user, bool use_tx);
! void ReleaseConnection(PGconn *conn);
  
  #endif /* CONNECTION_H */
--- 16,29 ----
  #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, 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
new file mode 100644
index 69e6a3e..9e09429
*** a/contrib/postgres_fdw/deparse.c
--- b/contrib/postgres_fdw/deparse.c
***************
*** 12,17 ****
--- 12,18 ----
   */
  #include "postgres.h"
  
+ #include "access/heapam.h"
  #include "access/htup_details.h"
  #include "access/transam.h"
  #include "catalog/pg_class.h"
*************** void
*** 86,94 ****
  deparseSimpleSql(StringInfo buf,
  				 PlannerInfo *root,
  				 RelOptInfo *baserel,
! 				 List *local_conds)
  {
  	RangeTblEntry *rte;
  	ListCell   *lc;
  	StringInfoData	foreign_relname;
  	bool		first;
--- 87,97 ----
  deparseSimpleSql(StringInfo buf,
  				 PlannerInfo *root,
  				 RelOptInfo *baserel,
! 				 List *local_conds,
! 				 AttrNumber anum_rowid)
  {
  	RangeTblEntry *rte;
+ 	Relation	rel;
  	ListCell   *lc;
  	StringInfoData	foreign_relname;
  	bool		first;
*************** deparseSimpleSql(StringInfo buf,
*** 125,130 ****
--- 128,151 ----
  	}
  
  	/*
+ 	 * 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.
*************** deparseSimpleSql(StringInfo buf,
*** 136,144 ****
  	 */
  	appendStringInfo(buf, "SELECT ");
  	rte = root->simple_rte_array[baserel->relid];
  	attr_used = list_union(attr_used, baserel->reltargetlist);
  	first = true;
! 	for (attr = 1; attr <= baserel->max_attr; attr++)
  	{
  		Var		   *var = NULL;
  		ListCell   *lc;
--- 157,166 ----
  	 */
  	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 <= RelationGetNumberOfAttributes(rel); attr++)
  	{
  		Var		   *var = NULL;
  		ListCell   *lc;
*************** deparseSimpleSql(StringInfo buf,
*** 167,172 ****
--- 189,198 ----
  		else
  			appendStringInfo(buf, "NULL");
  	}
+ 	if (anum_rowid != InvalidAttrNumber)
+ 		appendStringInfo(buf, "%sctid", (first ? "" : ","));
+ 
+ 	heap_close(rel, NoLock);
  	appendStringInfoChar(buf, ' ');
  
  	/*
*************** deparseAnalyzeSql(StringInfo buf, Relati
*** 283,288 ****
--- 309,410 ----
  }
  
  /*
+  * 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
new file mode 100644
index f81c727..db2c725
*** a/contrib/postgres_fdw/expected/postgres_fdw.out
--- b/contrib/postgres_fdw/expected/postgres_fdw.out
*************** SELECT srvname FROM postgres_fdw_connect
*** 736,741 ****
--- 736,1796 ----
  (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
new file mode 100644
index 6b870ab..787ee42
*** a/contrib/postgres_fdw/postgres_fdw.c
--- b/contrib/postgres_fdw/postgres_fdw.c
*************** typedef struct PostgresFdwPlanState {
*** 59,64 ****
--- 59,65 ----
  	List		   *param_conds;
  	List		   *local_conds;
  	int				width;			/* obtained by remote EXPLAIN */
+ 	AttrNumber		anum_rowid;
  
  	/* Cached catalog information. */
  	ForeignTable   *table;
*************** typedef struct PostgresAnalyzeState
*** 150,155 ****
--- 151,170 ----
  } 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);
*************** PG_FUNCTION_INFO_V1(postgres_fdw_handler
*** 158,163 ****
--- 173,183 ----
  /*
   * 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);
*************** static void postgresEndForeignScan(Forei
*** 179,184 ****
--- 199,221 ----
  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
*************** postgres_fdw_handler(PG_FUNCTION_ARGS)
*** 231,236 ****
--- 268,274 ----
  	FdwRoutine	*routine = makeNode(FdwRoutine);
  
  	/* Required handler functions. */
+ 	routine->GetForeignRelWidth = postgresGetForeignRelWidth;
  	routine->GetForeignRelSize = postgresGetForeignRelSize;
  	routine->GetForeignPaths = postgresGetForeignPaths;
  	routine->GetForeignPlan = postgresGetForeignPlan;
*************** postgres_fdw_handler(PG_FUNCTION_ARGS)
*** 239,244 ****
--- 277,288 ----
  	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;
*************** postgres_fdw_handler(PG_FUNCTION_ARGS)
*** 247,252 ****
--- 291,319 ----
  }
  
  /*
+  * 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
   *
*************** postgresGetForeignRelSize(PlannerInfo *r
*** 283,289 ****
  	 * We use PostgresFdwPlanState to pass various information to subsequent
  	 * functions.
  	 */
! 	fpstate = palloc0(sizeof(PostgresFdwPlanState));
  	initStringInfo(&fpstate->sql);
  	sql = &fpstate->sql;
  
--- 350,356 ----
  	 * We use PostgresFdwPlanState to pass various information to subsequent
  	 * functions.
  	 */
! 	fpstate = baserel->fdw_private;
  	initStringInfo(&fpstate->sql);
  	sql = &fpstate->sql;
  
*************** postgresGetForeignRelSize(PlannerInfo *r
*** 320,329 ****
  	 */
  	classifyConditions(root, baserel, &remote_conds, &param_conds,
  					   &local_conds);
! 	deparseSimpleSql(sql, root, baserel, local_conds);
  	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
--- 387,395 ----
  	 */
  	classifyConditions(root, baserel, &remote_conds, &param_conds,
  					   &local_conds);
! 	deparseSimpleSql(sql, root, baserel, local_conds, fpstate->anum_rowid);
  	if (list_length(remote_conds) > 0)
  		appendWhereClause(sql, true, remote_conds, root);
  
  	/*
  	 * If the table or the server is configured to use remote EXPLAIN, connect
*************** postgresGetForeignRelSize(PlannerInfo *r
*** 337,346 ****
  		PGconn		   *conn;
  
  		user = GetUserMapping(GetOuterUserId(), server->serverid);
! 		conn = GetConnection(server, user, false);
  		get_remote_estimate(sql->data, conn, &rows, &width,
  							&startup_cost, &total_cost);
! 		ReleaseConnection(conn);
  
  		/*
  		 * Estimate selectivity of conditions which are not used in remote
--- 403,412 ----
  		PGconn		   *conn;
  
  		user = GetUserMapping(GetOuterUserId(), server->serverid);
! 		conn = GetConnection(server, user, PGSQL_FDW_CONNTX_NONE);
  		get_remote_estimate(sql->data, conn, &rows, &width,
  							&startup_cost, &total_cost);
! 		ReleaseConnection(conn, false);
  
  		/*
  		 * Estimate selectivity of conditions which are not used in remote
*************** postgresGetForeignRelSize(PlannerInfo *r
*** 391,397 ****
  	fpstate->width = width;
  	fpstate->table = table;
  	fpstate->server = server;
- 	baserel->fdw_private = (void *) fpstate;
  }
  
  /*
--- 457,462 ----
*************** postgresBeginForeignScan(ForeignScanStat
*** 592,598 ****
  	table = GetForeignTable(relid);
  	server = GetForeignServer(table->serverid);
  	user = GetUserMapping(GetOuterUserId(), server->serverid);
! 	conn = GetConnection(server, user, true);
  	festate->conn = conn;
  
  	/* Result will be filled in first Iterate call. */
--- 657,663 ----
  	table = GetForeignTable(relid);
  	server = GetForeignServer(table->serverid);
  	user = GetUserMapping(GetOuterUserId(), server->serverid);
! 	conn = GetConnection(server, user, PGSQL_FDW_CONNTX_READ_ONLY);
  	festate->conn = conn;
  
  	/* Result will be filled in first Iterate call. */
*************** postgresEndForeignScan(ForeignScanState 
*** 724,730 ****
  	 * end of the scan to make the lifespan of remote transaction same as the
  	 * local query.
  	 */
! 	ReleaseConnection(festate->conn);
  	festate->conn = NULL;
  
  	/* Discard fetch results */
--- 789,795 ----
  	 * end of the scan to make the lifespan of remote transaction same as the
  	 * local query.
  	 */
! 	ReleaseConnection(festate->conn, false);
  	festate->conn = NULL;
  
  	/* Discard fetch results */
*************** get_remote_estimate(const char *sql, PGc
*** 790,796 ****
  		PQclear(res);
  
  		/* Release connection and let connection manager cleanup. */
! 		ReleaseConnection(conn);
  
  		PG_RE_THROW();
  	}
--- 855,861 ----
  		PQclear(res);
  
  		/* Release connection and let connection manager cleanup. */
! 		ReleaseConnection(conn, true);
  
  		PG_RE_THROW();
  	}
*************** execute_query(ForeignScanState *node)
*** 947,953 ****
  		PQclear(res);
  
  		/* Release connection and let connection manager cleanup. */
! 		ReleaseConnection(conn);
  
  		/* propagate error */
  		PG_RE_THROW();
--- 1012,1018 ----
  		PQclear(res);
  
  		/* Release connection and let connection manager cleanup. */
! 		ReleaseConnection(conn, true);
  
  		/* propagate error */
  		PG_RE_THROW();
*************** postgres_fdw_error_callback(void *arg)
*** 1105,1110 ****
--- 1170,1177 ----
  
  	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));
  }
*************** postgresAcquireSampleRowsFunc(Relation r
*** 1172,1178 ****
  	table = GetForeignTable(relation->rd_id);
  	server = GetForeignServer(table->serverid);
  	user = GetUserMapping(GetOuterUserId(), server->serverid);
! 	conn = GetConnection(server, user, true);
  
  	/*
  	 * Acquire sample rows from the result set.
--- 1239,1245 ----
  	table = GetForeignTable(relation->rd_id);
  	server = GetForeignServer(table->serverid);
  	user = GetUserMapping(GetOuterUserId(), server->serverid);
! 	conn = GetConnection(server, user, PGSQL_FDW_CONNTX_READ_ONLY);
  
  	/*
  	 * Acquire sample rows from the result set.
*************** postgresAcquireSampleRowsFunc(Relation r
*** 1239,1251 ****
  		PQclear(res);
  
  		/* Release connection and let connection manager cleanup. */
! 		ReleaseConnection(conn);
  
  		PG_RE_THROW();
  	}
  	PG_END_TRY();
  
! 	ReleaseConnection(conn);
  
  	/* We assume that we have no dead tuple. */
  	*totaldeadrows = 0.0;
--- 1306,1318 ----
  		PQclear(res);
  
  		/* Release connection and let connection manager cleanup. */
! 		ReleaseConnection(conn, true);
  
  		PG_RE_THROW();
  	}
  	PG_END_TRY();
  
! 	ReleaseConnection(conn, false);
  
  	/* We assume that we have no dead tuple. */
  	*totaldeadrows = 0.0;
*************** analyze_row_processor(PGresult *res, Pos
*** 1429,1431 ****
--- 1496,1813 ----
  
  	return;
  }
+ 
+ static List *
+ postgresPlanForeignModify(PlannerInfo *root,
+ 						  ModifyTable *plan,
+ 						  Index resultRelation,
+ 						  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, resultRelation);
+ 		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, resultRelation);
+ 			elog(DEBUG3, "Remote INSERT query: %s", sql.data);
+ 			break;
+ 		case CMD_UPDATE:
+ 			deparseUpdateSql(&sql, root, resultRelation);
+ 			elog(DEBUG3, "Remote UPDATE query: %s", sql.data);
+ 			break;
+ 		case CMD_DELETE:
+ 			deparseDeleteSql(&sql, root, resultRelation);
+ 			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
new file mode 100644
index b5cefb8..3756a0d
*** a/contrib/postgres_fdw/postgres_fdw.h
--- b/contrib/postgres_fdw/postgres_fdw.h
*************** int GetFetchCountOption(ForeignTable *ta
*** 30,36 ****
  void deparseSimpleSql(StringInfo buf,
  					  PlannerInfo *root,
  					  RelOptInfo *baserel,
! 					  List *local_conds);
  void appendWhereClause(StringInfo buf,
  					   bool has_where,
  					   List *exprs,
--- 30,37 ----
  void deparseSimpleSql(StringInfo buf,
  					  PlannerInfo *root,
  					  RelOptInfo *baserel,
! 					  List *local_conds,
! 					  AttrNumber anum_rowid);
  void appendWhereClause(StringInfo buf,
  					   bool has_where,
  					   List *exprs,
*************** void classifyConditions(PlannerInfo *roo
*** 41,45 ****
--- 42,49 ----
  						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
new file mode 100644
index 7845e70..70b09b4
*** a/contrib/postgres_fdw/sql/postgres_fdw.sql
--- b/contrib/postgres_fdw/sql/postgres_fdw.sql
*************** ERROR OUT;          -- ERROR
*** 300,305 ****
--- 300,318 ----
  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/ddl.sgml b/doc/src/sgml/ddl.sgml
new file mode 100644
index d6e5d64..30f9c6b
*** a/doc/src/sgml/ddl.sgml
--- b/doc/src/sgml/ddl.sgml
*************** ANALYZE measurement;
*** 3066,3076 ****
      <firstterm>user mapping</>, which can provide additional options based
      on the current <productname>PostgreSQL</productname> role.
     </para>
- 
-    <para>
-     Currently, foreign tables are read-only.  This limitation may be fixed
-     in a future release.
-    </para>
   </sect1>
  
   <sect1 id="ddl-others">
--- 3066,3071 ----
diff --git a/doc/src/sgml/fdwhandler.sgml b/doc/src/sgml/fdwhandler.sgml
new file mode 100644
index 2d604ed..cbf3181
*** a/doc/src/sgml/fdwhandler.sgml
--- b/doc/src/sgml/fdwhandler.sgml
***************
*** 89,94 ****
--- 89,142 ----
  
      <para>
  <programlisting>
+ AttrNumber
+ GetForeignRelWidth(PlannerInfo *root,
+                    RelOptInfo *baserel,
+                    Relation foreignrel,
+                    bool inhparent,
+                    List *targetList);
+ </programlisting>
+      Obtain the width of the result set to be fetched during a foreign table scan.
+      This is an optional handler, and called before <literal>GetForeignRelSize</>
+      for a query involving a foreign table
+      (during the construction of <literal>RelOptInfo</>).
+      <literal>root</> is the planner's global information about the query,
+      <literal>baserel</> is the planner's information being constructed for
+      this query, and <literal>foreignrel</> is a <literal>Relation</>
+      descriptor of the foreign table.
+      <literal>inhparent</> is a boolean to show whether the relation is
+      an inheritance parent, even though foreign tables do not support table
+      inheritance right now. <literal>targetList</> is the list of
+      <literal>TargetEntry</> to be returned from the (sub-)query
+      that is currently in focus.
+     </para>
+ 
+     <para>
+      The result value of this function will be assigned to
+      <literal>baserel-&gt;max_attr</>, that means it is the expected number
+      of columns being fetched during the foreign table scan.
+      It should not be smaller than the number of regular columns in the definition
+      of this foreign table.  You can only return a number greater than his value to
+      acquire slots for some additional attributes, which are called
+      <firstterm>pseudo-columns</>.
+      A typical usage of a pseudo-column is to carry an identifier of
+      a particular remote row to be updated or deleted from the scanning stage
+      to the modifying stage when the foreign table is the target of
+      a data-modifying SQL statement.
+      You can return the result of the helper function
+      <literal>get_pseudo_rowid_column</> if this <literal>"rowid"</>
+      pseudo-column is the only one you need.
+    </para>
+ 
+    <para>
+      In addition to that, pseudo-columns can be used to off-load the burden of
+      complex calculations to foreign computing resources by replacing an
+      expression with a reference to its result, which is calculated on the
+      remote side rather than locally.
+    </para>
+ 
+     <para>
+ <programlisting>
  void
  GetForeignRelSize (PlannerInfo *root,
                     RelOptInfo *baserel,
*************** GetForeignRelSize (PlannerInfo *root,
*** 96,102 ****
  </programlisting>
  
       Obtain relation size estimates for a foreign table.  This is called
!      at the beginning of planning for a query involving a foreign table.
       <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
--- 144,151 ----
  </programlisting>
  
       Obtain relation size estimates for a foreign table.  This is called
!      for a query involving a foreign table at the beginning of planning
!      or right after <literal>GetForeignRelWidth</>, if that callback is 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
*************** AcquireSampleRowsFunc (Relation relation
*** 315,320 ****
--- 364,479 ----
      </para>
  
      <para>
+      If a FDW supports writable foreign tables, it must implement at least both
+      <literal>BeginForeignModify</> and <literal>EndForeignModify</>.
+      In addition, some or all of <literal>ExecForeignInsert</>,
+      <literal>ExecForeignUpdate</> and <literal>ExecForeignDelete</> need
+      to be implement according to the capability of the FDW.
+     </para>     
+ 
+     <para>
+ <programlisting>
+ List *
+ PlanForeignModify(PlannerInfo *root,
+                   ModifyTable *plan,
+                   Index resultRelation,
+                   Plan *subplan);
+ </programlisting>
+      It allows FDW drivers to construct private information relevant to
+      the modification of the foreign table.  This private information must have
+      the form of a <literal>List *</>, which will be delivered as
+      third argument to <literal>BeginForeignModify</> during the execution stage.
+     </para>
+ 
+     <para>
+      <literal>root</> is the planner's global information about the query.
+      <literal>plan</> is the master plan to modify the result relation according
+      to its command type.  Please consider that <literal>ModifyTable</>
+      may have multiple result relations in a future revision, even though
+      currently there is no support for table inheritance on foreign tables.
+      <literal>resultRelation</> is an index for the result relation in the
+      range table entries, and <literal>subplan</> is the relevant scan plan;
+      that should be a <literal>ForeignScan</> for <literal>UPDATE</> or
+      <literal>DELETE</>, so the driver can access the private information of
+      the 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, during
+      executor startup.  This routine should perform any initialization
+      needed prior to the actual table modifications, but not start
+      modifying the actual tuples. (That should be done during 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 information relevant to this foreign table,
+      so it should be initialized to contain the per-scan state.
+      Please consider that <literal>ModifyTableState</> may have multiple
+      result relations in a future revision, even though currently there is no
+      support for table inheritance on foreign tables.  <literal>resultRelInfo</>
+      is the master information connected to this foreign table.
+      <literal>fdw_private</> is private information constructed in
+      <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 on behalf of the foreign table.
+      The supplied tuple is already formed according to the definition of
+      the relation, thus all the pseudo-columns are already filtered out.
+      It can return the 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</> from the backing
+      storage on behalf of the foreign table.
+      It can return the 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 on behalf of the foreign table with the given tuple.
+      It can return the 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
new file mode 100644
index dbd3755..ccc1f3a
*** a/src/backend/executor/execMain.c
--- b/src/backend/executor/execMain.c
***************
*** 44,49 ****
--- 44,50 ----
  #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"
*************** void
*** 934,939 ****
--- 935,941 ----
  CheckValidResultRel(Relation resultRel, CmdType operation)
  {
  	TriggerDesc *trigDesc = resultRel->trigdesc;
+ 	FdwRoutine	*fdwroutine;
  
  	switch (resultRel->rd_rel->relkind)
  	{
*************** CheckValidResultRel(Relation resultRel, 
*** 985,994 ****
  			}
  			break;
  		case RELKIND_FOREIGN_TABLE:
! 			ereport(ERROR,
! 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
! 					 errmsg("cannot change foreign table \"%s\"",
! 							RelationGetRelationName(resultRel))));
  			break;
  		default:
  			ereport(ERROR,
--- 987,1020 ----
  			}
  			break;
  		case RELKIND_FOREIGN_TABLE:
! 			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
new file mode 100644
index 9204859..1ee626f
*** a/src/backend/executor/nodeForeignscan.c
--- b/src/backend/executor/nodeForeignscan.c
***************
*** 25,30 ****
--- 25,31 ----
  #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);
*************** ExecForeignScan(ForeignScanState *node)
*** 93,98 ****
--- 94,226 ----
  					(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
*************** ExecInitForeignScan(ForeignScan *node, E
*** 103,108 ****
--- 231,237 ----
  {
  	ForeignScanState *scanstate;
  	Relation	currentRelation;
+ 	TupleDesc	tupdesc;
  	FdwRoutine *fdwroutine;
  
  	/* check for unsupported flags */
*************** ExecInitForeignScan(ForeignScan *node, E
*** 149,155 ****
  	/*
  	 * get the scan type from the relation descriptor.
  	 */
! 	ExecAssignScanType(&scanstate->ss, RelationGetDescr(currentRelation));
  
  	/*
  	 * Initialize result tuple type and projection info.
--- 278,289 ----
  	/*
  	 * get the scan type from the relation descriptor.
  	 */
! 	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
new file mode 100644
index d31015c..bf9421e
*** a/src/backend/executor/nodeModifyTable.c
--- b/src/backend/executor/nodeModifyTable.c
***************
*** 42,47 ****
--- 42,48 ----
  #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"
*************** ExecInsert(TupleTableSlot *slot,
*** 170,175 ****
--- 171,177 ----
  	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
*************** ExecInsert(TupleTableSlot *slot,
*** 225,230 ****
--- 227,240 ----
  
  		newId = InvalidOid;
  	}
+ 	else if (resultRelInfo->ri_fdwroutine)
+ 	{
+ 		FdwRoutine *fdwroutine = resultRelInfo->ri_fdwroutine;
+ 
+ 		num_rows = fdwroutine->ExecForeignInsert(resultRelInfo, tuple);
+ 
+ 		newId = InvalidOid;
+ 	}
  	else
  	{
  		/*
*************** ExecInsert(TupleTableSlot *slot,
*** 252,258 ****
  
  	if (canSetTag)
  	{
! 		(estate->es_processed)++;
  		estate->es_lastoid = newId;
  		setLastTid(&(tuple->t_self));
  	}
--- 262,268 ----
  
  	if (canSetTag)
  	{
! 		(estate->es_processed) += num_rows;
  		estate->es_lastoid = newId;
  		setLastTid(&(tuple->t_self));
  	}
*************** ExecInsert(TupleTableSlot *slot,
*** 285,291 ****
   * ----------------------------------------------------------------
   */
  static TupleTableSlot *
! ExecDelete(ItemPointer tupleid,
  		   HeapTupleHeader oldtuple,
  		   TupleTableSlot *planSlot,
  		   EPQState *epqstate,
--- 295,301 ----
   * ----------------------------------------------------------------
   */
  static TupleTableSlot *
! ExecDelete(Datum rowid,
  		   HeapTupleHeader oldtuple,
  		   TupleTableSlot *planSlot,
  		   EPQState *epqstate,
*************** ExecDelete(ItemPointer tupleid,
*** 296,301 ****
--- 306,312 ----
  	Relation	resultRelationDesc;
  	HTSU_Result result;
  	HeapUpdateFailureData hufd;
+ 	int			num_rows = 1;
  
  	/*
  	 * get information on the (current) result relation
*************** ExecDelete(ItemPointer tupleid,
*** 310,316 ****
  		bool		dodelete;
  
  		dodelete = ExecBRDeleteTriggers(estate, epqstate, resultRelInfo,
! 										tupleid);
  
  		if (!dodelete)			/* "do nothing" */
  			return NULL;
--- 321,327 ----
  		bool		dodelete;
  
  		dodelete = ExecBRDeleteTriggers(estate, epqstate, resultRelInfo,
! 										(ItemPointer)DatumGetPointer(rowid));
  
  		if (!dodelete)			/* "do nothing" */
  			return NULL;
*************** ExecDelete(ItemPointer tupleid,
*** 334,341 ****
--- 345,360 ----
  		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
  		 *
*************** ldelete:;
*** 430,439 ****
  	}
  
  	if (canSetTag)
! 		(estate->es_processed)++;
  
  	/* AFTER ROW DELETE Triggers */
! 	ExecARDeleteTriggers(estate, resultRelInfo, tupleid);
  
  	/* Process RETURNING if present */
  	if (resultRelInfo->ri_projectReturning)
--- 449,459 ----
  	}
  
  	if (canSetTag)
! 		(estate->es_processed) += num_rows;
  
  	/* AFTER ROW DELETE Triggers */
! 	ExecARDeleteTriggers(estate, resultRelInfo,
! 						 (ItemPointer)DatumGetPointer(rowid));
  
  	/* Process RETURNING if present */
  	if (resultRelInfo->ri_projectReturning)
*************** ldelete:;
*** 457,463 ****
  		}
  		else
  		{
! 			deltuple.t_self = *tupleid;
  			if (!heap_fetch(resultRelationDesc, SnapshotAny,
  							&deltuple, &delbuffer, false, NULL))
  				elog(ERROR, "failed to fetch deleted tuple for DELETE RETURNING");
--- 477,484 ----
  		}
  		else
  		{
! 			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");
*************** ldelete:;
*** 499,505 ****
   * ----------------------------------------------------------------
   */
  static TupleTableSlot *
! ExecUpdate(ItemPointer tupleid,
  		   HeapTupleHeader oldtuple,
  		   TupleTableSlot *slot,
  		   TupleTableSlot *planSlot,
--- 520,526 ----
   * ----------------------------------------------------------------
   */
  static TupleTableSlot *
! ExecUpdate(Datum rowid,
  		   HeapTupleHeader oldtuple,
  		   TupleTableSlot *slot,
  		   TupleTableSlot *planSlot,
*************** ExecUpdate(ItemPointer tupleid,
*** 513,518 ****
--- 534,540 ----
  	HTSU_Result result;
  	HeapUpdateFailureData hufd;
  	List	   *recheckIndexes = NIL;
+ 	int			num_rows = 1;
  
  	/*
  	 * abort the operation if not running transactions
*************** ExecUpdate(ItemPointer tupleid,
*** 537,543 ****
  		resultRelInfo->ri_TrigDesc->trig_update_before_row)
  	{
  		slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
! 									tupleid, slot);
  
  		if (slot == NULL)		/* "do nothing" */
  			return NULL;
--- 559,565 ----
  		resultRelInfo->ri_TrigDesc->trig_update_before_row)
  	{
  		slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
! 									(ItemPointer)DatumGetPointer(rowid), slot);
  
  		if (slot == NULL)		/* "do nothing" */
  			return NULL;
*************** ExecUpdate(ItemPointer tupleid,
*** 567,574 ****
--- 589,604 ----
  		/* 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
  		 *
*************** lreplace:;
*** 687,697 ****
  	}
  
  	if (canSetTag)
! 		(estate->es_processed)++;
  
  	/* AFTER ROW UPDATE Triggers */
! 	ExecARUpdateTriggers(estate, resultRelInfo, tupleid, tuple,
! 						 recheckIndexes);
  
  	list_free(recheckIndexes);
  
--- 717,728 ----
  	}
  
  	if (canSetTag)
! 		(estate->es_processed) += num_rows;
  
  	/* AFTER ROW UPDATE Triggers */
! 	ExecARUpdateTriggers(estate, resultRelInfo,
! 						 (ItemPointer) DatumGetPointer(rowid),
! 						 tuple, recheckIndexes);
  
  	list_free(recheckIndexes);
  
*************** ExecModifyTable(ModifyTableState *node)
*** 771,776 ****
--- 802,808 ----
  	TupleTableSlot *planSlot;
  	ItemPointer tupleid = NULL;
  	ItemPointerData tuple_ctid;
+ 	Datum		rowid = 0;
  	HeapTupleHeader oldtuple = NULL;
  
  	/*
*************** ExecModifyTable(ModifyTableState *node)
*** 859,875 ****
  		if (junkfilter != NULL)
  		{
  			/*
! 			 * extract the 'ctid' or 'wholerow' junk attribute.
  			 */
  			if (operation == CMD_UPDATE || operation == CMD_DELETE)
  			{
  				Datum		datum;
  				bool		isNull;
  
! 				if (resultRelInfo->ri_RelationDesc->rd_rel->relkind == RELKIND_RELATION)
  				{
  					datum = ExecGetJunkAttribute(slot,
! 												 junkfilter->jf_junkAttNo,
  												 &isNull);
  					/* shouldn't ever get a null result... */
  					if (isNull)
--- 891,909 ----
  		if (junkfilter != NULL)
  		{
  			/*
! 			 * extract the 'ctid', 'rowid' or 'wholerow' junk attribute.
  			 */
  			if (operation == CMD_UPDATE || operation == CMD_DELETE)
  			{
+ 				char		relkind;
  				Datum		datum;
  				bool		isNull;
  
! 				relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
! 				if (relkind == RELKIND_RELATION)
  				{
  					datum = ExecGetJunkAttribute(slot,
! 												 junkfilter->jf_junkRowidNo,
  												 &isNull);
  					/* shouldn't ever get a null result... */
  					if (isNull)
*************** ExecModifyTable(ModifyTableState *node)
*** 877,889 ****
  
  					tupleid = (ItemPointer) DatumGetPointer(datum);
  					tuple_ctid = *tupleid;		/* be sure we don't free
! 												 * ctid!! */
! 					tupleid = &tuple_ctid;
  				}
  				else
  				{
  					datum = ExecGetJunkAttribute(slot,
! 												 junkfilter->jf_junkAttNo,
  												 &isNull);
  					/* shouldn't ever get a null result... */
  					if (isNull)
--- 911,943 ----
  
  					tupleid = (ItemPointer) DatumGetPointer(datum);
  					tuple_ctid = *tupleid;		/* be sure we don't free
! 												 * 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_junkRecordNo,
  												 &isNull);
  					/* shouldn't ever get a null result... */
  					if (isNull)
*************** ExecModifyTable(ModifyTableState *node)
*** 906,916 ****
  				slot = ExecInsert(slot, planSlot, estate, node->canSetTag);
  				break;
  			case CMD_UPDATE:
! 				slot = ExecUpdate(tupleid, oldtuple, slot, planSlot,
  								&node->mt_epqstate, estate, node->canSetTag);
  				break;
  			case CMD_DELETE:
! 				slot = ExecDelete(tupleid, oldtuple, planSlot,
  								&node->mt_epqstate, estate, node->canSetTag);
  				break;
  			default:
--- 960,970 ----
  				slot = ExecInsert(slot, planSlot, estate, node->canSetTag);
  				break;
  			case CMD_UPDATE:
! 				slot = ExecUpdate(rowid, oldtuple, slot, planSlot,
  								&node->mt_epqstate, estate, node->canSetTag);
  				break;
  			case CMD_DELETE:
! 				slot = ExecDelete(rowid, oldtuple, planSlot,
  								&node->mt_epqstate, estate, node->canSetTag);
  				break;
  			default:
*************** ExecInitModifyTable(ModifyTable *node, E
*** 997,1002 ****
--- 1051,1058 ----
  	i = 0;
  	foreach(l, node->plans)
  	{
+ 		char	relkind;
+ 
  		subplan = (Plan *) lfirst(l);
  
  		/*
*************** ExecInitModifyTable(ModifyTable *node, E
*** 1022,1027 ****
--- 1078,1101 ----
  		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++;
  	}
*************** ExecInitModifyTable(ModifyTable *node, E
*** 1163,1168 ****
--- 1237,1244 ----
  			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)
*************** ExecInitModifyTable(ModifyTable *node, E
*** 1176,1191 ****
  				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)
  					{
! 						j->jf_junkAttNo = ExecFindJunkAttribute(j, "ctid");
! 						if (!AttributeNumberIsValid(j->jf_junkAttNo))
  							elog(ERROR, "could not find junk ctid column");
  					}
  					else
  					{
! 						j->jf_junkAttNo = ExecFindJunkAttribute(j, "wholerow");
! 						if (!AttributeNumberIsValid(j->jf_junkAttNo))
  							elog(ERROR, "could not find junk wholerow column");
  					}
  				}
--- 1252,1278 ----
  				if (operation == CMD_UPDATE || operation == CMD_DELETE)
  				{
  					/* For UPDATE/DELETE, find the appropriate junk attr now */
! 					if (relkind == RELKIND_RELATION)
  					{
! 						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_junkRecordNo
! 							= ExecFindJunkAttribute(j, "wholerow");
! 						if (!AttributeNumberIsValid(j->jf_junkRecordNo))
  							elog(ERROR, "could not find junk wholerow column");
  					}
  				}
*************** ExecEndModifyTable(ModifyTableState *nod
*** 1239,1244 ****
--- 1326,1346 ----
  {
  	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
new file mode 100644
index d8845aa..4363a28
*** a/src/backend/foreign/foreign.c
--- b/src/backend/foreign/foreign.c
*************** get_foreign_server_oid(const char *serve
*** 571,573 ****
--- 571,641 ----
  				 errmsg("server \"%s\" does not exist", servername)));
  	return oid;
  }
+ 
+ /*
+  * get_pseudo_rowid_column
+  *
+  * It picks up an attribute number to be used for the pseudo rowid column,
+  * if it exists.  It should be injected at rewriteHandler.c if the supplied query
+  * is a 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 the ForeignScan plan node with the supplied range-table id.
+  * Its typical usage is for PlanForeignModify to get the 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
new file mode 100644
index 9387ee9..5b1e8ac
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
*************** _copyModifyTable(const ModifyTable *from
*** 181,186 ****
--- 181,187 ----
  	COPY_NODE_FIELD(returningLists);
  	COPY_NODE_FIELD(rowMarks);
  	COPY_SCALAR_FIELD(epqParam);
+ 	COPY_NODE_FIELD(fdwPrivList);
  
  	return newnode;
  }
*************** _copyForeignScan(const ForeignScan *from
*** 594,599 ****
--- 595,601 ----
  	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
new file mode 100644
index 35c6287..c32ebd3
*** a/src/backend/nodes/outfuncs.c
--- b/src/backend/nodes/outfuncs.c
*************** _outModifyTable(StringInfo str, const Mo
*** 335,340 ****
--- 335,341 ----
  	WRITE_NODE_FIELD(returningLists);
  	WRITE_NODE_FIELD(rowMarks);
  	WRITE_INT_FIELD(epqParam);
+ 	WRITE_NODE_FIELD(fdwPrivList);
  }
  
  static void
*************** _outForeignScan(StringInfo str, const Fo
*** 562,567 ****
--- 563,569 ----
  	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
new file mode 100644
index 030f420..91db1b8
*** a/src/backend/optimizer/plan/createplan.c
--- b/src/backend/optimizer/plan/createplan.c
***************
*** 39,44 ****
--- 39,45 ----
  #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);
*************** create_foreignscan_plan(PlannerInfo *roo
*** 1943,1948 ****
--- 1944,1951 ----
  	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... */
*************** create_foreignscan_plan(PlannerInfo *roo
*** 2001,2006 ****
--- 2004,2025 ----
  		}
  	}
  
+ 	/*
+ 	 * 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;
  }
  
*************** make_result(PlannerInfo *root,
*** 4695,4701 ****
   * to make it look better sometime.
   */
  ModifyTable *
! make_modifytable(CmdType operation, bool canSetTag,
  				 List *resultRelations,
  				 List *subplans, List *returningLists,
  				 List *rowMarks, int epqParam)
--- 4714,4721 ----
   * to make it look better sometime.
   */
  ModifyTable *
! make_modifytable(PlannerInfo *root,
! 				 CmdType operation, bool canSetTag,
  				 List *resultRelations,
  				 List *subplans, List *returningLists,
  				 List *rowMarks, int epqParam)
*************** make_modifytable(CmdType operation, bool
*** 4704,4709 ****
--- 4724,4731 ----
  	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 ||
*************** make_modifytable(CmdType operation, bool
*** 4746,4751 ****
--- 4768,4797 ----
  	node->rowMarks = rowMarks;
  	node->epqParam = epqParam;
  
+ 	/*
+ 	 * Allow FDW driver to construct its private plan if the result relation
+ 	 * is a 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
new file mode 100644
index bd719b5..57ba192
*** a/src/backend/optimizer/plan/initsplan.c
--- b/src/backend/optimizer/plan/initsplan.c
*************** static void check_hashjoinable(RestrictI
*** 85,91 ****
   * "other rel" RelOptInfos for the members of any appendrels we find here.)
   */
  void
! add_base_rels_to_query(PlannerInfo *root, Node *jtnode)
  {
  	if (jtnode == NULL)
  		return;
--- 85,91 ----
   * "other rel" RelOptInfos for the members of any appendrels we find here.)
   */
  void
! add_base_rels_to_query(PlannerInfo *root, List *tlist, Node *jtnode)
  {
  	if (jtnode == NULL)
  		return;
*************** add_base_rels_to_query(PlannerInfo *root
*** 93,99 ****
  	{
  		int			varno = ((RangeTblRef *) jtnode)->rtindex;
  
! 		(void) build_simple_rel(root, varno, RELOPT_BASEREL);
  	}
  	else if (IsA(jtnode, FromExpr))
  	{
--- 93,99 ----
  	{
  		int			varno = ((RangeTblRef *) jtnode)->rtindex;
  
! 		(void) build_simple_rel(root, varno, tlist, RELOPT_BASEREL);
  	}
  	else if (IsA(jtnode, FromExpr))
  	{
*************** add_base_rels_to_query(PlannerInfo *root
*** 101,114 ****
  		ListCell   *l;
  
  		foreach(l, f->fromlist)
! 			add_base_rels_to_query(root, 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);
  	}
  	else
  		elog(ERROR, "unrecognized node type: %d",
--- 101,114 ----
  		ListCell   *l;
  
  		foreach(l, f->fromlist)
! 			add_base_rels_to_query(root, tlist, lfirst(l));
  	}
  	else if (IsA(jtnode, JoinExpr))
  	{
  		JoinExpr   *j = (JoinExpr *) jtnode;
  
! 		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
new file mode 100644
index c2488a4..4442444
*** a/src/backend/optimizer/plan/planmain.c
--- b/src/backend/optimizer/plan/planmain.c
*************** query_planner(PlannerInfo *root, List *t
*** 112,117 ****
--- 112,120 ----
  	 */
  	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);
*************** query_planner(PlannerInfo *root, List *t
*** 163,169 ****
  	 * 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);
  
  	/*
  	 * Examine the targetlist and join tree, adding entries to baserel
--- 166,172 ----
  	 * 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, 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
new file mode 100644
index b61005f..2d72244
*** a/src/backend/optimizer/plan/planner.c
--- b/src/backend/optimizer/plan/planner.c
*************** subquery_planner(PlannerGlobal *glob, Qu
*** 571,577 ****
  			else
  				rowMarks = root->rowMarks;
  
! 			plan = (Plan *) make_modifytable(parse->commandType,
  											 parse->canSetTag,
  									   list_make1_int(parse->resultRelation),
  											 list_make1(plan),
--- 571,578 ----
  			else
  				rowMarks = root->rowMarks;
  
! 			plan = (Plan *) make_modifytable(root,
! 											 parse->commandType,
  											 parse->canSetTag,
  									   list_make1_int(parse->resultRelation),
  											 list_make1(plan),
*************** inheritance_planner(PlannerInfo *root)
*** 964,970 ****
  		rowMarks = root->rowMarks;
  
  	/* And last, tack on a ModifyTable node to do the UPDATE/DELETE work */
! 	return (Plan *) make_modifytable(parse->commandType,
  									 parse->canSetTag,
  									 resultRelations,
  									 subplans,
--- 965,972 ----
  		rowMarks = root->rowMarks;
  
  	/* And last, tack on a ModifyTable node to do the UPDATE/DELETE work */
! 	return (Plan *) make_modifytable(root,
! 									 parse->commandType,
  									 parse->canSetTag,
  									 resultRelations,
  									 subplans,
*************** plan_cluster_use_sort(Oid tableOid, Oid 
*** 3385,3391 ****
  	setup_simple_rel_arrays(root);
  
  	/* Build RelOptInfo */
! 	rel = build_simple_rel(root, 1, RELOPT_BASEREL);
  
  	/* Locate IndexOptInfo for the target index */
  	indexInfo = NULL;
--- 3387,3393 ----
  	setup_simple_rel_arrays(root);
  
  	/* Build RelOptInfo */
! 	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
new file mode 100644
index b91e9f4..61bc447
*** a/src/backend/optimizer/prep/prepunion.c
--- b/src/backend/optimizer/prep/prepunion.c
*************** recurse_set_operations(Node *setOp, Plan
*** 236,242 ****
  		 * 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);
  
  		/* plan_params should not be in use in current query level */
  		Assert(root->plan_params == NIL);
--- 236,243 ----
  		 * used for anything here, but it carries the subroot data structures
  		 * forward to setrefs.c processing.
  		 */
! 		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
new file mode 100644
index 04d5028..7d6bc4b
*** a/src/backend/optimizer/util/plancat.c
--- b/src/backend/optimizer/util/plancat.c
***************
*** 25,30 ****
--- 25,31 ----
  #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"
*************** static List *build_index_tlist(PlannerIn
*** 80,86 ****
   */
  void
  get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
! 				  RelOptInfo *rel)
  {
  	Index		varno = rel->relid;
  	Relation	relation;
--- 81,87 ----
   */
  void
  get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
! 				  List *tlist, RelOptInfo *rel)
  {
  	Index		varno = rel->relid;
  	Relation	relation;
*************** get_relation_info(PlannerInfo *root, Oid
*** 104,109 ****
--- 105,134 ----
  	rel->max_attr = RelationGetNumberOfAttributes(relation);
  	rel->reltablespace = RelationGetForm(relation)->reltablespace;
  
+ 	/*
+ 	 * Adjust width of attr_needed slot in case FDW extension wants
+ 	 * to return pseudo-columns in addition to the columns in its
+ 	 * table definition.
+ 	 * GetForeignRelWidth, an optional FDW handler, enables a FDW
+ 	 * to save properties of a pseudo-column in its private field.
+ 	 * When the foreign table is the target of UPDATE/DELETE, the query rewriter
+ 	 * injects a "rowid" pseudo-column to track the remote row to be modified,
+ 	 * so the 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
new file mode 100644
index f724714..84b674c
*** a/src/backend/optimizer/util/relnode.c
--- b/src/backend/optimizer/util/relnode.c
*************** setup_simple_rel_arrays(PlannerInfo *roo
*** 80,86 ****
   *	  Construct a new RelOptInfo for a base relation or 'other' relation.
   */
  RelOptInfo *
! build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
  {
  	RelOptInfo *rel;
  	RangeTblEntry *rte;
--- 80,87 ----
   *	  Construct a new RelOptInfo for a base relation or 'other' relation.
   */
  RelOptInfo *
! build_simple_rel(PlannerInfo *root, int relid, List *tlist,
! 				 RelOptKind reloptkind)
  {
  	RelOptInfo *rel;
  	RangeTblEntry *rte;
*************** build_simple_rel(PlannerInfo *root, int 
*** 133,139 ****
  	{
  		case RTE_RELATION:
  			/* Table --- retrieve statistics from the system catalogs */
! 			get_relation_info(root, rte->relid, rte->inh, rel);
  			break;
  		case RTE_SUBQUERY:
  		case RTE_FUNCTION:
--- 134,140 ----
  	{
  		case RTE_RELATION:
  			/* Table --- retrieve statistics from the system catalogs */
! 			get_relation_info(root, rte->relid, rte->inh, tlist, rel);
  			break;
  		case RTE_SUBQUERY:
  		case RTE_FUNCTION:
*************** build_simple_rel(PlannerInfo *root, int 
*** 180,186 ****
  			if (appinfo->parent_relid != relid)
  				continue;
  
! 			(void) build_simple_rel(root, appinfo->child_relid,
  									RELOPT_OTHER_MEMBER_REL);
  		}
  	}
--- 181,187 ----
  			if (appinfo->parent_relid != relid)
  				continue;
  
! 			(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
new file mode 100644
index b785c26..6cd6241
*** a/src/backend/rewrite/rewriteHandler.c
--- b/src/backend/rewrite/rewriteHandler.c
*************** rewriteTargetListUD(Query *parsetree, Ra
*** 1165,1171 ****
  					Relation target_relation)
  {
  	Var		   *var;
! 	const char *attrname;
  	TargetEntry *tle;
  
  	if (target_relation->rd_rel->relkind == RELKIND_RELATION)
--- 1165,1174 ----
  					Relation target_relation)
  {
  	Var		   *var;
! 	List	   *varList;
! 	List	   *attNameList;
! 	ListCell   *cell1;
! 	ListCell   *cell2;
  	TargetEntry *tle;
  
  	if (target_relation->rd_rel->relkind == RELKIND_RELATION)
*************** rewriteTargetListUD(Query *parsetree, Ra
*** 1179,1186 ****
  					  -1,
  					  InvalidOid,
  					  0);
  
! 		attrname = "ctid";
  	}
  	else
  	{
--- 1182,1216 ----
  					  -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);
! 
! 		attNameList = list_make2("rowid", "record");
  	}
  	else
  	{
*************** rewriteTargetListUD(Query *parsetree, Ra
*** 1192,1207 ****
  							  parsetree->resultRelation,
  							  0,
  							  false);
! 
! 		attrname = "wholerow";
  	}
  
! 	tle = makeTargetEntry((Expr *) var,
! 						  list_length(parsetree->targetList) + 1,
! 						  pstrdup(attrname),
! 						  true);
! 
! 	parsetree->targetList = lappend(parsetree->targetList, tle);
  }
  
  
--- 1222,1242 ----
  							  parsetree->resultRelation,
  							  0,
  							  false);
! 		varList = list_make1(var);
! 		attNameList = list_make1("wholerow");
  	}
  
! 	/*
! 	 * 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
new file mode 100644
index 721cd25..153932a
*** a/src/include/foreign/fdwapi.h
--- b/src/include/foreign/fdwapi.h
***************
*** 14,19 ****
--- 14,20 ----
  
  #include "nodes/execnodes.h"
  #include "nodes/relation.h"
+ #include "utils/rel.h"
  
  /* To avoid including explain.h here, reference ExplainState thus: */
  struct ExplainState;
*************** struct ExplainState;
*** 22,27 ****
--- 23,33 ----
  /*
   * 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,
*************** typedef int (*AcquireSampleRowsFunc) (Re
*** 58,63 ****
--- 64,87 ----
  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
*************** typedef struct FdwRoutine
*** 90,95 ****
--- 114,126 ----
  	 * 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
new file mode 100644
index f8aa99e..b63000a
*** a/src/include/foreign/foreign.h
--- b/src/include/foreign/foreign.h
***************
*** 14,19 ****
--- 14,21 ----
  #define FOREIGN_H
  
  #include "nodes/parsenodes.h"
+ #include "nodes/plannodes.h"
+ #include "nodes/relation.h"
  
  
  /* Helper for obtaining username for user mapping */
*************** extern List *GetForeignColumnOptions(Oid
*** 81,84 ****
--- 83,90 ----
  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
new file mode 100644
index d4911bd..18ab231
*** a/src/include/nodes/execnodes.h
--- b/src/include/nodes/execnodes.h
*************** typedef struct ProjectionInfo
*** 268,276 ****
   *						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
   *						(execMain.c stores the "ctid" attno here).
   * ----------------
   */
  typedef struct JunkFilter
--- 268,278 ----
   *						attribute numbers of the "original" tuple and the
   *						attribute numbers of the "clean" tuple.
   *	  resultSlot:		tuple slot used to hold cleaned tuple.
!  *	  jf_junkRowidNo:	not used by junkfilter code.  Can be used by caller
!  *						to remember the attno used 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
*************** typedef struct JunkFilter
*** 280,286 ****
  	TupleDesc	jf_cleanTupType;
  	AttrNumber *jf_cleanMap;
  	TupleTableSlot *jf_resultSlot;
! 	AttrNumber	jf_junkAttNo;
  } JunkFilter;
  
  /* ----------------
--- 282,289 ----
  	TupleDesc	jf_cleanTupType;
  	AttrNumber *jf_cleanMap;
  	TupleTableSlot *jf_resultSlot;
! 	AttrNumber	jf_junkRowidNo;
! 	AttrNumber	jf_junkRecordNo;
  } JunkFilter;
  
  /* ----------------
*************** typedef struct JunkFilter
*** 303,308 ****
--- 306,313 ----
   *		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
*************** typedef struct ResultRelInfo
*** 320,325 ****
--- 325,332 ----
  	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
new file mode 100644
index fb9a863..93481aa
*** a/src/include/nodes/plannodes.h
--- b/src/include/nodes/plannodes.h
*************** typedef struct ModifyTable
*** 175,180 ****
--- 175,181 ----
  	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;
  
  /* ----------------
*************** typedef struct ForeignScan
*** 478,483 ****
--- 479,485 ----
  	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
new file mode 100644
index de889fb..adfc93d
*** a/src/include/optimizer/pathnode.h
--- b/src/include/optimizer/pathnode.h
*************** extern Path *reparameterize_path(Planner
*** 133,139 ****
   */
  extern void setup_simple_rel_arrays(PlannerInfo *root);
  extern RelOptInfo *build_simple_rel(PlannerInfo *root, int relid,
! 				 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,
--- 133,139 ----
   */
  extern void setup_simple_rel_arrays(PlannerInfo *root);
  extern RelOptInfo *build_simple_rel(PlannerInfo *root, int relid,
! 			   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
new file mode 100644
index e0d04db..c5fce41
*** a/src/include/optimizer/plancat.h
--- b/src/include/optimizer/plancat.h
*************** extern PGDLLIMPORT get_relation_info_hoo
*** 26,32 ****
  
  
  extern void get_relation_info(PlannerInfo *root, Oid relationObjectId,
! 				  bool inhparent, RelOptInfo *rel);
  
  extern void estimate_rel_size(Relation rel, int32 *attr_widths,
  				  BlockNumber *pages, double *tuples, double *allvisfrac);
--- 26,32 ----
  
  
  extern void get_relation_info(PlannerInfo *root, Oid relationObjectId,
! 				  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
new file mode 100644
index 0fe696c..746a603
*** a/src/include/optimizer/planmain.h
--- b/src/include/optimizer/planmain.h
*************** extern SetOp *make_setop(SetOpCmd cmd, S
*** 79,85 ****
  		   long numGroups, double outputRows);
  extern Result *make_result(PlannerInfo *root, List *tlist,
  			Node *resconstantqual, Plan *subplan);
! extern ModifyTable *make_modifytable(CmdType operation, bool canSetTag,
  				 List *resultRelations, List *subplans, List *returningLists,
  				 List *rowMarks, int epqParam);
  extern bool is_projection_capable_plan(Plan *plan);
--- 79,86 ----
  		   long numGroups, double outputRows);
  extern Result *make_result(PlannerInfo *root, List *tlist,
  			Node *resconstantqual, Plan *subplan);
! 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);
*************** extern bool is_projection_capable_plan(P
*** 90,96 ****
  extern int	from_collapse_limit;
  extern int	join_collapse_limit;
  
! extern void add_base_rels_to_query(PlannerInfo *root, 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);
--- 91,98 ----
  extern int	from_collapse_limit;
  extern int	join_collapse_limit;
  
! 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);