pgsql-v9.3-writable-fdw-poc.v1.patch
application/octet-stream
Filename: pgsql-v9.3-writable-fdw-poc.v1.patch
Type: application/octet-stream
Part: 0
Message:
[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 | 97 | 0 |
| src/backend/executor/execMain.c | 30 | 4 |
| src/backend/executor/nodeModifyTable.c | 60 | 5 |
| src/backend/rewrite/rewriteHandler.c | 2 | 1 |
| src/include/foreign/fdwapi.h | 19 | 0 |
| src/include/nodes/execnodes.h | 4 | 0 |
| src/include/storage/itemptr.h | 25 | 0 |
contrib/file_fdw/file_fdw.c | 97 ++++++++++++++++++++++++++++++++++
src/backend/executor/execMain.c | 34 ++++++++++--
src/backend/executor/nodeModifyTable.c | 65 +++++++++++++++++++++--
src/backend/rewrite/rewriteHandler.c | 3 +-
src/include/foreign/fdwapi.h | 19 +++++++
src/include/nodes/execnodes.h | 4 ++
src/include/storage/itemptr.h | 25 +++++++++
7 files changed, 237 insertions(+), 10 deletions(-)
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 7c7fedf..ef9f4b2 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -33,6 +33,7 @@
#include "optimizer/var.h"
#include "utils/memutils.h"
#include "utils/rel.h"
+#include "utils/lsyscache.h"
PG_MODULE_MAGIC;
@@ -96,6 +97,7 @@ 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 */
} FileFdwExecutionState;
/*
@@ -130,6 +132,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 void fileExecForeignInsert(ResultRelInfo *resultRelInfo,
+ HeapTuple tuple);
+static void fileExecForeignDelete(ResultRelInfo *resultRelInfo,
+ ItemPointer tupleid);
+static void fileExecForeignUpdate(ResultRelInfo *resultRelInfo,
+ ItemPointer tupleid,
+ HeapTuple tuple);
+static void fileEndForeignModify(ResultRelInfo *resultRelInfo);
/*
* Helper functions
@@ -169,6 +184,11 @@ file_fdw_handler(PG_FUNCTION_ARGS)
fdwroutine->ReScanForeignScan = fileReScanForeignScan;
fdwroutine->EndForeignScan = fileEndForeignScan;
fdwroutine->AnalyzeForeignTable = fileAnalyzeForeignTable;
+ fdwroutine->BeginForeignModify = fileBeginForeignModify;
+ fdwroutine->ExecForeignInsert = fileExecForeignInsert;
+ fdwroutine->ExecForeignDelete = fileExecForeignDelete;
+ fdwroutine->ExecForeignUpdate = fileExecForeignUpdate;
+ fdwroutine->EndForeignModify = fileEndForeignModify;
PG_RETURN_POINTER(fdwroutine);
}
@@ -510,6 +530,36 @@ fileGetForeignPlan(PlannerInfo *root,
List *scan_clauses)
{
Index scan_relid = baserel->relid;
+ ListCell *cell;
+
+ /*
+ * 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
@@ -598,6 +648,7 @@ fileBeginForeignScan(ForeignScanState *node, int eflags)
festate->filename = filename;
festate->options = options;
festate->cstate = cstate;
+ festate->lineno = 0;
node->fdw_state = (void *) festate;
}
@@ -612,6 +663,7 @@ fileIterateForeignScan(ForeignScanState *node)
{
FileFdwExecutionState *festate = (FileFdwExecutionState *) node->fdw_state;
TupleTableSlot *slot = node->ss.ss_ScanTupleSlot;
+ HeapTuple tup;
bool found;
ErrorContextCallback errcontext;
@@ -638,7 +690,12 @@ fileIterateForeignScan(ForeignScanState *node)
slot->tts_values, slot->tts_isnull,
NULL);
if (found)
+ {
ExecStoreVirtualTuple(slot);
+ tup = ExecMaterializeSlot(slot);
+ festate->lineno++;
+ PackPseudoItemPointer(&tup->t_self, festate->lineno);
+ }
/* Remove error callback. */
error_context_stack = errcontext.previous;
@@ -661,6 +718,7 @@ fileReScanForeignScan(ForeignScanState *node)
festate->filename,
NIL,
festate->options);
+ festate->lineno = 0;
}
/*
@@ -716,6 +774,45 @@ 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 void
+fileExecForeignInsert(ResultRelInfo *resultRelInfo, HeapTuple tuple)
+{
+ elog(INFO, "fdw_file: INSERT");
+}
+
+static void
+fileExecForeignDelete(ResultRelInfo *resultRelInfo,
+ ItemPointer tupleid)
+{
+ elog(INFO, "fdw_file: DELETE (lineno = %lu)",
+ (unsigned long) UnpackPseudoItemPointer(tupleid));
+}
+
+static void
+fileExecForeignUpdate(ResultRelInfo *resultRelInfo,
+ ItemPointer tupleid,
+ HeapTuple tuple)
+{
+ elog(INFO, "fdw_file: UPDATE (lineno = %lu)",
+ (unsigned long) UnpackPseudoItemPointer(tupleid));
+}
+
+static void
+fileEndForeignModify(ResultRelInfo *resultRelInfo)
+{
+ elog(INFO, "fdw_file: EndForeignModify method");
+}
+
/*
* check_selective_binary_conversion
*
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 440438b..7d20627 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -43,6 +43,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"
@@ -933,6 +934,7 @@ void
CheckValidResultRel(Relation resultRel, CmdType operation)
{
TriggerDesc *trigDesc = resultRel->trigdesc;
+ FdwRoutine *fdwroutine;
switch (resultRel->rd_rel->relkind)
{
@@ -984,10 +986,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 \"\"",
+ 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 \"\"",
+ RelationGetRelationName(resultRel))));
+ break;
+ default:
+ elog(ERROR, "unrecognized CmdType: %d", (int) operation);
+ break;
+ }
break;
default:
ereport(ERROR,
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index a7bce75..b2fc516 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -41,6 +41,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"
@@ -224,6 +225,14 @@ ExecInsert(TupleTableSlot *slot,
newId = InvalidOid;
}
+ else if (resultRelInfo->ri_fdwroutine)
+ {
+ FdwRoutine *fdwroutine = resultRelInfo->ri_fdwroutine;
+
+ fdwroutine->ExecForeignInsert(resultRelInfo, tuple);
+
+ newId = InvalidOid;
+ }
else
{
/*
@@ -334,6 +343,12 @@ ExecDelete(ItemPointer tupleid,
if (!dodelete) /* "do nothing" */
return NULL;
}
+ else if (resultRelInfo->ri_fdwroutine)
+ {
+ FdwRoutine *fdwroutine = resultRelInfo->ri_fdwroutine;
+
+ fdwroutine->ExecForeignDelete(resultRelInfo, tupleid);
+ }
else
{
/*
@@ -538,6 +553,12 @@ ExecUpdate(ItemPointer tupleid,
/* trigger might have changed tuple */
tuple = ExecMaterializeSlot(slot);
}
+ else if (resultRelInfo->ri_fdwroutine)
+ {
+ FdwRoutine *fdwroutine = resultRelInfo->ri_fdwroutine;
+
+ fdwroutine->ExecForeignUpdate(resultRelInfo, tupleid, tuple);
+ }
else
{
/*
@@ -805,10 +826,12 @@ ExecModifyTable(ModifyTableState *node)
*/
if (operation == CMD_UPDATE || operation == CMD_DELETE)
{
+ Relation rel = resultRelInfo->ri_RelationDesc;
Datum datum;
bool isNull;
- if (resultRelInfo->ri_RelationDesc->rd_rel->relkind == RELKIND_RELATION)
+ if (RelationGetForm(rel)->relkind == RELKIND_RELATION ||
+ RelationGetForm(rel)->relkind == RELKIND_FOREIGN_TABLE)
{
datum = ExecGetJunkAttribute(slot,
junkfilter->jf_junkAttNo,
@@ -964,6 +987,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++;
}
@@ -1104,21 +1143,22 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
resultRelInfo = mtstate->resultRelInfo;
for (i = 0; i < nplans; i++)
{
+ Relation rel = resultRelInfo->ri_RelationDesc;
JunkFilter *j;
subplan = mtstate->mt_plans[i]->plan;
if (operation == CMD_INSERT || operation == CMD_UPDATE)
- ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc,
- subplan->targetlist);
+ ExecCheckPlanOutput(rel, subplan->targetlist);
j = ExecInitJunkFilter(subplan->targetlist,
- resultRelInfo->ri_RelationDesc->rd_att->tdhasoid,
+ RelationGetDescr(rel)->tdhasoid,
ExecInitExtraTupleSlot(estate));
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 (RelationGetForm(rel)->relkind == RELKIND_RELATION ||
+ RelationGetForm(rel)->relkind == RELKIND_FOREIGN_TABLE)
{
j->jf_junkAttNo = ExecFindJunkAttribute(j, "ctid");
if (!AttributeNumberIsValid(j->jf_junkAttNo))
@@ -1181,6 +1221,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/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 8f75948..f461b87 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -1166,7 +1166,8 @@ rewriteTargetListUD(Query *parsetree, RangeTblEntry *target_rte,
const char *attrname;
TargetEntry *tle;
- if (target_relation->rd_rel->relkind == RELKIND_RELATION)
+ if (target_relation->rd_rel->relkind == RELKIND_RELATION ||
+ target_relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
{
/*
* Emit CTID so that executor can find the row to update or delete.
diff --git a/src/include/foreign/fdwapi.h b/src/include/foreign/fdwapi.h
index 721cd25..252d505 100644
--- a/src/include/foreign/fdwapi.h
+++ b/src/include/foreign/fdwapi.h
@@ -59,6 +59,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 void (*ExecForeignInsert_function) (ResultRelInfo *resultRelInfo,
+ HeapTuple tuple);
+typedef void (*ExecForeignDelete_function) (ResultRelInfo *resultRelInfo,
+ ItemPointer tupleid);
+typedef void (*ExecForeignUpdate_function) (ResultRelInfo *resultRelInfo,
+ ItemPointer tupleid,
+ 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 +104,11 @@ typedef struct FdwRoutine
* not provided.
*/
AnalyzeForeignTable_function AnalyzeForeignTable;
+ 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..db5b79b 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -303,6 +303,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 +322,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/storage/itemptr.h b/src/include/storage/itemptr.h
index 331812b..8fdee45 100644
--- a/src/include/storage/itemptr.h
+++ b/src/include/storage/itemptr.h
@@ -135,6 +135,31 @@ typedef ItemPointerData *ItemPointer;
(pointer)->ip_posid = InvalidOffsetNumber \
)
+/*
+ * PackPseudoItemPointer
+ * Pack a pseudo item pointer of foreign table
+ */
+#define PackPseudoItemPointer(pointer,value) \
+ do { \
+ AssertMacro((value) < (1UL << (8 * (sizeof(BlockIdData) + \
+ sizeof(OffsetNumber))))); \
+ BlockIdSet(&((pointer)->ip_blkid), \
+ (value) >> (8 * sizeof(OffsetNumber))); \
+ (pointer)->ip_posid = \
+ (value) & ((1 << (8 * sizeof(OffsetNumber))) - 1); \
+ } while(0)
+
+/*
+ * UnpackPseudoItemPointer
+ * Unpack a pseudo item pointer of foreign table
+ */
+#define UnpackPseudoItemPointer(pointer) \
+ ( \
+ (BlockIdGetBlockNumber(&(pointer)->ip_blkid) \
+ << (8 * sizeof(OffsetNumber))) \
+ | ((pointer)->ip_posid) \
+ )
+
/* ----------------
* externs
* ----------------