OSDN Git Service

Refactor hint application mechamism
authorKyotaro Horiguchi <horiguchi.kyotaro@lab.ntt.co.jp>
Thu, 9 Feb 2017 01:50:29 +0000 (10:50 +0900)
committerKyotaro Horiguchi <horiguchi.kyotaro@lab.ntt.co.jp>
Thu, 9 Feb 2017 01:50:29 +0000 (10:50 +0900)
Before we have the new hook set_rel_pathlist_hook, plan enforcment is
performed in two steps, one works only for queries with one relation,
the other works only for joins. Now they are performed once and at
once in set_rel_pathlist_hook. As a byproduct of this refactoring,
index restriction no longer runs for indexes on constraint-excluded
relations and expected/ut-S.out changed a bit.

The major changes by this patch are the followings.

- Remove hook to get_relation_info_hook:
  The function for the hook pg_hint_plan_get_relation_info is removed.

- pg_hint_plan_join_search no longer calls rebuild_scan_path. The
  function and a copied function set_plain_rel_pathlist are removed.

- delete_indexes has been renamed to restrict_indexes, which
  represents the functionality clearer. And the function was
  refactored main for readablity.

- Added several regtests for parallel.

core.c
expected/pg_hint_plan.out
expected/ut-S.out
expected/ut-W.out
make_join_rel.c
pg_hint_plan.c
sql/ut-W.sql

diff --git a/core.c b/core.c
index d6b2b79..91fbe62 100644 (file)
--- a/core.c
+++ b/core.c
@@ -3,15 +3,37 @@
  * core.c
  *       Routines copied from PostgreSQL core distribution.
  *
+
+ * The main purpose of this files is having access to static functions in core.
+ * Another purpose is tweaking functions behavior by replacing part of them by
+ * macro definitions. See at the end of pg_hint_plan.c for details. Anyway,
+ * this file *must* contain required functions without making any change.
+ *
+ * This file contains the following functions from corresponding files.
+ *
  * src/backend/optimizer/path/allpaths.c
+ *
+ *     static functions:
+ *        set_plain_rel_pathlist()
  *     set_append_rel_pathlist()
  *     generate_mergeappend_paths()
  *     get_cheapest_parameterized_child_path()
  *     accumulate_append_subpath()
- *     standard_join_search()
+ *
+ *  public functions:
+ *     standard_join_search(): This funcion is not static. The reason for
+ *        including this function is make_rels_by_clause_joins. In order to
+ *        avoid generating apparently unwanted join combination, we decided to
+ *        change the behavior of make_join_rel, which is called under this
+ *        function.
  *
  * src/backend/optimizer/path/joinrels.c
- *     join_search_one_level()
+ *
+ *     public functions:
+ *     join_search_one_level(): We have to modify this to call my definition of
+ *                 make_rels_by_clause_joins.
+ *
+ *     static functions:
  *     make_rels_by_clause_joins()
  *     make_rels_by_clauseless_joins()
  *     join_is_legal()
  *     mark_dummy_rel()
  *     restriction_is_constant_false()
  *
+ *
  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *-------------------------------------------------------------------------
  */
 
+
+/*
+ * set_plain_rel_pathlist
+ *       Build access paths for a plain relation (no subquery, no inheritance)
+ */
+static void
+set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
+{
+       Relids          required_outer;
+
+       /*
+        * We don't support pushing join clauses into the quals of a seqscan, but
+        * it could still have required parameterization due to LATERAL refs in
+        * its tlist.
+        */
+       required_outer = rel->lateral_relids;
+
+       /* Consider sequential scan */
+       add_path(rel, create_seqscan_path(root, rel, required_outer, 0));
+
+       /* If appropriate, consider parallel sequential scan */
+       if (rel->consider_parallel && required_outer == NULL)
+               create_plain_partial_paths(root, rel);
+
+       /* Consider index scans */
+       create_index_paths(root, rel);
+
+       /* Consider TID scans */
+       create_tidscan_paths(root, rel);
+}
+
 /*
  * set_append_rel_pathlist
  *       Build access paths for an "append relation"
index c66f6e7..fa3e4e8 100644 (file)
@@ -3441,34 +3441,42 @@ not used hint:
 duplication hint:
 error hint:
 
-                                    QUERY PLAN                                     
------------------------------------------------------------------------------------
+                       QUERY PLAN                       
+--------------------------------------------------------
  Merge Join
    Merge Cond: (p1.id = t1.id)
-   ->  Sort
+   ->  Merge Append
          Sort Key: p1.id
-         ->  Append
-               ->  Seq Scan on p1
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
-               ->  Seq Scan on p1_c1
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
-               ->  Seq Scan on p1_c2
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
-               ->  Seq Scan on p1_c3
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
-               ->  Seq Scan on p1_c4
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
-               ->  Seq Scan on p1_c1_c1
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
-               ->  Seq Scan on p1_c1_c2
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
-               ->  Seq Scan on p1_c3_c1
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
-               ->  Seq Scan on p1_c3_c2
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
+         ->  Index Scan using p1_pkey on p1
+               Index Cond: ((id >= 50) AND (id <= 51))
+               Filter: (ctid = '(1,1)'::tid)
+         ->  Index Scan using p1_c1_pkey on p1_c1
+               Index Cond: ((id >= 50) AND (id <= 51))
+               Filter: (ctid = '(1,1)'::tid)
+         ->  Index Scan using p1_c2_pkey on p1_c2
+               Index Cond: ((id >= 50) AND (id <= 51))
+               Filter: (ctid = '(1,1)'::tid)
+         ->  Index Scan using p1_c3_pkey on p1_c3
+               Index Cond: ((id >= 50) AND (id <= 51))
+               Filter: (ctid = '(1,1)'::tid)
+         ->  Index Scan using p1_c4_pkey on p1_c4
+               Index Cond: ((id >= 50) AND (id <= 51))
+               Filter: (ctid = '(1,1)'::tid)
+         ->  Index Scan using p1_c1_c1_pkey on p1_c1_c1
+               Index Cond: ((id >= 50) AND (id <= 51))
+               Filter: (ctid = '(1,1)'::tid)
+         ->  Index Scan using p1_c1_c2_pkey on p1_c1_c2
+               Index Cond: ((id >= 50) AND (id <= 51))
+               Filter: (ctid = '(1,1)'::tid)
+         ->  Index Scan using p1_c3_c1_pkey on p1_c3_c1
+               Index Cond: ((id >= 50) AND (id <= 51))
+               Filter: (ctid = '(1,1)'::tid)
+         ->  Index Scan using p1_c3_c2_pkey on p1_c3_c2
+               Index Cond: ((id >= 50) AND (id <= 51))
+               Filter: (ctid = '(1,1)'::tid)
    ->  Index Scan using t1_pkey on t1
          Index Cond: (id < 10)
-(25 rows)
+(33 rows)
 
 /*+BitmapScan(p1)*/
 EXPLAIN (COSTS false) SELECT * FROM p1, t1 WHERE p1.id >= 50 AND p1.id <= 51 AND p1.ctid = '(1,1)' AND p1.id = t1.id AND t1.id < 10;
@@ -3479,34 +3487,61 @@ not used hint:
 duplication hint:
 error hint:
 
-                                    QUERY PLAN                                     
------------------------------------------------------------------------------------
+                            QUERY PLAN                             
+-------------------------------------------------------------------
  Merge Join
    Merge Cond: (p1.id = t1.id)
    ->  Sort
          Sort Key: p1.id
          ->  Append
-               ->  Seq Scan on p1
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
-               ->  Seq Scan on p1_c1
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
-               ->  Seq Scan on p1_c2
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
-               ->  Seq Scan on p1_c3
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
-               ->  Seq Scan on p1_c4
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
-               ->  Seq Scan on p1_c1_c1
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
-               ->  Seq Scan on p1_c1_c2
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
-               ->  Seq Scan on p1_c3_c1
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
-               ->  Seq Scan on p1_c3_c2
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
+               ->  Bitmap Heap Scan on p1
+                     Recheck Cond: ((id >= 50) AND (id <= 51))
+                     Filter: (ctid = '(1,1)'::tid)
+                     ->  Bitmap Index Scan on p1_pkey
+                           Index Cond: ((id >= 50) AND (id <= 51))
+               ->  Bitmap Heap Scan on p1_c1
+                     Recheck Cond: ((id >= 50) AND (id <= 51))
+                     Filter: (ctid = '(1,1)'::tid)
+                     ->  Bitmap Index Scan on p1_c1_pkey
+                           Index Cond: ((id >= 50) AND (id <= 51))
+               ->  Bitmap Heap Scan on p1_c2
+                     Recheck Cond: ((id >= 50) AND (id <= 51))
+                     Filter: (ctid = '(1,1)'::tid)
+                     ->  Bitmap Index Scan on p1_c2_pkey
+                           Index Cond: ((id >= 50) AND (id <= 51))
+               ->  Bitmap Heap Scan on p1_c3
+                     Recheck Cond: ((id >= 50) AND (id <= 51))
+                     Filter: (ctid = '(1,1)'::tid)
+                     ->  Bitmap Index Scan on p1_c3_pkey
+                           Index Cond: ((id >= 50) AND (id <= 51))
+               ->  Bitmap Heap Scan on p1_c4
+                     Recheck Cond: ((id >= 50) AND (id <= 51))
+                     Filter: (ctid = '(1,1)'::tid)
+                     ->  Bitmap Index Scan on p1_c4_pkey
+                           Index Cond: ((id >= 50) AND (id <= 51))
+               ->  Bitmap Heap Scan on p1_c1_c1
+                     Recheck Cond: ((id >= 50) AND (id <= 51))
+                     Filter: (ctid = '(1,1)'::tid)
+                     ->  Bitmap Index Scan on p1_c1_c1_pkey
+                           Index Cond: ((id >= 50) AND (id <= 51))
+               ->  Bitmap Heap Scan on p1_c1_c2
+                     Recheck Cond: ((id >= 50) AND (id <= 51))
+                     Filter: (ctid = '(1,1)'::tid)
+                     ->  Bitmap Index Scan on p1_c1_c2_pkey
+                           Index Cond: ((id >= 50) AND (id <= 51))
+               ->  Bitmap Heap Scan on p1_c3_c1
+                     Recheck Cond: ((id >= 50) AND (id <= 51))
+                     Filter: (ctid = '(1,1)'::tid)
+                     ->  Bitmap Index Scan on p1_c3_c1_pkey
+                           Index Cond: ((id >= 50) AND (id <= 51))
+               ->  Bitmap Heap Scan on p1_c3_c2
+                     Recheck Cond: ((id >= 50) AND (id <= 51))
+                     Filter: (ctid = '(1,1)'::tid)
+                     ->  Bitmap Index Scan on p1_c3_c2_pkey
+                           Index Cond: ((id >= 50) AND (id <= 51))
    ->  Index Scan using t1_pkey on t1
          Index Cond: (id < 10)
-(25 rows)
+(52 rows)
 
 /*+TidScan(p1)*/
 EXPLAIN (COSTS false) SELECT * FROM p1, t1 WHERE p1.id >= 50 AND p1.id <= 51 AND p1.ctid = '(1,1)' AND p1.id = t1.id AND t1.id < 10;
@@ -3517,34 +3552,43 @@ not used hint:
 duplication hint:
 error hint:
 
