]> granicus.if.org Git - postgresql/commitdiff
Move inheritance expansion code into its own file
authorAlvaro Herrera <alvherre@alvh.no-ip.org>
Thu, 10 Jan 2019 17:54:31 +0000 (14:54 -0300)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
Thu, 10 Jan 2019 17:54:31 +0000 (14:54 -0300)
This commit moves expand_inherited_tables and underlings from
optimizer/prep/prepunionc.c to optimizer/utils/inherit.c.
Also, all of the AppendRelInfo-based expression manipulation routines
are moved to optimizer/utils/appendinfo.c.

No functional code changes.  One exception is the introduction of
make_append_rel_info, but that's still just moving around code.

Also, stop including <limits.h> in prepunion.c, which no longer needs
it since 3fc6e2d7f5b6.  I (Álvaro) noticed this because Amit was copying
that to inherit.c, which likewise doesn't need it.

Author: Amit Langote
Discussion: https://postgr.es/m/3be67028-a00a-502c-199a-da00eec8fb6e@lab.ntt.co.jp

15 files changed:
src/backend/optimizer/path/allpaths.c
src/backend/optimizer/path/equivclass.c
src/backend/optimizer/path/joinrels.c
src/backend/optimizer/plan/planmain.c
src/backend/optimizer/plan/planner.c
src/backend/optimizer/prep/prepunion.c
src/backend/optimizer/util/Makefile
src/backend/optimizer/util/appendinfo.c [new file with mode: 0644]
src/backend/optimizer/util/inherit.c [new file with mode: 0644]
src/backend/optimizer/util/pathnode.c
src/backend/optimizer/util/relnode.c
src/backend/partitioning/partprune.c
src/include/optimizer/appendinfo.h [new file with mode: 0644]
src/include/optimizer/inherit.h [new file with mode: 0644]
src/include/optimizer/prep.h

index 256fe16cdb0f5b1beaa59aa6e2118c1e9252c6c8..bc389b52e42d2492ed3c41a5309ef2d83b0fad61 100644 (file)
 #ifdef OPTIMIZER_DEBUG
 #include "nodes/print.h"
 #endif
+#include "optimizer/appendinfo.h"
 #include "optimizer/clauses.h"
 #include "optimizer/cost.h"
 #include "optimizer/geqo.h"
+#include "optimizer/inherit.h"
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
 #include "optimizer/plancat.h"
 #include "optimizer/planner.h"
-#include "optimizer/prep.h"
 #include "optimizer/restrictinfo.h"
 #include "optimizer/tlist.h"
 #include "optimizer/var.h"
index 0d994bd9aecdcd829614f3d2427c12a5747acfcd..6e134ae1d26091bd90673a0b726893494ae841a4 100644 (file)
 #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"
 #include "optimizer/planmain.h"
-#include "optimizer/prep.h"
 #include "optimizer/restrictinfo.h"
 #include "optimizer/var.h"
 #include "utils/lsyscache.h"
index 67b9ca83a74c9fe186b2fe873758b281d38dd8e3..38eeb23d81435993749f0eb6f4b4adc6b8aceaff 100644 (file)
 #include "postgres.h"
 
 #include "miscadmin.h"
+#include "optimizer/appendinfo.h"
 #include "optimizer/clauses.h"
 #include "optimizer/joininfo.h"
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
-#include "optimizer/prep.h"
 #include "partitioning/partbounds.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
index 0e0a5437086d8b21352b1ae1f264de7da06d323e..fc97a1bb5072e834f99316be1eb45c8318644042 100644 (file)
@@ -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"
index 3e33a17a5b774083b8c5db62b6106b3c1483d8da..98acb583040201aff2846b82ac4a1effcebc6942 100644 (file)
 #ifdef OPTIMIZER_DEBUG
 #include "nodes/print.h"
 #endif
+#include "optimizer/appendinfo.h"
 #include "optimizer/clauses.h"
 #include "optimizer/cost.h"
+#include "optimizer/inherit.h"
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
 #include "optimizer/plancat.h"
index 1d280c205e05daedbb4534548acc90cffb27a598..05388355fdee124cf7c57c0a08e8dca8d3cd3db4 100644 (file)
  * case, but most of the heavy lifting for that is done elsewhere,
  * notably in prepjointree.c and allpaths.c.
  *
- * There is also some code here to support planning of queries that use
- * inheritance (SELECT FROM foo*).  Inheritance trees are converted into
- * append relations, and thenceforth share code with the UNION ALL case.
- *
- *
  * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
@@ -28,8 +23,6 @@
  */
 #include "postgres.h"
 
-#include <limits.h>
-
 #include "access/heapam.h"
 #include "access/htup_details.h"
 #include "access/sysattr.h"
 #include "utils/syscache.h"
 
 
