pgsql-v9.3-writable-fdw-poc.v5.patch
application/octet-stream
Filename: pgsql-v9.3-writable-fdw-poc.v5.patch
Type: application/octet-stream
Part: 0
Message:
Re: [v9.3] writable foreign tables
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
Series: patch v9
| File | + | − |
|---|---|---|
| contrib/postgres_fdw/connection.c | 56 | 14 |
| contrib/postgres_fdw/connection.h | 6 | 2 |
| contrib/postgres_fdw/deparse.c | 124 | 2 |
| contrib/postgres_fdw/expected/postgres_fdw.out | 1153 | 0 |
| contrib/postgres_fdw/postgres_fdw.c | 392 | 13 |
| contrib/postgres_fdw/postgres_fdw.h | 5 | 1 |
| contrib/postgres_fdw/sql/postgres_fdw.sql | 10 | 0 |
| doc/src/sgml/fdwhandler.sgml | 151 | 1 |
| src/backend/executor/execMain.c | 30 | 4 |
| src/backend/executor/nodeForeignscan.c | 135 | 1 |
| src/backend/executor/nodeModifyTable.c | 126 | 24 |
| src/backend/foreign/foreign.c | 34 | 0 |
| src/backend/nodes/copyfuncs.c | 2 | 0 |
| src/backend/nodes/outfuncs.c | 2 | 0 |
| src/backend/optimizer/plan/createplan.c | 47 | 1 |
| src/backend/optimizer/plan/initsplan.c | 5 | 5 |
| src/backend/optimizer/plan/planmain.c | 1 | 1 |
| src/backend/optimizer/plan/planner.c | 5 | 3 |
| src/backend/optimizer/prep/prepunion.c | 2 | 1 |
| src/backend/optimizer/util/plancat.c | 26 | 1 |
| src/backend/optimizer/util/relnode.c | 4 | 3 |
| src/backend/rewrite/rewriteHandler.c | 45 | 10 |
| src/include/foreign/fdwapi.h | 31 | 0 |
| src/include/foreign/foreign.h | 3 | 0 |
| src/include/nodes/execnodes.h | 10 | 3 |
| src/include/nodes/plannodes.h | 2 | 0 |
| src/include/optimizer/pathnode.h | 1 | 1 |
| src/include/optimizer/plancat.h | 1 | 1 |
| src/include/optimizer/planmain.h | 4 | 2 |
contrib/postgres_fdw/connection.c | 70 +-
contrib/postgres_fdw/connection.h | 8 +-
contrib/postgres_fdw/deparse.c | 126 ++-
contrib/postgres_fdw/expected/postgres_fdw.out | 1153 ++++++++++++++++++++++++
contrib/postgres_fdw/postgres_fdw.c | 405 ++++++++-
contrib/postgres_fdw/postgres_fdw.h | 6 +-
contrib/postgres_fdw/sql/postgres_fdw.sql | 10 +
doc/src/sgml/fdwhandler.sgml | 152 +++-
src/backend/executor/execMain.c | 34 +-
src/backend/executor/nodeForeignscan.c | 136 ++-
src/backend/executor/nodeModifyTable.c | 150 ++-
src/backend/foreign/foreign.c | 34 +
src/backend/nodes/copyfuncs.c | 2 +
src/backend/nodes/outfuncs.c | 2 +
src/backend/optimizer/plan/createplan.c | 48 +-
src/backend/optimizer/plan/initsplan.c | 10 +-
src/backend/optimizer/plan/planmain.c | 2 +-
src/backend/optimizer/plan/planner.c | 8 +-
src/backend/optimizer/prep/prepunion.c | 3 +-
src/backend/optimizer/util/plancat.c | 27 +-
src/backend/optimizer/util/relnode.c | 7 +-
src/backend/rewrite/rewriteHandler.c | 55 +-
src/include/foreign/fdwapi.h | 31 +
src/include/foreign/foreign.h | 3 +
src/include/nodes/execnodes.h | 13 +-
src/include/nodes/plannodes.h | 2 +
src/include/optimizer/pathnode.h | 2 +-
src/include/optimizer/plancat.h | 2 +-
src/include/optimizer/planmain.h | 6 +-
29 files changed, 2413 insertions(+), 94 deletions(-)
diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index eab8b87..9ca7fbf 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -43,7 +43,7 @@ typedef struct ConnCacheEntry
Oid serverid; /* oid of foreign server */
Oid userid; /* oid of local user */
- bool use_tx; /* true when using remote transaction */
+ int conntx; /* one of PGSQL_FDW_CONNTX_* */
int refs; /* reference counter */
PGconn *conn; /* foreign server connection */
} ConnCacheEntry;
@@ -65,6 +65,8 @@ cleanup_connection(ResourceReleasePhase phase,
static PGconn *connect_pg_server(ForeignServer *server, UserMapping *user);
static void begin_remote_tx(PGconn *conn);
static void abort_remote_tx(PGconn *conn);
+static void commit_remote_tx(PGconn *conn);
+static void deallocate_remote_prepare(PGconn *conn);
/*
* Get a PGconn which can be used to execute foreign query on the remote
@@ -80,7 +82,7 @@ static void abort_remote_tx(PGconn *conn);
* FDW object to invalidate already established connections.
*/
PGconn *
-GetConnection(ForeignServer *server, UserMapping *user, bool use_tx)
+GetConnection(ForeignServer *server, UserMapping *user, int conntx)
{
bool found;
ConnCacheEntry *entry;
@@ -126,7 +128,7 @@ GetConnection(ForeignServer *server, UserMapping *user, bool use_tx)
entry = hash_search(ConnectionHash, &key, HASH_ENTER, &found);
if (!found)
{
- entry->use_tx = false;
+ entry->conntx = PGSQL_FDW_CONNTX_NONE;
entry->refs = 0;
entry->conn = NULL;
}
@@ -162,7 +164,7 @@ GetConnection(ForeignServer *server, UserMapping *user, bool use_tx)
{
/* Clear connection cache entry on error case. */
PQfinish(entry->conn);
- entry->use_tx = false;
+ entry->conntx = PGSQL_FDW_CONNTX_NONE;
entry->refs = 0;
entry->conn = NULL;
PG_RE_THROW();
@@ -182,10 +184,11 @@ GetConnection(ForeignServer *server, UserMapping *user, bool use_tx)
* are in. We need to remember whether this connection uses remote
* transaction to abort it when this connection is released completely.
*/
- if (use_tx && !entry->use_tx)
+ if (conntx > entry->conntx)
{
- begin_remote_tx(entry->conn);
- entry->use_tx = use_tx;
+ if (entry->conntx == PGSQL_FDW_CONNTX_NONE)
+ begin_remote_tx(entry->conn);
+ entry->conntx = conntx;
}
return entry->conn;
@@ -355,12 +358,45 @@ abort_remote_tx(PGconn *conn)
PQclear(res);
}
+static void
+commit_remote_tx(PGconn *conn)
+{
+ PGresult *res;
+
+ elog(DEBUG3, "committing remote transaction");
+
+ res = PQexec(conn, "COMMIT TRANSACTION");
+ if (PQresultStatus(res) != PGRES_COMMAND_OK)
+ {
+ PQclear(res);
+ elog(ERROR, "could not commit transaction: %s", PQerrorMessage(conn));
+ }
+ PQclear(res);
+}
+
+static void
+deallocate_remote_prepare(PGconn *conn)
+{
+ PGresult *res;
+
+ elog(DEBUG3, "deallocating remote prepares");
+
+ res = PQexec(conn, "DEALLOCATE PREPARE ALL");
+ if (PQresultStatus(res) != PGRES_COMMAND_OK)
+ {
+ PQclear(res);
+ elog(ERROR, "could not deallocate prepared statement: %s",
+ PQerrorMessage(conn));
+ }
+ PQclear(res);
+}
+
/*
* Mark the connection as "unused", and close it if the caller was the last
* user of the connection.
*/
void
-ReleaseConnection(PGconn *conn)
+ReleaseConnection(PGconn *conn, bool is_abort)
{
HASH_SEQ_STATUS scan;
ConnCacheEntry *entry;
@@ -412,7 +448,7 @@ ReleaseConnection(PGconn *conn)
PQtransactionStatus(conn) == PQTRANS_INERROR ? "INERROR" :
"UNKNOWN");
PQfinish(conn);
- entry->use_tx = false;
+ entry->conntx = PGSQL_FDW_CONNTX_NONE;
entry->refs = 0;
entry->conn = NULL;
return;
@@ -430,10 +466,16 @@ ReleaseConnection(PGconn *conn)
* If this connection uses remote transaction and there is no user other
* than the caller, abort the remote transaction and forget about it.
*/
- if (entry->use_tx && entry->refs == 0)
+ if (entry->conntx > PGSQL_FDW_CONNTX_NONE && entry->refs == 0)
{
- abort_remote_tx(conn);
- entry->use_tx = false;
+ if (entry->conntx > PGSQL_FDW_CONNTX_READ_ONLY)
+ deallocate_remote_prepare(conn);
+ if (is_abort || entry->conntx == PGSQL_FDW_CONNTX_READ_ONLY)
+ abort_remote_tx(conn);
+ else
+ commit_remote_tx(conn);
+
+ entry->conntx = PGSQL_FDW_CONNTX_NONE;
}
}
@@ -485,7 +527,7 @@ cleanup_connection(ResourceReleasePhase phase,
elog(DEBUG3, "discard postgres_fdw connection %p due to resowner cleanup",
entry->conn);
PQfinish(entry->conn);
- entry->use_tx = false;
+ entry->conntx = PGSQL_FDW_CONNTX_NONE;
entry->refs = 0;
entry->conn = NULL;
}
@@ -597,7 +639,7 @@ postgres_fdw_disconnect(PG_FUNCTION_ARGS)
/* Discard cached connection, and clear reference counter. */
PQfinish(entry->conn);
- entry->use_tx = false;
+ entry->conntx = PGSQL_FDW_CONNTX_NONE;
entry->refs = 0;
entry->conn = NULL;
diff --git a/contrib/postgres_fdw/connection.h b/contrib/postgres_fdw/connection.h
index 4c9d850..f97cc8a 100644
--- a/contrib/postgres_fdw/connection.h
+++ b/contrib/postgres_fdw/connection.h
@@ -16,10 +16,14 @@
#include "foreign/foreign.h"
#include "libpq-fe.h"
+#define PGSQL_FDW_CONNTX_NONE 0
+#define PGSQL_FDW_CONNTX_READ_ONLY 1
+#define PGSQL_FDW_CONNTX_READ_WRITE 2
+
/*
* Connection management
*/
-PGconn *GetConnection(ForeignServer *server, UserMapping *user, bool use_tx);
-void ReleaseConnection(PGconn *conn);
+PGconn *GetConnection(ForeignServer *server, UserMapping *user, int conntx);
+void ReleaseConnection(PGconn *conn, bool is_abort);
#endif /* CONNECTION_H */
diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index 69e6a3e..9e09429 100644
--- a/contrib/postgres_fdw/deparse.c
+++ b/contrib/postgres_fdw/deparse.c
@@ -12,6 +12,7 @@
*/
#include "postgres.h"
+#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/transam.h"
#include "catalog/pg_class.h"
@@ -86,9 +87,11 @@ void
deparseSimpleSql(StringInfo buf,
PlannerInfo *root,
RelOptInfo *baserel,
- List *local_conds)
+ List *local_conds,
+ AttrNumber anum_rowid)
{
RangeTblEntry *rte;
+ Relation rel;
ListCell *lc;
StringInfoData foreign_relname;
bool first;
@@ -125,6 +128,24 @@ deparseSimpleSql(StringInfo buf,
}
/*
+ * XXX - When this foreign table is target relation and RETURNING
+ * clause reference some column, we have to mark these columns as
+ * in-use. It is needed to support DELETE command, because INSERT
+ * and UPDATE implicitly add references to all the regular columns
+ * on baserel->reltargetlist.
+ */
+ if (root->parse->resultRelation == baserel->relid &&
+ root->parse->returningList)
+ {
+ List *attrs;
+
+ attrs = pull_var_clause((Node *) root->parse->returningList,
+ PVC_RECURSE_AGGREGATES,
+ PVC_RECURSE_PLACEHOLDERS);
+ attr_used = list_union(attr_used, attrs);
+ }
+
+ /*
* deparse SELECT clause
*
* List attributes which are in either target list or local restriction.
@@ -136,9 +157,10 @@ deparseSimpleSql(StringInfo buf,
*/
appendStringInfo(buf, "SELECT ");
rte = root->simple_rte_array[baserel->relid];
+ rel = heap_open(rte->relid, NoLock);
attr_used = list_union(attr_used, baserel->reltargetlist);
first = true;
- for (attr = 1; attr <= baserel->max_attr; attr++)
+ for (attr = 1; attr <= RelationGetNumberOfAttributes(rel); attr++)
{
Var *var = NULL;
ListCell *lc;
@@ -167,6 +189,10 @@ deparseSimpleSql(StringInfo buf,
else
appendStringInfo(buf, "NULL");
}
+ if (anum_rowid != InvalidAttrNumber)
+ appendStringInfo(buf, "%sctid", (first ? "" : ","));
+
+ heap_close(rel, NoLock);
appendStringInfoChar(buf, ' ');
/*
@@ -283,6 +309,102 @@ deparseAnalyzeSql(StringInfo buf, Relation rel)
}
/*
+ * deparse remote INSERT statement
+ */
+void
+deparseInsertSql(StringInfo buf, PlannerInfo *root, Index rtindex)
+{
+ RangeTblEntry *rte = root->simple_rte_array[rtindex];
+ Relation frel = heap_open(rte->relid, NoLock);
+ int i, j, nattrs = RelationGetNumberOfAttributes(frel);
+ bool is_first = true;
+
+ appendStringInfo(buf, "INSERT INTO ");
+ deparseRelation(buf, rte);
+ appendStringInfo(buf, "(");
+
+ for (i=0; i < nattrs; i++)
+ {
+ Form_pg_attribute attr = RelationGetDescr(frel)->attrs[i];
+ Var var;
+
+ if (attr->attisdropped)
+ continue;
+
+ if (!is_first)
+ appendStringInfo(buf, ",");
+
+ var.varno = rtindex;
+ var.varattno = attr->attnum;
+ deparseVar(buf, &var, root);
+ is_first = false;
+ }
+ appendStringInfo(buf, ") VALUES (");
+
+ for (i=0, j=1; i < nattrs; i++)
+ {
+ Form_pg_attribute attr = RelationGetDescr(frel)->attrs[i];
+
+ if (attr->attisdropped)
+ continue;
+
+ appendStringInfo(buf, "%s$%d", (j == 1 ? "" : ","), j);
+ j++;
+ }
+ appendStringInfo(buf, ")");
+ heap_close(frel, NoLock);
+}
+
+/*
+ * deparse remote UPDATE statement
+ */
+void
+deparseUpdateSql(StringInfo buf, PlannerInfo *root, Index rtindex)
+{
+ RangeTblEntry *rte = root->simple_rte_array[rtindex];
+ Relation frel = heap_open(rte->relid, NoLock);
+ int i, j, nattrs = RelationGetNumberOfAttributes(frel);
+ bool is_first = true;
+
+ appendStringInfo(buf, "UPDATE ");
+ deparseRelation(buf, rte);
+ appendStringInfo(buf, " SET ");
+
+ for (i=0, j=2; i < nattrs; i++)
+ {
+ Form_pg_attribute attr = RelationGetDescr(frel)->attrs[i];
+ Var var;
+
+ if (attr->attisdropped)
+ continue;
+
+ if (!is_first)
+ appendStringInfo(buf, ",");
+
+ var.varno = rtindex;
+ var.varattno = attr->attnum;
+ deparseVar(buf, &var, root);
+ appendStringInfo(buf, "=$%d", j++);
+ is_first = false;
+ }
+ appendStringInfo(buf, " WHERE ctid=$1");
+ heap_close(frel, NoLock);
+}
+
+/*
+ * deparse remote DELETE statement
+ */
+void
+deparseDeleteSql(StringInfo buf, PlannerInfo *root, Index rtindex)
+{
+ RangeTblEntry *rte = root->simple_rte_array[rtindex];
+
+ appendStringInfo(buf, "DELETE FROM ");
+ deparseRelation(buf, rte);
+ appendStringInfo(buf, " WHERE ctid = $1");
+}
+
+/*
* Deparse given expression into buf. Actual string operation is delegated to
* node-type-specific functions.
*
diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index f81c727..5ad00ea 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -736,6 +736,1159 @@ SELECT srvname FROM postgres_fdw_connections;
(0 rows)
-- ===================================================================
+-- test for writable foreign table stuff (PoC stage now)
+-- ===================================================================
+INSERT INTO ft2 (c1,c2,c3) (SELECT c1+1000,c2+100, c3 || c3 FROM ft2 LIMIT 20);
+INSERT INTO ft2 (c1,c2,c3) VALUES (1101,201,'aaa'), (1102,202,'bbb'),(1103,203,'ccc') RETURNING *;
+ c1 | c2 | c3 | c4 | c5 | c6 | c7 | c8
+------+-----+-----+----+----+----+----+----
+ 1101 | 201 | aaa | | | | |
+ 1102 | 202 | bbb | | | | |
+ 1103 | 203 | ccc | | | | |
+(3 rows)
+
+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)
+
+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 | | | | |
+(102 rows)
+
+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
+ 2 | 2 | 00002 | Sat Jan 03 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 | 9 | 00009 | 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
+ 12 | 2 | 00012 | Tue Jan 13 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 | 9 | 00019 | 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
+ 22 | 2 | 00022 | Fri Jan 23 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 | 9 | 00029 | 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
+ 32 | 2 | 00032 | Mon Feb 02 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 | 9 | 00039 | 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
+ 42 | 2 | 00042 | Thu Feb 12 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 | 9 | 00049 | 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
+ 52 | 2 | 00052 | Sun Feb 22 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 | 9 | 00059 | 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
+ 62 | 2 | 00062 | Wed Mar 04 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 | 9 | 00069 | 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
+ 72 | 2 | 00072 | Sat Mar 14 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 | 9 | 00079 | 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
+ 82 | 2 | 00082 | Tue Mar 24 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 | 9 | 00089 | 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
+ 92 | 2 | 00092 | Fri Apr 03 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 | 9 | 00099 | 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
+ 102 | 2 | 00102 | Sat Jan 03 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 | 9 | 00109 | 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
+ 112 | 2 | 00112 | Tue Jan 13 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 | 9 | 00119 | 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
+ 122 | 2 | 00122 | Fri Jan 23 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 | 9 | 00129 | 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
+ 132 | 2 | 00132 | Mon Feb 02 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 | 9 | 00139 | 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
+ 142 | 2 | 00142 | Thu Feb 12 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 | 9 | 00149 | 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
+ 152 | 2 | 00152 | Sun Feb 22 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 | 9 | 00159 | 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
+ 162 | 2 | 00162 | Wed Mar 04 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 | 9 | 00169 | 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
+ 172 | 2 | 00172 | Sat Mar 14 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 | 9 | 00179 | 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
+ 182 | 2 | 00182 | Tue Mar 24 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 | 9 | 00189 | 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
+ 192 | 2 | 00192 | Fri Apr 03 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 | 9 | 00199 | 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
+ 202 | 2 | 00202 | Sat Jan 03 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 | 9 | 00209 | 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
+ 212 | 2 | 00212 | Tue Jan 13 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 | 9 | 00219 | 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
+ 222 | 2 | 00222 | Fri Jan 23 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 | 9 | 00229 | 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
+ 232 | 2 | 00232 | Mon Feb 02 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 | 9 | 00239 | 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
+ 242 | 2 | 00242 | Thu Feb 12 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 | 9 | 00249 | 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
+ 252 | 2 | 00252 | Sun Feb 22 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 | 9 | 00259 | 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
+ 262 | 2 | 00262 | Wed Mar 04 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 | 9 | 00269 | 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
+ 272 | 2 | 00272 | Sat Mar 14 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 | 9 | 00279 | 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
+ 282 | 2 | 00282 | Tue Mar 24 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 | 9 | 00289 | 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
+ 292 | 2 | 00292 | Fri Apr 03 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 | 9 | 00299 | 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
+ 302 | 2 | 00302 | Sat Jan 03 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 | 9 | 00309 | 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
+ 312 | 2 | 00312 | Tue Jan 13 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 | 9 | 00319 | 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
+ 322 | 2 | 00322 | Fri Jan 23 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 | 9 | 00329 | 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
+ 332 | 2 | 00332 | Mon Feb 02 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 | 9 | 00339 | 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
+ 342 | 2 | 00342 | Thu Feb 12 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 | 9 | 00349 | 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
+ 352 | 2 | 00352 | Sun Feb 22 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 | 9 | 00359 | 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
+ 362 | 2 | 00362 | Wed Mar 04 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 | 9 | 00369 | 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
+ 372 | 2 | 00372 | Sat Mar 14 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 | 9 | 00379 | 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
+ 382 | 2 | 00382 | Tue Mar 24 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 | 9 | 00389 | 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
+ 392 | 2 | 00392 | Fri Apr 03 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 | 9 | 00399 | 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
+ 402 | 2 | 00402 | Sat Jan 03 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 | 9 | 00409 | 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
+ 412 | 2 | 00412 | Tue Jan 13 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 | 9 | 00419 | 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
+ 422 | 2 | 00422 | Fri Jan 23 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 | 9 | 00429 | 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
+ 432 | 2 | 00432 | Mon Feb 02 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 | 9 | 00439 | 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
+ 442 | 2 | 00442 | Thu Feb 12 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 | 9 | 00449 | 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
+ 452 | 2 | 00452 | Sun Feb 22 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 | 9 | 00459 | 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
+ 462 | 2 | 00462 | Wed Mar 04 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 | 9 | 00469 | 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
+ 472 | 2 | 00472 | Sat Mar 14 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 | 9 | 00479 | 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
+ 482 | 2 | 00482 | Tue Mar 24 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 | 9 | 00489 | 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
+ 492 | 2 | 00492 | Fri Apr 03 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 | 9 | 00499 | 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
+ 502 | 2 | 00502 | Sat Jan 03 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 | 9 | 00509 | 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
+ 512 | 2 | 00512 | Tue Jan 13 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 | 9 | 00519 | 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
+ 522 | 2 | 00522 | Fri Jan 23 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 | 9 | 00529 | 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
+ 532 | 2 | 00532 | Mon Feb 02 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 | 9 | 00539 | 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
+ 542 | 2 | 00542 | Thu Feb 12 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 | 9 | 00549 | 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
+ 552 | 2 | 00552 | Sun Feb 22 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 | 9 | 00559 | 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
+ 562 | 2 | 00562 | Wed Mar 04 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 | 9 | 00569 | 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
+ 572 | 2 | 00572 | Sat Mar 14 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 | 9 | 00579 | 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
+ 582 | 2 | 00582 | Tue Mar 24 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 | 9 | 00589 | 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
+ 592 | 2 | 00592 | Fri Apr 03 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 | 9 | 00599 | 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
+ 602 | 2 | 00602 | Sat Jan 03 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 | 9 | 00609 | 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
+ 612 | 2 | 00612 | Tue Jan 13 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 | 9 | 00619 | 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
+ 622 | 2 | 00622 | Fri Jan 23 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 | 9 | 00629 | 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
+ 632 | 2 | 00632 | Mon Feb 02 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 | 9 | 00639 | 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
+ 642 | 2 | 00642 | Thu Feb 12 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 | 9 | 00649 | 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
+ 652 | 2 | 00652 | Sun Feb 22 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 | 9 | 00659 | 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
+ 662 | 2 | 00662 | Wed Mar 04 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 | 9 | 00669 | 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
+ 672 | 2 | 00672 | Sat Mar 14 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 | 9 | 00679 | 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
+ 682 | 2 | 00682 | Tue Mar 24 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 | 9 | 00689 | 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
+ 692 | 2 | 00692 | Fri Apr 03 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 | 9 | 00699 | 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
+ 702 | 2 | 00702 | Sat Jan 03 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 | 9 | 00709 | 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
+ 712 | 2 | 00712 | Tue Jan 13 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 | 9 | 00719 | 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
+ 722 | 2 | 00722 | Fri Jan 23 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 | 9 | 00729 | 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
+ 732 | 2 | 00732 | Mon Feb 02 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 | 9 | 00739 | 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
+ 742 | 2 | 00742 | Thu Feb 12 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 | 9 | 00749 | 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
+ 752 | 2 | 00752 | Sun Feb 22 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 | 9 | 00759 | 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
+ 762 | 2 | 00762 | Wed Mar 04 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 | 9 | 00769 | 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
+ 772 | 2 | 00772 | Sat Mar 14 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 | 9 | 00779 | 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
+ 782 | 2 | 00782 | Tue Mar 24 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 | 9 | 00789 | 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
+ 792 | 2 | 00792 | Fri Apr 03 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 | 9 | 00799 | 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
+ 802 | 2 | 00802 | Sat Jan 03 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 | 9 | 00809 | 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
+ 812 | 2 | 00812 | Tue Jan 13 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 | 9 | 00819 | 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
+ 822 | 2 | 00822 | Fri Jan 23 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 | 9 | 00829 | 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
+ 832 | 2 | 00832 | Mon Feb 02 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 | 9 | 00839 | 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
+ 842 | 2 | 00842 | Thu Feb 12 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 | 9 | 00849 | 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
+ 852 | 2 | 00852 | Sun Feb 22 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 | 9 | 00859 | 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
+ 862 | 2 | 00862 | Wed Mar 04 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 | 9 | 00869 | 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
+ 872 | 2 | 00872 | Sat Mar 14 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 | 9 | 00879 | 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
+ 882 | 2 | 00882 | Tue Mar 24 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 | 9 | 00889 | 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
+ 892 | 2 | 00892 | Fri Apr 03 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 | 9 | 00899 | 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
+ 902 | 2 | 00902 | Sat Jan 03 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 | 9 | 00909 | 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
+ 912 | 2 | 00912 | Tue Jan 13 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 | 9 | 00919 | 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
+ 922 | 2 | 00922 | Fri Jan 23 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 | 9 | 00929 | 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
+ 932 | 2 | 00932 | Mon Feb 02 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 | 9 | 00939 | 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
+ 942 | 2 | 00942 | Thu Feb 12 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 | 9 | 00949 | 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
+ 952 | 2 | 00952 | Sun Feb 22 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 | 9 | 00959 | 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
+ 962 | 2 | 00962 | Wed Mar 04 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 | 9 | 00969 | 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
+ 972 | 2 | 00972 | Sat Mar 14 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 | 9 | 00979 | 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
+ 982 | 2 | 00982 | Tue Mar 24 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 | 9 | 00989 | 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
+ 992 | 2 | 00992 | Fri Apr 03 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 | 9 | 00999 | Fri Apr 10 00:00:00 1970 PST
+ 1000 | 0 | 01000 | Thu Jan 01 00:00:00 1970 PST
+ 1001 | 101 | 0000100001 |
+ 1002 | 102 | 0000200002 |
+ 1003 | 403 | 0000300003_update3 |
+ 1004 | 104 | 0000400004 |
+ 1006 | 106 | 0000600006 |
+ 1007 | 507 | 0000700007_update7 |
+ 1008 | 108 | 0000800008 |
+ 1009 | 109 | 0000900009 |
+ 1010 | 100 | 0001000010 |
+ 1011 | 101 | 0001100011 |
+ 1012 | 102 | 0001200012 |
+ 1013 | 403 | 0001300013_update3 |
+ 1014 | 104 | 0001400014 |
+ 1016 | 106 | 0001600016 |
+ 1017 | 507 | 0001700017_update7 |
+ 1018 | 108 | 0001800018 |
+ 1019 | 109 | 0001900019 |
+ 1020 | 100 | 0002000020 |
+ 1101 | 201 | aaa |
+ 1102 | 202 | bbb |
+ 1103 | 503 | ccc_update3 |
+(921 rows)
+
+-- ===================================================================
-- cleanup
-- ===================================================================
DROP OPERATOR === (int, int) CASCADE;
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 6b870ab..bdb2da8 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -59,6 +59,7 @@ typedef struct PostgresFdwPlanState {
List *param_conds;
List *local_conds;
int width; /* obtained by remote EXPLAIN */
+ AttrNumber anum_rowid;
/* Cached catalog information. */
ForeignTable *table;
@@ -150,6 +151,20 @@ typedef struct PostgresAnalyzeState
} PostgresAnalyzeState;
/*
+ * Describes a state of modify request for a foreign table
+ */
+typedef struct PostgresFdwModifyState
+{
+ PGconn *conn;
+ char *query;
+ char *p_name;
+ int p_nums;
+ Oid *p_types;
+ FmgrInfo *p_flinfo;
+ MemoryContext es_query_cxt;
+} PostgresFdwModifyState;
+
+/*
* SQL functions
*/
extern Datum postgres_fdw_handler(PG_FUNCTION_ARGS);
@@ -158,6 +173,11 @@ PG_FUNCTION_INFO_V1(postgres_fdw_handler);
/*
* FDW callback routines
*/
+static AttrNumber postgresGetForeignRelWidth(PlannerInfo *root,
+ RelOptInfo *baserel,
+ Relation foreignrel,
+ bool inhparent,
+ List *targetList);
static void postgresGetForeignRelSize(PlannerInfo *root,
RelOptInfo *baserel,
Oid foreigntableid);
@@ -179,6 +199,23 @@ static void postgresEndForeignScan(ForeignScanState *node);
static bool postgresAnalyzeForeignTable(Relation relation,
AcquireSampleRowsFunc *func,
BlockNumber *totalpages);
+static List *postgresPlanForeignModify(PlannerInfo *root,
+ ModifyTable *plan,
+ Index resultRelation,
+ Plan *subplan);
+static void postgresBeginForeignModify(ModifyTableState *mtstate,
+ ResultRelInfo *resultRelInfo,
+ List *fdw_private,
+ Plan *subplan,
+ int eflags);
+static int postgresExecForeignInsert(ResultRelInfo *resultRelInfo,
+ HeapTuple tuple);
+static int postgresExecForeignDelete(ResultRelInfo *resultRelInfo,
+ Datum rowid);
+static int postgresExecForeignUpdate(ResultRelInfo *resultRelInfo,
+ Datum rowid,
+ HeapTuple tuple);
+static void postgresEndForeignModify(ResultRelInfo *resultRelInfo);
/*
* Helper functions
@@ -231,6 +268,7 @@ postgres_fdw_handler(PG_FUNCTION_ARGS)
FdwRoutine *routine = makeNode(FdwRoutine);
/* Required handler functions. */
+ routine->GetForeignRelWidth = postgresGetForeignRelWidth;
routine->GetForeignRelSize = postgresGetForeignRelSize;
routine->GetForeignPaths = postgresGetForeignPaths;
routine->GetForeignPlan = postgresGetForeignPlan;
@@ -239,6 +277,12 @@ postgres_fdw_handler(PG_FUNCTION_ARGS)
routine->IterateForeignScan = postgresIterateForeignScan;
routine->ReScanForeignScan = postgresReScanForeignScan;
routine->EndForeignScan = postgresEndForeignScan;
+ routine->PlanForeignModify = postgresPlanForeignModify;
+ routine->BeginForeignModify = postgresBeginForeignModify;
+ routine->ExecForeignInsert = postgresExecForeignInsert;
+ routine->ExecForeignDelete = postgresExecForeignDelete;
+ routine->ExecForeignUpdate = postgresExecForeignUpdate;
+ routine->EndForeignModify = postgresEndForeignModify;
/* Optional handler functions. */
routine->AnalyzeForeignTable = postgresAnalyzeForeignTable;
@@ -247,6 +291,29 @@ postgres_fdw_handler(PG_FUNCTION_ARGS)
}
/*
+ * postgresGetForeignRelWidth
+ * Informs how many columns (including pseudo ones) are needed.
+ */
+static AttrNumber
+postgresGetForeignRelWidth(PlannerInfo *root,
+ RelOptInfo *baserel,
+ Relation foreignrel,
+ bool inhparent,
+ List *targetList)
+{
+ PostgresFdwPlanState *fpstate = palloc0(sizeof(PostgresFdwPlanState));
+
+ baserel->fdw_private = fpstate;
+
+ /* does rowid pseudo-column is required? */
+ fpstate->anum_rowid = get_pseudo_rowid_column(baserel, targetList);
+ if (fpstate->anum_rowid != InvalidAttrNumber)
+ return fpstate->anum_rowid;
+
+ return RelationGetNumberOfAttributes(foreignrel);
+}
+
+/*
* postgresGetForeignRelSize
* Estimate # of rows and width of the result of the scan
*
@@ -283,7 +350,7 @@ postgresGetForeignRelSize(PlannerInfo *root,
* We use PostgresFdwPlanState to pass various information to subsequent
* functions.
*/
- fpstate = palloc0(sizeof(PostgresFdwPlanState));
+ fpstate = baserel->fdw_private;
initStringInfo(&fpstate->sql);
sql = &fpstate->sql;
@@ -320,10 +387,9 @@ postgresGetForeignRelSize(PlannerInfo *root,
*/
classifyConditions(root, baserel, &remote_conds, ¶m_conds,
&local_conds);
- deparseSimpleSql(sql, root, baserel, local_conds);
+ deparseSimpleSql(sql, root, baserel, local_conds, fpstate->anum_rowid);
if (list_length(remote_conds) > 0)
appendWhereClause(sql, true, remote_conds, root);
- elog(DEBUG3, "Query SQL: %s", sql->data);
/*
* If the table or the server is configured to use remote EXPLAIN, connect
@@ -337,10 +403,10 @@ postgresGetForeignRelSize(PlannerInfo *root,
PGconn *conn;
user = GetUserMapping(GetOuterUserId(), server->serverid);
- conn = GetConnection(server, user, false);
+ conn = GetConnection(server, user, PGSQL_FDW_CONNTX_NONE);
get_remote_estimate(sql->data, conn, &rows, &width,
&startup_cost, &total_cost);
- ReleaseConnection(conn);
+ ReleaseConnection(conn, false);
/*
* Estimate selectivity of conditions which are not used in remote
@@ -391,7 +457,6 @@ postgresGetForeignRelSize(PlannerInfo *root,
fpstate->width = width;
fpstate->table = table;
fpstate->server = server;
- baserel->fdw_private = (void *) fpstate;
}
/*
@@ -592,7 +657,7 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags)
table = GetForeignTable(relid);
server = GetForeignServer(table->serverid);
user = GetUserMapping(GetOuterUserId(), server->serverid);
- conn = GetConnection(server, user, true);
+ conn = GetConnection(server, user, PGSQL_FDW_CONNTX_READ_ONLY);
festate->conn = conn;
/* Result will be filled in first Iterate call. */
@@ -724,7 +789,7 @@ postgresEndForeignScan(ForeignScanState *node)
* end of the scan to make the lifespan of remote transaction same as the
* local query.
*/
- ReleaseConnection(festate->conn);
+ ReleaseConnection(festate->conn, false);
festate->conn = NULL;
/* Discard fetch results */
@@ -790,7 +855,7 @@ get_remote_estimate(const char *sql, PGconn *conn,
PQclear(res);
/* Release connection and let connection manager cleanup. */
- ReleaseConnection(conn);
+ ReleaseConnection(conn, true);
PG_RE_THROW();
}
@@ -947,7 +1012,7 @@ execute_query(ForeignScanState *node)
PQclear(res);
/* Release connection and let connection manager cleanup. */
- ReleaseConnection(conn);
+ ReleaseConnection(conn, true);
/* propagate error */
PG_RE_THROW();
@@ -1105,6 +1170,8 @@ postgres_fdw_error_callback(void *arg)
relname = get_rel_name(errpos->relid);
colname = get_attname(errpos->relid, errpos->cur_attno);
+ if (!colname)
+ colname = "pseudo-column";
errcontext("column %s of foreign table %s",
quote_identifier(colname), quote_identifier(relname));
}
@@ -1172,7 +1239,7 @@ postgresAcquireSampleRowsFunc(Relation relation, int elevel,
table = GetForeignTable(relation->rd_id);
server = GetForeignServer(table->serverid);
user = GetUserMapping(GetOuterUserId(), server->serverid);
- conn = GetConnection(server, user, true);
+ conn = GetConnection(server, user, PGSQL_FDW_CONNTX_READ_ONLY);
/*
* Acquire sample rows from the result set.
@@ -1239,13 +1306,13 @@ postgresAcquireSampleRowsFunc(Relation relation, int elevel,
PQclear(res);
/* Release connection and let connection manager cleanup. */
- ReleaseConnection(conn);
+ ReleaseConnection(conn, true);
PG_RE_THROW();
}
PG_END_TRY();
- ReleaseConnection(conn);
+ ReleaseConnection(conn, false);
/* We assume that we have no dead tuple. */
*totaldeadrows = 0.0;
@@ -1429,3 +1496,315 @@ analyze_row_processor(PGresult *res, PostgresAnalyzeState *astate, bool first)
return;
}
+
+static List *
+postgresPlanForeignModify(PlannerInfo *root,
+ ModifyTable *plan,
+ Index resultRelaion,
+ Plan *subplan)
+{
+ CmdType operation = plan->operation;
+ StringInfoData sql;
+
+ initStringInfo(&sql);
+
+ /*
+ * XXX - In case of UPDATE or DELETE commands are quite "simple",
+ * we will be able to execute raw UPDATE or DELETE statement at
+ * the stage of scan, instead of combination SELECT ... FOR UPDATE
+ * and either of UPDATE or DELETE commands.
+ * It should be an idea of optimization in the future version.
+ *
+ * XXX - FOR UPDATE should be appended on the remote query of scan
+ * stage to avoid unexpected concurrent update on the target rows.
+ */
+ if (operation == CMD_UPDATE || operation == CMD_DELETE)
+ {
+ ForeignScan *fscan = (ForeignScan *) subplan;
+ Value *select_sql;
+
+ Assert(IsA(fscan, ForeignScan));
+ select_sql = list_nth(fscan->fdw_private,
+ FdwPrivateSelectSql);
+ appendStringInfo(&sql, "%s FOR UPDATE", strVal(select_sql));
+ strVal(select_sql) = pstrdup(sql.data);
+
+ resetStringInfo(&sql);
+ }
+
+ switch (operation)
+ {
+ case CMD_INSERT:
+ deparseInsertSql(&sql, root, resultRelaion);
+ elog(DEBUG3, "Remote INSERT query: %s", sql.data);
+ break;
+ case CMD_UPDATE:
+ deparseUpdateSql(&sql, root, resultRelaion);
+ elog(DEBUG3, "Remote UPDATE query: %s", sql.data);
+ break;
+ case CMD_DELETE:
+ deparseDeleteSql(&sql, root, resultRelaion);
+ elog(DEBUG3, "Remote DELETE query: %s", sql.data);
+ break;
+ default:
+ elog(ERROR, "unexpected operation: %d", (int) operation);
+ }
+ return list_make1(makeString(sql.data));
+}
+
+static void
+postgresBeginForeignModify(ModifyTableState *mtstate,
+ ResultRelInfo *resultRelInfo,
+ List *fdw_private,
+ Plan *subplan,
+ int eflags)
+{
+ PostgresFdwModifyState *fmstate;
+ CmdType operation = mtstate->operation;
+ Relation frel = resultRelInfo->ri_RelationDesc;
+ AttrNumber n_params = RelationGetNumberOfAttributes(frel) + 1;
+ ForeignTable *ftable;
+ ForeignServer *fserver;
+ UserMapping *fuser;
+ Oid out_func_oid;
+ bool isvarlena;
+
+ /*
+ * Construct PostgresFdwExecutionState
+ */
+ fmstate = palloc0(sizeof(PostgresFdwExecutionState));
+
+ ftable = GetForeignTable(RelationGetRelid(frel));
+ fserver = GetForeignServer(ftable->serverid);
+ fuser = GetUserMapping(GetOuterUserId(), fserver->serverid);
+
+ fmstate->query = strVal(linitial(fdw_private));
+ fmstate->conn = GetConnection(fserver, fuser,
+ PGSQL_FDW_CONNTX_READ_WRITE);
+ fmstate->p_name = NULL;
+ fmstate->p_types = palloc0(sizeof(Oid) * n_params);
+ fmstate->p_flinfo = palloc0(sizeof(FmgrInfo) * n_params);
+
+ /* 1st parameter should be ctid on UPDATE or DELETE */
+ if (operation == CMD_UPDATE || operation == CMD_DELETE)
+ {
+ fmstate->p_types[fmstate->p_nums] = TIDOID;
+ getTypeOutputInfo(TIDOID, &out_func_oid, &isvarlena);
+ fmgr_info(out_func_oid, &fmstate->p_flinfo[fmstate->p_nums]);
+ fmstate->p_nums++;
+ }
+ /* following parameters should be regular columns */
+ if (operation == CMD_UPDATE || operation == CMD_INSERT)
+ {
+ AttrNumber index;
+
+ for (index=0; index < RelationGetNumberOfAttributes(frel); index++)
+ {
+ Form_pg_attribute attr = RelationGetDescr(frel)->attrs[index];
+
+ if (attr->attisdropped)
+ continue;
+
+ fmstate->p_types[fmstate->p_nums] = attr->atttypid;
+ getTypeOutputInfo(attr->atttypid, &out_func_oid, &isvarlena);
+ fmgr_info(out_func_oid, &fmstate->p_flinfo[fmstate->p_nums]);
+ fmstate->p_nums++;
+ }
+ }
+ Assert(fmstate->p_nums <= n_params);
+ fmstate->es_query_cxt = mtstate->ps.state->es_query_cxt;
+ resultRelInfo->ri_fdw_state = fmstate;
+}
+
+static void
+prepare_foreign_modify(PostgresFdwModifyState *fmstate)
+{
+ static int prep_id = 1;
+ char prep_name[NAMEDATALEN];
+ PGresult *res;
+
+ snprintf(prep_name, sizeof(prep_name),
+ "pgsql_fdw_prep_%08x", prep_id++);
+
+ res = PQprepare(fmstate->conn,
+ prep_name,
+ fmstate->query,
+ fmstate->p_nums,
+ fmstate->p_types);
+ if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
+ {
+ PQclear(res);
+ elog(ERROR, "could not prepare statement (%s): %s",
+ fmstate->query, PQerrorMessage(fmstate->conn));
+ }
+ PQclear(res);
+
+ fmstate->p_name = MemoryContextStrdup(fmstate->es_query_cxt, prep_name);
+}
+
+static int
+setup_exec_prepared(ResultRelInfo *resultRelInfo,
+ const char *rowid, HeapTuple tuple,
+ const char *p_values[], int p_lengths[])
+{
+ PostgresFdwModifyState *fmstate = resultRelInfo->ri_fdw_state;
+ Relation frel = resultRelInfo->ri_RelationDesc;
+ TupleDesc tupdesc = RelationGetDescr(frel);
+ int i, p_idx = 0;
+
+ /* 1st parameter should be ctid */
+ if (rowid)
+ {
+ p_values[p_idx] = rowid;
+ p_lengths[p_idx] = strlen(rowid) + 1;
+ p_idx++;
+ }
+
+ /* following parameters are as TupleDesc */
+ if (HeapTupleIsValid(tuple))
+ {
+ for (i = 0; i < tupdesc->natts; i++)
+ {
+ Form_pg_attribute attr = tupdesc->attrs[i];
+ Datum value;
+ bool isnull;
+
+ if (attr->attisdropped)
+ continue;
+
+ value = heap_getattr(tuple, attr->attnum, tupdesc, &isnull);
+ if (isnull)
+ {
+ p_values[p_idx] = NULL;
+ p_lengths[p_idx] = 0;
+ }
+ else
+ {
+ p_values[p_idx] =
+ OutputFunctionCall(&fmstate->p_flinfo[p_idx], value);
+ p_lengths[p_idx] = strlen(p_values[p_idx]) + 1;
+ }
+ p_idx++;
+ }
+ }
+ return p_idx;
+}
+
+static int
+postgresExecForeignInsert(ResultRelInfo *resultRelInfo,
+ HeapTuple tuple)
+{
+ PostgresFdwModifyState *fmstate = resultRelInfo->ri_fdw_state;
+ const char **p_values = alloca(sizeof(char *) * fmstate->p_nums);
+ int *p_lengths = alloca(sizeof(int) * fmstate->p_nums);
+ AttrNumber nattrs;
+ PGresult *res;
+ int n_rows;
+
+ if (!fmstate->p_name)
+ prepare_foreign_modify(fmstate);
+
+ nattrs = setup_exec_prepared(resultRelInfo,
+ NULL, tuple,
+ p_values, p_lengths);
+ Assert(fmstate->p_nums == nattrs);
+
+ res = PQexecPrepared(fmstate->conn,
+ fmstate->p_name,
+ nattrs,
+ p_values,
+ p_lengths,
+ NULL, 0);
+ if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
+ {
+ PQclear(res);
+ elog(ERROR, "could not execute prepared statement (%s): %s",
+ fmstate->query, PQerrorMessage(fmstate->conn));
+ }
+ n_rows = atoi(PQcmdTuples(res));
+ PQclear(res);
+
+ return n_rows;
+}
+
+static int
+postgresExecForeignDelete(ResultRelInfo *resultRelInfo, Datum rowid)
+{
+ PostgresFdwModifyState *fmstate = resultRelInfo->ri_fdw_state;
+ const char *p_values[1];
+ int p_lengths[1];
+ AttrNumber nattrs;
+ PGresult *res;
+ int n_rows;
+
+ if (!fmstate->p_name)
+ prepare_foreign_modify(fmstate);
+
+ nattrs = setup_exec_prepared(resultRelInfo,
+ DatumGetCString(rowid), NULL,
+ p_values, p_lengths);
+ Assert(fmstate->p_nums == nattrs);
+
+ res = PQexecPrepared(fmstate->conn,
+ fmstate->p_name,
+ nattrs,
+ p_values,
+ p_lengths,
+ NULL, 0);
+ if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
+ {
+ PQclear(res);
+ elog(ERROR, "could not execute prepared statement (%s): %s",
+ fmstate->query, PQerrorMessage(fmstate->conn));
+ }
+ n_rows = atoi(PQcmdTuples(res));
+ PQclear(res);
+
+ return n_rows;
+}
+
+static int
+postgresExecForeignUpdate(ResultRelInfo *resultRelInfo,
+ Datum rowid, HeapTuple tuple)
+{
+ PostgresFdwModifyState *fmstate = resultRelInfo->ri_fdw_state;
+ const char **p_values = alloca(sizeof(char *) * (fmstate->p_nums + 1));
+ int *p_lengths = alloca(sizeof(int) * (fmstate->p_nums + 1));
+ AttrNumber nattrs;
+ PGresult *res;
+ int n_rows;
+
+ if (!fmstate->p_name)
+ prepare_foreign_modify(fmstate);
+
+ nattrs = setup_exec_prepared(resultRelInfo,
+ DatumGetCString(rowid), tuple,
+ p_values, p_lengths);
+ Assert(fmstate->p_nums == nattrs);
+
+ res = PQexecPrepared(fmstate->conn,
+ fmstate->p_name,
+ nattrs,
+ p_values,
+ p_lengths,
+ NULL, 0);
+ if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
+ {
+ PQclear(res);
+ elog(ERROR, "could not execute prepared statement (%s): %s",
+ fmstate->query, PQerrorMessage(fmstate->conn));
+ }
+ n_rows = atoi(PQcmdTuples(res));
+ PQclear(res);
+
+ return n_rows;
+}
+
+static void
+postgresEndForeignModify(ResultRelInfo *resultRelInfo)
+{
+ PostgresFdwModifyState *fmstate = resultRelInfo->ri_fdw_state;
+
+ ReleaseConnection(fmstate->conn, false);
+ fmstate->conn = NULL;
+}
diff --git a/contrib/postgres_fdw/postgres_fdw.h b/contrib/postgres_fdw/postgres_fdw.h
index b5cefb8..3756a0d 100644
--- a/contrib/postgres_fdw/postgres_fdw.h
+++ b/contrib/postgres_fdw/postgres_fdw.h
@@ -30,7 +30,8 @@ int GetFetchCountOption(ForeignTable *table, ForeignServer *server);
void deparseSimpleSql(StringInfo buf,
PlannerInfo *root,
RelOptInfo *baserel,
- List *local_conds);
+ List *local_conds,
+ AttrNumber anum_rowid);
void appendWhereClause(StringInfo buf,
bool has_where,
List *exprs,
@@ -41,5 +42,8 @@ void classifyConditions(PlannerInfo *root,
List **param_conds,
List **local_conds);
void deparseAnalyzeSql(StringInfo buf, Relation rel);
+void deparseInsertSql(StringInfo buf, PlannerInfo *root, Index rtindex);
+void deparseUpdateSql(StringInfo buf, PlannerInfo *root, Index rtindex);
+void deparseDeleteSql(StringInfo buf, PlannerInfo *root, Index rtindex);
#endif /* POSTGRESQL_FDW_H */
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index 7845e70..700d607 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -300,6 +300,16 @@ ERROR OUT; -- ERROR
SELECT srvname FROM postgres_fdw_connections;
-- ===================================================================
+-- test for writable foreign table stuff (PoC stage now)
+-- ===================================================================
+INSERT INTO ft2 (c1,c2,c3) (SELECT c1+1000,c2+100, c3 || c3 FROM ft2 LIMIT 20);
+INSERT INTO ft2 (c1,c2,c3) VALUES (1101,201,'aaa'), (1102,202,'bbb'),(1103,203,'ccc') RETURNING *;
+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 *;
+DELETE FROM ft2 WHERE c1 % 10 = 5 RETURNING *;
+SELECT c1,c2,c3,c4 FROM ft2 ORDER BY c1;
+
+-- ===================================================================
-- cleanup
-- ===================================================================
DROP OPERATOR === (int, int) CASCADE;
diff --git a/doc/src/sgml/fdwhandler.sgml b/doc/src/sgml/fdwhandler.sgml
index 2d604ed..a2ec974 100644
--- a/doc/src/sgml/fdwhandler.sgml
+++ b/doc/src/sgml/fdwhandler.sgml
@@ -89,6 +89,46 @@
<para>
<programlisting>
+AttrNumber
+GetForeignRelWidth(PlannerInfo *root,
+ RelOptInfo *baserel,
+ Relation foreignrel,
+ bool inhparent,
+ List *targetList);
+</programlisting>
+ Obtain width of the result set to be fetched on foreign table scan.
+ This is an optional handler, and called at the beginning of planning
+ for a query involving a foreign table; during construction of
+ <literal>RelOptInfo</>.
+ <literal>root</> is the planner's global information about the query;
+ <literal>baserel</> is the planner's information about this table under
+ construction; and <literal>foreignrel</> is <literal>Relation</>
+ descriptor of the foreign table.
+ <literal>inhparent</> is a boolean to show whether the relation is
+ inheritance parent, even though foreign table does not support table
+ inheritance right now. <literal>targetList</> is list of
+ <literal>TargetEntry</> to be returned from the (sub-)query being
+ currently focused on.
+ </para>
+
+ <para>
+ The result value of this function shall be assigned on the
+ <literal>baserel->max_attr</>; that means expected number of
+ columns being fetched on the foreign table scan.
+ It should not be smaller than number of regular columns in definition
+ of this foreign table. All we can do is expansion of this value to
+ acquire slots for some additional attributes; being called pseudo-
+ columns. A typical usage of pseudo-column is to move an identifier of
+ a particular remote row to be updated or deleted from scanning stage
+ to modifying stage when foreign table performs as result relation of
+ the query. In addition, it also allows off-load burden of complex
+ calculation into foreign computing resources, and replace it with
+ just a reference of its calculated result on the remote side, instead
+ of local calculation.
+ </para>
+
+ <para>
+<programlisting>
void
GetForeignRelSize (PlannerInfo *root,
RelOptInfo *baserel,
@@ -96,7 +136,8 @@ GetForeignRelSize (PlannerInfo *root,
</programlisting>
Obtain relation size estimates for a foreign table. This is called
- at the beginning of planning for a query involving a foreign table.
+ at the beginning of planning for a query involving a foreign table,
+ unless <literal>FixupGetForeignRelWidth</> is not configured.
<literal>root</> is the planner's global information about the query;
<literal>baserel</> is the planner's information about this table; and
<literal>foreigntableid</> is the <structname>pg_class</> OID of the
@@ -315,6 +356,115 @@ AcquireSampleRowsFunc (Relation relation, int elevel,
</para>
<para>
+ If FDW driver supports writable foreign tables, it must implement both
+ of <literal>BeginForeignModify</> and <literal>EndForeignModify</> at
+ least. In addition, some or all of <literal>ExecForeignInsert</>,
+ <literal>ExecForeignUpdate</> and <literal>ExecForeignDelete</> need
+ to be implement according to capability of FDW.
+ </para>
+
+ <para>
+<programlisting>
+List *
+PlanForeignModify(PlannerInfo *root,
+ ModifyTable *plan,
+ Index resultRelation,
+ Plan *subplan);
+</programlisting>
+ It allows FDW drivers to construct its own private stuff relevant to
+ the foreign table to be modified. This private stuff should have
+ form of <literal>List *</> data, then it shall be delivered as
+ 3rd argument of <literal>BeginForeignModify</> on execution stage.
+ </para>
+ <para>
+ <literal>root</> is the planner's global information about the query.
+ <literal>plan</> is the master plan to modify result relation according
+ to its command type. Please also pay attention <literal>ModifyTable</>
+ may have multiple result relations in the future revision, even though
+ we don't support inheritance feature on foreign tables.
+ <literal>resultRelation</> is an index towards result relation in the
+ range table entries, and <literal>subplan</> is the relevant scan plan;
+ that should be <literal>ForeignScan</> at <literal>UPDATE</> or
+ <literal>DELETE</>, so the driver can access private datum of scan stage
+ using this argument.
+ </para>
+
+ <para>
+<programlisting>
+void
+BeginForeignModify (ModifyTableState *mtstate,
+ ResultRelInfo *resultRelInfo,
+ List *fdw_private,
+ Plan *subplan,
+ int eflags);
+</programlisting>
+ It is invoked at beginning of foreign table modification, durting
+ execurot startup. This routine should perform any initialization
+ stuffs needed prior to actual table modifications, but not start
+ modifying the actual tuples. (That should be done for each call of
+ <function>ExecForeignInsert</>, <function>ExecForeignUpdate</> or
+ <function>ExecForeignDelete</>.)
+ </para>
+ <para>
+ The <structfield>ri_fdw_state</> field of <structname>ResultRelInfo</>
+ is reserved to store any private stuff relevant to this foreign table,
+ so it is assumed this routine handles any initialization stuff and
+ save its per-scan state on this field.
+ Please also pay attention <literal>ModifyTableState</> may have multiple
+ result relations in the future revision, even though we don't support
+ inheritance feature on foreign tables. <literal>resultRelInfo</> is
+ master information connected to this foreign table.
+ <literal>fdw_private</> is a private stuff constructed at
+ <literal>PlanForeignModify</>, and <literal>subplan</> is the relevant
+ scan plan of this table modification.
+ </para>
+
+ <para>
+<programlisting>
+int
+ExecForeignInsert (ResultRelInfo *resultRelInfo,
+ HeapTuple tuple);
+</programlisting>
+ Insert the given tuple into backing storage behalf on the foreign table.
+ The supplied tuple is already formed according to the definition of
+ relation, thus, all the pseudo-columns are already filtered out.
+ It can return number of rows being inserted, usually 1.
+ </para>
+
+ <para>
+<programlisting>
+int
+ExecForeignDelete (ResultRelInfo *resultRelInfo,
+ Datum rowid);
+</programlisting>
+ Delete the tuple being identified with <literal>rowid</> in the backing
+ storage behalf on the foreign table.
+ It can return number of rows being deleted, usually 1.
+ </para>
+
+ <para>
+<programlisting>
+int
+ExecForeignUpdate (ResultRelInfo *resultRelInfo,
+ Datum rowid,
+ HeapTuple tuple);
+</programlisting>
+ Update the tuple being identified with <literal>rowid</> in the backing
+ storage behalf on the foreign table, by the given tuple.
+ It can return number of rows being updated, usually 1.
+ </para>
+
+ <para>
+<programlisting>
+void
+EndForeignModify (ResultRelInfo *resultRelInfo);
+</programlisting>
+ End the modification and release resources. It is normally not important
+ to release palloc'd memory, but for example open files and connections
+ to remote servers should be cleaned up.
+ </para>
+
+ <para>
The <structname>FdwRoutine</> struct type is declared in
<filename>src/include/foreign/fdwapi.h</>, which see for additional
details.
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index dbd3755..ccc1f3a 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -44,6 +44,7 @@
#include "catalog/namespace.h"
#include "commands/trigger.h"
#include "executor/execdebug.h"
+#include "foreign/fdwapi.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "optimizer/clauses.h"
@@ -934,6 +935,7 @@ void
CheckValidResultRel(Relation resultRel, CmdType operation)
{
TriggerDesc *trigDesc = resultRel->trigdesc;
+ FdwRoutine *fdwroutine;
switch (resultRel->rd_rel->relkind)
{
@@ -985,10 +987,34 @@ CheckValidResultRel(Relation resultRel, CmdType operation)
}
break;
case RELKIND_FOREIGN_TABLE:
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("cannot change foreign table \"%s\"",
- RelationGetRelationName(resultRel))));
+ fdwroutine = GetFdwRoutineByRelId(RelationGetRelid(resultRel));
+ switch (operation)
+ {
+ case CMD_INSERT:
+ if (!fdwroutine || !fdwroutine->ExecForeignInsert)
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("cannot insert into foreign table \"%s\"",
+ RelationGetRelationName(resultRel))));
+ break;
+ case CMD_UPDATE:
+ if (!fdwroutine || !fdwroutine->ExecForeignUpdate)
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("cannot update foreign table \"%s\"",
+ RelationGetRelationName(resultRel))));
+ break;
+ case CMD_DELETE:
+ if (!fdwroutine || !fdwroutine->ExecForeignDelete)
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("cannot delete from foreign table \"%s\"",
+ RelationGetRelationName(resultRel))));
+ break;
+ default:
+ elog(ERROR, "unrecognized CmdType: %d", (int) operation);
+ break;
+ }
break;
default:
ereport(ERROR,
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index 9204859..1ee626f 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -25,6 +25,7 @@
#include "executor/executor.h"
#include "executor/nodeForeignscan.h"
#include "foreign/fdwapi.h"
+#include "nodes/nodeFuncs.h"
#include "utils/rel.h"
static TupleTableSlot *ForeignNext(ForeignScanState *node);
@@ -93,6 +94,133 @@ ExecForeignScan(ForeignScanState *node)
(ExecScanRecheckMtd) ForeignRecheck);
}
+/*
+ * pseudo_column_walker
+ *
+ * helper routine of GetPseudoTupleDesc. It pulls Var nodes that reference
+ * pseudo columns from targetlis of the relation
+ */
+typedef struct
+{
+ Relation relation;
+ Index varno;
+ List *pcolumns;
+ AttrNumber max_attno;
+} pseudo_column_walker_context;
+
+static bool
+pseudo_column_walker(Node *node, pseudo_column_walker_context *context)
+{
+ if (node == NULL)
+ return false;
+ if (IsA(node, Var))
+ {
+ Var *var = (Var *) node;
+ ListCell *cell;
+
+ if (var->varno == context->varno && var->varlevelsup == 0 &&
+ var->varattno > RelationGetNumberOfAttributes(context->relation))
+ {
+ foreach (cell, context->pcolumns)
+ {
+ Var *temp = lfirst(cell);
+
+ if (temp->varattno == var->varattno)
+ {
+ if (!equal(var, temp))
+ elog(ERROR, "asymmetric pseudo column appeared");
+ break;
+ }
+ }
+ if (!cell)
+ {
+ context->pcolumns = lappend(context->pcolumns, var);
+ if (var->varattno > context->max_attno)
+ context->max_attno = var->varattno;
+ }
+ }
+ return false;
+ }
+
+ /* Should not find an unplanned subquery */
+ Assert(!IsA(node, Query));
+
+ return expression_tree_walker(node, pseudo_column_walker,
+ (void *)context);
+}
+
+/*
+ * GetPseudoTupleDesc
+ *
+ * It generates TupleDesc structure including pseudo-columns if required.
+ */
+static TupleDesc
+GetPseudoTupleDesc(ForeignScan *node, Relation relation)
+{
+ pseudo_column_walker_context context;
+ List *target_list = node->scan.plan.targetlist;
+ TupleDesc tupdesc;
+ AttrNumber attno;
+ ListCell *cell;
+ ListCell *prev;
+ bool hasoid;
+
+ context.relation = relation;
+ context.varno = node->scan.scanrelid;
+ context.pcolumns = NIL;
+ context.max_attno = -1;
+
+ pseudo_column_walker((Node *)target_list, (void *)&context);
+ Assert(context.max_attno > RelationGetNumberOfAttributes(relation));
+
+ hasoid = RelationGetForm(relation)->relhasoids;
+ tupdesc = CreateTemplateTupleDesc(context.max_attno, hasoid);
+
+ for (attno = 1; attno <= context.max_attno; attno++)
+ {
+ /* case of regular columns */
+ if (attno <= RelationGetNumberOfAttributes(relation))
+ {
+ memcpy(tupdesc->attrs[attno - 1],
+ RelationGetDescr(relation)->attrs[attno - 1],
+ ATTRIBUTE_FIXED_PART_SIZE);
+ continue;
+ }
+
+ /* case of pseudo columns */
+ prev = NULL;
+ foreach (cell, context.pcolumns)
+ {
+ Var *var = lfirst(cell);
+
+ if (var->varattno == attno)
+ {
+ char namebuf[NAMEDATALEN];
+
+ snprintf(namebuf, sizeof(namebuf),
+ "pseudo_column_%d", attno);
+
+ TupleDescInitEntry(tupdesc,
+ attno,
+ namebuf,
+ var->vartype,
+ var->vartypmod,
+ 0);
+ TupleDescInitEntryCollation(tupdesc,
+ attno,
+ var->varcollid);
+ context.pcolumns
+ = list_delete_cell(context.pcolumns, cell, prev);
+ break;
+ }
+ prev = cell;
+ }
+ if (!cell)
+ elog(ERROR, "pseudo column %d of %s not in target list",
+ attno, RelationGetRelationName(relation));
+ }
+ return tupdesc;
+}
/* ----------------------------------------------------------------
* ExecInitForeignScan
@@ -103,6 +231,7 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
{
ForeignScanState *scanstate;
Relation currentRelation;
+ TupleDesc tupdesc;
FdwRoutine *fdwroutine;
/* check for unsupported flags */
@@ -149,7 +278,12 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
/*
* get the scan type from the relation descriptor.
*/
- ExecAssignScanType(&scanstate->ss, RelationGetDescr(currentRelation));
+ if (node->fsPseudoCol)
+ tupdesc = GetPseudoTupleDesc(node, currentRelation);
+ else
+ tupdesc = RelationGetDescr(currentRelation);
+
+ ExecAssignScanType(&scanstate->ss, tupdesc);
/*
* Initialize result tuple type and projection info.
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index d31015c..bf9421e 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -42,6 +42,7 @@
#include "commands/trigger.h"
#include "executor/executor.h"
#include "executor/nodeModifyTable.h"
+#include "foreign/fdwapi.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "storage/bufmgr.h"
@@ -170,6 +171,7 @@ ExecInsert(TupleTableSlot *slot,
Relation resultRelationDesc;
Oid newId;
List *recheckIndexes = NIL;
+ int num_rows = 1;
/*
* get the heap tuple out of the tuple table slot, making sure we have a
@@ -225,6 +227,14 @@ ExecInsert(TupleTableSlot *slot,
newId = InvalidOid;
}
+ else if (resultRelInfo->ri_fdwroutine)
+ {
+ FdwRoutine *fdwroutine = resultRelInfo->ri_fdwroutine;
+
+ num_rows = fdwroutine->ExecForeignInsert(resultRelInfo, tuple);
+
+ newId = InvalidOid;
+ }
else
{
/*
@@ -252,7 +262,7 @@ ExecInsert(TupleTableSlot *slot,
if (canSetTag)
{
- (estate->es_processed)++;
+ (estate->es_processed) += num_rows;
estate->es_lastoid = newId;
setLastTid(&(tuple->t_self));
}
@@ -285,7 +295,7 @@ ExecInsert(TupleTableSlot *slot,
* ----------------------------------------------------------------
*/
static TupleTableSlot *
-ExecDelete(ItemPointer tupleid,
+ExecDelete(Datum rowid,
HeapTupleHeader oldtuple,
TupleTableSlot *planSlot,
EPQState *epqstate,
@@ -296,6 +306,7 @@ ExecDelete(ItemPointer tupleid,
Relation resultRelationDesc;
HTSU_Result result;
HeapUpdateFailureData hufd;
+ int num_rows = 1;
/*
* get information on the (current) result relation
@@ -310,7 +321,7 @@ ExecDelete(ItemPointer tupleid,
bool dodelete;
dodelete = ExecBRDeleteTriggers(estate, epqstate, resultRelInfo,
- tupleid);
+ (ItemPointer)DatumGetPointer(rowid));
if (!dodelete) /* "do nothing" */
return NULL;
@@ -334,8 +345,16 @@ ExecDelete(ItemPointer tupleid,
if (!dodelete) /* "do nothing" */
return NULL;
}
+ else if (resultRelInfo->ri_fdwroutine)
+ {
+ FdwRoutine *fdwroutine = resultRelInfo->ri_fdwroutine;
+
+ num_rows = fdwroutine->ExecForeignDelete(resultRelInfo, rowid);
+ }
else
{
+ ItemPointer tupleid = (ItemPointer) DatumGetPointer(rowid);
+
/*
* delete the tuple
*
@@ -430,10 +449,11 @@ ldelete:;
}
if (canSetTag)
- (estate->es_processed)++;
+ (estate->es_processed) += num_rows;
/* AFTER ROW DELETE Triggers */
- ExecARDeleteTriggers(estate, resultRelInfo, tupleid);
+ ExecARDeleteTriggers(estate, resultRelInfo,
+ (ItemPointer)DatumGetPointer(rowid));
/* Process RETURNING if present */
if (resultRelInfo->ri_projectReturning)
@@ -457,7 +477,8 @@ ldelete:;
}
else
{
- deltuple.t_self = *tupleid;
+ ItemPointerCopy((ItemPointer)DatumGetPointer(rowid),
+ &deltuple.t_self);
if (!heap_fetch(resultRelationDesc, SnapshotAny,
&deltuple, &delbuffer, false, NULL))
elog(ERROR, "failed to fetch deleted tuple for DELETE RETURNING");
@@ -499,7 +520,7 @@ ldelete:;
* ----------------------------------------------------------------
*/
static TupleTableSlot *
-ExecUpdate(ItemPointer tupleid,
+ExecUpdate(Datum rowid,
HeapTupleHeader oldtuple,
TupleTableSlot *slot,
TupleTableSlot *planSlot,
@@ -513,6 +534,7 @@ ExecUpdate(ItemPointer tupleid,
HTSU_Result result;
HeapUpdateFailureData hufd;
List *recheckIndexes = NIL;
+ int num_rows = 1;
/*
* abort the operation if not running transactions
@@ -537,7 +559,7 @@ ExecUpdate(ItemPointer tupleid,
resultRelInfo->ri_TrigDesc->trig_update_before_row)
{
slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
- tupleid, slot);
+ (ItemPointer)DatumGetPointer(rowid), slot);
if (slot == NULL) /* "do nothing" */
return NULL;
@@ -567,8 +589,16 @@ ExecUpdate(ItemPointer tupleid,
/* trigger might have changed tuple */
tuple = ExecMaterializeSlot(slot);
}
+ else if (resultRelInfo->ri_fdwroutine)
+ {
+ FdwRoutine *fdwroutine = resultRelInfo->ri_fdwroutine;
+
+ num_rows = fdwroutine->ExecForeignUpdate(resultRelInfo, rowid, tuple);
+ }
else
{
+ ItemPointer tupleid = (ItemPointer) DatumGetPointer(rowid);
+
/*
* Check the constraints of the tuple
*
@@ -687,11 +717,12 @@ lreplace:;
}
if (canSetTag)
- (estate->es_processed)++;
+ (estate->es_processed) += num_rows;
/* AFTER ROW UPDATE Triggers */
- ExecARUpdateTriggers(estate, resultRelInfo, tupleid, tuple,
- recheckIndexes);
+ ExecARUpdateTriggers(estate, resultRelInfo,
+ (ItemPointer) DatumGetPointer(rowid),
+ tuple, recheckIndexes);
list_free(recheckIndexes);
@@ -771,6 +802,7 @@ ExecModifyTable(ModifyTableState *node)
TupleTableSlot *planSlot;
ItemPointer tupleid = NULL;
ItemPointerData tuple_ctid;
+ Datum rowid = 0;
HeapTupleHeader oldtuple = NULL;
/*
@@ -859,17 +891,19 @@ ExecModifyTable(ModifyTableState *node)
if (junkfilter != NULL)
{
/*
- * extract the 'ctid' or 'wholerow' junk attribute.
+ * extract the 'ctid', 'rowid' or 'wholerow' junk attribute.
*/
if (operation == CMD_UPDATE || operation == CMD_DELETE)
{
+ char relkind;
Datum datum;
bool isNull;
- if (resultRelInfo->ri_RelationDesc->rd_rel->relkind == RELKIND_RELATION)
+ relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
+ if (relkind == RELKIND_RELATION)
{
datum = ExecGetJunkAttribute(slot,
- junkfilter->jf_junkAttNo,
+ junkfilter->jf_junkRowidNo,
&isNull);
/* shouldn't ever get a null result... */
if (isNull)
@@ -877,13 +911,33 @@ ExecModifyTable(ModifyTableState *node)
tupleid = (ItemPointer) DatumGetPointer(datum);
tuple_ctid = *tupleid; /* be sure we don't free
- * ctid!! */
- tupleid = &tuple_ctid;
+ * ctid ! */
+ rowid = PointerGetDatum(&tuple_ctid);
+ }
+ else if (relkind == RELKIND_FOREIGN_TABLE)
+ {
+ datum = ExecGetJunkAttribute(slot,
+ junkfilter->jf_junkRowidNo,
+ &isNull);
+ /* shouldn't ever get a null result... */
+ if (isNull)
+ elog(ERROR, "rowid is NULL");
+
+ rowid = datum;
+
+ datum = ExecGetJunkAttribute(slot,
+ junkfilter->jf_junkRecordNo,
+ &isNull);
+ /* shouldn't ever get a null result... */
+ if (isNull)
+ elog(ERROR, "wholerow is NULL");
+
+ oldtuple = DatumGetHeapTupleHeader(datum);
}
else
{
datum = ExecGetJunkAttribute(slot,
- junkfilter->jf_junkAttNo,
+ junkfilter->jf_junkRecordNo,
&isNull);
/* shouldn't ever get a null result... */
if (isNull)
@@ -906,11 +960,11 @@ ExecModifyTable(ModifyTableState *node)
slot = ExecInsert(slot, planSlot, estate, node->canSetTag);
break;
case CMD_UPDATE:
- slot = ExecUpdate(tupleid, oldtuple, slot, planSlot,
+ slot = ExecUpdate(rowid, oldtuple, slot, planSlot,
&node->mt_epqstate, estate, node->canSetTag);
break;
case CMD_DELETE:
- slot = ExecDelete(tupleid, oldtuple, planSlot,
+ slot = ExecDelete(rowid, oldtuple, planSlot,
&node->mt_epqstate, estate, node->canSetTag);
break;
default:
@@ -997,6 +1051,8 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
i = 0;
foreach(l, node->plans)
{
+ char relkind;
+
subplan = (Plan *) lfirst(l);
/*
@@ -1022,6 +1078,24 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
estate->es_result_relation_info = resultRelInfo;
mtstate->mt_plans[i] = ExecInitNode(subplan, estate, eflags);
+ /*
+ * Also tells FDW extensions to init the plan for this result rel
+ */
+ relkind = RelationGetForm(resultRelInfo->ri_RelationDesc)->relkind;
+ if (relkind == RELKIND_FOREIGN_TABLE)
+ {
+ Oid relid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
+ FdwRoutine *fdwroutine = GetFdwRoutineByRelId(relid);
+ List *fdwprivate = list_nth(node->fdwPrivList, i);
+
+ Assert(fdwroutine != NULL);
+ resultRelInfo->ri_fdwroutine = fdwroutine;
+ resultRelInfo->ri_fdw_state = NULL;
+
+ if (fdwroutine->BeginForeignModify)
+ fdwroutine->BeginForeignModify(mtstate, resultRelInfo,
+ fdwprivate, subplan, eflags);
+ }
resultRelInfo++;
i++;
}
@@ -1163,6 +1237,8 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
for (i = 0; i < nplans; i++)
{
JunkFilter *j;
+ char relkind =
+ RelationGetForm(resultRelInfo->ri_RelationDesc)->relkind;
subplan = mtstate->mt_plans[i]->plan;
if (operation == CMD_INSERT || operation == CMD_UPDATE)
@@ -1176,16 +1252,27 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
if (operation == CMD_UPDATE || operation == CMD_DELETE)
{
/* For UPDATE/DELETE, find the appropriate junk attr now */
- if (resultRelInfo->ri_RelationDesc->rd_rel->relkind == RELKIND_RELATION)
+ if (relkind == RELKIND_RELATION)
{
- j->jf_junkAttNo = ExecFindJunkAttribute(j, "ctid");
- if (!AttributeNumberIsValid(j->jf_junkAttNo))
+ j->jf_junkRowidNo = ExecFindJunkAttribute(j, "ctid");
+ if (!AttributeNumberIsValid(j->jf_junkRowidNo))
elog(ERROR, "could not find junk ctid column");
}
+ else if (relkind == RELKIND_FOREIGN_TABLE)
+ {
+ j->jf_junkRowidNo = ExecFindJunkAttribute(j, "rowid");
+ if (!AttributeNumberIsValid(j->jf_junkRowidNo))
+ elog(ERROR, "could not find junk rowid column");
+ j->jf_junkRecordNo
+ = ExecFindJunkAttribute(j, "record");
+ if (!AttributeNumberIsValid(j->jf_junkRecordNo))
+ elog(ERROR, "could not find junk wholerow column");
+ }
else
{
- j->jf_junkAttNo = ExecFindJunkAttribute(j, "wholerow");
- if (!AttributeNumberIsValid(j->jf_junkAttNo))
+ j->jf_junkRecordNo
+ = ExecFindJunkAttribute(j, "wholerow");
+ if (!AttributeNumberIsValid(j->jf_junkRecordNo))
elog(ERROR, "could not find junk wholerow column");
}
}
@@ -1239,6 +1326,21 @@ ExecEndModifyTable(ModifyTableState *node)
{
int i;
+ /* Let the FDW shut dowm */
+ for (i=0; i < node->ps.state->es_num_result_relations; i++)
+ {
+ ResultRelInfo *resultRelInfo
+ = &node->ps.state->es_result_relations[i];
+
+ if (resultRelInfo->ri_fdwroutine)
+ {
+ FdwRoutine *fdwroutine = resultRelInfo->ri_fdwroutine;
+
+ if (fdwroutine->EndForeignModify)
+ fdwroutine->EndForeignModify(resultRelInfo);
+ }
+ }
+
/*
* Free the exprcontext
*/
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index d8845aa..20ddecd 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -571,3 +571,37 @@ get_foreign_server_oid(const char *servername, bool missing_ok)
errmsg("server \"%s\" does not exist", servername)));
return oid;
}
+
+/*
+ * get_pseudo_rowid_column
+ *
+ * It picks up an attribute number to be used for pseudo rowid columns,
+ * if exists. It should be injected at rewriteHandler.c if supplied query
+ * is UPDATE or DELETE command. Elsewhere, it returns InvalidAttrNumber.
+ */
+AttrNumber
+get_pseudo_rowid_column(RelOptInfo *baserel, List *targetList)
+{
+ ListCell *cell;
+
+ foreach (cell, targetList)
+ {
+ TargetEntry *tle = lfirst(cell);
+
+ if (tle->resjunk &&
+ tle->resname && strcmp(tle->resname, "rowid") == 0)
+ {
+ Var *var;
+
+ if (!IsA(tle->expr, Var))
+ elog(ERROR, "unexpected node on junk rowid entry: %d",
+ (int) nodeTag(tle->expr));
+
+ var = (Var *) tle->expr;
+ Assert(baserel->relid == var->varno);
+
+ return var->varattno;
+ }
+ }
+ return InvalidAttrNumber;
+}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 9387ee9..5b1e8ac 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -181,6 +181,7 @@ _copyModifyTable(const ModifyTable *from)
COPY_NODE_FIELD(returningLists);
COPY_NODE_FIELD(rowMarks);
COPY_SCALAR_FIELD(epqParam);
+ COPY_NODE_FIELD(fdwPrivList);
return newnode;
}
@@ -594,6 +595,7 @@ _copyForeignScan(const ForeignScan *from)
COPY_NODE_FIELD(fdw_exprs);
COPY_NODE_FIELD(fdw_private);
COPY_SCALAR_FIELD(fsSystemCol);
+ COPY_SCALAR_FIELD(fsPseudoCol);
return newnode;
}
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 35c6287..c32ebd3 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -335,6 +335,7 @@ _outModifyTable(StringInfo str, const ModifyTable *node)
WRITE_NODE_FIELD(returningLists);
WRITE_NODE_FIELD(rowMarks);
WRITE_INT_FIELD(epqParam);
+ WRITE_NODE_FIELD(fdwPrivList);
}
static void
@@ -562,6 +563,7 @@ _outForeignScan(StringInfo str, const ForeignScan *node)
WRITE_NODE_FIELD(fdw_exprs);
WRITE_NODE_FIELD(fdw_private);
WRITE_BOOL_FIELD(fsSystemCol);
+ WRITE_BOOL_FIELD(fsPseudoCol);
}
static void
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 030f420..e9edb87 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -39,6 +39,7 @@
#include "parser/parse_clause.h"
#include "parser/parsetree.h"
#include "utils/lsyscache.h"
+#include "utils/rel.h"
static Plan *create_plan_recurse(PlannerInfo *root, Path *best_path);
@@ -1943,6 +1944,8 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
RelOptInfo *rel = best_path->path.parent;
Index scan_relid = rel->relid;
RangeTblEntry *rte;
+ Relation relation;
+ AttrNumber num_attrs;
int i;
/* it should be a base rel... */
@@ -2001,6 +2004,22 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
}
}
+ /*
+ * Also, detect whether any pseudo columns are requested from rel.
+ */
+ relation = heap_open(rte->relid, NoLock);
+ scan_plan->fsPseudoCol = false;
+ num_attrs = RelationGetNumberOfAttributes(relation);
+ for (i = num_attrs + 1; i <= rel->max_attr; i++)
+ {
+ if (!bms_is_empty(rel->attr_needed[i - rel->min_attr]))
+ {
+ scan_plan->fsPseudoCol = true;
+ break;
+ }
+ }
+ heap_close(relation, NoLock);
+
return scan_plan;
}
@@ -4695,7 +4714,8 @@ make_result(PlannerInfo *root,
* to make it look better sometime.
*/
ModifyTable *
-make_modifytable(CmdType operation, bool canSetTag,
+make_modifytable(PlannerInfo *root,
+ CmdType operation, bool canSetTag,
List *resultRelations,
List *subplans, List *returningLists,
List *rowMarks, int epqParam)
@@ -4704,6 +4724,8 @@ make_modifytable(CmdType operation, bool canSetTag,
Plan *plan = &node->plan;
double total_size;
ListCell *subnode;
+ ListCell *resultRel;
+ List *fdw_priv_list = NIL;
Assert(list_length(resultRelations) == list_length(subplans));
Assert(returningLists == NIL ||
@@ -4746,6 +4768,30 @@ make_modifytable(CmdType operation, bool canSetTag,
node->rowMarks = rowMarks;
node->epqParam = epqParam;
+ /*
+ * Allows FDW driver to construct its private plan if result relation
+ * is foreign table.
+ */
+ forboth (resultRel, resultRelations, subnode, subplans)
+ {
+ RangeTblEntry *rte = rt_fetch(lfirst_int(resultRel),
+ root->parse->rtable);
+ List *fdw_private = NIL;
+ char relkind = get_rel_relkind(rte->relid);
+
+ if (relkind == RELKIND_FOREIGN_TABLE)
+ {
+ FdwRoutine *fdwroutine = GetFdwRoutineByRelId(rte->relid);
+
+ if (fdwroutine && fdwroutine->PlanForeignModify)
+ fdw_private = fdwroutine->PlanForeignModify(root, node,
+ lfirst_int(resultRel),
+ (Plan *) lfirst(subnode));
+ }
+ fdw_priv_list = lappend(fdw_priv_list, fdw_private);
+ }
+ node->fdwPrivList = fdw_priv_list;
+
return node;
}
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index bd719b5..57ba192 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -85,7 +85,7 @@ static void check_hashjoinable(RestrictInfo *restrictinfo);
* "other rel" RelOptInfos for the members of any appendrels we find here.)
*/
void
-add_base_rels_to_query(PlannerInfo *root, Node *jtnode)
+add_base_rels_to_query(PlannerInfo *root, List *tlist, Node *jtnode)
{
if (jtnode == NULL)
return;
@@ -93,7 +93,7 @@ add_base_rels_to_query(PlannerInfo *root, Node *jtnode)
{
int varno = ((RangeTblRef *) jtnode)->rtindex;
- (void) build_simple_rel(root, varno, RELOPT_BASEREL);
+ (void) build_simple_rel(root, varno, tlist, RELOPT_BASEREL);
}
else if (IsA(jtnode, FromExpr))
{
@@ -101,14 +101,14 @@ add_base_rels_to_query(PlannerInfo *root, Node *jtnode)
ListCell *l;
foreach(l, f->fromlist)
- add_base_rels_to_query(root, lfirst(l));
+ add_base_rels_to_query(root, tlist, lfirst(l));
}
else if (IsA(jtnode, JoinExpr))
{
JoinExpr *j = (JoinExpr *) jtnode;
- add_base_rels_to_query(root, j->larg);
- add_base_rels_to_query(root, j->rarg);
+ add_base_rels_to_query(root, tlist, j->larg);
+ add_base_rels_to_query(root, tlist, j->rarg);
}
else
elog(ERROR, "unrecognized node type: %d",
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c
index c2488a4..2d292dd 100644
--- a/src/backend/optimizer/plan/planmain.c
+++ b/src/backend/optimizer/plan/planmain.c
@@ -163,7 +163,7 @@ query_planner(PlannerInfo *root, List *tlist,
* rangetable may contain RTEs for rels not actively part of the query,
* for example views. We don't want to make RelOptInfos for them.
*/
- add_base_rels_to_query(root, (Node *) parse->jointree);
+ add_base_rels_to_query(root, tlist, (Node *) parse->jointree);
/*
* Examine the targetlist and join tree, adding entries to baserel
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index b61005f..2d72244 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -571,7 +571,8 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
else
rowMarks = root->rowMarks;
- plan = (Plan *) make_modifytable(parse->commandType,
+ plan = (Plan *) make_modifytable(root,
+ parse->commandType,
parse->canSetTag,
list_make1_int(parse->resultRelation),
list_make1(plan),
@@ -964,7 +965,8 @@ inheritance_planner(PlannerInfo *root)
rowMarks = root->rowMarks;
/* And last, tack on a ModifyTable node to do the UPDATE/DELETE work */
- return (Plan *) make_modifytable(parse->commandType,
+ return (Plan *) make_modifytable(root,
+ parse->commandType,
parse->canSetTag,
resultRelations,
subplans,
@@ -3385,7 +3387,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid)
setup_simple_rel_arrays(root);
/* Build RelOptInfo */
- rel = build_simple_rel(root, 1, RELOPT_BASEREL);
+ rel = build_simple_rel(root, 1, NIL, RELOPT_BASEREL);
/* Locate IndexOptInfo for the target index */
indexInfo = NULL;
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index b91e9f4..61bc447 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -236,7 +236,8 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,
* used for anything here, but it carries the subroot data structures
* forward to setrefs.c processing.
*/
- rel = build_simple_rel(root, rtr->rtindex, RELOPT_BASEREL);
+ rel = build_simple_rel(root, rtr->rtindex, refnames_tlist,
+ RELOPT_BASEREL);
/* plan_params should not be in use in current query level */
Assert(root->plan_params == NIL);
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 04d5028..b16cde3 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -25,6 +25,7 @@
#include "access/xlog.h"
#include "catalog/catalog.h"
#include "catalog/heap.h"
+#include "foreign/fdwapi.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "optimizer/clauses.h"
@@ -80,7 +81,7 @@ static List *build_index_tlist(PlannerInfo *root, IndexOptInfo *index,
*/
void
get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
- RelOptInfo *rel)
+ List *tlist, RelOptInfo *rel)
{
Index varno = rel->relid;
Relation relation;
@@ -104,6 +105,30 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
rel->max_attr = RelationGetNumberOfAttributes(relation);
rel->reltablespace = RelationGetForm(relation)->reltablespace;
+ /*
+ * Adjust width of attr_needed slot in case when FDW extension wants
+ * or needs to return pseudo-columns also, not only columns in its
+ * table definition.
+ * GetForeignRelWidth, an optional FDW handler, enables FDW extension
+ * to save properties of pseudo-column on its private field.
+ * When foreign-table is the target of UPDATE/DELETE, query-rewritter
+ * injects "rowid" pseudo-column to track remote row to be modified,
+ * so FDW has to track which varattno shall perform as "rowid".
+ */
+ if (RelationGetForm(relation)->relkind == RELKIND_FOREIGN_TABLE)
+ {
+ FdwRoutine *fdwroutine = GetFdwRoutineByRelId(relationObjectId);
+
+ if (fdwroutine->GetForeignRelWidth)
+ {
+ rel->max_attr = fdwroutine->GetForeignRelWidth(root, rel,
+ relation,
+ inhparent,
+ tlist);
+ Assert(rel->max_attr >= RelationGetNumberOfAttributes(relation));
+ }
+ }
+
Assert(rel->max_attr >= rel->min_attr);
rel->attr_needed = (Relids *)
palloc0((rel->max_attr - rel->min_attr + 1) * sizeof(Relids));
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index f724714..84b674c 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -80,7 +80,8 @@ setup_simple_rel_arrays(PlannerInfo *root)
* Construct a new RelOptInfo for a base relation or 'other' relation.
*/
RelOptInfo *
-build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
+build_simple_rel(PlannerInfo *root, int relid, List *tlist,
+ RelOptKind reloptkind)
{
RelOptInfo *rel;
RangeTblEntry *rte;
@@ -133,7 +134,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
{
case RTE_RELATION:
/* Table --- retrieve statistics from the system catalogs */
- get_relation_info(root, rte->relid, rte->inh, rel);
+ get_relation_info(root, rte->relid, rte->inh, tlist, rel);
break;
case RTE_SUBQUERY:
case RTE_FUNCTION:
@@ -180,7 +181,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
if (appinfo->parent_relid != relid)
continue;
- (void) build_simple_rel(root, appinfo->child_relid,
+ (void) build_simple_rel(root, appinfo->child_relid, tlist,
RELOPT_OTHER_MEMBER_REL);
}
}
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index b785c26..6cd6241 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -1165,7 +1165,10 @@ rewriteTargetListUD(Query *parsetree, RangeTblEntry *target_rte,
Relation target_relation)
{
Var *var;
- const char *attrname;
+ List *varList;
+ List *attNameList;
+ ListCell *cell1;
+ ListCell *cell2;
TargetEntry *tle;
if (target_relation->rd_rel->relkind == RELKIND_RELATION)
@@ -1179,8 +1182,35 @@ rewriteTargetListUD(Query *parsetree, RangeTblEntry *target_rte,
-1,
InvalidOid,
0);
+ varList = list_make1(var);
+ attNameList = list_make1("ctid");
+ }
+ else if (target_relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+ {
+ /*
+ * Emit Rowid so that executor can find the row to update or delete.
+ */
+ var = makeVar(parsetree->resultRelation,
+ RelationGetNumberOfAttributes(target_relation) + 1,
+ CSTRINGOID,
+ -2,
+ InvalidOid,
+ 0);
+ varList = list_make1(var);
+
+ /*
+ * Emit generic record Var so that executor will have the "old" view
+ * row to pass the RETURNING clause (or upcoming triggers).
+ */
+ var = makeVar(parsetree->resultRelation,
+ InvalidAttrNumber,
+ RECORDOID,
+ -1,
+ InvalidOid,
+ 0);
+ varList = lappend(varList, var);
- attrname = "ctid";
+ attNameList = list_make2("rowid", "record");
}
else
{
@@ -1192,16 +1222,21 @@ rewriteTargetListUD(Query *parsetree, RangeTblEntry *target_rte,
parsetree->resultRelation,
0,
false);
-
- attrname = "wholerow";
+ varList = list_make1(var);
+ attNameList = list_make1("wholerow");
}
- tle = makeTargetEntry((Expr *) var,
- list_length(parsetree->targetList) + 1,
- pstrdup(attrname),
- true);
-
- parsetree->targetList = lappend(parsetree->targetList, tle);
+ /*
+ * Append them to targetList
+ */
+ forboth (cell1, varList, cell2, attNameList)
+ {
+ tle = makeTargetEntry((Expr *)lfirst(cell1),
+ list_length(parsetree->targetList) + 1,
+ pstrdup((const char *)lfirst(cell2)),
+ true);
+ parsetree->targetList = lappend(parsetree->targetList, tle);
+ }
}
diff --git a/src/include/foreign/fdwapi.h b/src/include/foreign/fdwapi.h
index 721cd25..153932a 100644
--- a/src/include/foreign/fdwapi.h
+++ b/src/include/foreign/fdwapi.h
@@ -14,6 +14,7 @@
#include "nodes/execnodes.h"
#include "nodes/relation.h"
+#include "utils/rel.h"
/* To avoid including explain.h here, reference ExplainState thus: */
struct ExplainState;
@@ -22,6 +23,11 @@ struct ExplainState;
/*
* Callback function signatures --- see fdwhandler.sgml for more info.
*/
+typedef AttrNumber (*GetForeignRelWidth_function) (PlannerInfo *root,
+ RelOptInfo *baserel,
+ Relation foreignrel,
+ bool inhparent,
+ List *targetList);
typedef void (*GetForeignRelSize_function) (PlannerInfo *root,
RelOptInfo *baserel,
@@ -58,6 +64,24 @@ typedef int (*AcquireSampleRowsFunc) (Relation relation, int elevel,
typedef bool (*AnalyzeForeignTable_function) (Relation relation,
AcquireSampleRowsFunc *func,
BlockNumber *totalpages);
+typedef List *(*PlanForeignModify_function) (PlannerInfo *root,
+ ModifyTable *plan,
+ Index resultRelation,
+ Plan *subplan);
+
+typedef void (*BeginForeignModify_function) (ModifyTableState *mtstate,
+ ResultRelInfo *resultRelInfo,
+ List *fdw_private,
+ Plan *subplan,
+ int eflags);
+typedef int (*ExecForeignInsert_function) (ResultRelInfo *resultRelInfo,
+ HeapTuple tuple);
+typedef int (*ExecForeignDelete_function) (ResultRelInfo *resultRelInfo,
+ Datum rowid);
+typedef int (*ExecForeignUpdate_function) (ResultRelInfo *resultRelInfo,
+ Datum rowid,
+ HeapTuple tuple);
+typedef void (*EndForeignModify_function) (ResultRelInfo *resultRelInfo);
/*
* FdwRoutine is the struct returned by a foreign-data wrapper's handler
@@ -90,6 +114,13 @@ typedef struct FdwRoutine
* not provided.
*/
AnalyzeForeignTable_function AnalyzeForeignTable;
+ GetForeignRelWidth_function GetForeignRelWidth;
+ PlanForeignModify_function PlanForeignModify;
+ BeginForeignModify_function BeginForeignModify;
+ ExecForeignInsert_function ExecForeignInsert;
+ ExecForeignDelete_function ExecForeignDelete;
+ ExecForeignUpdate_function ExecForeignUpdate;
+ EndForeignModify_function EndForeignModify;
} FdwRoutine;
diff --git a/src/include/foreign/foreign.h b/src/include/foreign/foreign.h
index f8aa99e..823671b 100644
--- a/src/include/foreign/foreign.h
+++ b/src/include/foreign/foreign.h
@@ -14,6 +14,7 @@
#define FOREIGN_H
#include "nodes/parsenodes.h"
+#include "nodes/relation.h"
/* Helper for obtaining username for user mapping */
@@ -81,4 +82,6 @@ extern List *GetForeignColumnOptions(Oid relid, AttrNumber attnum);
extern Oid get_foreign_data_wrapper_oid(const char *fdwname, bool missing_ok);
extern Oid get_foreign_server_oid(const char *servername, bool missing_ok);
+extern AttrNumber get_pseudo_rowid_column(RelOptInfo *baserel,
+ List *targetList);
#endif /* FOREIGN_H */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index d4911bd..4c079b7 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -268,9 +268,11 @@ typedef struct ProjectionInfo
* attribute numbers of the "original" tuple and the
* attribute numbers of the "clean" tuple.
* resultSlot: tuple slot used to hold cleaned tuple.
- * junkAttNo: not used by junkfilter code. Can be used by caller
- * to remember the attno of a specific junk attribute
+ * jf_junkRowidNo: not used by junkfilter code. Can be used by caller
+ * to remember the attno of to track a particular tuple
+ * being updated or deleted.
* (execMain.c stores the "ctid" attno here).
+ * jf_junkRecordNo: Also, the attno of whole-row reference.
* ----------------
*/
typedef struct JunkFilter
@@ -280,7 +282,8 @@ typedef struct JunkFilter
TupleDesc jf_cleanTupType;
AttrNumber *jf_cleanMap;
TupleTableSlot *jf_resultSlot;
- AttrNumber jf_junkAttNo;
+ AttrNumber jf_junkRowidNo;
+ AttrNumber jf_junkRecordNo;
} JunkFilter;
/* ----------------
@@ -303,6 +306,8 @@ typedef struct JunkFilter
* ConstraintExprs array of constraint-checking expr states
* junkFilter for removing junk attributes from tuples
* projectReturning for computing a RETURNING list
+ * fdwroutine FDW callbacks if foreign table
+ * fdw_state opaque state of FDW module, or NULL
* ----------------
*/
typedef struct ResultRelInfo
@@ -320,6 +325,8 @@ typedef struct ResultRelInfo
List **ri_ConstraintExprs;
JunkFilter *ri_junkFilter;
ProjectionInfo *ri_projectReturning;
+ struct FdwRoutine *ri_fdwroutine;
+ void *ri_fdw_state;
} ResultRelInfo;
/* ----------------
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index fb9a863..93481aa 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -175,6 +175,7 @@ typedef struct ModifyTable
List *returningLists; /* per-target-table RETURNING tlists */
List *rowMarks; /* PlanRowMarks (non-locking only) */
int epqParam; /* ID of Param for EvalPlanQual re-eval */
+ List *fdwPrivList; /* private fields for foreign tables */
} ModifyTable;
/* ----------------
@@ -478,6 +479,7 @@ typedef struct ForeignScan
List *fdw_exprs; /* expressions that FDW may evaluate */
List *fdw_private; /* private data for FDW */
bool fsSystemCol; /* true if any "system column" is needed */
+ bool fsPseudoCol; /* true if any "pseudo column" is needed */
} ForeignScan;
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index de889fb..adfc93d 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -133,7 +133,7 @@ extern Path *reparameterize_path(PlannerInfo *root, Path *path,
*/
extern void setup_simple_rel_arrays(PlannerInfo *root);
extern RelOptInfo *build_simple_rel(PlannerInfo *root, int relid,
- RelOptKind reloptkind);
+ List *tlist, RelOptKind reloptkind);
extern RelOptInfo *find_base_rel(PlannerInfo *root, int relid);
extern RelOptInfo *find_join_rel(PlannerInfo *root, Relids relids);
extern RelOptInfo *build_join_rel(PlannerInfo *root,
diff --git a/src/include/optimizer/plancat.h b/src/include/optimizer/plancat.h
index e0d04db..c5fce41 100644
--- a/src/include/optimizer/plancat.h
+++ b/src/include/optimizer/plancat.h
@@ -26,7 +26,7 @@ extern PGDLLIMPORT get_relation_info_hook_type get_relation_info_hook;
extern void get_relation_info(PlannerInfo *root, Oid relationObjectId,
- bool inhparent, RelOptInfo *rel);
+ bool inhparent, List *tlist, RelOptInfo *rel);
extern void estimate_rel_size(Relation rel, int32 *attr_widths,
BlockNumber *pages, double *tuples, double *allvisfrac);
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index 0fe696c..746a603 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -79,7 +79,8 @@ extern SetOp *make_setop(SetOpCmd cmd, SetOpStrategy strategy, Plan *lefttree,
long numGroups, double outputRows);
extern Result *make_result(PlannerInfo *root, List *tlist,
Node *resconstantqual, Plan *subplan);
-extern ModifyTable *make_modifytable(CmdType operation, bool canSetTag,
+extern ModifyTable *make_modifytable(PlannerInfo *root,
+ CmdType operation, bool canSetTag,
List *resultRelations, List *subplans, List *returningLists,
List *rowMarks, int epqParam);
extern bool is_projection_capable_plan(Plan *plan);
@@ -90,7 +91,8 @@ extern bool is_projection_capable_plan(Plan *plan);
extern int from_collapse_limit;
extern int join_collapse_limit;
-extern void add_base_rels_to_query(PlannerInfo *root, Node *jtnode);
+extern void add_base_rels_to_query(PlannerInfo *root, List *tlist,
+ Node *jtnode);
extern void build_base_rel_tlists(PlannerInfo *root, List *final_tlist);
extern void add_vars_to_targetlist(PlannerInfo *root, List *vars,
Relids where_needed, bool create_new_ph);