-                                    QUERY PLAN                                     
------------------------------------------------------------------------------------
+                       QUERY PLAN                        
+---------------------------------------------------------
  Merge Join
    Merge Cond: (p1.id = t1.id)
    ->  Sort
          Sort Key: p1.id
          ->  Append
-               ->  Seq Scan on p1
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
-               ->  Seq Scan on p1_c1
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
-               ->  Seq Scan on p1_c2
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
-               ->  Seq Scan on p1_c3
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
-               ->  Seq Scan on p1_c4
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
-               ->  Seq Scan on p1_c1_c1
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
-               ->  Seq Scan on p1_c1_c2
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
-               ->  Seq Scan on p1_c3_c1
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
-               ->  Seq Scan on p1_c3_c2
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
+               ->  Tid Scan on p1
+                     TID Cond: (ctid = '(1,1)'::tid)
+                     Filter: ((id >= 50) AND (id <= 51))
+               ->  Tid Scan on p1_c1
+                     TID Cond: (ctid = '(1,1)'::tid)
+                     Filter: ((id >= 50) AND (id <= 51))
+               ->  Tid Scan on p1_c2
+                     TID Cond: (ctid = '(1,1)'::tid)
+                     Filter: ((id >= 50) AND (id <= 51))
+               ->  Tid Scan on p1_c3
+                     TID Cond: (ctid = '(1,1)'::tid)
+                     Filter: ((id >= 50) AND (id <= 51))
+               ->  Tid Scan on p1_c4
+                     TID Cond: (ctid = '(1,1)'::tid)
+                     Filter: ((id >= 50) AND (id <= 51))
+               ->  Tid Scan on p1_c1_c1
+                     TID Cond: (ctid = '(1,1)'::tid)
+                     Filter: ((id >= 50) AND (id <= 51))
+               ->  Tid Scan on p1_c1_c2
+                     TID Cond: (ctid = '(1,1)'::tid)
+                     Filter: ((id >= 50) AND (id <= 51))
+               ->  Tid Scan on p1_c3_c1
+                     TID Cond: (ctid = '(1,1)'::tid)
+                     Filter: ((id >= 50) AND (id <= 51))
+               ->  Tid Scan on p1_c3_c2
+                     TID Cond: (ctid = '(1,1)'::tid)
+                     Filter: ((id >= 50) AND (id <= 51))
    ->  Index Scan using t1_pkey on t1
          Index Cond: (id < 10)
-(25 rows)
+(34 rows)
 
 /*+NestLoop(p1 t1)*/
 EXPLAIN (COSTS false) SELECT * FROM p1, t1 WHERE p1.id >= 50 AND p1.id <= 51 AND p1.ctid = '(1,1)' AND p1.id = t1.id AND t1.id < 10;
@@ -3696,24 +3740,27 @@ not used hint:
 duplication hint:
 error hint:
 
-                                    QUERY PLAN                                     
------------------------------------------------------------------------------------
+                       QUERY PLAN                       
+--------------------------------------------------------
  Merge Join
    Merge Cond: (p1.id = t1.id)
-   ->  Sort
+   ->  Merge Append
          Sort Key: p1.id
-         ->  Append
-               ->  Seq Scan on p1
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
-               ->  Seq Scan on p1_c1
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
-               ->  Seq Scan on p1_c1_c1
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
-               ->  Seq Scan on p1_c1_c2
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
+         ->  Index Scan using p1_pkey on p1
+               Index Cond: ((id >= 50) AND (id <= 51))
+               Filter: (ctid = '(1,1)'::tid)
+         ->  Index Scan using p1_c1_pkey on p1_c1
+               Index Cond: ((id >= 50) AND (id <= 51))
+               Filter: (ctid = '(1,1)'::tid)
+         ->  Index Scan using p1_c1_c1_pkey on p1_c1_c1
+               Index Cond: ((id >= 50) AND (id <= 51))
+               Filter: (ctid = '(1,1)'::tid)
+         ->  Index Scan using p1_c1_c2_pkey on p1_c1_c2
+               Index Cond: ((id >= 50) AND (id <= 51))
+               Filter: (ctid = '(1,1)'::tid)
    ->  Index Scan using t1_pkey on t1
          Index Cond: (id < 10)
-(15 rows)
+(18 rows)
 
 /*+BitmapScan(p1)*/
 EXPLAIN (COSTS false) SELECT * FROM p1, t1 WHERE p1.id >= 50 AND p1.id <= 51 AND p1.ctid = '(1,1)' AND p1.id = t1.id AND t1.id < 10;
@@ -3724,24 +3771,36 @@ not used hint:
 duplication hint:
 error hint:
 
-                                    QUERY PLAN                                     
------------------------------------------------------------------------------------
+                            QUERY PLAN                             
+-------------------------------------------------------------------
  Merge Join
    Merge Cond: (p1.id = t1.id)
    ->  Sort
          Sort Key: p1.id
          ->  Append
-               ->  Seq Scan on p1
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
-               ->  Seq Scan on p1_c1
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
-               ->  Seq Scan on p1_c1_c1
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
-               ->  Seq Scan on p1_c1_c2
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
+               ->  Bitmap Heap Scan on p1
+                     Recheck Cond: ((id >= 50) AND (id <= 51))
+                     Filter: (ctid = '(1,1)'::tid)
+                     ->  Bitmap Index Scan on p1_pkey
+                           Index Cond: ((id >= 50) AND (id <= 51))
+               ->  Bitmap Heap Scan on p1_c1
+                     Recheck Cond: ((id >= 50) AND (id <= 51))
+                     Filter: (ctid = '(1,1)'::tid)
+                     ->  Bitmap Index Scan on p1_c1_pkey
+                           Index Cond: ((id >= 50) AND (id <= 51))
+               ->  Bitmap Heap Scan on p1_c1_c1
+                     Recheck Cond: ((id >= 50) AND (id <= 51))
+                     Filter: (ctid = '(1,1)'::tid)
+                     ->  Bitmap Index Scan on p1_c1_c1_pkey
+                           Index Cond: ((id >= 50) AND (id <= 51))
+               ->  Bitmap Heap Scan on p1_c1_c2
+                     Recheck Cond: ((id >= 50) AND (id <= 51))
+                     Filter: (ctid = '(1,1)'::tid)
+                     ->  Bitmap Index Scan on p1_c1_c2_pkey
+                           Index Cond: ((id >= 50) AND (id <= 51))
    ->  Index Scan using t1_pkey on t1
          Index Cond: (id < 10)
-(15 rows)
+(27 rows)
 
 /*+TidScan(p1)*/
 EXPLAIN (COSTS false) SELECT * FROM p1, t1 WHERE p1.id >= 50 AND p1.id <= 51 AND p1.ctid = '(1,1)' AND p1.id = t1.id AND t1.id < 10;
@@ -3752,24 +3811,28 @@ not used hint:
 duplication hint:
 error hint:
 
-                                    QUERY PLAN                                     
------------------------------------------------------------------------------------
+                       QUERY PLAN                        
+---------------------------------------------------------
  Merge Join
    Merge Cond: (p1.id = t1.id)
    ->  Sort
          Sort Key: p1.id
          ->  Append
-               ->  Seq Scan on p1
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
-               ->  Seq Scan on p1_c1
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
-               ->  Seq Scan on p1_c1_c1
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
-               ->  Seq Scan on p1_c1_c2
-                     Filter: ((id >= 50) AND (id <= 51) AND (ctid = '(1,1)'::tid))
+               ->  Tid Scan on p1
+                     TID Cond: (ctid = '(1,1)'::tid)
+                     Filter: ((id >= 50) AND (id <= 51))
+               ->  Tid Scan on p1_c1
+                     TID Cond: (ctid = '(1,1)'::tid)
+                     Filter: ((id >= 50) AND (id <= 51))
+               ->  Tid Scan on p1_c1_c1
+                     TID Cond: (ctid = '(1,1)'::tid)
+                     Filter: ((id >= 50) AND (id <= 51))
+               ->  Tid Scan on p1_c1_c2
+                     TID Cond: (ctid = '(1,1)'::tid)
+                     Filter: ((id >= 50) AND (id <= 51))
    ->  Index Scan using t1_pkey on t1
          Index Cond: (id < 10)
-(15 rows)
+(19 rows)
 
 /*+NestLoop(p1 t1)*/
 EXPLAIN (COSTS false) SELECT * FROM p1, t1 WHERE p1.id >= 50 AND p1.id <= 51 AND p1.ctid = '(1,1)' AND p1.id = t1.id AND t1.id < 10;
@@ -6428,13 +6491,8 @@ EXPLAIN (COSTS false) SELECT * FROM p2 WHERE id >= 50 AND id <= 51 AND p2.ctid =
 EXPLAIN (COSTS false) SELECT * FROM p2 WHERE id >= 50 AND id <= 51 AND p2.ctid = '(1,1)';
 LOG:  available indexes for IndexScan(p2): p2_pkey
 LOG:  available indexes for IndexScan(p2_c1): p2_c1_pkey
-LOG:  available indexes for IndexScan(p2_c2): p2_c2_pkey
-LOG:  available indexes for IndexScan(p2_c3): p2_c3_pkey
-LOG:  available indexes for IndexScan(p2_c4): p2_c4_pkey
 LOG:  available indexes for IndexScan(p2_c1_c1): p2_c1_c1_pkey
 LOG:  available indexes for IndexScan(p2_c1_c2): p2_c1_c2_pkey
-LOG:  available indexes for IndexScan(p2_c3_c1): p2_c3_c1_pkey
-LOG:  available indexes for IndexScan(p2_c3_c2): p2_c3_c2_pkey
 LOG:  pg_hint_plan:
 used hint:
 IndexScan(p2 p2_pkey)
@@ -6463,13 +6521,8 @@ error hint:
 EXPLAIN (COSTS false) SELECT * FROM p2 WHERE id >= 50 AND id <= 51 AND p2.ctid = '(1,1)';
 LOG:  available indexes for IndexScan(p2): p2_id_val_idx
 LOG:  available indexes for IndexScan(p2_c1): p2_c1_id_val_idx
-LOG:  available indexes for IndexScan(p2_c2): p2_c2_id_val_idx
-LOG:  available indexes for IndexScan(p2_c3): p2_c3_id_val_idx
-LOG:  available indexes for IndexScan(p2_c4): p2_c4_id_val_idx
 LOG:  available indexes for IndexScan(p2_c1_c1): p2_c1_c1_id_val_idx
 LOG:  available indexes for IndexScan(p2_c1_c2): p2_c1_c2_id_val_idx
-LOG:  available indexes for IndexScan(p2_c3_c1): p2_c3_c1_id_val_idx
-LOG:  available indexes for IndexScan(p2_c3_c2): p2_c3_c2_id_val_idx
 LOG:  pg_hint_plan:
 used hint:
 IndexScan(p2 p2_id_val_idx)
@@ -6498,13 +6551,8 @@ error hint:
 EXPLAIN (COSTS false) SELECT * FROM p2 WHERE id >= 50 AND id <= 51 AND p2.ctid = '(1,1)';
 LOG:  available indexes for IndexScan(p2): p2_val_id_idx
 LOG:  available indexes for IndexScan(p2_c1): p2_c1_val_id_idx
-LOG:  available indexes for IndexScan(p2_c2): p2_c2_val_id_idx
-LOG:  available indexes for IndexScan(p2_c3): p2_c3_val_id_idx
-LOG:  available indexes for IndexScan(p2_c4):
 LOG:  available indexes for IndexScan(p2_c1_c1): p2_c1_c1_val_id_idx
 LOG:  available indexes for IndexScan(p2_c1_c2): p2_c1_c2_val_id_idx
