• R/O
  • HTTP
  • SSH
  • HTTPS

pg_hint_plan: Commit

firtst release


Commit MetaInfo

Revision3d8c7b8a51c93409fd9c22a730e60ee13456fdbe (tree)
Time2021-10-06 11:57:29
AuthorKyotaro Horiguchi <horikyota.ntt@gmai...>
CommiterKyotaro Horiguchi

Log Message

Support "Memoize" Hint.

PostgreSQL 14 introduced the new optimization item "memoize", which
allows a join to memoize the inner result. Add a hint to control this
feature.

Change Summary

Incremental Difference

--- a/doc/hint_list-ja.html
+++ b/doc/hint_list-ja.html
@@ -74,6 +74,13 @@ PostgreSQL 9.2以降で動作します。</td></tr>
7474 <tr><td nowrap>Leading((テーブル集合<Sup>注1</Sup> テーブル集合<Sup>注1</Sup>))</td>
7575 <td>1つ目に指定したテーブル集合を外部表として、2つ目に指定したテーブル集合を内部表として結合します。書式中のテーブル集合はテーブルもしくは別のテーブル集合です。</td></tr>
7676
77+<tr><td rowspan="2">結合時の挙動制御</td>
78+ <td nowrap>Memoize(テーブル テーブル[ テーブル...])</td>
79+ <td>指定したテーブル間の結合の最上位のインナープランの結果キャッシング(memoize)を有効にします。強制するわけではないことに注意してください。</td></tr>
80+<tr>
81+ <td nowrap>NoMemoize(テーブル テーブル[ テーブル...])</td>
82+ <td>指定したテーブル間の結合の最上位のインナープランの結果キャッシング(memoize)を無効にします。</td></tr>
83+
7784 <tr><td>見積もり件数補正</td>
7885 <td nowrap>Rows(テーブル テーブル[ テーブル...] 件数補正)</td>
7986 <td>指定したテーブル間の結合結果の見積もり件数を補正します。件数補正として指定できるのは以下の4パターンです。
--- a/doc/hint_list.html
+++ b/doc/hint_list.html
@@ -10,7 +10,7 @@
1010 </head>
1111
1212 <body>
13-<h1 id="pg_hint_plan">pg_hint_plan 1.3 Appendices</h1>
13+<h1 id="pg_hint_plan">pg_hint_plan 1.4 Appendices</h1>
1414 <div class="navigation">
1515 <a href="pg_hint_plan.html">pg_hint_plan</a> &gt;
1616 <a href="hint_list.html">Appendix A. Hints list</a>
@@ -71,6 +71,13 @@
7171 <tr><td nowrap>Leading(&lt;join pair&gt;)</td>
7272 <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>
7373
74+<tr><td rowspan="2">Behavior control on Join</td>
75+ <td nowrap>Memoize(table table[ table...])</td>
76+ <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>
77+<tr><td nowrap>NoMemoize(table table[ table...])</td>
78+ <td nowrap>Inhibit the topmost join of a join among the specified tables from memoizing the inner result.</td>
79+</tr>
80+
7481 <tr><td>Row number correction</td>
7582 <td nowrap>Rows(table table[ table...] correction)</td>
7683 <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>
--- a/doc/pg_hint_plan-ja.html
+++ b/doc/pg_hint_plan-ja.html
@@ -10,7 +10,7 @@
1010 </head>
1111
1212 <body>
13-<h1 id="pg_hint_plan">pg_hint_plan 1.3</h1>
13+<h1 id="pg_hint_plan">pg_hint_plan 1.4</h1>
1414 <div class="navigation">
1515 <a href="pg_hint_plan-ja.html">pg_hint_plan</a>
1616 </div>
@@ -277,6 +277,24 @@ postgres-# ORDER BY a.aid;
277277 <span class="strong">postgres=# /*+ Leading((t1 (t2 t3))) */</span> SELECT...
278278 </pre>
279279 <p>この書式では2つの要素を丸括弧で囲ったものがネストする形になっており、一つの括弧内では1つ目の要素が外部/駆動表、2番めの要素が内部/被駆動表として結合されます。</p>
280+<h4>結合の挙動制御</h4>
281+<p>あるオブジェクトの組み合わせ最上位結合の挙動を制御するヒント句のグループです。「Memoize」のみを含みます。</p>
282+<p>Memoizeは最上位結合のインナープランの出力結果をキャッシングして高速化を試みる挙動を制御します。</p>
283+<p>以下の例では、テーブルaとテーブルbの結合のインナー側(この例ではa側)のMemoizeを禁止します。</p>
284+
285+<pre>
286+postgres=# /*+ NoMemoize(a b) */
287+EXPLAIN SELECT * FROM a, b WHERE a.val = b.val;
288+ QUERY PLAN
289+--------------------------------------------------------------------
290+ Hash Join (cost=270.00..1412.50 rows=100000 width=16)
291+ Hash Cond: (b.val = a.val)
292+ -> Seq Scan on b (cost=0.00..15.00 rows=1000 width=8)
293+ -> Hash (cost=145.00..145.00 rows=10000 width=8)
294+ -> Seq Scan on a (cost=0.00..145.00 rows=10000 width=8)
295+(5 行)
296+</pre>
297+
280298 <h4>見積もり件数補正</h4>
281299 <p>あるオブジェクトの結合結果の件数を補正できるヒント句のグループです。「Rows」のみを含みます。</p>
282300 <p>見積もり件数補正対象として指定できるオブジェクトは結合方式と同じです。補正できるのは結合結果の見積もり件数だけで、スキャンの見積もり件数を補正することはできません。</p>
--- a/doc/pg_hint_plan.html
+++ b/doc/pg_hint_plan.html
@@ -18,7 +18,7 @@ pre { margin: 0 2em 0 2em; padding:0.3em; border-style: solid; border-width:0.1e
1818 </head>
1919
2020 <body>
21-<h1 id="pg_hint_plan">pg_hint_plan 1.3</h1>
21+<h1 id="pg_hint_plan">pg_hint_plan 1.4</h1>
2222 <div class="navigation">
2323 <a href="pg_hint_plan.html">pg_hint_plan</a>
2424 </div>
@@ -153,6 +153,22 @@ during CREATE EXTENSION. Table hints are prioritized than comment hits.</p>
153153 <span class="strong">postgres-#</span> JOIN table table3 t3 ON (t2.key = t3.key);
154154 </pre>
155155
156+<h4>Hint for restricting join behavior</h4>
157+<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>
158+<pre>
159+postgres=# /*+ NoMemoize(a b) */
160+EXPLAIN SELECT * FROM a, b WHERE a.val = b.val;
161+ QUERY PLAN
162+--------------------------------------------------------------------
163+ Hash Join (cost=270.00..1412.50 rows=100000 width=16)
164+ Hash Cond: (b.val = a.val)
165+ -> Seq Scan on b (cost=0.00..15.00 rows=1000 width=8)
166+ -> Hash (cost=145.00..145.00 rows=10000 width=8)
167+ -> Seq Scan on a (cost=0.00..145.00 rows=10000 width=8)
168+</pre>
169+
170+
171+
156172 <h4>Hint for row number correction</h4>
157173 <p>This hint "Rows" corrects row number misestimation of joins that comes from restrictions of the planner. </p>
158174
--- a/expected/ut-J.out
+++ b/expected/ut-J.out
@@ -4717,3 +4717,68 @@ error hint:
47174717 -> Seq Scan on t2 (cost=xxx..xxx rows=100 width=xxx)
47184718
47194719 \! rm results/ut-J.tmpout
4720+-- Memoize
4721+EXPLAIN (COSTS false) SELECT * FROM t1, t2, t3 WHERE t1.val = t2.val and t2.id = t3.id;
4722+ QUERY PLAN
4723+--------------------------------------------
4724+ Nested Loop
4725+ -> Merge Join
4726+ Merge Cond: (t2.id = t3.id)
4727+ -> Index Scan using t2_pkey on t2
4728+ -> Sort
4729+ Sort Key: t3.id
4730+ -> Seq Scan on t3
4731+ -> Memoize
4732+ Cache Key: t2.val
4733+ -> Index Scan using t1_val on t1
4734+ Index Cond: (val = t2.val)
4735+(11 rows)
4736+
4737+/*+ nomemoize(t1 t2)*/
4738+EXPLAIN (COSTS false) SELECT * FROM t1, t2, t3 WHERE t1.val = t2.val and t2.id = t3.id; -- doesn't work
4739+LOG: pg_hint_plan:
4740+used hint:
4741+NoMemoize(t1 t2)
4742+not used hint:
4743+duplication hint:
4744+error hint:
4745+
4746+ QUERY PLAN
4747+--------------------------------------------
4748+ Nested Loop
4749+ -> Merge Join
4750+ Merge Cond: (t2.id = t3.id)
4751+ -> Index Scan using t2_pkey on t2
4752+ -> Sort
4753+ Sort Key: t3.id
4754+ -> Seq Scan on t3
4755+ -> Memoize
4756+ Cache Key: t2.val
4757+ -> Index Scan using t1_val on t1
4758+ Index Cond: (val = t2.val)
4759+(11 rows)
4760+
4761+/*+ nomemoize(t1 t2 t3)*/
4762+EXPLAIN (COSTS false) SELECT * FROM t1, t2, t3 WHERE t1.val = t2.val and t2.id = t3.id;
4763+LOG: pg_hint_plan:
4764+used hint:
4765+NoMemoize(t1 t2 t3)
4766+not used hint:
4767+duplication hint:
4768+error hint:
4769+
4770+ QUERY PLAN
4771+--------------------------------------------------
4772+ Merge Join
4773+ Merge Cond: (t1.val = t2.val)
4774+ -> Index Scan using t1_val on t1
4775+ -> Sort
4776+ Sort Key: t2.val
4777+ -> Merge Join
4778+ Merge Cond: (t2.id = t3.id)
4779+ -> Index Scan using t2_pkey on t2
4780+ -> Sort
4781+ Sort Key: t3.id
4782+ -> Seq Scan on t3
4783+(11 rows)
4784+
--- a/pg_hint_plan.c
+++ b/pg_hint_plan.c
@@ -94,6 +94,8 @@ PG_MODULE_MAGIC;
9494 #define HINT_LEADING "Leading"
9595 #define HINT_SET "Set"
9696 #define HINT_ROWS "Rows"
97+#define HINT_MEMOIZE "Memoize"
98+#define HINT_NOMEMOIZE "NoMemoize"
9799
98100 #define HINT_ARRAY_DEFAULT_INITSIZE 8
99101
@@ -122,7 +124,8 @@ enum
122124 {
123125 ENABLE_NESTLOOP = 0x01,
124126 ENABLE_MERGEJOIN = 0x02,
125- ENABLE_HASHJOIN = 0x04
127+ ENABLE_HASHJOIN = 0x04,
128+ ENABLE_MEMOIZE = 0x08
126129 } JOIN_TYPE_BITS;
127130
128131 #define ENABLE_ALL_SCAN (ENABLE_SEQSCAN | ENABLE_INDEXSCAN | \
@@ -160,6 +163,8 @@ typedef enum HintKeyword
160163 HINT_KEYWORD_SET,
161164 HINT_KEYWORD_ROWS,
162165 HINT_KEYWORD_PARALLEL,
166+ HINT_KEYWORD_MEMOIZE,
167+ HINT_KEYWORD_NOMEMOIZE,
163168
164169 HINT_KEYWORD_UNRECOGNIZED
165170 } HintKeyword;
@@ -186,7 +191,6 @@ typedef const char *(*HintParseFunction) (Hint *hint, HintState *hstate,
186191 Query *parse, const char *str);
187192
188193 /* hint types */
189-#define NUM_HINT_TYPE 6
190194 typedef enum HintType
191195 {
192196 HINT_TYPE_SCAN_METHOD,
@@ -194,7 +198,10 @@ typedef enum HintType
194198 HINT_TYPE_LEADING,
195199 HINT_TYPE_SET,
196200 HINT_TYPE_ROWS,
197- HINT_TYPE_PARALLEL
201+ HINT_TYPE_PARALLEL,
202+ HINT_TYPE_MEMOIZE,
203+
204+ NUM_HINT_TYPE
198205 } HintType;
199206
200207 typedef enum HintTypeBitmap
@@ -368,11 +375,13 @@ struct HintState
368375 JoinMethodHint **join_hints; /* parsed join hints */
369376 int init_join_mask; /* initial value join parameter */
370377 List **join_hint_level;
378+ List **memoize_hint_level;
371379 LeadingHint **leading_hint; /* parsed Leading hints */
372380 SetHint **set_hints; /* parsed Set hints */
373381 GucContext context; /* which GUC parameters can we set? */
374382 RowsHint **rows_hints; /* parsed Rows hints */
375383 ParallelHint **parallel_hints; /* parsed Parallel hints */
384+ JoinMethodHint **memoize_hints; /* parsed Memoize hints */
376385 };
377386
378387 /*
@@ -453,6 +462,9 @@ static int ParallelHintCmp(const ParallelHint *a, const ParallelHint *b);
453462 static const char *ParallelHintParse(ParallelHint *hint, HintState *hstate,
454463 Query *parse, const char *str);
455464
465+static Hint *MemoizeHintCreate(const char *hint_str, const char *keyword,
466+ HintKeyword hint_keyword);
467+
456468 static void quote_value(StringInfo buf, const char *value);
457469
458470 static const char *parse_quoted_value(const char *str, char **word,
@@ -588,6 +600,8 @@ static const HintParser parsers[] = {
588600 {HINT_SET, SetHintCreate, HINT_KEYWORD_SET},
589601 {HINT_ROWS, RowsHintCreate, HINT_KEYWORD_ROWS},
590602 {HINT_PARALLEL, ParallelHintCreate, HINT_KEYWORD_PARALLEL},
603+ {HINT_MEMOIZE, MemoizeHintCreate, HINT_KEYWORD_MEMOIZE},
604+ {HINT_NOMEMOIZE, MemoizeHintCreate, HINT_KEYWORD_NOMEMOIZE},
591605
592606 {NULL, NULL, HINT_KEYWORD_UNRECOGNIZED}
593607 };
@@ -944,6 +958,23 @@ ParallelHintDelete(ParallelHint *hint)
944958 }
945959
946960
961+static Hint *
962+MemoizeHintCreate(const char *hint_str, const char *keyword,
963+ HintKeyword hint_keyword)
964+{
965+ /*
966+ * MemoizeHintCreate shares the same struct with JoinMethodHint and the
967+ * only difference is the hint type.
968+ */
969+ JoinMethodHint *hint =
970+ (JoinMethodHint *)JoinMethodHintCreate(hint_str, keyword, hint_keyword);
971+
972+ hint->base.type = HINT_TYPE_MEMOIZE;
973+
974+ return (Hint *) hint;
975+}
976+
977+
947978 static HintState *
948979 HintStateCreate(void)
949980 {
@@ -970,6 +1001,7 @@ HintStateCreate(void)
9701001 hstate->join_hints = NULL;
9711002 hstate->init_join_mask = 0;
9721003 hstate->join_hint_level = NULL;
1004+ hstate->memoize_hint_level = NULL;
9731005 hstate->leading_hint = NULL;
9741006 hstate->context = superuser() ? PGC_SUSET : PGC_USERSET;
9751007 hstate->set_hints = NULL;
@@ -1940,6 +1972,8 @@ create_hintstate(Query *parse, const char *hints)
19401972 hstate->num_hints[HINT_TYPE_SET]);
19411973 hstate->parallel_hints = (ParallelHint **) (hstate->rows_hints +
19421974 hstate->num_hints[HINT_TYPE_ROWS]);
1975+ hstate->memoize_hints = (JoinMethodHint **) (hstate->parallel_hints +
1976+ hstate->num_hints[HINT_TYPE_PARALLEL]);
19431977
19441978 return hstate;
19451979 }
@@ -2103,6 +2137,10 @@ JoinMethodHintParse(JoinMethodHint *hint, HintState *hstate, Query *parse,
21032137 case HINT_KEYWORD_NOHASHJOIN:
21042138 hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_HASHJOIN;
21052139 break;
2140+ case HINT_KEYWORD_MEMOIZE:
2141+ case HINT_KEYWORD_NOMEMOIZE:
2142+ /* nothing to do here */
2143+ break;
21062144 default:
21072145 hint_ereport(str, ("Unrecognized hint keyword \"%s\".", keyword));
21082146 return NULL;
@@ -2431,6 +2469,8 @@ get_current_join_mask()
24312469 mask |= ENABLE_MERGEJOIN;
24322470 if (enable_hashjoin)
24332471 mask |= ENABLE_HASHJOIN;
2472+ if (enable_memoize)
2473+ mask |= ENABLE_MEMOIZE;
24342474
24352475 return mask;
24362476 }
@@ -2621,7 +2661,8 @@ setup_scan_method_enforcement(ScanMethodHint *scanhint, HintState *state)
26212661 }
26222662
26232663 static void
2624-set_join_config_options(unsigned char enforce_mask, GucContext context)
2664+set_join_config_options(unsigned char enforce_mask, bool set_memoize,
2665+ GucContext context)
26252666 {
26262667 unsigned char mask;
26272668
@@ -2635,6 +2676,9 @@ set_join_config_options(unsigned char enforce_mask, GucContext context)
26352676 SET_CONFIG_OPTION("enable_mergejoin", ENABLE_MERGEJOIN);
26362677 SET_CONFIG_OPTION("enable_hashjoin", ENABLE_HASHJOIN);
26372678
2679+ if (set_memoize)
2680+ SET_CONFIG_OPTION("enable_memoize", ENABLE_MEMOIZE);
2681+
26382682 /*
26392683 * Hash join may be rejected for the reason of estimated memory usage. Try
26402684 * getting rid of that limitation.
@@ -3811,6 +3855,30 @@ find_join_hint(Relids joinrelids)
38113855 return NULL;
38123856 }
38133857
3858+
3859+/*
3860+ * Return memoize hint which matches given joinrelids.
3861+ */
3862+static JoinMethodHint *
3863+find_memoize_hint(Relids joinrelids)
3864+{
3865+ List *join_hint;
3866+ ListCell *l;
3867+
3868+ join_hint = current_hint_state->memoize_hint_level[bms_num_members(joinrelids)];
3869+
3870+ foreach(l, join_hint)
3871+ {
3872+ JoinMethodHint *hint = (JoinMethodHint *) lfirst(l);
3873+
3874+ if (bms_equal(joinrelids, hint->joinrelids))
3875+ return hint;
3876+ }
3877+
3878+ return NULL;
3879+}
3880+
3881+
38143882 static Relids
38153883 OuterInnerJoinCreate(OuterInnerRels *outer_inner, LeadingHint *leading_hint,
38163884 PlannerInfo *root, List *initial_rels, HintState *hstate, int nbaserel)
@@ -3966,6 +4034,24 @@ transform_join_hints(HintState *hstate, PlannerInfo *root, int nbaserel,
39664034 lappend(hstate->join_hint_level[hint->nrels], hint);
39674035 }
39684036
4037+ /* ditto for memoize hints */
4038+ for (i = 0; i < hstate->num_hints[HINT_TYPE_MEMOIZE]; i++)
4039+ {
4040+ JoinMethodHint *hint = hstate->join_hints[i];
4041+
4042+ if (!hint_state_enabled(hint) || hint->nrels > nbaserel)
4043+ continue;
4044+
4045+ hint->joinrelids = create_bms_of_relids(&(hint->base), root,
4046+ initial_rels, hint->nrels, hint->relnames);
4047+
4048+ if (hint->joinrelids == NULL || hint->base.state == HINT_STATE_ERROR)
4049+ continue;
4050+
4051+ hstate->memoize_hint_level[hint->nrels] =
4052+ lappend(hstate->memoize_hint_level[hint->nrels], hint);
4053+ }
4054+
39694055 /*
39704056 * Create bitmap of relids from alias names for each rows hint.
39714057 * Bitmaps are more handy than strings in join searching.
@@ -4171,7 +4257,8 @@ transform_join_hints(HintState *hstate, PlannerInfo *root, int nbaserel,
41714257
41724258 if (hint_state_enabled(lhint))
41734259 {
4174- set_join_config_options(DISABLE_ALL_JOIN, current_hint_state->context);
4260+ set_join_config_options(DISABLE_ALL_JOIN, false,
4261+ current_hint_state->context);
41754262 return true;
41764263 }
41774264 return false;
@@ -4187,34 +4274,57 @@ static RelOptInfo *
41874274 make_join_rel_wrapper(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
41884275 {
41894276 Relids joinrelids;
4190- JoinMethodHint *hint;
4277+ JoinMethodHint *join_hint;
4278+ JoinMethodHint *memoize_hint;
41914279 RelOptInfo *rel;
41924280 int save_nestlevel;
41934281
41944282 joinrelids = bms_union(rel1->relids, rel2->relids);
4195- hint = find_join_hint(joinrelids);
4283+ join_hint = find_join_hint(joinrelids);
4284+ memoize_hint = find_memoize_hint(joinrelids);
41964285 bms_free(joinrelids);
41974286
4198- if (!hint)
4199- return pg_hint_plan_make_join_rel(root, rel1, rel2);
4287+ /* reject non-matching hints */
4288+ if (join_hint && join_hint->inner_nrels != 0)
4289+ join_hint = NULL;
4290+
4291+ if (memoize_hint && memoize_hint->inner_nrels != 0)
4292+ memoize_hint = NULL;
42004293
4201- if (hint->inner_nrels == 0)
4294+ if (join_hint || memoize_hint)
42024295 {
42034296 save_nestlevel = NewGUCNestLevel();
42044297
4205- set_join_config_options(hint->enforce_mask,
4206- current_hint_state->context);
4298+ if (join_hint)
4299+ set_join_config_options(join_hint->enforce_mask, false,
4300+ current_hint_state->context);
42074301
4208- rel = pg_hint_plan_make_join_rel(root, rel1, rel2);
4209- hint->base.state = HINT_STATE_USED;
4302+ if (memoize_hint)
4303+ {
4304+ bool memoize =
4305+ memoize_hint->base.hint_keyword == HINT_KEYWORD_MEMOIZE;
4306+ set_config_option_noerror("enable_memoize",
4307+ memoize ? "true" : "false",
4308+ current_hint_state->context,
4309+ PGC_S_SESSION, GUC_ACTION_SAVE,
4310+ true, ERROR);
4311+ }
4312+ }
4313+
4314+ /* do the work */
4315+ rel = pg_hint_plan_make_join_rel(root, rel1, rel2);
4316+
4317+ /* Restore the GUC variables we set above. */
4318+ if (join_hint || memoize_hint)
4319+ {
4320+ if (join_hint)
4321+ join_hint->base.state = HINT_STATE_USED;
4322+
4323+ if (memoize_hint)
4324+ memoize_hint->base.state = HINT_STATE_USED;
42104325
4211- /*
4212- * Restore the GUC variables we set above.
4213- */
42144326 AtEOXact_GUC(true, save_nestlevel);
42154327 }
4216- else
4217- rel = pg_hint_plan_make_join_rel(root, rel1, rel2);
42184328
42194329 return rel;
42204330 }
@@ -4233,42 +4343,63 @@ add_paths_to_joinrel_wrapper(PlannerInfo *root,
42334343 {
42344344 Relids joinrelids;
42354345 JoinMethodHint *join_hint;
4346+ JoinMethodHint *memoize_hint;
42364347 int save_nestlevel;
42374348
42384349 joinrelids = bms_union(outerrel->relids, innerrel->relids);
42394350 join_hint = find_join_hint(joinrelids);
4351+ memoize_hint = find_memoize_hint(joinrelids);
42404352 bms_free(joinrelids);
42414353
4242- if (join_hint && join_hint->inner_nrels != 0)
4354+ /* reject the found hints if they don't match this join */
4355+ if (join_hint && join_hint->inner_nrels == 0)
4356+ join_hint = NULL;
4357+
4358+ if (memoize_hint && memoize_hint->inner_nrels == 0)
4359+ memoize_hint = NULL;
4360+
4361+ /* set up configuration if needed */
4362+ if (join_hint || memoize_hint)
42434363 {
42444364 save_nestlevel = NewGUCNestLevel();
42454365
4246- if (bms_equal(join_hint->inner_joinrelids, innerrel->relids))
4366+ if (join_hint)
42474367 {
4248-
4249- set_join_config_options(join_hint->enforce_mask,
4250- current_hint_state->context);
4251-
4252- add_paths_to_joinrel(root, joinrel, outerrel, innerrel, jointype,
4253- sjinfo, restrictlist);
4254- join_hint->base.state = HINT_STATE_USED;
4368+ if (bms_equal(join_hint->inner_joinrelids, innerrel->relids))
4369+ set_join_config_options(join_hint->enforce_mask, false,
4370+ current_hint_state->context);
4371+ else
4372+ set_join_config_options(DISABLE_ALL_JOIN, false,
4373+ current_hint_state->context);
42554374 }
4256- else
4375+
4376+ if (memoize_hint)
42574377 {
4258- set_join_config_options(DISABLE_ALL_JOIN,
4259- current_hint_state->context);
4260- add_paths_to_joinrel(root, joinrel, outerrel, innerrel, jointype,
4261- sjinfo, restrictlist);
4378+ bool memoize =
4379+ memoize_hint->base.hint_keyword == HINT_KEYWORD_MEMOIZE;
4380+ set_config_option_noerror("enable_memoize",
4381+ memoize ? "true" : "false",
4382+ current_hint_state->context,
4383+ PGC_S_SESSION, GUC_ACTION_SAVE,
4384+ true, ERROR);
42624385 }
4386+ }
4387+
4388+ /* generate paths */
4389+ add_paths_to_joinrel(root, joinrel, outerrel, innerrel, jointype,
4390+ sjinfo, restrictlist);
4391+
4392+ /* restore GUC variables */
4393+ if (join_hint || memoize_hint)
4394+ {
4395+ if (join_hint)
4396+ join_hint->base.state = HINT_STATE_USED;
4397+
4398+ if (memoize_hint)
4399+ memoize_hint->base.state = HINT_STATE_USED;
42634400
4264- /*
4265- * Restore the GUC variables we set above.
4266- */
42674401 AtEOXact_GUC(true, save_nestlevel);
42684402 }
4269- else
4270- add_paths_to_joinrel(root, joinrel, outerrel, innerrel, jointype,
4271- sjinfo, restrictlist);
42724403 }
42734404
42744405 static int
@@ -4331,6 +4462,8 @@ pg_hint_plan_join_search(PlannerInfo *root, int levels_needed,
43314462 current_hint_state->join_hint_level =
43324463 palloc0(sizeof(List *) * (nbaserel + 1));
43334464 join_method_hints = palloc0(sizeof(JoinMethodHint *) * (nbaserel + 1));
4465+ current_hint_state->memoize_hint_level =
4466+ palloc0(sizeof(List *) * (nbaserel + 1));
43344467
43354468 leading_hint_enable = transform_join_hints(current_hint_state,
43364469 root, nbaserel,
@@ -4383,7 +4516,7 @@ pg_hint_plan_join_search(PlannerInfo *root, int levels_needed,
43834516 pfree(join_method_hints);
43844517
43854518 if (leading_hint_enable)
4386- set_join_config_options(current_hint_state->init_join_mask,
4519+ set_join_config_options(current_hint_state->init_join_mask, true,
43874520 current_hint_state->context);
43884521
43894522 return rel;
--- a/sql/ut-J.sql
+++ b/sql/ut-J.sql
@@ -829,3 +829,10 @@ EXPLAIN (COSTS true) SELECT * FROM s1.t1 FULL OUTER JOIN s1.t2 ON (t1.c1 = t2.c1
829829 \o
830830 \! sql/maskout.sh results/ut-J.tmpout
831831 \! rm results/ut-J.tmpout
832+
833+-- Memoize
834+EXPLAIN (COSTS false) SELECT * FROM t1, t2, t3 WHERE t1.val = t2.val and t2.id = t3.id;
835+/*+ nomemoize(t1 t2)*/
836+EXPLAIN (COSTS false) SELECT * FROM t1, t2, t3 WHERE t1.val = t2.val and t2.id = t3.id; -- doesn't work
837+/*+ nomemoize(t1 t2 t3)*/
838+EXPLAIN (COSTS false) SELECT * FROM t1, t2, t3 WHERE t1.val = t2.val and t2.id = t3.id;
Show on old repository browser