From 3b86331dd5a2368adc39c9fef92f3dd09d817a08 Mon Sep 17 00:00:00 2001 From: amit Date: Wed, 7 Nov 2018 16:51:31 +0900 Subject: [PATCH v11 4/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 only moves the code and contains no functional changes. --- 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 | 1 + src/backend/optimizer/prep/prepunion.c | 1573 ------------------------------- src/backend/optimizer/util/Makefile | 5 +- src/backend/optimizer/util/appendinfo.c | 851 +++++++++++++++++ src/backend/optimizer/util/inherit.c | 776 +++++++++++++++ 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 | 43 + src/include/optimizer/inherit.h | 25 + src/include/optimizer/prep.h | 19 - 15 files changed, 1708 insertions(+), 1594 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 be7b8a717e..a3a686a153 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 255e8f86a0..8b4ba1a828 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 be2e39fd4b..23335a1c2d 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 3f060927cc..825998ad51 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 b9b56d9280..db277385d5 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -39,6 +39,7 @@ #include "nodes/print.h" #endif #include "nodes/relation.h" +#include "optimizer/appendinfo.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" #include "optimizer/pathnode.h" diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index 632781acc2..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 * @@ -39,32 +34,21 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" -#include "optimizer/clauses.h" #include "optimizer/cost.h" #include "optimizer/pathnode.h" #include "optimizer/paths.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 "parser/parse_coerce.h" #include "parser/parsetree.h" -#include "partitioning/partprune.h" #include "utils/lsyscache.h" #include "utils/rel.h" #include "utils/selfuncs.h" #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, @@ -103,34 +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_append_rtentry(PlannerInfo *root, RelOptInfo *rel, - RangeTblEntry *rte, Index rti); -static void expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, - Index rti, RelOptInfo *rel); -static void expand_partitioned_rtentry(PlannerInfo *root, - RangeTblEntry *parentrte, - Index parentRTindex, RelOptInfo *parentrel); -static RelOptInfo *add_inheritance_child_rel(PlannerInfo *root, - RangeTblEntry *parentrte, - Index parentRTindex, RelOptInfo *parentrel, - PlanRowMark *top_parentrc, Relation childrel, - RangeTblEntry **childrte_p, Index *childRTindex_p); -static void make_inh_translation_list(TupleDesc old_tupdesc, - TupleDesc new_tupdesc, - Oid from_rel, Oid to_rel, - Index newvarno, List **translated_vars); -static RelOptInfo *build_append_child_rel(PlannerInfo *root, - RelOptInfo *parent, - Index childRTindex); -static List *add_rowmark_junk_columns(PlannerInfo *root, PlanRowMark *rc); -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); /* @@ -1467,1532 +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) -{ - int orig_rtable_size; - Index rti; - - Assert(root->simple_rel_array_size > 0); - orig_rtable_size = root->simple_rel_array_size; - - /* - * expand_append_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. - */ - for (rti = 1; rti < orig_rtable_size; rti++) - { - RelOptInfo *brel = root->simple_rel_array[rti]; - RangeTblEntry *rte = root->simple_rte_array[rti]; - - /* there may be empty slots corresponding to non-baserel RTEs */ - if (brel == NULL) - continue; - - if (rte->inh) - expand_append_rtentry(root, brel, rte, rti); - } -} - -/* - * expand_append_rtentry - * This initializes RelOptInfos for an appendrel's child relations, if - * any - * - * 'rel' is the appendrel parent, whose range table entry ('rte') has been - * marked to require adding children. An appendrel parent could either - * be a subquery (if we flattened UNION ALL query) or a table that's known - * to have inheritance children. The latter consists of both regular - * inheritance parents and partitioned tables. - * - * For a subquery parent, there is not much to be done here because the - * children's RTEs are already present in the query, so we just initialize - * RelOptInfos for them. Also, the AppendRelInfos for child subqueries - * have already been added. - * - * For tables, we need to add the children to the range table and initialize - * AppendRelInfos, RelOptInfos, and PlanRowMarks (if any) for them. For - * a partitioned parent, we only add the children remaining after pruning. - * For regular inheritance parents, we find the children using - * find_all_inheritors and add all of them. - * - * If it turns out that there are no children, then we set rte->inh to false - * to let the caller know that only the parent table needs to be scanned. The - * caller can accordingly switch to a non-Append path. For a partitioned - * parent, that means an empty relation because parents themselves contain no - * data. - * - * For the regular inheritance case, the parent also gets another RTE with - * inh = false to represent it as an appendrel child. The original RTE is - * considered to represent the whole inheritance set. - */ -static void -expand_append_rtentry(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte, - Index rti) -{ - Assert(rte->inh); - /* Inheritance parent (partitioned or not) or UNION ALL parent subquery. */ - Assert(rte->rtekind == RTE_RELATION || rte->rtekind == RTE_SUBQUERY); - - /* - * UNION ALL children already got RTEs and AppendRelInfos, so just build - * RelOptInfos and return. - * - * It might be a bit odd that this code is in this, because there is - * nothing to expand really. - */ - if (rte->rtekind == RTE_SUBQUERY) - { - ListCell *l; - - /* - * We don't need to use expand_planner_arrays in this case, because - * no new child RTEs are created. setup_simple_rel_arrays() and - * setup_append_rel_array would've considered these child RTEs when - * allocating space for various arrays. - */ - foreach(l, root->append_rel_list) - { - AppendRelInfo *appinfo = lfirst(l); - Index childRTindex = appinfo->child_relid; - - if (appinfo->parent_relid != rti) - continue; - - Assert(childRTindex < root->simple_rel_array_size); - Assert(root->simple_rte_array[childRTindex] != NULL); - - /* - * We set the correct value of baserestricinfo and - * baserestrict_min_security below. - */ - root->simple_rel_array[childRTindex] = - build_append_child_rel(root, rel, appinfo->child_relid); - } - } - else - { - Assert(rte->rtekind == RTE_RELATION); - Assert(has_subclass(rte->relid)); - - /* - * 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. - */ - Assert(rte->rellockmode != NoLock); - - if (rte->relkind == RELKIND_PARTITIONED_TABLE) - expand_partitioned_rtentry(root, rte, rti, rel); - else - expand_inherited_rtentry(root, rte, rti, rel); - } -} - -/* - * expand_inherited_rtentry - * 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. - * - * 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. - */ -static void -expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti, - RelOptInfo *rel) -{ - Oid parentOID; - PlanRowMark *oldrc; - LOCKMODE lockmode = rte->rellockmode; - List *inhOIDs; - ListCell *l; - int num_children; - int num_children_added = 0; - - Assert(rte->rtekind == RTE_RELATION); - Assert(lockmode != NoLock); - parentOID = rte->relid; - - /* 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 has_subclass() check performed by - * subquery_planner, if table once had a child but no longer does. - */ - num_children = list_length(inhOIDs); - if (num_children < 2) - { - /* Clear flag before returning */ - rte->inh = false; - return; - } - - /* - * If parent relation is selected FOR UPDATE/SHARE, preprocess_rowmarks - * should've set isParent = true. We'll generate a new PlanRowMark for - * each child. - */ - oldrc = get_plan_rowmark(root->rowMarks, rti); - Assert(oldrc == NULL || oldrc->isParent); - - /* - * Must expand PlannerInfo arrays by num_children before we can add - * children. - */ - expand_planner_arrays(root, num_children); - - foreach(l, inhOIDs) - { - Oid childOID = lfirst_oid(l); - Relation newrelation; - RangeTblEntry *childrte; - Index childRTindex; - - /* Already locked above. */ - newrelation = heap_open(childOID, NoLock); - - /* - * 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 (RELATION_IS_OTHER_TEMP(newrelation)) - { - heap_close(newrelation, lockmode); - continue; - } - - (void) add_inheritance_child_rel(root, rte, rti, rel, oldrc, - newrelation, &childrte, - &childRTindex); - Assert(childrte != NULL); - /* All regular inheritance children are leaf children. */ - Assert(!childrte->inh); - Assert(childRTindex > 0); - - /* Close child relations, but keep locks */ - heap_close(newrelation, NoLock); - num_children_added++; - } - - /* - * If all children, including the parent (as child rel), were - * excluded, mark the parent rel as empty. 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 (num_children_added == 0) - mark_dummy_rel(rel); - else if (num_children_added == 1) - rte->inh = false; - - /* - * Add junk columns needed by the row mark if any and also add the - * relevant expressions to the root parent's reltarget. - */ - if (oldrc) - { - List *tlist = add_rowmark_junk_columns(root, oldrc); - - build_base_rel_tlists(root, tlist); - } -} - -/* - * expand_partitioned_rtentry - * Prunes unnecessary partitions of a partitioned table and adds - * remaining ones to the Query and the PlannerInfo - * - * Partitions are added to the query in order in which they are found in - * the parent's PartitionDesc. - * - * Note: even though only the unpruned partitions will be added to the - * resulting plan, this still locks *all* partitions via find_all_inheritors - * when this function is called for the root partitioned table. - */ -static void -expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte, - Index parentRTindex, RelOptInfo *parentrel) -{ - LOCKMODE lockmode = parentrte->rellockmode; - PlanRowMark *rootrc = NULL; - int i; - Bitmapset *partindexes; - Index rootParentRTindex = parentrel->inh_root_parent > 0 ? - parentrel->inh_root_parent : - parentRTindex; - - /* If root partitioned table, lock *all* partitions in the tree. */ - if (parentRTindex == rootParentRTindex) - (void) find_all_inheritors(parentrte->relid, lockmode, NULL); - - /* - * Initialize partitioned_child_rels to contain this RT index. - * - * Note that during the set_append_rel_pathlist() phase, values of the - * indexes of partitioned relations that appear down in the tree will be - * bubbled up into root parent's list so that when we've created Paths for - * all the children, the root table's list will contain all such indexes. - */ - parentrel->partitioned_child_rels = list_make1_int(parentRTindex); - - /* Perform pruning. */ - partindexes = prune_append_rel_partitions(parentrel); - - /* Must expand PlannerInfo arrays before we can add children. */ - expand_planner_arrays(root, bms_num_members(partindexes)); - - /* - * For partitioned tables, we also store the partition RelOptInfo - * pointers in the parent's RelOptInfo. - */ - parentrel->part_rels = (RelOptInfo **) palloc0(sizeof(RelOptInfo *) * - parentrel->nparts); - - rootrc = get_plan_rowmark(root->rowMarks, rootParentRTindex); - Assert(rootrc == NULL || rootrc->isParent); - i = -1; - while ((i = bms_next_member(partindexes, i)) >= 0) - { - Oid childOID = parentrel->part_oids[i]; - Relation newrelation; - RelOptInfo *childrel; - RangeTblEntry *childrte; - Index childRTindex; - - /* Already locked above. */ - newrelation = heap_open(childOID, NoLock); - Assert(!RELATION_IS_OTHER_TEMP(newrelation)); - - /* - * A partitioned child table with 0 children is a dummy rel, so don't - * bother creating planner objects for it. - */ - if (newrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && - RelationGetPartitionDesc(newrelation)->nparts == 0) - { - heap_close(newrelation, NoLock); - continue; - } - - childrel = add_inheritance_child_rel(root, parentrte, parentRTindex, - parentrel, rootrc, newrelation, - &childrte, &childRTindex); - Assert(childrel != NULL); - parentrel->part_rels[i] = childrel; - - /* Close child relations, but keep locks */ - heap_close(newrelation, NoLock); - - /* If the child is partitioned itself, expand it too. */ - if (childrel->part_scheme) - { - Assert(childrte->inh); - expand_partitioned_rtentry(root, childrte, childRTindex, - childrel); - } - } - - /* - * Add junk columns needed by the row mark if any and also add the - * relevant expressions to the root parent's reltarget. - */ - if (rootrc) - { - List *tlist = add_rowmark_junk_columns(root, rootrc); - - build_base_rel_tlists(root, tlist); - } -} - -/* - * add_inheritance_child_rel - * Build a RangeTblEntry, an AppendRelInfo, a PlanRowMark, and finally - * a RelOptInfo for an inheritance child relation. - * - * The return value is the RelOptInfo that's added. - * - * 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. - */ -static RelOptInfo * -add_inheritance_child_rel(PlannerInfo *root, RangeTblEntry *parentrte, - Index parentRTindex, RelOptInfo *parentrel, - PlanRowMark *top_parentrc, Relation childrel, - RangeTblEntry **childrte_p, Index *childRTindex_p) -{ - Query *parse = root->parse; - Oid childOID = RelationGetRelid(childrel); - RangeTblEntry *childrte; - Index childRTindex; - AppendRelInfo *appinfo; - RelOptInfo *childrelopt; - - /* - * 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 appropriately. 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 as an append parent - * itself, so set its inh to true. - */ - childrte->inh = (childrte->relkind == RELKIND_PARTITIONED_TABLE); - childrte->requiredPerms = 0; - childrte->securityQuals = NIL; - parse->rtable = lappend(parse->rtable, childrte); - childRTindex = list_length(parse->rtable); - *childRTindex_p = childRTindex; - - /* Create an AppendRelInfo and add it to planner's global list. */ - appinfo = make_append_rel_info(parentrel, parentrte, - childrel->rd_att, - childOID, - childrel->rd_rel->reltype, - childRTindex); - root->append_rel_list = lappend(root->append_rel_list, 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 (childrte->relid != parentrte->relid) - { - 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); - } - - /* - * Add the RelOptInfo. Even though we may not really scan this relation - * for reasons such as contradictory quals, we still need to create one, - * because for every RTE in the query's range table, there must be an - * accompanying RelOptInfo. - */ - - /* First, store the RTE and appinfos into planner arrays. */ - Assert(root->simple_rte_array[childRTindex] == NULL); - root->simple_rte_array[childRTindex] = childrte; - Assert(root->append_rel_array[childRTindex] == NULL); - root->append_rel_array[childRTindex] = appinfo; - - childrelopt = build_append_child_rel(root, parentrel, childRTindex); - Assert(childrelopt != NULL); - - return childrelopt; -} - -/* - * 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(TupleDesc old_tupdesc, TupleDesc new_tupdesc, - Oid from_rel, Oid to_rel, - Index newvarno, List **translated_vars) -{ - List *vars = NIL; - 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 (from_rel == to_rel) - { - 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(to_rel, attname); - if (!newtup) - elog(ERROR, "could not find inherited attribute \"%s\" of relation \"%s\"", - attname, get_rel_name(to_rel)); - 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, get_rel_name(to_rel)); - if (attcollation != att->attcollation) - elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's collation", - attname, get_rel_name(to_rel)); - - 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; -} - -/* - * build_append_child_rel - * Build a RelOptInfo for child relation of an append rel - * - * After creating the RelOptInfo for the given child RT index, it goes on to - * initialize some of its fields based on the parent RelOptInfo. - * - * If the quals in baserestrictinfo turn out to be self-contradictory, - * RelOptInfo is marked dummy before returning. - */ -static RelOptInfo * -build_append_child_rel(PlannerInfo *root, - RelOptInfo *parent, - Index childRTindex) -{ - RelOptInfo *childrel; - RangeTblEntry *childRTE = root->simple_rte_array[childRTindex]; - AppendRelInfo *appinfo = root->append_rel_array[childRTindex]; - List *childquals; - ListCell *lc; - bool have_const_false_cq; - Index cq_min_security; - - /* Build the RelOptInfo. */ - childrel = build_simple_rel(root, childRTindex, parent); - - /* - * Propagate lateral_relids and lateral_referencers from appendrel - * parent rels to their child rels. We intentionally give each child rel - * the same minimum parameterization, even though it's quite possible that - * some don't reference all the lateral rels. This is because any append - * path for the parent will have to have the same parameterization for - * every child anyway, and there's no value in forcing extra - * reparameterize_path() calls. Similarly, a lateral reference to the - * parent prevents use of otherwise-movable join rels for each child. - */ - childrel->direct_lateral_relids = parent->direct_lateral_relids; - childrel->lateral_relids = parent->lateral_relids; - childrel->lateral_referencers = parent->lateral_referencers; - - /* - * We have to copy the parent's quals to the child, with appropriate - * substitution of variables. However, only the baserestrictinfo - * quals are needed before we can check for constraint exclusion; so - * do that first and then check to see if we can disregard this child. - * - * The child rel's targetlist might contain non-Var expressions, which - * means that substitution into the quals could produce opportunities - * for const-simplification, and perhaps even pseudoconstant quals. - * Therefore, transform each RestrictInfo separately to see if it - * reduces to a constant or pseudoconstant. (We must process them - * separately to keep track of the security level of each qual.) - */ - childquals = false; - cq_min_security = UINT_MAX; - have_const_false_cq = false; - foreach(lc, parent->baserestrictinfo) - { - RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); - Node *childqual = (Node *) rinfo->clause; - ListCell *lc2; - - Assert(IsA(rinfo, RestrictInfo)); - childqual = adjust_appendrel_attrs(root, childqual, - 1, &appinfo); - childqual = eval_const_expressions(root, childqual); - /* check for flat-out constant */ - if (childqual && IsA(childqual, Const)) - { - if (((Const *) childqual)->constisnull || - !DatumGetBool(((Const *) childqual)->constvalue)) - { - /* Restriction reduces to constant FALSE or NULL */ - have_const_false_cq = true; - break; - } - /* Restriction reduces to constant TRUE, so drop it */ - continue; - } - /* might have gotten an AND clause, if so flatten it */ - foreach(lc2, make_ands_implicit((Expr *) childqual)) - { - Node *onecq = (Node *) lfirst(lc2); - bool pseudoconstant; - - /* check for pseudoconstant (no Vars or volatile functions) */ - pseudoconstant = - !contain_vars_of_level(onecq, 0) && - !contain_volatile_functions(onecq); - if (pseudoconstant) - { - /* tell createplan.c to check for gating quals */ - root->hasPseudoConstantQuals = true; - } - /* reconstitute RestrictInfo with appropriate properties */ - childquals = lappend(childquals, - make_restrictinfo((Expr *) onecq, - rinfo->is_pushed_down, - rinfo->outerjoin_delayed, - pseudoconstant, - rinfo->security_level, - NULL, NULL, NULL)); - cq_min_security = Min(cq_min_security, rinfo->security_level); - } - } - - /* - * In addition to the quals inherited from the parent, we might - * have securityQuals associated with this particular child node. - * (Currently this can only happen in appendrels originating from - * UNION ALL; inheritance child tables don't have their own - * securityQuals.) Pull any such securityQuals up into the - * baserestrictinfo for the child. This is similar to - * process_security_barrier_quals() for the parent rel, except - * that we can't make any general deductions from such quals, - * since they don't hold for the whole appendrel. - */ - if (childRTE->securityQuals) - { - Index security_level = 0; - - foreach(lc, childRTE->securityQuals) - { - List *qualset = (List *) lfirst(lc); - ListCell *lc2; - - foreach(lc2, qualset) - { - Expr *qual = (Expr *) lfirst(lc2); - - /* - * not likely that we'd see constants here, so no - * check - */ - childquals = lappend(childquals, - make_restrictinfo(qual, - true, - false, - false, - security_level, - NULL, NULL, - NULL)); - cq_min_security = Min(cq_min_security, security_level); - } - security_level++; - } - Assert(security_level <= root->qual_security_level); - } - - /* Set child's version of baserestrictinfo. */ - childrel->baserestrictinfo = childquals; - childrel->baserestrict_min_security = cq_min_security; - - if (have_const_false_cq) - { - /* - * Some restriction clause reduced to constant FALSE or NULL after - * substitution, so this child need not be scanned. - */ - set_dummy_rel_pathlist(childrel); - } - - return childrel; -} - -/* - * add_rowmark_junk_columns - * Add necessary junk columns for rowmarked inheritance parent rel. - * - * These values are needed for locking of rels selected FOR UPDATE/SHARE, and - * to do EvalPlanQual rechecking. See comments for PlanRowMark in - * plannodes.h. - */ -static List * -add_rowmark_junk_columns(PlannerInfo *root, PlanRowMark *rc) -{ - List *tlist = root->processed_tlist; - Var *var; - char resname[32]; - TargetEntry *tle; - - if (rc->allMarkTypes & ~(1 << ROW_MARK_COPY)) - { - /* Need to fetch TID */ - var = makeVar(rc->rti, - SelfItemPointerAttributeNumber, - TIDOID, - -1, - InvalidOid, - 0); - snprintf(resname, sizeof(resname), "ctid%u", rc->rowmarkId); - tle = makeTargetEntry((Expr *) var, - list_length(tlist) + 1, - pstrdup(resname), - true); - tlist = lappend(tlist, tle); - } - if (rc->allMarkTypes & (1 << ROW_MARK_COPY)) - { - /* Need the whole row as a junk var */ - var = makeWholeRowVar(root->simple_rte_array[rc->rti], - rc->rti, - 0, - false); - snprintf(resname, sizeof(resname), "wholerow%u", rc->rowmarkId); - tle = makeTargetEntry((Expr *) var, - list_length(tlist) + 1, - pstrdup(resname), - true); - tlist = lappend(tlist, tle); - } - - /* For inheritance cases, always fetch the tableoid too. */ - var = makeVar(rc->rti, - TableOidAttributeNumber, - OIDOID, - -1, - InvalidOid, - 0); - snprintf(resname, sizeof(resname), "tableoid%u", rc->rowmarkId); - tle = makeTargetEntry((Expr *) var, - list_length(tlist) + 1, - pstrdup(resname), - true); - tlist = lappend(tlist, tle); - - return tlist; -} - -AppendRelInfo * -make_append_rel_info(RelOptInfo *parentrel, RangeTblEntry *parentrte, - TupleDesc childdesc, Oid childoid, Oid childreltype, - Index childRTindex) -{ - AppendRelInfo *appinfo = makeNode(AppendRelInfo); - - appinfo->parent_relid = parentrel->relid; - appinfo->child_relid = childRTindex; - appinfo->parent_reltype = parentrel->reltype; - appinfo->child_reltype = childreltype; - make_inh_translation_list(parentrel->tupdesc, childdesc, - parentrte->relid, childoid, - childRTindex, - &appinfo->translated_vars); - appinfo->parent_reloid = parentrte->relid; - - return appinfo; -} - -/* - * 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; - } - - /* - * This is needed, because inheritance_make_rel_from_joinlist needs to - * translate root->join_info_list executing make_rel_from_joinlist for a - * given child. - */ - if (IsA(node, SpecialJoinInfo)) - { - SpecialJoinInfo *oldinfo = (SpecialJoinInfo *) node; - SpecialJoinInfo *newinfo = makeNode(SpecialJoinInfo); - - memcpy(newinfo, oldinfo, sizeof(SpecialJoinInfo)); - newinfo->min_lefthand = adjust_child_relids(oldinfo->min_lefthand, - context->nappinfos, - context->appinfos); - newinfo->min_righthand = adjust_child_relids(oldinfo->min_righthand, - context->nappinfos, - context->appinfos); - newinfo->syn_lefthand = adjust_child_relids(oldinfo->syn_lefthand, - context->nappinfos, - context->appinfos); - newinfo->syn_righthand = adjust_child_relids(oldinfo->syn_righthand, - context->nappinfos, - context->appinfos); - newinfo->semi_rhs_exprs = - (List *) expression_tree_mutator((Node *) - oldinfo->semi_rhs_exprs, - adjust_appendrel_attrs_mutator, - (void *) context); - return (Node *) newinfo; - } - - /* Shouldn't need to handle planner auxiliary nodes here */ - 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 da9ccf32b4..8a5e2ad1bd 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..7d3937b3ce --- /dev/null +++ b/src/backend/optimizer/util/appendinfo.c @@ -0,0 +1,851 @@ +/*------------------------------------------------------------------------- + * + * 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(TupleDesc old_tupdesc, + TupleDesc new_tupdesc, + Oid from_rel, Oid to_rel, + 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); + +AppendRelInfo * +make_append_rel_info(RelOptInfo *parentrel, RangeTblEntry *parentrte, + TupleDesc childdesc, Oid childoid, Oid childreltype, + Index childRTindex) +{ + AppendRelInfo *appinfo = makeNode(AppendRelInfo); + + appinfo->parent_relid = parentrel->relid; + appinfo->child_relid = childRTindex; + appinfo->parent_reltype = parentrel->reltype; + appinfo->child_reltype = childreltype; + make_inh_translation_list(parentrel->tupdesc, childdesc, + parentrte->relid, childoid, + childRTindex, + &appinfo->translated_vars); + appinfo->parent_reloid = parentrte->relid; + + return appinfo; +} + +/* + * 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; + } + + /* + * This is needed, because inheritance_make_rel_from_joinlist needs to + * translate root->join_info_list executing make_rel_from_joinlist for a + * given child. + */ + if (IsA(node, SpecialJoinInfo)) + { + SpecialJoinInfo *oldinfo = (SpecialJoinInfo *) node; + SpecialJoinInfo *newinfo = makeNode(SpecialJoinInfo); + + memcpy(newinfo, oldinfo, sizeof(SpecialJoinInfo)); + newinfo->min_lefthand = adjust_child_relids(oldinfo->min_lefthand, + context->nappinfos, + context->appinfos); + newinfo->min_righthand = adjust_child_relids(oldinfo->min_righthand, + context->nappinfos, + context->appinfos); + newinfo->syn_lefthand = adjust_child_relids(oldinfo->syn_lefthand, + context->nappinfos, + context->appinfos); + newinfo->syn_righthand = adjust_child_relids(oldinfo->syn_righthand, + context->nappinfos, + context->appinfos); + newinfo->semi_rhs_exprs = + (List *) expression_tree_mutator((Node *) + oldinfo->semi_rhs_exprs, + adjust_appendrel_attrs_mutator, + (void *) context); + return (Node *) newinfo; + } + + /* Shouldn't need to handle planner auxiliary nodes here */ + 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_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; +} + +/* + * 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(TupleDesc old_tupdesc, TupleDesc new_tupdesc, + Oid from_rel, Oid to_rel, + Index newvarno, List **translated_vars) +{ + List *vars = NIL; + 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 (from_rel == to_rel) + { + 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(to_rel, attname); + if (!newtup) + elog(ERROR, "could not find inherited attribute \"%s\" of relation \"%s\"", + attname, get_rel_name(to_rel)); + 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, get_rel_name(to_rel)); + if (attcollation != att->attcollation) + elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's collation", + attname, get_rel_name(to_rel)); + + 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 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; +} diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c new file mode 100644 index 0000000000..2224c5911f --- /dev/null +++ b/src/backend/optimizer/util/inherit.c @@ -0,0 +1,776 @@ +/*------------------------------------------------------------------------- + * + * 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 "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_append_rtentry(PlannerInfo *root, RelOptInfo *rel, + RangeTblEntry *rte, Index rti); +static void expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, + Index rti, RelOptInfo *rel); +static void expand_partitioned_rtentry(PlannerInfo *root, + RangeTblEntry *parentrte, + Index parentRTindex, RelOptInfo *parentrel); +static RelOptInfo *add_inheritance_child_rel(PlannerInfo *root, + RangeTblEntry *parentrte, + Index parentRTindex, RelOptInfo *parentrel, + PlanRowMark *top_parentrc, Relation childrel, + RangeTblEntry **childrte_p, Index *childRTindex_p); +static RelOptInfo *build_append_child_rel(PlannerInfo *root, + RelOptInfo *parent, + Index childRTindex); +static List *add_rowmark_junk_columns(PlannerInfo *root, PlanRowMark *rc); + + +/* + * 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) +{ + int orig_rtable_size; + Index rti; + + Assert(root->simple_rel_array_size > 0); + orig_rtable_size = root->simple_rel_array_size; + + /* + * expand_append_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. + */ + for (rti = 1; rti < orig_rtable_size; rti++) + { + RelOptInfo *brel = root->simple_rel_array[rti]; + RangeTblEntry *rte = root->simple_rte_array[rti]; + + /* there may be empty slots corresponding to non-baserel RTEs */ + if (brel == NULL) + continue; + + if (rte->inh) + expand_append_rtentry(root, brel, rte, rti); + } +} + +/* + * expand_append_rtentry + * This initializes RelOptInfos for an appendrel's child relations, if + * any + * + * 'rel' is the appendrel parent, whose range table entry ('rte') has been + * marked to require adding children. An appendrel parent could either + * be a subquery (if we flattened UNION ALL query) or a table that's known + * to have inheritance children. The latter consists of both regular + * inheritance parents and partitioned tables. + * + * For a subquery parent, there is not much to be done here because the + * children's RTEs are already present in the query, so we just initialize + * RelOptInfos for them. Also, the AppendRelInfos for child subqueries + * have already been added. + * + * For tables, we need to add the children to the range table and initialize + * AppendRelInfos, RelOptInfos, and PlanRowMarks (if any) for them. For + * a partitioned parent, we only add the children remaining after pruning. + * For regular inheritance parents, we find the children using + * find_all_inheritors and add all of them. + * + * If it turns out that there are no children, then we set rte->inh to false + * to let the caller know that only the parent table needs to be scanned. The + * caller can accordingly switch to a non-Append path. For a partitioned + * parent, that means an empty relation because parents themselves contain no + * data. + * + * For the regular inheritance case, the parent also gets another RTE with + * inh = false to represent it as an appendrel child. The original RTE is + * considered to represent the whole inheritance set. + */ +static void +expand_append_rtentry(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte, + Index rti) +{ + Assert(rte->inh); + /* Inheritance parent (partitioned or not) or UNION ALL parent subquery. */ + Assert(rte->rtekind == RTE_RELATION || rte->rtekind == RTE_SUBQUERY); + + /* + * UNION ALL children already got RTEs and AppendRelInfos, so just build + * RelOptInfos and return. + * + * It might be a bit odd that this code is in this, because there is + * nothing to expand really. + */ + if (rte->rtekind == RTE_SUBQUERY) + { + ListCell *l; + + /* + * We don't need to use expand_planner_arrays in this case, because + * no new child RTEs are created. setup_simple_rel_arrays() and + * setup_append_rel_array would've considered these child RTEs when + * allocating space for various arrays. + */ + foreach(l, root->append_rel_list) + { + AppendRelInfo *appinfo = lfirst(l); + Index childRTindex = appinfo->child_relid; + + if (appinfo->parent_relid != rti) + continue; + + Assert(childRTindex < root->simple_rel_array_size); + Assert(root->simple_rte_array[childRTindex] != NULL); + + /* + * We set the correct value of baserestricinfo and + * baserestrict_min_security below. + */ + root->simple_rel_array[childRTindex] = + build_append_child_rel(root, rel, appinfo->child_relid); + } + } + else + { + Assert(rte->rtekind == RTE_RELATION); + Assert(has_subclass(rte->relid)); + + /* + * 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. + */ + Assert(rte->rellockmode != NoLock); + + if (rte->relkind == RELKIND_PARTITIONED_TABLE) + expand_partitioned_rtentry(root, rte, rti, rel); + else + expand_inherited_rtentry(root, rte, rti, rel); + } +} + +/* + * expand_inherited_rtentry + * 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. + * + * 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. + */ +static void +expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti, + RelOptInfo *rel) +{ + Oid parentOID; + PlanRowMark *oldrc; + LOCKMODE lockmode = rte->rellockmode; + List *inhOIDs; + ListCell *l; + int num_children; + int num_children_added = 0; + + Assert(rte->rtekind == RTE_RELATION); + Assert(lockmode != NoLock); + parentOID = rte->relid; + + /* 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 has_subclass() check performed by + * subquery_planner, if table once had a child but no longer does. + */ + num_children = list_length(inhOIDs); + if (num_children < 2) + { + /* Clear flag before returning */ + rte->inh = false; + return; + } + + /* + * If parent relation is selected FOR UPDATE/SHARE, preprocess_rowmarks + * should've set isParent = true. We'll generate a new PlanRowMark for + * each child. + */ + oldrc = get_plan_rowmark(root->rowMarks, rti); + Assert(oldrc == NULL || oldrc->isParent); + + /* + * Must expand PlannerInfo arrays by num_children before we can add + * children. + */ + expand_planner_arrays(root, num_children); + + foreach(l, inhOIDs) + { + Oid childOID = lfirst_oid(l); + Relation newrelation; + RangeTblEntry *childrte; + Index childRTindex; + + /* Already locked above. */ + newrelation = heap_open(childOID, NoLock); + + /* + * 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 (RELATION_IS_OTHER_TEMP(newrelation)) + { + heap_close(newrelation, lockmode); + continue; + } + + (void) add_inheritance_child_rel(root, rte, rti, rel, oldrc, + newrelation, &childrte, + &childRTindex); + Assert(childrte != NULL); + /* All regular inheritance children are leaf children. */ + Assert(!childrte->inh); + Assert(childRTindex > 0); + + /* Close child relations, but keep locks */ + heap_close(newrelation, NoLock); + num_children_added++; + } + + /* + * If all children, including the parent (as child rel), were + * excluded, mark the parent rel as empty. 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 (num_children_added == 0) + mark_dummy_rel(rel); + else if (num_children_added == 1) + rte->inh = false; + + /* + * Add junk columns needed by the row mark if any and also add the + * relevant expressions to the root parent's reltarget. + */ + if (oldrc) + { + List *tlist = add_rowmark_junk_columns(root, oldrc); + + build_base_rel_tlists(root, tlist); + } +} + +/* + * expand_partitioned_rtentry + * Prunes unnecessary partitions of a partitioned table and adds + * remaining ones to the Query and the PlannerInfo + * + * Partitions are added to the query in order in which they are found in + * the parent's PartitionDesc. + * + * Note: even though only the unpruned partitions will be added to the + * resulting plan, this still locks *all* partitions via find_all_inheritors + * when this function is called for the root partitioned table. + */ +static void +expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte, + Index parentRTindex, RelOptInfo *parentrel) +{ + LOCKMODE lockmode = parentrte->rellockmode; + PlanRowMark *rootrc = NULL; + int i; + Bitmapset *partindexes; + Index rootParentRTindex = parentrel->inh_root_parent > 0 ? + parentrel->inh_root_parent : + parentRTindex; + + /* If root partitioned table, lock *all* partitions in the tree. */ + if (parentRTindex == rootParentRTindex) + (void) find_all_inheritors(parentrte->relid, lockmode, NULL); + + /* + * Initialize partitioned_child_rels to contain this RT index. + * + * Note that during the set_append_rel_pathlist() phase, values of the + * indexes of partitioned relations that appear down in the tree will be + * bubbled up into root parent's list so that when we've created Paths for + * all the children, the root table's list will contain all such indexes. + */ + parentrel->partitioned_child_rels = list_make1_int(parentRTindex); + + /* Perform pruning. */ + partindexes = prune_append_rel_partitions(parentrel); + + /* Must expand PlannerInfo arrays before we can add children. */ + expand_planner_arrays(root, bms_num_members(partindexes)); + + /* + * For partitioned tables, we also store the partition RelOptInfo + * pointers in the parent's RelOptInfo. + */ + parentrel->part_rels = (RelOptInfo **) palloc0(sizeof(RelOptInfo *) * + parentrel->nparts); + + rootrc = get_plan_rowmark(root->rowMarks, rootParentRTindex); + Assert(rootrc == NULL || rootrc->isParent); + i = -1; + while ((i = bms_next_member(partindexes, i)) >= 0) + { + Oid childOID = parentrel->part_oids[i]; + Relation newrelation; + RelOptInfo *childrel; + RangeTblEntry *childrte; + Index childRTindex; + + /* Already locked above. */ + newrelation = heap_open(childOID, NoLock); + Assert(!RELATION_IS_OTHER_TEMP(newrelation)); + + /* + * A partitioned child table with 0 children is a dummy rel, so don't + * bother creating planner objects for it. + */ + if (newrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && + RelationGetPartitionDesc(newrelation)->nparts == 0) + { + heap_close(newrelation, NoLock); + continue; + } + + childrel = add_inheritance_child_rel(root, parentrte, parentRTindex, + parentrel, rootrc, newrelation, + &childrte, &childRTindex); + Assert(childrel != NULL); + parentrel->part_rels[i] = childrel; + + /* Close child relations, but keep locks */ + heap_close(newrelation, NoLock); + + /* If the child is partitioned itself, expand it too. */ + if (childrel->part_scheme) + { + Assert(childrte->inh); + expand_partitioned_rtentry(root, childrte, childRTindex, + childrel); + } + } + + /* + * Add junk columns needed by the row mark if any and also add the + * relevant expressions to the root parent's reltarget. + */ + if (rootrc) + { + List *tlist = add_rowmark_junk_columns(root, rootrc); + + build_base_rel_tlists(root, tlist); + } +} + +/* + * add_inheritance_child_rel + * Build a RangeTblEntry, an AppendRelInfo, a PlanRowMark, and finally + * a RelOptInfo for an inheritance child relation. + * + * The return value is the RelOptInfo that's added. + * + * 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. + */ +static RelOptInfo * +add_inheritance_child_rel(PlannerInfo *root, RangeTblEntry *parentrte, + Index parentRTindex, RelOptInfo *parentrel, + PlanRowMark *top_parentrc, Relation childrel, + RangeTblEntry **childrte_p, Index *childRTindex_p) +{ + Query *parse = root->parse; + Oid childOID = RelationGetRelid(childrel); + RangeTblEntry *childrte; + Index childRTindex; + AppendRelInfo *appinfo; + RelOptInfo *childrelopt; + + /* + * 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 appropriately. 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 as an append parent + * itself, so set its inh to true. + */ + childrte->inh = (childrte->relkind == RELKIND_PARTITIONED_TABLE); + childrte->requiredPerms = 0; + childrte->securityQuals = NIL; + parse->rtable = lappend(parse->rtable, childrte); + childRTindex = list_length(parse->rtable); + *childRTindex_p = childRTindex; + + /* Create an AppendRelInfo and add it to planner's global list. */ + appinfo = make_append_rel_info(parentrel, parentrte, + childrel->rd_att, + childOID, + childrel->rd_rel->reltype, + childRTindex); + root->append_rel_list = lappend(root->append_rel_list, 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 (childrte->relid != parentrte->relid) + { + 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); + } + + /* + * Add the RelOptInfo. Even though we may not really scan this relation + * for reasons such as contradictory quals, we still need to create one, + * because for every RTE in the query's range table, there must be an + * accompanying RelOptInfo. + */ + + /* First, store the RTE and appinfos into planner arrays. */ + Assert(root->simple_rte_array[childRTindex] == NULL); + root->simple_rte_array[childRTindex] = childrte; + Assert(root->append_rel_array[childRTindex] == NULL); + root->append_rel_array[childRTindex] = appinfo; + + childrelopt = build_append_child_rel(root, parentrel, childRTindex); + Assert(childrelopt != NULL); + + return childrelopt; +} + +/* + * build_append_child_rel + * Build a RelOptInfo for child relation of an append rel + * + * After creating the RelOptInfo for the given child RT index, it goes on to + * initialize some of its fields based on the parent RelOptInfo. + * + * If the quals in baserestrictinfo turn out to be self-contradictory, the + * RelOptInfo is marked dummy before returning. + */ +static RelOptInfo * +build_append_child_rel(PlannerInfo *root, + RelOptInfo *parent, + Index childRTindex) +{ + RelOptInfo *childrel; + RangeTblEntry *childRTE = root->simple_rte_array[childRTindex]; + AppendRelInfo *appinfo = root->append_rel_array[childRTindex]; + List *childquals; + ListCell *lc; + bool have_const_false_cq; + Index cq_min_security; + + /* Build the RelOptInfo. */ + childrel = build_simple_rel(root, childRTindex, parent); + + /* + * Propagate lateral_relids and lateral_referencers from appendrel + * parent rels to their child rels. We intentionally give each child rel + * the same minimum parameterization, even though it's quite possible that + * some don't reference all the lateral rels. This is because any append + * path for the parent will have to have the same parameterization for + * every child anyway, and there's no value in forcing extra + * reparameterize_path() calls. Similarly, a lateral reference to the + * parent prevents use of otherwise-movable join rels for each child. + */ + childrel->direct_lateral_relids = parent->direct_lateral_relids; + childrel->lateral_relids = parent->lateral_relids; + childrel->lateral_referencers = parent->lateral_referencers; + + /* + * We have to copy the parent's quals to the child, with appropriate + * substitution of variables. However, only the baserestrictinfo + * quals are needed before we can check for constraint exclusion; so + * do that first and then check to see if we can disregard this child. + * + * The child rel's targetlist might contain non-Var expressions, which + * means that substitution into the quals could produce opportunities + * for const-simplification, and perhaps even pseudoconstant quals. + * Therefore, transform each RestrictInfo separately to see if it + * reduces to a constant or pseudoconstant. (We must process them + * separately to keep track of the security level of each qual.) + */ + childquals = false; + cq_min_security = UINT_MAX; + have_const_false_cq = false; + foreach(lc, parent->baserestrictinfo) + { + RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); + Node *childqual = (Node *) rinfo->clause; + ListCell *lc2; + + Assert(IsA(rinfo, RestrictInfo)); + childqual = adjust_appendrel_attrs(root, childqual, + 1, &appinfo); + childqual = eval_const_expressions(root, childqual); + /* check for flat-out constant */ + if (childqual && IsA(childqual, Const)) + { + if (((Const *) childqual)->constisnull || + !DatumGetBool(((Const *) childqual)->constvalue)) + { + /* Restriction reduces to constant FALSE or NULL */ + have_const_false_cq = true; + break; + } + /* Restriction reduces to constant TRUE, so drop it */ + continue; + } + /* might have gotten an AND clause, if so flatten it */ + foreach(lc2, make_ands_implicit((Expr *) childqual)) + { + Node *onecq = (Node *) lfirst(lc2); + bool pseudoconstant; + + /* check for pseudoconstant (no Vars or volatile functions) */ + pseudoconstant = + !contain_vars_of_level(onecq, 0) && + !contain_volatile_functions(onecq); + if (pseudoconstant) + { + /* tell createplan.c to check for gating quals */ + root->hasPseudoConstantQuals = true; + } + /* reconstitute RestrictInfo with appropriate properties */ + childquals = lappend(childquals, + make_restrictinfo((Expr *) onecq, + rinfo->is_pushed_down, + rinfo->outerjoin_delayed, + pseudoconstant, + rinfo->security_level, + NULL, NULL, NULL)); + cq_min_security = Min(cq_min_security, rinfo->security_level); + } + } + + /* + * In addition to the quals inherited from the parent, we might + * have securityQuals associated with this particular child node. + * (Currently this can only happen in appendrels originating from + * UNION ALL; inheritance child tables don't have their own + * securityQuals.) Pull any such securityQuals up into the + * baserestrictinfo for the child. This is similar to + * process_security_barrier_quals() for the parent rel, except + * that we can't make any general deductions from such quals, + * since they don't hold for the whole appendrel. + */ + if (childRTE->securityQuals) + { + Index security_level = 0; + + foreach(lc, childRTE->securityQuals) + { + List *qualset = (List *) lfirst(lc); + ListCell *lc2; + + foreach(lc2, qualset) + { + Expr *qual = (Expr *) lfirst(lc2); + + /* + * not likely that we'd see constants here, so no + * check + */ + childquals = lappend(childquals, + make_restrictinfo(qual, + true, + false, + false, + security_level, + NULL, NULL, + NULL)); + cq_min_security = Min(cq_min_security, security_level); + } + security_level++; + } + Assert(security_level <= root->qual_security_level); + } + + /* Set child's version of baserestrictinfo. */ + childrel->baserestrictinfo = childquals; + childrel->baserestrict_min_security = cq_min_security; + + if (have_const_false_cq) + { + /* + * Some restriction clause reduced to constant FALSE or NULL after + * substitution, so this child need not be scanned. + */ + set_dummy_rel_pathlist(childrel); + } + + return childrel; +} + +/* + * add_rowmark_junk_columns + * Add necessary junk columns for rowmarked inheritance parent rel. + * + * These values are needed for locking of rels selected FOR UPDATE/SHARE, and + * to do EvalPlanQual rechecking. See comments for PlanRowMark in + * plannodes.h. + */ +static List * +add_rowmark_junk_columns(PlannerInfo *root, PlanRowMark *rc) +{ + List *tlist = root->processed_tlist; + Var *var; + char resname[32]; + TargetEntry *tle; + + if (rc->allMarkTypes & ~(1 << ROW_MARK_COPY)) + { + /* Need to fetch TID */ + var = makeVar(rc->rti, + SelfItemPointerAttributeNumber, + TIDOID, + -1, + InvalidOid, + 0); + snprintf(resname, sizeof(resname), "ctid%u", rc->rowmarkId); + tle = makeTargetEntry((Expr *) var, + list_length(tlist) + 1, + pstrdup(resname), + true); + tlist = lappend(tlist, tle); + } + if (rc->allMarkTypes & (1 << ROW_MARK_COPY)) + { + /* Need the whole row as a junk var */ + var = makeWholeRowVar(root->simple_rte_array[rc->rti], + rc->rti, + 0, + false); + snprintf(resname, sizeof(resname), "wholerow%u", rc->rowmarkId); + tle = makeTargetEntry((Expr *) var, + list_length(tlist) + 1, + pstrdup(resname), + true); + tlist = lappend(tlist, tle); + } + + /* For inheritance cases, always fetch the tableoid too. */ + var = makeVar(rc->rti, + TableOidAttributeNumber, + OIDOID, + -1, + InvalidOid, + 0); + snprintf(resname, sizeof(resname), "tableoid%u", rc->rowmarkId); + tle = makeTargetEntry((Expr *) var, + list_length(tlist) + 1, + pstrdup(resname), + true); + tlist = lappend(tlist, tle); + + return tlist; +} 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 fa39afcfe6..8ac73b4c34 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 64787de16f..9bb472fd98 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/cost.h" #include "optimizer/pathnode.h" diff --git a/src/include/optimizer/appendinfo.h b/src/include/optimizer/appendinfo.h new file mode 100644 index 0000000000..31011922f0 --- /dev/null +++ b/src/include/optimizer/appendinfo.h @@ -0,0 +1,43 @@ +/*------------------------------------------------------------------------- + * + * 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(RelOptInfo *parentrel, + RangeTblEntry *parentrte, + TupleDesc childdesc, Oid childoid, Oid childreltype, + 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 9c0795262f..55a69267c6 100644 --- a/src/include/optimizer/prep.h +++ b/src/include/optimizer/prep.h @@ -48,24 +48,5 @@ 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 AppendRelInfo *make_append_rel_info(RelOptInfo *parentrel, - RangeTblEntry *parentrte, - TupleDesc childdesc, Oid childoid, Oid childreltype, - Index childRTindex); -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