-LOG:  available indexes for IndexScan(p2_c3_c1): p2_c3_c1_val_id_idx
-LOG:  available indexes for IndexScan(p2_c3_c2): p2_c3_c2_val_id_idx
 LOG:  pg_hint_plan:
 used hint:
 IndexScan(p2 p2_val_id_idx)
@@ -6598,13 +6646,8 @@ error hint:
 EXPLAIN (COSTS false) SELECT * FROM p2 WHERE id >= 50 AND id <= 51 AND p2.ctid = '(1,1)';
 LOG:  available indexes for IndexScan(p2): p2_pkey
 LOG:  available indexes for IndexScan(p2_c1): p2_c1_pkey
-LOG:  available indexes for IndexScan(p2_c2): p2_c2_pkey
-LOG:  available indexes for IndexScan(p2_c3): p2_c3_pkey
-LOG:  available indexes for IndexScan(p2_c4): p2_c4_pkey
 LOG:  available indexes for IndexScan(p2_c1_c1): p2_c1_c1_pkey
 LOG:  available indexes for IndexScan(p2_c1_c2): p2_c1_c2_pkey
-LOG:  available indexes for IndexScan(p2_c3_c1): p2_c3_c1_pkey
-LOG:  available indexes for IndexScan(p2_c3_c2): p2_c3_c2_pkey
 LOG:  pg_hint_plan:
 used hint:
 IndexScan(p2 p2_pkey)
@@ -6633,13 +6676,8 @@ error hint:
 EXPLAIN (COSTS false) SELECT * FROM p2 WHERE id >= 50 AND id <= 51 AND p2.ctid = '(1,1)';
 LOG:  available indexes for IndexScan(p2): p2_id2_val
 LOG:  available indexes for IndexScan(p2_c1): p2_c1_id2_val
-LOG:  available indexes for IndexScan(p2_c2): p2_c2_id2_val
-LOG:  available indexes for IndexScan(p2_c3):
-LOG:  available indexes for IndexScan(p2_c4):
 LOG:  available indexes for IndexScan(p2_c1_c1):
 LOG:  available indexes for IndexScan(p2_c1_c2):
-LOG:  available indexes for IndexScan(p2_c3_c1):
-LOG:  available indexes for IndexScan(p2_c3_c2):
 LOG:  pg_hint_plan:
 used hint:
 IndexScan(p2 p2_id2_val)
@@ -6666,13 +6704,8 @@ error hint:
 EXPLAIN (COSTS false) SELECT * FROM p2 WHERE id >= 50 AND id <= 51 AND p2.ctid = '(1,1)';
 LOG:  available indexes for IndexScan(p2): p2_val2_id
 LOG:  available indexes for IndexScan(p2_c1):
-LOG:  available indexes for IndexScan(p2_c2):
-LOG:  available indexes for IndexScan(p2_c3):
-LOG:  available indexes for IndexScan(p2_c4):
 LOG:  available indexes for IndexScan(p2_c1_c1):
 LOG:  available indexes for IndexScan(p2_c1_c2):
-LOG:  available indexes for IndexScan(p2_c3_c1):
-LOG:  available indexes for IndexScan(p2_c3_c2):
 LOG:  pg_hint_plan:
 used hint:
 IndexScan(p2 p2_val2_id)
@@ -6698,13 +6731,8 @@ error hint:
 EXPLAIN (COSTS false) SELECT * FROM p2 WHERE id >= 50 AND id <= 51 AND p2.ctid = '(1,1)';
 LOG:  available indexes for IndexScan(p2): p2_pkey
 LOG:  available indexes for IndexScan(p2_c1): p2_c1_pkey
-LOG:  available indexes for IndexScan(p2_c2): p2_c2_pkey
-LOG:  available indexes for IndexScan(p2_c3): p2_c3_pkey
-LOG:  available indexes for IndexScan(p2_c4): p2_c4_pkey
 LOG:  available indexes for IndexScan(p2_c1_c1): p2_c1_c1_pkey
 LOG:  available indexes for IndexScan(p2_c1_c2): p2_c1_c2_pkey
-LOG:  available indexes for IndexScan(p2_c3_c1): p2_c3_c1_pkey
-LOG:  available indexes for IndexScan(p2_c3_c2): p2_c3_c2_pkey
 LOG:  pg_hint_plan:
 used hint:
 IndexScan(p2 p2_pkey)
@@ -6733,13 +6761,8 @@ error hint:
 EXPLAIN (COSTS false) SELECT * FROM p2 WHERE id >= 50 AND id <= 51 AND p2.ctid = '(1,1)';
 LOG:  available indexes for IndexScan(p2):
 LOG:  available indexes for IndexScan(p2_c1): p2_c1_id_val_idx
-LOG:  available indexes for IndexScan(p2_c2):
-LOG:  available indexes for IndexScan(p2_c3):
-LOG:  available indexes for IndexScan(p2_c4):
 LOG:  available indexes for IndexScan(p2_c1_c1):
 LOG:  available indexes for IndexScan(p2_c1_c2):
-LOG:  available indexes for IndexScan(p2_c3_c1):
-LOG:  available indexes for IndexScan(p2_c3_c2):
 LOG:  pg_hint_plan:
 used hint:
 IndexScan(p2 p2_c1_id_val_idx)
@@ -6765,13 +6788,8 @@ error hint:
 EXPLAIN (COSTS false) SELECT * FROM p2 WHERE id >= 50 AND id <= 51 AND p2.ctid = '(1,1)';
 LOG:  available indexes for IndexScan(p2):
 LOG:  available indexes for IndexScan(p2_c1):
-LOG:  available indexes for IndexScan(p2_c2):
-LOG:  available indexes for IndexScan(p2_c3):
-LOG:  available indexes for IndexScan(p2_c4):
 LOG:  available indexes for IndexScan(p2_c1_c1):
 LOG:  available indexes for IndexScan(p2_c1_c2):
-LOG:  available indexes for IndexScan(p2_c3_c1):
-LOG:  available indexes for IndexScan(p2_c3_c2):
 LOG:  pg_hint_plan:
 used hint:
 IndexScan(p2 no_exist)
@@ -6796,13 +6814,8 @@ error hint:
 EXPLAIN (COSTS false) SELECT * FROM p2 WHERE id >= 50 AND id <= 51 AND p2.ctid = '(1,1)';
 LOG:  available indexes for IndexScan(p2): p2_pkey
 LOG:  available indexes for IndexScan(p2_c1): p2_c1_id_val_idx p2_c1_pkey
-LOG:  available indexes for IndexScan(p2_c2): p2_c2_pkey
-LOG:  available indexes for IndexScan(p2_c3): p2_c3_pkey
-LOG:  available indexes for IndexScan(p2_c4): p2_c4_pkey
 LOG:  available indexes for IndexScan(p2_c1_c1): p2_c1_c1_pkey
 LOG:  available indexes for IndexScan(p2_c1_c2): p2_c1_c2_pkey
-LOG:  available indexes for IndexScan(p2_c3_c1): p2_c3_c1_pkey
-LOG:  available indexes for IndexScan(p2_c3_c2): p2_c3_c2_pkey
 LOG:  pg_hint_plan:
 used hint:
 IndexScan(p2 p2_pkey p2_c1_id_val_idx)
@@ -6831,13 +6844,8 @@ error hint:
 EXPLAIN (COSTS false) SELECT * FROM p2 WHERE id >= 50 AND id <= 51 AND p2.ctid = '(1,1)';
 LOG:  available indexes for IndexScan(p2): p2_pkey
 LOG:  available indexes for IndexScan(p2_c1): p2_c1_pkey
-LOG:  available indexes for IndexScan(p2_c2): p2_c2_pkey
-LOG:  available indexes for IndexScan(p2_c3): p2_c3_pkey
-LOG:  available indexes for IndexScan(p2_c4): p2_c4_pkey
 LOG:  available indexes for IndexScan(p2_c1_c1): p2_c1_c1_pkey
 LOG:  available indexes for IndexScan(p2_c1_c2): p2_c1_c2_pkey
-LOG:  available indexes for IndexScan(p2_c3_c1): p2_c3_c1_pkey
-LOG:  available indexes for IndexScan(p2_c3_c2): p2_c3_c2_pkey
 LOG:  pg_hint_plan:
 used hint:
 IndexScan(p2 p2_pkey no_exist)
@@ -6866,13 +6874,8 @@ error hint:
 EXPLAIN (COSTS false) SELECT * FROM p2 WHERE id >= 50 AND id <= 51 AND p2.ctid = '(1,1)';
 LOG:  available indexes for IndexScan(p2):
 LOG:  available indexes for IndexScan(p2_c1): p2_c1_id_val_idx
-LOG:  available indexes for IndexScan(p2_c2):
-LOG:  available indexes for IndexScan(p2_c3):
-LOG:  available indexes for IndexScan(p2_c4):
 LOG:  available indexes for IndexScan(p2_c1_c1):
 LOG:  available indexes for IndexScan(p2_c1_c2):
-LOG:  available indexes for IndexScan(p2_c3_c1):
-LOG:  available indexes for IndexScan(p2_c3_c2):
 LOG:  pg_hint_plan:
 used hint:
 IndexScan(p2 p2_c1_id_val_idx no_exist)
@@ -6898,13 +6901,8 @@ error hint:
 EXPLAIN (COSTS false) SELECT * FROM p2 WHERE id >= 50 AND id <= 51 AND p2.ctid = '(1,1)';
 LOG:  available indexes for IndexScan(p2): p2_pkey
 LOG:  available indexes for IndexScan(p2_c1): p2_c1_id_val_idx p2_c1_pkey
-LOG:  available indexes for IndexScan(p2_c2): p2_c2_pkey
-LOG:  available indexes for IndexScan(p2_c3): p2_c3_pkey
-LOG:  available indexes for IndexScan(p2_c4): p2_c4_pkey
 LOG:  available indexes for IndexScan(p2_c1_c1): p2_c1_c1_pkey
 LOG:  available indexes for IndexScan(p2_c1_c2): p2_c1_c2_pkey
-LOG:  available indexes for IndexScan(p2_c3_c1): p2_c3_c1_pkey
-LOG:  available indexes for IndexScan(p2_c3_c2): p2_c3_c2_pkey
 LOG:  pg_hint_plan:
 used hint:
 IndexScan(p2 p2_pkey p2_c1_id_val_idx no_exist)
index 18fb63b..06fc573 100644 (file)
@@ -5103,8 +5103,6 @@ error hint:
 EXPLAIN (COSTS false) UPDATE s1.p1 SET c4 = c4 WHERE c1 = 1;
 LOG:  available indexes for IndexScan(p1): p1_pkey
 LOG:  available indexes for IndexScan(p1c1): p1c1_pkey
-LOG:  available indexes for IndexScan(p1c2): p1c2_pkey
-LOG:  available indexes for IndexScan(p1c3): p1c3_pkey
 LOG:  pg_hint_plan:
 used hint:
 IndexScan(p1 p1_pkey)
@@ -5263,8 +5261,6 @@ EXPLAIN SELECT c4 FROM s1.p1 WHERE c2 * 2 < 100 AND c1 < 10;
 /*+IndexScan(p1 p1_parent)*/ EXPLAIN SELECT c4 FROM s1.p1 WHERE c2 * 2 < 100 AND c1 < 10;
 LOG:  available indexes for IndexScan(p1): p1_parent
 LOG:  available indexes for IndexScan(p1c1): p1c1_c4_expr_idx
