From bc6f2af5d9fb09d2fb29cd2ac07ffdaa2fbaef26 Mon Sep 17 00:00:00 2001 From: amit Date: Thu, 10 Jan 2019 13:36:46 +0900 Subject: [PATCH v12 1/6] Move inheritance expansion code into its own file This commit moves expand_inherited_tables and underlings currently in optimizer/prep/prepunionc.c to optimizer/utils/inherit.c. All of the AppendRelInfo based expression manipulation routines are moved to optimizer/utils/appendinfo.c. This commit mostly moves the code and contains no functional changes. One exception though is the introduction of make_append_rel_info, but it's still just moving around code. --- src/backend/optimizer/path/allpaths.c | 2 + src/backend/optimizer/path/equivclass.c | 1 + src/backend/optimizer/path/joinrels.c | 1 + src/backend/optimizer/plan/planmain.c | 2 + src/backend/optimizer/plan/planner.c | 2 + src/backend/optimizer/prep/prepunion.c | 1202 ------------------------------- src/backend/optimizer/util/Makefile | 5 +- src/backend/optimizer/util/appendinfo.c | 824 +++++++++++++++++++++ src/backend/optimizer/util/inherit.c | 451 ++++++++++++ src/backend/optimizer/util/pathnode.c | 1 + src/backend/optimizer/util/relnode.c | 1 + src/backend/partitioning/partprune.c | 1 + src/include/optimizer/appendinfo.h | 42 ++ src/include/optimizer/inherit.h | 25 + src/include/optimizer/prep.h | 18 - 15 files changed, 1356 insertions(+), 1222 deletions(-) create mode 100644 src/backend/optimizer/util/appendinfo.c create mode 100644 src/backend/optimizer/util/inherit.c create mode 100644 src/include/optimizer/appendinfo.h create mode 100644 src/include/optimizer/inherit.h diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 256fe16cdb..8bd71a9825 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -30,6 +30,8 @@ #ifdef OPTIMIZER_DEBUG #include "nodes/print.h" #endif +#include "optimizer/inherit.h" +#include "optimizer/appendinfo.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" #include "optimizer/geqo.h" diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c index 0d994bd9ae..7fad7ad1bf 100644 --- a/src/backend/optimizer/path/equivclass.c +++ b/src/backend/optimizer/path/equivclass.c @@ -22,6 +22,7 @@ #include "catalog/pg_type.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" +#include "optimizer/appendinfo.h" #include "optimizer/clauses.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c index 67b9ca83a7..22ca4d999a 100644 --- a/src/backend/optimizer/path/joinrels.c +++ b/src/backend/optimizer/path/joinrels.c @@ -15,6 +15,7 @@ #include "postgres.h" #include "miscadmin.h" +#include "optimizer/appendinfo.h" #include "optimizer/clauses.h" #include "optimizer/joininfo.h" #include "optimizer/pathnode.h" diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c index 0e0a543708..fc97a1bb50 100644 --- a/src/backend/optimizer/plan/planmain.c +++ b/src/backend/optimizer/plan/planmain.c @@ -20,7 +20,9 @@ */ #include "postgres.h" +#include "optimizer/appendinfo.h" #include "optimizer/clauses.h" +#include "optimizer/inherit.h" #include "optimizer/orclauses.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 3e33a17a5b..98acb58304 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -37,8 +37,10 @@ #ifdef OPTIMIZER_DEBUG #include "nodes/print.h" #endif +#include "optimizer/appendinfo.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" +#include "optimizer/inherit.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" #include "optimizer/plancat.h" diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index 1d280c205e..5d59385ef7 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 * @@ -54,13 +49,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 +87,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 +1423,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; -} diff --git a/src/backend/optimizer/util/Makefile b/src/backend/optimizer/util/Makefile index c54d0a690d..adffb01226 100644 --- a/src/backend/optimizer/util/Makefile +++ b/src/backend/optimizer/util/Makefile @@ -12,7 +12,8 @@ subdir = src/backend/optimizer/util top_builddir = ../../../.. include $(top_builddir)/src/Makefile.global -OBJS = clauses.o joininfo.o orclauses.o pathnode.o placeholder.o \ - plancat.o predtest.o relnode.o restrictinfo.o tlist.o var.o +OBJS = appendinfo.o clauses.o inherit.o joininfo.o orclauses.o pathnode.o \ + placeholder.o plancat.o predtest.o relnode.o restrictinfo.o tlist.o \ + var.o include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.c new file mode 100644 index 0000000000..01040c5cfe --- /dev/null +++ b/src/backend/optimizer/util/appendinfo.c @@ -0,0 +1,824 @@ +/*------------------------------------------------------------------------- + * + * appendinfo.c + * Routines for mapping between append parent(s) and children + * + * Portions Copyright (c) 1996-2019, 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/sysattr.h" +#include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" +#include "optimizer/appendinfo.h" +#include "parser/parsetree.h" +#include "utils/rel.h" +#include "utils/lsyscache.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, + 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); + +/* + * 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->translated_vars); + 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. + * + * 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. + */ +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; +} diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c new file mode 100644 index 0000000000..243d61da8f --- /dev/null +++ b/src/backend/optimizer/util/inherit.c @@ -0,0 +1,451 @@ +/*------------------------------------------------------------------------- + * + * inherit.c + * Routines to process child relations in inheritance trees + * + * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/optimizer/path/inherit.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include + +#include "access/sysattr.h" +#include "catalog/partition.h" +#include "catalog/pg_class.h" +#include "catalog/pg_inherits.h" +#include "catalog/pg_type.h" +#include "miscadmin.h" +#include "nodes/makefuncs.h" +#include "optimizer/appendinfo.h" +#include "optimizer/clauses.h" +#include "optimizer/inherit.h" +#include "optimizer/pathnode.h" +#include "optimizer/paths.h" +#include "optimizer/plancat.h" +#include "optimizer/planmain.h" +#include "optimizer/planner.h" +#include "optimizer/prep.h" +#include "optimizer/restrictinfo.h" +#include "optimizer/tlist.h" +#include "optimizer/var.h" +#include "partitioning/partprune.h" +#include "utils/rel.h" + +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); + +/* + * 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 = make_append_rel_info(parentrel, childrel, + parentRTindex, childRTindex); + *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); + } +} diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 5921e893c1..b2637d0e89 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -20,6 +20,7 @@ #include "foreign/fdwapi.h" #include "nodes/extensible.h" #include "nodes/nodeFuncs.h" +#include "optimizer/appendinfo.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" #include "optimizer/pathnode.h" diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 111b42d654..fe83ec4519 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -17,6 +17,7 @@ #include #include "miscadmin.h" +#include "optimizer/appendinfo.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" #include "optimizer/pathnode.h" diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.c index 0684083a8d..901433c68c 100644 --- a/src/backend/partitioning/partprune.c +++ b/src/backend/partitioning/partprune.c @@ -44,6 +44,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" +#include "optimizer/appendinfo.h" #include "optimizer/clauses.h" #include "optimizer/pathnode.h" #include "optimizer/planner.h" diff --git a/src/include/optimizer/appendinfo.h b/src/include/optimizer/appendinfo.h new file mode 100644 index 0000000000..2348c9c13a --- /dev/null +++ b/src/include/optimizer/appendinfo.h @@ -0,0 +1,42 @@ +/*------------------------------------------------------------------------- + * + * appendinfo.h + * Routines for mapping expressions between append rel parent(s) and + * children + * + * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/optimizer/appendinfo.h + * + *------------------------------------------------------------------------- + */ +#ifndef APPENDINFO_H +#define APPENDINFO_H + +#include "nodes/plannodes.h" +#include "nodes/relation.h" +#include "utils/relcache.h" + +extern AppendRelInfo *make_append_rel_info(Relation parentrel, + Relation childrel, + Index parentRTindex, Index childRTindex); +extern Bitmapset *translate_col_privs(const Bitmapset *parent_privs, + List *translated_vars); +extern Node *adjust_appendrel_attrs(PlannerInfo *root, Node *node, + int nappinfos, AppendRelInfo **appinfos); + +extern Node *adjust_appendrel_attrs_multilevel(PlannerInfo *root, Node *node, + Relids child_relids, + Relids top_parent_relids); + +extern AppendRelInfo **find_appinfos_by_relids(PlannerInfo *root, + Relids relids, int *nappinfos); + +extern SpecialJoinInfo *build_child_join_sjinfo(PlannerInfo *root, + SpecialJoinInfo *parent_sjinfo, + Relids left_relids, Relids right_relids); +extern Relids adjust_child_relids_multilevel(PlannerInfo *root, Relids relids, + Relids child_relids, Relids top_parent_relids); + +#endif /* APPENDINFO_H */ diff --git a/src/include/optimizer/inherit.h b/src/include/optimizer/inherit.h new file mode 100644 index 0000000000..c0bd54d9ca --- /dev/null +++ b/src/include/optimizer/inherit.h @@ -0,0 +1,25 @@ +/*------------------------------------------------------------------------- + * + * inherit.h + * prototypes for inherit.c. + * + * + * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/optimizer/inherit.h + * + *------------------------------------------------------------------------- + */ +#ifndef INHERIT_H +#define INHERIT_H + +#include "nodes/relation.h" + +/* + * inherit.c + * utilities for dealing with append relations + */ +extern void expand_inherited_tables(PlannerInfo *root); + +#endif /* INHERIT_H */ diff --git a/src/include/optimizer/prep.h b/src/include/optimizer/prep.h index d6991d5690..62d45dd142 100644 --- a/src/include/optimizer/prep.h +++ b/src/include/optimizer/prep.h @@ -47,22 +47,4 @@ extern PlanRowMark *get_plan_rowmark(List *rowmarks, Index rtindex); */ extern RelOptInfo *plan_set_operations(PlannerInfo *root); -extern void expand_inherited_tables(PlannerInfo *root); - -extern Node *adjust_appendrel_attrs(PlannerInfo *root, Node *node, - int nappinfos, AppendRelInfo **appinfos); - -extern Node *adjust_appendrel_attrs_multilevel(PlannerInfo *root, Node *node, - Relids child_relids, - Relids top_parent_relids); - -extern AppendRelInfo **find_appinfos_by_relids(PlannerInfo *root, - Relids relids, int *nappinfos); - -extern SpecialJoinInfo *build_child_join_sjinfo(PlannerInfo *root, - SpecialJoinInfo *parent_sjinfo, - Relids left_relids, Relids right_relids); -extern Relids adjust_child_relids_multilevel(PlannerInfo *root, Relids relids, - Relids child_relids, Relids top_parent_relids); - #endif /* PREP_H */ -- 2.11.0