diff options
Diffstat (limited to 'contrib/postgres_fdw/postgres_fdw.c')
-rw-r--r-- | contrib/postgres_fdw/postgres_fdw.c | 129 |
1 files changed, 87 insertions, 42 deletions
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index cc73a6902f5..649f15113d0 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -27,6 +27,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" +#include "optimizer/appendinfo.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" #include "optimizer/optimizer.h" @@ -345,7 +346,8 @@ static void postgresBeginForeignScan(ForeignScanState *node, int eflags); static TupleTableSlot *postgresIterateForeignScan(ForeignScanState *node); static void postgresReScanForeignScan(ForeignScanState *node); static void postgresEndForeignScan(ForeignScanState *node); -static void postgresAddForeignUpdateTargets(Query *parsetree, +static void postgresAddForeignUpdateTargets(PlannerInfo *root, + Index rtindex, RangeTblEntry *target_rte, Relation target_relation); static List *postgresPlanForeignModify(PlannerInfo *root, @@ -1669,36 +1671,27 @@ postgresEndForeignScan(ForeignScanState *node) * Add resjunk column(s) needed for update/delete on a foreign table */ static void -postgresAddForeignUpdateTargets(Query *parsetree, +postgresAddForeignUpdateTargets(PlannerInfo *root, + Index rtindex, RangeTblEntry *target_rte, Relation target_relation) { Var *var; - const char *attrname; - TargetEntry *tle; /* * In postgres_fdw, what we need is the ctid, same as for a regular table. */ /* Make a Var representing the desired value */ - var = makeVar(parsetree->resultRelation, + var = makeVar(rtindex, SelfItemPointerAttributeNumber, TIDOID, -1, InvalidOid, 0); - /* Wrap it in a resjunk TLE with the right name ... */ - attrname = "ctid"; - - tle = makeTargetEntry((Expr *) var, - list_length(parsetree->targetList) + 1, - pstrdup(attrname), - true); - - /* ... and add it to the query's targetlist */ - parsetree->targetList = lappend(parsetree->targetList, tle); + /* Register it as a row-identity column needed by this target rel */ + add_row_identity_var(root, var, rtindex, "ctid"); } /* @@ -1886,7 +1879,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, rte, resultRelInfo, mtstate->operation, - mtstate->mt_plans[subplan_index]->plan, + outerPlanState(mtstate)->plan, query, target_attrs, values_end_len, @@ -2086,8 +2079,7 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, */ if (plan && plan->operation == CMD_UPDATE && (resultRelInfo->ri_usesFdwDirectModify || - resultRelInfo->ri_FdwState) && - resultRelInfo > mtstate->resultRelInfo + mtstate->mt_whichplan) + resultRelInfo->ri_FdwState)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot route tuples into foreign table to be updated \"%s\"", @@ -2284,6 +2276,65 @@ postgresRecheckForeignScan(ForeignScanState *node, TupleTableSlot *slot) } /* + * find_modifytable_subplan + * Helper routine for postgresPlanDirectModify to find the + * ModifyTable subplan node that scans the specified RTI. + * + * Returns NULL if the subplan couldn't be identified. That's not a fatal + * error condition, we just abandon trying to do the update directly. + */ +static ForeignScan * +find_modifytable_subplan(PlannerInfo *root, + ModifyTable *plan, + Index rtindex, + int subplan_index) +{ + Plan *subplan = outerPlan(plan); + + /* + * The cases we support are (1) the desired ForeignScan is the immediate + * child of ModifyTable, or (2) it is the subplan_index'th child of an + * Append node that is the immediate child of ModifyTable. There is no + * point in looking further down, as that would mean that local joins are + * involved, so we can't do the update directly. + * + * There could be a Result atop the Append too, acting to compute the + * UPDATE targetlist values. We ignore that here; the tlist will be + * checked by our caller. + * + * In principle we could examine all the children of the Append, but it's + * currently unlikely that the core planner would generate such a plan + * with the children out-of-order. Moreover, such a search risks costing + * O(N^2) time when there are a lot of children. + */ + if (IsA(subplan, Append)) + { + Append *appendplan = (Append *) subplan; + + if (subplan_index < list_length(appendplan->appendplans)) + subplan = (Plan *) list_nth(appendplan->appendplans, subplan_index); + } + else if (IsA(subplan, Result) && IsA(outerPlan(subplan), Append)) + { + Append *appendplan = (Append *) outerPlan(subplan); + + if (subplan_index < list_length(appendplan->appendplans)) + subplan = (Plan *) list_nth(appendplan->appendplans, subplan_index); + } + + /* Now, have we got a ForeignScan on the desired rel? */ + if (IsA(subplan, ForeignScan)) + { + ForeignScan *fscan = (ForeignScan *) subplan; + + if (bms_is_member(rtindex, fscan->fs_relids)) + return fscan; + } + + return NULL; +} + +/* * postgresPlanDirectModify * Consider a direct foreign table modification * @@ -2297,13 +2348,13 @@ postgresPlanDirectModify(PlannerInfo *root, int subplan_index) { CmdType operation = plan->operation; - Plan *subplan; RelOptInfo *foreignrel; RangeTblEntry *rte; PgFdwRelationInfo *fpinfo; Relation rel; StringInfoData sql; ForeignScan *fscan; + List *processed_tlist = NIL; List *targetAttrs = NIL; List *remote_exprs; List *params_list = NIL; @@ -2321,19 +2372,17 @@ postgresPlanDirectModify(PlannerInfo *root, return false; /* - * It's unsafe to modify a foreign table directly if there are any local - * joins needed. + * Try to locate the ForeignScan subplan that's scanning resultRelation. */ - subplan = (Plan *) list_nth(plan->plans, subplan_index); - if (!IsA(subplan, ForeignScan)) + fscan = find_modifytable_subplan(root, plan, resultRelation, subplan_index); + if (!fscan) return false; - fscan = (ForeignScan *) subplan; /* * It's unsafe to modify a foreign table directly if there are any quals * that should be evaluated locally. */ - if (subplan->qual != NIL) + if (fscan->scan.plan.qual != NIL) return false; /* Safe to fetch data about the target foreign rel */ @@ -2354,32 +2403,28 @@ postgresPlanDirectModify(PlannerInfo *root, */ if (operation == CMD_UPDATE) { - int col; + ListCell *lc, + *lc2; /* - * We transmit only columns that were explicitly targets of the - * UPDATE, so as to avoid unnecessary data transmission. + * The expressions of concern are the first N columns of the processed + * targetlist, where N is the length of the rel's update_colnos. */ - col = -1; - while ((col = bms_next_member(rte->updatedCols, col)) >= 0) + get_translated_update_targetlist(root, resultRelation, + &processed_tlist, &targetAttrs); + forboth(lc, processed_tlist, lc2, targetAttrs) { - /* bit numbers are offset by FirstLowInvalidHeapAttributeNumber */ - AttrNumber attno = col + FirstLowInvalidHeapAttributeNumber; - TargetEntry *tle; + TargetEntry *tle = lfirst_node(TargetEntry, lc); + AttrNumber attno = lfirst_int(lc2); + + /* update's new-value expressions shouldn't be resjunk */ + Assert(!tle->resjunk); if (attno <= InvalidAttrNumber) /* shouldn't happen */ elog(ERROR, "system-column update is not supported"); - tle = get_tle_by_resno(subplan->targetlist, attno); - - if (!tle) - elog(ERROR, "attribute number %d not found in subplan targetlist", - attno); - if (!is_foreign_expr(root, foreignrel, (Expr *) tle->expr)) return false; - - targetAttrs = lappend_int(targetAttrs, attno); } } @@ -2430,7 +2475,7 @@ postgresPlanDirectModify(PlannerInfo *root, case CMD_UPDATE: deparseDirectUpdateSql(&sql, root, resultRelation, rel, foreignrel, - ((Plan *) fscan)->targetlist, + processed_tlist, targetAttrs, remote_exprs, ¶ms_list, returningList, &retrieved_attrs); |