-LOG:  available indexes for IndexScan(p1c2): p1c2_c4_expr_idx
-LOG:  available indexes for IndexScan(p1c3): p1c3_c4_expr_idx
 LOG:  pg_hint_plan:
 used hint:
 IndexScan(p1 p1_parent)
@@ -5317,14 +5313,7 @@ error hint:
 /*+IndexScan(p2 p2c1_pkey)*/ EXPLAIN (COSTS true) SELECT * FROM s1.p2 WHERE c1 = 1;
 LOG:  available indexes for IndexScan(p2):
 LOG:  available indexes for IndexScan(p2c1): p2c1_pkey
-LOG:  available indexes for IndexScan(p2c2):
-LOG:  available indexes for IndexScan(p2c3):
 LOG:  available indexes for IndexScan(p2c1c1):
-LOG:  available indexes for IndexScan(p2c1c2):
-LOG:  available indexes for IndexScan(p2c2c1):
-LOG:  available indexes for IndexScan(p2c2c2):
-LOG:  available indexes for IndexScan(p2c3c1):
-LOG:  available indexes for IndexScan(p2c3c2):
 LOG:  pg_hint_plan:
 used hint:
 IndexScan(p2 p2c1_pkey)
@@ -6056,8 +6045,6 @@ error hint:
 EXPLAIN (COSTS false) SELECT * FROM s1.p1 WHERE c1 = 1;
 LOG:  available indexes for IndexScanRegexp(p1): p1_pkey
 LOG:  available indexes for IndexScanRegexp(p1c1): p1c1_pkey
-LOG:  available indexes for IndexScanRegexp(p1c2): p1c2_pkey
-LOG:  available indexes for IndexScanRegexp(p1c3): p1c3_pkey
 LOG:  pg_hint_plan:
 used hint:
 IndexScanRegexp(p1 .*pkey)
@@ -6079,8 +6066,6 @@ error hint:
 EXPLAIN (COSTS false) SELECT * FROM s1.p1 WHERE c1 = 1;
 LOG:  available indexes for IndexScanRegexp(p1): p1_i2 p1_i
 LOG:  available indexes for IndexScanRegexp(p1c1): p1c1_i p1c1_c4_expr_idx
-LOG:  available indexes for IndexScanRegexp(p1c2): p1c2_i p1c2_c4_expr_idx
-LOG:  available indexes for IndexScanRegexp(p1c3): p1c3_i p1c3_c4_expr_idx
 LOG:  pg_hint_plan:
 used hint:
 IndexScanRegexp(p1 p1.*i)
@@ -6102,8 +6087,6 @@ error hint:
 EXPLAIN (COSTS false) SELECT * FROM s1.p1 WHERE c1 = 1;
 LOG:  available indexes for IndexScanRegexp(p1):
 LOG:  available indexes for IndexScanRegexp(p1c1):
-LOG:  available indexes for IndexScanRegexp(p1c2):
-LOG:  available indexes for IndexScanRegexp(p1c3):
 LOG:  pg_hint_plan:
 used hint:
 IndexScanRegexp(p1 no.*_exist)
index 8b47fb3..eb7de87 100644 (file)
@@ -3,22 +3,18 @@ ALTER SYSTEM SET session_preload_libraries TO 'pg_hint_plan';
 SET pg_hint_plan.enable_hint TO on;
 SET pg_hint_plan.debug_print TO on;
 SET client_min_messages TO LOG;
-SET search_path TO public;
-SET max_parallel_workers_per_gather TO 0;
-SET enable_indexscan to false;
-SET enable_bitmapscan to false;
-SET parallel_setup_cost to 0;
-SET parallel_tuple_cost to 0;
-SET min_parallel_relation_size to 0;
-SET max_parallel_workers_per_gather to 0;
 CREATE TABLE s1.tl (a int);
 INSERT INTO s1.tl (SELECT a FROM generate_series(0, 100000) a);
+-- Queries on ordinary tables
 EXPLAIN (COSTS false) SELECT * FROM s1.t1;
    QUERY PLAN   
 ----------------
  Seq Scan on t1
 (1 row)
 
+SET parallel_setup_cost to 0;
+SET parallel_tuple_cost to 0;
+SET min_parallel_relation_size to 0;
 /*+Parallel(t1 10)*/
 EXPLAIN (COSTS false) SELECT * FROM s1.t1;
 LOG:  pg_hint_plan:
@@ -51,6 +47,9 @@ error hint:
    ->  Parallel Seq Scan on t1
 (3 rows)
 
+SET parallel_setup_cost to DEFAULT;
+SET parallel_tuple_cost to DEFAULT;
+SET min_parallel_relation_size to DEFAULT;
 /*+Parallel(t1 10 hard)*/
 EXPLAIN (COSTS false) SELECT * FROM s1.t1;
 LOG:  pg_hint_plan:
@@ -67,8 +66,11 @@ error hint:
    ->  Parallel Seq Scan on t1
 (3 rows)
 
--- Inheritnce tables
-/*+Parallel(p1 10 soft)*/
+-- Queries on inheritance tables
+SET parallel_setup_cost to 0;
+SET parallel_tuple_cost to 0;
+SET min_parallel_relation_size to 0;
+/*+Parallel(p1 10)*/
 EXPLAIN (COSTS false) SELECT * FROM p1;
 LOG:  pg_hint_plan:
 used hint:
@@ -93,6 +95,9 @@ error hint:
          ->  Parallel Seq Scan on p1_c3_c2
 (12 rows)
 
+SET parallel_setup_cost to DEFAULT;
+SET parallel_tuple_cost to DEFAULT;
+SET min_parallel_relation_size to DEFAULT;
 /*+Parallel(p1 10 hard)*/
 EXPLAIN (COSTS false) SELECT * FROM p1;
 LOG:  pg_hint_plan:
@@ -118,6 +123,30 @@ error hint:
          ->  Parallel Seq Scan on p1_c3_c2
 (12 rows)
 
+-- hinting on children don't work
+/*+Parallel(p1_c1 10 hard)*/
+EXPLAIN (COSTS false) SELECT * FROM p1;
+LOG:  pg_hint_plan:
+used hint:
+not used hint:
+Parallel(p1_c1 10 hard)
+duplication hint:
+error hint:
+
+         QUERY PLAN         
+----------------------------
+ Append
+   ->  Seq Scan on p1
+   ->  Seq Scan on p1_c1
+   ->  Seq Scan on p1_c2
+   ->  Seq Scan on p1_c3
+   ->  Seq Scan on p1_c4
+   ->  Seq Scan on p1_c1_c1
+   ->  Seq Scan on p1_c1_c2
+   ->  Seq Scan on p1_c3_c1
+   ->  Seq Scan on p1_c3_c2
+(10 rows)
+
 -- Joins
 EXPLAIN (COSTS false) SELECT * FROM p1_c1 join p2_c1 on p1_c1.id = p2_c1.id;
                QUERY PLAN               
@@ -217,6 +246,9 @@ error hint:
 (15 rows)
 
 -- Joins on inheritance tables
+SET parallel_setup_cost to 0;
+SET parallel_tuple_cost to 0;
+SET min_parallel_relation_size to 0;
 /*+Parallel(p1 10)*/
 EXPLAIN (COSTS false) SELECT * FROM p1 join p2 on p1.id = p2.id;
 LOG:  pg_hint_plan:
@@ -255,6 +287,9 @@ error hint:
                      ->  Seq Scan on p2_c3_c2
 (25 rows)
 
+SET parallel_setup_cost to DEFAULT;
+SET parallel_tuple_cost to DEFAULT;
+SET min_parallel_relation_size to DEFAULT;
 /*+Parallel(p2 10 hard)*/
 EXPLAIN (COSTS false) SELECT * FROM p1 join p2 on p1.id = p2.id;
 LOG:  pg_hint_plan:
@@ -334,8 +369,265 @@ error hint:
                      ->  Parallel Seq Scan on p2_c3_c2
 (27 rows)
 
