Get rid of duplicate child RTE for a partitioned table.
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 26 Mar 2019 16:03:27 +0000 (12:03 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 26 Mar 2019 16:03:27 +0000 (12:03 -0400)
We've been creating duplicate RTEs for partitioned tables just
because we do so for regular inheritance parent tables.  But unlike
regular-inheritance parents which are themselves regular tables
and thus need to be scanned, partitioned tables don't need the
extra RTE.

This makes the conditions for building a child RTE the same as those
for building an AppendRelInfo, allowing minor simplification in
expand_single_inheritance_child.  Since the planner's actual processing
is driven off the AppendRelInfo list, nothing much changes beyond that,
we just have one fewer useless RTE entry.

Amit Langote, reviewed and hacked a bit by me

Discussion: https://postgr.es/m/9d7c5112-cb99-6a47-d3be-cf1ee6862a1d@lab.ntt.co.jp

contrib/postgres_fdw/expected/postgres_fdw.out
src/backend/optimizer/util/inherit.c

index 2be67bca02ab6b483a16a9b7af215efe947436f7..9ad9035c5df1823cfbedd7961701832e8ae34ad2 100644 (file)
@@ -8435,7 +8435,7 @@ SELECT t1.a,t2.b,t2.c FROM fprt1 t1 LEFT JOIN (SELECT * FROM fprt2 WHERE a < 10)
  Foreign Scan
    Output: t1.a, ftprt2_p1.b, ftprt2_p1.c
    Relations: (public.ftprt1_p1 t1) LEFT JOIN (public.ftprt2_p1 fprt2)
-   Remote SQL: SELECT r6.a, r9.b, r9.c FROM (public.fprt1_p1 r6 LEFT JOIN public.fprt2_p1 r9 ON (((r6.a = r9.b)) AND ((r6.b = r9.a)) AND ((r9.a < 10)))) WHERE ((r6.a < 10)) ORDER BY r6.a ASC NULLS LAST, r9.b ASC NULLS LAST, r9.c ASC NULLS LAST
+   Remote SQL: SELECT r5.a, r7.b, r7.c FROM (public.fprt1_p1 r5 LEFT JOIN public.fprt2_p1 r7 ON (((r5.a = r7.b)) AND ((r5.b = r7.a)) AND ((r7.a < 10)))) WHERE ((r5.a < 10)) ORDER BY r5.a ASC NULLS LAST, r7.b ASC NULLS LAST, r7.c ASC NULLS LAST
 (4 rows)
 
 SELECT t1.a,t2.b,t2.c FROM fprt1 t1 LEFT JOIN (SELECT * FROM fprt2 WHERE a < 10) t2 ON (t1.a = t2.b and t1.b = t2.a) WHERE t1.a < 10 ORDER BY 1,2,3;
index 1fa154e0cb141194d08fd1ff03beec7f5a03a375..1d1e5060e26af41d98c227862227057a3c7d0f06 100644 (file)
@@ -90,9 +90,10 @@ expand_inherited_tables(PlannerInfo *root)
  * 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.
+ * the inheritance set and one or more corresponding to the actual children.
+ * (But a partitioned table might have only one associated AppendRelInfo,
+ * since it's not itself scanned and hence doesn't need a second RTE to
+ * represent itself as a member of the set.)
  */
 static void
 expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
@@ -145,6 +146,9 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
    /* Scan the inheritance set and expand it */
    if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    {
+       /*
+        * Partitioned table, so set up for partitioning.
+        */
        Assert(rte->relkind == RELKIND_PARTITIONED_TABLE);
 
        if (root->glob->partition_directory == NULL)
@@ -161,6 +165,11 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
    }
    else
    {
+       /*
+        * Ordinary table, so process traditional-inheritance children.  (Note
+        * that partitioned tables are not allowed to have inheritance
+        * children, so it's not possible for both cases to apply.)
+        */
        List       *appinfos = NIL;
        RangeTblEntry *childrte;
        Index       childRTindex;
@@ -182,8 +191,7 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
        }
 
        /*
-        * This table has no partitions.  Expand any plain inheritance
-        * children in the order the OIDs were returned by
+        * Expand inheritance children in the order the OIDs were returned by
         * find_all_inheritors.
         */
        foreach(l, inhOIDs)
@@ -273,11 +281,6 @@ expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,
        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.
@@ -288,6 +291,11 @@ expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,
        return;
    }
 
+   /*
+    * Create a child RTE for each partition.  Note that unlike traditional
+    * inheritance, we don't need a child RTE for the partitioned table
+    * itself, because it's not going to be scanned.
+    */
    for (i = 0; i < partdesc->nparts; i++)
    {
        Oid         childOID = partdesc->oids[i];
@@ -321,8 +329,7 @@ expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,
 
 /*
  * expand_single_inheritance_child
- *     Build a RangeTblEntry and an AppendRelInfo, if appropriate, plus
- *     maybe a PlanRowMark.
+ *     Build a RangeTblEntry and an AppendRelInfo, plus maybe a PlanRowMark.
  *
  * We now expand the partition hierarchy level by level, creating a
  * corresponding hierarchy of AppendRelInfos and RelOptInfos, where each
@@ -371,9 +378,11 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
    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)
+   if (childrte->relkind == RELKIND_PARTITIONED_TABLE)
+   {
+       Assert(childOID != parentOID);
        childrte->inh = true;
+   }
    else
        childrte->inh = false;
    childrte->requiredPerms = 0;
@@ -383,36 +392,29 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
    *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.
+    * Build an AppendRelInfo struct for each parent/child pair.
     */
-   if (childrte->relkind != RELKIND_PARTITIONED_TABLE || childrte->inh)
-   {
-       appinfo = make_append_rel_info(parentrel, childrel,
-                                      parentRTindex, childRTindex);
-       *appinfos = lappend(*appinfos, appinfo);
+   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);
-       }
+   /*
+    * 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, we can 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);
    }
 
    /*