pgsql-v9.3-writable-fdw-poc.v4.patch
application/octet-stream
Filename: pgsql-v9.3-writable-fdw-poc.v4.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/file_fdw/file_fdw.c | 172 | 5 |
| src/backend/commands/trigger.c | 8 | 4 |
| src/backend/executor/execMain.c | 30 | 4 |
| src/backend/executor/nodeForeignscan.c | 135 | 1 |
| src/backend/executor/nodeModifyTable.c | 119 | 23 |
| src/backend/nodes/copyfuncs.c | 1 | 0 |
| src/backend/nodes/outfuncs.c | 1 | 0 |
| src/backend/optimizer/plan/createplan.c | 19 | 0 |
| src/backend/optimizer/plan/initsplan.c | 5 | 5 |
| src/backend/optimizer/plan/planmain.c | 1 | 1 |
| src/backend/optimizer/plan/planner.c | 1 | 1 |
| src/backend/optimizer/prep/prepunion.c | 2 | 1 |
| src/backend/optimizer/util/plancat.c | 22 | 1 |
| src/backend/optimizer/util/relnode.c | 4 | 3 |
| src/backend/rewrite/rewriteHandler.c | 45 | 10 |
| src/include/commands/trigger.h | 4 | 4 |
| src/include/foreign/fdwapi.h | 25 | 0 |
| src/include/nodes/execnodes.h | 10 | 3 |
| src/include/nodes/plannodes.h | 1 | 0 |
| src/include/optimizer/pathnode.h | 1 | 1 |
| src/include/optimizer/plancat.h | 1 | 1 |
| src/include/optimizer/planmain.h | 2 | 1 |
contrib/file_fdw/file_fdw.c | 177 +++++++++++++++++++++++++++++++-
src/backend/commands/trigger.c | 12 ++-
src/backend/executor/execMain.c | 34 +++++-
src/backend/executor/nodeForeignscan.c | 136 +++++++++++++++++++++++-
src/backend/executor/nodeModifyTable.c | 142 ++++++++++++++++++++-----
src/backend/nodes/copyfuncs.c | 1 +
src/backend/nodes/outfuncs.c | 1 +
src/backend/optimizer/plan/createplan.c | 19 ++++
src/backend/optimizer/plan/initsplan.c | 10 +-
src/backend/optimizer/plan/planmain.c | 2 +-
src/backend/optimizer/plan/planner.c | 2 +-
src/backend/optimizer/prep/prepunion.c | 3 +-
src/backend/optimizer/util/plancat.c | 23 ++++-
src/backend/optimizer/util/relnode.c | 7 +-
src/backend/rewrite/rewriteHandler.c | 55 ++++++++--
src/include/commands/trigger.h | 8 +-
src/include/foreign/fdwapi.h | 25 +++++
src/include/nodes/execnodes.h | 13 ++-
src/include/nodes/plannodes.h | 1 +
src/include/optimizer/pathnode.h | 2 +-
src/include/optimizer/plancat.h | 2 +-
src/include/optimizer/planmain.h | 3 +-
22 files changed, 609 insertions(+), 69 deletions(-)
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 7ab3ed6..14991a3 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -34,6 +34,7 @@
#include "optimizer/var.h"
#include "utils/memutils.h"
#include "utils/rel.h"
+#include "utils/lsyscache.h"
PG_MODULE_MAGIC;
@@ -87,6 +88,7 @@ typedef struct FileFdwPlanState
List *options; /* merged COPY options, excluding filename */
BlockNumber pages; /* estimate of file's physical size */
double ntuples; /* estimate of number of rows in file */
+ DefElem *anum_rowid; /* attribute number of rowid */
} FileFdwPlanState;
/*
@@ -97,6 +99,8 @@ typedef struct FileFdwExecutionState
char *filename; /* file to read */
List *options; /* merged COPY options, excluding filename */
CopyState cstate; /* state of reading file */
+ int lineno; /* pseudo ctid of the file */
+ AttrNumber anum_rowid; /* attribute number of rowid */
} FileFdwExecutionState;
/*
@@ -111,6 +115,10 @@ PG_FUNCTION_INFO_V1(file_fdw_validator);
/*
* FDW callback routines
*/
+static void fileGetForeignRelInfo(PlannerInfo *root,
+ RelOptInfo *baserel,
+ Oid foreigntableid,
+ bool inhparent, List *targetList);
static void fileGetForeignRelSize(PlannerInfo *root,
RelOptInfo *baserel,
Oid foreigntableid);
@@ -131,6 +139,19 @@ static void fileEndForeignScan(ForeignScanState *node);
static bool fileAnalyzeForeignTable(Relation relation,
AcquireSampleRowsFunc *func,
BlockNumber *totalpages);
+static void fileBeginForeignModify(CmdType operation,
+ ResultRelInfo *resultRelInfo,
+ EState *estate,
+ Plan *subplan,
+ int eflags);
+static int fileExecForeignInsert(ResultRelInfo *resultRelInfo,
+ HeapTuple tuple);
+static int fileExecForeignDelete(ResultRelInfo *resultRelInfo,
+ Datum rowid);
+static int fileExecForeignUpdate(ResultRelInfo *resultRelInfo,
+ Datum rowid,
+ HeapTuple tuple);
+static void fileEndForeignModify(ResultRelInfo *resultRelInfo);
/*
* Helper functions
@@ -169,7 +190,13 @@ file_fdw_handler(PG_FUNCTION_ARGS)
fdwroutine->IterateForeignScan = fileIterateForeignScan;
fdwroutine->ReScanForeignScan = fileReScanForeignScan;
fdwroutine->EndForeignScan = fileEndForeignScan;
+ fdwroutine->GetForeignRelInfo = fileGetForeignRelInfo;
fdwroutine->AnalyzeForeignTable = fileAnalyzeForeignTable;
+ fdwroutine->BeginForeignModify = fileBeginForeignModify;
+ fdwroutine->ExecForeignInsert = fileExecForeignInsert;
+ fdwroutine->ExecForeignDelete = fileExecForeignDelete;
+ fdwroutine->ExecForeignUpdate = fileExecForeignUpdate;
+ fdwroutine->EndForeignModify = fileEndForeignModify;
PG_RETURN_POINTER(fdwroutine);
}
@@ -424,6 +451,47 @@ get_file_fdw_attribute_options(Oid relid)
}
/*
+ * fileGetForeignRelInfo
+ * Adjust size of baserel->attr_needed according to var references
+ * within targetList.
+ */
+static void
+fileGetForeignRelInfo(PlannerInfo *root,
+ RelOptInfo *baserel,
+ Oid foreigntableid,
+ bool inhparent, List *targetList)
+{
+ FileFdwPlanState *fdw_private;
+ ListCell *cell;
+
+ fdw_private = (FileFdwPlanState *) palloc0(sizeof(FileFdwPlanState));
+
+ foreach(cell, targetList)
+ {
+ TargetEntry *tle = lfirst(cell);
+
+ if (tle->resjunk &&
+ tle->resname && strcmp(tle->resname, "rowid")==0)
+ {
+ Bitmapset *temp = NULL;
+ AttrNumber anum_rowid;
+ DefElem *defel;
+
+ pull_varattnos((Node *)tle, baserel->relid, &temp);
+ anum_rowid = bms_singleton_member(temp)
+ + FirstLowInvalidHeapAttributeNumber;
+ /* adjust attr_needed of baserel */
+ if (anum_rowid > baserel->max_attr)
+ baserel->max_attr = anum_rowid;
+ defel = makeDefElem("anum_rowid",
+ (Node *)makeInteger(anum_rowid));
+ fdw_private->anum_rowid = defel;
+ }
+ }
+ baserel->fdw_private = fdw_private;
+}
+
+/*
* fileGetForeignRelSize
* Obtain relation size estimates for a foreign table
*/
@@ -432,16 +500,14 @@ fileGetForeignRelSize(PlannerInfo *root,
RelOptInfo *baserel,
Oid foreigntableid)
{
- FileFdwPlanState *fdw_private;
+ FileFdwPlanState *fdw_private = (FileFdwPlanState *) baserel->fdw_private;
/*
* Fetch options. We only need filename at this point, but we might as
* well get everything and not need to re-fetch it later in planning.
*/
- fdw_private = (FileFdwPlanState *) palloc(sizeof(FileFdwPlanState));
fileGetOptions(foreigntableid,
&fdw_private->filename, &fdw_private->options);
- baserel->fdw_private = (void *) fdw_private;
/* Estimate relation size */
estimate_size(root, baserel, fdw_private);
@@ -473,6 +539,10 @@ fileGetForeignPaths(PlannerInfo *root,
coptions = list_make1(makeDefElem("convert_selectively",
(Node *) columns));
+ /* save attribute number of rowid pseudo column */
+ if (fdw_private->anum_rowid)
+ coptions = lappend(coptions, fdw_private->anum_rowid);
+
/* Estimate costs */
estimate_costs(root, baserel, fdw_private,
&startup_cost, &total_cost);
@@ -513,6 +583,35 @@ fileGetForeignPlan(PlannerInfo *root,
Index scan_relid = baserel->relid;
/*
+ * XXX - An evidence FDW module can know what kind of accesses are
+ * requires on the target relation, if UPDATE, INSERT or DELETE.
+ * It shall be also utilized to appropriate lock level on FDW
+ * extensions that performs behalf on real RDBMS.
+ */
+ if (root->parse->resultRelation == baserel->relid)
+ {
+ switch (root->parse->commandType)
+ {
+ case CMD_INSERT:
+ elog(INFO, "%s is the target relation of INSERT",
+ get_rel_name(foreigntableid));
+ break;
+ case CMD_UPDATE:
+ elog(INFO, "%s is the target relation of UPDATE",
+ get_rel_name(foreigntableid));
+ break;
+ case CMD_DELETE:
+ elog(INFO, "%s is the target relation of DELETE",
+ get_rel_name(foreigntableid));
+ break;
+ default:
+ elog(INFO, "%s is the target relation of ??????",
+ get_rel_name(foreigntableid));
+ break;
+ }
+ }
+
+ /*
* We have no native ability to evaluate restriction clauses, so we just
* put all the scan_clauses into the plan node's qual list for the
* executor to check. So all we have to do here is strip RestrictInfo
@@ -566,7 +665,10 @@ fileBeginForeignScan(ForeignScanState *node, int eflags)
ForeignScan *plan = (ForeignScan *) node->ss.ps.plan;
char *filename;
List *options;
+ ListCell *cell;
+ ListCell *prev;
CopyState cstate;
+ AttrNumber anum_rowid = InvalidAttrNumber;
FileFdwExecutionState *festate;
/*
@@ -582,6 +684,21 @@ fileBeginForeignScan(ForeignScanState *node, int eflags)
/* Add any options from the plan (currently only convert_selectively) */
options = list_concat(options, plan->fdw_private);
+ /* Fetch anum_rowid, if exist in options */
+ prev = NULL;
+ foreach (cell, options)
+ {
+ DefElem *defel = lfirst(cell);
+
+ if (strcmp(defel->defname, "anum_rowid") == 0)
+ {
+ anum_rowid = intVal(defel->arg);
+ options = list_delete_cell(options, cell, prev);
+ break;
+ }
+ prev = cell;
+ }
+
/*
* Create CopyState from FDW options. We always acquire all columns, so
* as to match the expected ScanTupleSlot signature.
@@ -599,6 +716,8 @@ fileBeginForeignScan(ForeignScanState *node, int eflags)
festate->filename = filename;
festate->options = options;
festate->cstate = cstate;
+ festate->anum_rowid = anum_rowid;
+ festate->lineno = 1;
node->fdw_state = (void *) festate;
}
@@ -639,7 +758,16 @@ fileIterateForeignScan(ForeignScanState *node)
slot->tts_values, slot->tts_isnull,
NULL);
if (found)
+ {
+ if (festate->anum_rowid != InvalidAttrNumber)
+ {
+ slot->tts_values[festate->anum_rowid - 1]
+ = Int32GetDatum(festate->lineno);
+ slot->tts_isnull[festate->anum_rowid - 1] = false;
+ }
ExecStoreVirtualTuple(slot);
+ festate->lineno++;
+ }
/* Remove error callback. */
error_context_stack = errcallback.previous;
@@ -662,6 +790,7 @@ fileReScanForeignScan(ForeignScanState *node)
festate->filename,
NIL,
festate->options);
+ festate->lineno = 1;
}
/*
@@ -717,6 +846,44 @@ fileAnalyzeForeignTable(Relation relation,
return true;
}
+static void
+fileBeginForeignModify(CmdType operation,
+ ResultRelInfo *resultRelInfo,
+ EState *estate,
+ Plan *subplan,
+ int eflags)
+{
+ elog(INFO, "fdw_file: BeginForeignModify method");
+}
+
+static int
+fileExecForeignInsert(ResultRelInfo *resultRelInfo, HeapTuple tuple)
+{
+ elog(INFO, "fdw_file: INSERT");
+ return 1;
+}
+
+static int
+fileExecForeignDelete(ResultRelInfo *resultRelInfo, Datum rowid)
+{
+ elog(INFO, "fdw_file: DELETE (lineno = %u)", DatumGetInt32(rowid));
+ return 1;
+}
+
+static int
+fileExecForeignUpdate(ResultRelInfo *resultRelInfo,
+ Datum rowid, HeapTuple tuple)
+{
+ elog(INFO, "fdw_file: UPDATE (lineno = %u)", DatumGetInt32(rowid));
+ return 1;
+}
+
+static void
+fileEndForeignModify(ResultRelInfo *resultRelInfo)
+{
+ elog(INFO, "fdw_file: EndForeignModify method");
+}
+
/*
* check_selective_binary_conversion
*
@@ -789,8 +956,8 @@ check_selective_binary_conversion(RelOptInfo *baserel,
break;
}
- /* Ignore system attributes. */
- if (attnum < 0)
+ /* Ignore system or pseudo attributes. */
+ if (attnum < 0 || attnum > RelationGetNumberOfAttributes(rel))
continue;
/* Get user attributes. */
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 98b8207..c1d178c 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2132,9 +2132,10 @@ ExecASDeleteTriggers(EState *estate, ResultRelInfo *relinfo)
bool
ExecBRDeleteTriggers(EState *estate, EPQState *epqstate,
ResultRelInfo *relinfo,
- ItemPointer tupleid)
+ Datum rowid)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
+ ItemPointer tupleid = (ItemPointer)DatumGetPointer(rowid);
bool result = true;
TriggerData LocTriggerData;
HeapTuple trigtuple;
@@ -2190,9 +2191,10 @@ ExecBRDeleteTriggers(EState *estate, EPQState *epqstate,
void
ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
- ItemPointer tupleid)
+ Datum rowid)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
+ ItemPointer tupleid = (ItemPointer)DatumGetPointer(rowid);
if (trigdesc && trigdesc->trig_delete_after_row)
{
@@ -2317,11 +2319,12 @@ ExecASUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
TupleTableSlot *
ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
ResultRelInfo *relinfo,
- ItemPointer tupleid, TupleTableSlot *slot)
+ Datum rowid, TupleTableSlot *slot)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
HeapTuple slottuple = ExecMaterializeSlot(slot);
HeapTuple newtuple = slottuple;
+ ItemPointer tupleid = (ItemPointer)DatumGetPointer(rowid);
TriggerData LocTriggerData;
HeapTuple trigtuple;
HeapTuple oldtuple;
@@ -2414,10 +2417,11 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
void
ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
- ItemPointer tupleid, HeapTuple newtuple,
+ Datum rowid, HeapTuple newtuple,
List *recheckIndexes)
{
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
+ ItemPointer tupleid = (ItemPointer)DatumGetPointer(rowid);
if (trigdesc && trigdesc->trig_update_after_row)
{
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index dbd3755..ccc1f3a 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -44,6 +44,7 @@
#include "catalog/namespace.h"
#include "commands/trigger.h"
#include "executor/execdebug.h"
+#include "foreign/fdwapi.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "optimizer/clauses.h"
@@ -934,6 +935,7 @@ void
CheckValidResultRel(Relation resultRel, CmdType operation)
{
TriggerDesc *trigDesc = resultRel->trigdesc;
+ FdwRoutine *fdwroutine;
switch (resultRel->rd_rel->relkind)
{
@@ -985,10 +987,34 @@ CheckValidResultRel(Relation resultRel, CmdType operation)
}
break;
case RELKIND_FOREIGN_TABLE:
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("cannot change foreign table \"%s\"",
- RelationGetRelationName(resultRel))));
+ fdwroutine = GetFdwRoutineByRelId(RelationGetRelid(resultRel));
+ switch (operation)
+ {
+ case CMD_INSERT:
+ if (!fdwroutine || !fdwroutine->ExecForeignInsert)
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("cannot insert into foreign table \"%s\"",
+ RelationGetRelationName(resultRel))));
+ break;
+ case CMD_UPDATE:
+ if (!fdwroutine || !fdwroutine->ExecForeignUpdate)
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("cannot update foreign table \"%s\"",
+ RelationGetRelationName(resultRel))));
+ break;
+ case CMD_DELETE:
+ if (!fdwroutine || !fdwroutine->ExecForeignDelete)
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("cannot delete from foreign table \"%s\"",
+ RelationGetRelationName(resultRel))));
+ break;
+ default:
+ elog(ERROR, "unrecognized CmdType: %d", (int) operation);
+ break;
+ }
break;
default:
ereport(ERROR,
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index 9204859..1ee626f 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -25,6 +25,7 @@
#include "executor/executor.h"
#include "executor/nodeForeignscan.h"
#include "foreign/fdwapi.h"
+#include "nodes/nodeFuncs.h"
#include "utils/rel.h"
static TupleTableSlot *ForeignNext(ForeignScanState *node);
@@ -93,6 +94,133 @@ ExecForeignScan(ForeignScanState *node)
(ExecScanRecheckMtd) ForeignRecheck);
}
+/*
+ * pseudo_column_walker
+ *
+ * helper routine of GetPseudoTupleDesc. It pulls Var nodes that reference
+ * pseudo columns from targetlis of the relation
+ */
+typedef struct
+{
+ Relation relation;
+ Index varno;
+ List *pcolumns;
+ AttrNumber max_attno;
+} pseudo_column_walker_context;
+
+static bool
+pseudo_column_walker(Node *node, pseudo_column_walker_context *context)
+{
+ if (node == NULL)
+ return false;
+ if (IsA(node, Var))
+ {
+ Var *var = (Var *) node;
+ ListCell *cell;
+
+ if (var->varno == context->varno && var->varlevelsup == 0 &&
+ var->varattno > RelationGetNumberOfAttributes(context->relation))
+ {
+ foreach (cell, context->pcolumns)
+ {
+ Var *temp = lfirst(cell);
+
+ if (temp->varattno == var->varattno)
+ {
+ if (!equal(var, temp))
+ elog(ERROR, "asymmetric pseudo column appeared");
+ break;
+ }
+ }
+ if (!cell)
+ {
+ context->pcolumns = lappend(context->pcolumns, var);
+ if (var->varattno > context->max_attno)
+ context->max_attno = var->varattno;
+ }
+ }
+ return false;
+ }
+
+ /* Should not find an unplanned subquery */
+ Assert(!IsA(node, Query));
+
+ return expression_tree_walker(node, pseudo_column_walker,
+ (void *)context);
+}
+
+/*
+ * GetPseudoTupleDesc
+ *
+ * It generates TupleDesc structure including pseudo-columns if required.
+ */
+static TupleDesc
+GetPseudoTupleDesc(ForeignScan *node, Relation relation)
+{
+ pseudo_column_walker_context context;
+ List *target_list = node->scan.plan.targetlist;
+ TupleDesc tupdesc;
+ AttrNumber attno;
+ ListCell *cell;
+ ListCell *prev;
+ bool hasoid;
+
+ context.relation = relation;
+ context.varno = node->scan.scanrelid;
+ context.pcolumns = NIL;
+ context.max_attno = -1;
+
+ pseudo_column_walker((Node *)target_list, (void *)&context);
+ Assert(context.max_attno > RelationGetNumberOfAttributes(relation));
+
+ hasoid = RelationGetForm(relation)->relhasoids;
+ tupdesc = CreateTemplateTupleDesc(context.max_attno, hasoid);
+
+ for (attno = 1; attno <= context.max_attno; attno++)
+ {
+ /* case of regular columns */
+ if (attno <= RelationGetNumberOfAttributes(relation))
+ {
+ memcpy(tupdesc->attrs[attno - 1],
+ RelationGetDescr(relation)->attrs[attno - 1],
+ ATTRIBUTE_FIXED_PART_SIZE);
+ continue;
+ }
+
+ /* case of pseudo columns */
+ prev = NULL;
+ foreach (cell, context.pcolumns)
+ {
+ Var *var = lfirst(cell);
+
+ if (var->varattno == attno)
+ {
+ char namebuf[NAMEDATALEN];
+
+ snprintf(namebuf, sizeof(namebuf),
+ "pseudo_column_%d", attno);
+
+ TupleDescInitEntry(tupdesc,
+ attno,
+ namebuf,
+ var->vartype,
+ var->vartypmod,
+ 0);
+ TupleDescInitEntryCollation(tupdesc,
+ attno,
+ var->varcollid);
+ context.pcolumns
+ = list_delete_cell(context.pcolumns, cell, prev);
+ break;
+ }
+ prev = cell;
+ }
+ if (!cell)
+ elog(ERROR, "pseudo column %d of %s not in target list",
+ attno, RelationGetRelationName(relation));
+ }
+ return tupdesc;
+}
/* ----------------------------------------------------------------
* ExecInitForeignScan
@@ -103,6 +231,7 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
{
ForeignScanState *scanstate;
Relation currentRelation;
+ TupleDesc tupdesc;
FdwRoutine *fdwroutine;
/* check for unsupported flags */
@@ -149,7 +278,12 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
/*
* get the scan type from the relation descriptor.
*/
- ExecAssignScanType(&scanstate->ss, RelationGetDescr(currentRelation));
+ if (node->fsPseudoCol)
+ tupdesc = GetPseudoTupleDesc(node, currentRelation);
+ else
+ tupdesc = RelationGetDescr(currentRelation);
+
+ ExecAssignScanType(&scanstate->ss, tupdesc);
/*
* Initialize result tuple type and projection info.
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index d31015c..bcb527c 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -42,6 +42,7 @@
#include "commands/trigger.h"
#include "executor/executor.h"
#include "executor/nodeModifyTable.h"
+#include "foreign/fdwapi.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "storage/bufmgr.h"
@@ -170,6 +171,7 @@ ExecInsert(TupleTableSlot *slot,
Relation resultRelationDesc;
Oid newId;
List *recheckIndexes = NIL;
+ int num_rows = 1;
/*
* get the heap tuple out of the tuple table slot, making sure we have a
@@ -225,6 +227,14 @@ ExecInsert(TupleTableSlot *slot,
newId = InvalidOid;
}
+ else if (resultRelInfo->ri_fdwroutine)
+ {
+ FdwRoutine *fdwroutine = resultRelInfo->ri_fdwroutine;
+
+ num_rows = fdwroutine->ExecForeignInsert(resultRelInfo, tuple);
+
+ newId = InvalidOid;
+ }
else
{
/*
@@ -252,7 +262,7 @@ ExecInsert(TupleTableSlot *slot,
if (canSetTag)
{
- (estate->es_processed)++;
+ (estate->es_processed) += num_rows;
estate->es_lastoid = newId;
setLastTid(&(tuple->t_self));
}
@@ -285,7 +295,7 @@ ExecInsert(TupleTableSlot *slot,
* ----------------------------------------------------------------
*/
static TupleTableSlot *
-ExecDelete(ItemPointer tupleid,
+ExecDelete(Datum rowid,
HeapTupleHeader oldtuple,
TupleTableSlot *planSlot,
EPQState *epqstate,
@@ -296,6 +306,7 @@ ExecDelete(ItemPointer tupleid,
Relation resultRelationDesc;
HTSU_Result result;
HeapUpdateFailureData hufd;
+ int num_rows = 1;
/*
* get information on the (current) result relation
@@ -310,7 +321,7 @@ ExecDelete(ItemPointer tupleid,
bool dodelete;
dodelete = ExecBRDeleteTriggers(estate, epqstate, resultRelInfo,
- tupleid);
+ rowid);
if (!dodelete) /* "do nothing" */
return NULL;
@@ -334,8 +345,16 @@ ExecDelete(ItemPointer tupleid,
if (!dodelete) /* "do nothing" */
return NULL;
}
+ else if (resultRelInfo->ri_fdwroutine)
+ {
+ FdwRoutine *fdwroutine = resultRelInfo->ri_fdwroutine;
+
+ num_rows = fdwroutine->ExecForeignDelete(resultRelInfo, rowid);
+ }
else
{
+ ItemPointer tupleid = (ItemPointer) DatumGetPointer(rowid);
+
/*
* delete the tuple
*
@@ -430,10 +449,10 @@ ldelete:;
}
if (canSetTag)
- (estate->es_processed)++;
+ (estate->es_processed) += num_rows;
/* AFTER ROW DELETE Triggers */
- ExecARDeleteTriggers(estate, resultRelInfo, tupleid);
+ ExecARDeleteTriggers(estate, resultRelInfo, rowid);
/* Process RETURNING if present */
if (resultRelInfo->ri_projectReturning)
@@ -457,7 +476,8 @@ ldelete:;
}
else
{
- deltuple.t_self = *tupleid;
+ ItemPointerCopy((ItemPointer)DatumGetPointer(rowid),
+ &deltuple.t_self);
if (!heap_fetch(resultRelationDesc, SnapshotAny,
&deltuple, &delbuffer, false, NULL))
elog(ERROR, "failed to fetch deleted tuple for DELETE RETURNING");
@@ -499,7 +519,7 @@ ldelete:;
* ----------------------------------------------------------------
*/
static TupleTableSlot *
-ExecUpdate(ItemPointer tupleid,
+ExecUpdate(Datum rowid,
HeapTupleHeader oldtuple,
TupleTableSlot *slot,
TupleTableSlot *planSlot,
@@ -513,6 +533,7 @@ ExecUpdate(ItemPointer tupleid,
HTSU_Result result;
HeapUpdateFailureData hufd;
List *recheckIndexes = NIL;
+ int num_rows = 1;
/*
* abort the operation if not running transactions
@@ -537,7 +558,7 @@ ExecUpdate(ItemPointer tupleid,
resultRelInfo->ri_TrigDesc->trig_update_before_row)
{
slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
- tupleid, slot);
+ rowid, slot);
if (slot == NULL) /* "do nothing" */
return NULL;
@@ -567,8 +588,16 @@ ExecUpdate(ItemPointer tupleid,
/* trigger might have changed tuple */
tuple = ExecMaterializeSlot(slot);
}
+ else if (resultRelInfo->ri_fdwroutine)
+ {
+ FdwRoutine *fdwroutine = resultRelInfo->ri_fdwroutine;
+
+ num_rows = fdwroutine->ExecForeignUpdate(resultRelInfo, rowid, tuple);
+ }
else
{
+ ItemPointer tupleid = (ItemPointer) DatumGetPointer(rowid);
+
/*
* Check the constraints of the tuple
*
@@ -687,10 +716,10 @@ lreplace:;
}
if (canSetTag)
- (estate->es_processed)++;
+ (estate->es_processed) += num_rows;
/* AFTER ROW UPDATE Triggers */
- ExecARUpdateTriggers(estate, resultRelInfo, tupleid, tuple,
+ ExecARUpdateTriggers(estate, resultRelInfo, rowid, tuple,
recheckIndexes);
list_free(recheckIndexes);
@@ -771,6 +800,7 @@ ExecModifyTable(ModifyTableState *node)
TupleTableSlot *planSlot;
ItemPointer tupleid = NULL;
ItemPointerData tuple_ctid;
+ Datum rowid = 0;
HeapTupleHeader oldtuple = NULL;
/*
@@ -859,17 +889,19 @@ ExecModifyTable(ModifyTableState *node)
if (junkfilter != NULL)
{
/*
- * extract the 'ctid' or 'wholerow' junk attribute.
+ * extract the 'ctid', 'rowid' or 'wholerow' junk attribute.
*/
if (operation == CMD_UPDATE || operation == CMD_DELETE)
{
+ char relkind;
Datum datum;
bool isNull;
- if (resultRelInfo->ri_RelationDesc->rd_rel->relkind == RELKIND_RELATION)
+ relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
+ if (relkind == RELKIND_RELATION)
{
datum = ExecGetJunkAttribute(slot,
- junkfilter->jf_junkAttNo,
+ junkfilter->jf_junkRowidNo,
&isNull);
/* shouldn't ever get a null result... */
if (isNull)
@@ -877,13 +909,33 @@ ExecModifyTable(ModifyTableState *node)
tupleid = (ItemPointer) DatumGetPointer(datum);
tuple_ctid = *tupleid; /* be sure we don't free
- * ctid!! */
- tupleid = &tuple_ctid;
+ * ctid ! */
+ rowid = PointerGetDatum(&tuple_ctid);
+ }
+ else if (relkind == RELKIND_FOREIGN_TABLE)
+ {
+ datum = ExecGetJunkAttribute(slot,
+ junkfilter->jf_junkRowidNo,
+ &isNull);
+ /* shouldn't ever get a null result... */
+ if (isNull)
+ elog(ERROR, "rowid is NULL");
+
+ rowid = datum;
+
+ datum = ExecGetJunkAttribute(slot,
+ junkfilter->jf_junkRecordNo,
+ &isNull);
+ /* shouldn't ever get a null result... */
+ if (isNull)
+ elog(ERROR, "wholerow is NULL");
+
+ oldtuple = DatumGetHeapTupleHeader(datum);
}
else
{
datum = ExecGetJunkAttribute(slot,
- junkfilter->jf_junkAttNo,
+ junkfilter->jf_junkRecordNo,
&isNull);
/* shouldn't ever get a null result... */
if (isNull)
@@ -906,11 +958,11 @@ ExecModifyTable(ModifyTableState *node)
slot = ExecInsert(slot, planSlot, estate, node->canSetTag);
break;
case CMD_UPDATE:
- slot = ExecUpdate(tupleid, oldtuple, slot, planSlot,
+ slot = ExecUpdate(rowid, oldtuple, slot, planSlot,
&node->mt_epqstate, estate, node->canSetTag);
break;
case CMD_DELETE:
- slot = ExecDelete(tupleid, oldtuple, planSlot,
+ slot = ExecDelete(rowid, oldtuple, planSlot,
&node->mt_epqstate, estate, node->canSetTag);
break;
default:
@@ -1022,6 +1074,22 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
estate->es_result_relation_info = resultRelInfo;
mtstate->mt_plans[i] = ExecInitNode(subplan, estate, eflags);
+ /*
+ * Also tells FDW extensions to init the plan for this result rel
+ */
+ if (RelationGetForm(resultRelInfo->ri_RelationDesc)->relkind == RELKIND_FOREIGN_TABLE)
+ {
+ Oid relid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
+ FdwRoutine *fdwroutine = GetFdwRoutineByRelId(relid);
+
+ Assert(fdwroutine != NULL);
+ resultRelInfo->ri_fdwroutine = fdwroutine;
+ resultRelInfo->ri_fdw_state = NULL;
+
+ if (fdwroutine->BeginForeignModify)
+ fdwroutine->BeginForeignModify(operation, resultRelInfo,
+ estate, subplan, eflags);
+ }
resultRelInfo++;
i++;
}
@@ -1163,6 +1231,8 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
for (i = 0; i < nplans; i++)
{
JunkFilter *j;
+ char relkind =
+ RelationGetForm(resultRelInfo->ri_RelationDesc)->relkind;
subplan = mtstate->mt_plans[i]->plan;
if (operation == CMD_INSERT || operation == CMD_UPDATE)
@@ -1176,16 +1246,27 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
if (operation == CMD_UPDATE || operation == CMD_DELETE)
{
/* For UPDATE/DELETE, find the appropriate junk attr now */
- if (resultRelInfo->ri_RelationDesc->rd_rel->relkind == RELKIND_RELATION)
+ if (relkind == RELKIND_RELATION)
{
- j->jf_junkAttNo = ExecFindJunkAttribute(j, "ctid");
- if (!AttributeNumberIsValid(j->jf_junkAttNo))
+ j->jf_junkRowidNo = ExecFindJunkAttribute(j, "ctid");
+ if (!AttributeNumberIsValid(j->jf_junkRowidNo))
elog(ERROR, "could not find junk ctid column");
}
+ else if (relkind == RELKIND_FOREIGN_TABLE)
+ {
+ j->jf_junkRowidNo = ExecFindJunkAttribute(j, "rowid");
+ if (!AttributeNumberIsValid(j->jf_junkRowidNo))
+ elog(ERROR, "could not find junk rowid column");
+ j->jf_junkRecordNo
+ = ExecFindJunkAttribute(j, "record");
+ if (!AttributeNumberIsValid(j->jf_junkRecordNo))
+ elog(ERROR, "could not find junk wholerow column");
+ }
else
{
- j->jf_junkAttNo = ExecFindJunkAttribute(j, "wholerow");
- if (!AttributeNumberIsValid(j->jf_junkAttNo))
+ j->jf_junkRecordNo
+ = ExecFindJunkAttribute(j, "wholerow");
+ if (!AttributeNumberIsValid(j->jf_junkRecordNo))
elog(ERROR, "could not find junk wholerow column");
}
}
@@ -1239,6 +1320,21 @@ ExecEndModifyTable(ModifyTableState *node)
{
int i;
+ /* Let the FDW shut dowm */
+ for (i=0; i < node->ps.state->es_num_result_relations; i++)
+ {
+ ResultRelInfo *resultRelInfo
+ = &node->ps.state->es_result_relations[i];
+
+ if (resultRelInfo->ri_fdwroutine)
+ {
+ FdwRoutine *fdwroutine = resultRelInfo->ri_fdwroutine;
+
+ if (fdwroutine->EndForeignModify)
+ fdwroutine->EndForeignModify(resultRelInfo);
+ }
+ }
+
/*
* Free the exprcontext
*/
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 9387ee9..2dd6261 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -594,6 +594,7 @@ _copyForeignScan(const ForeignScan *from)
COPY_NODE_FIELD(fdw_exprs);
COPY_NODE_FIELD(fdw_private);
COPY_SCALAR_FIELD(fsSystemCol);
+ COPY_SCALAR_FIELD(fsPseudoCol);
return newnode;
}
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 35c6287..39130ec 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -562,6 +562,7 @@ _outForeignScan(StringInfo str, const ForeignScan *node)
WRITE_NODE_FIELD(fdw_exprs);
WRITE_NODE_FIELD(fdw_private);
WRITE_BOOL_FIELD(fsSystemCol);
+ WRITE_BOOL_FIELD(fsPseudoCol);
}
static void
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 030f420..ff3b9ff 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -39,6 +39,7 @@
#include "parser/parse_clause.h"
#include "parser/parsetree.h"
#include "utils/lsyscache.h"
+#include "utils/rel.h"
static Plan *create_plan_recurse(PlannerInfo *root, Path *best_path);
@@ -1943,6 +1944,8 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
RelOptInfo *rel = best_path->path.parent;
Index scan_relid = rel->relid;
RangeTblEntry *rte;
+ Relation relation;
+ AttrNumber num_attrs;
int i;
/* it should be a base rel... */
@@ -2001,6 +2004,22 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
}
}
+ /*
+ * Also, detect whether any pseudo columns are requested from rel.
+ */
+ relation = heap_open(rte->relid, NoLock);
+ scan_plan->fsPseudoCol = false;
+ num_attrs = RelationGetNumberOfAttributes(relation);
+ for (i = num_attrs + 1; i <= rel->max_attr; i++)
+ {
+ if (!bms_is_empty(rel->attr_needed[i - rel->min_attr]))
+ {
+ scan_plan->fsPseudoCol = true;
+ break;
+ }
+ }
+ heap_close(relation, NoLock);
+
return scan_plan;
}
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index bd719b5..57ba192 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -85,7 +85,7 @@ static void check_hashjoinable(RestrictInfo *restrictinfo);
* "other rel" RelOptInfos for the members of any appendrels we find here.)
*/
void
-add_base_rels_to_query(PlannerInfo *root, Node *jtnode)
+add_base_rels_to_query(PlannerInfo *root, List *tlist, Node *jtnode)
{
if (jtnode == NULL)
return;
@@ -93,7 +93,7 @@ add_base_rels_to_query(PlannerInfo *root, Node *jtnode)
{
int varno = ((RangeTblRef *) jtnode)->rtindex;
- (void) build_simple_rel(root, varno, RELOPT_BASEREL);
+ (void) build_simple_rel(root, varno, tlist, RELOPT_BASEREL);
}
else if (IsA(jtnode, FromExpr))
{
@@ -101,14 +101,14 @@ add_base_rels_to_query(PlannerInfo *root, Node *jtnode)
ListCell *l;
foreach(l, f->fromlist)
- add_base_rels_to_query(root, lfirst(l));
+ add_base_rels_to_query(root, tlist, lfirst(l));
}
else if (IsA(jtnode, JoinExpr))
{
JoinExpr *j = (JoinExpr *) jtnode;
- add_base_rels_to_query(root, j->larg);
- add_base_rels_to_query(root, j->rarg);
+ add_base_rels_to_query(root, tlist, j->larg);
+ add_base_rels_to_query(root, tlist, j->rarg);
}
else
elog(ERROR, "unrecognized node type: %d",
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c
index c2488a4..2d292dd 100644
--- a/src/backend/optimizer/plan/planmain.c
+++ b/src/backend/optimizer/plan/planmain.c
@@ -163,7 +163,7 @@ query_planner(PlannerInfo *root, List *tlist,
* rangetable may contain RTEs for rels not actively part of the query,
* for example views. We don't want to make RelOptInfos for them.
*/
- add_base_rels_to_query(root, (Node *) parse->jointree);
+ add_base_rels_to_query(root, tlist, (Node *) parse->jointree);
/*
* Examine the targetlist and join tree, adding entries to baserel
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index b61005f..19e5503 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -3385,7 +3385,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid)
setup_simple_rel_arrays(root);
/* Build RelOptInfo */
- rel = build_simple_rel(root, 1, RELOPT_BASEREL);
+ rel = build_simple_rel(root, 1, NIL, RELOPT_BASEREL);
/* Locate IndexOptInfo for the target index */
indexInfo = NULL;
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index b91e9f4..61bc447 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -236,7 +236,8 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,
* used for anything here, but it carries the subroot data structures
* forward to setrefs.c processing.
*/
- rel = build_simple_rel(root, rtr->rtindex, RELOPT_BASEREL);
+ rel = build_simple_rel(root, rtr->rtindex, refnames_tlist,
+ RELOPT_BASEREL);
/* plan_params should not be in use in current query level */
Assert(root->plan_params == NIL);
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index abcd0ee..a37ab35 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -25,6 +25,7 @@
#include "access/xlog.h"
#include "catalog/catalog.h"
#include "catalog/heap.h"
+#include "foreign/fdwapi.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "optimizer/clauses.h"
@@ -80,7 +81,7 @@ static List *build_index_tlist(PlannerInfo *root, IndexOptInfo *index,
*/
void
get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
- RelOptInfo *rel)
+ List *tlist, RelOptInfo *rel)
{
Index varno = rel->relid;
Relation relation;
@@ -104,6 +105,26 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
rel->max_attr = RelationGetNumberOfAttributes(relation);
rel->reltablespace = RelationGetForm(relation)->reltablespace;
+ /*
+ * Adjust width of attr_needed slot in case when FDW extension wants
+ * or needs to return pseudo-columns also, not only columns in its
+ * table definition.
+ * GetForeignRelInfo, an optional FDW handler, enables FDW extension
+ * to save properties of pseudo-column on its private field.
+ * When foreign-table is the target of UPDATE/DELETE, query-rewritter
+ * injects "rowid" pseudo-column to track remote row to be modified,
+ * so FDW has to track which varattno shall perform as "rowid".
+ */
+ if (RelationGetForm(relation)->relkind == RELKIND_FOREIGN_TABLE)
+ {
+ FdwRoutine *fdwroutine = GetFdwRoutineByRelId(relationObjectId);
+
+ if (fdwroutine->GetForeignRelInfo)
+ fdwroutine->GetForeignRelInfo(root, rel,
+ relationObjectId,
+ inhparent, tlist);
+ }
+
Assert(rel->max_attr >= rel->min_attr);
rel->attr_needed = (Relids *)
palloc0((rel->max_attr - rel->min_attr + 1) * sizeof(Relids));
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index f724714..84b674c 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -80,7 +80,8 @@ setup_simple_rel_arrays(PlannerInfo *root)
* Construct a new RelOptInfo for a base relation or 'other' relation.
*/
RelOptInfo *
-build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
+build_simple_rel(PlannerInfo *root, int relid, List *tlist,
+ RelOptKind reloptkind)
{
RelOptInfo *rel;
RangeTblEntry *rte;
@@ -133,7 +134,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
{
case RTE_RELATION:
/* Table --- retrieve statistics from the system catalogs */
- get_relation_info(root, rte->relid, rte->inh, rel);
+ get_relation_info(root, rte->relid, rte->inh, tlist, rel);
break;
case RTE_SUBQUERY:
case RTE_FUNCTION:
@@ -180,7 +181,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
if (appinfo->parent_relid != relid)
continue;
- (void) build_simple_rel(root, appinfo->child_relid,
+ (void) build_simple_rel(root, appinfo->child_relid, tlist,
RELOPT_OTHER_MEMBER_REL);
}
}
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index b785c26..5c5a418 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -1165,7 +1165,10 @@ rewriteTargetListUD(Query *parsetree, RangeTblEntry *target_rte,
Relation target_relation)
{
Var *var;
- const char *attrname;
+ List *varList;
+ List *attNameList;
+ ListCell *cell1;
+ ListCell *cell2;
TargetEntry *tle;
if (target_relation->rd_rel->relkind == RELKIND_RELATION)
@@ -1179,8 +1182,35 @@ rewriteTargetListUD(Query *parsetree, RangeTblEntry *target_rte,
-1,
InvalidOid,
0);
+ varList = list_make1(var);
+ attNameList = list_make1("ctid");
+ }
+ else if (target_relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+ {
+ /*
+ * Emit Rowid so that executor can find the row to update or delete.
+ */
+ var = makeVar(parsetree->resultRelation,
+ RelationGetNumberOfAttributes(target_relation) + 1,
+ INTERNALOID,
+ -1,
+ InvalidOid,
+ 0);
+ varList = list_make1(var);
- attrname = "ctid";
+ /*
+ * 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
{
@@ -1192,16 +1222,21 @@ rewriteTargetListUD(Query *parsetree, RangeTblEntry *target_rte,
parsetree->resultRelation,
0,
false);
-
- attrname = "wholerow";
+ varList = list_make1(var);
+ attNameList = list_make1("wholerow");
}
- tle = makeTargetEntry((Expr *) var,
- list_length(parsetree->targetList) + 1,
- pstrdup(attrname),
- true);
-
- parsetree->targetList = lappend(parsetree->targetList, tle);
+ /*
+ * Append them to targetList
+ */
+ forboth (cell1, varList, cell2, attNameList)
+ {
+ tle = makeTargetEntry((Expr *)lfirst(cell1),
+ list_length(parsetree->targetList) + 1,
+ pstrdup((const char *)lfirst(cell2)),
+ true);
+ parsetree->targetList = lappend(parsetree->targetList, tle);
+ }
}
diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h
index e790b9d..8370e05 100644
--- a/src/include/commands/trigger.h
+++ b/src/include/commands/trigger.h
@@ -147,10 +147,10 @@ extern void ExecASDeleteTriggers(EState *estate,
extern bool ExecBRDeleteTriggers(EState *estate,
EPQState *epqstate,
ResultRelInfo *relinfo,
- ItemPointer tupleid);
+ Datum rowid);
extern void ExecARDeleteTriggers(EState *estate,
ResultRelInfo *relinfo,
- ItemPointer tupleid);
+ Datum rowid);
extern bool ExecIRDeleteTriggers(EState *estate,
ResultRelInfo *relinfo,
HeapTuple trigtuple);
@@ -161,11 +161,11 @@ extern void ExecASUpdateTriggers(EState *estate,
extern TupleTableSlot *ExecBRUpdateTriggers(EState *estate,
EPQState *epqstate,
ResultRelInfo *relinfo,
- ItemPointer tupleid,
+ Datum rowid,
TupleTableSlot *slot);
extern void ExecARUpdateTriggers(EState *estate,
ResultRelInfo *relinfo,
- ItemPointer tupleid,
+ Datum rowid,
HeapTuple newtuple,
List *recheckIndexes);
extern TupleTableSlot *ExecIRUpdateTriggers(EState *estate,
diff --git a/src/include/foreign/fdwapi.h b/src/include/foreign/fdwapi.h
index 721cd25..8b29532 100644
--- a/src/include/foreign/fdwapi.h
+++ b/src/include/foreign/fdwapi.h
@@ -22,6 +22,11 @@ struct ExplainState;
/*
* Callback function signatures --- see fdwhandler.sgml for more info.
*/
+typedef void (*GetForeignRelInfo_function) (PlannerInfo *root,
+ RelOptInfo *baserel,
+ Oid foreigntableid,
+ bool inhparent,
+ List *targetList);
typedef void (*GetForeignRelSize_function) (PlannerInfo *root,
RelOptInfo *baserel,
@@ -59,6 +64,20 @@ typedef bool (*AnalyzeForeignTable_function) (Relation relation,
AcquireSampleRowsFunc *func,
BlockNumber *totalpages);
+typedef void (*BeginForeignModify_function) (CmdType operation,
+ ResultRelInfo *resultRelInfo,
+ EState *estate,
+ 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
* function. It provides pointers to the callback functions needed by the
@@ -90,6 +109,12 @@ typedef struct FdwRoutine
* not provided.
*/
AnalyzeForeignTable_function AnalyzeForeignTable;
+ GetForeignRelInfo_function GetForeignRelInfo;
+ BeginForeignModify_function BeginForeignModify;
+ ExecForeignInsert_function ExecForeignInsert;
+ ExecForeignDelete_function ExecForeignDelete;
+ ExecForeignUpdate_function ExecForeignUpdate;
+ EndForeignModify_function EndForeignModify;
} FdwRoutine;
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index fec07b8..d8fa299 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -268,9 +268,11 @@ typedef struct ProjectionInfo
* attribute numbers of the "original" tuple and the
* attribute numbers of the "clean" tuple.
* resultSlot: tuple slot used to hold cleaned tuple.
- * junkAttNo: not used by junkfilter code. Can be used by caller
- * to remember the attno of a specific junk attribute
+ * jf_junkRowidNo: not used by junkfilter code. Can be used by caller
+ * to remember the attno of to track a particular tuple
+ * being updated or deleted.
* (execMain.c stores the "ctid" attno here).
+ * jf_junkRecordNo: Also, the attno of whole-row reference.
* ----------------
*/
typedef struct JunkFilter
@@ -280,7 +282,8 @@ typedef struct JunkFilter
TupleDesc jf_cleanTupType;
AttrNumber *jf_cleanMap;
TupleTableSlot *jf_resultSlot;
- AttrNumber jf_junkAttNo;
+ AttrNumber jf_junkRowidNo;
+ AttrNumber jf_junkRecordNo;
} JunkFilter;
/* ----------------
@@ -303,6 +306,8 @@ typedef struct JunkFilter
* ConstraintExprs array of constraint-checking expr states
* junkFilter for removing junk attributes from tuples
* projectReturning for computing a RETURNING list
+ * fdwroutine FDW callbacks if foreign table
+ * fdw_state opaque state of FDW module, or NULL
* ----------------
*/
typedef struct ResultRelInfo
@@ -320,6 +325,8 @@ typedef struct ResultRelInfo
List **ri_ConstraintExprs;
JunkFilter *ri_junkFilter;
ProjectionInfo *ri_projectReturning;
+ struct FdwRoutine *ri_fdwroutine;
+ void *ri_fdw_state;
} ResultRelInfo;
/* ----------------
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index fb9a863..a724863 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -478,6 +478,7 @@ typedef struct ForeignScan
List *fdw_exprs; /* expressions that FDW may evaluate */
List *fdw_private; /* private data for FDW */
bool fsSystemCol; /* true if any "system column" is needed */
+ bool fsPseudoCol; /* true if any "pseudo column" is needed */
} ForeignScan;
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index de889fb..adfc93d 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -133,7 +133,7 @@ extern Path *reparameterize_path(PlannerInfo *root, Path *path,
*/
extern void setup_simple_rel_arrays(PlannerInfo *root);
extern RelOptInfo *build_simple_rel(PlannerInfo *root, int relid,
- RelOptKind reloptkind);
+ List *tlist, RelOptKind reloptkind);
extern RelOptInfo *find_base_rel(PlannerInfo *root, int relid);
extern RelOptInfo *find_join_rel(PlannerInfo *root, Relids relids);
extern RelOptInfo *build_join_rel(PlannerInfo *root,
diff --git a/src/include/optimizer/plancat.h b/src/include/optimizer/plancat.h
index e0d04db..c5fce41 100644
--- a/src/include/optimizer/plancat.h
+++ b/src/include/optimizer/plancat.h
@@ -26,7 +26,7 @@ extern PGDLLIMPORT get_relation_info_hook_type get_relation_info_hook;
extern void get_relation_info(PlannerInfo *root, Oid relationObjectId,
- bool inhparent, RelOptInfo *rel);
+ bool inhparent, List *tlist, RelOptInfo *rel);
extern void estimate_rel_size(Relation rel, int32 *attr_widths,
BlockNumber *pages, double *tuples, double *allvisfrac);
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index 0fe696c..ad7bbd6 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -90,7 +90,8 @@ extern bool is_projection_capable_plan(Plan *plan);
extern int from_collapse_limit;
extern int join_collapse_limit;
-extern void add_base_rels_to_query(PlannerInfo *root, Node *jtnode);
+extern void add_base_rels_to_query(PlannerInfo *root, List *tlist,
+ Node *jtnode);
extern void build_base_rel_tlists(PlannerInfo *root, List *final_tlist);
extern void add_vars_to_targetlist(PlannerInfo *root, List *vars,
Relids where_needed, bool create_new_ph);