1 /*-------------------------------------------------------------------------
4 * do instructions or hints to the planner using C-style block comments
7 * Copyright (c) 2012, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
9 *-------------------------------------------------------------------------
12 #include "miscadmin.h"
13 #include "optimizer/cost.h"
14 #include "optimizer/geqo.h"
15 #include "optimizer/joininfo.h"
16 #include "optimizer/pathnode.h"
17 #include "optimizer/paths.h"
18 #include "optimizer/plancat.h"
19 #include "optimizer/planner.h"
20 #include "tcop/tcopprot.h"
21 #include "utils/lsyscache.h"
22 #include "utils/memutils.h"
24 #ifdef PG_MODULE_MAGIC
28 #if PG_VERSION_NUM < 90100
29 #error unsupported PostgreSQL version
32 #define BLOCK_COMMENT_START "/*"
33 #define BLOCK_COMMENT_END "*/"
34 #define HINT_COMMENT_KEYWORD "+"
35 #define HINT_START BLOCK_COMMENT_START HINT_COMMENT_KEYWORD
36 #define HINT_END BLOCK_COMMENT_END
39 #define HINT_SEQSCAN "SeqScan"
40 #define HINT_INDEXSCAN "IndexScan"
41 #define HINT_BITMAPSCAN "BitmapScan"
42 #define HINT_TIDSCAN "TidScan"
43 #define HINT_NOSEQSCAN "NoSeqScan"
44 #define HINT_NOINDEXSCAN "NoIndexScan"
45 #define HINT_NOBITMAPSCAN "NoBitmapScan"
46 #define HINT_NOTIDSCAN "NoTidScan"
47 #if PG_VERSION_NUM >= 90200
48 #define HINT_INDEXONLYSCAN "IndexonlyScan"
49 #define HINT_NOINDEXONLYSCAN "NoIndexonlyScan"
51 #define HINT_NESTLOOP "NestLoop"
52 #define HINT_MERGEJOIN "MergeJoin"
53 #define HINT_HASHJOIN "HashJoin"
54 #define HINT_NONESTLOOP "NoNestLoop"
55 #define HINT_NOMERGEJOIN "NoMergeJoin"
56 #define HINT_NOHASHJOIN "NoHashJoin"
57 #define HINT_LEADING "Leading"
58 #define HINT_SET "Set"
61 #define HINT_ARRAY_DEFAULT_INITSIZE 8
63 #define parse_ereport(str, detail) \
64 ereport(pg_hint_plan_parse_messages, \
65 (errmsg("hint syntax error at or near \"%s\"", (str)), \
68 #define skip_space(str) \
69 while (isspace(*str)) \
74 ENABLE_SEQSCAN = 0x01,
75 ENABLE_INDEXSCAN = 0x02,
76 ENABLE_BITMAPSCAN = 0x04,
77 ENABLE_TIDSCAN = 0x08,
78 #if PG_VERSION_NUM >= 90200
79 ENABLE_INDEXONLYSCAN = 0x10,
81 ENABLE_NESTLOOP = 0x20,
82 ENABLE_MERGEJOIN = 0x40,
83 ENABLE_HASHJOIN = 0x80
86 #define ENABLE_ALL_SCAN (ENABLE_SEQSCAN | ENABLE_INDEXSCAN | ENABLE_BITMAPSCAN \
88 #if PG_VERSION_NUM >= 90200
89 #define ENABLE_ALL_SCAN (ENABLE_SEQSCAN | ENABLE_INDEXSCAN | ENABLE_BITMAPSCAN \
90 | ENABLE_TIDSCAN | ENABLE_INDEXONLYSCAN)
92 #define ENABLE_ALL_JOIN (ENABLE_NESTLOOP | ENABLE_MERGEJOIN | ENABLE_HASHJOIN)
93 #define DISABLE_ALL_SCAN 0
94 #define DISABLE_ALL_JOIN 0
96 typedef struct Hint Hint;
97 typedef struct PlanHint PlanHint;
99 typedef Hint *(*HintCreateFunction) (char *hint_str, char *keyword);
100 typedef void (*HintDeleteFunction) (Hint *hint);
101 typedef const char *(*HintParseFunction) (Hint *hint, PlanHint *plan, Query *parse, const char *str);
104 typedef enum HintStatus
106 HINT_STATE_NOTUSED = 0, /* specified relation not used in query */
107 HINT_STATE_USED, /* hint is used */
108 HINT_STATE_DUPLICATION, /* specified hint duplication */
109 /* execute error (parse error does not include it) */
113 #define hint_state_enabled(hint) ((hint)->base.state == HINT_STATE_NOTUSED || \
114 (hint)->base.state == HINT_STATE_USED)
116 /* common data for all hints. */
119 const char *hint_str; /* must not do pfree */
120 const char *keyword; /* must not do pfree */
122 HintDeleteFunction delete_func;
123 HintParseFunction parser_func;
126 /* scan method hints */
127 typedef struct ScanMethodHint
132 unsigned char enforce_mask;
135 /* join method hints */
136 typedef struct JoinMethodHint
141 unsigned char enforce_mask;
145 /* join order hints */
146 typedef struct LeadingHint
149 List *relations; /* relation names specified in Leading hint */
152 /* change a run-time parameter hints */
153 typedef struct SetHint
156 char *name; /* name of variable */
161 * Describes a context of hint processing.
165 char *hint_str; /* original hint string */
167 /* for scan method hints */
168 int nscan_hints; /* # of valid scan hints */
169 int max_scan_hints; /* # of slots for scan hints */
170 ScanMethodHint **scan_hints; /* parsed scan hints */
171 int init_scan_mask; /* initial value scan parameter */
173 /* for join method hints */
174 int njoin_hints; /* # of valid join hints */
175 int max_join_hints; /* # of slots for join hints */
176 JoinMethodHint **join_hints; /* parsed join hints */
177 int init_join_mask; /* initial value join parameter */
179 int nlevel; /* # of relations to be joined */
180 List **join_hint_level;
182 /* for Leading hints */
183 int nleading_hints; /* # of valid leading hints */
184 int max_leading_hints; /* # of slots for leading hints */
185 LeadingHint **leading_hints; /* parsed Leading hints */
188 GucContext context; /* which GUC parameters can we set? */
189 int nset_hints; /* # of valid set hints */
190 int max_set_hints; /* # of slots for set hints */
191 SetHint **set_hints; /* parsed Set hints */
195 * Describes a hint parser module which is bound with particular hint keyword.
197 typedef struct HintParser
201 HintCreateFunction create_func;
204 /* Module callbacks */
208 static PlannedStmt *pg_hint_plan_planner(Query *parse, int cursorOptions,
209 ParamListInfo boundParams);
210 static void pg_hint_plan_get_relation_info(PlannerInfo *root, Oid relationObjectId,
211 bool inhparent, RelOptInfo *rel);
212 static RelOptInfo *pg_hint_plan_join_search(PlannerInfo *root, int levels_needed,
215 static Hint *ScanMethodHintCreate(char *hint_str, char *keyword);
216 static void ScanMethodHintDelete(ScanMethodHint *hint);
217 static const char *ScanMethodHintParse(ScanMethodHint *hint, PlanHint *plan, Query *parse, const char *str);
218 static Hint *JoinMethodHintCreate(char *hint_str, char *keyword);
219 static void JoinMethodHintDelete(JoinMethodHint *hint);
220 static const char *JoinMethodHintParse(JoinMethodHint *hint, PlanHint *plan, Query *parse, const char *str);
221 static Hint *LeadingHintCreate(char *hint_str, char *keyword);
222 static void LeadingHintDelete(LeadingHint *hint);
223 static const char *LeadingHintParse(LeadingHint *hint, PlanHint *plan, Query *parse, const char *str);
224 static Hint *SetHintCreate(char *hint_str, char *keyword);
225 static void SetHintDelete(SetHint *hint);
226 static const char *SetHintParse(SetHint *hint, PlanHint *plan, Query *parse, const char *str);
228 RelOptInfo *pg_hint_plan_standard_join_search(PlannerInfo *root, int levels_needed, List *initial_rels);
229 void pg_hint_plan_join_search_one_level(PlannerInfo *root, int level);
230 static void make_rels_by_clause_joins(PlannerInfo *root, RelOptInfo *old_rel, ListCell *other_rels);
231 static void make_rels_by_clauseless_joins(PlannerInfo *root, RelOptInfo *old_rel, ListCell *other_rels);
232 static bool has_join_restriction(PlannerInfo *root, RelOptInfo *rel);
233 static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte);
234 RelOptInfo *pg_hint_plan_make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2);
238 static bool pg_hint_plan_enable = true;
239 static bool pg_hint_plan_debug_print = false;
240 static int pg_hint_plan_parse_messages = INFO;
242 static const struct config_enum_entry parse_messages_level_options[] = {
243 {"debug", DEBUG2, true},
244 {"debug5", DEBUG5, false},
245 {"debug4", DEBUG4, false},
246 {"debug3", DEBUG3, false},
247 {"debug2", DEBUG2, false},
248 {"debug1", DEBUG1, false},
250 {"info", INFO, false},
251 {"notice", NOTICE, false},
252 {"warning", WARNING, false},
253 {"error", ERROR, false},
255 {"fatal", FATAL, true},
256 {"panic", PANIC, true},
261 /* Saved hook values in case of unload */
262 static planner_hook_type prev_planner_hook = NULL;
263 static get_relation_info_hook_type prev_get_relation_info = NULL;
264 static join_search_hook_type prev_join_search = NULL;
266 /* フック関数をまたがって使用する情報を管理する */
267 static PlanHint *global = NULL;
269 static const HintParser parsers[] = {
270 {HINT_SEQSCAN, true, ScanMethodHintCreate},
271 {HINT_INDEXSCAN, true, ScanMethodHintCreate},
272 {HINT_BITMAPSCAN, true, ScanMethodHintCreate},
273 {HINT_TIDSCAN, true, ScanMethodHintCreate},
274 {HINT_NOSEQSCAN, true, ScanMethodHintCreate},
275 {HINT_NOINDEXSCAN, true, ScanMethodHintCreate},
276 {HINT_NOBITMAPSCAN, true, ScanMethodHintCreate},
277 {HINT_NOTIDSCAN, true, ScanMethodHintCreate},
278 #if PG_VERSION_NUM >= 90200
279 {HINT_INDEXONLYSCAN, true, ScanMethodHintCreate},
280 {HINT_NOINDEXONLYSCAN, true, ScanMethodHintCreate},
282 {HINT_NESTLOOP, true, JoinMethodHintCreate},
283 {HINT_MERGEJOIN, true, JoinMethodHintCreate},
284 {HINT_HASHJOIN, true, JoinMethodHintCreate},
285 {HINT_NONESTLOOP, true, JoinMethodHintCreate},
286 {HINT_NOMERGEJOIN, true, JoinMethodHintCreate},
287 {HINT_NOHASHJOIN, true, JoinMethodHintCreate},
288 {HINT_LEADING, true, LeadingHintCreate},
289 {HINT_SET, true, SetHintCreate},
294 * Module load callbacks
299 /* Define custom GUC variables. */
300 DefineCustomBoolVariable("pg_hint_plan.enable",
301 "Instructions or hints to the planner using block comments.",
303 &pg_hint_plan_enable,
311 DefineCustomBoolVariable("pg_hint_plan.debug_print",
312 "Logs each query's parse results of the hint.",
314 &pg_hint_plan_debug_print,
322 DefineCustomEnumVariable("pg_hint_plan.parse_messages",
323 "Messege level of the parse error.",
325 &pg_hint_plan_parse_messages,
327 parse_messages_level_options,
335 prev_planner_hook = planner_hook;
336 planner_hook = pg_hint_plan_planner;
337 prev_get_relation_info = get_relation_info_hook;
338 get_relation_info_hook = pg_hint_plan_get_relation_info;
339 prev_join_search = join_search_hook;
340 join_search_hook = pg_hint_plan_join_search;
344 * Module unload callback
350 /* Uninstall hooks. */
351 planner_hook = prev_planner_hook;
352 get_relation_info_hook = prev_get_relation_info;
353 join_search_hook = prev_join_search;
357 ScanMethodHintCreate(char *hint_str, char *keyword)
359 ScanMethodHint *hint;
361 hint = palloc(sizeof(ScanMethodHint));
362 hint->base.hint_str = hint_str;
363 hint->base.keyword = keyword;
364 hint->base.state = HINT_STATE_NOTUSED;
365 hint->base.delete_func = (HintDeleteFunction) ScanMethodHintDelete;
366 hint->base.parser_func = (HintParseFunction) ScanMethodHintParse;
367 hint->relname = NULL;
368 hint->indexnames = NIL;
369 hint->enforce_mask = 0;
371 return (Hint *) hint;
375 ScanMethodHintDelete(ScanMethodHint *hint)
381 pfree(hint->relname);
382 list_free_deep(hint->indexnames);
387 JoinMethodHintCreate(char *hint_str, char *keyword)
389 JoinMethodHint *hint;
391 hint = palloc(sizeof(JoinMethodHint));
392 hint->base.hint_str = hint_str;
393 hint->base.keyword = keyword;
394 hint->base.state = HINT_STATE_NOTUSED;
395 hint->base.delete_func = (HintDeleteFunction) JoinMethodHintDelete;
396 hint->base.parser_func = (HintParseFunction) JoinMethodHintParse;
398 hint->relnames = NULL;
399 hint->enforce_mask = 0;
400 hint->joinrelids = NULL;
402 return (Hint *) hint;
406 JoinMethodHintDelete(JoinMethodHint *hint)
415 for (i = 0; i < hint->nrels; i++)
416 pfree(hint->relnames[i]);
417 pfree(hint->relnames);
419 bms_free(hint->joinrelids);
424 LeadingHintCreate(char *hint_str, char *keyword)
428 hint = palloc(sizeof(LeadingHint));
429 hint->base.hint_str = hint_str;
430 hint->base.keyword = keyword;
431 hint->base.state = HINT_STATE_NOTUSED;
432 hint->base.delete_func = (HintDeleteFunction)LeadingHintDelete;
433 hint->base.parser_func = (HintParseFunction) LeadingHintParse;
434 hint->relations = NIL;
436 return (Hint *) hint;
440 LeadingHintDelete(LeadingHint *hint)
445 list_free_deep(hint->relations);
450 SetHintCreate(char *hint_str, char *keyword)
454 hint = palloc(sizeof(SetHint));
455 hint->base.hint_str = hint_str;
456 hint->base.keyword = keyword;
457 hint->base.state = HINT_STATE_NOTUSED;
458 hint->base.delete_func = (HintDeleteFunction) SetHintDelete;
459 hint->base.parser_func = (HintParseFunction) SetHintParse;
463 return (Hint *) hint;
467 SetHintDelete(SetHint *hint)
484 hint = palloc(sizeof(PlanHint));
485 hint->hint_str = NULL;
486 hint->nscan_hints = 0;
487 hint->max_scan_hints = 0;
488 hint->scan_hints = NULL;
489 hint->init_scan_mask = 0;
490 hint->njoin_hints = 0;
491 hint->max_join_hints = 0;
492 hint->join_hints = NULL;
493 hint->init_join_mask = 0;
495 hint->join_hint_level = NULL;
496 hint->nleading_hints = 0;
497 hint->max_leading_hints = 0;
498 hint->leading_hints = NULL;
499 hint->context = superuser() ? PGC_SUSET : PGC_USERSET;
500 hint->nset_hints = 0;
501 hint->max_set_hints = 0;
502 hint->set_hints = NULL;
508 PlanHintDelete(PlanHint *hint)
516 pfree(hint->hint_str);
518 for (i = 0; i < hint->nscan_hints; i++)
519 ScanMethodHintDelete(hint->scan_hints[i]);
520 if (hint->scan_hints)
521 pfree(hint->scan_hints);
523 for (i = 0; i < hint->njoin_hints; i++)
524 JoinMethodHintDelete(hint->join_hints[i]);
525 if (hint->join_hints)
526 pfree(hint->join_hints);
528 for (i = 2; i <= hint->nlevel; i++)
529 list_free(hint->join_hint_level[i]);
530 if (hint->join_hint_level)
531 pfree(hint->join_hint_level);
533 for (i = 0; i < hint->nleading_hints; i++)
534 LeadingHintDelete(hint->leading_hints[i]);
535 if (hint->leading_hints)
536 pfree(hint->leading_hints);
538 for (i = 0; i < hint->nset_hints; i++)
539 SetHintDelete(hint->set_hints[i]);
541 pfree(hint->set_hints);
547 PlanHintIsEmpty(PlanHint *hint)
549 if (hint->nscan_hints == 0 &&
550 hint->njoin_hints == 0 &&
551 hint->nleading_hints == 0 &&
552 hint->nset_hints == 0)
558 /* TODO オブジェクト名のクォート処理を追加 */
560 all_hint_dump(PlanHint *hint, StringInfo buf, const char *title, HintStatus state)
565 appendStringInfo(buf, "%s:\n", title);
566 for (i = 0; i < hint->nscan_hints; i++)
568 ScanMethodHint *h = hint->scan_hints[i];
570 if (h->base.state != state)
573 appendStringInfo(buf, "%s(%s", h->base.keyword, h->relname);
574 foreach(l, h->indexnames)
575 appendStringInfo(buf, " %s", (char *) lfirst(l));
576 appendStringInfoString(buf, ")\n");
579 for (i = 0; i < hint->njoin_hints; i++)
581 JoinMethodHint *h = hint->join_hints[i];
584 if (h->base.state != state)
588 if (h->enforce_mask == ENABLE_ALL_JOIN)
591 appendStringInfo(buf, "%s(%s", h->base.keyword, h->relnames[0]);
592 for (j = 1; j < h->nrels; j++)
593 appendStringInfo(buf, " %s", h->relnames[j]);
594 appendStringInfoString(buf, ")\n");
597 for (i = 0; i < hint->nset_hints; i++)
599 SetHint *h = hint->set_hints[i];
601 if (h->base.state != state)
604 appendStringInfo(buf, "%s(%s %s)\n", HINT_SET, h->name, h->value);
607 for (i = 0; i < hint->nleading_hints; i++)
609 LeadingHint *h = hint->leading_hints[i];
612 if (h->base.state != state)
616 foreach(l, h->relations)
620 appendStringInfo(buf, "%s(%s", HINT_LEADING, (char *)lfirst(l));
624 appendStringInfo(buf, " %s", (char *)lfirst(l));
627 appendStringInfoString(buf, ")\n");
632 PlanHintDump(PlanHint *hint)
638 elog(LOG, "pg_hint_plan:\nno hint");
642 initStringInfo(&buf);
644 appendStringInfoString(&buf, "pg_hint_plan:\n");
645 all_hint_dump(hint, &buf, "used hint", HINT_STATE_USED);
646 all_hint_dump(hint, &buf, "not used hint", HINT_STATE_NOTUSED);
647 all_hint_dump(hint, &buf, "duplication hint", HINT_STATE_DUPLICATION);
648 all_hint_dump(hint, &buf, "error hint", HINT_STATE_ERROR);
650 elog(LOG, "%s", buf.data);
656 RelnameCmp(const void *a, const void *b)
658 const char *relnamea = *((const char **) a);
659 const char *relnameb = *((const char **) b);
661 return strcmp(relnamea, relnameb);
665 ScanMethodHintCmp(const void *a, const void *b, bool order)
667 const ScanMethodHint *hinta = *((const ScanMethodHint **) a);
668 const ScanMethodHint *hintb = *((const ScanMethodHint **) b);
671 if ((result = RelnameCmp(&hinta->relname, &hintb->relname)) != 0)
676 return hinta->base.hint_str - hintb->base.hint_str;
682 ScanMethodHintCmpIsOrder(const void *a, const void *b)
684 return ScanMethodHintCmp(a, b, true);
688 JoinMethodHintCmp(const void *a, const void *b, bool order)
690 const JoinMethodHint *hinta = *((const JoinMethodHint **) a);
691 const JoinMethodHint *hintb = *((const JoinMethodHint **) b);
694 if (hinta->nrels != hintb->nrels)
695 return hinta->nrels - hintb->nrels;
697 for (i = 0; i < hinta->nrels; i++)
700 if ((result = RelnameCmp(&hinta->relnames[i], &hintb->relnames[i])) != 0)
706 return hinta->base.hint_str - hintb->base.hint_str;
712 JoinMethodHintCmpIsOrder(const void *a, const void *b)
714 return JoinMethodHintCmp(a, b, true);
718 LeadingHintCmp(const void *a, const void *b, bool order)
720 const LeadingHint *hinta = *((const LeadingHint **) a);
721 const LeadingHint *hintb = *((const LeadingHint **) b);
725 return hinta->base.hint_str - hintb->base.hint_str;
732 LeadingHintCmpIsOrder(const void *a, const void *b)
734 return LeadingHintCmp(a, b, true);
739 SetHintCmp(const void *a, const void *b, bool order)
741 const SetHint *hinta = *((const SetHint **) a);
742 const SetHint *hintb = *((const SetHint **) b);
745 if ((result = strcmp(hinta->name, hintb->name)) != 0)
750 return hinta->base.hint_str - hintb->base.hint_str;
756 SetHintCmpIsOrder(const void *a, const void *b)
758 return SetHintCmp(a, b, true);
761 #if PG_VERSION_NUM < 90200
763 set_config_option_wrapper(const char *name, const char *value,
764 GucContext context, GucSource source,
765 GucAction action, bool changeVal, int elevel)
768 MemoryContext ccxt = CurrentMemoryContext;
772 result = set_config_option(name, value, context, source,
783 ecxt = MemoryContextSwitchTo(ccxt);
784 errdata = CopyErrorData();
785 ereport(elevel, (errcode(errdata->sqlerrcode),
786 errmsg("%s", errdata->message),
787 errdata->detail ? errdetail("%s", errdata->detail) : 0,
788 errdata->hint ? errhint("%s", errdata->hint) : 0));
789 FreeErrorData(errdata);
791 MemoryContextSwitchTo(ecxt);
798 #define set_config_option(name, value, context, source, \
799 action, changeVal, elevel) \
800 set_config_option_wrapper(name, value, context, source, \
801 action, changeVal, elevel)
805 set_config_options(SetHint **options, int noptions, GucContext context)
810 save_nestlevel = NewGUCNestLevel();
812 for (i = 0; i < noptions; i++)
814 SetHint *hint = options[i];
817 if (!hint_state_enabled(hint))
820 result = set_config_option(hint->name, hint->value, context,
821 PGC_S_SESSION, GUC_ACTION_SAVE, true,
822 pg_hint_plan_parse_messages);
824 hint->base.state = HINT_STATE_USED;
826 hint->base.state = HINT_STATE_ERROR;
829 return save_nestlevel;
832 #define SET_CONFIG_OPTION(name, type_bits) \
833 set_config_option((name), \
834 (enforce_mask & (type_bits)) ? "true" : "false", \
835 context, PGC_S_SESSION, GUC_ACTION_SAVE, true, ERROR)
838 set_scan_config_options(unsigned char enforce_mask, GucContext context)
840 SET_CONFIG_OPTION("enable_seqscan", ENABLE_SEQSCAN);
841 SET_CONFIG_OPTION("enable_indexscan", ENABLE_INDEXSCAN);
842 SET_CONFIG_OPTION("enable_bitmapscan", ENABLE_BITMAPSCAN);
843 SET_CONFIG_OPTION("enable_tidscan", ENABLE_TIDSCAN);
844 #if PG_VERSION_NUM >= 90200
845 SET_CONFIG_OPTION("enable_indexonlyscan", ENABLE_INDEXSCAN);
850 set_join_config_options(unsigned char enforce_mask, GucContext context)
852 SET_CONFIG_OPTION("enable_nestloop", ENABLE_NESTLOOP);
853 SET_CONFIG_OPTION("enable_mergejoin", ENABLE_MERGEJOIN);
854 SET_CONFIG_OPTION("enable_hashjoin", ENABLE_HASHJOIN);
862 parse_keyword(const char *str, StringInfo buf)
866 while (!isspace(*str) && *str != '(' && *str != '\0')
867 appendStringInfoCharMacro(buf, *str++);
873 skip_opened_parenthesis(const char *str)
879 parse_ereport(str, ("Opened parenthesis is necessary."));
889 skip_closed_parenthesis(const char *str)
895 parse_ereport(str, ("Closed parenthesis is necessary."));
905 * 二重引用符で囲まれているかもしれないトークンを読み取り word 引数に palloc
906 * で確保したバッファに格納してそのポインタを返す。
908 * 正常にパースできた場合は残りの文字列の先頭位置を、異常があった場合は NULL を
912 parse_quote_value(const char *str, char **word, char *value_type)
920 initStringInfo(&buf);
933 /* 二重引用符が閉じられていない場合はパース中断 */
937 parse_ereport(str, ("Unterminated quoted %s.", value_type));
942 * エスケープ対象のダブルクウォートをスキップする。
943 * もしブロックコメントの開始文字列や終了文字列もオブジェクト名とし
944 * て使用したい場合は、/ と * もエスケープ対象とすることで使用できる
945 * が、処理対象としていない。もしテーブル名にこれらの文字が含まれる
946 * 場合は、エイリアスを指定する必要がある。
956 if (isspace(*str) || *str == ')' || *str == '\0')
959 appendStringInfoCharMacro(&buf, *str++);
965 parse_ereport(str, ("%s is necessary.", value_type));
975 skip_option_delimiter(const char *str)
983 parse_ereport(str, ("Must be specified space."));
991 parse_hints(PlanHint *plan, Query *parse, const char *str)
996 initStringInfo(&buf);
999 const HintParser *parser;
1001 /* in error message, we output the comment including the keyword. */
1002 head = (char *) str;
1004 /* parse only the keyword of the hint. */
1005 resetStringInfo(&buf);
1006 str = parse_keyword(str, &buf);
1008 for (parser = parsers; parser->keyword != NULL; parser++)
1010 char *keyword = parser->keyword;
1013 if (strcasecmp(buf.data, keyword) != 0)
1016 hint = parser->create_func(head, keyword);
1018 if (parser->have_paren)
1020 /* parser of each hint does parse in a parenthesis. */
1021 if ((str = skip_opened_parenthesis(str)) == NULL ||
1022 (str = hint->parser_func(hint, plan, parse, str)) == NULL ||
1023 (str = skip_closed_parenthesis(str)) == NULL)
1025 hint->delete_func(hint);
1032 if ((str = hint->parser_func(hint, plan, parse, str)) == NULL)
1034 hint->delete_func(hint);
1040 * 直前のヒントに括弧の指定がなければ次のヒントの間に空白が必要
1042 if (!isspace(*str) && *str == '\0')
1043 parse_ereport(str, ("Delimiter of the hint is necessary."));
1051 if (parser->keyword == NULL)
1053 parse_ereport(head, ("Keyword \"%s\" does not exist.", buf.data));
1063 * Do basic parsing of the query head comment.
1066 parse_head_comment(Query *parse)
1075 /* get client-supplied query string. */
1076 p = debug_query_string;
1080 /* extract query head comment. */
1081 len = strlen(HINT_START);
1083 if (strncmp(p, HINT_START, len))
1090 /* find hint end keyword. */
1091 if ((tail = strstr(p, HINT_END)) == NULL)
1093 parse_ereport(head, ("unterminated block comment"));
1097 /* 入れ子にしたブロックコメントはサポートしない */
1098 if ((head = strstr(p, BLOCK_COMMENT_START)) != NULL && head < tail)
1099 parse_ereport(head, ("block comments nest doesn't supported"));
1103 head = palloc(len + 1);
1104 memcpy(head, p, len);
1108 plan = PlanHintCreate();
1109 plan->hint_str = head;
1111 /* parse each hint. */
1112 parse_hints(plan, parse, p);
1114 /* When nothing specified a hint, we free PlanHint and returns NULL. */
1115 if (PlanHintIsEmpty(plan))
1117 PlanHintDelete(plan);
1121 /* 重複したscan method hintを検索する */
1122 qsort(plan->scan_hints, plan->nscan_hints, sizeof(ScanMethodHint *), ScanMethodHintCmpIsOrder);
1123 for (i = 0; i < plan->nscan_hints - 1; i++)
1125 if (ScanMethodHintCmp(plan->scan_hints + i,
1126 plan->scan_hints + i + 1, false) == 0)
1127 plan->scan_hints[i]->base.state = HINT_STATE_DUPLICATION;
1130 /* 重複したjoin method hintを検索する */
1131 qsort(plan->join_hints, plan->njoin_hints, sizeof(JoinMethodHint *), JoinMethodHintCmpIsOrder);
1132 for (i = 0; i < plan->njoin_hints - 1; i++)
1134 if (JoinMethodHintCmp(plan->join_hints + i,
1135 plan->join_hints + i + 1, false) == 0)
1136 plan->join_hints[i]->base.state = HINT_STATE_DUPLICATION;
1139 /* 重複したSet hintを検索する */
1140 qsort(plan->set_hints, plan->nset_hints, sizeof(SetHint *), SetHintCmpIsOrder);
1141 for (i = 0; i < plan->nset_hints - 1; i++)
1143 if (SetHintCmp(plan->set_hints + i,
1144 plan->set_hints + i + 1, false) == 0)
1145 plan->set_hints[i]->base.state = HINT_STATE_DUPLICATION;
1148 /* 重複したLeading hintを検索する */
1149 for (i = 0; i < plan->nleading_hints - 1; i++)
1151 if (LeadingHintCmp(plan->leading_hints + i,
1152 plan->leading_hints + i + 1, false) == 0)
1153 plan->leading_hints[i]->base.state = HINT_STATE_DUPLICATION;
1160 * スキャン方式ヒントのカッコ内をパースする
1163 ScanMethodHintParse(ScanMethodHint *hint, PlanHint *plan, Query *parse, const char *str)
1165 const char *keyword = hint->base.keyword;
1168 * スキャン方式のヒントでリレーション名が読み取れない場合はヒント無効
1170 if ((str = parse_quote_value(str, &hint->relname, "ralation name")) == NULL)
1176 * インデックスリストを受け付けるヒントであれば、インデックス参照をパース
1179 if (strcmp(keyword, HINT_INDEXSCAN) == 0 ||
1180 #if PG_VERSION_NUM >= 90200
1181 strcmp(keyword, HINT_INDEXONLYSCAN) == 0 ||
1183 strcmp(keyword, HINT_BITMAPSCAN) == 0)
1185 while (*str != ')' && *str != '\0')
1189 str = parse_quote_value(str, &indexname, "index name");
1193 hint->indexnames = lappend(hint->indexnames, indexname);
1198 /* カッコが閉じていなければヒント無効。 */
1199 skip_space(str); /* just in case */
1202 parse_ereport(str, ("Closed parenthesis is necessary."));
1207 * ヒントごとに決まっている許容スキャン方式をビットマスクとして設定
1209 if (strcasecmp(keyword, HINT_SEQSCAN) == 0)
1210 hint->enforce_mask = ENABLE_SEQSCAN;
1211 else if (strcasecmp(keyword, HINT_INDEXSCAN) == 0)
1212 hint->enforce_mask = ENABLE_INDEXSCAN;
1213 else if (strcasecmp(keyword, HINT_BITMAPSCAN) == 0)
1214 hint->enforce_mask = ENABLE_BITMAPSCAN;
1215 else if (strcasecmp(keyword, HINT_TIDSCAN) == 0)
1216 hint->enforce_mask = ENABLE_TIDSCAN;
1217 else if (strcasecmp(keyword, HINT_NOSEQSCAN) == 0)
1218 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_SEQSCAN;
1219 else if (strcasecmp(keyword, HINT_NOINDEXSCAN) == 0)
1220 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_INDEXSCAN;
1221 else if (strcasecmp(keyword, HINT_NOBITMAPSCAN) == 0)
1222 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_BITMAPSCAN;
1223 else if (strcasecmp(keyword, HINT_NOTIDSCAN) == 0)
1224 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_TIDSCAN;
1227 parse_ereport(str, ("unrecognized hint keyword \"%s\"", keyword));
1232 * 出来上がったヒント情報を追加。スロットが足りない場合は二倍に拡張する。
1234 if (plan->nscan_hints == 0)
1236 plan->max_scan_hints = HINT_ARRAY_DEFAULT_INITSIZE;
1237 plan->scan_hints = palloc(sizeof(ScanMethodHint *) * plan->max_scan_hints);
1239 else if (plan->nscan_hints == plan->max_scan_hints)
1241 plan->max_scan_hints *= 2;
1242 plan->scan_hints = repalloc(plan->scan_hints,
1243 sizeof(ScanMethodHint *) * plan->max_scan_hints);
1246 plan->scan_hints[plan->nscan_hints] = hint;
1247 plan->nscan_hints++;
1253 JoinMethodHintParse(JoinMethodHint *hint, PlanHint *plan, Query *parse, const char *str)
1256 const char *keyword = hint->base.keyword;
1260 hint->relnames = palloc(sizeof(char *));
1262 while ((str = parse_quote_value(str, &relname, "table name")) != NULL)
1265 hint->relnames = repalloc(hint->relnames, sizeof(char *) * hint->nrels);
1266 hint->relnames[hint->nrels - 1] = relname;
1276 /* Join 対象のテーブルは最低でも2つ指定する必要がある */
1277 if (hint->nrels < 2)
1279 parse_ereport(str, ("Specified relation more than two."));
1284 qsort(hint->relnames, hint->nrels, sizeof(char *), RelnameCmp);
1286 if (strcasecmp(keyword, HINT_NESTLOOP) == 0)
1287 hint->enforce_mask = ENABLE_NESTLOOP;
1288 else if (strcasecmp(keyword, HINT_MERGEJOIN) == 0)
1289 hint->enforce_mask = ENABLE_MERGEJOIN;
1290 else if (strcasecmp(keyword, HINT_HASHJOIN) == 0)
1291 hint->enforce_mask = ENABLE_HASHJOIN;
1292 else if (strcasecmp(keyword, HINT_NONESTLOOP) == 0)
1293 hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_NESTLOOP;
1294 else if (strcasecmp(keyword, HINT_NOMERGEJOIN) == 0)
1295 hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_MERGEJOIN;
1296 else if (strcasecmp(keyword, HINT_NOHASHJOIN) == 0)
1297 hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_HASHJOIN;
1300 parse_ereport(str, ("unrecognized hint keyword \"%s\"", keyword));
1304 if (plan->njoin_hints == 0)
1306 plan->max_join_hints = HINT_ARRAY_DEFAULT_INITSIZE;
1307 plan->join_hints = palloc(sizeof(JoinMethodHint *) * plan->max_join_hints);
1309 else if (plan->njoin_hints == plan->max_join_hints)
1311 plan->max_join_hints *= 2;
1312 plan->join_hints = repalloc(plan->join_hints,
1313 sizeof(JoinMethodHint *) * plan->max_join_hints);
1316 plan->join_hints[plan->njoin_hints] = hint;
1317 plan->njoin_hints++;
1323 LeadingHintParse(LeadingHint *hint, PlanHint *plan, Query *parse, const char *str)
1330 if ((str = parse_quote_value(str, &relname, "relation name")) == NULL)
1333 hint->relations = lappend(hint->relations, relname);
1342 parse_ereport(str, ("Must be specified space."));
1347 /* テーブル指定が1つのみの場合は、ヒントはエラーとする */
1348 if (list_length(hint->relations) == 1)
1350 parse_ereport(hint->base.hint_str,
1351 ("In %s hint, specified relation name 2 or more.", HINT_LEADING));
1352 hint->base.state = HINT_STATE_ERROR;
1355 if (plan->nleading_hints == 0)
1357 plan->max_leading_hints = HINT_ARRAY_DEFAULT_INITSIZE;
1358 plan->leading_hints = palloc(sizeof(LeadingHint *) * plan->max_leading_hints);
1360 else if (plan->nleading_hints == plan->max_leading_hints)
1362 plan->max_leading_hints *= 2;
1363 plan->leading_hints = repalloc(plan->leading_hints,
1364 sizeof(LeadingHint *) * plan->max_leading_hints);
1367 plan->leading_hints[plan->nleading_hints] = hint;
1368 plan->nleading_hints++;
1374 SetHintParse(SetHint *hint, PlanHint *plan, Query *parse, const char *str)
1376 if ((str = parse_quote_value(str, &hint->name, "parameter name")) == NULL ||
1377 (str = skip_option_delimiter(str)) == NULL ||
1378 (str = parse_quote_value(str, &hint->value, "parameter value")) == NULL)
1384 parse_ereport(str, ("Closed parenthesis is necessary."));
1388 if (plan->nset_hints == 0)
1390 plan->max_set_hints = HINT_ARRAY_DEFAULT_INITSIZE;
1391 plan->set_hints = palloc(sizeof(SetHint *) * plan->max_set_hints);
1393 else if (plan->nset_hints == plan->max_set_hints)
1395 plan->max_set_hints *= 2;
1396 plan->set_hints = repalloc(plan->set_hints,
1397 sizeof(SetHint *) * plan->max_set_hints);
1400 plan->set_hints[plan->nset_hints] = hint;
1406 static PlannedStmt *
1407 pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
1410 PlannedStmt *result;
1413 * hintが指定されない、または空のhintを指定された場合は通常のparser処理をお
1415 * 他のフック関数で実行されるhint処理をスキップするために、global 変数をNULL
1418 if (!pg_hint_plan_enable || (global = parse_head_comment(parse)) == NULL)
1422 if (prev_planner_hook)
1423 return (*prev_planner_hook) (parse, cursorOptions, boundParams);
1425 return standard_planner(parse, cursorOptions, boundParams);
1428 /* Set hint で指定されたGUCパラメータを設定する */
1429 save_nestlevel = set_config_options(global->set_hints, global->nset_hints,
1433 global->init_scan_mask |= ENABLE_SEQSCAN;
1434 if (enable_indexscan)
1435 global->init_scan_mask |= ENABLE_INDEXSCAN;
1436 if (enable_bitmapscan)
1437 global->init_scan_mask |= ENABLE_BITMAPSCAN;
1439 global->init_scan_mask |= ENABLE_TIDSCAN;
1440 #if PG_VERSION_NUM >= 90200
1441 if (enable_indexonlyscan)
1442 global->init_scan_mask |= ENABLE_INDEXONLYSCAN;
1444 if (enable_nestloop)
1445 global->init_join_mask |= ENABLE_NESTLOOP;
1446 if (enable_mergejoin)
1447 global->init_join_mask |= ENABLE_MERGEJOIN;
1448 if (enable_hashjoin)
1449 global->init_join_mask |= ENABLE_HASHJOIN;
1451 if (prev_planner_hook)
1452 result = (*prev_planner_hook) (parse, cursorOptions, boundParams);
1454 result = standard_planner(parse, cursorOptions, boundParams);
1457 * Restore the GUC variables we set above.
1459 AtEOXact_GUC(true, save_nestlevel);
1462 * Print hint if debugging.
1464 if (pg_hint_plan_debug_print)
1466 PlanHintDump(global);
1468 elog_node_display(INFO, "rtable", parse->rtable, true);
1472 PlanHintDelete(global);
1479 * aliasnameと一致するSCANヒントを探す
1481 static ScanMethodHint *
1482 find_scan_hint(PlannerInfo *root, RelOptInfo *rel)
1487 /* RELOPT_BASEREL でなければ、scan method ヒントが適用できない。 */
1488 if (rel->reloptkind != RELOPT_BASEREL)
1491 rte = root->simple_rte_array[rel->relid];
1494 * VALUESリストはValuesScanのみが選択できるため、ヒントが適用できない。
1496 if (rte->rtekind == RTE_VALUES)
1499 for (i = 0; i < global->nscan_hints; i++)
1501 ScanMethodHint *hint = global->scan_hints[i];
1503 if (!hint_state_enabled(hint))
1506 if (RelnameCmp(&rte->eref->aliasname, &hint->relname) == 0)
1514 pg_hint_plan_get_relation_info(PlannerInfo *root, Oid relationObjectId,
1515 bool inhparent, RelOptInfo *rel)
1517 ScanMethodHint *hint;
1522 if (prev_get_relation_info)
1523 (*prev_get_relation_info) (root, relationObjectId, inhparent, rel);
1525 /* 有効なヒントが指定されなかった場合は処理をスキップする。 */
1529 /* scan hint が指定されない場合は、GUCパラメータをリセットする。 */
1530 if ((hint = find_scan_hint(root, rel)) == NULL)
1532 set_scan_config_options(global->init_scan_mask, global->context);
1536 set_scan_config_options(hint->enforce_mask, global->context);
1537 hint->base.state = HINT_STATE_USED;
1539 /* インデックスを全て削除し、スキャンに使えなくする */
1540 if (hint->enforce_mask == ENABLE_SEQSCAN ||
1541 hint->enforce_mask == ENABLE_TIDSCAN)
1543 list_free_deep(rel->indexlist);
1544 rel->indexlist = NIL;
1545 hint->base.state = HINT_STATE_USED;
1550 /* 後でパスを作り直すため、ここではなにもしない */
1551 if (hint->indexnames == NULL)
1554 /* 指定されたインデックスのみをのこす */
1556 for (cell = list_head(rel->indexlist); cell; cell = next)
1558 IndexOptInfo *info = (IndexOptInfo *) lfirst(cell);
1559 char *indexname = get_rel_name(info->indexoid);
1561 bool use_index = false;
1565 foreach(l, hint->indexnames)
1567 if (RelnameCmp(&indexname, &lfirst(l)) == 0)
1575 rel->indexlist = list_delete_cell(rel->indexlist, cell, prev);
1584 scan_relid_aliasname(PlannerInfo *root, char *aliasname, bool check_ambiguous, const char *str)
1586 /* TODO refnameRangeTblEntry を参考 */
1590 for (i = 1; i < root->simple_rel_array_size; i++)
1592 if (root->simple_rel_array[i] == NULL)
1595 Assert(i == root->simple_rel_array[i]->relid);
1597 if (RelnameCmp(&aliasname, &root->simple_rte_array[i]->eref->aliasname)
1601 if (!check_ambiguous)
1605 parse_ereport(str, ("relation name \"%s\" is ambiguous", aliasname));
1614 * relidビットマスクと一致するヒントを探す
1616 static JoinMethodHint *
1617 find_join_hint(Relids joinrelids)
1622 join_hint = global->join_hint_level[bms_num_members(joinrelids)];
1623 foreach(l, join_hint)
1625 JoinMethodHint *hint = (JoinMethodHint *) lfirst(l);
1626 if (bms_equal(joinrelids, hint->joinrelids))
1634 * 結合方式のヒントを使用しやすい構造に変換する。
1637 transform_join_hints(PlanHint *plan, PlannerInfo *root, int level, List *initial_rels)
1644 plan->nlevel = root->simple_rel_array_size - 1;
1645 plan->join_hint_level = palloc0(sizeof(List *) * (root->simple_rel_array_size));
1646 for (i = 0; i < plan->njoin_hints; i++)
1648 JoinMethodHint *hint = plan->join_hints[i];
1652 if (!hint_state_enabled(hint))
1655 for (j = 0; j < hint->nrels; j++)
1657 char *relname = hint->relnames[j];
1659 relid = scan_relid_aliasname(root, relname, true, hint->base.hint_str);
1662 parse_ereport(hint->base.hint_str, ("Relation \"%s\" does not exist.", relname));
1666 hint->joinrelids = bms_add_member(hint->joinrelids, relid);
1672 plan->join_hint_level[hint->nrels] =
1673 lappend(plan->join_hint_level[hint->nrels], hint);
1676 if (plan->nleading_hints == 0)
1679 // TODO エラーヒントならばスキップ
1680 /* Leading hint は、全ての join 方式が有効な hint として登録する */
1683 foreach(l, plan->leading_hints[plan->nleading_hints - 1]->relations)
1685 char *relname = (char *)lfirst(l);
1686 JoinMethodHint *hint;
1688 i = scan_relid_aliasname(root, relname, true, plan->hint_str);
1691 parse_ereport(plan->hint_str, ("Relation \"%s\" does not exist.", relname));
1692 plan->leading_hints[plan->nleading_hints - 1]->base.state =
1697 joinrelids = bms_add_member(joinrelids, i);
1703 if (njoinrels > plan->nlevel)
1705 parse_ereport(plan->hint_str, ("In %s hint, specified relation name %d or less.", HINT_LEADING, plan->nlevel));
1706 plan->leading_hints[plan->nleading_hints - 1]->base.state =
1711 /* Leading で指定した組み合わせ以外の join hint を削除する */
1712 hint = find_join_hint(joinrelids);
1713 list_free(plan->join_hint_level[njoinrels]);
1715 plan->join_hint_level[njoinrels] = lappend(NIL, hint);
1719 * Here relnames is not set, since Relids bitmap is sufficient to
1720 * control paths of this query afterwards.
1722 // TODO plan->hint_strをLeadingHint構造に変更後修正
1723 hint = (JoinMethodHint *) JoinMethodHintCreate(plan->hint_str, HINT_LEADING);
1724 hint->nrels = njoinrels;
1725 hint->enforce_mask = ENABLE_ALL_JOIN;
1726 hint->joinrelids = bms_copy(joinrelids);
1727 plan->join_hint_level[njoinrels] = lappend(NIL, hint);
1729 if (plan->njoin_hints == 0)
1731 plan->max_join_hints = HINT_ARRAY_DEFAULT_INITSIZE;
1732 plan->join_hints = palloc(sizeof(JoinMethodHint *) * plan->max_join_hints);
1734 else if (plan->njoin_hints == plan->max_join_hints)
1736 plan->max_join_hints *= 2;
1737 plan->join_hints = repalloc(plan->join_hints,
1738 sizeof(JoinMethodHint *) * plan->max_join_hints);
1741 plan->join_hints[plan->njoin_hints] = hint;
1742 plan->njoin_hints++;
1745 plan->leading_hints[plan->nleading_hints - 1]->base.state =
1750 if (global->nleading_hints > 0 &&
1751 hint_state_enabled(global->leading_hints[global->nleading_hints - 1]))
1752 set_join_config_options(DISABLE_ALL_JOIN, global->context);
1754 bms_free(joinrelids);
1758 rebuild_scan_path(PlanHint *plan, PlannerInfo *root, int level, List *initial_rels)
1762 foreach(l, initial_rels)
1764 RelOptInfo *rel = (RelOptInfo *) lfirst(l);
1765 RangeTblEntry *rte = root->simple_rte_array[rel->relid];
1766 ScanMethodHint *hint;
1769 * スキャン方式が選択できるリレーションのみ、スキャンパスを再生成
1772 if (rel->reloptkind != RELOPT_BASEREL || rte->rtekind == RTE_VALUES)
1776 * scan method hint が指定されていなければ、初期値のGUCパラメータでscan
1779 if ((hint = find_scan_hint(root, rel)) == NULL)
1780 set_scan_config_options(plan->init_scan_mask, plan->context);
1783 set_scan_config_options(hint->enforce_mask, plan->context);
1784 hint->base.state = HINT_STATE_USED;
1787 rel->pathlist = NIL; /* TODO 解放の必要はある? */
1788 set_plain_rel_pathlist(root, rel, rte);
1792 * Restore the GUC variables we set above.
1794 set_scan_config_options(plan->init_scan_mask, plan->context);
1798 * make_join_rel() をラップする関数
1800 * ヒントにしたがって、enabele_* パラメータを変更した上で、make_join_rel()を
1804 make_join_rel_wrapper(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
1807 JoinMethodHint *hint;
1811 joinrelids = bms_union(rel1->relids, rel2->relids);
1812 hint = find_join_hint(joinrelids);
1813 bms_free(joinrelids);
1816 return pg_hint_plan_make_join_rel(root, rel1, rel2);
1818 save_nestlevel = NewGUCNestLevel();
1820 set_join_config_options(hint->enforce_mask, global->context);
1822 rel = pg_hint_plan_make_join_rel(root, rel1, rel2);
1823 hint->base.state = HINT_STATE_USED;
1826 * Restore the GUC variables we set above.
1828 AtEOXact_GUC(true, save_nestlevel);
1834 pg_hint_plan_join_search(PlannerInfo *root, int levels_needed, List *initial_rels)
1837 * pg_hint_planが無効、または有効なヒントが1つも指定されなかった場合は、標準
1842 if (prev_join_search)
1843 return (*prev_join_search) (root, levels_needed, initial_rels);
1844 else if (enable_geqo && levels_needed >= geqo_threshold)
1845 return geqo(root, levels_needed, initial_rels);
1847 return standard_join_search(root, levels_needed, initial_rels);
1850 transform_join_hints(global, root, levels_needed, initial_rels);
1851 rebuild_scan_path(global, root, levels_needed, initial_rels);
1854 * GEQOを使用する条件を満たした場合は、GEQOを用いた結合方式の検索を行う。
1855 * このとき、スキャン方式のヒントとSetヒントのみが有効になり、結合方式や結合
1856 * 順序はヒント句は無効になりGEQOのアルゴリズムで決定される。
1858 if (enable_geqo && levels_needed >= geqo_threshold)
1859 return geqo(root, levels_needed, initial_rels);
1861 return pg_hint_plan_standard_join_search(root, levels_needed, initial_rels);
1864 #define standard_join_search pg_hint_plan_standard_join_search
1865 #define join_search_one_level pg_hint_plan_join_search_one_level
1866 #define make_join_rel make_join_rel_wrapper
1869 #undef make_join_rel
1870 #define make_join_rel pg_hint_plan_make_join_rel
1871 #define add_paths_to_joinrel(root, joinrel, outerrel, innerrel, jointype, sjinfo, restrictlist) \
1873 ScanMethodHint *hint = NULL; \
1874 if ((hint = find_scan_hint((root), (innerrel))) != NULL) \
1876 set_scan_config_options(hint->enforce_mask, global->context); \
1877 hint->base.state = HINT_STATE_USED; \
1879 add_paths_to_joinrel((root), (joinrel), (outerrel), (innerrel), (jointype), (sjinfo), (restrictlist)); \
1881 set_scan_config_options(global->init_scan_mask, global->context); \
1883 #include "make_join_rel.c"