OSDN Git Service

Support "Memoize" Hint.
authorKyotaro Horiguchi <horikyota.ntt@gmail.com>
Tue, 5 Oct 2021 11:38:58 +0000 (20:38 +0900)
committerKyotaro Horiguchi <horikyota.ntt@gmail.com>
Wed, 6 Oct 2021 02:57:29 +0000 (11:57 +0900)
PostgreSQL 14 introduced the new optimization item "memoize", which
allows a join to memoize the inner result.  Add a hint to control this
feature.

doc/hint_list-ja.html
doc/hint_list.html
doc/pg_hint_plan-ja.html
doc/pg_hint_plan.html
expected/ut-J.out
pg_hint_plan.c
sql/ut-J.sql

index d349af2..5d59056 100755 (executable)
@@ -74,6 +74,13 @@ PostgreSQL 9.2以降で動作します。</td></tr>
 <tr><td nowrap>Leading((テーブル集合<Sup>注1</Sup> テーブル集合<Sup>注1</Sup>))</td>
   <td>1つ目に指定したテーブル集合を外部表として、2つ目に指定したテーブル集合を内部表として結合します。書式中のテーブル集合はテーブルもしくは別のテーブル集合です。</td></tr>
 
+<tr><td rowspan="2">結合時の挙動制御</td>
+  <td nowrap>Memoize(テーブル テーブル[ テーブル...])</td>
+  <td>指定したテーブル間の結合の最上位のインナープランの結果キャッシング(memoize)を有効にします。強制するわけではないことに注意してください。</td></tr>
+<tr>
+  <td nowrap>NoMemoize(テーブル テーブル[ テーブル...])</td>
+  <td>指定したテーブル間の結合の最上位のインナープランの結果キャッシング(memoize)を無効にします。</td></tr>
+
 <tr><td>見積もり件数補正</td>
   <td nowrap>Rows(テーブル テーブル[ テーブル...] 件数補正)</td>
   <td>指定したテーブル間の結合結果の見積もり件数を補正します。件数補正として指定できるのは以下の4パターンです。
index a4d411e..f70b86a 100755 (executable)
@@ -10,7 +10,7 @@
 </head>
 
 <body>
-<h1 id="pg_hint_plan">pg_hint_plan 1.3 Appendices</h1>
+<h1 id="pg_hint_plan">pg_hint_plan 1.4 Appendices</h1>
 <div class="navigation">
   <a href="pg_hint_plan.html">pg_hint_plan</a> &gt;
   <a href="hint_list.html">Appendix A. Hints list</a>
 <tr><td nowrap>Leading(&lt;join pair&gt;)</td>
        <td>Forces join order and directions as specified. A join pair is a pair of tables and/or other join pairs enclosed by parentheses, which can make a nested structure.</td>
 
