mirror of
https://github.com/postgres/postgres.git
synced 2025-08-27 07:42:10 +03:00
Rework planning and execution of UPDATE and DELETE.
This patch makes two closely related sets of changes: 1. For UPDATE, the subplan of the ModifyTable node now only delivers the new values of the changed columns (i.e., the expressions computed in the query's SET clause) plus row identity information such as CTID. ModifyTable must re-fetch the original tuple to merge in the old values of any unchanged columns. The core advantage of this is that the changed columns are uniform across all tables of an inherited or partitioned target relation, whereas the other columns might not be. A secondary advantage, when the UPDATE involves joins, is that less data needs to pass through the plan tree. The disadvantage of course is an extra fetch of each tuple to be updated. However, that seems to be very nearly free in context; even worst-case tests don't show it to add more than a couple percent to the total query cost. At some point it might be interesting to combine the re-fetch with the tuple access that ModifyTable must do anyway to mark the old tuple dead; but that would require a good deal of refactoring and it seems it wouldn't buy all that much, so this patch doesn't attempt it. 2. For inherited UPDATE/DELETE, instead of generating a separate subplan for each target relation, we now generate a single subplan that is just exactly like a SELECT's plan, then stick ModifyTable on top of that. To let ModifyTable know which target relation a given incoming row refers to, a tableoid junk column is added to the row identity information. This gets rid of the horrid hack that was inheritance_planner(), eliminating O(N^2) planning cost and memory consumption in cases where there were many unprunable target relations. Point 2 of course requires point 1, so that there is a uniform definition of the non-junk columns to be returned by the subplan. We can't insist on uniform definition of the row identity junk columns however, if we want to keep the ability to have both plain and foreign tables in a partitioning hierarchy. Since it wouldn't scale very far to have every child table have its own row identity column, this patch includes provisions to merge similar row identity columns into one column of the subplan result. In particular, we can merge the whole-row Vars typically used as row identity by FDWs into one column by pretending they are type RECORD. (It's still okay for the actual composite Datums to be labeled with the table's rowtype OID, though.) There is more that can be done to file down residual inefficiencies in this patch, but it seems to be committable now. FDW authors should note several API changes: * The argument list for AddForeignUpdateTargets() has changed, and so has the method it must use for adding junk columns to the query. Call add_row_identity_var() instead of manipulating the parse tree directly. You might want to reconsider exactly what you're adding, too. * PlanDirectModify() must now work a little harder to find the ForeignScan plan node; if the foreign table is part of a partitioning hierarchy then the ForeignScan might not be the direct child of ModifyTable. See postgres_fdw for sample code. * To check whether a relation is a target relation, it's no longer sufficient to compare its relid to root->parse->resultRelation. Instead, check it against all_result_relids or leaf_result_relids, as appropriate. Amit Langote and Tom Lane Discussion: https://postgr.es/m/CA+HiwqHpHdqdDn48yCEhynnniahH78rwcrv1rEX65-fsZGBOLQ@mail.gmail.com
This commit is contained in:
@@ -1149,7 +1149,7 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
|
||||
Var *parentvar = (Var *) lfirst(parentvars);
|
||||
Node *childvar = (Node *) lfirst(childvars);
|
||||
|
||||
if (IsA(parentvar, Var))
|
||||
if (IsA(parentvar, Var) && parentvar->varno == parentRTindex)
|
||||
{
|
||||
int pndx = parentvar->varattno - rel->min_attr;
|
||||
int32 child_width = 0;
|
||||
|
@@ -3398,7 +3398,7 @@ check_index_predicates(PlannerInfo *root, RelOptInfo *rel)
|
||||
* and pass them through to EvalPlanQual via a side channel; but for now,
|
||||
* we just don't remove implied quals at all for target relations.
|
||||
*/
|
||||
is_target_rel = (rel->relid == root->parse->resultRelation ||
|
||||
is_target_rel = (bms_is_member(rel->relid, root->all_result_relids) ||
|
||||
get_plan_rowmark(root->rowMarks, rel->relid) != NULL);
|
||||
|
||||
/*
|
||||
|
@@ -298,11 +298,12 @@ static SetOp *make_setop(SetOpCmd cmd, SetOpStrategy strategy, Plan *lefttree,
|
||||
static LockRows *make_lockrows(Plan *lefttree, List *rowMarks, int epqParam);
|
||||
static Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
|
||||
static ProjectSet *make_project_set(List *tlist, Plan *subplan);
|
||||
static ModifyTable *make_modifytable(PlannerInfo *root,
|
||||
static ModifyTable *make_modifytable(PlannerInfo *root, Plan *subplan,
|
||||
CmdType operation, bool canSetTag,
|
||||
Index nominalRelation, Index rootRelation,
|
||||
bool partColsUpdated,
|
||||
List *resultRelations, List *subplans, List *subroots,
|
||||
List *resultRelations,
|
||||
List *updateColnosLists,
|
||||
List *withCheckOptionLists, List *returningLists,
|
||||
List *rowMarks, OnConflictExpr *onconflict, int epqParam);
|
||||
static GatherMerge *create_gather_merge_plan(PlannerInfo *root,
|
||||
@@ -2292,12 +2293,7 @@ create_groupingsets_plan(PlannerInfo *root, GroupingSetsPath *best_path)
|
||||
/*
|
||||
* During setrefs.c, we'll need the grouping_map to fix up the cols lists
|
||||
* in GroupingFunc nodes. Save it for setrefs.c to use.
|
||||
*
|
||||
* This doesn't work if we're in an inheritance subtree (see notes in
|
||||
* create_modifytable_plan). Fortunately we can't be because there would
|
||||
* never be grouping in an UPDATE/DELETE; but let's Assert that.
|
||||
*/
|
||||
Assert(root->inhTargetKind == INHKIND_NONE);
|
||||
Assert(root->grouping_map == NULL);
|
||||
root->grouping_map = grouping_map;
|
||||
|
||||
@@ -2459,12 +2455,7 @@ create_minmaxagg_plan(PlannerInfo *root, MinMaxAggPath *best_path)
|
||||
* with InitPlan output params. (We can't just do that locally in the
|
||||
* MinMaxAgg node, because path nodes above here may have Agg references
|
||||
* as well.) Save the mmaggregates list to tell setrefs.c to do that.
|
||||
*
|
||||
* This doesn't work if we're in an inheritance subtree (see notes in
|
||||
* create_modifytable_plan). Fortunately we can't be because there would
|
||||
* never be aggregates in an UPDATE/DELETE; but let's Assert that.
|
||||
*/
|
||||
Assert(root->inhTargetKind == INHKIND_NONE);
|
||||
Assert(root->minmax_aggs == NIL);
|
||||
root->minmax_aggs = best_path->mmaggregates;
|
||||
|
||||
@@ -2681,46 +2672,24 @@ static ModifyTable *
|
||||
create_modifytable_plan(PlannerInfo *root, ModifyTablePath *best_path)
|
||||
{
|
||||
ModifyTable *plan;
|
||||
List *subplans = NIL;
|
||||
ListCell *subpaths,
|
||||
*subroots;
|
||||
Path *subpath = best_path->subpath;
|
||||
Plan *subplan;
|
||||
|
||||
/* Build the plan for each input path */
|
||||
forboth(subpaths, best_path->subpaths,
|
||||
subroots, best_path->subroots)
|
||||
{
|
||||
Path *subpath = (Path *) lfirst(subpaths);
|
||||
PlannerInfo *subroot = (PlannerInfo *) lfirst(subroots);
|
||||
Plan *subplan;
|
||||
/* Subplan must produce exactly the specified tlist */
|
||||
subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
|
||||
|
||||
/*
|
||||
* In an inherited UPDATE/DELETE, reference the per-child modified
|
||||
* subroot while creating Plans from Paths for the child rel. This is
|
||||
* a kluge, but otherwise it's too hard to ensure that Plan creation
|
||||
* functions (particularly in FDWs) don't depend on the contents of
|
||||
* "root" matching what they saw at Path creation time. The main
|
||||
* downside is that creation functions for Plans that might appear
|
||||
* below a ModifyTable cannot expect to modify the contents of "root"
|
||||
* and have it "stick" for subsequent processing such as setrefs.c.
|
||||
* That's not great, but it seems better than the alternative.
|
||||
*/
|
||||
subplan = create_plan_recurse(subroot, subpath, CP_EXACT_TLIST);
|
||||
|
||||
/* Transfer resname/resjunk labeling, too, to keep executor happy */
|
||||
apply_tlist_labeling(subplan->targetlist, subroot->processed_tlist);
|
||||
|
||||
subplans = lappend(subplans, subplan);
|
||||
}
|
||||
/* Transfer resname/resjunk labeling, too, to keep executor happy */
|
||||
apply_tlist_labeling(subplan->targetlist, root->processed_tlist);
|
||||
|
||||
plan = make_modifytable(root,
|
||||
subplan,
|
||||
best_path->operation,
|
||||
best_path->canSetTag,
|
||||
best_path->nominalRelation,
|
||||
best_path->rootRelation,
|
||||
best_path->partColsUpdated,
|
||||
best_path->resultRelations,
|
||||
subplans,
|
||||
best_path->subroots,
|
||||
best_path->updateColnosLists,
|
||||
best_path->withCheckOptionLists,
|
||||
best_path->returningLists,
|
||||
best_path->rowMarks,
|
||||
@@ -6916,11 +6885,12 @@ make_project_set(List *tlist,
|
||||
* Build a ModifyTable plan node
|
||||
*/
|
||||
static ModifyTable *
|
||||
make_modifytable(PlannerInfo *root,
|
||||
make_modifytable(PlannerInfo *root, Plan *subplan,
|
||||
CmdType operation, bool canSetTag,
|
||||
Index nominalRelation, Index rootRelation,
|
||||
bool partColsUpdated,
|
||||
List *resultRelations, List *subplans, List *subroots,
|
||||
List *resultRelations,
|
||||
List *updateColnosLists,
|
||||
List *withCheckOptionLists, List *returningLists,
|
||||
List *rowMarks, OnConflictExpr *onconflict, int epqParam)
|
||||
{
|
||||
@@ -6928,17 +6898,17 @@ make_modifytable(PlannerInfo *root,
|
||||
List *fdw_private_list;
|
||||
Bitmapset *direct_modify_plans;
|
||||
ListCell *lc;
|
||||
ListCell *lc2;
|
||||
int i;
|
||||
|
||||
Assert(list_length(resultRelations) == list_length(subplans));
|
||||
Assert(list_length(resultRelations) == list_length(subroots));
|
||||
Assert(operation == CMD_UPDATE ?
|
||||
list_length(resultRelations) == list_length(updateColnosLists) :
|
||||
updateColnosLists == NIL);
|
||||
Assert(withCheckOptionLists == NIL ||
|
||||
list_length(resultRelations) == list_length(withCheckOptionLists));
|
||||
Assert(returningLists == NIL ||
|
||||
list_length(resultRelations) == list_length(returningLists));
|
||||
|
||||
node->plan.lefttree = NULL;
|
||||
node->plan.lefttree = subplan;
|
||||
node->plan.righttree = NULL;
|
||||
node->plan.qual = NIL;
|
||||
/* setrefs.c will fill in the targetlist, if needed */
|
||||
@@ -6950,7 +6920,6 @@ make_modifytable(PlannerInfo *root,
|
||||
node->rootRelation = rootRelation;
|
||||
node->partColsUpdated = partColsUpdated;
|
||||
node->resultRelations = resultRelations;
|
||||
node->plans = subplans;
|
||||
if (!onconflict)
|
||||
{
|
||||
node->onConflictAction = ONCONFLICT_NONE;
|
||||
@@ -6977,6 +6946,7 @@ make_modifytable(PlannerInfo *root,
|
||||
node->exclRelRTI = onconflict->exclRelIndex;
|
||||
node->exclRelTlist = onconflict->exclRelTlist;
|
||||
}
|
||||
node->updateColnosLists = updateColnosLists;
|
||||
node->withCheckOptionLists = withCheckOptionLists;
|
||||
node->returningLists = returningLists;
|
||||
node->rowMarks = rowMarks;
|
||||
@@ -6989,10 +6959,9 @@ make_modifytable(PlannerInfo *root,
|
||||
fdw_private_list = NIL;
|
||||
direct_modify_plans = NULL;
|
||||
i = 0;
|
||||
forboth(lc, resultRelations, lc2, subroots)
|
||||
foreach(lc, resultRelations)
|
||||
{
|
||||
Index rti = lfirst_int(lc);
|
||||
PlannerInfo *subroot = lfirst_node(PlannerInfo, lc2);
|
||||
FdwRoutine *fdwroutine;
|
||||
List *fdw_private;
|
||||
bool direct_modify;
|
||||
@@ -7004,16 +6973,16 @@ make_modifytable(PlannerInfo *root,
|
||||
* so it's not a baserel; and there are also corner cases for
|
||||
* updatable views where the target rel isn't a baserel.)
|
||||
*/
|
||||
if (rti < subroot->simple_rel_array_size &&
|
||||
subroot->simple_rel_array[rti] != NULL)
|
||||
if (rti < root->simple_rel_array_size &&
|
||||
root->simple_rel_array[rti] != NULL)
|
||||
{
|
||||
RelOptInfo *resultRel = subroot->simple_rel_array[rti];
|
||||
RelOptInfo *resultRel = root->simple_rel_array[rti];
|
||||
|
||||
fdwroutine = resultRel->fdwroutine;
|
||||
}
|
||||
else
|
||||
{
|
||||
RangeTblEntry *rte = planner_rt_fetch(rti, subroot);
|
||||
RangeTblEntry *rte = planner_rt_fetch(rti, root);
|
||||
|
||||
Assert(rte->rtekind == RTE_RELATION);
|
||||
if (rte->relkind == RELKIND_FOREIGN_TABLE)
|
||||
@@ -7036,16 +7005,16 @@ make_modifytable(PlannerInfo *root,
|
||||
fdwroutine->IterateDirectModify != NULL &&
|
||||
fdwroutine->EndDirectModify != NULL &&
|
||||
withCheckOptionLists == NIL &&
|
||||
!has_row_triggers(subroot, rti, operation) &&
|
||||
!has_stored_generated_columns(subroot, rti))
|
||||
direct_modify = fdwroutine->PlanDirectModify(subroot, node, rti, i);
|
||||
!has_row_triggers(root, rti, operation) &&
|
||||
!has_stored_generated_columns(root, rti))
|
||||
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(subroot, node, rti, i);
|
||||
fdw_private = fdwroutine->PlanForeignModify(root, node, rti, i);
|
||||
else
|
||||
fdw_private = NIL;
|
||||
fdw_private_list = lappend(fdw_private_list, fdw_private);
|
||||
|
@@ -263,6 +263,13 @@ query_planner(PlannerInfo *root,
|
||||
*/
|
||||
add_other_rels_to_query(root);
|
||||
|
||||
/*
|
||||
* Distribute any UPDATE/DELETE row identity variables to the target
|
||||
* relations. This can't be done till we've finished expansion of
|
||||
* appendrels.
|
||||
*/
|
||||
distribute_row_identity_vars(root);
|
||||
|
||||
/*
|
||||
* Ready to do the primary planning.
|
||||
*/
|
||||
|
@@ -129,9 +129,7 @@ typedef struct
|
||||
/* Local functions */
|
||||
static Node *preprocess_expression(PlannerInfo *root, Node *expr, int kind);
|
||||
static void preprocess_qual_conditions(PlannerInfo *root, Node *jtnode);
|
||||
static void inheritance_planner(PlannerInfo *root);
|
||||
static void grouping_planner(PlannerInfo *root, bool inheritance_update,
|
||||
double tuple_fraction);
|
||||
static void grouping_planner(PlannerInfo *root, double tuple_fraction);
|
||||
static grouping_sets_data *preprocess_grouping_sets(PlannerInfo *root);
|
||||
static List *remap_to_groupclause_idx(List *groupClause, List *gsets,
|
||||
int *tleref_to_colnum_map);
|
||||
@@ -615,15 +613,19 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
|
||||
root->multiexpr_params = NIL;
|
||||
root->eq_classes = NIL;
|
||||
root->ec_merging_done = false;
|
||||
root->all_result_relids =
|
||||
parse->resultRelation ? bms_make_singleton(parse->resultRelation) : NULL;
|
||||
root->leaf_result_relids = NULL; /* we'll find out leaf-ness later */
|
||||
root->append_rel_list = NIL;
|
||||
root->row_identity_vars = NIL;
|
||||
root->rowMarks = NIL;
|
||||
memset(root->upper_rels, 0, sizeof(root->upper_rels));
|
||||
memset(root->upper_targets, 0, sizeof(root->upper_targets));
|
||||
root->processed_tlist = NIL;
|
||||
root->update_colnos = NIL;
|
||||
root->grouping_map = NULL;
|
||||
root->minmax_aggs = NIL;
|
||||
root->qual_security_level = 0;
|
||||
root->inhTargetKind = INHKIND_NONE;
|
||||
root->hasPseudoConstantQuals = false;
|
||||
root->hasAlternativeSubPlans = false;
|
||||
root->hasRecursion = hasRecursion;
|
||||
@@ -743,6 +745,19 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
|
||||
list_length(rte->securityQuals));
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have now verified that the query target relation is
|
||||
* non-inheriting, mark it as a leaf target.
|
||||
*/
|
||||
if (parse->resultRelation)
|
||||
{
|
||||
RangeTblEntry *rte = rt_fetch(parse->resultRelation, parse->rtable);
|
||||
|
||||
if (!rte->inh)
|
||||
root->leaf_result_relids =
|
||||
bms_make_singleton(parse->resultRelation);
|
||||
}
|
||||
|
||||
/*
|
||||
* Preprocess RowMark information. We need to do this after subquery
|
||||
* pullup, so that all base relations are present.
|
||||
@@ -999,14 +1014,9 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
|
||||
remove_useless_result_rtes(root);
|
||||
|
||||
/*
|
||||
* Do the main planning. If we have an inherited target relation, that
|
||||
* needs special processing, else go straight to grouping_planner.
|
||||
* Do the main planning.
|
||||
*/
|
||||
if (parse->resultRelation &&
|
||||
rt_fetch(parse->resultRelation, parse->rtable)->inh)
|
||||
inheritance_planner(root);
|
||||
else
|
||||
grouping_planner(root, false, tuple_fraction);
|
||||
grouping_planner(root, tuple_fraction);
|
||||
|
||||
/*
|
||||
* Capture the set of outer-level param IDs we have access to, for use in
|
||||
@@ -1180,621 +1190,6 @@ preprocess_phv_expression(PlannerInfo *root, Expr *expr)
|
||||
return (Expr *) preprocess_expression(root, (Node *) expr, EXPRKIND_PHV);
|
||||
}
|
||||
|
||||
/*
|
||||
* inheritance_planner
|
||||
* Generate Paths in the case where the result relation is an
|
||||
* inheritance set.
|
||||
*
|
||||
* We have to handle this case differently from cases where a source relation
|
||||
* is an inheritance set. Source inheritance is expanded at the bottom of the
|
||||
* plan tree (see allpaths.c), but target inheritance has to be expanded at
|
||||
* the top. The reason is that for UPDATE, each target relation needs a
|
||||
* different targetlist matching its own column set. Fortunately,
|
||||
* the UPDATE/DELETE target can never be the nullable side of an outer join,
|
||||
* so it's OK to generate the plan this way.
|
||||
*
|
||||
* Returns nothing; the useful output is in the Paths we attach to
|
||||
* the (UPPERREL_FINAL, NULL) upperrel stored in *root.
|
||||
*
|
||||
* Note that we have not done set_cheapest() on the final rel; it's convenient
|
||||
* to leave this to the caller.
|
||||
*/
|
||||
static void
|
||||
inheritance_planner(PlannerInfo *root)
|
||||
{
|
||||
Query *parse = root->parse;
|
||||
int top_parentRTindex = parse->resultRelation;
|
||||
List *select_rtable;
|
||||
List *select_appinfos;
|
||||
List *child_appinfos;
|
||||
List *old_child_rtis;
|
||||
List *new_child_rtis;
|
||||
Bitmapset *subqueryRTindexes;
|
||||
Index next_subquery_rti;
|
||||
int nominalRelation = -1;
|
||||
Index rootRelation = 0;
|
||||
List *final_rtable = NIL;
|
||||
List *final_rowmarks = NIL;
|
||||
List *final_appendrels = NIL;
|
||||
int save_rel_array_size = 0;
|
||||
RelOptInfo **save_rel_array = NULL;
|
||||
AppendRelInfo **save_append_rel_array = NULL;
|
||||
List *subpaths = NIL;
|
||||
List *subroots = NIL;
|
||||
List *resultRelations = NIL;
|
||||
List *withCheckOptionLists = NIL;
|
||||
List *returningLists = NIL;
|
||||
List *rowMarks;
|
||||
RelOptInfo *final_rel;
|
||||
ListCell *lc;
|
||||
ListCell *lc2;
|
||||
Index rti;
|
||||
RangeTblEntry *parent_rte;
|
||||
Bitmapset *parent_relids;
|
||||
Query **parent_parses;
|
||||
|
||||
/* Should only get here for UPDATE or DELETE */
|
||||
Assert(parse->commandType == CMD_UPDATE ||
|
||||
parse->commandType == CMD_DELETE);
|
||||
|
||||
/*
|
||||
* We generate a modified instance of the original Query for each target
|
||||
* relation, plan that, and put all the plans into a list that will be
|
||||
* controlled by a single ModifyTable node. All the instances share the
|
||||
* same rangetable, but each instance must have its own set of subquery
|
||||
* RTEs within the finished rangetable because (1) they are likely to get
|
||||
* scribbled on during planning, and (2) it's not inconceivable that
|
||||
* subqueries could get planned differently in different cases. We need
|
||||
* not create duplicate copies of other RTE kinds, in particular not the
|
||||
* target relations, because they don't have either of those issues. Not
|
||||
* having to duplicate the target relations is important because doing so
|
||||
* (1) would result in a rangetable of length O(N^2) for N targets, with
|
||||
* at least O(N^3) work expended here; and (2) would greatly complicate
|
||||
* management of the rowMarks list.
|
||||
*
|
||||
* To begin with, generate a bitmapset of the relids of the subquery RTEs.
|
||||
*/
|
||||
subqueryRTindexes = NULL;
|
||||
rti = 1;
|
||||
foreach(lc, parse->rtable)
|
||||
{
|
||||
RangeTblEntry *rte = lfirst_node(RangeTblEntry, lc);
|
||||
|
||||
if (rte->rtekind == RTE_SUBQUERY)
|
||||
subqueryRTindexes = bms_add_member(subqueryRTindexes, rti);
|
||||
rti++;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the parent RTE is a partitioned table, we should use that as the
|
||||
* nominal target relation, because the RTEs added for partitioned tables
|
||||
* (including the root parent) as child members of the inheritance set do
|
||||
* not appear anywhere else in the plan, so the confusion explained below
|
||||
* for non-partitioning inheritance cases is not possible.
|
||||
*/
|
||||
parent_rte = rt_fetch(top_parentRTindex, parse->rtable);
|
||||
Assert(parent_rte->inh);
|
||||
if (parent_rte->relkind == RELKIND_PARTITIONED_TABLE)
|
||||
{
|
||||
nominalRelation = top_parentRTindex;
|
||||
rootRelation = top_parentRTindex;
|
||||
}
|
||||
|
||||
/*
|
||||
* Before generating the real per-child-relation plans, do a cycle of
|
||||
* planning as though the query were a SELECT. The objective here is to
|
||||
* find out which child relations need to be processed, using the same
|
||||
* expansion and pruning logic as for a SELECT. We'll then pull out the
|
||||
* RangeTblEntry-s generated for the child rels, and make use of the
|
||||
* AppendRelInfo entries for them to guide the real planning. (This is
|
||||
* rather inefficient; we could perhaps stop short of making a full Path
|
||||
* tree. But this whole function is inefficient and slated for
|
||||
* destruction, so let's not contort query_planner for that.)
|
||||
*/
|
||||
{
|
||||
PlannerInfo *subroot;
|
||||
|
||||
/*
|
||||
* Flat-copy the PlannerInfo to prevent modification of the original.
|
||||
*/
|
||||
subroot = makeNode(PlannerInfo);
|
||||
memcpy(subroot, root, sizeof(PlannerInfo));
|
||||
|
||||
/*
|
||||
* Make a deep copy of the parsetree for this planning cycle to mess
|
||||
* around with, and change it to look like a SELECT. (Hack alert: the
|
||||
* target RTE still has updatedCols set if this is an UPDATE, so that
|
||||
* expand_partitioned_rtentry will correctly update
|
||||
* subroot->partColsUpdated.)
|
||||
*/
|
||||
subroot->parse = copyObject(root->parse);
|
||||
|
||||
subroot->parse->commandType = CMD_SELECT;
|
||||
subroot->parse->resultRelation = 0;
|
||||
|
||||
/*
|
||||
* Ensure the subroot has its own copy of the original
|
||||
* append_rel_list, since it'll be scribbled on. (Note that at this
|
||||
* point, the list only contains AppendRelInfos for flattened UNION
|
||||
* ALL subqueries.)
|
||||
*/
|
||||
subroot->append_rel_list = copyObject(root->append_rel_list);
|
||||
|
||||
/*
|
||||
* Better make a private copy of the rowMarks, too.
|
||||
*/
|
||||
subroot->rowMarks = copyObject(root->rowMarks);
|
||||
|
||||
/* There shouldn't be any OJ info to translate, as yet */
|
||||
Assert(subroot->join_info_list == NIL);
|
||||
/* and we haven't created PlaceHolderInfos, either */
|
||||
Assert(subroot->placeholder_list == NIL);
|
||||
|
||||
/* Generate Path(s) for accessing this result relation */
|
||||
grouping_planner(subroot, true, 0.0 /* retrieve all tuples */ );
|
||||
|
||||
/* Extract the info we need. */
|
||||
select_rtable = subroot->parse->rtable;
|
||||
select_appinfos = subroot->append_rel_list;
|
||||
|
||||
/*
|
||||
* We need to propagate partColsUpdated back, too. (The later
|
||||
* planning cycles will not set this because they won't run
|
||||
* expand_partitioned_rtentry for the UPDATE target.)
|
||||
*/
|
||||
root->partColsUpdated = subroot->partColsUpdated;
|
||||
}
|
||||
|
||||
/*----------
|
||||
* Since only one rangetable can exist in the final plan, we need to make
|
||||
* sure that it contains all the RTEs needed for any child plan. This is
|
||||
* complicated by the need to use separate subquery RTEs for each child.
|
||||
* We arrange the final rtable as follows:
|
||||
* 1. All original rtable entries (with their original RT indexes).
|
||||
* 2. All the relation RTEs generated for children of the target table.
|
||||
* 3. Subquery RTEs for children after the first. We need N * (K - 1)
|
||||
* RT slots for this, if there are N subqueries and K child tables.
|
||||
* 4. Additional RTEs generated during the child planning runs, such as
|
||||
* children of inheritable RTEs other than the target table.
|
||||
* We assume that each child planning run will create an identical set
|
||||
* of type-4 RTEs.
|
||||
*
|
||||
* So the next thing to do is append the type-2 RTEs (the target table's
|
||||
* children) to the original rtable. We look through select_appinfos
|
||||
* to find them.
|
||||
*
|
||||
* To identify which AppendRelInfos are relevant as we thumb through
|
||||
* select_appinfos, we need to look for both direct and indirect children
|
||||
* of top_parentRTindex, so we use a bitmap of known parent relids.
|
||||
* expand_inherited_rtentry() always processes a parent before any of that
|
||||
* parent's children, so we should see an intermediate parent before its
|
||||
* children.
|
||||
*----------
|
||||
*/
|
||||
child_appinfos = NIL;
|
||||
old_child_rtis = NIL;
|
||||
new_child_rtis = NIL;
|
||||
parent_relids = bms_make_singleton(top_parentRTindex);
|
||||
foreach(lc, select_appinfos)
|
||||
{
|
||||
AppendRelInfo *appinfo = lfirst_node(AppendRelInfo, lc);
|
||||
RangeTblEntry *child_rte;
|
||||
|
||||
/* append_rel_list contains all append rels; ignore others */
|
||||
if (!bms_is_member(appinfo->parent_relid, parent_relids))
|
||||
continue;
|
||||
|
||||
/* remember relevant AppendRelInfos for use below */
|
||||
child_appinfos = lappend(child_appinfos, appinfo);
|
||||
|
||||
/* extract RTE for this child rel */
|
||||
child_rte = rt_fetch(appinfo->child_relid, select_rtable);
|
||||
|
||||
/* and append it to the original rtable */
|
||||
parse->rtable = lappend(parse->rtable, child_rte);
|
||||
|
||||
/* remember child's index in the SELECT rtable */
|
||||
old_child_rtis = lappend_int(old_child_rtis, appinfo->child_relid);
|
||||
|
||||
/* and its new index in the final rtable */
|
||||
new_child_rtis = lappend_int(new_child_rtis, list_length(parse->rtable));
|
||||
|
||||
/* if child is itself partitioned, update parent_relids */
|
||||
if (child_rte->inh)
|
||||
{
|
||||
Assert(child_rte->relkind == RELKIND_PARTITIONED_TABLE);
|
||||
parent_relids = bms_add_member(parent_relids, appinfo->child_relid);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* It's possible that the RTIs we just assigned for the child rels in the
|
||||
* final rtable are different from what they were in the SELECT query.
|
||||
* Adjust the AppendRelInfos so that they will correctly map RT indexes to
|
||||
* the final indexes. We can do this left-to-right since no child rel's
|
||||
* final RT index could be greater than what it had in the SELECT query.
|
||||
*/
|
||||
forboth(lc, old_child_rtis, lc2, new_child_rtis)
|
||||
{
|
||||
int old_child_rti = lfirst_int(lc);
|
||||
int new_child_rti = lfirst_int(lc2);
|
||||
|
||||
if (old_child_rti == new_child_rti)
|
||||
continue; /* nothing to do */
|
||||
|
||||
Assert(old_child_rti > new_child_rti);
|
||||
|
||||
ChangeVarNodes((Node *) child_appinfos,
|
||||
old_child_rti, new_child_rti, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Now set up rangetable entries for subqueries for additional children
|
||||
* (the first child will just use the original ones). These all have to
|
||||
* look more or less real, or EXPLAIN will get unhappy; so we just make
|
||||
* them all clones of the original subqueries.
|
||||
*/
|
||||
next_subquery_rti = list_length(parse->rtable) + 1;
|
||||
if (subqueryRTindexes != NULL)
|
||||
{
|
||||
int n_children = list_length(child_appinfos);
|
||||
|
||||
while (n_children-- > 1)
|
||||
{
|
||||
int oldrti = -1;
|
||||
|
||||
while ((oldrti = bms_next_member(subqueryRTindexes, oldrti)) >= 0)
|
||||
{
|
||||
RangeTblEntry *subqrte;
|
||||
|
||||
subqrte = rt_fetch(oldrti, parse->rtable);
|
||||
parse->rtable = lappend(parse->rtable, copyObject(subqrte));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The query for each child is obtained by translating the query for its
|
||||
* immediate parent, since the AppendRelInfo data we have shows deltas
|
||||
* between parents and children. We use the parent_parses array to
|
||||
* remember the appropriate query trees. This is indexed by parent relid.
|
||||
* Since the maximum number of parents is limited by the number of RTEs in
|
||||
* the SELECT query, we use that number to allocate the array. An extra
|
||||
* entry is needed since relids start from 1.
|
||||
*/
|
||||
parent_parses = (Query **) palloc0((list_length(select_rtable) + 1) *
|
||||
sizeof(Query *));
|
||||
parent_parses[top_parentRTindex] = parse;
|
||||
|
||||
/*
|
||||
* And now we can get on with generating a plan for each child table.
|
||||
*/
|
||||
foreach(lc, child_appinfos)
|
||||
{
|
||||
AppendRelInfo *appinfo = lfirst_node(AppendRelInfo, lc);
|
||||
Index this_subquery_rti = next_subquery_rti;
|
||||
Query *parent_parse;
|
||||
PlannerInfo *subroot;
|
||||
RangeTblEntry *child_rte;
|
||||
RelOptInfo *sub_final_rel;
|
||||
Path *subpath;
|
||||
|
||||
/*
|
||||
* expand_inherited_rtentry() always processes a parent before any of
|
||||
* that parent's children, so the parent query for this relation
|
||||
* should already be available.
|
||||
*/
|
||||
parent_parse = parent_parses[appinfo->parent_relid];
|
||||
Assert(parent_parse != NULL);
|
||||
|
||||
/*
|
||||
* We need a working copy of the PlannerInfo so that we can control
|
||||
* propagation of information back to the main copy.
|
||||
*/
|
||||
subroot = makeNode(PlannerInfo);
|
||||
memcpy(subroot, root, sizeof(PlannerInfo));
|
||||
|
||||
/*
|
||||
* Generate modified query with this rel as target. We first apply
|
||||
* adjust_appendrel_attrs, which copies the Query and changes
|
||||
* references to the parent RTE to refer to the current child RTE,
|
||||
* then fool around with subquery RTEs.
|
||||
*/
|
||||
subroot->parse = (Query *)
|
||||
adjust_appendrel_attrs(subroot,
|
||||
(Node *) parent_parse,
|
||||
1, &appinfo);
|
||||
|
||||
/*
|
||||
* If there are securityQuals attached to the parent, move them to the
|
||||
* child rel (they've already been transformed properly for that).
|
||||
*/
|
||||
parent_rte = rt_fetch(appinfo->parent_relid, subroot->parse->rtable);
|
||||
child_rte = rt_fetch(appinfo->child_relid, subroot->parse->rtable);
|
||||
child_rte->securityQuals = parent_rte->securityQuals;
|
||||
parent_rte->securityQuals = NIL;
|
||||
|
||||
/*
|
||||
* HACK: setting this to a value other than INHKIND_NONE signals to
|
||||
* relation_excluded_by_constraints() to treat the result relation as
|
||||
* being an appendrel member.
|
||||
*/
|
||||
subroot->inhTargetKind =
|
||||
(rootRelation != 0) ? INHKIND_PARTITIONED : INHKIND_INHERITED;
|
||||
|
||||
/*
|
||||
* If this child is further partitioned, remember it as a parent.
|
||||
* Since a partitioned table does not have any data, we don't need to
|
||||
* create a plan for it, and we can stop processing it here. We do,
|
||||
* however, need to remember its modified PlannerInfo for use when
|
||||
* processing its children, since we'll update their varnos based on
|
||||
* the delta from immediate parent to child, not from top to child.
|
||||
*
|
||||
* Note: a very non-obvious point is that we have not yet added
|
||||
* duplicate subquery RTEs to the subroot's rtable. We mustn't,
|
||||
* because then its children would have two sets of duplicates,
|
||||
* confusing matters.
|
||||
*/
|
||||
if (child_rte->inh)
|
||||
{
|
||||
Assert(child_rte->relkind == RELKIND_PARTITIONED_TABLE);
|
||||
parent_parses[appinfo->child_relid] = subroot->parse;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the nominal target relation of the ModifyTable node if not
|
||||
* already done. If the target is a partitioned table, we already set
|
||||
* nominalRelation to refer to the partition root, above. For
|
||||
* non-partitioned inheritance cases, we'll use the first child
|
||||
* relation (even if it's excluded) as the nominal target relation.
|
||||
* Because of the way expand_inherited_rtentry works, that should be
|
||||
* the RTE representing the parent table in its role as a simple
|
||||
* member of the inheritance set.
|
||||
*
|
||||
* It would be logically cleaner to *always* use the inheritance
|
||||
* parent RTE as the nominal relation; but that RTE is not otherwise
|
||||
* referenced in the plan in the non-partitioned inheritance case.
|
||||
* Instead the duplicate child RTE created by expand_inherited_rtentry
|
||||
* is used elsewhere in the plan, so using the original parent RTE
|
||||
* would give rise to confusing use of multiple aliases in EXPLAIN
|
||||
* output for what the user will think is the "same" table. OTOH,
|
||||
* it's not a problem in the partitioned inheritance case, because
|
||||
* there is no duplicate RTE for the parent.
|
||||
*/
|
||||
if (nominalRelation < 0)
|
||||
nominalRelation = appinfo->child_relid;
|
||||
|
||||
/*
|
||||
* As above, each child plan run needs its own append_rel_list and
|
||||
* rowmarks, which should start out as pristine copies of the
|
||||
* originals. There can't be any references to UPDATE/DELETE target
|
||||
* rels in them; but there could be subquery references, which we'll
|
||||
* fix up in a moment.
|
||||
*/
|
||||
subroot->append_rel_list = copyObject(root->append_rel_list);
|
||||
subroot->rowMarks = copyObject(root->rowMarks);
|
||||
|
||||
/*
|
||||
* If this isn't the first child Query, adjust Vars and jointree
|
||||
* entries to reference the appropriate set of subquery RTEs.
|
||||
*/
|
||||
if (final_rtable != NIL && subqueryRTindexes != NULL)
|
||||
{
|
||||
int oldrti = -1;
|
||||
|
||||
while ((oldrti = bms_next_member(subqueryRTindexes, oldrti)) >= 0)
|
||||
{
|
||||
Index newrti = next_subquery_rti++;
|
||||
|
||||
ChangeVarNodes((Node *) subroot->parse, oldrti, newrti, 0);
|
||||
ChangeVarNodes((Node *) subroot->append_rel_list,
|
||||
oldrti, newrti, 0);
|
||||
ChangeVarNodes((Node *) subroot->rowMarks, oldrti, newrti, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* There shouldn't be any OJ info to translate, as yet */
|
||||
Assert(subroot->join_info_list == NIL);
|
||||
/* and we haven't created PlaceHolderInfos, either */
|
||||
Assert(subroot->placeholder_list == NIL);
|
||||
|
||||
/* Generate Path(s) for accessing this result relation */
|
||||
grouping_planner(subroot, true, 0.0 /* retrieve all tuples */ );
|
||||
|
||||
/*
|
||||
* Select cheapest path in case there's more than one. We always run
|
||||
* modification queries to conclusion, so we care only for the
|
||||
* cheapest-total path.
|
||||
*/
|
||||
sub_final_rel = fetch_upper_rel(subroot, UPPERREL_FINAL, NULL);
|
||||
set_cheapest(sub_final_rel);
|
||||
subpath = sub_final_rel->cheapest_total_path;
|
||||
|
||||
/*
|
||||
* If this child rel was excluded by constraint exclusion, exclude it
|
||||
* from the result plan.
|
||||
*/
|
||||
if (IS_DUMMY_REL(sub_final_rel))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* If this is the first non-excluded child, its post-planning rtable
|
||||
* becomes the initial contents of final_rtable; otherwise, copy its
|
||||
* modified subquery RTEs into final_rtable, to ensure we have sane
|
||||
* copies of those. Also save the first non-excluded child's version
|
||||
* of the rowmarks list; we assume all children will end up with
|
||||
* equivalent versions of that. Likewise for append_rel_list.
|
||||
*/
|
||||
if (final_rtable == NIL)
|
||||
{
|
||||
final_rtable = subroot->parse->rtable;
|
||||
final_rowmarks = subroot->rowMarks;
|
||||
final_appendrels = subroot->append_rel_list;
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert(list_length(final_rtable) ==
|
||||
list_length(subroot->parse->rtable));
|
||||
if (subqueryRTindexes != NULL)
|
||||
{
|
||||
int oldrti = -1;
|
||||
|
||||
while ((oldrti = bms_next_member(subqueryRTindexes, oldrti)) >= 0)
|
||||
{
|
||||
Index newrti = this_subquery_rti++;
|
||||
RangeTblEntry *subqrte;
|
||||
ListCell *newrticell;
|
||||
|
||||
subqrte = rt_fetch(newrti, subroot->parse->rtable);
|
||||
newrticell = list_nth_cell(final_rtable, newrti - 1);
|
||||
lfirst(newrticell) = subqrte;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to collect all the RelOptInfos from all child plans into
|
||||
* the main PlannerInfo, since setrefs.c will need them. We use the
|
||||
* last child's simple_rel_array, so we have to propagate forward the
|
||||
* RelOptInfos that were already built in previous children.
|
||||
*/
|
||||
Assert(subroot->simple_rel_array_size >= save_rel_array_size);
|
||||
for (rti = 1; rti < save_rel_array_size; rti++)
|
||||
{
|
||||
RelOptInfo *brel = save_rel_array[rti];
|
||||
|
||||
if (brel)
|
||||
subroot->simple_rel_array[rti] = brel;
|
||||
}
|
||||
save_rel_array_size = subroot->simple_rel_array_size;
|
||||
save_rel_array = subroot->simple_rel_array;
|
||||
save_append_rel_array = subroot->append_rel_array;
|
||||
|
||||
/*
|
||||
* Make sure any initplans from this rel get into the outer list. Note
|
||||
* we're effectively assuming all children generate the same
|
||||
* init_plans.
|
||||
*/
|
||||
root->init_plans = subroot->init_plans;
|
||||
|
||||
/* Build list of sub-paths */
|
||||
subpaths = lappend(subpaths, subpath);
|
||||
|
||||
/* Build list of modified subroots, too */
|
||||
subroots = lappend(subroots, subroot);
|
||||
|
||||
/* Build list of target-relation RT indexes */
|
||||
resultRelations = lappend_int(resultRelations, appinfo->child_relid);
|
||||
|
||||
/* Build lists of per-relation WCO and RETURNING targetlists */
|
||||
if (parse->withCheckOptions)
|
||||
withCheckOptionLists = lappend(withCheckOptionLists,
|
||||
subroot->parse->withCheckOptions);
|
||||
if (parse->returningList)
|
||||
returningLists = lappend(returningLists,
|
||||
subroot->parse->returningList);
|
||||
|
||||
Assert(!parse->onConflict);
|
||||
}
|
||||
|
||||
/* Result path must go into outer query's FINAL upperrel */
|
||||
final_rel = fetch_upper_rel(root, UPPERREL_FINAL, NULL);
|
||||
|
||||
/*
|
||||
* We don't currently worry about setting final_rel's consider_parallel
|
||||
* flag in this case, nor about allowing FDWs or create_upper_paths_hook
|
||||
* to get control here.
|
||||
*/
|
||||
|
||||
if (subpaths == NIL)
|
||||
{
|
||||
/*
|
||||
* We managed to exclude every child rel, so generate a dummy path
|
||||
* representing the empty set. Although it's clear that no data will
|
||||
* be updated or deleted, we will still need to have a ModifyTable
|
||||
* node so that any statement triggers are executed. (This could be
|
||||
* cleaner if we fixed nodeModifyTable.c to support zero child nodes,
|
||||
* but that probably wouldn't be a net win.)
|
||||
*/
|
||||
Path *dummy_path;
|
||||
|
||||
/* tlist processing never got done, either */
|
||||
root->processed_tlist = preprocess_targetlist(root);
|
||||
final_rel->reltarget = create_pathtarget(root, root->processed_tlist);
|
||||
|
||||
/* Make a dummy path, cf set_dummy_rel_pathlist() */
|
||||
dummy_path = (Path *) create_append_path(NULL, final_rel, NIL, NIL,
|
||||
NIL, NULL, 0, false,
|
||||
-1);
|
||||
|
||||
/* These lists must be nonempty to make a valid ModifyTable node */
|
||||
subpaths = list_make1(dummy_path);
|
||||
subroots = list_make1(root);
|
||||
resultRelations = list_make1_int(parse->resultRelation);
|
||||
if (parse->withCheckOptions)
|
||||
withCheckOptionLists = list_make1(parse->withCheckOptions);
|
||||
if (parse->returningList)
|
||||
returningLists = list_make1(parse->returningList);
|
||||
/* Disable tuple routing, too, just to be safe */
|
||||
root->partColsUpdated = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Put back the final adjusted rtable into the original copy of the
|
||||
* Query. (We mustn't do this if we found no non-excluded children,
|
||||
* since we never saved an adjusted rtable at all.)
|
||||
*/
|
||||
parse->rtable = final_rtable;
|
||||
root->simple_rel_array_size = save_rel_array_size;
|
||||
root->simple_rel_array = save_rel_array;
|
||||
root->append_rel_array = save_append_rel_array;
|
||||
|
||||
/* Must reconstruct original's simple_rte_array, too */
|
||||
root->simple_rte_array = (RangeTblEntry **)
|
||||
palloc0((list_length(final_rtable) + 1) * sizeof(RangeTblEntry *));
|
||||
rti = 1;
|
||||
foreach(lc, final_rtable)
|
||||
{
|
||||
RangeTblEntry *rte = lfirst_node(RangeTblEntry, lc);
|
||||
|
||||
root->simple_rte_array[rti++] = rte;
|
||||
}
|
||||
|
||||
/* Put back adjusted rowmarks and appendrels, too */
|
||||
root->rowMarks = final_rowmarks;
|
||||
root->append_rel_list = final_appendrels;
|
||||
}
|
||||
|
||||
/*
|
||||
* If there was a FOR [KEY] UPDATE/SHARE clause, the LockRows node will
|
||||
* have dealt with fetching non-locked marked rows, else we need to have
|
||||
* ModifyTable do that.
|
||||
*/
|
||||
if (parse->rowMarks)
|
||||
rowMarks = NIL;
|
||||
else
|
||||
rowMarks = root->rowMarks;
|
||||
|
||||
/* Create Path representing a ModifyTable to do the UPDATE/DELETE work */
|
||||
add_path(final_rel, (Path *)
|
||||
create_modifytable_path(root, final_rel,
|
||||
parse->commandType,
|
||||
parse->canSetTag,
|
||||
nominalRelation,
|
||||
rootRelation,
|
||||
root->partColsUpdated,
|
||||
resultRelations,
|
||||
subpaths,
|
||||
subroots,
|
||||
withCheckOptionLists,
|
||||
returningLists,
|
||||
rowMarks,
|
||||
NULL,
|
||||
assign_special_exec_param(root)));
|
||||
}
|
||||
|
||||
/*--------------------
|
||||
* grouping_planner
|
||||
* Perform planning steps related to grouping, aggregation, etc.
|
||||
@@ -1802,11 +1197,6 @@ inheritance_planner(PlannerInfo *root)
|
||||
* This function adds all required top-level processing to the scan/join
|
||||
* Path(s) produced by query_planner.
|
||||
*
|
||||
* If inheritance_update is true, we're being called from inheritance_planner
|
||||
* and should not include a ModifyTable step in the resulting Path(s).
|
||||
* (inheritance_planner will create a single ModifyTable node covering all the
|
||||
* target tables.)
|
||||
*
|
||||
* tuple_fraction is the fraction of tuples we expect will be retrieved.
|
||||
* tuple_fraction is interpreted as follows:
|
||||
* 0: expect all tuples to be retrieved (normal case)
|
||||
@@ -1824,8 +1214,7 @@ inheritance_planner(PlannerInfo *root)
|
||||
*--------------------
|
||||
*/
|
||||
static void
|
||||
grouping_planner(PlannerInfo *root, bool inheritance_update,
|
||||
double tuple_fraction)
|
||||
grouping_planner(PlannerInfo *root, double tuple_fraction)
|
||||
{
|
||||
Query *parse = root->parse;
|
||||
int64 offset_est = 0;
|
||||
@@ -1969,7 +1358,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
|
||||
* that we can transfer its decoration (resnames etc) to the topmost
|
||||
* tlist of the finished Plan. This is kept in processed_tlist.
|
||||
*/
|
||||
root->processed_tlist = preprocess_targetlist(root);
|
||||
preprocess_targetlist(root);
|
||||
|
||||
/*
|
||||
* Mark all the aggregates with resolved aggtranstypes, and detect
|
||||
@@ -2307,16 +1696,117 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
|
||||
}
|
||||
|
||||
/*
|
||||
* If this is an INSERT/UPDATE/DELETE, and we're not being called from
|
||||
* inheritance_planner, add the ModifyTable node.
|
||||
* If this is an INSERT/UPDATE/DELETE, add the ModifyTable node.
|
||||
*/
|
||||
if (parse->commandType != CMD_SELECT && !inheritance_update)
|
||||
if (parse->commandType != CMD_SELECT)
|
||||
{
|
||||
Index rootRelation;
|
||||
List *withCheckOptionLists;
|
||||
List *returningLists;
|
||||
List *resultRelations = NIL;
|
||||
List *updateColnosLists = NIL;
|
||||
List *withCheckOptionLists = NIL;
|
||||
List *returningLists = NIL;
|
||||
List *rowMarks;
|
||||
|
||||
if (bms_membership(root->all_result_relids) == BMS_MULTIPLE)
|
||||
{
|
||||
/* Inherited UPDATE/DELETE */
|
||||
RelOptInfo *top_result_rel = find_base_rel(root,
|
||||
parse->resultRelation);
|
||||
int resultRelation = -1;
|
||||
|
||||
/* Add only leaf children to ModifyTable. */
|
||||
while ((resultRelation = bms_next_member(root->leaf_result_relids,
|
||||
resultRelation)) >= 0)
|
||||
{
|
||||
RelOptInfo *this_result_rel = find_base_rel(root,
|
||||
resultRelation);
|
||||
|
||||
/*
|
||||
* Also exclude any leaf rels that have turned dummy since
|
||||
* being added to the list, for example, by being excluded
|
||||
* by constraint exclusion.
|
||||
*/
|
||||
if (IS_DUMMY_REL(this_result_rel))
|
||||
continue;
|
||||
|
||||
/* Build per-target-rel lists needed by ModifyTable */
|
||||
resultRelations = lappend_int(resultRelations,
|
||||
resultRelation);
|
||||
if (parse->commandType == CMD_UPDATE)
|
||||
{
|
||||
List *update_colnos = root->update_colnos;
|
||||
|
||||
if (this_result_rel != top_result_rel)
|
||||
update_colnos =
|
||||
adjust_inherited_attnums_multilevel(root,
|
||||
update_colnos,
|
||||
this_result_rel->relid,
|
||||
top_result_rel->relid);
|
||||
updateColnosLists = lappend(updateColnosLists,
|
||||
update_colnos);
|
||||
}
|
||||
if (parse->withCheckOptions)
|
||||
{
|
||||
List *withCheckOptions = parse->withCheckOptions;
|
||||
|
||||
if (this_result_rel != top_result_rel)
|
||||
withCheckOptions = (List *)
|
||||
adjust_appendrel_attrs_multilevel(root,
|
||||
(Node *) withCheckOptions,
|
||||
this_result_rel->relids,
|
||||
top_result_rel->relids);
|
||||
withCheckOptionLists = lappend(withCheckOptionLists,
|
||||
withCheckOptions);
|
||||
}
|
||||
if (parse->returningList)
|
||||
{
|
||||
List *returningList = parse->returningList;
|
||||
|
||||
if (this_result_rel != top_result_rel)
|
||||
returningList = (List *)
|
||||
adjust_appendrel_attrs_multilevel(root,
|
||||
(Node *) returningList,
|
||||
this_result_rel->relids,
|
||||
top_result_rel->relids);
|
||||
returningLists = lappend(returningLists,
|
||||
returningList);
|
||||
}
|
||||
}
|
||||
|
||||
if (resultRelations == NIL)
|
||||
{
|
||||
/*
|
||||
* We managed to exclude every child rel, so generate a
|
||||
* dummy one-relation plan using info for the top target
|
||||
* rel (even though that may not be a leaf target).
|
||||
* Although it's clear that no data will be updated or
|
||||
* deleted, we still need to have a ModifyTable node so
|
||||
* that any statement triggers will be executed. (This
|
||||
* could be cleaner if we fixed nodeModifyTable.c to allow
|
||||
* zero target relations, but that probably wouldn't be a
|
||||
* net win.)
|
||||
*/
|
||||
resultRelations = list_make1_int(parse->resultRelation);
|
||||
if (parse->commandType == CMD_UPDATE)
|
||||
updateColnosLists = list_make1(root->update_colnos);
|
||||
if (parse->withCheckOptions)
|
||||
withCheckOptionLists = list_make1(parse->withCheckOptions);
|
||||
if (parse->returningList)
|
||||
returningLists = list_make1(parse->returningList);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Single-relation INSERT/UPDATE/DELETE. */
|
||||
resultRelations = list_make1_int(parse->resultRelation);
|
||||
if (parse->commandType == CMD_UPDATE)
|
||||
updateColnosLists = list_make1(root->update_colnos);
|
||||
if (parse->withCheckOptions)
|
||||
withCheckOptionLists = list_make1(parse->withCheckOptions);
|
||||
if (parse->returningList)
|
||||
returningLists = list_make1(parse->returningList);
|
||||
}
|
||||
|
||||
/*
|
||||
* If target is a partition root table, we need to mark the
|
||||
* ModifyTable node appropriately for that.
|
||||
@@ -2327,20 +1817,6 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
|
||||
else
|
||||
rootRelation = 0;
|
||||
|
||||
/*
|
||||
* Set up the WITH CHECK OPTION and RETURNING lists-of-lists, if
|
||||
* needed.
|
||||
*/
|
||||
if (parse->withCheckOptions)
|
||||
withCheckOptionLists = list_make1(parse->withCheckOptions);
|
||||
else
|
||||
withCheckOptionLists = NIL;
|
||||
|
||||
if (parse->returningList)
|
||||
returningLists = list_make1(parse->returningList);
|
||||
else
|
||||
returningLists = NIL;
|
||||
|
||||
/*
|
||||
* If there was a FOR [KEY] UPDATE/SHARE clause, the LockRows node
|
||||
* will have dealt with fetching non-locked marked rows, else we
|
||||
@@ -2353,14 +1829,14 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
|
||||
|
||||
path = (Path *)
|
||||
create_modifytable_path(root, final_rel,
|
||||
path,
|
||||
parse->commandType,
|
||||
parse->canSetTag,
|
||||
parse->resultRelation,
|
||||
rootRelation,
|
||||
false,
|
||||
list_make1_int(parse->resultRelation),
|
||||
list_make1(path),
|
||||
list_make1(root),
|
||||
root->partColsUpdated,
|
||||
resultRelations,
|
||||
updateColnosLists,
|
||||
withCheckOptionLists,
|
||||
returningLists,
|
||||
rowMarks,
|
||||
|
@@ -868,6 +868,29 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
|
||||
set_upper_references(root, plan, rtoffset);
|
||||
else
|
||||
{
|
||||
/*
|
||||
* The tlist of a childless Result could contain
|
||||
* unresolved ROWID_VAR Vars, in case it's representing a
|
||||
* target relation which is completely empty because of
|
||||
* constraint exclusion. Replace any such Vars by null
|
||||
* constants, as though they'd been resolved for a leaf
|
||||
* scan node that doesn't support them. We could have
|
||||
* fix_scan_expr do this, but since the case is only
|
||||
* expected to occur here, it seems safer to special-case
|
||||
* it here and keep the assertions that ROWID_VARs
|
||||
* shouldn't be seen by fix_scan_expr.
|
||||
*/
|
||||
foreach(l, splan->plan.targetlist)
|
||||
{
|
||||
TargetEntry *tle = (TargetEntry *) lfirst(l);
|
||||
Var *var = (Var *) tle->expr;
|
||||
|
||||
if (var && IsA(var, Var) && var->varno == ROWID_VAR)
|
||||
tle->expr = (Expr *) makeNullConst(var->vartype,
|
||||
var->vartypmod,
|
||||
var->varcollid);
|
||||
}
|
||||
|
||||
splan->plan.targetlist =
|
||||
fix_scan_list(root, splan->plan.targetlist,
|
||||
rtoffset, NUM_EXEC_TLIST(plan));
|
||||
@@ -897,23 +920,20 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
|
||||
if (splan->returningLists)
|
||||
{
|
||||
List *newRL = NIL;
|
||||
Plan *subplan = outerPlan(splan);
|
||||
ListCell *lcrl,
|
||||
*lcrr,
|
||||
*lcp;
|
||||
*lcrr;
|
||||
|
||||
/*
|
||||
* Pass each per-subplan returningList through
|
||||
* Pass each per-resultrel returningList through
|
||||
* set_returning_clause_references().
|
||||
*/
|
||||
Assert(list_length(splan->returningLists) == list_length(splan->resultRelations));
|
||||
Assert(list_length(splan->returningLists) == list_length(splan->plans));
|
||||
forthree(lcrl, splan->returningLists,
|
||||
lcrr, splan->resultRelations,
|
||||
lcp, splan->plans)
|
||||
forboth(lcrl, splan->returningLists,
|
||||
lcrr, splan->resultRelations)
|
||||
{
|
||||
List *rlist = (List *) lfirst(lcrl);
|
||||
Index resultrel = lfirst_int(lcrr);
|
||||
Plan *subplan = (Plan *) lfirst(lcp);
|
||||
|
||||
rlist = set_returning_clause_references(root,
|
||||
rlist,
|
||||
@@ -983,12 +1003,6 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
|
||||
rc->rti += rtoffset;
|
||||
rc->prti += rtoffset;
|
||||
}
|
||||
foreach(l, splan->plans)
|
||||
{
|
||||
lfirst(l) = set_plan_refs(root,
|
||||
(Plan *) lfirst(l),
|
||||
rtoffset);
|
||||
}
|
||||
|
||||
/*
|
||||
* Append this ModifyTable node's final result relation RT
|
||||
@@ -1792,6 +1806,13 @@ fix_alternative_subplan(PlannerInfo *root, AlternativeSubPlan *asplan,
|
||||
* choosing the best implementation for AlternativeSubPlans,
|
||||
* looking up operator opcode info for OpExpr and related nodes,
|
||||
* and adding OIDs from regclass Const nodes into root->glob->relationOids.
|
||||
*
|
||||
* 'node': the expression to be modified
|
||||
* 'rtoffset': how much to increment varnos by
|
||||
* 'num_exec': estimated number of executions of expression
|
||||
*
|
||||
* The expression tree is either copied-and-modified, or modified in-place
|
||||
* if that seems safe.
|
||||
*/
|
||||
static Node *
|
||||
fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset, double num_exec)
|
||||
@@ -1840,11 +1861,12 @@ fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context)
|
||||
Assert(var->varlevelsup == 0);
|
||||
|
||||
/*
|
||||
* We should not see any Vars marked INNER_VAR or OUTER_VAR. But an
|
||||
* indexqual expression could contain INDEX_VAR Vars.
|
||||
* We should not see Vars marked INNER_VAR, OUTER_VAR, or ROWID_VAR.
|
||||
* But an indexqual expression could contain INDEX_VAR Vars.
|
||||
*/
|
||||
Assert(var->varno != INNER_VAR);
|
||||
Assert(var->varno != OUTER_VAR);
|
||||
Assert(var->varno != ROWID_VAR);
|
||||
if (!IS_SPECIAL_VARNO(var->varno))
|
||||
var->varno += context->rtoffset;
|
||||
if (var->varnosyn > 0)
|
||||
@@ -1907,6 +1929,7 @@ fix_scan_expr_walker(Node *node, fix_scan_expr_context *context)
|
||||
{
|
||||
if (node == NULL)
|
||||
return false;
|
||||
Assert(!(IsA(node, Var) && ((Var *) node)->varno == ROWID_VAR));
|
||||
Assert(!IsA(node, PlaceHolderVar));
|
||||
Assert(!IsA(node, AlternativeSubPlan));
|
||||
fix_expr_common(context->root, node);
|
||||
|
@@ -2535,7 +2535,6 @@ finalize_plan(PlannerInfo *root, Plan *plan,
|
||||
case T_ModifyTable:
|
||||
{
|
||||
ModifyTable *mtplan = (ModifyTable *) plan;
|
||||
ListCell *l;
|
||||
|
||||
/* Force descendant scan nodes to reference epqParam */
|
||||
locally_added_param = mtplan->epqParam;
|
||||
@@ -2550,16 +2549,6 @@ finalize_plan(PlannerInfo *root, Plan *plan,
|
||||
finalize_primnode((Node *) mtplan->onConflictWhere,
|
||||
&context);
|
||||
/* exclRelTlist contains only Vars, doesn't need examination */
|
||||
foreach(l, mtplan->plans)
|
||||
{
|
||||
context.paramids =
|
||||
bms_add_members(context.paramids,
|
||||
finalize_plan(root,
|
||||
(Plan *) lfirst(l),
|
||||
gather_param,
|
||||
valid_params,
|
||||
scan_params));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
@@ -920,15 +920,18 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
|
||||
subroot->multiexpr_params = NIL;
|
||||
subroot->eq_classes = NIL;
|
||||
subroot->ec_merging_done = false;
|
||||
subroot->all_result_relids = NULL;
|
||||
subroot->leaf_result_relids = NULL;
|
||||
subroot->append_rel_list = NIL;
|
||||
subroot->row_identity_vars = NIL;
|
||||
subroot->rowMarks = NIL;
|
||||
memset(subroot->upper_rels, 0, sizeof(subroot->upper_rels));
|
||||
memset(subroot->upper_targets, 0, sizeof(subroot->upper_targets));
|
||||
subroot->processed_tlist = NIL;
|
||||
subroot->update_colnos = NIL;
|
||||
subroot->grouping_map = NULL;
|
||||
subroot->minmax_aggs = NIL;
|
||||
subroot->qual_security_level = 0;
|
||||
subroot->inhTargetKind = INHKIND_NONE;
|
||||
subroot->hasRecursion = false;
|
||||
subroot->wt_param_id = -1;
|
||||
subroot->non_recursive_path = NULL;
|
||||
|
@@ -3,30 +3,26 @@
|
||||
* preptlist.c
|
||||
* Routines to preprocess the parse tree target list
|
||||
*
|
||||
* For INSERT and UPDATE queries, the targetlist must contain an entry for
|
||||
* each attribute of the target relation in the correct order. For UPDATE and
|
||||
* DELETE queries, it must also contain junk tlist entries needed to allow the
|
||||
* executor to identify the rows to be updated or deleted. For all query
|
||||
* types, we may need to add junk tlist entries for Vars used in the RETURNING
|
||||
* list and row ID information needed for SELECT FOR UPDATE locking and/or
|
||||
* EvalPlanQual checking.
|
||||
* For an INSERT, the targetlist must contain an entry for each attribute of
|
||||
* the target relation in the correct order.
|
||||
*
|
||||
* For an UPDATE, the targetlist just contains the expressions for the new
|
||||
* column values.
|
||||
*
|
||||
* For UPDATE and DELETE queries, the targetlist must also contain "junk"
|
||||
* tlist entries needed to allow the executor to identify the rows to be
|
||||
* updated or deleted; for example, the ctid of a heap row. (The planner
|
||||
* adds these; they're not in what we receive from the planner/rewriter.)
|
||||
*
|
||||
* For all query types, there can be additional junk tlist entries, such as
|
||||
* sort keys, Vars needed for a RETURNING list, and row ID information needed
|
||||
* for SELECT FOR UPDATE locking and/or EvalPlanQual checking.
|
||||
*
|
||||
* The query rewrite phase also does preprocessing of the targetlist (see
|
||||
* rewriteTargetListIU). The division of labor between here and there is
|
||||
* partially historical, but it's not entirely arbitrary. In particular,
|
||||
* consider an UPDATE across an inheritance tree. What rewriteTargetListIU
|
||||
* does need be done only once (because it depends only on the properties of
|
||||
* the parent relation). What's done here has to be done over again for each
|
||||
* child relation, because it depends on the properties of the child, which
|
||||
* might be of a different relation type, or have more columns and/or a
|
||||
* different column order than the parent.
|
||||
*
|
||||
* The fact that rewriteTargetListIU sorts non-resjunk tlist entries by column
|
||||
* position, which expand_targetlist depends on, violates the above comment
|
||||
* because the sorting is only valid for the parent relation. In inherited
|
||||
* UPDATE cases, adjust_inherited_tlist runs in between to take care of fixing
|
||||
* the tlists for child tables to keep expand_targetlist happy. We do it like
|
||||
* that because it's faster in typical non-inherited cases.
|
||||
* partially historical, but it's not entirely arbitrary. The stuff done
|
||||
* here is closely connected to physical access to tables, whereas the
|
||||
* rewriter's work is more concerned with SQL semantics.
|
||||
*
|
||||
*
|
||||
* Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
|
||||
@@ -40,18 +36,17 @@
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include "access/sysattr.h"
|
||||
#include "access/table.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "optimizer/appendinfo.h"
|
||||
#include "optimizer/optimizer.h"
|
||||
#include "optimizer/prep.h"
|
||||
#include "optimizer/tlist.h"
|
||||
#include "parser/parse_coerce.h"
|
||||
#include "parser/parsetree.h"
|
||||
#include "rewrite/rewriteHandler.h"
|
||||
#include "utils/rel.h"
|
||||
|
||||
static List *extract_update_colnos(List *tlist);
|
||||
static List *expand_targetlist(List *tlist, int command_type,
|
||||
Index result_relation, Relation rel);
|
||||
|
||||
@@ -60,12 +55,15 @@ static List *expand_targetlist(List *tlist, int command_type,
|
||||
* preprocess_targetlist
|
||||
* Driver for preprocessing the parse tree targetlist.
|
||||
*
|
||||
* Returns the new targetlist.
|
||||
* The preprocessed targetlist is returned in root->processed_tlist.
|
||||
* Also, if this is an UPDATE, we return a list of target column numbers
|
||||
* in root->update_colnos. (Resnos in processed_tlist will be consecutive,
|
||||
* so do not look at that to find out which columns are targets!)
|
||||
*
|
||||
* As a side effect, if there's an ON CONFLICT UPDATE clause, its targetlist
|
||||
* is also preprocessed (and updated in-place).
|
||||
*/
|
||||
List *
|
||||
void
|
||||
preprocess_targetlist(PlannerInfo *root)
|
||||
{
|
||||
Query *parse = root->parse;
|
||||
@@ -99,23 +97,38 @@ preprocess_targetlist(PlannerInfo *root)
|
||||
Assert(command_type == CMD_SELECT);
|
||||
|
||||
/*
|
||||
* For UPDATE/DELETE, add any junk column(s) needed to allow the executor
|
||||
* to identify the rows to be updated or deleted. Note that this step
|
||||
* scribbles on parse->targetList, which is not very desirable, but we
|
||||
* keep it that way to avoid changing APIs used by FDWs.
|
||||
*/
|
||||
if (command_type == CMD_UPDATE || command_type == CMD_DELETE)
|
||||
rewriteTargetListUD(parse, target_rte, target_relation);
|
||||
|
||||
/*
|
||||
* for heap_form_tuple to work, the targetlist must match the exact order
|
||||
* of the attributes. We also need to fill in any missing attributes. -ay
|
||||
* 10/94
|
||||
* In an INSERT, the executor expects the targetlist to match the exact
|
||||
* order of the target table's attributes, including entries for
|
||||
* attributes not mentioned in the source query.
|
||||
*
|
||||
* In an UPDATE, we don't rearrange the tlist order, but we need to make a
|
||||
* separate list of the target attribute numbers, in tlist order, and then
|
||||
* renumber the processed_tlist entries to be consecutive.
|
||||
*/
|
||||
tlist = parse->targetList;
|
||||
if (command_type == CMD_INSERT || command_type == CMD_UPDATE)
|
||||
if (command_type == CMD_INSERT)
|
||||
tlist = expand_targetlist(tlist, command_type,
|
||||
result_relation, target_relation);
|
||||
else if (command_type == CMD_UPDATE)
|
||||
root->update_colnos = extract_update_colnos(tlist);
|
||||
|
||||
/*
|
||||
* For non-inherited UPDATE/DELETE, register any junk column(s) needed to
|
||||
* allow the executor to identify the rows to be updated or deleted. In
|
||||
* the inheritance case, we do nothing now, leaving this to be dealt with
|
||||
* when expand_inherited_rtentry() makes the leaf target relations. (But
|
||||
* there might not be any leaf target relations, in which case we must do
|
||||
* this in distribute_row_identity_vars().)
|
||||
*/
|
||||
if ((command_type == CMD_UPDATE || command_type == CMD_DELETE) &&
|
||||
!target_rte->inh)
|
||||
{
|
||||
/* row-identity logic expects to add stuff to processed_tlist */
|
||||
root->processed_tlist = tlist;
|
||||
add_row_identity_columns(root, result_relation,
|
||||
target_rte, target_relation);
|
||||
tlist = root->processed_tlist;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add necessary junk columns for rowmarked rels. These values are needed
|
||||
@@ -123,6 +136,14 @@ preprocess_targetlist(PlannerInfo *root)
|
||||
* rechecking. See comments for PlanRowMark in plannodes.h. If you
|
||||
* change this stanza, see also expand_inherited_rtentry(), which has to
|
||||
* be able to add on junk columns equivalent to these.
|
||||
*
|
||||
* (Someday it might be useful to fold these resjunk columns into the
|
||||
* row-identity-column management used for UPDATE/DELETE. Today is not
|
||||
* that day, however. One notable issue is that it seems important that
|
||||
* the whole-row Vars made here use the real table rowtype, not RECORD, so
|
||||
* that conversion to/from child relations' rowtypes will happen. Also,
|
||||
* since these entries don't potentially bloat with more and more child
|
||||
* relations, there's not really much need for column sharing.)
|
||||
*/
|
||||
foreach(lc, root->rowMarks)
|
||||
{
|
||||
@@ -222,6 +243,8 @@ preprocess_targetlist(PlannerInfo *root)
|
||||
list_free(vars);
|
||||
}
|
||||
|
||||
root->processed_tlist = tlist;
|
||||
|
||||
/*
|
||||
* If there's an ON CONFLICT UPDATE clause, preprocess its targetlist too
|
||||
* while we have the relation open.
|
||||
@@ -235,8 +258,35 @@ preprocess_targetlist(PlannerInfo *root)
|
||||
|
||||
if (target_relation)
|
||||
table_close(target_relation, NoLock);
|
||||
}
|
||||
|
||||
return tlist;
|
||||
/*
|
||||
* extract_update_colnos
|
||||
* Extract a list of the target-table column numbers that
|
||||
* an UPDATE's targetlist wants to assign to, then renumber.
|
||||
*
|
||||
* The convention in the parser and rewriter is that the resnos in an
|
||||
* UPDATE's non-resjunk TLE entries are the target column numbers
|
||||
* to assign to. Here, we extract that info into a separate list, and
|
||||
* then convert the tlist to the sequential-numbering convention that's
|
||||
* used by all other query types.
|
||||
*/
|
||||
static List *
|
||||
extract_update_colnos(List *tlist)
|
||||
{
|
||||
List *update_colnos = NIL;
|
||||
AttrNumber nextresno = 1;
|
||||
ListCell *lc;
|
||||
|
||||
foreach(lc, tlist)
|
||||
{
|
||||
TargetEntry *tle = (TargetEntry *) lfirst(lc);
|
||||
|
||||
if (!tle->resjunk)
|
||||
update_colnos = lappend_int(update_colnos, tle->resno);
|
||||
tle->resno = nextresno++;
|
||||
}
|
||||
return update_colnos;
|
||||
}
|
||||
|
||||
|
||||
@@ -251,6 +301,10 @@ preprocess_targetlist(PlannerInfo *root)
|
||||
* Given a target list as generated by the parser and a result relation,
|
||||
* add targetlist entries for any missing attributes, and ensure the
|
||||
* non-junk attributes appear in proper field order.
|
||||
*
|
||||
* command_type is a bit of an archaism now: it's CMD_INSERT when we're
|
||||
* processing an INSERT, all right, but the only other use of this function
|
||||
* is for ON CONFLICT UPDATE tlists, for which command_type is CMD_UPDATE.
|
||||
*/
|
||||
static List *
|
||||
expand_targetlist(List *tlist, int command_type,
|
||||
|
@@ -15,9 +15,12 @@
|
||||
#include "postgres.h"
|
||||
|
||||
#include "access/htup_details.h"
|
||||
#include "access/table.h"
|
||||
#include "foreign/fdwapi.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "nodes/nodeFuncs.h"
|
||||
#include "optimizer/appendinfo.h"
|
||||
#include "optimizer/pathnode.h"
|
||||
#include "parser/parsetree.h"
|
||||
#include "utils/lsyscache.h"
|
||||
#include "utils/rel.h"
|
||||
@@ -37,8 +40,6 @@ static void make_inh_translation_list(Relation oldrelation,
|
||||
AppendRelInfo *appinfo);
|
||||
static Node *adjust_appendrel_attrs_mutator(Node *node,
|
||||
adjust_appendrel_attrs_context *context);
|
||||
static List *adjust_inherited_tlist(List *tlist,
|
||||
AppendRelInfo *context);
|
||||
|
||||
|
||||
/*
|
||||
@@ -194,7 +195,6 @@ Node *
|
||||
adjust_appendrel_attrs(PlannerInfo *root, Node *node, int nappinfos,
|
||||
AppendRelInfo **appinfos)
|
||||
{
|
||||
Node *result;
|
||||
adjust_appendrel_attrs_context context;
|
||||
|
||||
context.root = root;
|
||||
@@ -204,40 +204,10 @@ adjust_appendrel_attrs(PlannerInfo *root, Node *node, int nappinfos,
|
||||
/* If there's nothing to adjust, don't call this function. */
|
||||
Assert(nappinfos >= 1 && appinfos != NULL);
|
||||
|
||||
/*
|
||||
* Must be prepared to start with a Query or a bare expression tree.
|
||||
*/
|
||||
if (node && IsA(node, Query))
|
||||
{
|
||||
Query *newnode;
|
||||
int cnt;
|
||||
/* Should never be translating a Query tree. */
|
||||
Assert(node == NULL || !IsA(node, Query));
|
||||
|
||||
newnode = query_tree_mutator((Query *) node,
|
||||
adjust_appendrel_attrs_mutator,
|
||||
(void *) &context,
|
||||
QTW_IGNORE_RC_SUBQUERIES);
|
||||
for (cnt = 0; cnt < nappinfos; cnt++)
|
||||
{
|
||||
AppendRelInfo *appinfo = appinfos[cnt];
|
||||
|
||||
if (newnode->resultRelation == appinfo->parent_relid)
|
||||
{
|
||||
newnode->resultRelation = appinfo->child_relid;
|
||||
/* Fix tlist resnos too, if it's inherited UPDATE */
|
||||
if (newnode->commandType == CMD_UPDATE)
|
||||
newnode->targetList =
|
||||
adjust_inherited_tlist(newnode->targetList,
|
||||
appinfo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
result = (Node *) newnode;
|
||||
}
|
||||
else
|
||||
result = adjust_appendrel_attrs_mutator(node, &context);
|
||||
|
||||
return result;
|
||||
return adjust_appendrel_attrs_mutator(node, &context);
|
||||
}
|
||||
|
||||
static Node *
|
||||
@@ -343,6 +313,57 @@ adjust_appendrel_attrs_mutator(Node *node,
|
||||
}
|
||||
/* system attributes don't need any other translation */
|
||||
}
|
||||
else if (var->varno == ROWID_VAR)
|
||||
{
|
||||
/*
|
||||
* If it's a ROWID_VAR placeholder, see if we've reached a leaf
|
||||
* target rel, for which we can translate the Var to a specific
|
||||
* instantiation. We should never be asked to translate to a set
|
||||
* of relids containing more than one leaf target rel, so the
|
||||
* answer will be unique. If we're still considering non-leaf
|
||||
* inheritance levels, return the ROWID_VAR Var as-is.
|
||||
*/
|
||||
Relids leaf_result_relids = context->root->leaf_result_relids;
|
||||
Index leaf_relid = 0;
|
||||
|
||||
for (cnt = 0; cnt < nappinfos; cnt++)
|
||||
{
|
||||
if (bms_is_member(appinfos[cnt]->child_relid,
|
||||
leaf_result_relids))
|
||||
{
|
||||
if (leaf_relid)
|
||||
elog(ERROR, "cannot translate to multiple leaf relids");
|
||||
leaf_relid = appinfos[cnt]->child_relid;
|
||||
}
|
||||
}
|
||||
|
||||
if (leaf_relid)
|
||||
{
|
||||
RowIdentityVarInfo *ridinfo = (RowIdentityVarInfo *)
|
||||
list_nth(context->root->row_identity_vars, var->varattno - 1);
|
||||
|
||||
if (bms_is_member(leaf_relid, ridinfo->rowidrels))
|
||||
{
|
||||
/* Substitute the Var given in the RowIdentityVarInfo */
|
||||
var = copyObject(ridinfo->rowidvar);
|
||||
/* ... but use the correct relid */
|
||||
var->varno = leaf_relid;
|
||||
/* varnosyn in the RowIdentityVarInfo is probably wrong */
|
||||
var->varnosyn = 0;
|
||||
var->varattnosyn = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* This leaf rel can't return the desired value, so
|
||||
* substitute a NULL of the correct type.
|
||||
*/
|
||||
return (Node *) makeNullConst(var->vartype,
|
||||
var->vartypmod,
|
||||
var->varcollid);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (Node *) var;
|
||||
}
|
||||
if (IsA(node, CurrentOfExpr))
|
||||
@@ -361,44 +382,6 @@ adjust_appendrel_attrs_mutator(Node *node,
|
||||
}
|
||||
return (Node *) cexpr;
|
||||
}
|
||||
if (IsA(node, RangeTblRef))
|
||||
{
|
||||
RangeTblRef *rtr = (RangeTblRef *) copyObject(node);
|
||||
|
||||
for (cnt = 0; cnt < nappinfos; cnt++)
|
||||
{
|
||||
AppendRelInfo *appinfo = appinfos[cnt];
|
||||
|
||||
if (rtr->rtindex == appinfo->parent_relid)
|
||||
{
|
||||
rtr->rtindex = appinfo->child_relid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (Node *) rtr;
|
||||
}
|
||||
if (IsA(node, JoinExpr))
|
||||
{
|
||||
/* Copy the JoinExpr node with correct mutation of subnodes */
|
||||
JoinExpr *j;
|
||||
AppendRelInfo *appinfo;
|
||||
|
||||
j = (JoinExpr *) expression_tree_mutator(node,
|
||||
adjust_appendrel_attrs_mutator,
|
||||
(void *) context);
|
||||
/* now fix JoinExpr's rtindex (probably never happens) */
|
||||
for (cnt = 0; cnt < nappinfos; cnt++)
|
||||
{
|
||||
appinfo = appinfos[cnt];
|
||||
|
||||
if (j->rtindex == appinfo->parent_relid)
|
||||
{
|
||||
j->rtindex = appinfo->child_relid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (Node *) j;
|
||||
}
|
||||
if (IsA(node, PlaceHolderVar))
|
||||
{
|
||||
/* Copy the PlaceHolderVar node with correct mutation of subnodes */
|
||||
@@ -486,6 +469,9 @@ adjust_appendrel_attrs_mutator(Node *node,
|
||||
*/
|
||||
Assert(!IsA(node, SubLink));
|
||||
Assert(!IsA(node, Query));
|
||||
/* We should never see these Query substructures, either. */
|
||||
Assert(!IsA(node, RangeTblRef));
|
||||
Assert(!IsA(node, JoinExpr));
|
||||
|
||||
return expression_tree_mutator(node, adjust_appendrel_attrs_mutator,
|
||||
(void *) context);
|
||||
@@ -621,100 +607,101 @@ adjust_child_relids_multilevel(PlannerInfo *root, Relids relids,
|
||||
}
|
||||
|
||||
/*
|
||||
* Adjust the targetlist entries of an inherited UPDATE operation
|
||||
*
|
||||
* The expressions have already been fixed, but we have to make sure that
|
||||
* the target resnos match the child table (they may not, in the case of
|
||||
* a column that was added after-the-fact by ALTER TABLE). In some cases
|
||||
* this can force us to re-order the tlist to preserve resno ordering.
|
||||
* (We do all this work in special cases so that preptlist.c is fast for
|
||||
* the typical case.)
|
||||
*
|
||||
* The given tlist has already been through expression_tree_mutator;
|
||||
* therefore the TargetEntry nodes are fresh copies that it's okay to
|
||||
* scribble on.
|
||||
*
|
||||
* Note that this is not needed for INSERT because INSERT isn't inheritable.
|
||||
* adjust_inherited_attnums
|
||||
* Translate an integer list of attribute numbers from parent to child.
|
||||
*/
|
||||
static List *
|
||||
adjust_inherited_tlist(List *tlist, AppendRelInfo *context)
|
||||
List *
|
||||
adjust_inherited_attnums(List *attnums, AppendRelInfo *context)
|
||||
{
|
||||
bool changed_it = false;
|
||||
ListCell *tl;
|
||||
List *new_tlist;
|
||||
bool more;
|
||||
int attrno;
|
||||
List *result = NIL;
|
||||
ListCell *lc;
|
||||
|
||||
/* This should only happen for an inheritance case, not UNION ALL */
|
||||
Assert(OidIsValid(context->parent_reloid));
|
||||
|
||||
/* Scan tlist and update resnos to match attnums of child rel */
|
||||
foreach(tl, tlist)
|
||||
/* Look up each attribute in the AppendRelInfo's translated_vars list */
|
||||
foreach(lc, attnums)
|
||||
{
|
||||
TargetEntry *tle = (TargetEntry *) lfirst(tl);
|
||||
AttrNumber parentattno = lfirst_int(lc);
|
||||
Var *childvar;
|
||||
|
||||
if (tle->resjunk)
|
||||
continue; /* ignore junk items */
|
||||
|
||||
/* Look up the translation of this column: it must be a Var */
|
||||
if (tle->resno <= 0 ||
|
||||
tle->resno > list_length(context->translated_vars))
|
||||
if (parentattno <= 0 ||
|
||||
parentattno > list_length(context->translated_vars))
|
||||
elog(ERROR, "attribute %d of relation \"%s\" does not exist",
|
||||
tle->resno, get_rel_name(context->parent_reloid));
|
||||
childvar = (Var *) list_nth(context->translated_vars, tle->resno - 1);
|
||||
parentattno, get_rel_name(context->parent_reloid));
|
||||
childvar = (Var *) list_nth(context->translated_vars, parentattno - 1);
|
||||
if (childvar == NULL || !IsA(childvar, Var))
|
||||
elog(ERROR, "attribute %d of relation \"%s\" does not exist",
|
||||
tle->resno, get_rel_name(context->parent_reloid));
|
||||
parentattno, get_rel_name(context->parent_reloid));
|
||||
|
||||
if (tle->resno != childvar->varattno)
|
||||
{
|
||||
tle->resno = childvar->varattno;
|
||||
changed_it = true;
|
||||
}
|
||||
result = lappend_int(result, childvar->varattno);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we changed anything, re-sort the tlist by resno, and make sure
|
||||
* resjunk entries have resnos above the last real resno. The sort
|
||||
* algorithm is a bit stupid, but for such a seldom-taken path, small is
|
||||
* probably better than fast.
|
||||
*/
|
||||
if (!changed_it)
|
||||
return tlist;
|
||||
/*
|
||||
* adjust_inherited_attnums_multilevel
|
||||
* As above, but traverse multiple inheritance levels as needed.
|
||||
*/
|
||||
List *
|
||||
adjust_inherited_attnums_multilevel(PlannerInfo *root, List *attnums,
|
||||
Index child_relid, Index top_parent_relid)
|
||||
{
|
||||
AppendRelInfo *appinfo = root->append_rel_array[child_relid];
|
||||
|
||||
new_tlist = NIL;
|
||||
more = true;
|
||||
for (attrno = 1; more; attrno++)
|
||||
if (!appinfo)
|
||||
elog(ERROR, "child rel %d not found in append_rel_array", child_relid);
|
||||
|
||||
/* Recurse if immediate parent is not the top parent. */
|
||||
if (appinfo->parent_relid != top_parent_relid)
|
||||
attnums = adjust_inherited_attnums_multilevel(root, attnums,
|
||||
appinfo->parent_relid,
|
||||
top_parent_relid);
|
||||
|
||||
/* Now translate for this child */
|
||||
return adjust_inherited_attnums(attnums, appinfo);
|
||||
}
|
||||
|
||||
/*
|
||||
* get_translated_update_targetlist
|
||||
* Get the processed_tlist of an UPDATE query, translated as needed to
|
||||
* match a child target relation.
|
||||
*
|
||||
* Optionally also return the list of target column numbers translated
|
||||
* to this target relation. (The resnos in processed_tlist MUST NOT be
|
||||
* relied on for this purpose.)
|
||||
*/
|
||||
void
|
||||
get_translated_update_targetlist(PlannerInfo *root, Index relid,
|
||||
List **processed_tlist, List **update_colnos)
|
||||
{
|
||||
/* This is pretty meaningless for commands other than UPDATE. */
|
||||
Assert(root->parse->commandType == CMD_UPDATE);
|
||||
if (relid == root->parse->resultRelation)
|
||||
{
|
||||
more = false;
|
||||
foreach(tl, tlist)
|
||||
{
|
||||
TargetEntry *tle = (TargetEntry *) lfirst(tl);
|
||||
|
||||
if (tle->resjunk)
|
||||
continue; /* ignore junk items */
|
||||
|
||||
if (tle->resno == attrno)
|
||||
new_tlist = lappend(new_tlist, tle);
|
||||
else if (tle->resno > attrno)
|
||||
more = true;
|
||||
}
|
||||
/*
|
||||
* Non-inheritance case, so it's easy. The caller might be expecting
|
||||
* a tree it can scribble on, though, so copy.
|
||||
*/
|
||||
*processed_tlist = copyObject(root->processed_tlist);
|
||||
if (update_colnos)
|
||||
*update_colnos = copyObject(root->update_colnos);
|
||||
}
|
||||
|
||||
foreach(tl, tlist)
|
||||
else
|
||||
{
|
||||
TargetEntry *tle = (TargetEntry *) lfirst(tl);
|
||||
|
||||
if (!tle->resjunk)
|
||||
continue; /* here, ignore non-junk items */
|
||||
|
||||
tle->resno = attrno;
|
||||
new_tlist = lappend(new_tlist, tle);
|
||||
attrno++;
|
||||
Assert(bms_is_member(relid, root->all_result_relids));
|
||||
*processed_tlist = (List *)
|
||||
adjust_appendrel_attrs_multilevel(root,
|
||||
(Node *) root->processed_tlist,
|
||||
bms_make_singleton(relid),
|
||||
bms_make_singleton(root->parse->resultRelation));
|
||||
if (update_colnos)
|
||||
*update_colnos =
|
||||
adjust_inherited_attnums_multilevel(root, root->update_colnos,
|
||||
relid,
|
||||
root->parse->resultRelation);
|
||||
}
|
||||
|
||||
return new_tlist;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -746,3 +733,270 @@ find_appinfos_by_relids(PlannerInfo *root, Relids relids, int *nappinfos)
|
||||
}
|
||||
return appinfos;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* ROW-IDENTITY VARIABLE MANAGEMENT
|
||||
*
|
||||
* This code lacks a good home, perhaps. We choose to keep it here because
|
||||
* adjust_appendrel_attrs_mutator() is its principal co-conspirator. That
|
||||
* function does most of what is needed to expand ROWID_VAR Vars into the
|
||||
* right things.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* add_row_identity_var
|
||||
* Register a row-identity column to be used in UPDATE/DELETE.
|
||||
*
|
||||
* The Var must be equal(), aside from varno, to any other row-identity
|
||||
* column with the same rowid_name. Thus, for example, "wholerow"
|
||||
* row identities had better use vartype == RECORDOID.
|
||||
*
|
||||
* rtindex is currently redundant with rowid_var->varno, but we specify
|
||||
* it as a separate parameter in case this is ever generalized to support
|
||||
* non-Var expressions. (We could reasonably handle expressions over
|
||||
* Vars of the specified rtindex, but for now that seems unnecessary.)
|
||||
*/
|
||||
void
|
||||
add_row_identity_var(PlannerInfo *root, Var *orig_var,
|
||||
Index rtindex, const char *rowid_name)
|
||||
{
|
||||
TargetEntry *tle;
|
||||
Var *rowid_var;
|
||||
RowIdentityVarInfo *ridinfo;
|
||||
ListCell *lc;
|
||||
|
||||
/* For now, the argument must be just a Var of the given rtindex */
|
||||
Assert(IsA(orig_var, Var));
|
||||
Assert(orig_var->varno == rtindex);
|
||||
Assert(orig_var->varlevelsup == 0);
|
||||
|
||||
/*
|
||||
* If we're doing non-inherited UPDATE/DELETE, there's little need for
|
||||
* ROWID_VAR shenanigans. Just shove the presented Var into the
|
||||
* processed_tlist, and we're done.
|
||||
*/
|
||||
if (rtindex == root->parse->resultRelation)
|
||||
{
|
||||
tle = makeTargetEntry((Expr *) orig_var,
|
||||
list_length(root->processed_tlist) + 1,
|
||||
pstrdup(rowid_name),
|
||||
true);
|
||||
root->processed_tlist = lappend(root->processed_tlist, tle);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Otherwise, rtindex should reference a leaf target relation that's being
|
||||
* added to the query during expand_inherited_rtentry().
|
||||
*/
|
||||
Assert(bms_is_member(rtindex, root->leaf_result_relids));
|
||||
Assert(root->append_rel_array[rtindex] != NULL);
|
||||
|
||||
/*
|
||||
* We have to find a matching RowIdentityVarInfo, or make one if there is
|
||||
* none. To allow using equal() to match the vars, change the varno to
|
||||
* ROWID_VAR, leaving all else alone.
|
||||
*/
|
||||
rowid_var = copyObject(orig_var);
|
||||
/* This could eventually become ChangeVarNodes() */
|
||||
rowid_var->varno = ROWID_VAR;
|
||||
|
||||
/* Look for an existing row-id column of the same name */
|
||||
foreach(lc, root->row_identity_vars)
|
||||
{
|
||||
ridinfo = (RowIdentityVarInfo *) lfirst(lc);
|
||||
if (strcmp(rowid_name, ridinfo->rowidname) != 0)
|
||||
continue;
|
||||
if (equal(rowid_var, ridinfo->rowidvar))
|
||||
{
|
||||
/* Found a match; we need only record that rtindex needs it too */
|
||||
ridinfo->rowidrels = bms_add_member(ridinfo->rowidrels, rtindex);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Ooops, can't handle this */
|
||||
elog(ERROR, "conflicting uses of row-identity name \"%s\"",
|
||||
rowid_name);
|
||||
}
|
||||
}
|
||||
|
||||
/* No request yet, so add a new RowIdentityVarInfo */
|
||||
ridinfo = makeNode(RowIdentityVarInfo);
|
||||
ridinfo->rowidvar = copyObject(rowid_var);
|
||||
/* for the moment, estimate width using just the datatype info */
|
||||
ridinfo->rowidwidth = get_typavgwidth(exprType((Node *) rowid_var),
|
||||
exprTypmod((Node *) rowid_var));
|
||||
ridinfo->rowidname = pstrdup(rowid_name);
|
||||
ridinfo->rowidrels = bms_make_singleton(rtindex);
|
||||
|
||||
root->row_identity_vars = lappend(root->row_identity_vars, ridinfo);
|
||||
|
||||
/* Change rowid_var into a reference to this row_identity_vars entry */
|
||||
rowid_var->varattno = list_length(root->row_identity_vars);
|
||||
|
||||
/* Push the ROWID_VAR reference variable into processed_tlist */
|
||||
tle = makeTargetEntry((Expr *) rowid_var,
|
||||
list_length(root->processed_tlist) + 1,
|
||||
pstrdup(rowid_name),
|
||||
true);
|
||||
root->processed_tlist = lappend(root->processed_tlist, tle);
|
||||
}
|
||||
|
||||
/*
|
||||
* add_row_identity_columns
|
||||
*
|
||||
* This function adds the row identity columns needed by the core code.
|
||||
* FDWs might call add_row_identity_var() for themselves to add nonstandard
|
||||
* columns. (Duplicate requests are fine.)
|
||||
*/
|
||||
void
|
||||
add_row_identity_columns(PlannerInfo *root, Index rtindex,
|
||||
RangeTblEntry *target_rte,
|
||||
Relation target_relation)
|
||||
{
|
||||
CmdType commandType = root->parse->commandType;
|
||||
char relkind = target_relation->rd_rel->relkind;
|
||||
Var *var;
|
||||
|
||||
Assert(commandType == CMD_UPDATE || commandType == CMD_DELETE);
|
||||
|
||||
if (relkind == RELKIND_RELATION ||
|
||||
relkind == RELKIND_MATVIEW ||
|
||||
relkind == RELKIND_PARTITIONED_TABLE)
|
||||
{
|
||||
/*
|
||||
* Emit CTID so that executor can find the row to update or delete.
|
||||
*/
|
||||
var = makeVar(rtindex,
|
||||
SelfItemPointerAttributeNumber,
|
||||
TIDOID,
|
||||
-1,
|
||||
InvalidOid,
|
||||
0);
|
||||
add_row_identity_var(root, var, rtindex, "ctid");
|
||||
}
|
||||
else if (relkind == RELKIND_FOREIGN_TABLE)
|
||||
{
|
||||
/*
|
||||
* Let the foreign table's FDW add whatever junk TLEs it wants.
|
||||
*/
|
||||
FdwRoutine *fdwroutine;
|
||||
|
||||
fdwroutine = GetFdwRoutineForRelation(target_relation, false);
|
||||
|
||||
if (fdwroutine->AddForeignUpdateTargets != NULL)
|
||||
fdwroutine->AddForeignUpdateTargets(root, rtindex,
|
||||
target_rte, target_relation);
|
||||
|
||||
/*
|
||||
* For UPDATE, we need to make the FDW fetch unchanged columns by
|
||||
* asking it to fetch a whole-row Var. That's because the top-level
|
||||
* targetlist only contains entries for changed columns, but
|
||||
* ExecUpdate will need to build the complete new tuple. (Actually,
|
||||
* we only really need this in UPDATEs that are not pushed to the
|
||||
* remote side, but it's hard to tell if that will be the case at the
|
||||
* point when this function is called.)
|
||||
*
|
||||
* We will also need the whole row if there are any row triggers, so
|
||||
* that the executor will have the "old" row to pass to the trigger.
|
||||
* Alas, this misses system columns.
|
||||
*/
|
||||
if (commandType == CMD_UPDATE ||
|
||||
(target_relation->trigdesc &&
|
||||
(target_relation->trigdesc->trig_delete_after_row ||
|
||||
target_relation->trigdesc->trig_delete_before_row)))
|
||||
{
|
||||
var = makeVar(rtindex,
|
||||
InvalidAttrNumber,
|
||||
RECORDOID,
|
||||
-1,
|
||||
InvalidOid,
|
||||
0);
|
||||
add_row_identity_var(root, var, rtindex, "wholerow");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* distribute_row_identity_vars
|
||||
*
|
||||
* After we have finished identifying all the row identity columns
|
||||
* needed by an inherited UPDATE/DELETE query, make sure that these
|
||||
* columns will be generated by all the target relations.
|
||||
*
|
||||
* This is more or less like what build_base_rel_tlists() does,
|
||||
* except that it would not understand what to do with ROWID_VAR Vars.
|
||||
* Since that function runs before inheritance relations are expanded,
|
||||
* it will never see any such Vars anyway.
|
||||
*/
|
||||
void
|
||||
distribute_row_identity_vars(PlannerInfo *root)
|
||||
{
|
||||
Query *parse = root->parse;
|
||||
int result_relation = parse->resultRelation;
|
||||
RangeTblEntry *target_rte;
|
||||
RelOptInfo *target_rel;
|
||||
ListCell *lc;
|
||||
|
||||
/* There's nothing to do if this isn't an inherited UPDATE/DELETE. */
|
||||
if (parse->commandType != CMD_UPDATE && parse->commandType != CMD_DELETE)
|
||||
{
|
||||
Assert(root->row_identity_vars == NIL);
|
||||
return;
|
||||
}
|
||||
target_rte = rt_fetch(result_relation, parse->rtable);
|
||||
if (!target_rte->inh)
|
||||
{
|
||||
Assert(root->row_identity_vars == NIL);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ordinarily, we expect that leaf result relation(s) will have added some
|
||||
* ROWID_VAR Vars to the query. However, it's possible that constraint
|
||||
* exclusion suppressed every leaf relation. The executor will get upset
|
||||
* if the plan has no row identity columns at all, even though it will
|
||||
* certainly process no rows. Handle this edge case by re-opening the top
|
||||
* result relation and adding the row identity columns it would have used,
|
||||
* as preprocess_targetlist() would have done if it weren't marked "inh".
|
||||
* (This is a bit ugly, but it seems better to confine the ugliness and
|
||||
* extra cycles to this unusual corner case.) We needn't worry about
|
||||
* fixing the rel's reltarget, as that won't affect the finished plan.
|
||||
*/
|
||||
if (root->row_identity_vars == NIL)
|
||||
{
|
||||
Relation target_relation;
|
||||
|
||||
target_relation = table_open(target_rte->relid, NoLock);
|
||||
add_row_identity_columns(root, result_relation,
|
||||
target_rte, target_relation);
|
||||
table_close(target_relation, NoLock);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Dig through the processed_tlist to find the ROWID_VAR reference Vars,
|
||||
* and forcibly copy them into the reltarget list of the topmost target
|
||||
* relation. That's sufficient because they'll be copied to the
|
||||
* individual leaf target rels (with appropriate translation) later,
|
||||
* during appendrel expansion --- see set_append_rel_size().
|
||||
*/
|
||||
target_rel = find_base_rel(root, result_relation);
|
||||
|
||||
foreach(lc, root->processed_tlist)
|
||||
{
|
||||
TargetEntry *tle = lfirst(lc);
|
||||
Var *var = (Var *) tle->expr;
|
||||
|
||||
if (var && IsA(var, Var) && var->varno == ROWID_VAR)
|
||||
{
|
||||
target_rel->reltarget->exprs =
|
||||
lappend(target_rel->reltarget->exprs, copyObject(var));
|
||||
/* reltarget cost and width will be computed later */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -219,6 +219,10 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel,
|
||||
* targetlist and update parent rel's reltarget. This should match what
|
||||
* preprocess_targetlist() would have added if the mark types had been
|
||||
* requested originally.
|
||||
*
|
||||
* (Someday it might be useful to fold these resjunk columns into the
|
||||
* row-identity-column management used for UPDATE/DELETE. Today is not
|
||||
* that day, however.)
|
||||
*/
|
||||
if (oldrc)
|
||||
{
|
||||
@@ -585,6 +589,46 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
|
||||
|
||||
root->rowMarks = lappend(root->rowMarks, childrc);
|
||||
}
|
||||
|
||||
/*
|
||||
* If we are creating a child of the query target relation (only possible
|
||||
* in UPDATE/DELETE), add it to all_result_relids, as well as
|
||||
* leaf_result_relids if appropriate, and make sure that we generate
|
||||
* required row-identity data.
|
||||
*/
|
||||
if (bms_is_member(parentRTindex, root->all_result_relids))
|
||||
{
|
||||
/* OK, record the child as a result rel too. */
|
||||
root->all_result_relids = bms_add_member(root->all_result_relids,
|
||||
childRTindex);
|
||||
|
||||
/* Non-leaf partitions don't need any row identity info. */
|
||||
if (childrte->relkind != RELKIND_PARTITIONED_TABLE)
|
||||
{
|
||||
Var *rrvar;
|
||||
|
||||
root->leaf_result_relids = bms_add_member(root->leaf_result_relids,
|
||||
childRTindex);
|
||||
|
||||
/*
|
||||
* If we have any child target relations, assume they all need to
|
||||
* generate a junk "tableoid" column. (If only one child survives
|
||||
* pruning, we wouldn't really need this, but it's not worth
|
||||
* thrashing about to avoid it.)
|
||||
*/
|
||||
rrvar = makeVar(childRTindex,
|
||||
TableOidAttributeNumber,
|
||||
OIDOID,
|
||||
-1,
|
||||
InvalidOid,
|
||||
0);
|
||||
add_row_identity_var(root, rrvar, childRTindex, "tableoid");
|
||||
|
||||
/* Register any row-identity columns needed by this child. */
|
||||
add_row_identity_columns(root, childRTindex,
|
||||
childrte, childrel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@@ -3540,6 +3540,7 @@ create_lockrows_path(PlannerInfo *root, RelOptInfo *rel,
|
||||
* Creates a pathnode that represents performing INSERT/UPDATE/DELETE mods
|
||||
*
|
||||
* 'rel' is the parent relation associated with the result
|
||||
* 'subpath' is a Path producing source data
|
||||
* 'operation' is the operation type
|
||||
* 'canSetTag' is true if we set the command tag/es_processed
|
||||
* 'nominalRelation' is the parent RT index for use of EXPLAIN
|
||||
@@ -3547,8 +3548,8 @@ create_lockrows_path(PlannerInfo *root, RelOptInfo *rel,
|
||||
* 'partColsUpdated' is true if any partitioning columns are being updated,
|
||||
* either from the target relation or a descendent partitioned table.
|
||||
* 'resultRelations' is an integer list of actual RT indexes of target rel(s)
|
||||
* 'subpaths' is a list of Path(s) producing source data (one per rel)
|
||||
* 'subroots' is a list of PlannerInfo structs (one per rel)
|
||||
* 'updateColnosLists' is a list of UPDATE target column number lists
|
||||
* (one sublist per rel); or NIL if not an UPDATE
|
||||
* 'withCheckOptionLists' is a list of WCO lists (one per rel)
|
||||
* 'returningLists' is a list of RETURNING tlists (one per rel)
|
||||
* 'rowMarks' is a list of PlanRowMarks (non-locking only)
|
||||
@@ -3557,21 +3558,21 @@ create_lockrows_path(PlannerInfo *root, RelOptInfo *rel,
|
||||
*/
|
||||
ModifyTablePath *
|
||||
create_modifytable_path(PlannerInfo *root, RelOptInfo *rel,
|
||||
Path *subpath,
|
||||
CmdType operation, bool canSetTag,
|
||||
Index nominalRelation, Index rootRelation,
|
||||
bool partColsUpdated,
|
||||
List *resultRelations, List *subpaths,
|
||||
List *subroots,
|
||||
List *resultRelations,
|
||||
List *updateColnosLists,
|
||||
List *withCheckOptionLists, List *returningLists,
|
||||
List *rowMarks, OnConflictExpr *onconflict,
|
||||
int epqParam)
|
||||
{
|
||||
ModifyTablePath *pathnode = makeNode(ModifyTablePath);
|
||||
double total_size;
|
||||
ListCell *lc;
|
||||
|
||||
Assert(list_length(resultRelations) == list_length(subpaths));
|
||||
Assert(list_length(resultRelations) == list_length(subroots));
|
||||
Assert(operation == CMD_UPDATE ?
|
||||
list_length(resultRelations) == list_length(updateColnosLists) :
|
||||
updateColnosLists == NIL);
|
||||
Assert(withCheckOptionLists == NIL ||
|
||||
list_length(resultRelations) == list_length(withCheckOptionLists));
|
||||
Assert(returningLists == NIL ||
|
||||
@@ -3589,7 +3590,7 @@ create_modifytable_path(PlannerInfo *root, RelOptInfo *rel,
|
||||
pathnode->path.pathkeys = NIL;
|
||||
|
||||
/*
|
||||
* Compute cost & rowcount as sum of subpath costs & rowcounts.
|
||||
* Compute cost & rowcount as subpath cost & rowcount (if RETURNING)
|
||||
*
|
||||
* Currently, we don't charge anything extra for the actual table
|
||||
* modification work, nor for the WITH CHECK OPTIONS or RETURNING
|
||||
@@ -3598,42 +3599,34 @@ create_modifytable_path(PlannerInfo *root, RelOptInfo *rel,
|
||||
* costs to change any higher-level planning choices. But we might want
|
||||
* to make it look better sometime.
|
||||
*/
|
||||
pathnode->path.startup_cost = 0;
|
||||
pathnode->path.total_cost = 0;
|
||||
pathnode->path.rows = 0;
|
||||
total_size = 0;
|
||||
foreach(lc, subpaths)
|
||||
pathnode->path.startup_cost = subpath->startup_cost;
|
||||
pathnode->path.total_cost = subpath->total_cost;
|
||||
if (returningLists != NIL)
|
||||
{
|
||||
Path *subpath = (Path *) lfirst(lc);
|
||||
pathnode->path.rows = subpath->rows;
|
||||
|
||||
if (lc == list_head(subpaths)) /* first node? */
|
||||
pathnode->path.startup_cost = subpath->startup_cost;
|
||||
pathnode->path.total_cost += subpath->total_cost;
|
||||
if (returningLists != NIL)
|
||||
{
|
||||
pathnode->path.rows += subpath->rows;
|
||||
total_size += subpath->pathtarget->width * subpath->rows;
|
||||
}
|
||||
/*
|
||||
* Set width to match the subpath output. XXX this is totally wrong:
|
||||
* we should return an average of the RETURNING tlist widths. But
|
||||
* it's what happened historically, and improving it is a task for
|
||||
* another day. (Again, it's mostly window dressing.)
|
||||
*/
|
||||
pathnode->path.pathtarget->width = subpath->pathtarget->width;
|
||||
}
|
||||
else
|
||||
{
|
||||
pathnode->path.rows = 0;
|
||||
pathnode->path.pathtarget->width = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set width to the average width of the subpath outputs. XXX this is
|
||||
* totally wrong: we should return an average of the RETURNING tlist
|
||||
* widths. But it's what happened historically, and improving it is a task
|
||||
* for another day.
|
||||
*/
|
||||
if (pathnode->path.rows > 0)
|
||||
total_size /= pathnode->path.rows;
|
||||
pathnode->path.pathtarget->width = rint(total_size);
|
||||
|
||||
pathnode->subpath = subpath;
|
||||
pathnode->operation = operation;
|
||||
pathnode->canSetTag = canSetTag;
|
||||
pathnode->nominalRelation = nominalRelation;
|
||||
pathnode->rootRelation = rootRelation;
|
||||
pathnode->partColsUpdated = partColsUpdated;
|
||||
pathnode->resultRelations = resultRelations;
|
||||
pathnode->subpaths = subpaths;
|
||||
pathnode->subroots = subroots;
|
||||
pathnode->updateColnosLists = updateColnosLists;
|
||||
pathnode->withCheckOptionLists = withCheckOptionLists;
|
||||
pathnode->returningLists = returningLists;
|
||||
pathnode->rowMarks = rowMarks;
|
||||
|
@@ -1515,18 +1515,11 @@ relation_excluded_by_constraints(PlannerInfo *root,
|
||||
|
||||
/*
|
||||
* When constraint_exclusion is set to 'partition' we only handle
|
||||
* appendrel members. Normally, they are RELOPT_OTHER_MEMBER_REL
|
||||
* relations, but we also consider inherited target relations as
|
||||
* appendrel members for the purposes of constraint exclusion
|
||||
* (since, indeed, they were appendrel members earlier in
|
||||
* inheritance_planner).
|
||||
*
|
||||
* In both cases, partition pruning was already applied, so there
|
||||
* is no need to consider the rel's partition constraints here.
|
||||
* appendrel members. Partition pruning has already been applied,
|
||||
* so there is no need to consider the rel's partition constraints
|
||||
* here.
|
||||
*/
|
||||
if (rel->reloptkind == RELOPT_OTHER_MEMBER_REL ||
|
||||
(rel->relid == root->parse->resultRelation &&
|
||||
root->inhTargetKind != INHKIND_NONE))
|
||||
if (rel->reloptkind == RELOPT_OTHER_MEMBER_REL)
|
||||
break; /* appendrel member, so process it */
|
||||
return false;
|
||||
|
||||
@@ -1539,9 +1532,7 @@ relation_excluded_by_constraints(PlannerInfo *root,
|
||||
* its partition constraints haven't been considered yet, so
|
||||
* include them in the processing here.
|
||||
*/
|
||||
if (rel->reloptkind == RELOPT_BASEREL &&
|
||||
!(rel->relid == root->parse->resultRelation &&
|
||||
root->inhTargetKind != INHKIND_NONE))
|
||||
if (rel->reloptkind == RELOPT_BASEREL)
|
||||
include_partition = true;
|
||||
break; /* always try to exclude */
|
||||
}
|
||||
|
@@ -977,8 +977,6 @@ build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
|
||||
foreach(vars, input_rel->reltarget->exprs)
|
||||
{
|
||||
Var *var = (Var *) lfirst(vars);
|
||||
RelOptInfo *baserel;
|
||||
int ndx;
|
||||
|
||||
/*
|
||||
* Ignore PlaceHolderVars in the input tlists; we'll make our own
|
||||
@@ -996,17 +994,35 @@ build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
|
||||
elog(ERROR, "unexpected node type in rel targetlist: %d",
|
||||
(int) nodeTag(var));
|
||||
|
||||
/* Get the Var's original base rel */
|
||||
baserel = find_base_rel(root, var->varno);
|
||||
|
||||
/* Is it still needed above this joinrel? */
|
||||
ndx = var->varattno - baserel->min_attr;
|
||||
if (bms_nonempty_difference(baserel->attr_needed[ndx], relids))
|
||||
if (var->varno == ROWID_VAR)
|
||||
{
|
||||
/* Yup, add it to the output */
|
||||
joinrel->reltarget->exprs = lappend(joinrel->reltarget->exprs, var);
|
||||
/* UPDATE/DELETE row identity vars are always needed */
|
||||
RowIdentityVarInfo *ridinfo = (RowIdentityVarInfo *)
|
||||
list_nth(root->row_identity_vars, var->varattno - 1);
|
||||
|
||||
joinrel->reltarget->exprs = lappend(joinrel->reltarget->exprs,
|
||||
var);
|
||||
/* Vars have cost zero, so no need to adjust reltarget->cost */
|
||||
joinrel->reltarget->width += baserel->attr_widths[ndx];
|
||||
joinrel->reltarget->width += ridinfo->rowidwidth;
|
||||
}
|
||||
else
|
||||
{
|
||||
RelOptInfo *baserel;
|
||||
int ndx;
|
||||
|
||||
/* Get the Var's original base rel */
|
||||
baserel = find_base_rel(root, var->varno);
|
||||
|
||||
/* Is it still needed above this joinrel? */
|
||||
ndx = var->varattno - baserel->min_attr;
|
||||
if (bms_nonempty_difference(baserel->attr_needed[ndx], relids))
|
||||
{
|
||||
/* Yup, add it to the output */
|
||||
joinrel->reltarget->exprs = lappend(joinrel->reltarget->exprs,
|
||||
var);
|
||||
/* Vars have cost zero, so no need to adjust reltarget->cost */
|
||||
joinrel->reltarget->width += baserel->attr_widths[ndx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user