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