diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/commands/explain.c | 55 | ||||
-rw-r--r-- | src/backend/executor/execMain.c | 1 | ||||
-rw-r--r-- | src/backend/executor/nodeForeignscan.c | 17 | ||||
-rw-r--r-- | src/backend/executor/nodeModifyTable.c | 60 | ||||
-rw-r--r-- | src/backend/nodes/copyfuncs.c | 2 | ||||
-rw-r--r-- | src/backend/nodes/outfuncs.c | 2 | ||||
-rw-r--r-- | src/backend/nodes/readfuncs.c | 1 | ||||
-rw-r--r-- | src/backend/optimizer/plan/createplan.c | 21 | ||||
-rw-r--r-- | src/backend/optimizer/util/plancat.c | 47 |
9 files changed, 188 insertions, 18 deletions
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 9cd3127937..787b0b93cc 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -900,7 +900,29 @@ ExplainNode(PlanState *planstate, List *ancestors, pname = sname = "WorkTable Scan"; break; case T_ForeignScan: - pname = sname = "Foreign Scan"; + sname = "Foreign Scan"; + switch (((ForeignScan *) plan)->operation) + { + case CMD_SELECT: + pname = "Foreign Scan"; + operation = "Select"; + break; + case CMD_INSERT: + pname = "Foreign Insert"; + operation = "Insert"; + break; + case CMD_UPDATE: + pname = "Foreign Update"; + operation = "Update"; + break; + case CMD_DELETE: + pname = "Foreign Delete"; + operation = "Delete"; + break; + default: + pname = "???"; + break; + } break; case T_CustomScan: sname = "Custom Scan"; @@ -1648,6 +1670,19 @@ show_plan_tlist(PlanState *planstate, List *ancestors, ExplainState *es) return; if (IsA(plan, RecursiveUnion)) return; + /* + * Likewise for ForeignScan that executes a direct INSERT/UPDATE/DELETE + * + * Note: the tlist for a ForeignScan that executes a direct INSERT/UPDATE + * might contain subplan output expressions that are confusing in this + * context. The tlist for a ForeignScan that executes a direct UPDATE/ + * DELETE always contains "junk" target columns to identify the exact row + * to update or delete, which would be confusing in this context. So, we + * suppress it in all the cases. + */ + if (IsA(plan, ForeignScan) && + ((ForeignScan *) plan)->operation != CMD_SELECT) + return; /* Set up deparsing context */ context = set_deparse_context_planstate(es->deparse_cxt, @@ -2236,8 +2271,16 @@ show_foreignscan_info(ForeignScanState *fsstate, ExplainState *es) FdwRoutine *fdwroutine = fsstate->fdwroutine; /* Let the FDW emit whatever fields it wants */ - if (fdwroutine->ExplainForeignScan != NULL) - fdwroutine->ExplainForeignScan(fsstate, es); + if (((ForeignScan *) fsstate->ss.ps.plan)->operation != CMD_SELECT) + { + if (fdwroutine->ExplainDirectModify != NULL) + fdwroutine->ExplainDirectModify(fsstate, es); + } + else + { + if (fdwroutine->ExplainForeignScan != NULL) + fdwroutine->ExplainForeignScan(fsstate, es); + } } /* @@ -2623,8 +2666,10 @@ show_modifytable_info(ModifyTableState *mtstate, List *ancestors, } } - /* Give FDW a chance */ - if (fdwroutine && fdwroutine->ExplainForeignModify != NULL) + /* Give FDW a chance if needed */ + if (!resultRelInfo->ri_usesFdwDirectModify && + fdwroutine != NULL && + fdwroutine->ExplainForeignModify != NULL) { List *fdw_private = (List *) list_nth(node->fdwPrivLists, j); diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 687256279a..ac0230411c 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -1245,6 +1245,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo, else resultRelInfo->ri_FdwRoutine = NULL; resultRelInfo->ri_FdwState = NULL; + resultRelInfo->ri_usesFdwDirectModify = false; resultRelInfo->ri_ConstraintExprs = NULL; resultRelInfo->ri_junkFilter = NULL; resultRelInfo->ri_projectReturning = NULL; diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c index 388c922749..300f947d43 100644 --- a/src/backend/executor/nodeForeignscan.c +++ b/src/backend/executor/nodeForeignscan.c @@ -48,7 +48,10 @@ ForeignNext(ForeignScanState *node) /* Call the Iterate function in short-lived context */ oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - slot = node->fdwroutine->IterateForeignScan(node); + if (plan->operation != CMD_SELECT) + slot = node->fdwroutine->IterateDirectModify(node); + else + slot = node->fdwroutine->IterateForeignScan(node); MemoryContextSwitchTo(oldcontext); /* @@ -226,7 +229,10 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) /* * Tell the FDW to initialize the scan. */ - fdwroutine->BeginForeignScan(scanstate, eflags); + if (node->operation != CMD_SELECT) + fdwroutine->BeginDirectModify(scanstate, eflags); + else + fdwroutine->BeginForeignScan(scanstate, eflags); return scanstate; } @@ -240,8 +246,13 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) void ExecEndForeignScan(ForeignScanState *node) { + ForeignScan *plan = (ForeignScan *) node->ss.ps.plan; + /* Let the FDW shut down */ - node->fdwroutine->EndForeignScan(node); + if (plan->operation != CMD_SELECT) + node->fdwroutine->EndDirectModify(node); + else + node->fdwroutine->EndForeignScan(node); /* Shut down any outer plan. */ if (outerPlanState(node)) diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 27051e80b0..e62c8aad65 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -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); diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index df7c2fa892..4589834305 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -188,6 +188,7 @@ _copyModifyTable(const ModifyTable *from) COPY_NODE_FIELD(withCheckOptionLists); COPY_NODE_FIELD(returningLists); COPY_NODE_FIELD(fdwPrivLists); + COPY_BITMAPSET_FIELD(fdwDirectModifyPlans); COPY_NODE_FIELD(rowMarks); COPY_SCALAR_FIELD(epqParam); COPY_SCALAR_FIELD(onConflictAction); @@ -648,6 +649,7 @@ _copyForeignScan(const ForeignScan *from) /* * copy remainder of node */ + COPY_SCALAR_FIELD(operation); COPY_SCALAR_FIELD(fs_server); COPY_NODE_FIELD(fdw_exprs); COPY_NODE_FIELD(fdw_private); diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 548a3b9e57..1144a4c1c7 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -356,6 +356,7 @@ _outModifyTable(StringInfo str, const ModifyTable *node) WRITE_NODE_FIELD(withCheckOptionLists); WRITE_NODE_FIELD(returningLists); WRITE_NODE_FIELD(fdwPrivLists); + WRITE_BITMAPSET_FIELD(fdwDirectModifyPlans); WRITE_NODE_FIELD(rowMarks); WRITE_INT_FIELD(epqParam); WRITE_ENUM_FIELD(onConflictAction, OnConflictAction); @@ -608,6 +609,7 @@ _outForeignScan(StringInfo str, const ForeignScan *node) _outScanInfo(str, (const Scan *) node); + WRITE_ENUM_FIELD(operation, CmdType); WRITE_OID_FIELD(fs_server); WRITE_NODE_FIELD(fdw_exprs); WRITE_NODE_FIELD(fdw_private); diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index a2c2243fb5..f5d677e6ac 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1481,6 +1481,7 @@ _readModifyTable(void) READ_NODE_FIELD(withCheckOptionLists); READ_NODE_FIELD(returningLists); READ_NODE_FIELD(fdwPrivLists); + READ_BITMAPSET_FIELD(fdwDirectModifyPlans); READ_NODE_FIELD(rowMarks); READ_INT_FIELD(epqParam); READ_ENUM_FIELD(onConflictAction, OnConflictAction); diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index f08f0ea01f..087cb9c441 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -4906,6 +4906,7 @@ make_foreignscan(List *qptlist, plan->lefttree = outer_plan; plan->righttree = NULL; node->scan.scanrelid = scanrelid; + node->operation = CMD_SELECT; /* fs_server will be filled in by create_foreignscan_plan */ node->fs_server = InvalidOid; node->fdw_exprs = fdw_exprs; @@ -6021,6 +6022,7 @@ make_modifytable(PlannerInfo *root, { ModifyTable *node = makeNode(ModifyTable); List *fdw_private_list; + Bitmapset *direct_modify_plans; ListCell *lc; int i; @@ -6078,12 +6080,14 @@ make_modifytable(PlannerInfo *root, * construct private plan data, and accumulate it all into a list. */ fdw_private_list = NIL; + direct_modify_plans = NULL; i = 0; foreach(lc, resultRelations) { Index rti = lfirst_int(lc); FdwRoutine *fdwroutine; List *fdw_private; + bool direct_modify; /* * If possible, we want to get the FdwRoutine from our RelOptInfo for @@ -6110,7 +6114,23 @@ make_modifytable(PlannerInfo *root, fdwroutine = NULL; } + /* + * If the target foreign table has any row-level triggers, we can't + * modify the foreign table directly. + */ + direct_modify = false; if (fdwroutine != NULL && + fdwroutine->PlanDirectModify != NULL && + fdwroutine->BeginDirectModify != NULL && + fdwroutine->IterateDirectModify != NULL && + fdwroutine->EndDirectModify != NULL && + !has_row_triggers(root, rti, operation)) + direct_modify = fdwroutine->PlanDirectModify(root, node, rti, i); + if (direct_modify) + direct_modify_plans = bms_add_member(direct_modify_plans, i); + + if (!direct_modify && + fdwroutine != NULL && fdwroutine->PlanForeignModify != NULL) fdw_private = fdwroutine->PlanForeignModify(root, node, rti, i); else @@ -6119,6 +6139,7 @@ make_modifytable(PlannerInfo *root, i++; } node->fdwPrivLists = fdw_private_list; + node->fdwDirectModifyPlans = direct_modify_plans; return node; } diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index ad715bbcc5..546067b064 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -1540,3 +1540,50 @@ has_unique_index(RelOptInfo *rel, AttrNumber attno) } return false; } + + +/* + * has_row_triggers + * + * Detect whether the specified relation has any row-level triggers for event. + */ +bool +has_row_triggers(PlannerInfo *root, Index rti, CmdType event) +{ + RangeTblEntry *rte = planner_rt_fetch(rti, root); + Relation relation; + TriggerDesc *trigDesc; + bool result = false; + + /* Assume we already have adequate lock */ + relation = heap_open(rte->relid, NoLock); + + trigDesc = relation->trigdesc; + switch (event) + { + case CMD_INSERT: + if (trigDesc && + (trigDesc->trig_insert_after_row || + trigDesc->trig_insert_before_row)) + result = true; + break; + case CMD_UPDATE: + if (trigDesc && + (trigDesc->trig_update_after_row || + trigDesc->trig_update_before_row)) + result = true; + break; + case CMD_DELETE: + if (trigDesc && + (trigDesc->trig_delete_after_row || + trigDesc->trig_delete_before_row)) + result = true; + break; + default: + elog(ERROR, "unrecognized CmdType: %d", (int) event); + break; + } + + heap_close(relation, NoLock); + return result; +} |