-typedef struct
-{
-       PlannerInfo *root;
-       int                     nappinfos;
-       AppendRelInfo **appinfos;
-} adjust_appendrel_attrs_context;
-
 static RelOptInfo *recurse_set_operations(Node *setOp, PlannerInfo *root,
                                           List *colTypes, List *colCollations,
                                           bool junkOK,
@@ -99,31 +85,6 @@ static List *generate_append_tlist(List *colTypes, List *colCollations,
                                          List *input_tlists,
                                          List *refnames_tlist);
 static List *generate_setop_grouplist(SetOperationStmt *op, List *targetlist);
-static void expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte,
-                                                Index rti);
-static void expand_partitioned_rtentry(PlannerInfo *root,
-                                                  RangeTblEntry *parentrte,
-                                                  Index parentRTindex, Relation parentrel,
-                                                  PlanRowMark *top_parentrc, LOCKMODE lockmode,
-                                                  List **appinfos);
-static void expand_single_inheritance_child(PlannerInfo *root,
-                                                               RangeTblEntry *parentrte,
-                                                               Index parentRTindex, Relation parentrel,
-                                                               PlanRowMark *top_parentrc, Relation childrel,
-                                                               List **appinfos, RangeTblEntry **childrte_p,
-                                                               Index *childRTindex_p);
-static void make_inh_translation_list(Relation oldrelation,
-                                                 Relation newrelation,
-                                                 Index newvarno,
-                                                 List **translated_vars);
-static Bitmapset *translate_col_privs(const Bitmapset *parent_privs,
-                                       List *translated_vars);
-static Node *adjust_appendrel_attrs_mutator(Node *node,
-                                                          adjust_appendrel_attrs_context *context);
-static Relids adjust_child_relids(Relids relids, int nappinfos,
-                                       AppendRelInfo **appinfos);
-static List *adjust_inherited_tlist(List *tlist,
-                                          AppendRelInfo *context);
 
 
 /*
@@ -1460,1168 +1421,3 @@ generate_setop_grouplist(SetOperationStmt *op, List *targetlist)
        Assert(lg == NULL);
        return grouplist;
 }
-
-
-/*
- * expand_inherited_tables
- *             Expand each rangetable entry that represents an inheritance set
- *             into an "append relation".  At the conclusion of this process,
- *             the "inh" flag is set in all and only those RTEs that are append
- *             relation parents.
- */
-void
-expand_inherited_tables(PlannerInfo *root)
-{
-       Index           nrtes;
-       Index           rti;
-       ListCell   *rl;
-
-       /*
-        * expand_inherited_rtentry may add RTEs to parse->rtable. The function is
-        * expected to recursively handle any RTEs that it creates with inh=true.
-        * So just scan as far as the original end of the rtable list.
-        */
-       nrtes = list_length(root->parse->rtable);
-       rl = list_head(root->parse->rtable);
-       for (rti = 1; rti <= nrtes; rti++)
-       {
-               RangeTblEntry *rte = (RangeTblEntry *) lfirst(rl);
-
-               expand_inherited_rtentry(root, rte, rti);
-               rl = lnext(rl);
-       }
-}
-
-/*
- * expand_inherited_rtentry
- *             Check whether a rangetable entry represents an inheritance set.
- *             If so, add entries for all the child tables to the query's
- *             rangetable, and build AppendRelInfo nodes for all the child tables
- *             and add them to root->append_rel_list.  If not, clear the entry's
- *             "inh" flag to prevent later code from looking for AppendRelInfos.
- *
- * Note that the original RTE is considered to represent the whole
- * inheritance set.  The first of the generated RTEs is an RTE for the same
- * table, but with inh = false, to represent the parent table in its role
- * as a simple member of the inheritance set.
- *
- * A childless table is never considered to be an inheritance set. For
- * regular inheritance, a parent RTE must always have at least two associated
- * AppendRelInfos: one corresponding to the parent table as a simple member of
- * inheritance set and one or more corresponding to the actual children.
- * Since a partitioned table is not scanned, it might have only one associated
- * AppendRelInfo.
- */
-static void
-expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
-{
-       Oid                     parentOID;
-       PlanRowMark *oldrc;
-       Relation        oldrelation;
-       LOCKMODE        lockmode;
-       List       *inhOIDs;
-       ListCell   *l;
-
-       /* Does RT entry allow inheritance? */
-       if (!rte->inh)
-               return;
-       /* Ignore any already-expanded UNION ALL nodes */
-       if (rte->rtekind != RTE_RELATION)
-       {
-               Assert(rte->rtekind == RTE_SUBQUERY);
-               return;
-       }
-       /* Fast path for common case of childless table */
-       parentOID = rte->relid;
-       if (!has_subclass(parentOID))
-       {
-               /* Clear flag before returning */
-               rte->inh = false;
-               return;
-       }
-
-       /*
-        * The rewriter should already have obtained an appropriate lock on each
-        * relation named in the query.  However, for each child relation we add
-        * to the query, we must obtain an appropriate lock, because this will be
-        * the first use of those relations in the parse/rewrite/plan pipeline.
-        * Child rels should use the same lockmode as their parent.
-        */
-       lockmode = rte->rellockmode;
-
-       /* Scan for all members of inheritance set, acquire needed locks */
-       inhOIDs = find_all_inheritors(parentOID, lockmode, NULL);
-
-       /*
-        * Check that there's at least one descendant, else treat as no-child
-        * case.  This could happen despite above has_subclass() check, if table
-        * once had a child but no longer does.
-        */
-       if (list_length(inhOIDs) < 2)
-       {
-               /* Clear flag before returning */
-               rte->inh = false;
-               return;
-       }
-
-       /*
-        * If parent relation is selected FOR UPDATE/SHARE, we need to mark its
-        * PlanRowMark as isParent = true, and generate a new PlanRowMark for each
-        * child.
-        */
-       oldrc = get_plan_rowmark(root->rowMarks, rti);
-       if (oldrc)
-               oldrc->isParent = true;
-
-       /*
-        * Must open the parent relation to examine its tupdesc.  We need not lock
-        * it; we assume the rewriter already did.
-        */
-       oldrelation = heap_open(parentOID, NoLock);
-
-       /* Scan the inheritance set and expand it */
-       if (RelationGetPartitionDesc(oldrelation) != NULL)
-       {
-               Assert(rte->relkind == RELKIND_PARTITIONED_TABLE);
-
-               /*
-                * If this table has partitions, recursively expand them in the order
-                * in which they appear in the PartitionDesc.  While at it, also
-                * extract the partition key columns of all the partitioned tables.
-                */
-               expand_partitioned_rtentry(root, rte, rti, oldrelation, oldrc,
-                                                                  lockmode, &root->append_rel_list);
-       }
-       else
-       {
-               List       *appinfos = NIL;
-               RangeTblEntry *childrte;
-               Index           childRTindex;
-
-               /*
-                * This table has no partitions.  Expand any plain inheritance
-                * children in the order the OIDs were returned by
-                * find_all_inheritors.
-                */
-               foreach(l, inhOIDs)
-               {
-                       Oid                     childOID = lfirst_oid(l);
-                       Relation        newrelation;
-
-                       /* Open rel if needed; we already have required locks */
-                       if (childOID != parentOID)
-                               newrelation = heap_open(childOID, NoLock);
-                       else
-                               newrelation = oldrelation;
-
-                       /*
-                        * It is possible that the parent table has children that are temp
-                        * tables of other backends.  We cannot safely access such tables
-                        * (because of buffering issues), and the best thing to do seems
-                        * to be to silently ignore them.
-                        */
-                       if (childOID != parentOID && RELATION_IS_OTHER_TEMP(newrelation))
-                       {
-                               heap_close(newrelation, lockmode);
-                               continue;
-                       }
-
-                       expand_single_inheritance_child(root, rte, rti, oldrelation, oldrc,
-                                                                                       newrelation,
-                                                                                       &appinfos, &childrte,
-                                                                                       &childRTindex);
-
-                       /* Close child relations, but keep locks */
-                       if (childOID != parentOID)
-                               heap_close(newrelation, NoLock);
-               }
-
-               /*
-                * If all the children were temp tables, pretend it's a
-                * non-inheritance situation; we don't need Append node in that case.
-                * The duplicate RTE we added for the parent table is harmless, so we
-                * don't bother to get rid of it; ditto for the useless PlanRowMark
-                * node.
-                */
-               if (list_length(appinfos) < 2)
-                       rte->inh = false;
-               else
-                       root->append_rel_list = list_concat(root->append_rel_list,
-                                                                                               appinfos);
-
-       }
-
-       heap_close(oldrelation, NoLock);
-}
-
-/*
- * expand_partitioned_rtentry
- *             Recursively expand an RTE for a partitioned table.
- */
-static void
-expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,
-                                                  Index parentRTindex, Relation parentrel,
-                                                  PlanRowMark *top_parentrc, LOCKMODE lockmode,
-                                                  List **appinfos)
-{
-       int                     i;
-       RangeTblEntry *childrte;
-       Index           childRTindex;
-       PartitionDesc partdesc = RelationGetPartitionDesc(parentrel);
-
-       check_stack_depth();
-
-       /* A partitioned table should always have a partition descriptor. */
-       Assert(partdesc);
-
-       Assert(parentrte->inh);
-
-       /*
-        * Note down whether any partition key cols are being updated. Though it's
-        * the root partitioned table's updatedCols we are interested in, we
-        * instead use parentrte to get the updatedCols. This is convenient
-        * because parentrte already has the root partrel's updatedCols translated
-        * to match the attribute ordering of parentrel.
-        */
-       if (!root->partColsUpdated)
-               root->partColsUpdated =
-                       has_partition_attrs(parentrel, parentrte->updatedCols, NULL);
-
-       /* First expand the partitioned table itself. */
-       expand_single_inheritance_child(root, parentrte, parentRTindex, parentrel,
-                                                                       top_parentrc, parentrel,
-                                                                       appinfos, &childrte, &childRTindex);
-
-       /*
-        * If the partitioned table has no partitions, treat this as the
-        * non-inheritance case.
-        */
-       if (partdesc->nparts == 0)
-       {
-               parentrte->inh = false;
-               return;
-       }
-
-       for (i = 0; i < partdesc->nparts; i++)
-       {
-               Oid                     childOID = partdesc->oids[i];
-               Relation        childrel;
-
-               /* Open rel; we already have required locks */
-               childrel = heap_open(childOID, NoLock);
-
-               /*
-                * Temporary partitions belonging to other sessions should have been
-                * disallowed at definition, but for paranoia's sake, let's double
-                * check.
-                */
-               if (RELATION_IS_OTHER_TEMP(childrel))
-                       elog(ERROR, "temporary relation from another session found as partition");
-
-               expand_single_inheritance_child(root, parentrte, parentRTindex,
-                                                                               parentrel, top_parentrc, childrel,
-                                                                               appinfos, &childrte, &childRTindex);
-
-               /* If this child is itself partitioned, recurse */
-               if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
-                       expand_partitioned_rtentry(root, childrte, childRTindex,
-                                                                          childrel, top_parentrc, lockmode,
-                                                                          appinfos);
-
-               /* Close child relation, but keep locks */
-               heap_close(childrel, NoLock);
-       }
-}
-
-/*
- * expand_single_inheritance_child
- *             Build a RangeTblEntry and an AppendRelInfo, if appropriate, plus
- *             maybe a PlanRowMark.
- *
- * We now expand the partition hierarchy level by level, creating a
- * corresponding hierarchy of AppendRelInfos and RelOptInfos, where each
- * partitioned descendant acts as a parent of its immediate partitions.
- * (This is a difference from what older versions of PostgreSQL did and what
- * is still done in the case of table inheritance for unpartitioned tables,
- * where the hierarchy is flattened during RTE expansion.)
- *
- * PlanRowMarks still carry the top-parent's RTI, and the top-parent's
- * allMarkTypes field still accumulates values from all descendents.
- *
- * "parentrte" and "parentRTindex" are immediate parent's RTE and
- * RTI. "top_parentrc" is top parent's PlanRowMark.
- *
- * The child RangeTblEntry and its RTI are returned in "childrte_p" and
- * "childRTindex_p" resp.
- */
-static void
-expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
-                                                               Index parentRTindex, Relation parentrel,
-                                                               PlanRowMark *top_parentrc, Relation childrel,
-                                                               List **appinfos, RangeTblEntry **childrte_p,
-                                                               Index *childRTindex_p)
-{
-       Query      *parse = root->parse;
-       Oid                     parentOID = RelationGetRelid(parentrel);
-       Oid                     childOID = RelationGetRelid(childrel);
-       RangeTblEntry *childrte;
-       Index           childRTindex;
-       AppendRelInfo *appinfo;
-
-       /*
-        * Build an RTE for the child, and attach to query's rangetable list. We
-        * copy most fields of the parent's RTE, but replace relation OID and
-        * relkind, and set inh = false.  Also, set requiredPerms to zero since
-        * all required permissions checks are done on the original RTE. Likewise,
-        * set the child's securityQuals to empty, because we only want to apply
-        * the parent's RLS conditions regardless of what RLS properties
-        * individual children may have.  (This is an intentional choice to make
-        * inherited RLS work like regular permissions checks.) The parent
-        * securityQuals will be propagated to children along with other base
-        * restriction clauses, so we don't need to do it here.
-        */
-       childrte = copyObject(parentrte);
-       *childrte_p = childrte;
-       childrte->relid = childOID;
-       childrte->relkind = childrel->rd_rel->relkind;
-       /* A partitioned child will need to be expanded further. */
-       if (childOID != parentOID &&
-               childrte->relkind == RELKIND_PARTITIONED_TABLE)
-               childrte->inh = true;
-       else
-               childrte->inh = false;
-       childrte->requiredPerms = 0;
-       childrte->securityQuals = NIL;
-       parse->rtable = lappend(parse->rtable, childrte);
-       childRTindex = list_length(parse->rtable);
-       *childRTindex_p = childRTindex;
-
-       /*
-        * We need an AppendRelInfo if paths will be built for the child RTE. If
-        * childrte->inh is true, then we'll always need to generate append paths
-        * for it.  If childrte->inh is false, we must scan it if it's not a
-        * partitioned table; but if it is a partitioned table, then it never has
-        * any data of its own and need not be scanned.
-        */
-       if (childrte->relkind != RELKIND_PARTITIONED_TABLE || childrte->inh)
-       {
-               appinfo = makeNode(AppendRelInfo);
-               appinfo->parent_relid = parentRTindex;
-               appinfo->child_relid = childRTindex;
-               appinfo->parent_reltype = parentrel->rd_rel->reltype;
-               appinfo->child_reltype = childrel->rd_rel->reltype;
-               make_inh_translation_list(parentrel, childrel, childRTindex,
-                                                                 &appinfo->translated_vars);
-               appinfo->parent_reloid = parentOID;
-               *appinfos = lappend(*appinfos, appinfo);
-
-               /*
-                * Translate the column permissions bitmaps to the child's attnums (we
-                * have to build the translated_vars list before we can do this). But
-                * if this is the parent table, leave copyObject's result alone.
-                *
-                * Note: we need to do this even though the executor won't run any
-                * permissions checks on the child RTE.  The insertedCols/updatedCols
-                * bitmaps may be examined for trigger-firing purposes.
-                */
-               if (childOID != parentOID)
-               {
-                       childrte->selectedCols = translate_col_privs(parentrte->selectedCols,
-                                                                                                                appinfo->translated_vars);
-                       childrte->insertedCols = translate_col_privs(parentrte->insertedCols,
-                                                                                                                appinfo->translated_vars);
-                       childrte->updatedCols = translate_col_privs(parentrte->updatedCols,
-                                                                                                               appinfo->translated_vars);
-               }
-       }
-
-       /*
-        * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE.
-        */
-       if (top_parentrc)
-       {
-               PlanRowMark *childrc = makeNode(PlanRowMark);
-
-               childrc->rti = childRTindex;
-               childrc->prti = top_parentrc->rti;
-               childrc->rowmarkId = top_parentrc->rowmarkId;
-               /* Reselect rowmark type, because relkind might not match parent */
-               childrc->markType = select_rowmark_type(childrte,
-                                                                                               top_parentrc->strength);
-               childrc->allMarkTypes = (1 << childrc->markType);
-               childrc->strength = top_parentrc->strength;
-               childrc->waitPolicy = top_parentrc->waitPolicy;
-
-               /*
-                * We mark RowMarks for partitioned child tables as parent RowMarks so
-                * that the executor ignores them (except their existence means that
-                * the child tables be locked using appropriate mode).
-                */
-               childrc->isParent = (childrte->relkind == RELKIND_PARTITIONED_TABLE);
-
-               /* Include child's rowmark type in top parent's allMarkTypes */
-               top_parentrc->allMarkTypes |= childrc->allMarkTypes;
-
-               root->rowMarks = lappend(root->rowMarks, childrc);
-       }
-}
-
-/*
- * make_inh_translation_list
- *       Build the list of translations from parent Vars to child Vars for
- *       an inheritance child.
- *
- * For paranoia's sake, we match type/collation as well as attribute name.
- */
-static void
-make_inh_translation_list(Relation oldrelation, Relation newrelation,
-                                                 Index newvarno,
-                                                 List **translated_vars)
-{
-       List       *vars = NIL;
-       TupleDesc       old_tupdesc = RelationGetDescr(oldrelation);
-       TupleDesc       new_tupdesc = RelationGetDescr(newrelation);
-       Oid                     new_relid = RelationGetRelid(newrelation);
-       int                     oldnatts = old_tupdesc->natts;
-       int                     newnatts = new_tupdesc->natts;
-       int                     old_attno;
-       int                     new_attno = 0;
-
-       for (old_attno = 0; old_attno < oldnatts; old_attno++)
-       {
-               Form_pg_attribute att;
-               char       *attname;
-               Oid                     atttypid;
-               int32           atttypmod;
-               Oid                     attcollation;
-
-               att = TupleDescAttr(old_tupdesc, old_attno);
-               if (att->attisdropped)
-               {
-                       /* Just put NULL into this list entry */
-                       vars = lappend(vars, NULL);
-                       continue;
-               }
-               attname = NameStr(att->attname);
-               atttypid = att->atttypid;
-               atttypmod = att->atttypmod;
-               attcollation = att->attcollation;
-
-               /*
-                * When we are generating the "translation list" for the parent table
-                * of an inheritance set, no need to search for matches.
-                */
-               if (oldrelation == newrelation)
-               {
-                       vars = lappend(vars, makeVar(newvarno,
-                                                                                (AttrNumber) (old_attno + 1),
-                                                                                atttypid,
-                                                                                atttypmod,
-                                                                                attcollation,
-                                                                                0));
-                       continue;
-               }
-
-               /*
-                * Otherwise we have to search for the matching column by name.
-                * There's no guarantee it'll have the same column position, because
-                * of cases like ALTER TABLE ADD COLUMN and multiple inheritance.
-                * However, in simple cases, the relative order of columns is mostly
-                * the same in both relations, so try the column of newrelation that
-                * follows immediately after the one that we just found, and if that
-                * fails, let syscache handle it.
-                */
-               if (new_attno >= newnatts ||
-                       (att = TupleDescAttr(new_tupdesc, new_attno))->attisdropped ||
-                       strcmp(attname, NameStr(att->attname)) != 0)
-               {
-                       HeapTuple       newtup;
-
-                       newtup = SearchSysCacheAttName(new_relid, attname);
-                       if (!newtup)
-                               elog(ERROR, "could not find inherited attribute \"%s\" of relation \"%s\"",
-                                        attname, RelationGetRelationName(newrelation));
-                       new_attno = ((Form_pg_attribute) GETSTRUCT(newtup))->attnum - 1;
-                       ReleaseSysCache(newtup);
-
-                       att = TupleDescAttr(new_tupdesc, new_attno);
-               }
-
-               /* Found it, check type and collation match */
-               if (atttypid != att->atttypid || atttypmod != att->atttypmod)
-                       elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's type",
-                                attname, RelationGetRelationName(newrelation));
-               if (attcollation != att->attcollation)
-                       elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's collation",
-                                attname, RelationGetRelationName(newrelation));
-
-               vars = lappend(vars, makeVar(newvarno,
-                                                                        (AttrNumber) (new_attno + 1),
-                                                                        atttypid,
-                                                                        atttypmod,
-                                                                        attcollation,
-                                                                        0));
-               new_attno++;
-       }
-
-       *translated_vars = vars;
-}
-
-/*
- * translate_col_privs
- *       Translate a bitmapset representing per-column privileges from the
- *       parent rel's attribute numbering to the child's.
- *
- * The only surprise here is that we don't translate a parent whole-row
- * reference into a child whole-row reference.  That would mean requiring
- * permissions on all child columns, which is overly strict, since the
- * query is really only going to reference the inherited columns.  Instead
- * we set the per-column bits for all inherited columns.
- */
-static Bitmapset *
-translate_col_privs(const Bitmapset *parent_privs,
-                                       List *translated_vars)
-{
-       Bitmapset  *child_privs = NULL;
-       bool            whole_row;
-       int                     attno;
-       ListCell   *lc;
-
-       /* System attributes have the same numbers in all tables */
-       for (attno = FirstLowInvalidHeapAttributeNumber + 1; attno < 0; attno++)
-       {
-               if (bms_is_member(attno - FirstLowInvalidHeapAttributeNumber,
-                                                 parent_privs))
-                       child_privs = bms_add_member(child_privs,
-                                                                                attno - FirstLowInvalidHeapAttributeNumber);
-       }
-
-       /* Check if parent has whole-row reference */
-       whole_row = bms_is_member(InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber,
-                                                         parent_privs);
-
-       /* And now translate the regular user attributes, using the vars list */
-       attno = InvalidAttrNumber;
-       foreach(lc, translated_vars)
-       {
-               Var                *var = lfirst_node(Var, lc);
-
-               attno++;
-               if (var == NULL)                /* ignore dropped columns */
-                       continue;
-               if (whole_row ||
-                       bms_is_member(attno - FirstLowInvalidHeapAttributeNumber,
-                                                 parent_privs))
-                       child_privs = bms_add_member(child_privs,
-                                                                                var->varattno - FirstLowInvalidHeapAttributeNumber);
-       }
-
-       return child_privs;
-}
-
-/*
- * adjust_appendrel_attrs
- *       Copy the specified query or expression and translate Vars referring to a
- *       parent rel to refer to the corresponding child rel instead.  We also
- *       update rtindexes appearing outside Vars, such as resultRelation and
- *       jointree relids.
- *
- * Note: this is only applied after conversion of sublinks to subplans,
- * so we don't need to cope with recursion into sub-queries.
- *
- * Note: this is not hugely different from what pullup_replace_vars() does;
- * maybe we should try to fold the two routines together.
- */
-Node *
-adjust_appendrel_attrs(PlannerInfo *root, Node *node, int nappinfos,
-                                          AppendRelInfo **appinfos)
-{
-       Node       *result;
-       adjust_appendrel_attrs_context context;
-
-       context.root = root;
-       context.nappinfos = nappinfos;
-       context.appinfos = appinfos;
-
-       /* If there's nothing to adjust, don't call this function. */
-       Assert(nappinfos >= 1 && appinfos != NULL);
-
-       /*
-        * Must be prepared to start with a Query or a bare expression tree.
-        */
-       if (node && IsA(node, Query))
-       {
-               Query      *newnode;
-               int                     cnt;
-
-               newnode = query_tree_mutator((Query *) node,
-                                                                        adjust_appendrel_attrs_mutator,
-                                                                        (void *) &context,
-                                                                        QTW_IGNORE_RC_SUBQUERIES);
-               for (cnt = 0; cnt < nappinfos; cnt++)
-               {
-                       AppendRelInfo *appinfo = appinfos[cnt];
-
-                       if (newnode->resultRelation == appinfo->parent_relid)
-                       {
-                               newnode->resultRelation = appinfo->child_relid;
-                               /* Fix tlist resnos too, if it's inherited UPDATE */
-                               if (newnode->commandType == CMD_UPDATE)
-                                       newnode->targetList =
-                                               adjust_inherited_tlist(newnode->targetList,
-                                                                                          appinfo);
-                               break;
-                       }
-               }
-
-               result = (Node *) newnode;
-       }
-       else
-               result = adjust_appendrel_attrs_mutator(node, &context);
-
-       return result;
-}
-
-static Node *
-adjust_appendrel_attrs_mutator(Node *node,
-                                                          adjust_appendrel_attrs_context *context)
-{
-       AppendRelInfo **appinfos = context->appinfos;
-       int                     nappinfos = context->nappinfos;
-       int                     cnt;
-
-       if (node == NULL)
-               return NULL;
-       if (IsA(node, Var))
-       {
-               Var                *var = (Var *) copyObject(node);
-               AppendRelInfo *appinfo = NULL;
-
-               for (cnt = 0; cnt < nappinfos; cnt++)
-               {
-                       if (var->varno == appinfos[cnt]->parent_relid)
-                       {
-                               appinfo = appinfos[cnt];
-                               break;
-                       }
-               }
-
-               if (var->varlevelsup == 0 && appinfo)
-               {
-                       var->varno = appinfo->child_relid;
-                       var->varnoold = appinfo->child_relid;
-                       if (var->varattno > 0)
-                       {
-                               Node       *newnode;
-
-                               if (var->varattno > list_length(appinfo->translated_vars))
-                                       elog(ERROR, "attribute %d of relation \"%s\" does not exist",
-                                                var->varattno, get_rel_name(appinfo->parent_reloid));
-                               newnode = copyObject(list_nth(appinfo->translated_vars,
-                                                                                         var->varattno - 1));
-                               if (newnode == NULL)
-                                       elog(ERROR, "attribute %d of relation \"%s\" does not exist",
-                                                var->varattno, get_rel_name(appinfo->parent_reloid));
-                               return newnode;
-                       }
-                       else if (var->varattno == 0)
-                       {
-                               /*
-                                * Whole-row Var: if we are dealing with named rowtypes, we
-                                * can use a whole-row Var for the child table plus a coercion
-                                * step to convert the tuple layout to the parent's rowtype.
-                                * Otherwise we have to generate a RowExpr.
-                                */
-                               if (OidIsValid(appinfo->child_reltype))
-                               {
-                                       Assert(var->vartype == appinfo->parent_reltype);
-                                       if (appinfo->parent_reltype != appinfo->child_reltype)
-                                       {
-                                               ConvertRowtypeExpr *r = makeNode(ConvertRowtypeExpr);
-
-                                               r->arg = (Expr *) var;
-                                               r->resulttype = appinfo->parent_reltype;
-                                               r->convertformat = COERCE_IMPLICIT_CAST;
-                                               r->location = -1;
-                                               /* Make sure the Var node has the right type ID, too */
-                                               var->vartype = appinfo->child_reltype;
-                                               return (Node *) r;
-                                       }
-                               }
-                               else
-                               {
-                                       /*
-                                        * Build a RowExpr containing the translated variables.
-                                        *
-                                        * In practice var->vartype will always be RECORDOID here,
-                                        * so we need to come up with some suitable column names.
-                                        * We use the parent RTE's column names.
-                                        *
-                                        * Note: we can't get here for inheritance cases, so there
-                                        * is no need to worry that translated_vars might contain
-                                        * some dummy NULLs.
-                                        */
-                                       RowExpr    *rowexpr;
-                                       List       *fields;
-                                       RangeTblEntry *rte;
-
-                                       rte = rt_fetch(appinfo->parent_relid,
-                                                                  context->root->parse->rtable);
-                                       fields = copyObject(appinfo->translated_vars);
-                                       rowexpr = makeNode(RowExpr);
-                                       rowexpr->args = fields;
-                                       rowexpr->row_typeid = var->vartype;
-                                       rowexpr->row_format = COERCE_IMPLICIT_CAST;
-                                       rowexpr->colnames = copyObject(rte->eref->colnames);
-                                       rowexpr->location = -1;
-
-                                       return (Node *) rowexpr;
-                               }
-                       }
-                       /* system attributes don't need any other translation */
-               }
-               return (Node *) var;
-       }
-       if (IsA(node, CurrentOfExpr))
-       {
-               CurrentOfExpr *cexpr = (CurrentOfExpr *) copyObject(node);
-
-               for (cnt = 0; cnt < nappinfos; cnt++)
-               {
-                       AppendRelInfo *appinfo = appinfos[cnt];
-
-                       if (cexpr->cvarno == appinfo->parent_relid)
-                       {
-                               cexpr->cvarno = appinfo->child_relid;
-                               break;
-                       }
-               }
-               return (Node *) cexpr;
-       }
-       if (IsA(node, RangeTblRef))
-       {
-               RangeTblRef *rtr = (RangeTblRef *) copyObject(node);
-
-               for (cnt = 0; cnt < nappinfos; cnt++)
-               {
-                       AppendRelInfo *appinfo = appinfos[cnt];
-
-                       if (rtr->rtindex == appinfo->parent_relid)
-                       {
-                               rtr->rtindex = appinfo->child_relid;
-                               break;
-                       }
-               }
-               return (Node *) rtr;
-       }
-       if (IsA(node, JoinExpr))
-       {
-               /* Copy the JoinExpr node with correct mutation of subnodes */
-               JoinExpr   *j;
-               AppendRelInfo *appinfo;
-
-               j = (JoinExpr *) expression_tree_mutator(node,
-                                                                                                adjust_appendrel_attrs_mutator,
-                                                                                                (void *) context);
-               /* now fix JoinExpr's rtindex (probably never happens) */
-               for (cnt = 0; cnt < nappinfos; cnt++)
-               {
-                       appinfo = appinfos[cnt];
-
-                       if (j->rtindex == appinfo->parent_relid)
-                       {
-                               j->rtindex = appinfo->child_relid;
-                               break;
-                       }
-               }
-               return (Node *) j;
-       }
-       if (IsA(node, PlaceHolderVar))
-       {
-               /* Copy the PlaceHolderVar node with correct mutation of subnodes */
-               PlaceHolderVar *phv;
-
-               phv = (PlaceHolderVar *) expression_tree_mutator(node,
-                                                                                                                adjust_appendrel_attrs_mutator,
-                                                                                                                (void *) context);
-               /* now fix PlaceHolderVar's relid sets */
-               if (phv->phlevelsup == 0)
-                       phv->phrels = adjust_child_relids(phv->phrels, context->nappinfos,
-                                                                                         context->appinfos);
-               return (Node *) phv;
-       }
-       /* Shouldn't need to handle planner auxiliary nodes here */
-       Assert(!IsA(node, SpecialJoinInfo));
-       Assert(!IsA(node, AppendRelInfo));
-       Assert(!IsA(node, PlaceHolderInfo));
-       Assert(!IsA(node, MinMaxAggInfo));
-
-       /*
-        * We have to process RestrictInfo nodes specially.  (Note: although
-        * set_append_rel_pathlist will hide RestrictInfos in the parent's
-        * baserestrictinfo list from us, it doesn't hide those in joininfo.)
-        */
-       if (IsA(node, RestrictInfo))
-       {
-               RestrictInfo *oldinfo = (RestrictInfo *) node;
-               RestrictInfo *newinfo = makeNode(RestrictInfo);
-
-               /* Copy all flat-copiable fields */
-               memcpy(newinfo, oldinfo, sizeof(RestrictInfo));
-
-               /* Recursively fix the clause itself */
-               newinfo->clause = (Expr *)
-                       adjust_appendrel_attrs_mutator((Node *) oldinfo->clause, context);
-
-               /* and the modified version, if an OR clause */
-               newinfo->orclause = (Expr *)
-                       adjust_appendrel_attrs_mutator((Node *) oldinfo->orclause, context);
-
-               /* adjust relid sets too */
-               newinfo->clause_relids = adjust_child_relids(oldinfo->clause_relids,
-                                                                                                        context->nappinfos,
-                                                                                                        context->appinfos);
-               newinfo->required_relids = adjust_child_relids(oldinfo->required_relids,
-                                                                                                          context->nappinfos,
-                                                                                                          context->appinfos);
-               newinfo->outer_relids = adjust_child_relids(oldinfo->outer_relids,
-                                                                                                       context->nappinfos,
-                                                                                                       context->appinfos);
-               newinfo->nullable_relids = adjust_child_relids(oldinfo->nullable_relids,
-                                                                                                          context->nappinfos,
-                                                                                                          context->appinfos);
-               newinfo->left_relids = adjust_child_relids(oldinfo->left_relids,
-                                                                                                  context->nappinfos,
-                                                                                                  context->appinfos);
-               newinfo->right_relids = adjust_child_relids(oldinfo->right_relids,
-                                                                                                       context->nappinfos,
-                                                                                                       context->appinfos);
-
-               /*
-                * Reset cached derivative fields, since these might need to have
-                * different values when considering the child relation.  Note we
-                * don't reset left_ec/right_ec: each child variable is implicitly
-                * equivalent to its parent, so still a member of the same EC if any.
-                */
-               newinfo->eval_cost.startup = -1;
-               newinfo->norm_selec = -1;
-               newinfo->outer_selec = -1;
-               newinfo->left_em = NULL;
-               newinfo->right_em = NULL;
-               newinfo->scansel_cache = NIL;
-               newinfo->left_bucketsize = -1;
-               newinfo->right_bucketsize = -1;
-               newinfo->left_mcvfreq = -1;
-               newinfo->right_mcvfreq = -1;
-
-               return (Node *) newinfo;
-       }
-
-       /*
-        * NOTE: we do not need to recurse into sublinks, because they should
-        * already have been converted to subplans before we see them.
-        */
-       Assert(!IsA(node, SubLink));
-       Assert(!IsA(node, Query));
-
-       return expression_tree_mutator(node, adjust_appendrel_attrs_mutator,
-                                                                  (void *) context);
-}
-
-/*
- * Substitute child relids for parent relids in a Relid set.  The array of
- * appinfos specifies the substitutions to be performed.
- */
-static Relids
-adjust_child_relids(Relids relids, int nappinfos, AppendRelInfo **appinfos)
-{
-       Bitmapset  *result = NULL;
-       int                     cnt;
-
-       for (cnt = 0; cnt < nappinfos; cnt++)
-       {
-               AppendRelInfo *appinfo = appinfos[cnt];
-
-               /* Remove parent, add child */
-               if (bms_is_member(appinfo->parent_relid, relids))
-               {
-                       /* Make a copy if we are changing the set. */
-                       if (!result)
-                               result = bms_copy(relids);
-
-                       result = bms_del_member(result, appinfo->parent_relid);
-                       result = bms_add_member(result, appinfo->child_relid);
-               }
-       }
-
-       /* If we made any changes, return the modified copy. */
-       if (result)
-               return result;
-
-       /* Otherwise, return the original set without modification. */
-       return relids;
-}
-
-/*
- * Replace any relid present in top_parent_relids with its child in
- * child_relids. Members of child_relids can be multiple levels below top
- * parent in the partition hierarchy.
- */
-Relids
-adjust_child_relids_multilevel(PlannerInfo *root, Relids relids,
-                                                          Relids child_relids, Relids top_parent_relids)
-{
-       AppendRelInfo **appinfos;
-       int                     nappinfos;
-       Relids          parent_relids = NULL;
-       Relids          result;
-       Relids          tmp_result = NULL;
-       int                     cnt;
-
-       /*
-        * If the given relids set doesn't contain any of the top parent relids,
-        * it will remain unchanged.
-        */
-       if (!bms_overlap(relids, top_parent_relids))
-               return relids;
-
-       appinfos = find_appinfos_by_relids(root, child_relids, &nappinfos);
-
-       /* Construct relids set for the immediate parent of the given child. */
-       for (cnt = 0; cnt < nappinfos; cnt++)
-       {
-               AppendRelInfo *appinfo = appinfos[cnt];
-
-               parent_relids = bms_add_member(parent_relids, appinfo->parent_relid);
-       }
-
-       /* Recurse if immediate parent is not the top parent. */
-       if (!bms_equal(parent_relids, top_parent_relids))
-       {
-               tmp_result = adjust_child_relids_multilevel(root, relids,
-                                                                                                       parent_relids,
-                                                                                                       top_parent_relids);
-               relids = tmp_result;
-       }
-
-       result = adjust_child_relids(relids, nappinfos, appinfos);
-
-       /* Free memory consumed by any intermediate result. */
-       if (tmp_result)
-               bms_free(tmp_result);
-       bms_free(parent_relids);
-       pfree(appinfos);
-
-       return result;
-}
-
-/*
- * Adjust the targetlist entries of an inherited UPDATE operation
- *
- * The expressions have already been fixed, but we have to make sure that
- * the target resnos match the child table (they may not, in the case of
- * a column that was added after-the-fact by ALTER TABLE).  In some cases
- * this can force us to re-order the tlist to preserve resno ordering.
- * (We do all this work in special cases so that preptlist.c is fast for
- * the typical case.)
- *
- * The given tlist has already been through expression_tree_mutator;
- * therefore the TargetEntry nodes are fresh copies that it's okay to
- * scribble on.
- *
- * Note that this is not needed for INSERT because INSERT isn't inheritable.
- */
-static List *
-adjust_inherited_tlist(List *tlist, AppendRelInfo *context)
-{
-       bool            changed_it = false;
-       ListCell   *tl;
-       List       *new_tlist;
-       bool            more;
-       int                     attrno;
-
-       /* This should only happen for an inheritance case, not UNION ALL */
-       Assert(OidIsValid(context->parent_reloid));
-
-       /* Scan tlist and update resnos to match attnums of child rel */
-       foreach(tl, tlist)
-       {
-               TargetEntry *tle = (TargetEntry *) lfirst(tl);
-               Var                *childvar;
-
-               if (tle->resjunk)
-                       continue;                       /* ignore junk items */
-
-               /* Look up the translation of this column: it must be a Var */
-               if (tle->resno <= 0 ||
-                       tle->resno > list_length(context->translated_vars))
-                       elog(ERROR, "attribute %d of relation \"%s\" does not exist",
-                                tle->resno, get_rel_name(context->parent_reloid));
-               childvar = (Var *) list_nth(context->translated_vars, tle->resno - 1);
-               if (childvar == NULL || !IsA(childvar, Var))
-                       elog(ERROR, "attribute %d of relation \"%s\" does not exist",
-                                tle->resno, get_rel_name(context->parent_reloid));
-
-               if (tle->resno != childvar->varattno)
-               {
-                       tle->resno = childvar->varattno;
-                       changed_it = true;
-               }
-       }
-
-       /*
-        * If we changed anything, re-sort the tlist by resno, and make sure
-        * resjunk entries have resnos above the last real resno.  The sort
-        * algorithm is a bit stupid, but for such a seldom-taken path, small is
-        * probably better than fast.
-        */
-       if (!changed_it)
-               return tlist;
-
-       new_tlist = NIL;
-       more = true;
-       for (attrno = 1; more; attrno++)
-       {
-               more = false;
-               foreach(tl, tlist)
-               {
-                       TargetEntry *tle = (TargetEntry *) lfirst(tl);
-
-                       if (tle->resjunk)
-                               continue;               /* ignore junk items */
-
-                       if (tle->resno == attrno)
-                               new_tlist = lappend(new_tlist, tle);
-                       else if (tle->resno > attrno)
-                               more = true;
-               }
-       }
-
-       foreach(tl, tlist)
-       {
-               TargetEntry *tle = (TargetEntry *) lfirst(tl);
-
-               if (!tle->resjunk)
-                       continue;                       /* here, ignore non-junk items */
-
-               tle->resno = attrno;
-               new_tlist = lappend(new_tlist, tle);
-               attrno++;
-       }
-
-       return new_tlist;
-}
-
-/*
- * adjust_appendrel_attrs_multilevel
- *       Apply Var translations from a toplevel appendrel parent down to a child.
- *
- * In some cases we need to translate expressions referencing a parent relation
- * to reference an appendrel child that's multiple levels removed from it.
- */
-Node *
-adjust_appendrel_attrs_multilevel(PlannerInfo *root, Node *node,
-                                                                 Relids child_relids,
-                                                                 Relids top_parent_relids)
-{
-       AppendRelInfo **appinfos;
-       Bitmapset  *parent_relids = NULL;
-       int                     nappinfos;
-       int                     cnt;
-
-       Assert(bms_num_members(child_relids) == bms_num_members(top_parent_relids));
-
-       appinfos = find_appinfos_by_relids(root, child_relids, &nappinfos);
-
-       /* Construct relids set for the immediate parent of given child. */
-       for (cnt = 0; cnt < nappinfos; cnt++)
-       {
-               AppendRelInfo *appinfo = appinfos[cnt];
-
-               parent_relids = bms_add_member(parent_relids, appinfo->parent_relid);
-       }
-
-       /* Recurse if immediate parent is not the top parent. */
-       if (!bms_equal(parent_relids, top_parent_relids))
-               node = adjust_appendrel_attrs_multilevel(root, node, parent_relids,
-                                                                                                top_parent_relids);
-
-       /* Now translate for this child */
-       node = adjust_appendrel_attrs(root, node, nappinfos, appinfos);
-
-       pfree(appinfos);
-
-       return node;
-}
-
-/*
- * Construct the SpecialJoinInfo for a child-join by translating
- * SpecialJoinInfo for the join between parents. left_relids and right_relids
- * are the relids of left and right side of the join respectively.
- */
-SpecialJoinInfo *
-build_child_join_sjinfo(PlannerInfo *root, SpecialJoinInfo *parent_sjinfo,
-                                               Relids left_relids, Relids right_relids)
-{
-       SpecialJoinInfo *sjinfo = makeNode(SpecialJoinInfo);
-       AppendRelInfo **left_appinfos;
-       int                     left_nappinfos;
-       AppendRelInfo **right_appinfos;
-       int                     right_nappinfos;
-
-       memcpy(sjinfo, parent_sjinfo, sizeof(SpecialJoinInfo));
-       left_appinfos = find_appinfos_by_relids(root, left_relids,
-                                                                                       &left_nappinfos);
-       right_appinfos = find_appinfos_by_relids(root, right_relids,
-                                                                                        &right_nappinfos);
-
-       sjinfo->min_lefthand = adjust_child_relids(sjinfo->min_lefthand,
-                                                                                          left_nappinfos, left_appinfos);
-       sjinfo->min_righthand = adjust_child_relids(sjinfo->min_righthand,
-                                                                                               right_nappinfos,
-                                                                                               right_appinfos);
-       sjinfo->syn_lefthand = adjust_child_relids(sjinfo->syn_lefthand,
-                                                                                          left_nappinfos, left_appinfos);
-       sjinfo->syn_righthand = adjust_child_relids(sjinfo->syn_righthand,
-                                                                                               right_nappinfos,
-                                                                                               right_appinfos);
-       sjinfo->semi_rhs_exprs = (List *) adjust_appendrel_attrs(root,
-                                                                                                                        (Node *) sjinfo->semi_rhs_exprs,
-                                                                                                                        right_nappinfos,
-                                                                                                                        right_appinfos);
-
-       pfree(left_appinfos);
-       pfree(right_appinfos);
-
-       return sjinfo;
-}
-
-/*
- * find_appinfos_by_relids
- *             Find AppendRelInfo structures for all relations specified by relids.
- *
- * The AppendRelInfos are returned in an array, which can be pfree'd by the
- * caller. *nappinfos is set to the number of entries in the array.
- */
-AppendRelInfo **
-find_appinfos_by_relids(PlannerInfo *root, Relids relids, int *nappinfos)
-{
-       AppendRelInfo **appinfos;
-       int                     cnt = 0;
-       int                     i;
-
-       *nappinfos = bms_num_members(relids);
-       appinfos = (AppendRelInfo **) palloc(sizeof(AppendRelInfo *) * *nappinfos);
-
-       i = -1;
-       while ((i = bms_next_member(relids, i)) >= 0)
-       {
-               AppendRelInfo *appinfo = root->append_rel_array[i];
-
-               if (!appinfo)
-                       elog(ERROR, "child rel %d not found in append_rel_array", i);
-
-               appinfos[cnt++] = appinfo;
-       }
-       return appinfos;
-}
index c54d0a690d85a9004b4877af6f283c1a732a47b5..adffb01226797c090ff19f1dca6aee18456cc4d1 100644 (file)
@@ -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 (file)
index 0000000..d48e3a0
--- /dev/null
@@ -0,0 +1,826 @@
+/*-------------------------------------------------------------------------
+ *
+ * appendinfo.c
+ *       Routines for mapping between append parent(s) and children
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *       src/backend/optimizer/path/appendinfo.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/sysattr.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
+#include "optimizer/appendinfo.h"
+#include "parser/parsetree.h"
+#include "utils/rel.h"
+#include "utils/lsyscache.h"
+#include "utils/syscache.h"
+
+
+typedef struct
+{
+       PlannerInfo *root;
+       int                     nappinfos;
+       AppendRelInfo **appinfos;
+} adjust_appendrel_attrs_context;
+
+static void make_inh_translation_list(Relation oldrelation,
+                                                 Relation newrelation,
+                                                 Index newvarno,
+                                                 List **translated_vars);
+static Node *adjust_appendrel_attrs_mutator(Node *node,
+                                                          adjust_appendrel_attrs_context *context);
+static Relids adjust_child_relids(Relids relids, int nappinfos,
+                                       AppendRelInfo **appinfos);
+static List *adjust_inherited_tlist(List *tlist,
+                                          AppendRelInfo *context);
+
+
+/*
+ * make_append_rel_info
+ *       Build an AppendRelInfo for the parent-child pair
+ */
+AppendRelInfo *
+make_append_rel_info(Relation parentrel, Relation childrel,
+                                        Index parentRTindex, Index childRTindex)
+{
+       AppendRelInfo *appinfo = makeNode(AppendRelInfo);
+
+       appinfo->parent_relid = parentRTindex;
+       appinfo->child_relid = childRTindex;
+       appinfo->parent_reltype = parentrel->rd_rel->reltype;
+       appinfo->child_reltype = childrel->rd_rel->reltype;
+       make_inh_translation_list(parentrel, childrel, childRTindex,
+                                                         &appinfo->translated_vars);
+       appinfo->parent_reloid = RelationGetRelid(parentrel);
+
+       return appinfo;
+}
+
+/*
+ * make_inh_translation_list
+ *       Build the list of translations from parent Vars to child Vars for
+ *       an inheritance child.
+ *
+ * For paranoia's sake, we match type/collation as well as attribute name.
+ */
+static void
+make_inh_translation_list(Relation oldrelation, Relation newrelation,
+                                                 Index newvarno,
+                                                 List **translated_vars)
+{
+       List       *vars = NIL;
+       TupleDesc       old_tupdesc = RelationGetDescr(oldrelation);
+       TupleDesc       new_tupdesc = RelationGetDescr(newrelation);
+       Oid                     new_relid = RelationGetRelid(newrelation);
+       int                     oldnatts = old_tupdesc->natts;
+       int                     newnatts = new_tupdesc->natts;
+       int                     old_attno;
+       int                     new_attno = 0;
+
+       for (old_attno = 0; old_attno < oldnatts; old_attno++)
+       {
+               Form_pg_attribute att;
+               char       *attname;
+               Oid                     atttypid;
+               int32           atttypmod;
+               Oid                     attcollation;
+
+               att = TupleDescAttr(old_tupdesc, old_attno);
+               if (att->attisdropped)
+               {
+                       /* Just put NULL into this list entry */
+                       vars = lappend(vars, NULL);
+                       continue;
+               }
+               attname = NameStr(att->attname);
+               atttypid = att->atttypid;
+               atttypmod = att->atttypmod;
+               attcollation = att->attcollation;
+
+               /*
+                * When we are generating the "translation list" for the parent table
+                * of an inheritance set, no need to search for matches.
+                */
+               if (oldrelation == newrelation)
+               {
+                       vars = lappend(vars, makeVar(newvarno,
+                                                                                (AttrNumber) (old_attno + 1),
+                                                                                atttypid,
+                                                                                atttypmod,
+                                                                                attcollation,
+                                                                                0));
+                       continue;
+               }
+
+               /*
+                * Otherwise we have to search for the matching column by name.
+                * There's no guarantee it'll have the same column position, because
+                * of cases like ALTER TABLE ADD COLUMN and multiple inheritance.
+                * However, in simple cases, the relative order of columns is mostly
+                * the same in both relations, so try the column of newrelation that
+                * follows immediately after the one that we just found, and if that
+                * fails, let syscache handle it.
+                */
+               if (new_attno >= newnatts ||
+                       (att = TupleDescAttr(new_tupdesc, new_attno))->attisdropped ||
+                       strcmp(attname, NameStr(att->attname)) != 0)
+               {
+                       HeapTuple       newtup;
+
+                       newtup = SearchSysCacheAttName(new_relid, attname);
+                       if (!newtup)
+                               elog(ERROR, "could not find inherited attribute \"%s\" of relation \"%s\"",
+                                        attname, RelationGetRelationName(newrelation));
+                       new_attno = ((Form_pg_attribute) GETSTRUCT(newtup))->attnum - 1;
+                       ReleaseSysCache(newtup);
+
+                       att = TupleDescAttr(new_tupdesc, new_attno);
+               }
+
+               /* Found it, check type and collation match */
+               if (atttypid != att->atttypid || atttypmod != att->atttypmod)
+                       elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's type",
+                                attname, RelationGetRelationName(newrelation));
+               if (attcollation != att->attcollation)
+                       elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's collation",
+                                attname, RelationGetRelationName(newrelation));
+
+               vars = lappend(vars, makeVar(newvarno,
+                                                                        (AttrNumber) (new_attno + 1),
+                                                                        atttypid,
+                                                                        atttypmod,
+                                                                        attcollation,
+                                                                        0));
+               new_attno++;
+       }
+
+       *translated_vars = vars;
+}
+
+/*
+ * translate_col_privs
+ *       Translate a bitmapset representing per-column privileges from the
+ *       parent rel's attribute numbering to the child's.
+ *
+ * The only surprise here is that we don't translate a parent whole-row
+ * reference into a child whole-row reference.  That would mean requiring
+ * permissions on all child columns, which is overly strict, since the
+ * query is really only going to reference the inherited columns.  Instead
+ * we set the per-column bits for all inherited columns.
+ */
+Bitmapset *
+translate_col_privs(const Bitmapset *parent_privs,
+                                       List *translated_vars)
+{
+       Bitmapset  *child_privs = NULL;
+       bool            whole_row;
+       int                     attno;
+       ListCell   *lc;
+
+       /* System attributes have the same numbers in all tables */
+       for (attno = FirstLowInvalidHeapAttributeNumber + 1; attno < 0; attno++)
+       {
+               if (bms_is_member(attno - FirstLowInvalidHeapAttributeNumber,
+                                                 parent_privs))
+                       child_privs = bms_add_member(child_privs,
+                                                                                attno - FirstLowInvalidHeapAttributeNumber);
+       }
+
+       /* Check if parent has whole-row reference */
+       whole_row = bms_is_member(InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber,
+                                                         parent_privs);
+
+       /* And now translate the regular user attributes, using the vars list */
+       attno = InvalidAttrNumber;
+       foreach(lc, translated_vars)
+       {
+               Var                *var = lfirst_node(Var, lc);
+
+               attno++;
+               if (var == NULL)                /* ignore dropped columns */
+                       continue;
+               if (whole_row ||
+                       bms_is_member(attno - FirstLowInvalidHeapAttributeNumber,
+                                                 parent_privs))
+                       child_privs = bms_add_member(child_privs,
+                                                                                var->varattno - FirstLowInvalidHeapAttributeNumber);
+       }
+
+       return child_privs;
+}
+
+/*
+ * adjust_appendrel_attrs
+ *       Copy the specified query or expression and translate Vars referring to a
+ *       parent rel to refer to the corresponding child rel instead.  We also
+ *       update rtindexes appearing outside Vars, such as resultRelation and
+ *       jointree relids.
+ *
+ * Note: this is only applied after conversion of sublinks to subplans,
+ * so we don't need to cope with recursion into sub-queries.
+ *
+ * Note: this is not hugely different from what pullup_replace_vars() does;
+ * maybe we should try to fold the two routines together.
+ */
+Node *
+adjust_appendrel_attrs(PlannerInfo *root, Node *node, int nappinfos,
+                                          AppendRelInfo **appinfos)
+{
+       Node       *result;
+       adjust_appendrel_attrs_context context;
+
+       context.root = root;
+       context.nappinfos = nappinfos;
+       context.appinfos = appinfos;
+
+       /* If there's nothing to adjust, don't call this function. */
+       Assert(nappinfos >= 1 && appinfos != NULL);
+
+       /*
+        * Must be prepared to start with a Query or a bare expression tree.
+        */
+       if (node && IsA(node, Query))
+       {
+               Query      *newnode;
+               int                     cnt;
+
+               newnode = query_tree_mutator((Query *) node,
+                                                                        adjust_appendrel_attrs_mutator,
+                                                                        (void *) &context,
+                                                                        QTW_IGNORE_RC_SUBQUERIES);
+               for (cnt = 0; cnt < nappinfos; cnt++)
+               {
+                       AppendRelInfo *appinfo = appinfos[cnt];
+
+                       if (newnode->resultRelation == appinfo->parent_relid)
+                       {
+                               newnode->resultRelation = appinfo->child_relid;
+                               /* Fix tlist resnos too, if it's inherited UPDATE */
+                               if (newnode->commandType == CMD_UPDATE)
+                                       newnode->targetList =
+                                               adjust_inherited_tlist(newnode->targetList,
+                                                                                          appinfo);
+                               break;
+                       }
+               }
+
+               result = (Node *) newnode;
+       }
+       else
+               result = adjust_appendrel_attrs_mutator(node, &context);
+
+       return result;
+}
+
+static Node *
+adjust_appendrel_attrs_mutator(Node *node,
+                                                          adjust_appendrel_attrs_context *context)
+{
+       AppendRelInfo **appinfos = context->appinfos;
+       int                     nappinfos = context->nappinfos;
+       int                     cnt;
+
+       if (node == NULL)
+               return NULL;
+       if (IsA(node, Var))
+       {
+               Var                *var = (Var *) copyObject(node);
+               AppendRelInfo *appinfo = NULL;
+
+               for (cnt = 0; cnt < nappinfos; cnt++)
+               {
+                       if (var->varno == appinfos[cnt]->parent_relid)
+                       {
+                               appinfo = appinfos[cnt];
+                               break;
+                       }
+               }
+
+               if (var->varlevelsup == 0 && appinfo)
+               {
+                       var->varno = appinfo->child_relid;
+                       var->varnoold = appinfo->child_relid;
+                       if (var->varattno > 0)
+                       {
+                               Node       *newnode;
+
+                               if (var->varattno > list_length(appinfo->translated_vars))
+                                       elog(ERROR, "attribute %d of relation \"%s\" does not exist",
+                                                var->varattno, get_rel_name(appinfo->parent_reloid));
+                               newnode = copyObject(list_nth(appinfo->translated_vars,
+                                                                                         var->varattno - 1));
+                               if (newnode == NULL)
+                                       elog(ERROR, "attribute %d of relation \"%s\" does not exist",
+                                                var->varattno, get_rel_name(appinfo->parent_reloid));
+                               return newnode;
+                       }
+                       else if (var->varattno == 0)
+                       {
+                               /*
+                                * Whole-row Var: if we are dealing with named rowtypes, we
+                                * can use a whole-row Var for the child table plus a coercion
+                                * step to convert the tuple layout to the parent's rowtype.
+                                * Otherwise we have to generate a RowExpr.
+                                */
+                               if (OidIsValid(appinfo->child_reltype))
+                               {
+                                       Assert(var->vartype == appinfo->parent_reltype);
+                                       if (appinfo->parent_reltype != appinfo->child_reltype)
+                                       {
+                                               ConvertRowtypeExpr *r = makeNode(ConvertRowtypeExpr);
+
+                                               r->arg = (Expr *) var;
+                                               r->resulttype = appinfo->parent_reltype;
+                                               r->convertformat = COERCE_IMPLICIT_CAST;
+                                               r->location = -1;
+                                               /* Make sure the Var node has the right type ID, too */
+                                               var->vartype = appinfo->child_reltype;
+                                               return (Node *) r;
+                                       }
+                               }
+                               else
+                               {
+                                       /*
+                                        * Build a RowExpr containing the translated variables.
+                                        *
+                                        * In practice var->vartype will always be RECORDOID here,
+                                        * so we need to come up with some suitable column names.
+                                        * We use the parent RTE's column names.
+                                        *
+                                        * Note: we can't get here for inheritance cases, so there
+                                        * is no need to worry that translated_vars might contain
+                                        * some dummy NULLs.
+                                        */
+                                       RowExpr    *rowexpr;
+                                       List       *fields;
+                                       RangeTblEntry *rte;
+
+                                       rte = rt_fetch(appinfo->parent_relid,
+                                                                  context->root->parse->rtable);
+                                       fields = copyObject(appinfo->translated_vars);
+                                       rowexpr = makeNode(RowExpr);
+                                       rowexpr->args = fields;
+                                       rowexpr->row_typeid = var->vartype;
+                                       rowexpr->row_format = COERCE_IMPLICIT_CAST;
+                                       rowexpr->colnames = copyObject(rte->eref->colnames);
+                                       rowexpr->location = -1;
+
+                                       return (Node *) rowexpr;
+                               }
+                       }
+                       /* system attributes don't need any other translation */
+               }
+               return (Node *) var;
+       }
+       if (IsA(node, CurrentOfExpr))
+       {
+               CurrentOfExpr *cexpr = (CurrentOfExpr *) copyObject(node);
+
+               for (cnt = 0; cnt < nappinfos; cnt++)
+               {
+                       AppendRelInfo *appinfo = appinfos[cnt];
+
+                       if (cexpr->cvarno == appinfo->parent_relid)
+                       {
+                               cexpr->cvarno = appinfo->child_relid;
+                               break;
+                       }
+               }
+               return (Node *) cexpr;
+       }
+       if (IsA(node, RangeTblRef))
+       {
+               RangeTblRef *rtr = (RangeTblRef *) copyObject(node);
+
+               for (cnt = 0; cnt < nappinfos; cnt++)
+               {
+                       AppendRelInfo *appinfo = appinfos[cnt];
+
+                       if (rtr->rtindex == appinfo->parent_relid)
+                       {
+                               rtr->rtindex = appinfo->child_relid;
+                               break;
+                       }
+               }
+               return (Node *) rtr;
+       }
+       if (IsA(node, JoinExpr))
+       {
+               /* Copy the JoinExpr node with correct mutation of subnodes */
+               JoinExpr   *j;
+               AppendRelInfo *appinfo;
+
+               j = (JoinExpr *) expression_tree_mutator(node,
+                                                                                                adjust_appendrel_attrs_mutator,
+                                                                                                (void *) context);
+               /* now fix JoinExpr's rtindex (probably never happens) */
+               for (cnt = 0; cnt < nappinfos; cnt++)
+               {
+                       appinfo = appinfos[cnt];
+
+                       if (j->rtindex == appinfo->parent_relid)
+                       {
+                               j->rtindex = appinfo->child_relid;
+                               break;
+                       }
+               }
+               return (Node *) j;
+       }
+       if (IsA(node, PlaceHolderVar))
+       {
+               /* Copy the PlaceHolderVar node with correct mutation of subnodes */
+               PlaceHolderVar *phv;
+
+               phv = (PlaceHolderVar *) expression_tree_mutator(node,
+                                                                                                                adjust_appendrel_attrs_mutator,
+                                                                                                                (void *) context);
+               /* now fix PlaceHolderVar's relid sets */
+               if (phv->phlevelsup == 0)
+                       phv->phrels = adjust_child_relids(phv->phrels, context->nappinfos,
+                                                                                         context->appinfos);
+               return (Node *) phv;
+       }
+       /* Shouldn't need to handle planner auxiliary nodes here */
+       Assert(!IsA(node, SpecialJoinInfo));
+       Assert(!IsA(node, AppendRelInfo));
+       Assert(!IsA(node, PlaceHolderInfo));
+       Assert(!IsA(node, MinMaxAggInfo));
+
+       /*
+        * We have to process RestrictInfo nodes specially.  (Note: although
+        * set_append_rel_pathlist will hide RestrictInfos in the parent's
+        * baserestrictinfo list from us, it doesn't hide those in joininfo.)
+        */
+       if (IsA(node, RestrictInfo))
+       {
+               RestrictInfo *oldinfo = (RestrictInfo *) node;
+               RestrictInfo *newinfo = makeNode(RestrictInfo);
+
+               /* Copy all flat-copiable fields */
+               memcpy(newinfo, oldinfo, sizeof(RestrictInfo));
+
+               /* Recursively fix the clause itself */
+               newinfo->clause = (Expr *)
+                       adjust_appendrel_attrs_mutator((Node *) oldinfo->clause, context);
+
+               /* and the modified version, if an OR clause */
+               newinfo->orclause = (Expr *)
+                       adjust_appendrel_attrs_mutator((Node *) oldinfo->orclause, context);
+
+               /* adjust relid sets too */
+               newinfo->clause_relids = adjust_child_relids(oldinfo->clause_relids,
+                                                                                                        context->nappinfos,
+                                                                                                        context->appinfos);
+               newinfo->required_relids = adjust_child_relids(oldinfo->required_relids,
+                                                                                                          context->nappinfos,
+                                                                                                          context->appinfos);
+               newinfo->outer_relids = adjust_child_relids(oldinfo->outer_relids,
+                                                                                                       context->nappinfos,
+                                                                                                       context->appinfos);
+               newinfo->nullable_relids = adjust_child_relids(oldinfo->nullable_relids,
+                                                                                                          context->nappinfos,
+                                                                                                          context->appinfos);
+               newinfo->left_relids = adjust_child_relids(oldinfo->left_relids,
+                                                                                                  context->nappinfos,
+                                                                                                  context->appinfos);
+               newinfo->right_relids = adjust_child_relids(oldinfo->right_relids,
+                                                                                                       context->nappinfos,
+                                                                                                       context->appinfos);
+
+               /*
+                * Reset cached derivative fields, since these might need to have
+                * different values when considering the child relation.  Note we
+                * don't reset left_ec/right_ec: each child variable is implicitly
+                * equivalent to its parent, so still a member of the same EC if any.
+                */
+               newinfo->eval_cost.startup = -1;
+               newinfo->norm_selec = -1;
+               newinfo->outer_selec = -1;
+               newinfo->left_em = NULL;
+               newinfo->right_em = NULL;
+               newinfo->scansel_cache = NIL;
+               newinfo->left_bucketsize = -1;
+               newinfo->right_bucketsize = -1;
+               newinfo->left_mcvfreq = -1;
+               newinfo->right_mcvfreq = -1;
+
+               return (Node *) newinfo;
+       }
+
+       /*
+        * NOTE: we do not need to recurse into sublinks, because they should
+        * already have been converted to subplans before we see them.
+        */
+       Assert(!IsA(node, SubLink));
+       Assert(!IsA(node, Query));
+
+       return expression_tree_mutator(node, adjust_appendrel_attrs_mutator,
+                                                                  (void *) context);
+}
+
+/*
+ * Substitute child relids for parent relids in a Relid set.  The array of
+ * appinfos specifies the substitutions to be performed.
+ */
+static Relids
+adjust_child_relids(Relids relids, int nappinfos, AppendRelInfo **appinfos)
+{
+       Bitmapset  *result = NULL;
+       int                     cnt;
+
+       for (cnt = 0; cnt < nappinfos; cnt++)
+       {
+               AppendRelInfo *appinfo = appinfos[cnt];
+
+               /* Remove parent, add child */
+               if (bms_is_member(appinfo->parent_relid, relids))
+               {
+                       /* Make a copy if we are changing the set. */
+                       if (!result)
+                               result = bms_copy(relids);
+
+                       result = bms_del_member(result, appinfo->parent_relid);
+                       result = bms_add_member(result, appinfo->child_relid);
+               }
+       }
+
+       /* If we made any changes, return the modified copy. */
+       if (result)
+               return result;
+
+       /* Otherwise, return the original set without modification. */
+       return relids;
+}
+
+/*
+ * Replace any relid present in top_parent_relids with its child in
+ * child_relids. Members of child_relids can be multiple levels below top
+ * parent in the partition hierarchy.
+ */
+Relids
+adjust_child_relids_multilevel(PlannerInfo *root, Relids relids,
+                                                          Relids child_relids, Relids top_parent_relids)
+{
+       AppendRelInfo **appinfos;
+       int                     nappinfos;
+       Relids          parent_relids = NULL;
+       Relids          result;
+       Relids          tmp_result = NULL;
+       int                     cnt;
+
+       /*
+        * If the given relids set doesn't contain any of the top parent relids,
+        * it will remain unchanged.
+        */
+       if (!bms_overlap(relids, top_parent_relids))
+               return relids;
+
+       appinfos = find_appinfos_by_relids(root, child_relids, &nappinfos);
+
+       /* Construct relids set for the immediate parent of the given child. */
+       for (cnt = 0; cnt < nappinfos; cnt++)
+       {
+               AppendRelInfo *appinfo = appinfos[cnt];
+
+               parent_relids = bms_add_member(parent_relids, appinfo->parent_relid);
+       }
+
+       /* Recurse if immediate parent is not the top parent. */
+       if (!bms_equal(parent_relids, top_parent_relids))
+       {
+               tmp_result = adjust_child_relids_multilevel(root, relids,
+                                                                                                       parent_relids,
+                                                                                                       top_parent_relids);
+               relids = tmp_result;
+       }
+
+       result = adjust_child_relids(relids, nappinfos, appinfos);
+
+       /* Free memory consumed by any intermediate result. */
+       if (tmp_result)
+               bms_free(tmp_result);
+       bms_free(parent_relids);
+       pfree(appinfos);
+
+       return result;
+}
+
+/*
+ * Adjust the targetlist entries of an inherited UPDATE operation
+ *
+ * The expressions have already been fixed, but we have to make sure that
+ * the target resnos match the child table (they may not, in the case of
+ * a column that was added after-the-fact by ALTER TABLE).  In some cases
+ * this can force us to re-order the tlist to preserve resno ordering.
+ * (We do all this work in special cases so that preptlist.c is fast for
+ * the typical case.)
+ *
+ * The given tlist has already been through expression_tree_mutator;
+ * therefore the TargetEntry nodes are fresh copies that it's okay to
+ * scribble on.
+ *
+ * Note that this is not needed for INSERT because INSERT isn't inheritable.
+ */
+static List *
+adjust_inherited_tlist(List *tlist, AppendRelInfo *context)
+{
+       bool            changed_it = false;
+       ListCell   *tl;
+       List       *new_tlist;
+       bool            more;
+       int                     attrno;
+
+       /* This should only happen for an inheritance case, not UNION ALL */
+       Assert(OidIsValid(context->parent_reloid));
+
+       /* Scan tlist and update resnos to match attnums of child rel */
+       foreach(tl, tlist)
+       {
+               TargetEntry *tle = (TargetEntry *) lfirst(tl);
+               Var                *childvar;
+
+               if (tle->resjunk)
+                       continue;                       /* ignore junk items */
+
+               /* Look up the translation of this column: it must be a Var */
+               if (tle->resno <= 0 ||
+                       tle->resno > list_length(context->translated_vars))
+                       elog(ERROR, "attribute %d of relation \"%s\" does not exist",
+                                tle->resno, get_rel_name(context->parent_reloid));
+               childvar = (Var *) list_nth(context->translated_vars, tle->resno - 1);
+               if (childvar == NULL || !IsA(childvar, Var))
+                       elog(ERROR, "attribute %d of relation \"%s\" does not exist",
+                                tle->resno, get_rel_name(context->parent_reloid));
+
+               if (tle->resno != childvar->varattno)
+               {
+                       tle->resno = childvar->varattno;
+                       changed_it = true;
+               }
+       }
+
+       /*
+        * If we changed anything, re-sort the tlist by resno, and make sure
+        * resjunk entries have resnos above the last real resno.  The sort
+        * algorithm is a bit stupid, but for such a seldom-taken path, small is
+        * probably better than fast.
+        */
+       if (!changed_it)
+               return tlist;
+
+       new_tlist = NIL;
+       more = true;
+       for (attrno = 1; more; attrno++)
+       {
+               more = false;
+               foreach(tl, tlist)
+               {
+                       TargetEntry *tle = (TargetEntry *) lfirst(tl);
+
+                       if (tle->resjunk)
+                               continue;               /* ignore junk items */
+
+                       if (tle->resno == attrno)
+                               new_tlist = lappend(new_tlist, tle);
+                       else if (tle->resno > attrno)
+                               more = true;
+               }
+       }
+
+       foreach(tl, tlist)
+       {
+               TargetEntry *tle = (TargetEntry *) lfirst(tl);
+
+               if (!tle->resjunk)
+                       continue;                       /* here, ignore non-junk items */
+
+               tle->resno = attrno;
+               new_tlist = lappend(new_tlist, tle);
+               attrno++;
+       }
+
+       return new_tlist;
+}
+
+/*
+ * adjust_appendrel_attrs_multilevel
+ *       Apply Var translations from a toplevel appendrel parent down to a child.
+ *
+ * In some cases we need to translate expressions referencing a parent relation
+ * to reference an appendrel child that's multiple levels removed from it.
+ */
+Node *
+adjust_appendrel_attrs_multilevel(PlannerInfo *root, Node *node,
+                                                                 Relids child_relids,
+                                                                 Relids top_parent_relids)
+{
+       AppendRelInfo **appinfos;
+       Bitmapset  *parent_relids = NULL;
+       int                     nappinfos;
+       int                     cnt;
+
+       Assert(bms_num_members(child_relids) == bms_num_members(top_parent_relids));
+
+       appinfos = find_appinfos_by_relids(root, child_relids, &nappinfos);
+
+       /* Construct relids set for the immediate parent of given child. */
+       for (cnt = 0; cnt < nappinfos; cnt++)
+       {
+               AppendRelInfo *appinfo = appinfos[cnt];
+
+               parent_relids = bms_add_member(parent_relids, appinfo->parent_relid);
+       }
+
+       /* Recurse if immediate parent is not the top parent. */
+       if (!bms_equal(parent_relids, top_parent_relids))
+               node = adjust_appendrel_attrs_multilevel(root, node, parent_relids,
+                                                                                                top_parent_relids);
+
+       /* Now translate for this child */
+       node = adjust_appendrel_attrs(root, node, nappinfos, appinfos);
+
+       pfree(appinfos);
+
+       return node;
+}
+
+/*
+ * Construct the SpecialJoinInfo for a child-join by translating
+ * SpecialJoinInfo for the join between parents. left_relids and right_relids
+ * are the relids of left and right side of the join respectively.
+ */
+SpecialJoinInfo *
+build_child_join_sjinfo(PlannerInfo *root, SpecialJoinInfo *parent_sjinfo,
+                                               Relids left_relids, Relids right_relids)
+{
+       SpecialJoinInfo *sjinfo = makeNode(SpecialJoinInfo);
+       AppendRelInfo **left_appinfos;
+       int                     left_nappinfos;
+       AppendRelInfo **right_appinfos;
+       int                     right_nappinfos;
+
+       memcpy(sjinfo, parent_sjinfo, sizeof(SpecialJoinInfo));
+       left_appinfos = find_appinfos_by_relids(root, left_relids,
+                                                                                       &left_nappinfos);
+       right_appinfos = find_appinfos_by_relids(root, right_relids,
+                                                                                        &right_nappinfos);
+
+       sjinfo->min_lefthand = adjust_child_relids(sjinfo->min_lefthand,
+                                                                                          left_nappinfos, left_appinfos);
+       sjinfo->min_righthand = adjust_child_relids(sjinfo->min_righthand,
+                                                                                               right_nappinfos,
+                                                                                               right_appinfos);
+       sjinfo->syn_lefthand = adjust_child_relids(sjinfo->syn_lefthand,
+                                                                                          left_nappinfos, left_appinfos);
+       sjinfo->syn_righthand = adjust_child_relids(sjinfo->syn_righthand,
+                                                                                               right_nappinfos,
+                                                                                               right_appinfos);
+       sjinfo->semi_rhs_exprs = (List *) adjust_appendrel_attrs(root,
+                                                                                                                        (Node *) sjinfo->semi_rhs_exprs,
+                                                                                                                        right_nappinfos,
+                                                                                                                        right_appinfos);
+
+       pfree(left_appinfos);
+       pfree(right_appinfos);
+
+       return sjinfo;
+}
+
+/*
+ * find_appinfos_by_relids
+ *             Find AppendRelInfo structures for all relations specified by relids.
+ *
+ * The AppendRelInfos are returned in an array, which can be pfree'd by the
+ * caller. *nappinfos is set to the number of entries in the array.
+ */
+AppendRelInfo **
+find_appinfos_by_relids(PlannerInfo *root, Relids relids, int *nappinfos)
+{
+       AppendRelInfo **appinfos;
+       int                     cnt = 0;
+       int                     i;
+
+       *nappinfos = bms_num_members(relids);
+       appinfos = (AppendRelInfo **) palloc(sizeof(AppendRelInfo *) * *nappinfos);
+
+       i = -1;
+       while ((i = bms_next_member(relids, i)) >= 0)
+       {
+               AppendRelInfo *appinfo = root->append_rel_array[i];
+
+               if (!appinfo)
+                       elog(ERROR, "child rel %d not found in append_rel_array", i);
+
+               appinfos[cnt++] = appinfo;
+       }
+       return appinfos;
+}
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
new file mode 100644 (file)
index 0000000..350e6af
--- /dev/null
@@ -0,0 +1,439 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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 "access/heapam.h"
+#include "catalog/partition.h"
+#include "catalog/pg_inherits.h"
+#include "miscadmin.h"
+#include "optimizer/appendinfo.h"
+#include "optimizer/inherit.h"
+#include "optimizer/planner.h"
+#include "optimizer/prep.h"
+#include "utils/rel.h"
+
+
+static void expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte,
+                                                Index rti);
+static void expand_partitioned_rtentry(PlannerInfo *root,
+                                                  RangeTblEntry *parentrte,
+                                                  Index parentRTindex, Relation parentrel,
+                                                  PlanRowMark *top_parentrc, LOCKMODE lockmode,
+                                                  List **appinfos);
+static void expand_single_inheritance_child(PlannerInfo *root,
+                                                               RangeTblEntry *parentrte,
+                                                               Index parentRTindex, Relation parentrel,
+                                                               PlanRowMark *top_parentrc, Relation childrel,
+                                                               List **appinfos, RangeTblEntry **childrte_p,
+                                                               Index *childRTindex_p);
+
+
+/*
+ * expand_inherited_tables
+ *             Expand each rangetable entry that represents an inheritance set
+ *             into an "append relation".  At the conclusion of this process,
+ *             the "inh" flag is set in all and only those RTEs that are append
+ *             relation parents.
+ */
+void
+expand_inherited_tables(PlannerInfo *root)
+{
+       Index           nrtes;
+       Index           rti;
+       ListCell   *rl;
+
+       /*
+        * expand_inherited_rtentry may add RTEs to parse->rtable. The function is
+        * expected to recursively handle any RTEs that it creates with inh=true.
+        * So just scan as far as the original end of the rtable list.
+        */
+       nrtes = list_length(root->parse->rtable);
+       rl = list_head(root->parse->rtable);
+       for (rti = 1; rti <= nrtes; rti++)
+       {
+               RangeTblEntry *rte = (RangeTblEntry *) lfirst(rl);
+
+               expand_inherited_rtentry(root, rte, rti);
+               rl = lnext(rl);
+       }
+}
+
+/*
+ * expand_inherited_rtentry
+ *             Check whether a rangetable entry represents an inheritance set.
+ *             If so, add entries for all the child tables to the query's
+ *             rangetable, and build AppendRelInfo nodes for all the child tables
+ *             and add them to root->append_rel_list.  If not, clear the entry's
+ *             "inh" flag to prevent later code from looking for AppendRelInfos.
+ *
+ * Note that the original RTE is considered to represent the whole
+ * inheritance set.  The first of the generated RTEs is an RTE for the same
+ * table, but with inh = false, to represent the parent table in its role
+ * as a simple member of the inheritance set.
+ *
+ * A childless table is never considered to be an inheritance set. For
+ * regular inheritance, a parent RTE must always have at least two associated
+ * AppendRelInfos: one corresponding to the parent table as a simple member of
+ * inheritance set and one or more corresponding to the actual children.
+ * Since a partitioned table is not scanned, it might have only one associated
+ * AppendRelInfo.
+ */
+static void
+expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
+{
+       Oid                     parentOID;
+       PlanRowMark *oldrc;
+       Relation        oldrelation;
+       LOCKMODE        lockmode;
+       List       *inhOIDs;
+       ListCell   *l;
+
+       /* Does RT entry allow inheritance? */
+       if (!rte->inh)
+               return;
+       /* Ignore any already-expanded UNION ALL nodes */
+       if (rte->rtekind != RTE_RELATION)
+       {
+               Assert(rte->rtekind == RTE_SUBQUERY);
+               return;
+       }
+       /* Fast path for common case of childless table */
+       parentOID = rte->relid;
+       if (!has_subclass(parentOID))
+       {
+               /* Clear flag before returning */
+               rte->inh = false;
+               return;
+       }
+
+       /*
+        * The rewriter should already have obtained an appropriate lock on each
+        * relation named in the query.  However, for each child relation we add
+        * to the query, we must obtain an appropriate lock, because this will be
+        * the first use of those relations in the parse/rewrite/plan pipeline.
+        * Child rels should use the same lockmode as their parent.
+        */
+       lockmode = rte->rellockmode;
+
+       /* Scan for all members of inheritance set, acquire needed locks */
+       inhOIDs = find_all_inheritors(parentOID, lockmode, NULL);
+
+       /*
+        * Check that there's at least one descendant, else treat as no-child
+        * case.  This could happen despite above has_subclass() check, if table
+        * once had a child but no longer does.
+        */
+       if (list_length(inhOIDs) < 2)
+       {
+               /* Clear flag before returning */
+               rte->inh = false;
+               return;
+       }
+
+       /*
+        * If parent relation is selected FOR UPDATE/SHARE, we need to mark its
+        * PlanRowMark as isParent = true, and generate a new PlanRowMark for each
+        * child.
+        */
+       oldrc = get_plan_rowmark(root->rowMarks, rti);
+       if (oldrc)
+               oldrc->isParent = true;
+
+       /*
+        * Must open the parent relation to examine its tupdesc.  We need not lock
+        * it; we assume the rewriter already did.
+        */
+       oldrelation = heap_open(parentOID, NoLock);
+
+       /* Scan the inheritance set and expand it */
+       if (RelationGetPartitionDesc(oldrelation) != NULL)
+       {
+               Assert(rte->relkind == RELKIND_PARTITIONED_TABLE);
+
+               /*
+                * If this table has partitions, recursively expand them in the order
+                * in which they appear in the PartitionDesc.  While at it, also
+                * extract the partition key columns of all the partitioned tables.
+                */
+               expand_partitioned_rtentry(root, rte, rti, oldrelation, oldrc,
+                                                                  lockmode, &root->append_rel_list);
+       }
+       else
+       {
+               List       *appinfos = NIL;
+               RangeTblEntry *childrte;
+               Index           childRTindex;
+
+               /*
+                * This table has no partitions.  Expand any plain inheritance
+                * children in the order the OIDs were returned by
+                * find_all_inheritors.
+                */
+               foreach(l, inhOIDs)
+               {
+                       Oid                     childOID = lfirst_oid(l);
+                       Relation        newrelation;
+
+                       /* Open rel if needed; we already have required locks */
+                       if (childOID != parentOID)
+                               newrelation = heap_open(childOID, NoLock);
+                       else
+                               newrelation = oldrelation;
+
+                       /*
+                        * It is possible that the parent table has children that are temp
+                        * tables of other backends.  We cannot safely access such tables
+                        * (because of buffering issues), and the best thing to do seems
+                        * to be to silently ignore them.
+                        */
+                       if (childOID != parentOID && RELATION_IS_OTHER_TEMP(newrelation))
+                       {
+                               heap_close(newrelation, lockmode);
+                               continue;
+                       }
+
+                       expand_single_inheritance_child(root, rte, rti, oldrelation, oldrc,
+                                                                                       newrelation,
+                                                                                       &appinfos, &childrte,
+                                                                                       &childRTindex);
+
+                       /* Close child relations, but keep locks */
+                       if (childOID != parentOID)
+                               heap_close(newrelation, NoLock);
+               }
+
+               /*
+                * If all the children were temp tables, pretend it's a
+                * non-inheritance situation; we don't need Append node in that case.
+                * The duplicate RTE we added for the parent table is harmless, so we
+                * don't bother to get rid of it; ditto for the useless PlanRowMark
+                * node.
+                */
+               if (list_length(appinfos) < 2)
+                       rte->inh = false;
+               else
+                       root->append_rel_list = list_concat(root->append_rel_list,
+                                                                                               appinfos);
+
+       }
+
+       heap_close(oldrelation, NoLock);
+}
+
+/*
+ * expand_partitioned_rtentry
+ *             Recursively expand an RTE for a partitioned table.
+ */
+static void
+expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,
+                                                  Index parentRTindex, Relation parentrel,
+                                                  PlanRowMark *top_parentrc, LOCKMODE lockmode,
+                                                  List **appinfos)
+{
+       int                     i;
+       RangeTblEntry *childrte;
+       Index           childRTindex;
+       PartitionDesc partdesc = RelationGetPartitionDesc(parentrel);
+
+       check_stack_depth();
+
+       /* A partitioned table should always have a partition descriptor. */
+       Assert(partdesc);
+
+       Assert(parentrte->inh);
+
+       /*
+        * Note down whether any partition key cols are being updated. Though it's
+        * the root partitioned table's updatedCols we are interested in, we
+        * instead use parentrte to get the updatedCols. This is convenient
+        * because parentrte already has the root partrel's updatedCols translated
+        * to match the attribute ordering of parentrel.
+        */
+       if (!root->partColsUpdated)
+               root->partColsUpdated =
+                       has_partition_attrs(parentrel, parentrte->updatedCols, NULL);
+
+       /* First expand the partitioned table itself. */
+       expand_single_inheritance_child(root, parentrte, parentRTindex, parentrel,
+                                                                       top_parentrc, parentrel,
+                                                                       appinfos, &childrte, &childRTindex);
+
+       /*
+        * If the partitioned table has no partitions, treat this as the
+        * non-inheritance case.
+        */
+       if (partdesc->nparts == 0)
+       {
+               parentrte->inh = false;
+               return;
+       }
+
+       for (i = 0; i < partdesc->nparts; i++)
+       {
+               Oid                     childOID = partdesc->oids[i];
+               Relation        childrel;
+
+               /* Open rel; we already have required locks */
+               childrel = heap_open(childOID, NoLock);
+
+               /*
+                * Temporary partitions belonging to other sessions should have been
+                * disallowed at definition, but for paranoia's sake, let's double
+                * check.
+                */
+               if (RELATION_IS_OTHER_TEMP(childrel))
+                       elog(ERROR, "temporary relation from another session found as partition");
+
+               expand_single_inheritance_child(root, parentrte, parentRTindex,
+                                                                               parentrel, top_parentrc, childrel,
+                                                                               appinfos, &childrte, &childRTindex);
+
+               /* If this child is itself partitioned, recurse */
+               if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+                       expand_partitioned_rtentry(root, childrte, childRTindex,
+                                                                          childrel, top_parentrc, lockmode,
+                                                                          appinfos);
+
+               /* Close child relation, but keep locks */
+               heap_close(childrel, NoLock);
+       }
+}
+
+/*
+ * expand_single_inheritance_child
+ *             Build a RangeTblEntry and an AppendRelInfo, if appropriate, plus
+ *             maybe a PlanRowMark.
+ *
+ * We now expand the partition hierarchy level by level, creating a
+ * corresponding hierarchy of AppendRelInfos and RelOptInfos, where each
+ * partitioned descendant acts as a parent of its immediate partitions.
+ * (This is a difference from what older versions of PostgreSQL did and what
+ * is still done in the case of table inheritance for unpartitioned tables,
+ * where the hierarchy is flattened during RTE expansion.)
+ *
+ * PlanRowMarks still carry the top-parent's RTI, and the top-parent's
+ * allMarkTypes field still accumulates values from all descendents.
+ *
+ * "parentrte" and "parentRTindex" are immediate parent's RTE and
+ * RTI. "top_parentrc" is top parent's PlanRowMark.
+ *
+ * The child RangeTblEntry and its RTI are returned in "childrte_p" and
+ * "childRTindex_p" resp.
+ */
+static void
+expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
+                                                               Index parentRTindex, Relation parentrel,
+                                                               PlanRowMark *top_parentrc, Relation childrel,
+                                                               List **appinfos, RangeTblEntry **childrte_p,
+                                                               Index *childRTindex_p)
+{
+       Query      *parse = root->parse;
+       Oid                     parentOID = RelationGetRelid(parentrel);
+       Oid                     childOID = RelationGetRelid(childrel);
+       RangeTblEntry *childrte;
+       Index           childRTindex;
+       AppendRelInfo *appinfo;
+
+       /*
+        * Build an RTE for the child, and attach to query's rangetable list. We
+        * copy most fields of the parent's RTE, but replace relation OID and
+        * relkind, and set inh = false.  Also, set requiredPerms to zero since
+        * all required permissions checks are done on the original RTE. Likewise,
+        * set the child's securityQuals to empty, because we only want to apply
+        * the parent's RLS conditions regardless of what RLS properties
+        * individual children may have.  (This is an intentional choice to make
+        * inherited RLS work like regular permissions checks.) The parent
+        * securityQuals will be propagated to children along with other base
+        * restriction clauses, so we don't need to do it here.
+        */
+       childrte = copyObject(parentrte);
+       *childrte_p = childrte;
+       childrte->relid = childOID;
+       childrte->relkind = childrel->rd_rel->relkind;
+       /* A partitioned child will need to be expanded further. */
+       if (childOID != parentOID &&
+               childrte->relkind == RELKIND_PARTITIONED_TABLE)
+               childrte->inh = true;
+       else
+               childrte->inh = false;
+       childrte->requiredPerms = 0;
+       childrte->securityQuals = NIL;
+       parse->rtable = lappend(parse->rtable, childrte);
+       childRTindex = list_length(parse->rtable);
+       *childRTindex_p = childRTindex;
+
+       /*
+        * We need an AppendRelInfo if paths will be built for the child RTE. If
+        * childrte->inh is true, then we'll always need to generate append paths
+        * for it.  If childrte->inh is false, we must scan it if it's not a
+        * partitioned table; but if it is a partitioned table, then it never has
+        * any data of its own and need not be scanned.
+        */
+       if (childrte->relkind != RELKIND_PARTITIONED_TABLE || childrte->inh)
+       {
+               appinfo = make_append_rel_info(parentrel, childrel,
+                                                                          parentRTindex, childRTindex);
+               *appinfos = lappend(*appinfos, appinfo);
+
+               /*
+                * Translate the column permissions bitmaps to the child's attnums (we
+                * have to build the translated_vars list before we can do this). But
+                * if this is the parent table, leave copyObject's result alone.
+                *
+                * Note: we need to do this even though the executor won't run any
+                * permissions checks on the child RTE.  The insertedCols/updatedCols
+                * bitmaps may be examined for trigger-firing purposes.
+                */
+               if (childOID != parentOID)
+               {
+                       childrte->selectedCols = translate_col_privs(parentrte->selectedCols,
+                                                                                                                appinfo->translated_vars);
+                       childrte->insertedCols = translate_col_privs(parentrte->insertedCols,
+                                                                                                                appinfo->translated_vars);
+                       childrte->updatedCols = translate_col_privs(parentrte->updatedCols,
+                                                                                                               appinfo->translated_vars);
+               }
+       }
+
+       /*
+        * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE.
+        */
+       if (top_parentrc)
+       {
+               PlanRowMark *childrc = makeNode(PlanRowMark);
+
+               childrc->rti = childRTindex;
+               childrc->prti = top_parentrc->rti;
+               childrc->rowmarkId = top_parentrc->rowmarkId;
+               /* Reselect rowmark type, because relkind might not match parent */
+               childrc->markType = select_rowmark_type(childrte,
+                                                                                               top_parentrc->strength);
+               childrc->allMarkTypes = (1 << childrc->markType);
+               childrc->strength = top_parentrc->strength;
+               childrc->waitPolicy = top_parentrc->waitPolicy;
+
+               /*
+                * We mark RowMarks for partitioned child tables as parent RowMarks so
+                * that the executor ignores them (except their existence means that
+                * the child tables be locked using appropriate mode).
+                */
+               childrc->isParent = (childrte->relkind == RELKIND_PARTITIONED_TABLE);
+
+               /* Include child's rowmark type in top parent's allMarkTypes */
+               top_parentrc->allMarkTypes |= childrc->allMarkTypes;
+
+               root->rowMarks = lappend(root->rowMarks, childrc);
+       }
+}
index 5921e893c12fd186a2859876edf10be17d99effa..b2637d0e89ae026b11795634daa5efd2c608d21f 100644 (file)
@@ -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"
index 111b42d654899f110584acdfc2bd3fa10a56e9bd..fe83ec451925d764a8f9ff3bc0680ffb194c2782 100644 (file)
@@ -17,6 +17,7 @@
 #include <limits.h>
 
 #include "miscadmin.h"