+-- Mixture with a scan hint
+-- p1 can be parallel
+/*+Parallel(p1 10 hard) IndexScan(p2) */
+EXPLAIN (COSTS false) SELECT * FROM p1 join p2 on p1.id = p2.id;
+LOG:  pg_hint_plan:
+used hint:
+IndexScan(p2)
+Parallel(p1 10 hard)
+not used hint:
+duplication hint:
+error hint:
+
+                          QUERY PLAN                          
+--------------------------------------------------------------
+ Hash Join
+   Hash Cond: (p2.id = p1.id)
+   ->  Append
+         ->  Index Scan using p2_id2_val on p2
+         ->  Index Scan using p2_c1_id2_val on p2_c1
+         ->  Index Scan using p2_c2_id2_val on p2_c2
+         ->  Index Scan using p2_c3_id_val_idx on p2_c3
+         ->  Index Scan using p2_c4_id_val_idx on p2_c4
+         ->  Index Scan using p2_c1_c1_id_val_idx on p2_c1_c1
+         ->  Index Scan using p2_c1_c2_id_val_idx on p2_c1_c2
+         ->  Index Scan using p2_c3_c1_id_val_idx on p2_c3_c1
+         ->  Index Scan using p2_c3_c2_id_val_idx on p2_c3_c2
+   ->  Hash
+         ->  Gather
+               Workers Planned: 10
+               ->  Append
+                     ->  Parallel Seq Scan on p1
+                     ->  Parallel Seq Scan on p1_c1
+                     ->  Parallel Seq Scan on p1_c2
+                     ->  Parallel Seq Scan on p1_c3
+                     ->  Parallel Seq Scan on p1_c4
+                     ->  Parallel Seq Scan on p1_c1_c1
+                     ->  Parallel Seq Scan on p1_c1_c2
+                     ->  Parallel Seq Scan on p1_c3_c1
+                     ->  Parallel Seq Scan on p1_c3_c2
+(25 rows)
+
+-- seqscan doesn't harm parallelism
+/*+Parallel(p1 10 hard) SeqScan(p1) */
+EXPLAIN (COSTS false) SELECT * FROM p1 join p2 on p1.id = p2.id;
+LOG:  pg_hint_plan:
+used hint:
+SeqScan(p1)
+Parallel(p1 10 hard)
+not used hint:
+duplication hint:
+error hint:
+
+                   QUERY PLAN                    
+-------------------------------------------------
+ Gather
+   Workers Planned: 10
+   ->  Hash Join
+         Hash Cond: (p1.id = p2.id)
+         ->  Append
+               ->  Parallel Seq Scan on p1
+               ->  Parallel Seq Scan on p1_c1
+               ->  Parallel Seq Scan on p1_c2
+               ->  Parallel Seq Scan on p1_c3
+               ->  Parallel Seq Scan on p1_c4
+               ->  Parallel Seq Scan on p1_c1_c1
+               ->  Parallel Seq Scan on p1_c1_c2
+               ->  Parallel Seq Scan on p1_c3_c1
+               ->  Parallel Seq Scan on p1_c3_c2
+         ->  Hash
+               ->  Append
+                     ->  Seq Scan on p2
+                     ->  Seq Scan on p2_c1
+                     ->  Seq Scan on p2_c2
+                     ->  Seq Scan on p2_c3
+                     ->  Seq Scan on p2_c4
+                     ->  Seq Scan on p2_c1_c1
+                     ->  Seq Scan on p2_c1_c2
+                     ->  Seq Scan on p2_c3_c1
+                     ->  Seq Scan on p2_c3_c2
+(25 rows)
+
+-- parallelism is not available for the case
+/*+Parallel(p1 10 hard) IndexScan(p1) */
+EXPLAIN (COSTS false) SELECT * FROM p1 join p2 on p1.id = p2.id;
+LOG:  pg_hint_plan:
+used hint:
+IndexScan(p1)
+Parallel(p1 10 hard)
+not used hint:
+duplication hint:
+error hint:
+
+                       QUERY PLAN                       
+--------------------------------------------------------
+ Hash Join
+   Hash Cond: (p1.id = p2.id)
+   ->  Append
+         ->  Index Scan using p1_pkey on p1
+         ->  Index Scan using p1_c1_pkey on p1_c1
+         ->  Index Scan using p1_c2_pkey on p1_c2
+         ->  Index Scan using p1_c3_pkey on p1_c3
+         ->  Index Scan using p1_c4_pkey on p1_c4
+         ->  Index Scan using p1_c1_c1_pkey on p1_c1_c1
+         ->  Index Scan using p1_c1_c2_pkey on p1_c1_c2
+         ->  Index Scan using p1_c3_c1_pkey on p1_c3_c1
+         ->  Index Scan using p1_c3_c2_pkey on p1_c3_c2
+   ->  Hash
+         ->  Append
+               ->  Seq Scan on p2
+               ->  Seq Scan on p2_c1
+               ->  Seq Scan on p2_c2
+               ->  Seq Scan on p2_c3
+               ->  Seq Scan on p2_c4
+               ->  Seq Scan on p2_c1_c1
+               ->  Seq Scan on p2_c1_c2
+               ->  Seq Scan on p2_c3_c1
+               ->  Seq Scan on p2_c3_c2
+(23 rows)
+
+-- Parallel on UNION
+EXPLAIN (COSTS false) SELECT id FROM p1 UNION ALL SELECT id FROM p2;
+         QUERY PLAN         
+----------------------------
+ Append
+   ->  Seq Scan on p1
+   ->  Seq Scan on p1_c1
+   ->  Seq Scan on p1_c2
+   ->  Seq Scan on p1_c3
+   ->  Seq Scan on p1_c4
+   ->  Seq Scan on p1_c1_c1
+   ->  Seq Scan on p1_c1_c2
+   ->  Seq Scan on p1_c3_c1
+   ->  Seq Scan on p1_c3_c2
+   ->  Seq Scan on p2
+   ->  Seq Scan on p2_c1
+   ->  Seq Scan on p2_c2
+   ->  Seq Scan on p2_c3
+   ->  Seq Scan on p2_c4
+   ->  Seq Scan on p2_c1_c1
+   ->  Seq Scan on p2_c1_c2
+   ->  Seq Scan on p2_c3_c1
+   ->  Seq Scan on p2_c3_c2
+(19 rows)
+
+-- some of the scans are not parallel, so this cannot be parallel
+SET parallel_setup_cost to 0;
+SET parallel_tuple_cost to 0;
+SET min_parallel_relation_size to 0;
+/*+Parallel(p1 10) */
+EXPLAIN (COSTS false) SELECT id FROM p1 UNION ALL SELECT id FROM p2;
+LOG:  pg_hint_plan:
+used hint:
+Parallel(p1 10 soft)
+not used hint:
+duplication hint:
+error hint:
+
+         QUERY PLAN         
+----------------------------
+ Append
+   ->  Seq Scan on p1
+   ->  Seq Scan on p1_c1
+   ->  Seq Scan on p1_c2
+   ->  Seq Scan on p1_c3
+   ->  Seq Scan on p1_c4
+   ->  Seq Scan on p1_c1_c1
+   ->  Seq Scan on p1_c1_c2
+   ->  Seq Scan on p1_c3_c1
+   ->  Seq Scan on p1_c3_c2
+   ->  Seq Scan on p2
+   ->  Seq Scan on p2_c1
+   ->  Seq Scan on p2_c2
+   ->  Seq Scan on p2_c3
+   ->  Seq Scan on p2_c4
+   ->  Seq Scan on p2_c1_c1
+   ->  Seq Scan on p2_c1_c2
+   ->  Seq Scan on p2_c3_c1
+   ->  Seq Scan on p2_c3_c2
+(19 rows)
+
+-- all children are parallel, so this can be parallel
+/*+Parallel(p1 10) Parallel(p2 10) */
+EXPLAIN (COSTS false) SELECT id FROM p1 UNION ALL SELECT id FROM p2;
+LOG:  pg_hint_plan:
+used hint:
+Parallel(p1 10 soft)
+Parallel(p2 10 soft)
+not used hint:
+duplication hint:
+error hint:
+
+                QUERY PLAN                 
+-------------------------------------------
+ Gather
+   Workers Planned: 1
+   ->  Append
+         ->  Parallel Seq Scan on p1
+         ->  Parallel Seq Scan on p1_c1
+         ->  Parallel Seq Scan on p1_c2
+         ->  Parallel Seq Scan on p1_c3
+         ->  Parallel Seq Scan on p1_c4
+         ->  Parallel Seq Scan on p1_c1_c1
+         ->  Parallel Seq Scan on p1_c1_c2
+         ->  Parallel Seq Scan on p1_c3_c1
+         ->  Parallel Seq Scan on p1_c3_c2
+         ->  Parallel Seq Scan on p2
+         ->  Parallel Seq Scan on p2_c1
+         ->  Parallel Seq Scan on p2_c2
+         ->  Parallel Seq Scan on p2_c3
+         ->  Parallel Seq Scan on p2_c4
+         ->  Parallel Seq Scan on p2_c1_c1
+         ->  Parallel Seq Scan on p2_c1_c2
+         ->  Parallel Seq Scan on p2_c3_c1
+         ->  Parallel Seq Scan on p2_c3_c2
+(21 rows)
+
+SET parallel_setup_cost to DEFAULT;
+SET parallel_tuple_cost to DEFAULT;
+SET min_parallel_relation_size to DEFAULT;
+/*+Parallel(p1 10 hard)Parallel(p2 10 hard) */
+EXPLAIN (COSTS false) SELECT id FROM p1 UNION ALL SELECT id FROM p2;
+LOG:  pg_hint_plan:
+used hint:
+Parallel(p1 10 hard)
+Parallel(p2 10 hard)
+not used hint:
+duplication hint:
+error hint:
+
+                QUERY PLAN                 
+-------------------------------------------
+ Gather
+   Workers Planned: 10
+   ->  Append
+         ->  Parallel Seq Scan on p1
+         ->  Parallel Seq Scan on p1_c1
+         ->  Parallel Seq Scan on p1_c2
+         ->  Parallel Seq Scan on p1_c3
+         ->  Parallel Seq Scan on p1_c4
+         ->  Parallel Seq Scan on p1_c1_c1
+         ->  Parallel Seq Scan on p1_c1_c2
+         ->  Parallel Seq Scan on p1_c3_c1
+         ->  Parallel Seq Scan on p1_c3_c2
+         ->  Parallel Seq Scan on p2
+         ->  Parallel Seq Scan on p2_c1
+         ->  Parallel Seq Scan on p2_c2
+         ->  Parallel Seq Scan on p2_c3
+         ->  Parallel Seq Scan on p2_c4
+         ->  Parallel Seq Scan on p2_c1_c1
+         ->  Parallel Seq Scan on p2_c1_c2
+         ->  Parallel Seq Scan on p2_c3_c1
+         ->  Parallel Seq Scan on p2_c3_c2
+(21 rows)
+
 -- Negative hint
 SET max_parallel_workers_per_gather to 5;
+SET parallel_setup_cost to 0;
+SET parallel_tuple_cost to 0;
+SET min_parallel_relation_size to 0;
 EXPLAIN (COSTS false) SELECT * FROM p1;
                 QUERY PLAN                 
 -------------------------------------------
index 109c4fa..94bbfb8 100644 (file)
@@ -1,7 +1,8 @@
 /*-------------------------------------------------------------------------
  *
  * make_join_rel.c
- *       Routines copied from PostgreSQL core distribution.
+ *       Routines copied from PostgreSQL core distribution with some
+ *       modifications.
  *
  * src/backend/optimizer/path/joinrels.c
  *     make_join_rel()
index 9e0d15c..0fa0303 100644 (file)
@@ -374,9 +374,6 @@ static void pg_hint_plan_ProcessUtility(Node *parsetree,
                                                        DestReceiver *dest, char *completionTag);
 static PlannedStmt *pg_hint_plan_planner(Query *parse, int cursorOptions,
                                                                                 ParamListInfo boundParams);
-static void pg_hint_plan_get_relation_info(PlannerInfo *root,
-                                                                                  Oid relationObjectId,
-                                                                                  bool inhparent, RelOptInfo *rel);
 static RelOptInfo *pg_hint_plan_join_search(PlannerInfo *root,
                                                                                        int levels_needed,
                                                                                        List *initial_rels);
@@ -456,6 +453,8 @@ static void make_rels_by_clauseless_joins(PlannerInfo *root,
                                                                                  RelOptInfo *old_rel,
                                                                                  ListCell *other_rels);
 static bool has_join_restriction(PlannerInfo *root, RelOptInfo *rel);
+static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
+                                                                  RangeTblEntry *rte);
 static void set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
                                                                        Index rti, RangeTblEntry *rte);
 static void generate_mergeappend_paths(PlannerInfo *root, RelOptInfo *rel,
@@ -537,7 +536,6 @@ static const struct config_enum_entry parse_debug_level_options[] = {
 /* Saved hook values in case of unload */
 static ProcessUtility_hook_type prev_ProcessUtility = NULL;
 static planner_hook_type prev_planner = NULL;
-static get_relation_info_hook_type prev_get_relation_info = NULL;
 static join_search_hook_type prev_join_search = NULL;
 static set_rel_pathlist_hook_type prev_set_rel_pathlist = NULL;
 
@@ -671,8 +669,6 @@ _PG_init(void)
        ProcessUtility_hook = pg_hint_plan_ProcessUtility;
        prev_planner = planner_hook;
        planner_hook = pg_hint_plan_planner;
-       prev_get_relation_info = get_relation_info_hook;
-       get_relation_info_hook = pg_hint_plan_get_relation_info;
        prev_join_search = join_search_hook;
        join_search_hook = pg_hint_plan_join_search;
        prev_set_rel_pathlist = set_rel_pathlist_hook;
@@ -697,7 +693,6 @@ _PG_fini(void)
        /* Uninstall hooks. */
        ProcessUtility_hook = prev_ProcessUtility;
        planner_hook = prev_planner;
-       get_relation_info_hook = prev_get_relation_info;
        join_search_hook = prev_join_search;
        set_rel_pathlist_hook = prev_set_rel_pathlist;
 
@@ -2528,7 +2523,7 @@ setup_guc_enforcement(SetHint **options, int noptions, GucContext context)
  * If hint is not NULL, set up using it, elsewise reset to initial environment.
  */
 static void
