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"
23 #ifdef PG_MODULE_MAGIC
27 #if PG_VERSION_NUM < 90100
28 #error unsupported PostgreSQL version
31 #define BLOCK_COMMENT_START "/*"
32 #define BLOCK_COMMENT_END "*/"
33 #define HINT_COMMENT_KEYWORD "+"
34 #define HINT_START BLOCK_COMMENT_START HINT_COMMENT_KEYWORD
35 #define HINT_END BLOCK_COMMENT_END
38 #define HINT_SEQSCAN "SeqScan"
39 #define HINT_INDEXSCAN "IndexScan"
40 #define HINT_BITMAPSCAN "BitmapScan"
41 #define HINT_TIDSCAN "TidScan"
42 #define HINT_NOSEQSCAN "NoSeqScan"
43 #define HINT_NOINDEXSCAN "NoIndexScan"
44 #define HINT_NOBITMAPSCAN "NoBitmapScan"
45 #define HINT_NOTIDSCAN "NoTidScan"
46 #if PG_VERSION_NUM >= 90200
47 #define HINT_INDEXONLYSCAN "IndexonlyScan"
48 #define HINT_NOINDEXONLYSCAN "NoIndexonlyScan"
50 #define HINT_NESTLOOP "NestLoop"
51 #define HINT_MERGEJOIN "MergeJoin"
52 #define HINT_HASHJOIN "HashJoin"
53 #define HINT_NONESTLOOP "NoNestLoop"
54 #define HINT_NOMERGEJOIN "NoMergeJoin"
55 #define HINT_NOHASHJOIN "NoHashJoin"
56 #define HINT_LEADING "Leading"
57 #define HINT_SET "Set"
60 #define HINT_ARRAY_DEFAULT_INITSIZE 8
62 #define parse_ereport(str, detail) \
63 ereport(pg_hint_plan_parse_messages, \
64 (errmsg("hint syntax error at or near \"%s\"", (str)), \
67 #define skip_space(str) \
68 while (isspace(*str)) \
73 ENABLE_SEQSCAN = 0x01,
74 ENABLE_INDEXSCAN = 0x02,
75 ENABLE_BITMAPSCAN = 0x04,
76 ENABLE_TIDSCAN = 0x08,
77 #if PG_VERSION_NUM >= 90200
78 ENABLE_INDEXONLYSCAN = 0x10,
80 ENABLE_NESTLOOP = 0x20,
81 ENABLE_MERGEJOIN = 0x40,
82 ENABLE_HASHJOIN = 0x80
85 #define ENABLE_ALL_SCAN (ENABLE_SEQSCAN | ENABLE_INDEXSCAN | ENABLE_BITMAPSCAN \
87 #if PG_VERSION_NUM >= 90200
88 #define ENABLE_ALL_SCAN (ENABLE_SEQSCAN | ENABLE_INDEXSCAN | ENABLE_BITMAPSCAN \
89 | ENABLE_TIDSCAN | ENABLE_INDEXONLYSCAN)
91 #define ENABLE_ALL_JOIN (ENABLE_NESTLOOP | ENABLE_MERGEJOIN | ENABLE_HASHJOIN)
92 #define DISABLE_ALL_SCAN 0
93 #define DISABLE_ALL_JOIN 0
95 typedef struct Hint Hint;
96 typedef struct PlanHint PlanHint;
98 typedef Hint *(*HintCreateFunction) (char *hint_str, char *keyword);
99 typedef void (*HintDeleteFunction) (Hint *hint);
100 typedef const char *(*HintParseFunction) (Hint *hint, PlanHint *plan, Query *parse, const char *str);
103 typedef enum HintStatus
105 HINT_STATE_NOTUSED = 0, /* specified relation not used in query */
106 HINT_STATE_USED, /* hint is used */
107 HINT_STATE_DUPLICATION, /* specified hint duplication */
108 /* execute error (parse error does not include it) */
112 #define hint_state_enabled(hint) ((hint)->base.state == HINT_STATE_NOTUSED || \
113 (hint)->base.state == HINT_STATE_USED)
115 /* common data for all hints. */
118 const char *hint_str; /* must not do pfree */
119 const char *keyword; /* must not do pfree */
121 HintDeleteFunction delete_func;
122 HintParseFunction parser_func;
125 /* scan method hints */
126 typedef struct ScanMethodHint
131 unsigned char enforce_mask;
134 /* join method hints */
135 typedef struct JoinMethodHint
140 unsigned char enforce_mask;
144 /* join order hints */
145 typedef struct LeadingHint
148 List *relations; /* relation names specified in Leading hint */
151 /* change a run-time parameter hints */
152 typedef struct SetHint
155 char *name; /* name of variable */
160 * Describes a context of hint processing.
164 char *hint_str; /* original hint string */
166 /* for scan method hints */
167 int nscan_hints; /* # of valid scan hints */
168 int max_scan_hints; /* # of slots for scan hints */
169 ScanMethodHint **scan_hints; /* parsed scan hints */
170 int init_scan_mask; /* initial value scan parameter */
172 /* for join method hints */
173 int njoin_hints; /* # of valid join hints */
174 int max_join_hints; /* # of slots for join hints */
175 JoinMethodHint **join_hints; /* parsed join hints */
177 int nlevel; /* # of relations to be joined */
178 List **join_hint_level;
180 /* for Leading hints */
181 int nleading_hints; /* # of valid leading hints */
182 int max_leading_hints; /* # of slots for leading hints */
183 LeadingHint **leading_hints; /* parsed Leading hints */
186 GucContext context; /* which GUC parameters can we set? */
187 int nset_hints; /* # of valid set hints */
188 int max_set_hints; /* # of slots for set hints */
189 SetHint **set_hints; /* parsed Set hints */
193 * Describes a hint parser module which is bound with particular hint keyword.
195 typedef struct HintParser
199 HintCreateFunction create_func;
202 /* Module callbacks */
206 static PlannedStmt *pg_hint_plan_planner(Query *parse, int cursorOptions,
207 ParamListInfo boundParams);
208 static void pg_hint_plan_get_relation_info(PlannerInfo *root, Oid relationObjectId,
209 bool inhparent, RelOptInfo *rel);
210 static RelOptInfo *pg_hint_plan_join_search(PlannerInfo *root, int levels_needed,
213 static Hint *ScanMethodHintCreate(char *hint_str, char *keyword);
214 static void ScanMethodHintDelete(ScanMethodHint *hint);
215 static const char *ScanMethodHintParse(ScanMethodHint *hint, PlanHint *plan, Query *parse, const char *str);
216 static Hint *JoinMethodHintCreate(char *hint_str, char *keyword);
217 static void JoinMethodHintDelete(JoinMethodHint *hint);
218 static const char *JoinMethodHintParse(JoinMethodHint *hint, PlanHint *plan, Query *parse, const char *str);
219 static Hint *LeadingHintCreate(char *hint_str, char *keyword);
220 static void LeadingHintDelete(LeadingHint *hint);
221 static const char *LeadingHintParse(LeadingHint *hint, PlanHint *plan, Query *parse, const char *str);
222 static Hint *SetHintCreate(char *hint_str, char *keyword);
223 static void SetHintDelete(SetHint *hint);
224 static const char *SetHintParse(SetHint *hint, PlanHint *plan, Query *parse, const char *str);
226 RelOptInfo *standard_join_search_org(PlannerInfo *root, int levels_needed, List *initial_rels);
227 void pg_hint_plan_join_search_one_level(PlannerInfo *root, int level);
228 static void make_rels_by_clause_joins(PlannerInfo *root, RelOptInfo *old_rel, ListCell *other_rels);
229 static void make_rels_by_clauseless_joins(PlannerInfo *root, RelOptInfo *old_rel, ListCell *other_rels);
230 static bool has_join_restriction(PlannerInfo *root, RelOptInfo *rel);
231 static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte);
235 static bool pg_hint_plan_enable = true;
236 static bool pg_hint_plan_debug_print = false;
237 static int pg_hint_plan_parse_messages = INFO;
239 static const struct config_enum_entry parse_messages_level_options[] = {
240 {"debug", DEBUG2, true},
241 {"debug5", DEBUG5, false},
242 {"debug4", DEBUG4, false},
243 {"debug3", DEBUG3, false},
244 {"debug2", DEBUG2, false},
245 {"debug1", DEBUG1, false},
247 {"info", INFO, false},
248 {"notice", NOTICE, false},
249 {"warning", WARNING, false},
250 {"error", ERROR, false},
252 {"fatal", FATAL, true},
253 {"panic", PANIC, true},
258 /* Saved hook values in case of unload */
259 static planner_hook_type prev_planner_hook = NULL;
260 static get_relation_info_hook_type prev_get_relation_info = NULL;
261 static join_search_hook_type prev_join_search = NULL;
263 /* フック関数をまたがって使用する情報を管理する */
264 static PlanHint *global = NULL;
266 static const HintParser parsers[] = {
267 {HINT_SEQSCAN, true, ScanMethodHintCreate},
268 {HINT_INDEXSCAN, true, ScanMethodHintCreate},
269 {HINT_BITMAPSCAN, true, ScanMethodHintCreate},
270 {HINT_TIDSCAN, true, ScanMethodHintCreate},
271 {HINT_NOSEQSCAN, true, ScanMethodHintCreate},
272 {HINT_NOINDEXSCAN, true, ScanMethodHintCreate},
273 {HINT_NOBITMAPSCAN, true, ScanMethodHintCreate},
274 {HINT_NOTIDSCAN, true, ScanMethodHintCreate},
275 #if PG_VERSION_NUM >= 90200
276 {HINT_INDEXONLYSCAN, true, ScanMethodHintCreate},
277 {HINT_NOINDEXONLYSCAN, true, ScanMethodHintCreate},
279 {HINT_NESTLOOP, true, JoinMethodHintCreate},
280 {HINT_MERGEJOIN, true, JoinMethodHintCreate},
281 {HINT_HASHJOIN, true, JoinMethodHintCreate},
282 {HINT_NONESTLOOP, true, JoinMethodHintCreate},
283 {HINT_NOMERGEJOIN, true, JoinMethodHintCreate},
284 {HINT_NOHASHJOIN, true, JoinMethodHintCreate},
285 {HINT_LEADING, true, LeadingHintCreate},
286 {HINT_SET, true, SetHintCreate},
291 * Module load callbacks
296 /* Define custom GUC variables. */
297 DefineCustomBoolVariable("pg_hint_plan.enable",
298 "Instructions or hints to the planner using block comments.",
300 &pg_hint_plan_enable,
308 DefineCustomBoolVariable("pg_hint_plan.debug_print",
309 "Logs each query's parse results of the hint.",
311 &pg_hint_plan_debug_print,
319 DefineCustomEnumVariable("pg_hint_plan.parse_messages",
320 "Messege level of the parse error.",
322 &pg_hint_plan_parse_messages,
324 parse_messages_level_options,
332 prev_planner_hook = planner_hook;
333 planner_hook = pg_hint_plan_planner;
334 prev_get_relation_info = get_relation_info_hook;
335 get_relation_info_hook = pg_hint_plan_get_relation_info;
336 prev_join_search = join_search_hook;
337 join_search_hook = pg_hint_plan_join_search;
341 * Module unload callback
347 /* Uninstall hooks. */
348 planner_hook = prev_planner_hook;
349 get_relation_info_hook = prev_get_relation_info;
350 join_search_hook = prev_join_search;
354 ScanMethodHintCreate(char *hint_str, char *keyword)
356 ScanMethodHint *hint;
358 hint = palloc(sizeof(ScanMethodHint));
359 hint->base.hint_str = hint_str;
360 hint->base.keyword = keyword;
361 hint->base.state = HINT_STATE_NOTUSED;
362 hint->base.delete_func = (HintDeleteFunction) ScanMethodHintDelete;
363 hint->base.parser_func = (HintParseFunction) ScanMethodHintParse;
364 hint->relname = NULL;
365 hint->indexnames = NIL;
366 hint->enforce_mask = 0;
368 return (Hint *) hint;
372 ScanMethodHintDelete(ScanMethodHint *hint)
378 pfree(hint->relname);
379 list_free_deep(hint->indexnames);
384 JoinMethodHintCreate(char *hint_str, char *keyword)
386 JoinMethodHint *hint;
388 hint = palloc(sizeof(JoinMethodHint));
389 hint->base.hint_str = hint_str;
390 hint->base.keyword = keyword;
391 hint->base.state = HINT_STATE_NOTUSED;
392 hint->base.delete_func = (HintDeleteFunction) JoinMethodHintDelete;
393 hint->base.parser_func = (HintParseFunction) JoinMethodHintParse;
395 hint->relnames = NULL;
396 hint->enforce_mask = 0;
397 hint->joinrelids = NULL;
399 return (Hint *) hint;
403 JoinMethodHintDelete(JoinMethodHint *hint)
412 for (i = 0; i < hint->nrels; i++)
413 pfree(hint->relnames[i]);
414 pfree(hint->relnames);
416 bms_free(hint->joinrelids);
421 LeadingHintCreate(char *hint_str, char *keyword)
425 hint = palloc(sizeof(LeadingHint));
426 hint->base.hint_str = hint_str;
427 hint->base.keyword = keyword;
428 hint->base.state = HINT_STATE_NOTUSED;
429 hint->base.delete_func = (HintDeleteFunction)LeadingHintDelete;
430 hint->base.parser_func = (HintParseFunction) LeadingHintParse;
431 hint->relations = NIL;
433 return (Hint *) hint;
437 LeadingHintDelete(LeadingHint *hint)
442 list_free_deep(hint->relations);
447 SetHintCreate(char *hint_str, char *keyword)
451 hint = palloc(sizeof(SetHint));
452 hint->base.hint_str = hint_str;
453 hint->base.keyword = keyword;
454 hint->base.state = HINT_STATE_NOTUSED;
455 hint->base.delete_func = (HintDeleteFunction) SetHintDelete;
456 hint->base.parser_func = (HintParseFunction) SetHintParse;
460 return (Hint *) hint;
464 SetHintDelete(SetHint *hint)
481 hint = palloc(sizeof(PlanHint));
482 hint->hint_str = NULL;
483 hint->init_scan_mask = 0;
484 hint->nscan_hints = 0;
485 hint->max_scan_hints = 0;
486 hint->scan_hints = NULL;
487 hint->njoin_hints = 0;
488 hint->max_join_hints = 0;
489 hint->join_hints = NULL;
491 hint->join_hint_level = NULL;
492 hint->nleading_hints = 0;
493 hint->max_leading_hints = 0;
494 hint->leading_hints = NULL;
495 hint->context = superuser() ? PGC_SUSET : PGC_USERSET;
496 hint->nset_hints = 0;
497 hint->max_set_hints = 0;
498 hint->set_hints = NULL;
504 PlanHintDelete(PlanHint *hint)
512 pfree(hint->hint_str);
514 for (i = 0; i < hint->nscan_hints; i++)
515 ScanMethodHintDelete(hint->scan_hints[i]);
516 if (hint->scan_hints)
517 pfree(hint->scan_hints);
519 for (i = 0; i < hint->njoin_hints; i++)
520 JoinMethodHintDelete(hint->join_hints[i]);
521 if (hint->join_hints)
522 pfree(hint->join_hints);
524 for (i = 2; i <= hint->nlevel; i++)
525 list_free(hint->join_hint_level[i]);
526 if (hint->join_hint_level)
527 pfree(hint->join_hint_level);
529 for (i = 0; i < hint->nleading_hints; i++)
530 LeadingHintDelete(hint->leading_hints[i]);
531 if (hint->leading_hints)
532 pfree(hint->leading_hints);
534 for (i = 0; i < hint->nset_hints; i++)
535 SetHintDelete(hint->set_hints[i]);
537 pfree(hint->set_hints);
543 PlanHintIsEmpty(PlanHint *hint)
545 if (hint->nscan_hints == 0 &&
546 hint->njoin_hints == 0 &&
547 hint->nleading_hints == 0 &&
548 hint->nset_hints == 0)
554 /* TODO オブジェクト名のクォート処理を追加 */
556 all_hint_dump(PlanHint *hint, StringInfo buf, const char *title, HintStatus state)
561 appendStringInfo(buf, "%s:\n", title);
562 for (i = 0; i < hint->nscan_hints; i++)
564 ScanMethodHint *h = hint->scan_hints[i];
566 if (h->base.state != state)
569 appendStringInfo(buf, "%s(%s", h->base.keyword, h->relname);
570 foreach(l, h->indexnames)
571 appendStringInfo(buf, " %s", (char *) lfirst(l));
572 appendStringInfoString(buf, ")\n");
575 for (i = 0; i < hint->njoin_hints; i++)
577 JoinMethodHint *h = hint->join_hints[i];
580 if (h->base.state != state)
584 if (h->enforce_mask == ENABLE_ALL_JOIN)
587 appendStringInfo(buf, "%s(%s", h->base.keyword, h->relnames[0]);
588 for (j = 1; j < h->nrels; j++)
589 appendStringInfo(buf, " %s", h->relnames[j]);
590 appendStringInfoString(buf, ")\n");
593 for (i = 0; i < hint->nset_hints; i++)
595 SetHint *h = hint->set_hints[i];
597 if (h->base.state != state)
600 appendStringInfo(buf, "%s(%s %s)\n", HINT_SET, h->name, h->value);
603 for (i = 0; i < hint->nleading_hints; i++)
605 LeadingHint *h = hint->leading_hints[i];
608 if (h->base.state != state)
612 foreach(l, h->relations)
616 appendStringInfo(buf, "%s(%s", HINT_LEADING, (char *)lfirst(l));
620 appendStringInfo(buf, " %s", (char *)lfirst(l));
623 appendStringInfoString(buf, ")\n");
628 PlanHintDump(PlanHint *hint)
634 elog(LOG, "pg_hint_plan:\nno hint");
638 initStringInfo(&buf);
640 appendStringInfoString(&buf, "pg_hint_plan:\n");
641 all_hint_dump(hint, &buf, "used hint", HINT_STATE_USED);
642 all_hint_dump(hint, &buf, "not used hint", HINT_STATE_NOTUSED);
643 all_hint_dump(hint, &buf, "duplication hint", HINT_STATE_DUPLICATION);
644 all_hint_dump(hint, &buf, "error hint", HINT_STATE_ERROR);
646 elog(LOG, "%s", buf.data);
652 RelnameCmp(const void *a, const void *b)
654 const char *relnamea = *((const char **) a);
655 const char *relnameb = *((const char **) b);
657 return strcmp(relnamea, relnameb);
661 ScanMethodHintCmp(const void *a, const void *b, bool order)
663 const ScanMethodHint *hinta = *((const ScanMethodHint **) a);
664 const ScanMethodHint *hintb = *((const ScanMethodHint **) b);
667 if ((result = RelnameCmp(&hinta->relname, &hintb->relname)) != 0)
672 return hinta->base.hint_str - hintb->base.hint_str;
678 ScanMethodHintCmpIsOrder(const void *a, const void *b)
680 return ScanMethodHintCmp(a, b, true);
684 JoinMethodHintCmp(const void *a, const void *b, bool order)
686 const JoinMethodHint *hinta = *((const JoinMethodHint **) a);
687 const JoinMethodHint *hintb = *((const JoinMethodHint **) b);
690 if (hinta->nrels != hintb->nrels)
691 return hinta->nrels - hintb->nrels;
693 for (i = 0; i < hinta->nrels; i++)
696 if ((result = RelnameCmp(&hinta->relnames[i], &hintb->relnames[i])) != 0)
702 return hinta->base.hint_str - hintb->base.hint_str;
708 JoinMethodHintCmpIsOrder(const void *a, const void *b)
710 return JoinMethodHintCmp(a, b, true);
714 LeadingHintCmp(const void *a, const void *b, bool order)
716 const LeadingHint *hinta = *((const LeadingHint **) a);
717 const LeadingHint *hintb = *((const LeadingHint **) b);
721 return hinta->base.hint_str - hintb->base.hint_str;
728 LeadingHintCmpIsOrder(const void *a, const void *b)
730 return LeadingHintCmp(a, b, true);
735 SetHintCmp(const void *a, const void *b, bool order)
737 const SetHint *hinta = *((const SetHint **) a);
738 const SetHint *hintb = *((const SetHint **) b);
741 if ((result = strcmp(hinta->name, hintb->name)) != 0)
746 return hinta->base.hint_str - hintb->base.hint_str;
752 SetHintCmpIsOrder(const void *a, const void *b)
754 return SetHintCmp(a, b, true);
757 #if PG_VERSION_NUM < 90200
759 set_config_option_wrapper(const char *name, const char *value,
760 GucContext context, GucSource source,
761 GucAction action, bool changeVal, int elevel)
764 MemoryContext ccxt = CurrentMemoryContext;
768 result = set_config_option(name, value, context, source,
779 ecxt = MemoryContextSwitchTo(ccxt);
780 errdata = CopyErrorData();
781 ereport(elevel, (errcode(errdata->sqlerrcode),
782 errmsg("%s", errdata->message),
783 errdata->detail ? errdetail("%s", errdata->detail) : 0,
784 errdata->hint ? errhint("%s", errdata->hint) : 0));
785 FreeErrorData(errdata);
787 MemoryContextSwitchTo(ecxt);
794 #define set_config_option(name, value, context, source, \
795 action, changeVal, elevel) \
796 set_config_option_wrapper(name, value, context, source, \
797 action, changeVal, elevel)
801 set_config_options(SetHint **options, int noptions, GucContext context)
806 save_nestlevel = NewGUCNestLevel();
808 for (i = 0; i < noptions; i++)
810 SetHint *hint = options[i];
813 if (!hint_state_enabled(hint))
816 result = set_config_option(hint->name, hint->value, context,
817 PGC_S_SESSION, GUC_ACTION_SAVE, true,
818 pg_hint_plan_parse_messages);
820 hint->base.state = HINT_STATE_USED;
822 hint->base.state = HINT_STATE_ERROR;
825 return save_nestlevel;
828 #define SET_CONFIG_OPTION(name, type_bits) \
829 set_config_option((name), \
830 (enforce_mask & (type_bits)) ? "true" : "false", \
831 context, PGC_S_SESSION, GUC_ACTION_SAVE, true, ERROR)
834 set_scan_config_options(unsigned char enforce_mask, GucContext context)
836 SET_CONFIG_OPTION("enable_seqscan", ENABLE_SEQSCAN);
837 SET_CONFIG_OPTION("enable_indexscan", ENABLE_INDEXSCAN);
838 SET_CONFIG_OPTION("enable_bitmapscan", ENABLE_BITMAPSCAN);
839 SET_CONFIG_OPTION("enable_tidscan", ENABLE_TIDSCAN);
840 #if PG_VERSION_NUM >= 90200
841 SET_CONFIG_OPTION("enable_indexonlyscan", ENABLE_INDEXSCAN);
846 set_join_config_options(unsigned char enforce_mask, GucContext context)
848 SET_CONFIG_OPTION("enable_nestloop", ENABLE_NESTLOOP);
849 SET_CONFIG_OPTION("enable_mergejoin", ENABLE_MERGEJOIN);
850 SET_CONFIG_OPTION("enable_hashjoin", ENABLE_HASHJOIN);
858 parse_keyword(const char *str, StringInfo buf)
862 while (!isspace(*str) && *str != '(' && *str != '\0')
863 appendStringInfoCharMacro(buf, *str++);
869 skip_opened_parenthesis(const char *str)
875 parse_ereport(str, ("Opened parenthesis is necessary."));
885 skip_closed_parenthesis(const char *str)
891 parse_ereport(str, ("Closed parenthesis is necessary."));
901 * 二重引用符で囲まれているかもしれないトークンを読み取り word 引数に palloc
902 * で確保したバッファに格納してそのポインタを返す。
904 * 正常にパースできた場合は残りの文字列の先頭位置を、異常があった場合は NULL を
908 parse_quote_value(const char *str, char **word, char *value_type)
916 initStringInfo(&buf);
929 /* 二重引用符が閉じられていない場合はパース中断 */
933 parse_ereport(str, ("Unterminated quoted %s.", value_type));
938 * エスケープ対象のダブルクウォートをスキップする。
939 * もしブロックコメントの開始文字列や終了文字列もオブジェクト名とし
940 * て使用したい場合は、/ と * もエスケープ対象とすることで使用できる
941 * が、処理対象としていない。もしテーブル名にこれらの文字が含まれる
942 * 場合は、エイリアスを指定する必要がある。
952 if (isspace(*str) || *str == ')' || *str == '\0')
955 appendStringInfoCharMacro(&buf, *str++);
961 parse_ereport(str, ("%s is necessary.", value_type));
971 skip_option_delimiter(const char *str)
979 parse_ereport(str, ("Must be specified space."));
987 parse_hints(PlanHint *plan, Query *parse, const char *str)
992 initStringInfo(&buf);
995 const HintParser *parser;
997 /* in error message, we output the comment including the keyword. */
1000 /* parse only the keyword of the hint. */
1001 resetStringInfo(&buf);
1002 str = parse_keyword(str, &buf);
1004 for (parser = parsers; parser->keyword != NULL; parser++)
1006 char *keyword = parser->keyword;
1009 if (strcasecmp(buf.data, keyword) != 0)
1012 hint = parser->create_func(head, keyword);
1014 if (parser->have_paren)
1016 /* parser of each hint does parse in a parenthesis. */
1017 if ((str = skip_opened_parenthesis(str)) == NULL ||
1018 (str = hint->parser_func(hint, plan, parse, str)) == NULL ||
1019 (str = skip_closed_parenthesis(str)) == NULL)
1021 hint->delete_func(hint);
1028 if ((str = hint->parser_func(hint, plan, parse, str)) == NULL)
1030 hint->delete_func(hint);
1036 * 直前のヒントに括弧の指定がなければ次のヒントの間に空白が必要
1038 if (!isspace(*str) && *str == '\0')
1039 parse_ereport(str, ("Delimiter of the hint is necessary."));
1047 if (parser->keyword == NULL)
1049 parse_ereport(head, ("Keyword \"%s\" does not exist.", buf.data));
1059 * Do basic parsing of the query head comment.
1062 parse_head_comment(Query *parse)
1071 /* get client-supplied query string. */
1072 p = debug_query_string;
1076 /* extract query head comment. */
1077 len = strlen(HINT_START);
1079 if (strncmp(p, HINT_START, len))
1086 /* find hint end keyword. */
1087 if ((tail = strstr(p, HINT_END)) == NULL)
1089 parse_ereport(head, ("unterminated block comment"));
1093 /* 入れ子にしたブロックコメントはサポートしない */
1094 if ((head = strstr(p, BLOCK_COMMENT_START)) != NULL && head < tail)
1095 parse_ereport(head, ("block comments nest doesn't supported"));
1099 head = palloc(len + 1);
1100 memcpy(head, p, len);
1104 plan = PlanHintCreate();
1105 plan->hint_str = head;
1107 /* parse each hint. */
1108 parse_hints(plan, parse, p);
1110 /* When nothing specified a hint, we free PlanHint and returns NULL. */
1111 if (PlanHintIsEmpty(plan))
1113 PlanHintDelete(plan);
1117 /* 重複したscan method hintを検索する */
1118 qsort(plan->scan_hints, plan->nscan_hints, sizeof(ScanMethodHint *), ScanMethodHintCmpIsOrder);
1119 for (i = 0; i < plan->nscan_hints - 1; i++)
1121 if (ScanMethodHintCmp(plan->scan_hints + i,
1122 plan->scan_hints + i + 1, false) == 0)
1123 plan->scan_hints[i]->base.state = HINT_STATE_DUPLICATION;
1126 /* 重複したjoin method hintを検索する */
1127 qsort(plan->join_hints, plan->njoin_hints, sizeof(JoinMethodHint *), JoinMethodHintCmpIsOrder);
1128 for (i = 0; i < plan->njoin_hints - 1; i++)
1130 if (JoinMethodHintCmp(plan->join_hints + i,
1131 plan->join_hints + i + 1, false) == 0)
1132 plan->join_hints[i]->base.state = HINT_STATE_DUPLICATION;
1135 /* 重複したSet hintを検索する */
1136 qsort(plan->set_hints, plan->nset_hints, sizeof(SetHint *), SetHintCmpIsOrder);
1137 for (i = 0; i < plan->nset_hints - 1; i++)
1139 if (SetHintCmp(plan->set_hints + i,
1140 plan->set_hints + i + 1, false) == 0)
1141 plan->set_hints[i]->base.state = HINT_STATE_DUPLICATION;
1144 /* 重複したLeading hintを検索する */
1145 for (i = 0; i < plan->nleading_hints - 1; i++)
1147 if (LeadingHintCmp(plan->leading_hints + i,
1148 plan->leading_hints + i + 1, false) == 0)
1149 plan->leading_hints[i]->base.state = HINT_STATE_DUPLICATION;
1156 * スキャン方式ヒントのカッコ内をパースする
1159 ScanMethodHintParse(ScanMethodHint *hint, PlanHint *plan, Query *parse, const char *str)
1161 const char *keyword = hint->base.keyword;
1164 * スキャン方式のヒントでリレーション名が読み取れない場合はヒント無効
1166 if ((str = parse_quote_value(str, &hint->relname, "ralation name")) == NULL)
1172 * インデックスリストを受け付けるヒントであれば、インデックス参照をパース
1175 if (strcmp(keyword, HINT_INDEXSCAN) == 0 ||
1176 #if PG_VERSION_NUM >= 90200
1177 strcmp(keyword, HINT_INDEXONLYSCAN) == 0 ||
1179 strcmp(keyword, HINT_BITMAPSCAN) == 0)
1181 while (*str != ')' && *str != '\0')
1185 str = parse_quote_value(str, &indexname, "index name");
1189 hint->indexnames = lappend(hint->indexnames, indexname);
1194 /* カッコが閉じていなければヒント無効。 */
1195 skip_space(str); /* just in case */
1198 parse_ereport(str, ("Closed parenthesis is necessary."));
1203 * ヒントごとに決まっている許容スキャン方式をビットマスクとして設定
1205 if (strcasecmp(keyword, HINT_SEQSCAN) == 0)
1206 hint->enforce_mask = ENABLE_SEQSCAN;
1207 else if (strcasecmp(keyword, HINT_INDEXSCAN) == 0)
1208 hint->enforce_mask = ENABLE_INDEXSCAN;
1209 else if (strcasecmp(keyword, HINT_BITMAPSCAN) == 0)
1210 hint->enforce_mask = ENABLE_BITMAPSCAN;
1211 else if (strcasecmp(keyword, HINT_TIDSCAN) == 0)
1212 hint->enforce_mask = ENABLE_TIDSCAN;
1213 else if (strcasecmp(keyword, HINT_NOSEQSCAN) == 0)
1214 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_SEQSCAN;
1215 else if (strcasecmp(keyword, HINT_NOINDEXSCAN) == 0)
1216 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_INDEXSCAN;
1217 else if (strcasecmp(keyword, HINT_NOBITMAPSCAN) == 0)
1218 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_BITMAPSCAN;
1219 else if (strcasecmp(keyword, HINT_NOTIDSCAN) == 0)
1220 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_TIDSCAN;
1223 parse_ereport(str, ("unrecognized hint keyword \"%s\"", keyword));
1228 * 出来上がったヒント情報を追加。スロットが足りない場合は二倍に拡張する。
1230 if (plan->nscan_hints == 0)
1232 plan->max_scan_hints = HINT_ARRAY_DEFAULT_INITSIZE;
1233 plan->scan_hints = palloc(sizeof(ScanMethodHint *) * plan->max_scan_hints);
1235 else if (plan->nscan_hints == plan->max_scan_hints)
1237 plan->max_scan_hints *= 2;
1238 plan->scan_hints = repalloc(plan->scan_hints,
1239 sizeof(ScanMethodHint *) * plan->max_scan_hints);
1242 plan->scan_hints[plan->nscan_hints] = hint;
1243 plan->nscan_hints++;
1249 JoinMethodHintParse(JoinMethodHint *hint, PlanHint *plan, Query *parse, const char *str)
1252 const char *keyword = hint->base.keyword;
1256 hint->relnames = palloc(sizeof(char *));
1258 while ((str = parse_quote_value(str, &relname, "table name")) != NULL)
1261 hint->relnames = repalloc(hint->relnames, sizeof(char *) * hint->nrels);
1262 hint->relnames[hint->nrels - 1] = relname;
1272 /* Join 対象のテーブルは最低でも2つ指定する必要がある */
1273 if (hint->nrels < 2)
1275 parse_ereport(str, ("Specified relation more than two."));
1280 qsort(hint->relnames, hint->nrels, sizeof(char *), RelnameCmp);
1282 if (strcasecmp(keyword, HINT_NESTLOOP) == 0)
1283 hint->enforce_mask = ENABLE_NESTLOOP;
1284 else if (strcasecmp(keyword, HINT_MERGEJOIN) == 0)
1285 hint->enforce_mask = ENABLE_MERGEJOIN;
1286 else if (strcasecmp(keyword, HINT_HASHJOIN) == 0)
1287 hint->enforce_mask = ENABLE_HASHJOIN;
1288 else if (strcasecmp(keyword, HINT_NONESTLOOP) == 0)
1289 hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_NESTLOOP;
1290 else if (strcasecmp(keyword, HINT_NOMERGEJOIN) == 0)
1291 hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_MERGEJOIN;
1292 else if (strcasecmp(keyword, HINT_NOHASHJOIN) == 0)
1293 hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_HASHJOIN;
1296 parse_ereport(str, ("unrecognized hint keyword \"%s\"", keyword));
1300 if (plan->njoin_hints == 0)
1302 plan->max_join_hints = HINT_ARRAY_DEFAULT_INITSIZE;
1303 plan->join_hints = palloc(sizeof(JoinMethodHint *) * plan->max_join_hints);
1305 else if (plan->njoin_hints == plan->max_join_hints)
1307 plan->max_join_hints *= 2;
1308 plan->join_hints = repalloc(plan->join_hints,
1309 sizeof(JoinMethodHint *) * plan->max_join_hints);
1312 plan->join_hints[plan->njoin_hints] = hint;
1313 plan->njoin_hints++;
1319 LeadingHintParse(LeadingHint *hint, PlanHint *plan, Query *parse, const char *str)
1326 if ((str = parse_quote_value(str, &relname, "relation name")) == NULL)
1329 hint->relations = lappend(hint->relations, relname);
1338 parse_ereport(str, ("Must be specified space."));
1343 /* テーブル指定が1つのみの場合は、ヒントはエラーとする */
1344 if (list_length(hint->relations) == 1)
1346 parse_ereport(hint->base.hint_str,
1347 ("In %s hint, specified relation name 2 or more.", HINT_LEADING));
1348 hint->base.state = HINT_STATE_ERROR;
1351 if (plan->nleading_hints == 0)
1353 plan->max_leading_hints = HINT_ARRAY_DEFAULT_INITSIZE;
1354 plan->leading_hints = palloc(sizeof(LeadingHint *) * plan->max_leading_hints);
1356 else if (plan->nleading_hints == plan->max_leading_hints)
1358 plan->max_leading_hints *= 2;
1359 plan->leading_hints = repalloc(plan->leading_hints,
1360 sizeof(LeadingHint *) * plan->max_leading_hints);
1363 plan->leading_hints[plan->nleading_hints] = hint;
1364 plan->nleading_hints++;
1370 SetHintParse(SetHint *hint, PlanHint *plan, Query *parse, const char *str)
1372 if ((str = parse_quote_value(str, &hint->name, "parameter name")) == NULL ||
1373 (str = skip_option_delimiter(str)) == NULL ||
1374 (str = parse_quote_value(str, &hint->value, "parameter value")) == NULL)
1380 parse_ereport(str, ("Closed parenthesis is necessary."));
1384 if (plan->nset_hints == 0)
1386 plan->max_set_hints = HINT_ARRAY_DEFAULT_INITSIZE;
1387 plan->set_hints = palloc(sizeof(SetHint *) * plan->max_set_hints);
1389 else if (plan->nset_hints == plan->max_set_hints)
1391 plan->max_set_hints *= 2;
1392 plan->set_hints = repalloc(plan->set_hints,
1393 sizeof(SetHint *) * plan->max_set_hints);
1396 plan->set_hints[plan->nset_hints] = hint;
1402 static PlannedStmt *
1403 pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
1406 PlannedStmt *result;
1409 * hintが指定されない、または空のhintを指定された場合は通常のparser処理をお
1411 * 他のフック関数で実行されるhint処理をスキップするために、global 変数をNULL
1414 if (!pg_hint_plan_enable || (global = parse_head_comment(parse)) == NULL)
1418 if (prev_planner_hook)
1419 return (*prev_planner_hook) (parse, cursorOptions, boundParams);
1421 return standard_planner(parse, cursorOptions, boundParams);
1424 /* Set hint で指定されたGUCパラメータを設定する */
1425 save_nestlevel = set_config_options(global->set_hints, global->nset_hints,
1429 global->init_scan_mask |= ENABLE_SEQSCAN;
1430 if (enable_indexscan)
1431 global->init_scan_mask |= ENABLE_INDEXSCAN;
1432 if (enable_bitmapscan)
1433 global->init_scan_mask |= ENABLE_BITMAPSCAN;
1435 global->init_scan_mask |= ENABLE_TIDSCAN;
1436 #if PG_VERSION_NUM >= 90200
1437 if (enable_indexonlyscan)
1438 global->init_scan_mask |= ENABLE_INDEXONLYSCAN;
1442 * TODO ビュー定義で指定したテーブル数が1つの場合にもこのタイミングでGUCを変更する必
1445 if (list_length(parse->rtable) == 1 &&
1446 ((RangeTblEntry *) linitial(parse->rtable))->rtekind == RTE_RELATION)
1449 RangeTblEntry *rte = linitial(parse->rtable);
1451 for (i = 0; i < global->nscan_hints; i++)
1453 ScanMethodHint *hint = global->scan_hints[i];
1455 if (!hint_state_enabled(hint))
1458 if (RelnameCmp(&rte->eref->aliasname, &hint->relname) != 0)
1459 parse_ereport(hint->base.hint_str, ("Relation \"%s\" does not exist.", hint->relname));
1461 set_scan_config_options(hint->enforce_mask, global->context);
1465 if (prev_planner_hook)
1466 result = (*prev_planner_hook) (parse, cursorOptions, boundParams);
1468 result = standard_planner(parse, cursorOptions, boundParams);
1471 * Restore the GUC variables we set above.
1473 AtEOXact_GUC(true, save_nestlevel);
1476 * Print hint if debugging.
1478 if (pg_hint_plan_debug_print)
1480 PlanHintDump(global);
1482 elog_node_display(INFO, "rtable", parse->rtable, true);
1486 PlanHintDelete(global);
1493 * aliasnameと一致するSCANヒントを探す
1495 static ScanMethodHint *
1496 find_scan_hint(RangeTblEntry *rte)
1500 for (i = 0; i < global->nscan_hints; i++)
1502 ScanMethodHint *hint = global->scan_hints[i];
1504 if (!hint_state_enabled(hint))
1507 if (RelnameCmp(&rte->eref->aliasname, &hint->relname) == 0)
1515 pg_hint_plan_get_relation_info(PlannerInfo *root, Oid relationObjectId,
1516 bool inhparent, RelOptInfo *rel)
1518 ScanMethodHint *hint;
1523 if (prev_get_relation_info)
1524 (*prev_get_relation_info) (root, relationObjectId, inhparent, rel);
1526 /* 有効なヒントが指定されなかった場合は処理をスキップする。 */
1530 if (rel->reloptkind != RELOPT_BASEREL)
1533 /* scan hint が指定されない場合は、GUCパラメータをリセットする。 */
1534 if ((hint = find_scan_hint(root->simple_rte_array[rel->relid])) == NULL)
1537 set_scan_config_options(global->init_scan_mask, global->context);
1542 /* インデックスを全て削除し、スキャンに使えなくする */
1543 if (hint->enforce_mask == ENABLE_SEQSCAN ||
1544 hint->enforce_mask == ENABLE_TIDSCAN)
1546 list_free_deep(rel->indexlist);
1547 rel->indexlist = NIL;
1548 hint->base.state = HINT_STATE_USED;
1553 /* 後でパスを作り直すため、ここではなにもしない */
1554 if (hint->indexnames == NULL)
1557 /* 指定されたインデックスのみをのこす */
1559 for (cell = list_head(rel->indexlist); cell; cell = next)
1561 IndexOptInfo *info = (IndexOptInfo *) lfirst(cell);
1562 char *indexname = get_rel_name(info->indexoid);
1564 bool use_index = false;
1568 foreach(l, hint->indexnames)
1570 if (RelnameCmp(&indexname, &lfirst(l)) == 0)
1578 rel->indexlist = list_delete_cell(rel->indexlist, cell, prev);
1585 hint->base.state = HINT_STATE_USED;
1589 scan_relid_aliasname(PlannerInfo *root, char *aliasname, bool check_ambiguous, const char *str)
1591 /* TODO refnameRangeTblEntry を参考 */
1595 for (i = 1; i < root->simple_rel_array_size; i++)
1597 if (root->simple_rel_array[i] == NULL)
1600 Assert(i == root->simple_rel_array[i]->relid);
1602 if (RelnameCmp(&aliasname, &root->simple_rte_array[i]->eref->aliasname)
1606 if (!check_ambiguous)
1610 parse_ereport(str, ("relation name \"%s\" is ambiguous", aliasname));
1619 * relidビットマスクと一致するヒントを探す
1621 static JoinMethodHint *
1622 scan_join_hint(Relids joinrelids)
1627 join_hint = global->join_hint_level[bms_num_members(joinrelids)];
1628 foreach(l, join_hint)
1630 JoinMethodHint *hint = (JoinMethodHint *) lfirst(l);
1631 if (bms_equal(joinrelids, hint->joinrelids))
1639 * 結合方式のヒントを使用しやすい構造に変換する。
1642 transform_join_hints(PlanHint *plan, PlannerInfo *root, int level, List *initial_rels)
1649 plan->nlevel = root->simple_rel_array_size - 1;
1650 plan->join_hint_level = palloc0(sizeof(List *) * (root->simple_rel_array_size));
1651 for (i = 0; i < plan->njoin_hints; i++)
1653 JoinMethodHint *hint = plan->join_hints[i];
1657 if (!hint_state_enabled(hint))
1660 for (j = 0; j < hint->nrels; j++)
1662 char *relname = hint->relnames[j];
1664 relid = scan_relid_aliasname(root, relname, true, hint->base.hint_str);
1667 parse_ereport(hint->base.hint_str, ("Relation \"%s\" does not exist.", relname));
1671 hint->joinrelids = bms_add_member(hint->joinrelids, relid);
1677 plan->join_hint_level[hint->nrels] =
1678 lappend(plan->join_hint_level[hint->nrels], hint);
1681 if (plan->nleading_hints == 0)
1684 // TODO エラーヒントならばスキップ
1685 /* Leading hint は、全ての join 方式が有効な hint として登録する */
1688 foreach(l, plan->leading_hints[plan->nleading_hints - 1]->relations)
1690 char *relname = (char *)lfirst(l);
1691 JoinMethodHint *hint;
1693 i = scan_relid_aliasname(root, relname, true, plan->hint_str);
1696 parse_ereport(plan->hint_str, ("Relation \"%s\" does not exist.", relname));
1697 plan->leading_hints[plan->nleading_hints - 1]->base.state =
1702 joinrelids = bms_add_member(joinrelids, i);
1708 if (njoinrels > plan->nlevel)
1710 parse_ereport(plan->hint_str, ("In %s hint, specified relation name %d or less.", HINT_LEADING, plan->nlevel));
1711 plan->leading_hints[plan->nleading_hints - 1]->base.state =
1716 /* Leading で指定した組み合わせ以外の join hint を削除する */
1717 hint = scan_join_hint(joinrelids);
1718 list_free(plan->join_hint_level[njoinrels]);
1720 plan->join_hint_level[njoinrels] = lappend(NIL, hint);
1724 * Here relnames is not set, since Relids bitmap is sufficient to
1725 * control paths of this query afterwards.
1727 // TODO plan->hint_strをLeadingHint構造に変更後修正
1728 hint = (JoinMethodHint *) JoinMethodHintCreate(plan->hint_str, HINT_LEADING);
1729 hint->nrels = njoinrels;
1730 hint->enforce_mask = ENABLE_ALL_JOIN;
1731 hint->joinrelids = bms_copy(joinrelids);
1732 plan->join_hint_level[njoinrels] = lappend(NIL, hint);
1734 if (plan->njoin_hints == 0)
1736 plan->max_join_hints = HINT_ARRAY_DEFAULT_INITSIZE;
1737 plan->join_hints = palloc(sizeof(JoinMethodHint *) * plan->max_join_hints);
1739 else if (plan->njoin_hints == plan->max_join_hints)
1741 plan->max_join_hints *= 2;
1742 plan->join_hints = repalloc(plan->join_hints,
1743 sizeof(JoinMethodHint *) * plan->max_join_hints);
1746 plan->join_hints[plan->njoin_hints] = hint;
1747 plan->njoin_hints++;
1750 plan->leading_hints[plan->nleading_hints - 1]->base.state =
1755 if (global->nleading_hints > 0 &&
1756 hint_state_enabled(global->leading_hints[global->nleading_hints - 1]))
1757 set_join_config_options(DISABLE_ALL_JOIN, global->context);
1759 bms_free(joinrelids);
1763 rebuild_scan_path(PlanHint *plan, PlannerInfo *root, int level, List *initial_rels)
1766 int save_nestlevel = 0;
1768 for (i = 0; i < plan->nscan_hints; i++)
1770 ScanMethodHint *hint = plan->scan_hints[i];
1773 if (hint->enforce_mask == ENABLE_SEQSCAN)
1776 if (!hint_state_enabled(hint))
1779 foreach(l, initial_rels)
1781 RelOptInfo *rel = (RelOptInfo *) lfirst(l);
1782 RangeTblEntry *rte = root->simple_rte_array[rel->relid];
1785 * スキャン方式が選択できるリレーションのみ、スキャンパスを再生成
1788 if (rel->reloptkind != RELOPT_BASEREL ||
1789 rte->rtekind == RTE_VALUES ||
1790 RelnameCmp(&hint->relname, &rte->eref->aliasname) != 0)
1794 * 複数のスキャンヒントが指定されていた場合でも、1つのネストレベルで
1795 * スキャン関連のGUCパラメータを変更する。
1797 if (save_nestlevel == 0)
1798 save_nestlevel = NewGUCNestLevel();
1801 * TODO ヒントで指定されたScan方式が最安価でない場合のみ、Pathを生成
1804 set_scan_config_options(hint->enforce_mask, plan->context);
1806 rel->pathlist = NIL; /* TODO 解放 */
1807 set_plain_rel_pathlist(root, rel, rte);
1808 hint->base.state = HINT_STATE_USED;
1815 * Restore the GUC variables we set above.
1817 if (save_nestlevel != 0)
1818 AtEOXact_GUC(true, save_nestlevel);
1822 * src/backend/optimizer/path/joinrels.c
1823 * export make_join_rel() をラップする関数
1825 * ヒントにしたがって、enabele_* パラメータを変更した上で、make_join_rel()を
1829 pg_hint_plan_make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
1832 JoinMethodHint *hint;
1836 joinrelids = bms_union(rel1->relids, rel2->relids);
1837 hint = scan_join_hint(joinrelids);
1838 bms_free(joinrelids);
1841 return make_join_rel(root, rel1, rel2);
1843 save_nestlevel = NewGUCNestLevel();
1845 set_join_config_options(hint->enforce_mask, global->context);
1847 rel = make_join_rel(root, rel1, rel2);
1848 hint->base.state = HINT_STATE_USED;
1851 * Restore the GUC variables we set above.
1853 AtEOXact_GUC(true, save_nestlevel);
1859 pg_hint_plan_join_search(PlannerInfo *root, int levels_needed, List *initial_rels)
1862 * pg_hint_planが無効、または有効なヒントが1つも指定されなかった場合は、標準
1867 if (prev_join_search)
1868 return (*prev_join_search) (root, levels_needed, initial_rels);
1869 else if (enable_geqo && levels_needed >= geqo_threshold)
1870 return geqo(root, levels_needed, initial_rels);
1872 return standard_join_search(root, levels_needed, initial_rels);
1875 transform_join_hints(global, root, levels_needed, initial_rels);
1876 rebuild_scan_path(global, root, levels_needed, initial_rels);
1879 * GEQOを使用する条件を満たした場合は、GEQOを用いた結合方式の検索を行う。
1880 * このとき、スキャン方式のヒントとSetヒントのみが有効になり、結合方式や結合
1881 * 順序はヒント句は無効になりGEQOのアルゴリズムで決定される。
1883 if (enable_geqo && levels_needed >= geqo_threshold)
1884 return geqo(root, levels_needed, initial_rels);
1886 return standard_join_search_org(root, levels_needed, initial_rels);
1889 #define standard_join_search standard_join_search_org
1890 #define join_search_one_level pg_hint_plan_join_search_one_level
1891 #define make_join_rel pg_hint_plan_make_join_rel