+<tr><td rowspan="2">Behavior control on Join</td>
+  <td nowrap>Memoize(table table[ table...])</td>
+  <td nowrap>Allow the topmost join of a join among the specified tables to memoize the inner result. (Note that this doesn't enforce.)</td></tr>
+<tr><td nowrap>NoMemoize(table table[ table...])</td>
+  <td nowrap>Inhibit the topmost join of a join among the specified tables from memoizing the inner result.</td>
+</tr>
+
 <tr><td>Row number correction</td>
        <td nowrap>Rows(table table[ table...] correction)</td>
   <td>Corrects row number of a result of the joins consist of the specfied tables. The available correction methods are absolute (#&ltn&gt), addition (+&ltn&gt), subtract (-&ltn&gt) and multiplication (*&ltn&gt). &ltn&gt should be a string that strtod() can read.</td></tr>
index 5fb4f3d..d7905c4 100755 (executable)
@@ -10,7 +10,7 @@
 </head>
 
 <body>
-<h1 id="pg_hint_plan">pg_hint_plan 1.3</h1>
+<h1 id="pg_hint_plan">pg_hint_plan 1.4</h1>
 <div class="navigation">
   <a href="pg_hint_plan-ja.html">pg_hint_plan</a>
 </div>
@@ -277,6 +277,24 @@ postgres-#  ORDER BY a.aid;
 <span class="strong">postgres=# /*+ Leading((t1 (t2 t3))) */</span> SELECT...
 </pre>
 <p>この書式では2つの要素を丸括弧で囲ったものがネストする形になっており、一つの括弧内では1つ目の要素が外部/駆動表、2番めの要素が内部/被駆動表として結合されます。</p>
+<h4>結合の挙動制御</h4>
+<p>あるオブジェクトの組み合わせ最上位結合の挙動を制御するヒント句のグループです。「Memoize」のみを含みます。</p>
+<p>Memoizeは最上位結合のインナープランの出力結果をキャッシングして高速化を試みる挙動を制御します。</p>
+<p>以下の例では、テーブルaとテーブルbの結合のインナー側(この例ではa側)のMemoizeを禁止します。</p>
+
+<pre>
+postgres=# /*+ NoMemoize(a b) */
+EXPLAIN SELECT * FROM a, b WHERE a.val = b.val;
+                             QUERY PLAN                             
+--------------------------------------------------------------------
+ Hash Join  (cost=270.00..1412.50 rows=100000 width=16)
+   Hash Cond: (b.val = a.val)
+   ->  Seq Scan on b  (cost=0.00..15.00 rows=1000 width=8)
+   ->  Hash  (cost=145.00..145.00 rows=10000 width=8)
+         ->  Seq Scan on a  (cost=0.00..145.00 rows=10000 width=8)
+(5 行)
+</pre>
+
 <h4>見積もり件数補正</h4>
 <p>あるオブジェクトの結合結果の件数を補正できるヒント句のグループです。「Rows」のみを含みます。</p>
 <p>見積もり件数補正対象として指定できるオブジェクトは結合方式と同じです。補正できるのは結合結果の見積もり件数だけで、スキャンの見積もり件数を補正することはできません。</p>
index 8b2b6b0..dd640c7 100755 (executable)
@@ -18,7 +18,7 @@ pre { margin: 0 2em 0 2em; padding:0.3em; border-style: solid; border-width:0.1e
 </head>
 
 <body>
-<h1 id="pg_hint_plan">pg_hint_plan 1.3</h1>
+<h1 id="pg_hint_plan">pg_hint_plan 1.4</h1>
 <div class="navigation">
   <a href="pg_hint_plan.html">pg_hint_plan</a>
 </div>
@@ -153,6 +153,22 @@ during CREATE EXTENSION. Table hints are prioritized than comment hits.</p>
 <span class="strong">postgres-#</span>     JOIN table table3 t3 ON (t2.key = t3.key);
 </pre>
 
+<h4>Hint for restricting join behavior</h4>
+<p>This hint "Memoize" and "NoMemoize" allows and disallows a join to memoize the inner result. In the following example, The NoMemoize hint inhibits the topmost hash join from memoizing the result of table a.</p>
+<pre>
+postgres=# /*+ NoMemoize(a b) */
+EXPLAIN SELECT * FROM a, b WHERE a.val = b.val;
+                             QUERY PLAN                             
+--------------------------------------------------------------------
+ Hash Join  (cost=270.00..1412.50 rows=100000 width=16)
+   Hash Cond: (b.val = a.val)
+   ->  Seq Scan on b  (cost=0.00..15.00 rows=1000 width=8)
+   ->  Hash  (cost=145.00..145.00 rows=10000 width=8)
+         ->  Seq Scan on a  (cost=0.00..145.00 rows=10000 width=8)
+</pre>
+
+  
+
 <h4>Hint for row number correction</h4>
 <p>This hint "Rows" corrects row number misestimation of joins that comes from restrictions of the planner. </p>
 
index 1d91c2c..44b724d 100644 (file)
@@ -4717,3 +4717,68 @@ error hint:
          ->  Seq Scan on t2  (cost=xxx..xxx rows=100 width=xxx)
 
 \! rm results/ut-J.tmpout
+-- Memoize
+EXPLAIN (COSTS false) SELECT * FROM t1, t2, t3 WHERE t1.val = t2.val and t2.id = t3.id;
+                 QUERY PLAN                 
+--------------------------------------------
+ Nested Loop
+   ->  Merge Join
+         Merge Cond: (t2.id = t3.id)
+         ->  Index Scan using t2_pkey on t2
+         ->  Sort
+               Sort Key: t3.id
+               ->  Seq Scan on t3
+   ->  Memoize
+         Cache Key: t2.val
+         ->  Index Scan using t1_val on t1
+               Index Cond: (val = t2.val)
+(11 rows)
+
+/*+ nomemoize(t1 t2)*/
+EXPLAIN (COSTS false) SELECT * FROM t1, t2, t3 WHERE t1.val = t2.val and t2.id = t3.id;  -- doesn't work
+LOG:  pg_hint_plan:
+used hint:
+NoMemoize(t1 t2)
+not used hint:
+duplication hint:
+error hint:
+
+                 QUERY PLAN                 
+--------------------------------------------
+ Nested Loop
+   ->  Merge Join
+         Merge Cond: (t2.id = t3.id)
+         ->  Index Scan using t2_pkey on t2
+         ->  Sort
+               Sort Key: t3.id
+               ->  Seq Scan on t3
+   ->  Memoize
+         Cache Key: t2.val
+         ->  Index Scan using t1_val on t1
+               Index Cond: (val = t2.val)
+(11 rows)
+
+/*+ nomemoize(t1 t2 t3)*/
+EXPLAIN (COSTS false) SELECT * FROM t1, t2, t3 WHERE t1.val = t2.val and t2.id = t3.id;
+LOG:  pg_hint_plan:
+used hint:
+NoMemoize(t1 t2 t3)
+not used hint:
+duplication hint:
+error hint:
+
+                    QUERY PLAN                    
+--------------------------------------------------
+ Merge Join
+   Merge Cond: (t1.val = t2.val)
+   ->  Index Scan using t1_val on t1
+   ->  Sort
+         Sort Key: t2.val
+         ->  Merge Join
+               Merge Cond: (t2.id = t3.id)
+               ->  Index Scan using t2_pkey on t2
+               ->  Sort
+                     Sort Key: t3.id
+                     ->  Seq Scan on t3
+(11 rows)
+
index 6130800..a3e5b1b 100644 (file)
@@ -94,6 +94,8 @@ PG_MODULE_MAGIC;
 #define HINT_LEADING                   "Leading"
 #define HINT_SET                               "Set"
 #define HINT_ROWS                              "Rows"
+#define HINT_MEMOIZE                   "Memoize"
+#define HINT_NOMEMOIZE                 "NoMemoize"
 
 #define HINT_ARRAY_DEFAULT_INITSIZE 8
 
@@ -122,7 +124,8 @@ enum
 {
        ENABLE_NESTLOOP = 0x01,
        ENABLE_MERGEJOIN = 0x02,
-       ENABLE_HASHJOIN = 0x04
+       ENABLE_HASHJOIN = 0x04,
+       ENABLE_MEMOIZE  = 0x08
 } JOIN_TYPE_BITS;
 
 #define ENABLE_ALL_SCAN (ENABLE_SEQSCAN | ENABLE_INDEXSCAN | \
@@ -160,6 +163,8 @@ typedef enum HintKeyword
        HINT_KEYWORD_SET,
        HINT_KEYWORD_ROWS,
        HINT_KEYWORD_PARALLEL,
+       HINT_KEYWORD_MEMOIZE,
+       HINT_KEYWORD_NOMEMOIZE,
 
        HINT_KEYWORD_UNRECOGNIZED
 } HintKeyword;
@@ -186,7 +191,6 @@ typedef const char *(*HintParseFunction) (Hint *hint, HintState *hstate,
                                                                                  Query *parse, const char *str);
 
 /* hint types */
-#define NUM_HINT_TYPE  6
 typedef enum HintType
 {
        HINT_TYPE_SCAN_METHOD,
@@ -194,7 +198,10 @@ typedef enum HintType
        HINT_TYPE_LEADING,
        HINT_TYPE_SET,
        HINT_TYPE_ROWS,
-       HINT_TYPE_PARALLEL
+       HINT_TYPE_PARALLEL,
+       HINT_TYPE_MEMOIZE,
+
+       NUM_HINT_TYPE
 } HintType;
 
 typedef enum HintTypeBitmap
@@ -368,11 +375,13 @@ struct HintState
        JoinMethodHint **join_hints;            /* parsed join hints */
        int                             init_join_mask;         /* initial value join parameter */
        List              **join_hint_level;
+       List              **memoize_hint_level;
        LeadingHint       **leading_hint;               /* parsed Leading hints */
        SetHint           **set_hints;                  /* parsed Set hints */
        GucContext              context;                        /* which GUC parameters can we set? */
        RowsHint          **rows_hints;                 /* parsed Rows hints */
        ParallelHint  **parallel_hints;         /* parsed Parallel hints */
+       JoinMethodHint **memoize_hints;         /* parsed Memoize hints */
 };
 
 /*
@@ -453,6 +462,9 @@ static int ParallelHintCmp(const ParallelHint *a, const ParallelHint *b);
 static const char *ParallelHintParse(ParallelHint *hint, HintState *hstate,
                                                                         Query *parse, const char *str);
 
+static Hint *MemoizeHintCreate(const char *hint_str, const char *keyword,
+                                                          HintKeyword hint_keyword);
+
 static void quote_value(StringInfo buf, const char *value);
 
 static const char *parse_quoted_value(const char *str, char **word,
@@ -588,6 +600,8 @@ static const HintParser parsers[] = {
        {HINT_SET, SetHintCreate, HINT_KEYWORD_SET},
        {HINT_ROWS, RowsHintCreate, HINT_KEYWORD_ROWS},
        {HINT_PARALLEL, ParallelHintCreate, HINT_KEYWORD_PARALLEL},
+       {HINT_MEMOIZE, MemoizeHintCreate, HINT_KEYWORD_MEMOIZE},
+       {HINT_NOMEMOIZE, MemoizeHintCreate, HINT_KEYWORD_NOMEMOIZE},
 
        {NULL, NULL, HINT_KEYWORD_UNRECOGNIZED}
 };
@@ -944,6 +958,23 @@ ParallelHintDelete(ParallelHint *hint)
 }
 
 
+static Hint *
+MemoizeHintCreate(const char *hint_str, const char *keyword,
+                                 HintKeyword hint_keyword)
+{
+       /*
+        * MemoizeHintCreate shares the same struct with JoinMethodHint and the
+        * only difference is the hint type.
+        */
+       JoinMethodHint *hint =
+               (JoinMethodHint *)JoinMethodHintCreate(hint_str, keyword, hint_keyword);
+
+       hint->base.type = HINT_TYPE_MEMOIZE;
+
+       return (Hint *) hint;
+}
+
+
 static HintState *
 HintStateCreate(void)
 {
@@ -970,6 +1001,7 @@ HintStateCreate(void)
        hstate->join_hints = NULL;
        hstate->init_join_mask = 0;
        hstate->join_hint_level = NULL;
+       hstate->memoize_hint_level = NULL;
        hstate->leading_hint = NULL;
        hstate->context = superuser() ? PGC_SUSET : PGC_USERSET;
        hstate->set_hints = NULL;
@@ -1940,6 +1972,8 @@ create_hintstate(Query *parse, const char *hints)
                hstate->num_hints[HINT_TYPE_SET]);
        hstate->parallel_hints = (ParallelHint **) (hstate->rows_hints +
                hstate->num_hints[HINT_TYPE_ROWS]);