-setup_parallel_plan_enfocement(ParallelHint *hint, HintState *state)
+setup_parallel_plan_enforcement(ParallelHint *hint, HintState *state)
 {
        if (hint)
        {
@@ -3041,9 +3036,9 @@ find_parallel_hint(PlannerInfo *root, Index relid, RelOptInfo *rel)
         *   - not a base relation and inheritance children
         *   - not an ordinary relation (such as join and subquery)
         */
-       if (rel && ((rel->reloptkind != RELOPT_BASEREL &&
-                                rel->reloptkind != RELOPT_OTHER_MEMBER_REL) ||
-                               rel->rtekind != RTE_RELATION))
+       if (!rel || rel->rtekind != RTE_RELATION ||
+               (rel->reloptkind != RELOPT_BASEREL &&
+                rel->reloptkind != RELOPT_OTHER_MEMBER_REL))
                return NULL;
 
        rte = root->simple_rte_array[relid];
@@ -3095,13 +3090,18 @@ regexpeq(const char *s1, const char *s2)
        return DatumGetBool(result);
 }
 
+
+/* Remove indexes instructed not to use by hint. */
 static void
-delete_indexes(ScanMethodHint *hint, RelOptInfo *rel, Oid relationObjectId)
+restrict_indexes(PlannerInfo *root, ScanMethodHint *hint, RelOptInfo *rel,
+                          bool using_parent_hint)
 {
        ListCell           *cell;
        ListCell           *prev;
        ListCell           *next;
        StringInfoData  buf;
+       RangeTblEntry  *rte = root->simple_rte_array[rel->relid];
+       Oid                             relationObjectId = rte->relid;
 
        /*
         * We delete all the IndexOptInfo list and prevent you from being usable by
@@ -3164,10 +3164,11 @@ delete_indexes(ScanMethodHint *hint, RelOptInfo *rel, Oid relationObjectId)
                }
 
                /*
-                * to make the index a candidate when definition of this index is
-                * matched with the index's definition of current_hint_state.
+                * Apply index restriction of parent hint to children. Since index
+                * inheritance is not explicitly described we should search for an
+                * children's index with the same definition to that of the parent.
                 */
-               if (OidIsValid(relationObjectId) && !use_index)
+               if (using_parent_hint && !use_index)
                {
                        foreach(l, current_hint_state->parent_index_infos)
                        {
@@ -3175,71 +3176,61 @@ delete_indexes(ScanMethodHint *hint, RelOptInfo *rel, Oid relationObjectId)
                                HeapTuple                       ht_idx;
                                ParentIndexInfo    *p_info = (ParentIndexInfo *)lfirst(l);
 
-                               /* check to match the parameter of unique */
-                               if (p_info->indisunique != info->unique)
-                                       continue;
-
-                               /* check to match the parameter of index's method */
-                               if (p_info->method != info->relam)
-                                       continue;
-
-                               /* to check to match the indexkey's configuration */
-                               if ((list_length(p_info->column_names)) !=
-                                        info->ncolumns)
+                               /*
+                                * we check the 'same' index by comparing uniqueness, access
+                                * method and index key columns.
+                                */
+                               if (p_info->indisunique != info->unique ||
+                                       p_info->method != info->relam ||
+                                       list_length(p_info->column_names) != info->ncolumns)
                                        continue;
 
-                               /* check to match the indexkey's configuration */
+                               /* Check if index key columns match */
                                for (i = 0; i < info->ncolumns; i++)
                                {
                                        char       *c_attname = NULL;
                                        char       *p_attname = NULL;
 
-                                       p_attname =
-                                               list_nth(p_info->column_names, i);
+                                       p_attname = list_nth(p_info->column_names, i);
 
-                                       /* both are expressions */
+                                       /*
+                                        * if both of the key of the same position are expressions,
+                                        * ignore them for now and check later.
+                                        */
                                        if (info->indexkeys[i] == 0 && !p_attname)
                                                continue;
 
-                                       /* one's column is expression, the other is not */
+                                       /* deny if one is expression while another is not */
                                        if (info->indexkeys[i] == 0 || !p_attname)
                                                break;
 
                                        c_attname = get_attname(relationObjectId,
                                                                                                info->indexkeys[i]);
 
-                                       if (strcmp(p_attname, c_attname) != 0)
-                                               break;
-
-                                       if (p_info->indcollation[i] != info->indexcollations[i])
-                                               break;
-
-                                       if (p_info->opclass[i] != info->opcintype[i])
-                                               break;
-
-                                       if (((p_info->indoption[i] & INDOPTION_DESC) != 0) !=
-                                               info->reverse_sort[i])
+                                       /* deny if any of column attributes don't match */
+                                       if (strcmp(p_attname, c_attname) != 0 ||
+                                               p_info->indcollation[i] != info->indexcollations[i] ||
+                                               p_info->opclass[i] != info->opcintype[i]||
+                                               ((p_info->indoption[i] & INDOPTION_DESC) != 0)
+                                               != info->reverse_sort[i] ||
+                                               ((p_info->indoption[i] & INDOPTION_NULLS_FIRST) != 0)
+                                               != info->nulls_first[i])
                                                break;
-
-                                       if (((p_info->indoption[i] & INDOPTION_NULLS_FIRST) != 0) !=
-                                               info->nulls_first[i])
-                                               break;
-
                                }
 
+                               /* deny this if any difference found */
                                if (i != info->ncolumns)
                                        continue;
 
+                               /* check on key expressions  */
                                if ((p_info->expression_str && (info->indexprs != NIL)) ||
                                        (p_info->indpred_str && (info->indpred != NIL)))
                                {
-                                       /*
-                                        * Fetch the pg_index tuple by the Oid of the index
-                                        */
+                                       /* fetch the index of this child */
                                        ht_idx = SearchSysCache1(INDEXRELID,
                                                                                         ObjectIdGetDatum(info->indexoid));
 
-                                       /* check to match the expression's parameter of index */
+                                       /* check expressions if both expressions are available */
                                        if (p_info->expression_str &&
                                                !heap_attisnull(ht_idx, Anum_pg_index_indexprs))
                                        {
@@ -3260,17 +3251,17 @@ delete_indexes(ScanMethodHint *hint, RelOptInfo *rel, Oid relationObjectId)
                                                                                                         ObjectIdGetDatum(
                                                                                                                 relationObjectId));
 
+                                               /* deny if expressions don't match */
                                                if (strcmp(p_info->expression_str,
                                                                   text_to_cstring(DatumGetTextP(result))) != 0)
                                                {
                                                        /* Clean up */
                                                        ReleaseSysCache(ht_idx);
-
                                                        continue;
                                                }
                                        }
 
-                                       /* Check to match the predicate's parameter of index */
+                                       /* compare index predicates  */
                                        if (p_info->indpred_str &&
                                                !heap_attisnull(ht_idx, Anum_pg_index_indpred))
                                        {
@@ -3278,10 +3269,6 @@ delete_indexes(ScanMethodHint *hint, RelOptInfo *rel, Oid relationObjectId)
                                                bool        isnull;
                                                Datum       result;
 
-                                               /*
-                                                * to change the predicate's parameter of child's
-                                                * index to strings
-                                                */
                                                predDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
                                                                                                         Anum_pg_index_indpred,
                                                                                                         &isnull);
@@ -3296,7 +3283,6 @@ delete_indexes(ScanMethodHint *hint, RelOptInfo *rel, Oid relationObjectId)
                                                {
                                                        /* Clean up */
                                                        ReleaseSysCache(ht_idx);
-
                                                        continue;
                                                }
                                        }
@@ -3332,16 +3318,21 @@ delete_indexes(ScanMethodHint *hint, RelOptInfo *rel, Oid relationObjectId)
 
        if (debug_level == 1)
        {
-               char   *relname;
                StringInfoData  rel_buf;
+               char *disprelname = "";
 
-               if (OidIsValid(relationObjectId))
-                       relname = get_rel_name(relationObjectId);
+               /*
+                * If this hint targetted the parent, use the real name of this
+                * child. Otherwise use hint specification.
+                */
+               if (using_parent_hint)
+                       disprelname = get_rel_name(rte->relid);
                else
-                       relname = hint->relname;
+                       disprelname = hint->relname;
+                       
 
                initStringInfo(&rel_buf);
-               quote_value(&rel_buf, relname);
+               quote_value(&rel_buf, disprelname);
 
                ereport(LOG,
                                (errmsg("available indexes for %s(%s):%s",
@@ -3433,15 +3424,27 @@ get_parent_index_info(Oid indexoid, Oid relid)
 }
 
 /*
- * Set planner guc parameters according to corresponding scan hints.
+ * cancel hint enforcement
  */
 static void
-setup_hint_enforcement(PlannerInfo *root, Oid relationObjectId,
-                                          bool inhparent, RelOptInfo *rel)
+reset_hint_enforcement()
 {
-       Index new_parent_relid = 0;
+       setup_scan_method_enforcement(NULL, current_hint_state);
+       setup_parallel_plan_enforcement(NULL, current_hint_state);
+}
+
+/*
+ * Set planner guc parameters according to corresponding scan hints.
+ */
+static bool
+setup_hint_enforcement(PlannerInfo *root, RelOptInfo *rel)
+{
+       Index   new_parent_relid = 0;
        ListCell *l;
-       ScanMethodHint *scanhint = NULL;
+       ScanMethodHint *shint = NULL;
+       ParallelHint   *phint = NULL;
+       bool                    inhparent = root->simple_rte_array[rel->relid]->inh;
+       Oid             relationObjectId = root->simple_rte_array[rel->relid]->relid;
 
        /*
         * We could register the parent relation of the following children here
@@ -3455,13 +3458,13 @@ setup_hint_enforcement(PlannerInfo *root, Oid relationObjectId,
                if (debug_level > 1)
                        ereport(pg_hint_plan_message_level,
                                        (errhidestmt(true),
-                                        errmsg ("pg_hint_plan%s: get_relation_info"
+                                        errmsg ("pg_hint_plan%s: setup_hint_enforcement"
                                                         " skipping inh parent: relation=%u(%s), inhparent=%d,"
                                                         " current_hint_state=%p, hint_inhibit_level=%d",
                                                         qnostr, relationObjectId,
                                                         get_rel_name(relationObjectId),
                                                         inhparent, current_hint_state, hint_inhibit_level)));
-               return;
+               return false;
        }
 
        /* Find the parent for this relation other than the registered parent */
@@ -3492,28 +3495,27 @@ setup_hint_enforcement(PlannerInfo *root, Oid relationObjectId,
                 * hint to other childrens of this parent so remember it * to avoid
                 * hinthintredundant setup cost.
                 */
-               ScanMethodHint *parent_scan_hint = NULL;
-               ParallelHint   *parent_parallel_hint = NULL;
-
                current_hint_state->parent_relid = new_parent_relid;
                                
                /* Find hints for the parent */
-               current_hint_state->parent_scan_hint = parent_scan_hint = 
+               current_hint_state->parent_scan_hint =
                        find_scan_hint(root, current_hint_state->parent_relid, NULL);
 
-               current_hint_state->parent_parallel_hint = parent_parallel_hint = 
+               current_hint_state->parent_parallel_hint =
                        find_parallel_hint(root, current_hint_state->parent_relid, NULL);
 
-               if (parent_scan_hint)
+               /*
+                * If hint is found for the parent, apply it for this child instead
+                * of its own.
+                */
+               if (current_hint_state->parent_scan_hint)
                {
-                       /*
-                        * If hint is found for the parent, apply it for this child instead
-                        * of its own.
-                        */
-                       parent_scan_hint->base.state = HINT_STATE_USED;
+                       ScanMethodHint * pshint = current_hint_state->parent_scan_hint;
+
+                       pshint->base.state = HINT_STATE_USED;
 
                        /* Apply index mask in the same manner to the parent. */
-                       if (parent_scan_hint->indexnames)
+                       if (pshint->indexnames)
                        {
                                Oid                     parentrel_oid;
                                Relation        parent_rel;
@@ -3530,7 +3532,7 @@ setup_hint_enforcement(PlannerInfo *root, Oid relationObjectId,
                                        ListCell   *lc;
                                        ParentIndexInfo *parent_index_info;
 
-                                       foreach(lc, parent_scan_hint->indexnames)
+                                       foreach(lc, pshint->indexnames)
                                        {
                                                if (RelnameCmp(&indexname, &lfirst(lc)) == 0)
                                                        break;
@@ -3541,106 +3543,82 @@ setup_hint_enforcement(PlannerInfo *root, Oid relationObjectId,
                                        parent_index_info =
                                                get_parent_index_info(indexoid, parentrel_oid);
                                        current_hint_state->parent_index_infos =
-                                               lappend(current_hint_state->parent_index_infos, parent_index_info);
+                                               lappend(current_hint_state->parent_index_infos,
+                                                               parent_index_info);
                                }
                                heap_close(parent_rel, NoLock);
                        }
                }
-
-               /* Setup planner environment */
-               setup_scan_method_enforcement(parent_scan_hint, current_hint_state);
-               setup_parallel_plan_enfocement(
-                       find_parallel_hint(root, rel->relid, rel), current_hint_state);
        }
 
-       /* Process index restriction hint inheritance */
-       if (current_hint_state->parent_scan_hint != 0)
-       {
-               delete_indexes(current_hint_state->parent_scan_hint, rel,
-                                          relationObjectId);
-
-               /* Scan fixation status is the same to the parent. */
-               if (debug_level > 1)
-                       ereport(pg_hint_plan_message_level,
-                                       (errhidestmt(true),
-                                        errmsg("pg_hint_plan%s: get_relation_info:"
-                                                       " index deletion by parent hint: "
-                                                       "relation=%u(%s), inhparent=%d, current_hint_state=%p,"
-                                                       " hint_inhibit_level=%d",
-                                                       qnostr, relationObjectId,
-                                                       get_rel_name(relationObjectId),
-                                                       inhparent, current_hint_state, hint_inhibit_level)));
-               return;
-       }
+       if (current_hint_state->parent_scan_hint)
+               shint = current_hint_state->parent_scan_hint;
+       else
+               shint = find_scan_hint(root, rel->relid, rel);
 
-       /* This table doesn't have a parent hint. Apply its own hint if any. */
-       if ((scanhint = find_scan_hint(root, rel->relid, rel)) != NULL)
+       if (shint)
        {
-               setup_scan_method_enforcement(scanhint, current_hint_state);
+               bool using_parent_hint =
+                       (shint == current_hint_state->parent_scan_hint);
 
-               delete_indexes(scanhint, rel, InvalidOid);
+               /* Setup scan enforcement environment */
+               setup_scan_method_enforcement(shint, current_hint_state);
+
+               /* restrict unwanted inexes */
+               restrict_indexes(root, shint, rel, using_parent_hint);
 
                if (debug_level > 1)
+               {
+                       char *additional_message = "";
+
+                       if (shint == current_hint_state->parent_scan_hint)
+                               additional_message = " by parent hint";
+
                        ereport(pg_hint_plan_message_level,
                                        (errhidestmt(true),
-                                        errmsg ("pg_hint_plan%s: get_relation_info"
-                                                        " index deletion:"
-                                                        " relation=%u(%s), inhparent=%d, current_hint=%p,"
+                                        errmsg ("pg_hint_plan%s: setup_hint_enforcement"
+                                                        " index deletion%s:"
+                                                        " relation=%u(%s), inhparent=%d, "
+                                                        "current_hint_state=%p,"
                                                         " hint_inhibit_level=%d, scanmask=0x%x",
-                                                        qnostr, relationObjectId,
+                                                        qnostr, additional_message,
+                                                        relationObjectId,
                                                         get_rel_name(relationObjectId),
-                                                        inhparent, current_hint_state, hint_inhibit_level,
-                                                        scanhint->enforce_mask)));
-               return;
+                                                        inhparent, current_hint_state,
+                                                        hint_inhibit_level,
+                                                        shint->enforce_mask)));
+               }
        }
 
        /* Do the same for parallel plan enforcement */
-       setup_parallel_plan_enfocement(find_parallel_hint(root, rel->relid, rel),
-                                                                  current_hint_state);
-
-       /* Nothing to apply. Reset the scan mask to intial state */
-       if (debug_level > 1)
-               ereport(pg_hint_plan_message_level,
-                               (errhidestmt (true),
-                                errmsg ("pg_hint_plan%s: get_relation_info"
-                                                " no hint applied:"
-                                                " relation=%u(%s), inhparent=%d, current_hint=%p,"
-                                                " hint_inhibit_level=%d, scanmask=0x%x",
-                                                qnostr, relationObjectId,
-                                                get_rel_name(relationObjectId),
-                                                inhparent, current_hint_state, hint_inhibit_level,
-                                                current_hint_state->init_scan_mask)));
-       setup_scan_method_enforcement(NULL,     current_hint_state);
-}
+       if (current_hint_state->parent_parallel_hint)
+               phint = current_hint_state->parent_parallel_hint;
+       else
+               phint = find_parallel_hint(root, rel->relid, rel);
 
-static void
-pg_hint_plan_get_relation_info(PlannerInfo *root, Oid relationObjectId,
-                                                          bool inhparent, RelOptInfo *rel)
-{
-       if (prev_get_relation_info)
-               (*prev_get_relation_info) (root, relationObjectId, inhparent, rel);
+       setup_parallel_plan_enforcement(phint, current_hint_state);
 
-       /* 
-        * Do nothing if we don't have a valid hint in this context or current
-        * nesting depth is at SPI calls.
-        */
-       if (!current_hint_state || hint_inhibit_level > 0)
+       /* Nothing to apply. Reset the scan mask to intial state */
+       if (!shint && ! phint)
        {
                if (debug_level > 1)
                        ereport(pg_hint_plan_message_level,
                                        (errhidestmt (true),
                                         errmsg ("pg_hint_plan%s: get_relation_info"
-                                                        " no hint to apply: relation=%u(%s), inhparent=%d,"
-                                                        " current_hint_state=%p, hint_inhibit_level=%d",
+                                                        " no hint applied:"
+                                                        " relation=%u(%s), inhparent=%d, current_hint=%p,"
+                                                        " hint_inhibit_level=%d, scanmask=0x%x",
                                                         qnostr, relationObjectId,
                                                         get_rel_name(relationObjectId),
-                                                        inhparent, current_hint_state, hint_inhibit_level)));
-               return;
-       }
+                                                        inhparent, current_hint_state, hint_inhibit_level,
+                                                        current_hint_state->init_scan_mask)));
 
-       setup_hint_enforcement(root, relationObjectId, inhparent, rel);
+               setup_scan_method_enforcement(NULL,     current_hint_state);
 
-       return;
+               return false;
+       }
+
+       return true;
 }
 
 /*
@@ -4086,150 +4064,6 @@ transform_join_hints(HintState *hstate, PlannerInfo *root, int nbaserel,
 }
 
 /*
- * set_plain_rel_pathlist
- *       Build access paths for a plain relation (no subquery, no inheritance)
- *
- * This function was copied and edited from set_plain_rel_pathlist() in
- * src/backend/optimizer/path/allpaths.c
- *
- * - removed parallel stuff.
- */
-static void
-set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
-{
-       Relids          required_outer;
-
-       /*
-        * We don't support pushing join clauses into the quals of a seqscan, but
-        * it could still have required parameterization due to LATERAL refs in
-        * its tlist.
-        */
-       required_outer = rel->lateral_relids;
-
-       /* Consider sequential scan */
-       add_path(rel, create_seqscan_path(root, rel, required_outer, 0));
-
-       /* If appropriate, consider parallel sequential scan */
-       if (rel->consider_parallel && required_outer == NULL)
-       {
-               ParallelHint *phint = find_parallel_hint(root, rel->relid, rel);
-
-               /* Consider parallel paths only if not inhibited by hint */
-               if (!phint || phint->nworkers > 0)
-                       create_plain_partial_paths(root, rel);
-
-               /*
-                * Overwirte parallel_workers if requested. partial_pathlist seems to
-                * have up to one path but looping over all possible paths don't harm.
-                */
-               if (phint && phint->nworkers > 0 && phint->force_parallel)
-               {
-                       ListCell *l;
-                       foreach (l, rel->partial_pathlist)
-                       {
-                               Path *ppath = (Path *) lfirst(l);
-                               
-                               Assert(ppath->parallel_workers > 0);
-                               ppath->parallel_workers = phint->nworkers;
-                       }
-               }                       
-       }
-
-       /* Consider index scans */
-       create_index_paths(root, rel);
-
-       /* Consider TID scans */
-       create_tidscan_paths(root, rel);
-}
-
-/*
- * Clear exiting access paths and create new ones applying hints.
- * This does the similar to set_rel_pathlist
- */
-static void
-rebuild_scan_path(HintState *hstate, PlannerInfo *root, int level,
-                                 List *initial_rels)
-{
-       ListCell   *l;
-
-       foreach(l, initial_rels)
-       {
-               RelOptInfo         *rel = (RelOptInfo *) lfirst(l);
-               RangeTblEntry  *rte;
-
-               /* Skip relations which we can't choose scan method. */
-               if (rel->reloptkind != RELOPT_BASEREL || rel->rtekind != RTE_RELATION)
-                       continue;
-
-               rte = root->simple_rte_array[rel->relid];
-
-               /* We can't force scan method of foreign tables */
-               if (rte->relkind == RELKIND_FOREIGN_TABLE)
-                       continue;
-
-               /*
-                * Create scan paths with GUC parameters which are at the beginning of
-                * planner if scan method hint is not specified, otherwise use
-                * specified hints and mark the hint as used.
-                */
-               setup_scan_method_enforcement(find_scan_hint(root, rel->relid, rel),
-                                                                         hstate);
-
-               /* Setup parallel environment according to the hint */
-               setup_parallel_plan_enfocement(
-                       find_parallel_hint(root, rel->relid, rel), current_hint_state);
-
-               /* remove existing partial paths from this baserel */
-               list_free_deep(rel->partial_pathlist);
-               rel->partial_pathlist = NIL;
-
-               /* remove existing paths from this baserel */
-               list_free_deep(rel->pathlist);
-               rel->pathlist = NIL;
-
-               if (rte->inh)
-               {
-                       ListCell *l;
-
-                       /* remove partial paths from all chlidren */
-                       foreach (l, root->append_rel_list)
-                       {
-                               AppendRelInfo  *appinfo = (AppendRelInfo *) lfirst(l);
-                               RelOptInfo         *childrel;
-
-                               if (appinfo->parent_relid != rel->relid)
-                                       continue;
-
-                               childrel = root->simple_rel_array[appinfo->child_relid];
-                               list_free_deep(childrel->partial_pathlist);
-                               childrel->partial_pathlist = NIL;
-                       }
-                       /* It's an "append relation", process accordingly */
-                       set_append_rel_pathlist(root, rel, rel->relid, rte);
-               }
-               else
-               {
-                       set_plain_rel_pathlist(root, rel, rte);
-               }
-
-               /*
-                * If this is a baserel, consider gathering any partial paths we may
-                * hinthave created for it.
-                */
-               if (rel->reloptkind == RELOPT_BASEREL)
-                       generate_gather_paths(root, rel);
-
-               /* Now find the cheapest of the paths for this rel */
-               set_cheapest(rel);
-       }
-
-       /*
-        * Restore the GUC variables we set above.
-        */
-       setup_scan_method_enforcement(NULL, hstate);
-}
-
-/*
  * wrapper of make_join_rel()
  *
  * call make_join_rel() after changing enable_* parameters according to given
@@ -4380,9 +4214,6 @@ pg_hint_plan_join_search(PlannerInfo *root, int levels_needed,
                        return standard_join_search(root, levels_needed, initial_rels);
        }
 
-       /* apply scan method hint rebuild scan path. */
-       rebuild_scan_path(current_hint_state, root, levels_needed, initial_rels);
-
        /*
         * In the case using GEQO, only scan method hints and Set hints have
         * effect.  Join method and join order is not controllable by hints.
@@ -4428,55 +4259,94 @@ pg_hint_plan_set_rel_pathlist(PlannerInfo * root, RelOptInfo *rel,
                                                          Index rti, RangeTblEntry *rte)
 {
        ParallelHint   *phint;
-       List               *oldpathlist;
        ListCell           *l;
-       bool                    regenerate = false;
 
-       /* Hint has not been parsed yet, or this is not a parallel'able relation */
-       if (current_hint_state == NULL || rel->partial_pathlist == NIL)
+       /* call the previous hook */
+       if (prev_set_rel_pathlist)
+               prev_set_rel_pathlist(root, rel, rti, rte);
+
+       /* Nothing to do when hint has not been parsed yet */
+       if (current_hint_state == NULL)
                return;
 
-       phint = find_parallel_hint(root, rel->relid, rel);
+       /* Don't touch dummy rel */
+       if (IS_DUMMY_REL(rel))
+               return;
 
-       if (phint == NULL || !phint->force_parallel)
+       /* We cannot handle if this requires an outer */
+       if (rel->lateral_relids)
                return;
 
-       /*
-        * This relation contains gather paths previously created and they prevent
-        * adding new gahter path with same cost. Remove them.
-        */
-       oldpathlist = rel->pathlist;
-       rel->pathlist = NIL;
-       foreach (l, oldpathlist)
+       if (!setup_hint_enforcement(root, rel))
        {
-               Path *path = (Path *) lfirst(l);
+               /*
+                * No enforcement requested, but we might have to generate gather
+                * path on this relation
+                */
 
-               if (IsA(path, GatherPath) &&
-                       path->parallel_workers != phint->nworkers)
+               /* If no need of a gather path, just return */
+               if (rel->reloptkind != RELOPT_BASEREL || max_hint_nworkers < 1 ||
+                       rel->partial_pathlist == NIL)
+                       return;
+
+               /* Lower the priorities of existing paths, then add a new path */
+               foreach (l, rel->pathlist)
                {
-                       pfree(path);
-                       regenerate = true;
+                       Path *path = (Path *) lfirst(l);
+
+                       if (path->startup_cost < disable_cost)
+                       {
+                               path->startup_cost += disable_cost;
+                               path->total_cost += disable_cost;
+                       }
                }
-               else
-                       rel->pathlist = lappend(rel->pathlist, path);
+
+               generate_gather_paths(root, rel);
+               return;
        }
-       list_free(oldpathlist);
 
-       if (regenerate)
+       /* Here, we regenerate paths with the current hint restriction */
+
+       /* Remove prviously paths except dummy rels */
+       list_free_deep(rel->pathlist);
+       rel->pathlist = NIL;
+
+       /* Rebuild access paths */
+       set_plain_rel_pathlist(root, rel, rte);
+
+       /*
+        * create_plain_partial_paths creates partial paths with reasonably
+        * estimated number of workers. Force the requested number of workers if
+        * hard mode.
+        */
+       phint = find_parallel_hint(root, rel->relid, rel);
+
+       if (phint)
        {
-               foreach (l, rel->partial_pathlist)
+               /* if inhibiting parallel, remove existing partial paths  */
+               if (phint->nworkers == 0 && rel->partial_pathlist)
                {
-                       Path *ppath = (Path *) lfirst(l);
-                       
-                       if (phint && phint->nworkers > 0 && phint->force_parallel)
+                       list_free_deep(rel->partial_pathlist);
+                       rel->partial_pathlist = NIL;
+               }
+
+               /* enforce number of workers if requested */
+               if (rel->partial_pathlist && phint->force_parallel)
+               {
+                       foreach (l, rel->partial_pathlist)
                        {
-                               Assert(ppath->parallel_workers > 0);
+                               Path *ppath = (Path *) lfirst(l);
+
                                ppath->parallel_workers = phint->nworkers;
-                       }                       
+                       }
                }
-               
-               generate_gather_paths(root, rel);
+
+               /* Generate gather paths for base rels */
+               if (rel->reloptkind == RELOPT_BASEREL)
+                       generate_gather_paths(root, rel);
        }
+
+       reset_hint_enforcement();
 }
 
 /*
@@ -4484,7 +4354,8 @@ pg_hint_plan_set_rel_pathlist(PlannerInfo * root, RelOptInfo *rel,
  *       Build access paths for a base relation
  *
  * This function was copied and edited from set_rel_pathlist() in
- * src/backend/optimizer/path/allpaths.c
+ * src/backend/optimizer/path/allpaths.c in order not to copy other static
+ * functions not required here.
  */
 static void
 set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
