diff options
Diffstat (limited to 'src/backend/executor/nodeModifyTable.c')
-rw-r--r-- | src/backend/executor/nodeModifyTable.c | 151 |
1 files changed, 133 insertions, 18 deletions
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index cb084d03d47..a6f247e1bc3 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" @@ -225,6 +226,24 @@ ExecInsert(TupleTableSlot *slot, newId = InvalidOid; } + else if (resultRelInfo->ri_FdwRoutine) + { + /* + * insert into foreign table: let the FDW do it + */ + slot = resultRelInfo->ri_FdwRoutine->ExecForeignInsert(estate, + resultRelInfo, + slot, + planSlot); + + if (slot == NULL) /* "do nothing" */ + return NULL; + + /* FDW might have changed tuple */ + tuple = ExecMaterializeSlot(slot); + + newId = InvalidOid; + } else { /* @@ -279,7 +298,9 @@ ExecInsert(TupleTableSlot *slot, * When deleting from a table, tupleid identifies the tuple to * delete and oldtuple is NULL. When deleting from a view, * oldtuple is passed to the INSTEAD OF triggers and identifies - * what to delete, and tupleid is invalid. + * what to delete, and tupleid is invalid. When deleting from a + * foreign table, both tupleid and oldtuple are NULL; the FDW has + * to figure out which row to delete using data from the planSlot. * * Returns RETURNING result if any, otherwise NULL. * ---------------------------------------------------------------- @@ -296,6 +317,7 @@ ExecDelete(ItemPointer tupleid, Relation resultRelationDesc; HTSU_Result result; HeapUpdateFailureData hufd; + TupleTableSlot *slot = NULL; /* * get information on the (current) result relation @@ -334,6 +356,27 @@ ExecDelete(ItemPointer tupleid, if (!dodelete) /* "do nothing" */ return NULL; } + else if (resultRelInfo->ri_FdwRoutine) + { + /* + * delete from foreign table: let the FDW do it + * + * We offer the trigger tuple slot as a place to store RETURNING data, + * although the FDW can return some other slot if it wants. Set up + * the slot's tupdesc so the FDW doesn't need to do that for itself. + */ + slot = estate->es_trig_tuple_slot; + if (slot->tts_tupleDescriptor != RelationGetDescr(resultRelationDesc)) + ExecSetSlotDescriptor(slot, RelationGetDescr(resultRelationDesc)); + + slot = resultRelInfo->ri_FdwRoutine->ExecForeignDelete(estate, + resultRelInfo, + slot, + planSlot); + + if (slot == NULL) /* "do nothing" */ + return NULL; + } else { /* @@ -443,34 +486,49 @@ ldelete:; * We have to put the target tuple into a slot, which means first we * gotta fetch it. We can use the trigger tuple slot. */ - TupleTableSlot *slot = estate->es_trig_tuple_slot; TupleTableSlot *rslot; HeapTupleData deltuple; Buffer delbuffer; - if (oldtuple != NULL) + if (resultRelInfo->ri_FdwRoutine) { - deltuple.t_data = oldtuple; - deltuple.t_len = HeapTupleHeaderGetDatumLength(oldtuple); - ItemPointerSetInvalid(&(deltuple.t_self)); - deltuple.t_tableOid = InvalidOid; + /* FDW must have provided a slot containing the deleted row */ + Assert(!TupIsNull(slot)); delbuffer = InvalidBuffer; } else { - deltuple.t_self = *tupleid; - if (!heap_fetch(resultRelationDesc, SnapshotAny, - &deltuple, &delbuffer, false, NULL)) - elog(ERROR, "failed to fetch deleted tuple for DELETE RETURNING"); - } + slot = estate->es_trig_tuple_slot; + if (oldtuple != NULL) + { + deltuple.t_data = oldtuple; + deltuple.t_len = HeapTupleHeaderGetDatumLength(oldtuple); + ItemPointerSetInvalid(&(deltuple.t_self)); + deltuple.t_tableOid = InvalidOid; + delbuffer = InvalidBuffer; + } + else + { + deltuple.t_self = *tupleid; + if (!heap_fetch(resultRelationDesc, SnapshotAny, + &deltuple, &delbuffer, false, NULL)) + elog(ERROR, "failed to fetch deleted tuple for DELETE RETURNING"); + } - if (slot->tts_tupleDescriptor != RelationGetDescr(resultRelationDesc)) - ExecSetSlotDescriptor(slot, RelationGetDescr(resultRelationDesc)); - ExecStoreTuple(&deltuple, slot, InvalidBuffer, false); + if (slot->tts_tupleDescriptor != RelationGetDescr(resultRelationDesc)) + ExecSetSlotDescriptor(slot, RelationGetDescr(resultRelationDesc)); + ExecStoreTuple(&deltuple, slot, InvalidBuffer, false); + } rslot = ExecProcessReturning(resultRelInfo->ri_projectReturning, slot, planSlot); + /* + * Before releasing the target tuple again, make sure rslot has a + * local copy of any pass-by-reference values. + */ + ExecMaterializeSlot(rslot); + ExecClearTuple(slot); if (BufferIsValid(delbuffer)) ReleaseBuffer(delbuffer); @@ -494,7 +552,9 @@ ldelete:; * When updating a table, tupleid identifies the tuple to * update and oldtuple is NULL. When updating a view, oldtuple * is passed to the INSTEAD OF triggers and identifies what to - * update, and tupleid is invalid. + * update, and tupleid is invalid. When updating a foreign table, + * both tupleid and oldtuple are NULL; the FDW has to figure out + * which row to update using data from the planSlot. * * Returns RETURNING result if any, otherwise NULL. * ---------------------------------------------------------------- @@ -568,6 +628,22 @@ ExecUpdate(ItemPointer tupleid, /* trigger might have changed tuple */ tuple = ExecMaterializeSlot(slot); } + else if (resultRelInfo->ri_FdwRoutine) + { + /* + * update in foreign table: let the FDW do it + */ + slot = resultRelInfo->ri_FdwRoutine->ExecForeignUpdate(estate, + resultRelInfo, + slot, + planSlot); + + if (slot == NULL) /* "do nothing" */ + return NULL; + + /* FDW might have changed tuple */ + tuple = ExecMaterializeSlot(slot); + } else { LockTupleMode lockmode; @@ -867,10 +943,12 @@ ExecModifyTable(ModifyTableState *node) */ 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, @@ -884,6 +962,10 @@ ExecModifyTable(ModifyTableState *node) * ctid!! */ tupleid = &tuple_ctid; } + else if (relkind == RELKIND_FOREIGN_TABLE) + { + /* do nothing; FDW must fetch any junk attrs it wants */ + } else { datum = ExecGetJunkAttribute(slot, @@ -1026,6 +1108,19 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) estate->es_result_relation_info = resultRelInfo; mtstate->mt_plans[i] = ExecInitNode(subplan, estate, eflags); + /* Also let FDWs init themselves for foreign-table result rels */ + if (resultRelInfo->ri_FdwRoutine != NULL && + resultRelInfo->ri_FdwRoutine->BeginForeignModify != NULL) + { + List *fdw_private = (List *) list_nth(node->fdwPrivLists, i); + + resultRelInfo->ri_FdwRoutine->BeginForeignModify(mtstate, + resultRelInfo, + fdw_private, + i, + eflags); + } + resultRelInfo++; i++; } @@ -1180,12 +1275,19 @@ 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) + char relkind; + + relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind; + if (relkind == RELKIND_RELATION) { j->jf_junkAttNo = ExecFindJunkAttribute(j, "ctid"); if (!AttributeNumberIsValid(j->jf_junkAttNo)) elog(ERROR, "could not find junk ctid column"); } + else if (relkind == RELKIND_FOREIGN_TABLE) + { + /* FDW must fetch any junk attrs it wants */ + } else { j->jf_junkAttNo = ExecFindJunkAttribute(j, "wholerow"); @@ -1244,6 +1346,19 @@ ExecEndModifyTable(ModifyTableState *node) int i; /* + * Allow any FDWs to shut down + */ + for (i = 0; i < node->mt_nplans; i++) + { + ResultRelInfo *resultRelInfo = node->resultRelInfo + i; + + if (resultRelInfo->ri_FdwRoutine != NULL && + resultRelInfo->ri_FdwRoutine->EndForeignModify != NULL) + resultRelInfo->ri_FdwRoutine->EndForeignModify(node->ps.state, + resultRelInfo); + } + + /* * Free the exprcontext */ ExecFreeExprContext(&node->ps); |