mirror of
https://github.com/postgres/postgres.git
synced 2025-06-27 23:21:58 +03:00
Directly modify foreign tables.
postgres_fdw can now sent an UPDATE or DELETE statement directly to the foreign server in simple cases, rather than sending a SELECT FOR UPDATE statement and then updating or deleting rows one-by-one. Etsuro Fujita, reviewed by Rushabh Lathia, Shigeru Hanada, Kyotaro Horiguchi, Albe Laurenz, Thom Brown, and me.
This commit is contained in:
@ -138,13 +138,17 @@ ExecCheckPlanOutput(Relation resultRel, List *targetList)
|
||||
* tupleSlot: slot holding tuple actually inserted/updated/deleted
|
||||
* planSlot: slot holding tuple returned by top subplan node
|
||||
*
|
||||
* Note: If tupleSlot is NULL, the FDW should have already provided econtext's
|
||||
* scan tuple.
|
||||
*
|
||||
* Returns a slot holding the result tuple
|
||||
*/
|
||||
static TupleTableSlot *
|
||||
ExecProcessReturning(ProjectionInfo *projectReturning,
|
||||
ExecProcessReturning(ResultRelInfo *resultRelInfo,
|
||||
TupleTableSlot *tupleSlot,
|
||||
TupleTableSlot *planSlot)
|
||||
{
|
||||
ProjectionInfo *projectReturning = resultRelInfo->ri_projectReturning;
|
||||
ExprContext *econtext = projectReturning->pi_exprContext;
|
||||
|
||||
/*
|
||||
@ -154,7 +158,20 @@ ExecProcessReturning(ProjectionInfo *projectReturning,
|
||||
ResetExprContext(econtext);
|
||||
|
||||
/* Make tuple and any needed join variables available to ExecProject */
|
||||
econtext->ecxt_scantuple = tupleSlot;
|
||||
if (tupleSlot)
|
||||
econtext->ecxt_scantuple = tupleSlot;
|
||||
else
|
||||
{
|
||||
HeapTuple tuple;
|
||||
|
||||
/*
|
||||
* RETURNING expressions might reference the tableoid column, so
|
||||
* initialize t_tableOid before evaluating them.
|
||||
*/
|
||||
Assert(!TupIsNull(econtext->ecxt_scantuple));
|
||||
tuple = ExecMaterializeSlot(econtext->ecxt_scantuple);
|
||||
tuple->t_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
|
||||
}
|
||||
econtext->ecxt_outertuple = planSlot;
|
||||
|
||||
/* Compute the RETURNING expressions */
|
||||
@ -496,8 +513,7 @@ ExecInsert(ModifyTableState *mtstate,
|
||||
|
||||
/* Process RETURNING if present */
|
||||
if (resultRelInfo->ri_projectReturning)
|
||||
return ExecProcessReturning(resultRelInfo->ri_projectReturning,
|
||||
slot, planSlot);
|
||||
return ExecProcessReturning(resultRelInfo, slot, planSlot);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -738,8 +754,7 @@ ldelete:;
|
||||
ExecStoreTuple(&deltuple, slot, InvalidBuffer, false);
|
||||
}
|
||||
|
||||
rslot = ExecProcessReturning(resultRelInfo->ri_projectReturning,
|
||||
slot, planSlot);
|
||||
rslot = ExecProcessReturning(resultRelInfo, slot, planSlot);
|
||||
|
||||
/*
|
||||
* Before releasing the target tuple again, make sure rslot has a
|
||||
@ -1024,8 +1039,7 @@ lreplace:;
|
||||
|
||||
/* Process RETURNING if present */
|
||||
if (resultRelInfo->ri_projectReturning)
|
||||
return ExecProcessReturning(resultRelInfo->ri_projectReturning,
|
||||
slot, planSlot);
|
||||
return ExecProcessReturning(resultRelInfo, slot, planSlot);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -1380,6 +1394,26 @@ ExecModifyTable(ModifyTableState *node)
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* If resultRelInfo->ri_usesFdwDirectModify is true, all we need to do
|
||||
* here is compute the RETURNING expressions.
|
||||
*/
|
||||
if (resultRelInfo->ri_usesFdwDirectModify)
|
||||
{
|
||||
Assert(resultRelInfo->ri_projectReturning);
|
||||
|
||||
/*
|
||||
* A scan slot containing the data that was actually inserted,
|
||||
* updated or deleted has already been made available to
|
||||
* ExecProcessReturning by IterateDirectModify, so no need to
|
||||
* provide it here.
|
||||
*/
|
||||
slot = ExecProcessReturning(resultRelInfo, NULL, planSlot);
|
||||
|
||||
estate->es_result_relation_info = saved_resultRelInfo;
|
||||
return slot;
|
||||
}
|
||||
|
||||
EvalPlanQualSetSlot(&node->mt_epqstate, planSlot);
|
||||
slot = planSlot;
|
||||
|
||||
@ -1559,6 +1593,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
|
||||
{
|
||||
subplan = (Plan *) lfirst(l);
|
||||
|
||||
/* Initialize the usesFdwDirectModify flag */
|
||||
resultRelInfo->ri_usesFdwDirectModify = bms_is_member(i,
|
||||
node->fdwDirectModifyPlans);
|
||||
|
||||
/*
|
||||
* Verify result relation is a valid target for the current operation
|
||||
*/
|
||||
@ -1583,7 +1621,8 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
|
||||
mtstate->mt_plans[i] = ExecInitNode(subplan, estate, eflags);
|
||||
|
||||
/* Also let FDWs init themselves for foreign-table result rels */
|
||||
if (resultRelInfo->ri_FdwRoutine != NULL &&
|
||||
if (!resultRelInfo->ri_usesFdwDirectModify &&
|
||||
resultRelInfo->ri_FdwRoutine != NULL &&
|
||||
resultRelInfo->ri_FdwRoutine->BeginForeignModify != NULL)
|
||||
{
|
||||
List *fdw_private = (List *) list_nth(node->fdwPrivLists, i);
|
||||
@ -1910,7 +1949,8 @@ ExecEndModifyTable(ModifyTableState *node)
|
||||
{
|
||||
ResultRelInfo *resultRelInfo = node->resultRelInfo + i;
|
||||
|
||||
if (resultRelInfo->ri_FdwRoutine != NULL &&
|
||||
if (!resultRelInfo->ri_usesFdwDirectModify &&
|
||||
resultRelInfo->ri_FdwRoutine != NULL &&
|
||||
resultRelInfo->ri_FdwRoutine->EndForeignModify != NULL)
|
||||
resultRelInfo->ri_FdwRoutine->EndForeignModify(node->ps.state,
|
||||
resultRelInfo);
|
||||
|
Reference in New Issue
Block a user