index 3bbd85b..7b4431f 100644 (file)
@@ -3,36 +3,47 @@ ALTER SYSTEM SET session_preload_libraries TO 'pg_hint_plan';
 SET pg_hint_plan.enable_hint TO on;
 SET pg_hint_plan.debug_print TO on;
 SET client_min_messages TO LOG;
-SET search_path TO public;
-SET max_parallel_workers_per_gather TO 0;
-SET enable_indexscan to false;
-SET enable_bitmapscan to false;
-SET parallel_setup_cost to 0;
-SET parallel_tuple_cost to 0;
-SET min_parallel_relation_size to 0;
-SET max_parallel_workers_per_gather to 0;
+
 
 CREATE TABLE s1.tl (a int);
 INSERT INTO s1.tl (SELECT a FROM generate_series(0, 100000) a);
 
+-- Queries on ordinary tables
 EXPLAIN (COSTS false) SELECT * FROM s1.t1;
 
+SET parallel_setup_cost to 0;
+SET parallel_tuple_cost to 0;
+SET min_parallel_relation_size to 0;
 /*+Parallel(t1 10)*/
 EXPLAIN (COSTS false) SELECT * FROM s1.t1;
 
 /*+Parallel(t1 10 soft)*/
 EXPLAIN (COSTS false) SELECT * FROM s1.t1;
