]> granicus.if.org Git - postgresql/commitdiff
Fix EquivalenceClass processing for nested append relations.
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 28 Mar 2014 15:50:01 +0000 (11:50 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 28 Mar 2014 15:50:01 +0000 (11:50 -0400)
The original coding of EquivalenceClasses didn't foresee that appendrel
child relations might themselves be appendrels; but this is possible for
example when a UNION ALL subquery scans a table with inheritance children.
The oversight led to failure to optimize ordering-related issues very well
for the grandchild tables.  After some false starts involving explicitly
flattening the appendrel representation, we found that this could be fixed
easily by removing a few implicit assumptions about appendrel parent rels
not being children themselves.

Kyotaro Horiguchi and Tom Lane, reviewed by Noah Misch

src/backend/optimizer/path/allpaths.c
src/backend/optimizer/path/equivclass.c
src/backend/optimizer/plan/createplan.c
src/test/regress/expected/union.out
src/test/regress/sql/union.sql

index 03be7b1fe88970645b6a7a5db8875fd4ff84d1c7..5777cb2ff0c009d7a70be34626d060609a32426a 100644 (file)
@@ -1021,10 +1021,15 @@ get_cheapest_parameterized_child_path(PlannerInfo *root, RelOptInfo *rel,
  * accumulate_append_subpath
  *             Add a subpath to the list being built for an Append or MergeAppend
  *
- * It's possible that the child is itself an Append path, in which case
- * we can "cut out the middleman" and just add its child paths to our
- * own list.  (We don't try to do this earlier because we need to
- * apply both levels of transformation to the quals.)
+ * It's possible that the child is itself an Append or MergeAppend path, in
+ * which case we can "cut out the middleman" and just add its child paths to
+ * our own list.  (We don't try to do this earlier because we need to apply
+ * both levels of transformation to the quals.)
+ *
+ * Note that if we omit a child MergeAppend in this way, we are effectively
+ * omitting a sort step, which seems fine: if the parent is to be an Append,
+ * its result would be unsorted anyway, while if the parent is to be a
+ * MergeAppend, there's no point in a separate sort on a child.
  */
 static List *
 accumulate_append_subpath(List *subpaths, Path *path)
@@ -1036,6 +1041,13 @@ accumulate_append_subpath(List *subpaths, Path *path)
                /* list_copy is important here to avoid sharing list substructure */
                return list_concat(subpaths, list_copy(apath->subpaths));
        }
+       else if (IsA(path, MergeAppendPath))
+       {
+               MergeAppendPath *mpath = (MergeAppendPath *) path;
+
+               /* list_copy is important here to avoid sharing list substructure */
+               return list_concat(subpaths, list_copy(mpath->subpaths));
+       }
        else
                return lappend(subpaths, path);
 }