+#include "optimizer/appendinfo.h"
 #include "optimizer/clauses.h"
 #include "optimizer/cost.h"
 #include "optimizer/pathnode.h"
index 0684083a8d3e6a5d998d24559f8617682bb8ef16..901433c68c771309d8ce949134c914f58fc1230d 100644 (file)
@@ -44,6 +44,7 @@
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
+#include "optimizer/appendinfo.h"
 #include "optimizer/clauses.h"
 #include "optimizer/pathnode.h"
 #include "optimizer/planner.h"
diff --git a/src/include/optimizer/appendinfo.h b/src/include/optimizer/appendinfo.h
new file mode 100644 (file)
index 0000000..2348c9c
--- /dev/null
@@ -0,0 +1,42 @@
+/*-------------------------------------------------------------------------
+ *
+ * appendinfo.h
+ *       Routines for mapping expressions between append rel parent(s) and
+ *       children
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/optimizer/appendinfo.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef APPENDINFO_H
+#define APPENDINFO_H
+
+#include "nodes/plannodes.h"
+#include "nodes/relation.h"
+#include "utils/relcache.h"
+
+extern AppendRelInfo *make_append_rel_info(Relation parentrel,
+                                        Relation childrel,
+                                        Index parentRTindex, Index childRTindex);
+extern Bitmapset *translate_col_privs(const Bitmapset *parent_privs,
+                                       List *translated_vars);
+extern Node *adjust_appendrel_attrs(PlannerInfo *root, Node *node,
+                                          int nappinfos, AppendRelInfo **appinfos);
+
+extern Node *adjust_appendrel_attrs_multilevel(PlannerInfo *root, Node *node,
+                                                                 Relids child_relids,
+                                                                 Relids top_parent_relids);
+
+extern AppendRelInfo **find_appinfos_by_relids(PlannerInfo *root,
+                                               Relids relids, int *nappinfos);
+
+extern SpecialJoinInfo *build_child_join_sjinfo(PlannerInfo *root,
+                                               SpecialJoinInfo *parent_sjinfo,
+                                               Relids left_relids, Relids right_relids);
+extern Relids adjust_child_relids_multilevel(PlannerInfo *root, Relids relids,
+                                                          Relids child_relids, Relids top_parent_relids);
+
+#endif                                                 /* APPENDINFO_H */
diff --git a/src/include/optimizer/inherit.h b/src/include/optimizer/inherit.h
new file mode 100644 (file)
index 0000000..b2687ab
--- /dev/null
@@ -0,0 +1,22 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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"
+
+
+extern void expand_inherited_tables(PlannerInfo *root);
+
+#endif                                                 /* INHERIT_H */
index d6991d56905030df87911cf9ed6fdbf684e2859a..62d45dd142660c41bfe94c0977453df8619fe460 100644 (file)
@@ -47,22 +47,4 @@ extern PlanRowMark *get_plan_rowmark(List *rowmarks, Index rtindex);
  */
 extern RelOptInfo *plan_set_operations(PlannerInfo *root);
 
-extern void expand_inherited_tables(PlannerInfo *root);
-
-extern Node *adjust_appendrel_attrs(PlannerInfo *root, Node *node,
-                                          int nappinfos, AppendRelInfo **appinfos);
-
-extern Node *adjust_appendrel_attrs_multilevel(PlannerInfo *root, Node *node,
-                                                                 Relids child_relids,
-                                                                 Relids top_parent_relids);
-
-extern AppendRelInfo **find_appinfos_by_relids(PlannerInfo *root,
-                                               Relids relids, int *nappinfos);
-
-extern SpecialJoinInfo *build_child_join_sjinfo(PlannerInfo *root,
-                                               SpecialJoinInfo *parent_sjinfo,
-                                               Relids left_relids, Relids right_relids);
-extern Relids adjust_child_relids_multilevel(PlannerInfo *root, Relids relids,
-                                                          Relids child_relids, Relids top_parent_relids);
-
 #endif                                                 /* PREP_H */