+SET parallel_setup_cost to DEFAULT;
+SET parallel_tuple_cost to DEFAULT;
+SET min_parallel_relation_size to DEFAULT;
 
 /*+Parallel(t1 10 hard)*/
 EXPLAIN (COSTS false) SELECT * FROM s1.t1;
 
--- Inheritnce tables
-/*+Parallel(p1 10 soft)*/
+-- Queries on inheritance tables
+SET parallel_setup_cost to 0;
+SET parallel_tuple_cost to 0;
+SET min_parallel_relation_size to 0;
+/*+Parallel(p1 10)*/
 EXPLAIN (COSTS false) SELECT * FROM p1;
+SET parallel_setup_cost to DEFAULT;
+SET parallel_tuple_cost to DEFAULT;
+SET min_parallel_relation_size to DEFAULT;
 
 /*+Parallel(p1 10 hard)*/
 EXPLAIN (COSTS false) SELECT * FROM p1;
 
+-- hinting on children don't work
+/*+Parallel(p1_c1 10 hard)*/
+EXPLAIN (COSTS false) SELECT * FROM p1;
+
+
 -- Joins
 EXPLAIN (COSTS false) SELECT * FROM p1_c1 join p2_c1 on p1_c1.id = p2_c1.id;
 
@@ -45,9 +56,16 @@ EXPLAIN (COSTS false) SELECT * FROM p1_c1 join p2_c1 on p1_c1.id = p2_c1.id;
 /*+Parallel(p1_c1 10 hard) Parallel(p2_c1 10 hard)*/
 EXPLAIN (COSTS false) SELECT * FROM p1_c1 join p2_c1 on p1_c1.id = p2_c1.id;
 
+
 -- Joins on inheritance tables
+SET parallel_setup_cost to 0;
+SET parallel_tuple_cost to 0;
+SET min_parallel_relation_size to 0;
 /*+Parallel(p1 10)*/
 EXPLAIN (COSTS false) SELECT * FROM p1 join p2 on p1.id = p2.id;
+SET parallel_setup_cost to DEFAULT;
+SET parallel_tuple_cost to DEFAULT;
+SET min_parallel_relation_size to DEFAULT;
 
 /*+Parallel(p2 10 hard)*/
 EXPLAIN (COSTS false) SELECT * FROM p1 join p2 on p1.id = p2.id;
@@ -55,8 +73,47 @@ EXPLAIN (COSTS false) SELECT * FROM p1 join p2 on p1.id = p2.id;
 /*+Parallel(p2 10 hard) Parallel(p1 5 hard) */
 EXPLAIN (COSTS false) SELECT * FROM p1 join p2 on p1.id = p2.id;
 
+
+-- Mixture with a scan hint
+-- p1 can be parallel
+/*+Parallel(p1 10 hard) IndexScan(p2) */
+EXPLAIN (COSTS false) SELECT * FROM p1 join p2 on p1.id = p2.id;
+
+-- seqscan doesn't harm parallelism
+/*+Parallel(p1 10 hard) SeqScan(p1) */
+EXPLAIN (COSTS false) SELECT * FROM p1 join p2 on p1.id = p2.id;
+
+-- parallelism is not available for the case
+/*+Parallel(p1 10 hard) IndexScan(p1) */
+EXPLAIN (COSTS false) SELECT * FROM p1 join p2 on p1.id = p2.id;
+
+
+-- Parallel on UNION
+EXPLAIN (COSTS false) SELECT id FROM p1 UNION ALL SELECT id FROM p2;
+
+-- some of the scans are not parallel, so this cannot be parallel
+SET parallel_setup_cost to 0;
+SET parallel_tuple_cost to 0;
+SET min_parallel_relation_size to 0;
+/*+Parallel(p1 10) */
+EXPLAIN (COSTS false) SELECT id FROM p1 UNION ALL SELECT id FROM p2;
+
+-- all children are parallel, so this can be parallel
+/*+Parallel(p1 10) Parallel(p2 10) */
+EXPLAIN (COSTS false) SELECT id FROM p1 UNION ALL SELECT id FROM p2;
+SET parallel_setup_cost to DEFAULT;
+SET parallel_tuple_cost to DEFAULT;
+SET min_parallel_relation_size to DEFAULT;
+
+/*+Parallel(p1 10 hard)Parallel(p2 10 hard) */
+EXPLAIN (COSTS false) SELECT id FROM p1 UNION ALL SELECT id FROM p2;
+
+
 -- Negative hint
 SET max_parallel_workers_per_gather to 5;
+SET parallel_setup_cost to 0;
+SET parallel_tuple_cost to 0;
+SET min_parallel_relation_size to 0;
 EXPLAIN (COSTS false) SELECT * FROM p1;
 
 /*+Parallel(p1 0 hard)*/