diff options
Diffstat (limited to 'src/backend/optimizer/prep/prepunion.c')
-rw-r--r-- | src/backend/optimizer/prep/prepunion.c | 1204 |
1 files changed, 0 insertions, 1204 deletions
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index 1d280c205e0..05388355fde 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -12,11 +12,6 @@ * case, but most of the heavy lifting for that is done elsewhere, * notably in prepjointree.c and allpaths.c. * - * There is also some code here to support planning of queries that use - * inheritance (SELECT FROM foo*). Inheritance trees are converted into - * append relations, and thenceforth share code with the UNION ALL case. - * - * * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * @@ -28,8 +23,6 @@ */ #include "postgres.h" -#include <limits.h> - #include "access/heapam.h" #include "access/htup_details.h" #include "access/sysattr.h" @@ -54,13 +47,6 @@ #include "utils/syscache.h" -typedef struct -{ - PlannerInfo *root; - int nappinfos; - AppendRelInfo **appinfos; -} adjust_appendrel_attrs_context; - static RelOptInfo *recurse_set_operations(Node *setOp, PlannerInfo *root, List *colTypes, List *colCollations, bool junkOK, @@ -99,31 +85,6 @@ static List *generate_append_tlist(List *colTypes, List *colCollations, List *input_tlists, List *refnames_tlist); static List *generate_setop_grouplist(SetOperationStmt *op, List *targetlist); -static void expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, - Index rti); -static void expand_partitioned_rtentry(PlannerInfo *root, - RangeTblEntry *parentrte, - Index parentRTindex, Relation parentrel, - PlanRowMark *top_parentrc, LOCKMODE lockmode, - List **appinfos); -static void expand_single_inheritance_child(PlannerInfo *root, - RangeTblEntry *parentrte, - Index parentRTindex, Relation parentrel, - PlanRowMark *top_parentrc, Relation childrel, - List **appinfos, RangeTblEntry **childrte_p, - Index *childRTindex_p); -static void make_inh_translation_list(Relation oldrelation, - Relation newrelation, - Index newvarno, - List **translated_vars); -static Bitmapset *translate_col_privs(const Bitmapset *parent_privs, - List *translated_vars); -static Node *adjust_appendrel_attrs_mutator(Node *node, - adjust_appendrel_attrs_context *context); -static Relids adjust_child_relids(Relids relids, int nappinfos, - AppendRelInfo **appinfos); -static List *adjust_inherited_tlist(List *tlist, - AppendRelInfo *context); /* @@ -1460,1168 +1421,3 @@ generate_setop_grouplist(SetOperationStmt *op, List *targetlist) Assert(lg == NULL); return grouplist; } - - -/* - * expand_inherited_tables - * Expand each rangetable entry that represents an inheritance set - * into an "append relation". At the conclusion of this process, - * the "inh" flag is set in all and only those RTEs that are append - * relation parents. - */ -void -expand_inherited_tables(PlannerInfo *root) -{ - Index nrtes; - Index rti; - ListCell *rl; - - /* - * expand_inherited_rtentry may add RTEs to parse->rtable. The function is - * expected to recursively handle any RTEs that it creates with inh=true. - * So just scan as far as the original end of the rtable list. - */ - nrtes = list_length(root->parse->rtable); - rl = list_head(root->parse->rtable); - for (rti = 1; rti <= nrtes; rti++) - { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(rl); - - expand_inherited_rtentry(root, rte, rti); - rl = lnext(rl); - } -} - -/* - * expand_inherited_rtentry - * Check whether a rangetable entry represents an inheritance set. - * If so, add entries for all the child tables to the query's - * rangetable, and build AppendRelInfo nodes for all the child tables - * and add them to root->append_rel_list. If not, clear the entry's - * "inh" flag to prevent later code from looking for AppendRelInfos. - * - * Note that the original RTE is considered to represent the whole - * inheritance set. The first of the generated RTEs is an RTE for the same - * table, but with inh = false, to represent the parent table in its role - * as a simple member of the inheritance set. - * - * A childless table is never considered to be an inheritance set. For - * regular inheritance, a parent RTE must always have at least two associated - * AppendRelInfos: one corresponding to the parent table as a simple member of - * inheritance set and one or more corresponding to the actual children. - * Since a partitioned table is not scanned, it might have only one associated - * AppendRelInfo. - */ -static void -expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) -{ - Oid parentOID; - PlanRowMark *oldrc; - Relation oldrelation; - LOCKMODE lockmode; - List *inhOIDs; - ListCell *l; - - /* Does RT entry allow inheritance? */ - if (!rte->inh) - return; - /* Ignore any already-expanded UNION ALL nodes */ - if (rte->rtekind != RTE_RELATION) - { - Assert(rte->rtekind == RTE_SUBQUERY); - return; - } - /* Fast path for common case of childless table */ - parentOID = rte->relid; - if (!has_subclass(parentOID)) - { - /* Clear flag before returning */ - rte->inh = false; - return; - } - - /* - * The rewriter should already have obtained an appropriate lock on each - * relation named in the query. However, for each child relation we add - * to the query, we must obtain an appropriate lock, because this will be - * the first use of those relations in the parse/rewrite/plan pipeline. - * Child rels should use the same lockmode as their parent. - */ - lockmode = rte->rellockmode; - - /* Scan for all members of inheritance set, acquire needed locks */ - inhOIDs = find_all_inheritors(parentOID, lockmode, NULL); - - /* - * Check that there's at least one descendant, else treat as no-child - * case. This could happen despite above has_subclass() check, if table - * once had a child but no longer does. - */ - if (list_length(inhOIDs) < 2) - { - /* Clear flag before returning */ - rte->inh = false; - return; - } - - /* - * If parent relation is selected FOR UPDATE/SHARE, we need to mark its - * PlanRowMark as isParent = true, and generate a new PlanRowMark for each - * child. - */ - oldrc = get_plan_rowmark(root->rowMarks, rti); - if (oldrc) - oldrc->isParent = true; - - /* - * Must open the parent relation to examine its tupdesc. We need not lock - * it; we assume the rewriter already did. - */ - oldrelation = heap_open(parentOID, NoLock); - - /* Scan the inheritance set and expand it */ - if (RelationGetPartitionDesc(oldrelation) != NULL) - { - Assert(rte->relkind == RELKIND_PARTITIONED_TABLE); - - /* - * If this table has partitions, recursively expand them in the order - * in which they appear in the PartitionDesc. While at it, also - * extract the partition key columns of all the partitioned tables. - */ - expand_partitioned_rtentry(root, rte, rti, oldrelation, oldrc, - lockmode, &root->append_rel_list); - } - else - { - List *appinfos = NIL; - RangeTblEntry *childrte; - Index childRTindex; - - /* - * This table has no partitions. Expand any plain inheritance - * children in the order the OIDs were returned by - * find_all_inheritors. - */ - foreach(l, inhOIDs) - { - Oid childOID = lfirst_oid(l); - Relation newrelation; - - /* Open rel if needed; we already have required locks */ - if (childOID != parentOID) - newrelation = heap_open(childOID, NoLock); - else - newrelation = oldrelation; - - /* - * It is possible that the parent table has children that are temp - * tables of other backends. We cannot safely access such tables - * (because of buffering issues), and the best thing to do seems - * to be to silently ignore them. - */ - if (childOID != parentOID && RELATION_IS_OTHER_TEMP(newrelation)) - { - heap_close(newrelation, lockmode); - continue; - } - - expand_single_inheritance_child(root, rte, rti, oldrelation, oldrc, - newrelation, - &appinfos, &childrte, - &childRTindex); - - /* Close child relations, but keep locks */ - if (childOID != parentOID) - heap_close(newrelation, NoLock); - } - - /* - * If all the children were temp tables, pretend it's a - * non-inheritance situation; we don't need Append node in that case. - * The duplicate RTE we added for the parent table is harmless, so we - * don't bother to get rid of it; ditto for the useless PlanRowMark - * node. - */ - if (list_length(appinfos) < 2) - rte->inh = false; - else - root->append_rel_list = list_concat(root->append_rel_list, - appinfos); - - } - - heap_close(oldrelation, NoLock); -} - -/* - * expand_partitioned_rtentry - * Recursively expand an RTE for a partitioned table. - */ -static void -expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte, - Index parentRTindex, Relation parentrel, - PlanRowMark *top_parentrc, LOCKMODE lockmode, - List **appinfos) -{ - int i; - RangeTblEntry *childrte; - Index childRTindex; - PartitionDesc partdesc = RelationGetPartitionDesc(parentrel); - - check_stack_depth(); - - /* A partitioned table should always have a partition descriptor. */ - Assert(partdesc); - - Assert(parentrte->inh); - - /* - * Note down whether any partition key cols are being updated. Though it's - * the root partitioned table's updatedCols we are interested in, we - * instead use parentrte to get the updatedCols. This is convenient - * because parentrte already has the root partrel's updatedCols translated - * to match the attribute ordering of parentrel. - */ - if (!root->partColsUpdated) - root->partColsUpdated = - has_partition_attrs(parentrel, parentrte->updatedCols, NULL); - - /* First expand the partitioned table itself. */ - expand_single_inheritance_child(root, parentrte, parentRTindex, parentrel, - top_parentrc, parentrel, - appinfos, &childrte, &childRTindex); - - /* - * If the partitioned table has no partitions, treat this as the - * non-inheritance case. - */ - if (partdesc->nparts == 0) - { - parentrte->inh = false; - return; - } - - for (i = 0; i < partdesc->nparts; i++) - { - Oid childOID = partdesc->oids[i]; - Relation childrel; - - /* Open rel; we already have required locks */ - childrel = heap_open(childOID, NoLock); - - /* - * Temporary partitions belonging to other sessions should have been - * disallowed at definition, but for paranoia's sake, let's double - * check. - */ - if (RELATION_IS_OTHER_TEMP(childrel)) - elog(ERROR, "temporary relation from another session found as partition"); - - expand_single_inheritance_child(root, parentrte, parentRTindex, - parentrel, top_parentrc, childrel, - appinfos, &childrte, &childRTindex); - - /* If this child is itself partitioned, recurse */ - if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - expand_partitioned_rtentry(root, childrte, childRTindex, - childrel, top_parentrc, lockmode, - appinfos); - - /* Close child relation, but keep locks */ - heap_close(childrel, NoLock); - } -} - -/* - * expand_single_inheritance_child - * Build a RangeTblEntry and an AppendRelInfo, if appropriate, plus - * maybe a PlanRowMark. - * - * We now expand the partition hierarchy level by level, creating a - * corresponding hierarchy of AppendRelInfos and RelOptInfos, where each - * partitioned descendant acts as a parent of its immediate partitions. - * (This is a difference from what older versions of PostgreSQL did and what - * is still done in the case of table inheritance for unpartitioned tables, - * where the hierarchy is flattened during RTE expansion.) - * - * PlanRowMarks still carry the top-parent's RTI, and the top-parent's - * allMarkTypes field still accumulates values from all descendents. - * - * "parentrte" and "parentRTindex" are immediate parent's RTE and - * RTI. "top_parentrc" is top parent's PlanRowMark. - * - * The child RangeTblEntry and its RTI are returned in "childrte_p" and - * "childRTindex_p" resp. - */ -static void -expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, - Index parentRTindex, Relation parentrel, - PlanRowMark *top_parentrc, Relation childrel, - List **appinfos, RangeTblEntry **childrte_p, - Index *childRTindex_p) -{ - Query *parse = root->parse; - Oid parentOID = RelationGetRelid(parentrel); - Oid childOID = RelationGetRelid(childrel); - RangeTblEntry *childrte; - Index childRTindex; - AppendRelInfo *appinfo; - - /* - * Build an RTE for the child, and attach to query's rangetable list. We - * copy most fields of the parent's RTE, but replace relation OID and - * relkind, and set inh = false. Also, set requiredPerms to zero since - * all required permissions checks are done on the original RTE. Likewise, - * set the child's securityQuals to empty, because we only want to apply - * the parent's RLS conditions regardless of what RLS properties - * individual children may have. (This is an intentional choice to make - * inherited RLS work like regular permissions checks.) The parent - * securityQuals will be propagated to children along with other base - * restriction clauses, so we don't need to do it here. - */ - childrte = copyObject(parentrte); - *childrte_p = childrte; - childrte->relid = childOID; - childrte->relkind = childrel->rd_rel->relkind; - /* A partitioned child will need to be expanded further. */ - if (childOID != parentOID && - childrte->relkind == RELKIND_PARTITIONED_TABLE) - childrte->inh = true; - else - childrte->inh = false; - childrte->requiredPerms = 0; - childrte->securityQuals = NIL; - parse->rtable = lappend(parse->rtable, childrte); - childRTindex = list_length(parse->rtable); - *childRTindex_p = childRTindex; - - /* - * We need an AppendRelInfo if paths will be built for the child RTE. If - * childrte->inh is true, then we'll always need to generate append paths - * for it. If childrte->inh is false, we must scan it if it's not a - * partitioned table; but if it is a partitioned table, then it never has - * any data of its own and need not be scanned. - */ - if (childrte->relkind != RELKIND_PARTITIONED_TABLE || childrte->inh) - { - 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->translated_vars); - appinfo->parent_reloid = parentOID; - *appinfos = lappend(*appinfos, appinfo); - - /* - * Translate the column permissions bitmaps to the child's attnums (we - * have to build the translated_vars list before we can do this). But - * if this is the parent table, leave copyObject's result alone. - * - * Note: we need to do this even though the executor won't run any - * permissions checks on the child RTE. The insertedCols/updatedCols - * bitmaps may be examined for trigger-firing purposes. - */ - if (childOID != parentOID) - { - childrte->selectedCols = translate_col_privs(parentrte->selectedCols, - appinfo->translated_vars); - childrte->insertedCols = translate_col_privs(parentrte->insertedCols, - appinfo->translated_vars); - childrte->updatedCols = translate_col_privs(parentrte->updatedCols, - appinfo->translated_vars); - } - } - - /* - * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE. - */ - if (top_parentrc) - { - PlanRowMark *childrc = makeNode(PlanRowMark); - - childrc->rti = childRTindex; - childrc->prti = top_parentrc->rti; - childrc->rowmarkId = top_parentrc->rowmarkId; - /* Reselect rowmark type, because relkind might not match parent */ - childrc->markType = select_rowmark_type(childrte, - top_parentrc->strength); - childrc->allMarkTypes = (1 << childrc->markType); - childrc->strength = top_parentrc->strength; - childrc->waitPolicy = top_parentrc->waitPolicy; - - /* - * We mark RowMarks for partitioned child tables as parent RowMarks so - * that the executor ignores them (except their existence means that - * the child tables be locked using appropriate mode). - */ - childrc->isParent = (childrte->relkind == RELKIND_PARTITIONED_TABLE); - - /* Include child's rowmark type in top parent's allMarkTypes */ - top_parentrc->allMarkTypes |= childrc->allMarkTypes; - - root->rowMarks = lappend(root->rowMarks, childrc); - } -} - -/* - * make_inh_translation_list - * Build the list of translations from parent Vars to child Vars for - * an inheritance child. - * - * 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, - List **translated_vars) -{ - List *vars = NIL; - 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; - - 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)); - 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 (!newtup) - elog(ERROR, "could not find inherited attribute \"%s\" of relation \"%s\"", - attname, RelationGetRelationName(newrelation)); - new_attno = ((Form_pg_attribute) GETSTRUCT(newtup))->attnum - 1; - 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)); - new_attno++; - } - - *translated_vars = vars; -} - -/* - * translate_col_privs - * Translate a bitmapset representing per-column privileges from the - * parent rel's attribute numbering to the child's. - * - * The only surprise here is that we don't translate a parent whole-row - * reference into a child whole-row reference. That would mean requiring - * permissions on all child columns, which is overly strict, since the - * query is really only going to reference the inherited columns. Instead - * we set the per-column bits for all inherited columns. - */ -static Bitmapset * -translate_col_privs(const Bitmapset *parent_privs, - List *translated_vars) -{ - Bitmapset *child_privs = NULL; - bool whole_row; - int attno; - ListCell *lc; - - /* System attributes have the same numbers in all tables */ - for (attno = FirstLowInvalidHeapAttributeNumber + 1; attno < 0; attno++) - { - if (bms_is_member(attno - FirstLowInvalidHeapAttributeNumber, - parent_privs)) - child_privs = bms_add_member(child_privs, - attno - FirstLowInvalidHeapAttributeNumber); - } - - /* Check if parent has whole-row reference */ - whole_row = bms_is_member(InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber, - parent_privs); - - /* And now translate the regular user attributes, using the vars list */ - attno = InvalidAttrNumber; - foreach(lc, translated_vars) - { - Var *var = lfirst_node(Var, lc); - - attno++; - if (var == NULL) /* ignore dropped columns */ - continue; - if (whole_row || - bms_is_member(attno - FirstLowInvalidHeapAttributeNumber, - parent_privs)) - child_privs = bms_add_member(child_privs, - var->varattno - FirstLowInvalidHeapAttributeNumber); - } - - return child_privs; -} - -/* - * 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) -{ - Node *result; - 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); - - /* - * Must be prepared to start with a Query or a bare expression tree. - */ - if (node && IsA(node, Query)) - { - Query *newnode; - int cnt; - - 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; -} - -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; - - for (cnt = 0; cnt < nappinfos; cnt++) - { - if (var->varno == appinfos[cnt]->parent_relid) - { - appinfo = appinfos[cnt]; - break; - } - } - - if (var->varlevelsup == 0 && appinfo) - { - var->varno = appinfo->child_relid; - var->varnoold = appinfo->child_relid; - 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 */ - } - 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, 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 */ - 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)); - - return expression_tree_mutator(node, adjust_appendrel_attrs_mutator, - (void *) context); -} - -/* - * Substitute child relids for parent relids in a Relid set. The array of - * appinfos specifies the substitutions to be performed. - */ -static 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 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. - */ -static List * -adjust_inherited_tlist(List *tlist, AppendRelInfo *context) -{ - bool changed_it = false; - ListCell *tl; - List *new_tlist; - bool more; - int attrno; - - /* 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) - { - TargetEntry *tle = (TargetEntry *) lfirst(tl); - 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)) - 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); - if (childvar == NULL || !IsA(childvar, Var)) - elog(ERROR, "attribute %d of relation \"%s\" does not exist", - tle->resno, get_rel_name(context->parent_reloid)); - - if (tle->resno != childvar->varattno) - { - tle->resno = childvar->varattno; - changed_it = true; - } - } - - /* - * 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; - - new_tlist = NIL; - more = true; - for (attrno = 1; more; attrno++) - { - 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; - } - } - - foreach(tl, tlist) - { - TargetEntry *tle = (TargetEntry *) lfirst(tl); - - if (!tle->resjunk) - continue; /* here, ignore non-junk items */ - - tle->resno = attrno; - new_tlist = lappend(new_tlist, tle); - attrno++; - } - - return new_tlist; -} - -/* - * 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; -} - -/* - * Construct the SpecialJoinInfo for a child-join by translating - * SpecialJoinInfo for the join between parents. left_relids and right_relids - * are the relids of left and right side of the join respectively. - */ -SpecialJoinInfo * -build_child_join_sjinfo(PlannerInfo *root, SpecialJoinInfo *parent_sjinfo, - Relids left_relids, Relids right_relids) -{ - SpecialJoinInfo *sjinfo = makeNode(SpecialJoinInfo); - AppendRelInfo **left_appinfos; - int left_nappinfos; - AppendRelInfo **right_appinfos; - int right_nappinfos; - - memcpy(sjinfo, parent_sjinfo, sizeof(SpecialJoinInfo)); - left_appinfos = find_appinfos_by_relids(root, left_relids, - &left_nappinfos); - right_appinfos = find_appinfos_by_relids(root, right_relids, - &right_nappinfos); - - sjinfo->min_lefthand = adjust_child_relids(sjinfo->min_lefthand, - left_nappinfos, left_appinfos); - sjinfo->min_righthand = adjust_child_relids(sjinfo->min_righthand, - right_nappinfos, - right_appinfos); - sjinfo->syn_lefthand = adjust_child_relids(sjinfo->syn_lefthand, - left_nappinfos, left_appinfos); - sjinfo->syn_righthand = adjust_child_relids(sjinfo->syn_righthand, - right_nappinfos, - right_appinfos); - sjinfo->semi_rhs_exprs = (List *) adjust_appendrel_attrs(root, - (Node *) sjinfo->semi_rhs_exprs, - right_nappinfos, - right_appinfos); - - pfree(left_appinfos); - pfree(right_appinfos); - - return sjinfo; -} - -/* - * 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; -} |