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 */