+       hstate->memoize_hints = (JoinMethodHint **) (hstate->parallel_hints +
+               hstate->num_hints[HINT_TYPE_PARALLEL]);
 
        return hstate;
 }
@@ -2103,6 +2137,10 @@ JoinMethodHintParse(JoinMethodHint *hint, HintState *hstate, Query *parse,
                case HINT_KEYWORD_NOHASHJOIN:
                        hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_HASHJOIN;
                        break;
+               case HINT_KEYWORD_MEMOIZE:
+               case HINT_KEYWORD_NOMEMOIZE:
+                       /* nothing to do here */
+                       break;
                default:
                        hint_ereport(str, ("Unrecognized hint keyword \"%s\".", keyword));
                        return NULL;
@@ -2431,6 +2469,8 @@ get_current_join_mask()
                mask |= ENABLE_MERGEJOIN;
        if (enable_hashjoin)
                mask |= ENABLE_HASHJOIN;
+       if (enable_memoize)
+               mask |= ENABLE_MEMOIZE;
 
        return mask;
 }
@@ -2621,7 +2661,8 @@ setup_scan_method_enforcement(ScanMethodHint *scanhint, HintState *state)
 }
 
 static void
-set_join_config_options(unsigned char enforce_mask, GucContext context)
+set_join_config_options(unsigned char enforce_mask, bool set_memoize,
+                                               GucContext context)
 {
        unsigned char   mask;
 
@@ -2635,6 +2676,9 @@ set_join_config_options(unsigned char enforce_mask, GucContext context)
        SET_CONFIG_OPTION("enable_mergejoin", ENABLE_MERGEJOIN);
        SET_CONFIG_OPTION("enable_hashjoin", ENABLE_HASHJOIN);
 
+       if (set_memoize)
+               SET_CONFIG_OPTION("enable_memoize", ENABLE_MEMOIZE);
+
        /*
         * Hash join may be rejected for the reason of estimated memory usage. Try
         * getting rid of that limitation.
@@ -3811,6 +3855,30 @@ find_join_hint(Relids joinrelids)
        return NULL;
 }
 
+
+/*
+ * Return memoize hint which matches given joinrelids.
+ */
+static JoinMethodHint *
+find_memoize_hint(Relids joinrelids)
+{
+       List       *join_hint;
+       ListCell   *l;
+
+       join_hint = current_hint_state->memoize_hint_level[bms_num_members(joinrelids)];
+
+       foreach(l, join_hint)
+       {
+               JoinMethodHint *hint = (JoinMethodHint *) lfirst(l);
+
+               if (bms_equal(joinrelids, hint->joinrelids))
+                       return hint;
+       }
+
+       return NULL;
+}
+
+
 static Relids
 OuterInnerJoinCreate(OuterInnerRels *outer_inner, LeadingHint *leading_hint,
        PlannerInfo *root, List *initial_rels, HintState *hstate, int nbaserel)
@@ -3966,6 +4034,24 @@ transform_join_hints(HintState *hstate, PlannerInfo *root, int nbaserel,
                        lappend(hstate->join_hint_level[hint->nrels], hint);
        }
 
+       /* ditto for memoize hints */
+       for (i = 0; i < hstate->num_hints[HINT_TYPE_MEMOIZE]; i++)
+       {
+               JoinMethodHint *hint = hstate->join_hints[i];
+
+               if (!hint_state_enabled(hint) || hint->nrels > nbaserel)
+                       continue;
+
+               hint->joinrelids = create_bms_of_relids(&(hint->base), root,
+                                                                        initial_rels, hint->nrels, hint->relnames);
+
+               if (hint->joinrelids == NULL || hint->base.state == HINT_STATE_ERROR)
+                       continue;
+
+               hstate->memoize_hint_level[hint->nrels] =
+                       lappend(hstate->memoize_hint_level[hint->nrels], hint);
+       }
+
        /*
         * Create bitmap of relids from alias names for each rows hint.
         * Bitmaps are more handy than strings in join searching.
@@ -4171,7 +4257,8 @@ transform_join_hints(HintState *hstate, PlannerInfo *root, int nbaserel,
 
        if (hint_state_enabled(lhint))
        {
-               set_join_config_options(DISABLE_ALL_JOIN, current_hint_state->context);
+               set_join_config_options(DISABLE_ALL_JOIN, false,
+                                                               current_hint_state->context);
                return true;
        }
        return false;
@@ -4187,34 +4274,57 @@ static RelOptInfo *
 make_join_rel_wrapper(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
 {
        Relids                  joinrelids;
-       JoinMethodHint *hint;
+       JoinMethodHint *join_hint;
+       JoinMethodHint *memoize_hint;
        RelOptInfo         *rel;
        int                             save_nestlevel;
 
        joinrelids = bms_union(rel1->relids, rel2->relids);
-       hint = find_join_hint(joinrelids);
+       join_hint = find_join_hint(joinrelids);
+       memoize_hint = find_memoize_hint(joinrelids);
        bms_free(joinrelids);
 
-       if (!hint)
-               return pg_hint_plan_make_join_rel(root, rel1, rel2);
+       /* reject non-matching hints */
+       if (join_hint && join_hint->inner_nrels != 0)
+               join_hint = NULL;
+       
+       if (memoize_hint && memoize_hint->inner_nrels != 0)
+               memoize_hint = NULL;
 
-       if (hint->inner_nrels == 0)
+       if (join_hint || memoize_hint)
        {
                save_nestlevel = NewGUCNestLevel();
 
-               set_join_config_options(hint->enforce_mask,
-                                                               current_hint_state->context);
+               if (join_hint)
+                       set_join_config_options(join_hint->enforce_mask, false,
+                                                                       current_hint_state->context);
 
-               rel = pg_hint_plan_make_join_rel(root, rel1, rel2);
-               hint->base.state = HINT_STATE_USED;
+               if (memoize_hint)
+               {
+                       bool memoize =
+                               memoize_hint->base.hint_keyword == HINT_KEYWORD_MEMOIZE;
+                       set_config_option_noerror("enable_memoize",
+                                                                         memoize ? "true" : "false",
+                                                                         current_hint_state->context,
+                                                                         PGC_S_SESSION, GUC_ACTION_SAVE,
+                                                                         true, ERROR);
+               }
+       }
+
+       /* do the work */
+       rel = pg_hint_plan_make_join_rel(root, rel1, rel2);
+
+       /* Restore the GUC variables we set above. */
+       if (join_hint || memoize_hint)
+       {
+               if (join_hint)
+                       join_hint->base.state = HINT_STATE_USED;
+
+               if (memoize_hint)
+                       memoize_hint->base.state = HINT_STATE_USED;
 
-               /*
-                * Restore the GUC variables we set above.
-                */
                AtEOXact_GUC(true, save_nestlevel);
        }
-       else
-               rel = pg_hint_plan_make_join_rel(root, rel1, rel2);
 
        return rel;
 }