index 35d2a83599d7dfdaa0ecdcbc0d6cfab268dd27df..ac12f84fd5ea441cae2f894b54bdfd8574e5a41b 100644 (file)
@@ -1937,16 +1937,20 @@ add_child_rel_equivalences(PlannerInfo *root,
                if (cur_ec->ec_has_volatile)
                        continue;
 
-               /* No point in searching if parent rel not mentioned in eclass */
-               if (!bms_is_subset(parent_rel->relids, cur_ec->ec_relids))
+               /*
+                * No point in searching if parent rel not mentioned in eclass; but
+                * we can't tell that for sure if parent rel is itself a child.
+                */
+               if (parent_rel->reloptkind == RELOPT_BASEREL &&
+                       !bms_is_subset(parent_rel->relids, cur_ec->ec_relids))
                        continue;
 
                foreach(lc2, cur_ec->ec_members)
                {
                        EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 
-                       if (cur_em->em_is_const || cur_em->em_is_child)
-                               continue;               /* ignore consts and children here */
+                       if (cur_em->em_is_const)
+                               continue;               /* ignore consts here */
 
                        /* Does it reference parent_rel? */
                        if (bms_overlap(cur_em->em_relids, parent_rel->relids))
index 184d37a88182ba9f067cea2905478f925c0ce9cf..784805fbf4343a7a3d2b887e2bd70f02067967de 100644 (file)
@@ -751,7 +751,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path)
 
        /* Compute sort column info, and adjust MergeAppend's tlist as needed */
        (void) prepare_sort_from_pathkeys(root, plan, pathkeys,
-                                                                         NULL,
+                                                                         best_path->path.parent->relids,
                                                                          NULL,
                                                                          true,
                                                                          &node->numCols,
index 6f9ee5eb47116f23ddd3dad3440f4c6e1a4541fc..6a7d0f2a889cb9c59755b8e322bb39c8fe98569f 100644 (file)
@@ -502,9 +502,56 @@ explain (costs off)
                Index Cond: (ab = 'ab'::text)
 (7 rows)
 
+--
+-- Test that ORDER BY for UNION ALL can be pushed down to inheritance
+-- children.
+--
+CREATE TEMP TABLE t1c (b text, a text);
+ALTER TABLE t1c INHERIT t1;
+CREATE TEMP TABLE t2c (primary key (ab)) INHERITS (t2);
+INSERT INTO t1c VALUES ('v', 'w'), ('c', 'd'), ('m', 'n'), ('e', 'f');
+INSERT INTO t2c VALUES ('vw'), ('cd'), ('mn'), ('ef');
+CREATE INDEX t1c_ab_idx on t1c ((a || b));
+set enable_seqscan = on;
+set enable_indexonlyscan = off;
+explain (costs off)
+  SELECT * FROM
+  (SELECT a || b AS ab FROM t1
+   UNION ALL
+   SELECT ab FROM t2) t
+  ORDER BY 1 LIMIT 8;
+                   QUERY PLAN                   
+------------------------------------------------
+ Limit
+   ->  Merge Append
+         Sort Key: ((t1.a || t1.b))
+         ->  Index Scan using t1_ab_idx on t1
+         ->  Index Scan using t1c_ab_idx on t1c
+         ->  Index Scan using t2_pkey on t2
+         ->  Index Scan using t2c_pkey on t2c
+(7 rows)
+
+  SELECT * FROM
+  (SELECT a || b AS ab FROM t1
+   UNION ALL
+   SELECT ab FROM t2) t
+  ORDER BY 1 LIMIT 8;
+ ab 
+----
+ ab
+ ab
+ cd
+ dc
+ ef
+ fe
+ mn
+ nm
+(8 rows)
+
 reset enable_seqscan;
 reset enable_indexscan;
 reset enable_bitmapscan;
+reset enable_indexonlyscan;
 -- Test constraint exclusion of UNION ALL subqueries
 explain (costs off)
  SELECT * FROM
index d567cf148191870756825fde711784a2704e60e6..5205435e2fd4e75b3b7ded9bd960ec1f14606ea5 100644 (file)
@@ -198,9 +198,38 @@ explain (costs off)
   SELECT * FROM t2) t
  WHERE ab = 'ab';
 
+--
+-- Test that ORDER BY for UNION ALL can be pushed down to inheritance
+-- children.
+--
+
+CREATE TEMP TABLE t1c (b text, a text);
+ALTER TABLE t1c INHERIT t1;
+CREATE TEMP TABLE t2c (primary key (ab)) INHERITS (t2);
+INSERT INTO t1c VALUES ('v', 'w'), ('c', 'd'), ('m', 'n'), ('e', 'f');
+INSERT INTO t2c VALUES ('vw'), ('cd'), ('mn'), ('ef');
+CREATE INDEX t1c_ab_idx on t1c ((a || b));
+
+set enable_seqscan = on;
+set enable_indexonlyscan = off;
+
+explain (costs off)
+  SELECT * FROM
+  (SELECT a || b AS ab FROM t1
+   UNION ALL
+   SELECT ab FROM t2) t
+  ORDER BY 1 LIMIT 8;
+
+  SELECT * FROM
+  (SELECT a || b AS ab FROM t1
+   UNION ALL
+   SELECT ab FROM t2) t
+  ORDER BY 1 LIMIT 8;
+
 reset enable_seqscan;
 reset enable_indexscan;
 reset enable_bitmapscan;
+reset enable_indexonlyscan;
 
 -- Test constraint exclusion of UNION ALL subqueries
 explain (costs off)