| /*------------------------------------------------------------------------- |
| * |
| * appendinfo.c |
| * Routines for mapping between append parent(s) and children |
| * |
| * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group |
| * Portions Copyright (c) 1994, Regents of the University of California |
| * |
| * |
| * IDENTIFICATION |
| * src/backend/optimizer/path/appendinfo.c |
| * |
| *------------------------------------------------------------------------- |
| */ |
| #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" |
| #include "utils/syscache.h" |
| |
| |
| typedef struct |
| { |
| PlannerInfo *root; |
| int nappinfos; |
| AppendRelInfo **appinfos; |
| } adjust_appendrel_attrs_context; |
| |
| static void make_inh_translation_list(Relation oldrelation, |
| Relation newrelation, |
| Index newvarno, |
| AppendRelInfo *appinfo); |
| static Node *adjust_appendrel_attrs_mutator(Node *node, |
| adjust_appendrel_attrs_context *context); |
| |
| |
| /* |
| * make_append_rel_info |
| * Build an AppendRelInfo for the parent-child pair |
| */ |
| AppendRelInfo * |
| make_append_rel_info(Relation parentrel, Relation childrel, |
| Index parentRTindex, Index childRTindex) |
| { |
| AppendRelInfo *appinfo = makeNode(AppendRelInfo); |
| |
| appinfo->parent_relid = parentRTindex; |
| appinfo->child_relid = childRTindex; |
| appinfo->parent_reltype = parentrel->rd_rel->reltype; |
| appinfo->child_reltype = childrel->rd_rel->reltype; |
| make_inh_translation_list(parentrel, childrel, childRTindex, appinfo); |
| appinfo->parent_reloid = RelationGetRelid(parentrel); |
| |
| return appinfo; |
| } |
| |
| /* |
| * make_inh_translation_list |
| * Build the list of translations from parent Vars to child Vars for |
| * an inheritance child, as well as a reverse-translation array. |
| * |
| * The reverse-translation array has an entry for each child relation |
| * column, which is either the 1-based index of the corresponding parent |
| * column, or 0 if there's no match (that happens for dropped child columns, |
| * as well as child columns beyond those of the parent, which are allowed in |
| * traditional inheritance though not partitioning). |
| * |
| * For paranoia's sake, we match type/collation as well as attribute name. |
| */ |
| static void |
| make_inh_translation_list(Relation oldrelation, Relation newrelation, |
| Index newvarno, |
| AppendRelInfo *appinfo) |
| { |
| List *vars = NIL; |
| AttrNumber *pcolnos; |
| TupleDesc old_tupdesc = RelationGetDescr(oldrelation); |
| TupleDesc new_tupdesc = RelationGetDescr(newrelation); |
| Oid new_relid = RelationGetRelid(newrelation); |
| int oldnatts = old_tupdesc->natts; |
| int newnatts = new_tupdesc->natts; |
| int old_attno; |
| int new_attno = 0; |
| |
| /* Initialize reverse-translation array with all entries zero */ |
| appinfo->num_child_cols = newnatts; |
| appinfo->parent_colnos = pcolnos = |
| (AttrNumber *) palloc0(newnatts * sizeof(AttrNumber)); |
| |
| for (old_attno = 0; old_attno < oldnatts; old_attno++) |
| { |
| Form_pg_attribute att; |
| char *attname; |
| Oid atttypid; |
| int32 atttypmod; |
| Oid attcollation; |
| |
| att = TupleDescAttr(old_tupdesc, old_attno); |
| if (att->attisdropped) |
| { |
| /* Just put NULL into this list entry */ |
| vars = lappend(vars, NULL); |
| continue; |
| } |
| attname = NameStr(att->attname); |
| atttypid = att->atttypid; |
| atttypmod = att->atttypmod; |
| attcollation = att->attcollation; |
| |
| /* |
| * When we are generating the "translation list" for the parent table |
| * of an inheritance set, no need to search for matches. |
| */ |
| if (oldrelation == newrelation) |
| { |
| vars = lappend(vars, makeVar(newvarno, |
| (AttrNumber) (old_attno + 1), |
| atttypid, |
| atttypmod, |
| attcollation, |
| 0)); |
| pcolnos[old_attno] = old_attno + 1; |
| continue; |
| } |
| |
| /* |
| * Otherwise we have to search for the matching column by name. |
| * There's no guarantee it'll have the same column position, because |
| * of cases like ALTER TABLE ADD COLUMN and multiple inheritance. |
| * However, in simple cases, the relative order of columns is mostly |
| * the same in both relations, so try the column of newrelation that |
| * follows immediately after the one that we just found, and if that |
| * fails, let syscache handle it. |
| */ |
| if (new_attno >= newnatts || |
| (att = TupleDescAttr(new_tupdesc, new_attno))->attisdropped || |
| strcmp(attname, NameStr(att->attname)) != 0) |
| { |
| HeapTuple newtup; |
| |
| newtup = SearchSysCacheAttName(new_relid, attname); |
| if (!HeapTupleIsValid(newtup)) |
| elog(ERROR, "could not find inherited attribute \"%s\" of relation \"%s\"", |
| attname, RelationGetRelationName(newrelation)); |
| new_attno = ((Form_pg_attribute) GETSTRUCT(newtup))->attnum - 1; |
| Assert(new_attno >= 0 && new_attno < newnatts); |
| ReleaseSysCache(newtup); |
| |
| att = TupleDescAttr(new_tupdesc, new_attno); |
| } |
| |
| /* Found it, check type and collation match */ |
| if (atttypid != att->atttypid || atttypmod != att->atttypmod) |
| elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's type", |
| attname, RelationGetRelationName(newrelation)); |
| if (attcollation != att->attcollation) |
| elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's collation", |
| attname, RelationGetRelationName(newrelation)); |
| |
| vars = lappend(vars, makeVar(newvarno, |
| (AttrNumber) (new_attno + 1), |
| atttypid, |
| atttypmod, |
| attcollation, |
| 0)); |
| pcolnos[new_attno] = old_attno + 1; |
| new_attno++; |
| } |
| |
| appinfo->translated_vars = vars; |
| } |
| |
| /* |
| * adjust_appendrel_attrs |
| * Copy the specified query or expression and translate Vars referring to a |
| * parent rel to refer to the corresponding child rel instead. We also |
| * update rtindexes appearing outside Vars, such as resultRelation and |
| * jointree relids. |
| * |
| * Note: this is only applied after conversion of sublinks to subplans, |
| * so we don't need to cope with recursion into sub-queries. |
| * |
| * Note: this is not hugely different from what pullup_replace_vars() does; |
| * maybe we should try to fold the two routines together. |
| */ |
| Node * |
| adjust_appendrel_attrs(PlannerInfo *root, Node *node, int nappinfos, |
| AppendRelInfo **appinfos) |
| { |
| adjust_appendrel_attrs_context context; |
| |
| context.root = root; |
| context.nappinfos = nappinfos; |
| context.appinfos = appinfos; |
| |
| /* If there's nothing to adjust, don't call this function. */ |
| Assert(nappinfos >= 1 && appinfos != NULL); |
| |
| /* Should never be translating a Query tree. */ |
| Assert(node == NULL || !IsA(node, Query)); |
| |
| return adjust_appendrel_attrs_mutator(node, &context); |
| } |
| |
| static Node * |
| adjust_appendrel_attrs_mutator(Node *node, |
| adjust_appendrel_attrs_context *context) |
| { |
| AppendRelInfo **appinfos = context->appinfos; |
| int nappinfos = context->nappinfos; |
| int cnt; |
| |
| if (node == NULL) |
| return NULL; |
| if (IsA(node, Var)) |
| { |
| Var *var = (Var *) copyObject(node); |
| AppendRelInfo *appinfo = NULL; |
| |
| if (var->varlevelsup != 0) |
| return (Node *) var; /* no changes needed */ |
| |
| for (cnt = 0; cnt < nappinfos; cnt++) |
| { |
| if (var->varno == appinfos[cnt]->parent_relid) |
| { |
| appinfo = appinfos[cnt]; |
| break; |
| } |
| } |
| |
| if (appinfo) |
| { |
| var->varno = appinfo->child_relid; |
| /* it's now a generated Var, so drop any syntactic labeling */ |
| var->varnosyn = 0; |
| var->varattnosyn = 0; |
| if (var->varattno > 0) |
| { |
| Node *newnode; |
| |
| if (var->varattno > list_length(appinfo->translated_vars)) |
| elog(ERROR, "attribute %d of relation \"%s\" does not exist", |
| var->varattno, get_rel_name(appinfo->parent_reloid)); |
| newnode = copyObject(list_nth(appinfo->translated_vars, |
| var->varattno - 1)); |
| if (newnode == NULL) |
| elog(ERROR, "attribute %d of relation \"%s\" does not exist", |
| var->varattno, get_rel_name(appinfo->parent_reloid)); |
| return newnode; |
| } |
| else if (var->varattno == 0) |
| { |
| /* |
| * Whole-row Var: if we are dealing with named rowtypes, we |
| * can use a whole-row Var for the child table plus a coercion |
| * step to convert the tuple layout to the parent's rowtype. |
| * Otherwise we have to generate a RowExpr. |
| */ |
| if (OidIsValid(appinfo->child_reltype)) |
| { |
| Assert(var->vartype == appinfo->parent_reltype); |
| if (appinfo->parent_reltype != appinfo->child_reltype) |
| { |
| ConvertRowtypeExpr *r = makeNode(ConvertRowtypeExpr); |
| |
| r->arg = (Expr *) var; |
| r->resulttype = appinfo->parent_reltype; |
| r->convertformat = COERCE_IMPLICIT_CAST; |
| r->location = -1; |
| /* Make sure the Var node has the right type ID, too */ |
| var->vartype = appinfo->child_reltype; |
| return (Node *) r; |
| } |
| } |
| else |
| { |
| /* |
| * Build a RowExpr containing the translated variables. |
| * |
| * In practice var->vartype will always be RECORDOID here, |
| * so we need to come up with some suitable column names. |
| * We use the parent RTE's column names. |
| * |
| * Note: we can't get here for inheritance cases, so there |
| * is no need to worry that translated_vars might contain |
| * some dummy NULLs. |
| */ |
| RowExpr *rowexpr; |
| List *fields; |
| RangeTblEntry *rte; |
| |
| rte = rt_fetch(appinfo->parent_relid, |
| context->root->parse->rtable); |
| fields = copyObject(appinfo->translated_vars); |
| rowexpr = makeNode(RowExpr); |
| rowexpr->args = fields; |
| rowexpr->row_typeid = var->vartype; |
| rowexpr->row_format = COERCE_IMPLICIT_CAST; |
| rowexpr->colnames = copyObject(rte->eref->colnames); |
| rowexpr->location = -1; |
| |
| return (Node *) rowexpr; |
| } |
| } |
| /* 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)) |
| { |
| CurrentOfExpr *cexpr = (CurrentOfExpr *) copyObject(node); |
| |
| for (cnt = 0; cnt < nappinfos; cnt++) |
| { |
| AppendRelInfo *appinfo = appinfos[cnt]; |
| |
| if (cexpr->cvarno == appinfo->parent_relid) |
| { |
| cexpr->cvarno = appinfo->child_relid; |
| break; |
| } |
| } |
| return (Node *) cexpr; |
| } |
| if (IsA(node, PlaceHolderVar)) |
| { |
| /* Copy the PlaceHolderVar node with correct mutation of subnodes */ |
| PlaceHolderVar *phv; |
| |
| phv = (PlaceHolderVar *) expression_tree_mutator(node, |
| adjust_appendrel_attrs_mutator, |
| (void *) context); |
| /* now fix PlaceHolderVar's relid sets */ |
| if (phv->phlevelsup == 0) |
| phv->phrels = adjust_child_relids(phv->phrels, context->nappinfos, |
| context->appinfos); |
| return (Node *) phv; |
| } |
| /* Shouldn't need to handle planner auxiliary nodes here */ |
| Assert(!IsA(node, SpecialJoinInfo)); |
| Assert(!IsA(node, AppendRelInfo)); |
| Assert(!IsA(node, PlaceHolderInfo)); |
| Assert(!IsA(node, MinMaxAggInfo)); |
| |
| /* |
| * We have to process RestrictInfo nodes specially. (Note: although |
| * set_append_rel_pathlist will hide RestrictInfos in the parent's |
| * baserestrictinfo list from us, it doesn't hide those in joininfo.) |
| */ |
| if (IsA(node, RestrictInfo)) |
| { |
| RestrictInfo *oldinfo = (RestrictInfo *) node; |
| RestrictInfo *newinfo = makeNode(RestrictInfo); |
| |
| /* Copy all flat-copiable fields */ |
| memcpy(newinfo, oldinfo, sizeof(RestrictInfo)); |
| |
| /* Recursively fix the clause itself */ |
| newinfo->clause = (Expr *) |
| adjust_appendrel_attrs_mutator((Node *) oldinfo->clause, context); |
| |
| /* and the modified version, if an OR clause */ |
| newinfo->orclause = (Expr *) |
| adjust_appendrel_attrs_mutator((Node *) oldinfo->orclause, context); |
| |
| /* adjust relid sets too */ |
| newinfo->clause_relids = adjust_child_relids(oldinfo->clause_relids, |
| context->nappinfos, |
| context->appinfos); |
| newinfo->required_relids = adjust_child_relids(oldinfo->required_relids, |
| context->nappinfos, |
| context->appinfos); |
| newinfo->outer_relids = adjust_child_relids(oldinfo->outer_relids, |
| context->nappinfos, |
| context->appinfos); |
| newinfo->nullable_relids = adjust_child_relids(oldinfo->nullable_relids, |
| context->nappinfos, |
| context->appinfos); |
| newinfo->left_relids = adjust_child_relids(oldinfo->left_relids, |
| context->nappinfos, |
| context->appinfos); |
| newinfo->right_relids = adjust_child_relids(oldinfo->right_relids, |
| context->nappinfos, |
| context->appinfos); |
| |
| /* |
| * Reset cached derivative fields, since these might need to have |
| * different values when considering the child relation. Note we |
| * don't reset left_ec/right_ec: each child variable is implicitly |
| * equivalent to its parent, so still a member of the same EC if any. |
| */ |
| newinfo->eval_cost.startup = -1; |
| newinfo->norm_selec = -1; |
| newinfo->outer_selec = -1; |
| newinfo->left_em = NULL; |
| newinfo->right_em = NULL; |
| newinfo->scansel_cache = NIL; |
| newinfo->left_bucketsize = -1; |
| newinfo->right_bucketsize = -1; |
| newinfo->left_mcvfreq = -1; |
| newinfo->right_mcvfreq = -1; |
| |
| return (Node *) newinfo; |
| } |
| |
| /* |
| * NOTE: we do not need to recurse into sublinks, because they should |
| * already have been converted to subplans before we see them. |
| */ |
| Assert(!IsA(node, SubLink)); |
| Assert(!IsA(node, Query)); |
| /* We should never see these Query substructures, either. */ |
| Assert(!IsA(node, RangeTblRef)); |
| Assert(!IsA(node, JoinExpr)); |
| |
| node = expression_tree_mutator(node, adjust_appendrel_attrs_mutator, |
| (void *) context); |
| |
| /* |
| * In GPDB, if you have two SubPlans referring to the same initplan, we |
| * require two separate copies of the subplan, one for each SubPlan |
| * reference. That's because even if a plan is otherwise the same, we |
| * may want to later apply different flow to different SubPlans |
| * referring it. Any subplan that is left unused, because we created |
| * the new copy here, will be removed by remove_unused_subplans(). |
| */ |
| if (IsA(node, SubPlan)) |
| { |
| SubPlan *sp = (SubPlan *) node; |
| |
| if (!sp->is_initplan) |
| { |
| PlannerInfo *root = context->root; |
| Plan *newsubplan = (Plan *) copyObject(planner_subplan_get_plan(root, sp)); |
| PlannerInfo *newsubroot = makeNode(PlannerInfo); |
| PlannerInfo *oldsubroot = planner_subplan_get_root(root, sp); |
| |
| memcpy(newsubroot, oldsubroot, sizeof(PlannerInfo)); |
| newsubroot->append_rel_list = (List *) copyObject(oldsubroot->append_rel_list); |
| |
| /* |
| * Add the subplan and its subroot to the global lists. |
| */ |
| root->glob->subplans = lappend(root->glob->subplans, newsubplan); |
| root->glob->subroots = lappend(root->glob->subroots, newsubroot); |
| |
| /* |
| * expression_tree_mutator made a copy of the SubPlan already, so |
| * we can modify it directly. |
| */ |
| sp->plan_id = list_length(root->glob->subplans); |
| sp->plan_name = psprintf("%s (copy %d)", sp->plan_name, sp->plan_id); |
| } |
| } |
| |
| return node; |
| } |
| |
| /* |
| * adjust_appendrel_attrs_multilevel |
| * Apply Var translations from a toplevel appendrel parent down to a child. |
| * |
| * In some cases we need to translate expressions referencing a parent relation |
| * to reference an appendrel child that's multiple levels removed from it. |
| */ |
| Node * |
| adjust_appendrel_attrs_multilevel(PlannerInfo *root, Node *node, |
| Relids child_relids, |
| Relids top_parent_relids) |
| { |
| AppendRelInfo **appinfos; |
| Bitmapset *parent_relids = NULL; |
| int nappinfos; |
| int cnt; |
| |
| Assert(bms_num_members(child_relids) == bms_num_members(top_parent_relids)); |
| |
| appinfos = find_appinfos_by_relids(root, child_relids, &nappinfos); |
| |
| /* Construct relids set for the immediate parent of given child. */ |
| for (cnt = 0; cnt < nappinfos; cnt++) |
| { |
| AppendRelInfo *appinfo = appinfos[cnt]; |
| |
| parent_relids = bms_add_member(parent_relids, appinfo->parent_relid); |
| } |
| |
| /* Recurse if immediate parent is not the top parent. */ |
| if (!bms_equal(parent_relids, top_parent_relids)) |
| node = adjust_appendrel_attrs_multilevel(root, node, parent_relids, |
| top_parent_relids); |
| |
| /* Now translate for this child */ |
| node = adjust_appendrel_attrs(root, node, nappinfos, appinfos); |
| |
| pfree(appinfos); |
| |
| return node; |
| } |
| |
| /* |
| * Substitute child relids for parent relids in a Relid set. The array of |
| * appinfos specifies the substitutions to be performed. |
| */ |
| Relids |
| adjust_child_relids(Relids relids, int nappinfos, AppendRelInfo **appinfos) |
| { |
| Bitmapset *result = NULL; |
| int cnt; |
| |
| for (cnt = 0; cnt < nappinfos; cnt++) |
| { |
| AppendRelInfo *appinfo = appinfos[cnt]; |
| |
| /* Remove parent, add child */ |
| if (bms_is_member(appinfo->parent_relid, relids)) |
| { |
| /* Make a copy if we are changing the set. */ |
| if (!result) |
| result = bms_copy(relids); |
| |
| result = bms_del_member(result, appinfo->parent_relid); |
| result = bms_add_member(result, appinfo->child_relid); |
| } |
| } |
| |
| /* If we made any changes, return the modified copy. */ |
| if (result) |
| return result; |
| |
| /* Otherwise, return the original set without modification. */ |
| return relids; |
| } |
| |
| /* |
| * Replace any relid present in top_parent_relids with its child in |
| * child_relids. Members of child_relids can be multiple levels below top |
| * parent in the partition hierarchy. |
| */ |
| Relids |
| adjust_child_relids_multilevel(PlannerInfo *root, Relids relids, |
| Relids child_relids, Relids top_parent_relids) |
| { |
| AppendRelInfo **appinfos; |
| int nappinfos; |
| Relids parent_relids = NULL; |
| Relids result; |
| Relids tmp_result = NULL; |
| int cnt; |
| |
| /* |
| * If the given relids set doesn't contain any of the top parent relids, |
| * it will remain unchanged. |
| */ |
| if (!bms_overlap(relids, top_parent_relids)) |
| return relids; |
| |
| appinfos = find_appinfos_by_relids(root, child_relids, &nappinfos); |
| |
| /* Construct relids set for the immediate parent of the given child. */ |
| for (cnt = 0; cnt < nappinfos; cnt++) |
| { |
| AppendRelInfo *appinfo = appinfos[cnt]; |
| |
| parent_relids = bms_add_member(parent_relids, appinfo->parent_relid); |
| } |
| |
| /* Recurse if immediate parent is not the top parent. */ |
| if (!bms_equal(parent_relids, top_parent_relids)) |
| { |
| tmp_result = adjust_child_relids_multilevel(root, relids, |
| parent_relids, |
| top_parent_relids); |
| relids = tmp_result; |
| } |
| |
| result = adjust_child_relids(relids, nappinfos, appinfos); |
| |
| /* Free memory consumed by any intermediate result. */ |
| if (tmp_result) |
| bms_free(tmp_result); |
| bms_free(parent_relids); |
| pfree(appinfos); |
| |
| return result; |
| } |
| |
| /* |
| * adjust_inherited_attnums |
| * Translate an integer list of attribute numbers from parent to child. |
| */ |
| List * |
| adjust_inherited_attnums(List *attnums, AppendRelInfo *context) |
| { |
| List *result = NIL; |
| ListCell *lc; |
| |
| /* This should only happen for an inheritance case, not UNION ALL */ |
| Assert(OidIsValid(context->parent_reloid)); |
| |
| /* Look up each attribute in the AppendRelInfo's translated_vars list */ |
| foreach(lc, attnums) |
| { |
| AttrNumber parentattno = lfirst_int(lc); |
| Var *childvar; |
| |
| /* Look up the translation of this column: it must be a Var */ |
| if (parentattno <= 0 || |
| parentattno > list_length(context->translated_vars)) |
| elog(ERROR, "attribute %d of relation \"%s\" does not exist", |
| 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", |
| parentattno, get_rel_name(context->parent_reloid)); |
| |
| result = lappend_int(result, childvar->varattno); |
| } |
| return result; |
| } |
| |
| /* |
| * 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]; |
| |
| 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) |
| { |
| /* |
| * 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); |
| } |
| else |
| { |
| 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); |
| } |
| } |
| |
| /* |
| * find_appinfos_by_relids |
| * Find AppendRelInfo structures for all relations specified by relids. |
| * |
| * The AppendRelInfos are returned in an array, which can be pfree'd by the |
| * caller. *nappinfos is set to the number of entries in the array. |
| */ |
| AppendRelInfo ** |
| find_appinfos_by_relids(PlannerInfo *root, Relids relids, int *nappinfos) |
| { |
| AppendRelInfo **appinfos; |
| int cnt = 0; |
| int i; |
| |
| *nappinfos = bms_num_members(relids); |
| appinfos = (AppendRelInfo **) palloc(sizeof(AppendRelInfo *) * *nappinfos); |
| |
| i = -1; |
| while ((i = bms_next_member(relids, i)) >= 0) |
| { |
| AppendRelInfo *appinfo = root->append_rel_array[i]; |
| |
| if (!appinfo) |
| elog(ERROR, "child rel %d not found in append_rel_array", i); |
| |
| appinfos[cnt++] = appinfo; |
| } |
| 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; |
| Var *varSegid = NULL; |
| |
| Assert(commandType == CMD_UPDATE || commandType == CMD_DELETE); |
| |
| if (relkind == RELKIND_RELATION || |
| relkind == RELKIND_DIRECTORY_TABLE || |
| 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"); |
| |
| /* |
| * GPDB also needs gp_segment_id. ctid is only unique in the same |
| * segment. |
| */ |
| { |
| Oid reloid; |
| Oid vartypeid; |
| int32 type_mod; |
| Oid type_coll; |
| |
| reloid = RelationGetRelid(target_relation); |
| get_atttypetypmodcoll(reloid, GpSegmentIdAttributeNumber, &vartypeid, &type_mod, &type_coll); |
| varSegid = makeVar(rtindex, |
| GpSegmentIdAttributeNumber, |
| vartypeid, |
| type_mod, |
| type_coll, |
| 0); |
| add_row_identity_var(root, varSegid, rtindex, "gp_segment_id"); |
| } |
| |
| /* |
| * GPDB_14_MERGE_FIXME: We need wholerow when updating AO table using |
| * pg14 optimizer, this can avoid extra seqscan in ExecUpdate |
| * node. However, the ORCA optimizer is not updated consistent with |
| * pg14, we still can use planSlot instead of using extra seqscan in |
| * ExecUpdate. As a result, if the targetlist contains all the columns |
| * of relation, we don't need wholerow junk attr. |
| * |
| * Maybe we need fix this problem complete in the future by |
| * redesigning AO/AOCS storage format or making the update plan is |
| * consistent whether it generated by pg optimizer or ORCA optimizer. |
| */ |
| if (commandType == CMD_UPDATE && RelationIsNonblockRelation(target_relation)) |
| { |
| var = makeVar(rtindex, |
| InvalidAttrNumber, |
| RECORDOID, |
| -1, |
| InvalidOid, |
| 0); |
| add_row_identity_var(root, var, rtindex, "wholerow"); |
| } |
| } |
| 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 */ |
| } |
| } |
| } |