@@ -4233,42 +4343,63 @@ add_paths_to_joinrel_wrapper(PlannerInfo *root,
 {
        Relids                  joinrelids;
        JoinMethodHint *join_hint;
+       JoinMethodHint *memoize_hint;
        int                             save_nestlevel;
 
        joinrelids = bms_union(outerrel->relids, innerrel->relids);
        join_hint = find_join_hint(joinrelids);
+       memoize_hint = find_memoize_hint(joinrelids);
        bms_free(joinrelids);
 
-       if (join_hint && join_hint->inner_nrels != 0)
+       /* reject the found hints if they don't match this join */
+       if (join_hint && join_hint->inner_nrels == 0)
+               join_hint = NULL;
+
+       if (memoize_hint && memoize_hint->inner_nrels == 0)
+               memoize_hint = NULL;
+
+       /* set up configuration if needed */
+       if (join_hint || memoize_hint)
        {
                save_nestlevel = NewGUCNestLevel();
 
-               if (bms_equal(join_hint->inner_joinrelids, innerrel->relids))
+               if (join_hint)
                {
-
-                       set_join_config_options(join_hint->enforce_mask,
-                                                                       current_hint_state->context);
-
-                       add_paths_to_joinrel(root, joinrel, outerrel, innerrel, jointype,
-                                                                sjinfo, restrictlist);
-                       join_hint->base.state = HINT_STATE_USED;
+                       if (bms_equal(join_hint->inner_joinrelids, innerrel->relids))
+                               set_join_config_options(join_hint->enforce_mask, false,
+                                                                               current_hint_state->context);
+                       else
+                               set_join_config_options(DISABLE_ALL_JOIN, false,
+                                                                               current_hint_state->context);
                }
-               else
+
+               if (memoize_hint)
                {
-                       set_join_config_options(DISABLE_ALL_JOIN,
-                                                                       current_hint_state->context);
-                       add_paths_to_joinrel(root, joinrel, outerrel, innerrel, jointype,
-                                                                sjinfo, restrictlist);
+                       bool memoize =
+                               memoize_hint->base.hint_keyword == HINT_KEYWORD_MEMOIZE;
+                       set_config_option_noerror("enable_memoize",
+                                                                         memoize ? "true" : "false",
+                                                                         current_hint_state->context,
+                                                                         PGC_S_SESSION, GUC_ACTION_SAVE,
+                                                                         true, ERROR);
                }
+       }
+
+       /* generate paths */
+       add_paths_to_joinrel(root, joinrel, outerrel, innerrel, jointype,
+                                                sjinfo, restrictlist);
+
+       /* restore GUC variables */
+       if (join_hint || memoize_hint)
+       {
+               if (join_hint)
+                       join_hint->base.state = HINT_STATE_USED;
+
+               if (memoize_hint)
+                       memoize_hint->base.state = HINT_STATE_USED;
 
-               /*
-                * Restore the GUC variables we set above.
-                */
                AtEOXact_GUC(true, save_nestlevel);
        }
-       else
-               add_paths_to_joinrel(root, joinrel, outerrel, innerrel, jointype,
-                                                        sjinfo, restrictlist);
 }
 
 static int
@@ -4331,6 +4462,8 @@ pg_hint_plan_join_search(PlannerInfo *root, int levels_needed,
        current_hint_state->join_hint_level =
                palloc0(sizeof(List *) * (nbaserel + 1));
        join_method_hints = palloc0(sizeof(JoinMethodHint *) * (nbaserel + 1));
+       current_hint_state->memoize_hint_level =
+               palloc0(sizeof(List *) * (nbaserel + 1));
 
        leading_hint_enable = transform_join_hints(current_hint_state,
                                                                                           root, nbaserel,
@@ -4383,7 +4516,7 @@ pg_hint_plan_join_search(PlannerInfo *root, int levels_needed,
        pfree(join_method_hints);
 
        if (leading_hint_enable)
-               set_join_config_options(current_hint_state->init_join_mask,
+               set_join_config_options(current_hint_state->init_join_mask, true,
                                                                current_hint_state->context);
 
        return rel;
index a52116f..d916068 100644 (file)
@@ -829,3 +829,10 @@ EXPLAIN (COSTS true) SELECT * FROM s1.t1 FULL OUTER JOIN s1.t2 ON (t1.c1 = t2.c1
 \o
 \! sql/maskout.sh results/ut-J.tmpout
 \! rm results/ut-J.tmpout
+
+-- Memoize
+EXPLAIN (COSTS false) SELECT * FROM t1, t2, t3 WHERE t1.val = t2.val and t2.id = t3.id;
+/*+ nomemoize(t1 t2)*/
+EXPLAIN (COSTS false) SELECT * FROM t1, t2, t3 WHERE t1.val = t2.val and t2.id = t3.id;  -- doesn't work
+/*+ nomemoize(t1 t2 t3)*/
+EXPLAIN (COSTS false) SELECT * FROM t1, t2, t3 WHERE t1.val = t2.val and t2.id = t3.id;