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/geqo.h"
14 #include "optimizer/joininfo.h"
15 #include "optimizer/pathnode.h"
16 #include "optimizer/paths.h"
17 #include "optimizer/plancat.h"
18 #include "optimizer/planner.h"
19 #include "tcop/tcopprot.h"
20 #include "utils/lsyscache.h"
22 #ifdef PG_MODULE_MAGIC
26 #if PG_VERSION_NUM < 90100
27 #error unsupported PostgreSQL version
30 #define HINT_START "/*"
34 #define HINT_SEQSCAN "SeqScan"
35 #define HINT_INDEXSCAN "IndexScan"
36 #define HINT_BITMAPSCAN "BitmapScan"
37 #define HINT_TIDSCAN "TidScan"
38 #define HINT_NOSEQSCAN "NoSeqScan"
39 #define HINT_NOINDEXSCAN "NoIndexScan"
40 #define HINT_NOBITMAPSCAN "NoBitmapScan"
41 #define HINT_NOTIDSCAN "NoTidScan"
42 #if PG_VERSION_NUM >= 90200
43 #define HINT_INDEXONLYSCAN "IndexonlyScan"
44 #define HINT_NOINDEXONLYSCAN "NoIndexonlyScan"
46 #define HINT_NESTLOOP "NestLoop"
47 #define HINT_MERGEJOIN "MergeJoin"
48 #define HINT_HASHJOIN "HashJoin"
49 #define HINT_NONESTLOOP "NoNestLoop"
50 #define HINT_NOMERGEJOIN "NoMergeJoin"
51 #define HINT_NOHASHJOIN "NoHashJoin"
52 #define HINT_LEADING "Leading"
53 #define HINT_SET "Set"
56 #define HINT_ARRAY_DEFAULT_INITSIZE 8
58 #define parse_ereport(str, detail) \
59 ereport(pg_hint_plan_parse_messages, \
60 (errmsg("hint syntax error at or near \"%s\"", (str)), \
63 #define skip_space(str) \
64 while (isspace(*str)) \
69 ENABLE_SEQSCAN = 0x01,
70 ENABLE_INDEXSCAN = 0x02,
71 ENABLE_BITMAPSCAN = 0x04,
72 ENABLE_TIDSCAN = 0x08,
73 ENABLE_NESTLOOP = 0x10,
74 ENABLE_MERGEJOIN = 0x20,
75 ENABLE_HASHJOIN = 0x40
78 #define ENABLE_ALL_SCAN (ENABLE_SEQSCAN | ENABLE_INDEXSCAN | ENABLE_BITMAPSCAN \
80 #define ENABLE_ALL_JOIN (ENABLE_NESTLOOP | ENABLE_MERGEJOIN | ENABLE_HASHJOIN)
82 typedef struct Hint Hint;
83 typedef struct PlanHint PlanHint;
85 typedef Hint *(*HintCreateFunction) (char *hint_str, char *keyword);
86 typedef void (*HintDeleteFunction) (Hint *hint);
87 typedef const char *(*HintParseFunction) (Hint *hint, PlanHint *plan, Query *parse, const char *str);
90 /* common data for all hints. */
93 const char *hint_str; /* must not do pfree */
94 const char *keyword; /* must not do pfree */
96 HintDeleteFunction delete_func;
97 HintParseFunction parser_func;
100 /* scan method hints */
101 typedef struct ScanMethodHint
106 unsigned char enforce_mask;
109 /* join method hints */
110 typedef struct JoinMethodHint
115 unsigned char enforce_mask;
119 /* join order hints */
120 typedef struct LeadingHint
123 List *relations; /* relation names specified in Leading hint */
126 /* change a run-time parameter hints */
127 typedef struct SetHint
130 char *name; /* name of variable */
135 * Describes a context of hint processing.
139 char *hint_str; /* original hint string */
141 /* for scan method hints */
142 int nscan_hints; /* # of valid scan hints */
143 int max_scan_hints; /* # of slots for scan hints */
144 ScanMethodHint **scan_hints; /* parsed scan hints */
146 /* for join method hints */
147 int njoin_hints; /* # of valid join hints */
148 int max_join_hints; /* # of slots for join hints */
149 JoinMethodHint **join_hints; /* parsed join hints */
151 int nlevel; /* # of relations to be joined */
152 List **join_hint_level;
154 /* for Leading hints */
155 List *leading; /* relation names specified in Leading hint */
158 GucContext context; /* which GUC parameters can we set? */
159 List *set_hints; /* parsed Set hints */
163 * Describes a hint parser module which is bound with particular hint keyword.
165 typedef struct HintParser
169 HintCreateFunction create_func;
172 /* Module callbacks */
176 static PlannedStmt *pg_hint_plan_planner(Query *parse, int cursorOptions,
177 ParamListInfo boundParams);
178 static void pg_hint_plan_get_relation_info(PlannerInfo *root, Oid relationObjectId,
179 bool inhparent, RelOptInfo *rel);
180 static RelOptInfo *pg_hint_plan_join_search(PlannerInfo *root, int levels_needed,
183 static Hint *ScanMethodHintCreate(char *hint_str, char *keyword);
184 static void ScanMethodHintDelete(ScanMethodHint *hint);
185 static const char *ScanMethodHintParse(Hint *hint, PlanHint *plan, Query *parse, const char *str);
186 static Hint *JoinMethodHintCreate(char *hint_str, char *keyword);
187 static void JoinMethodHintDelete(JoinMethodHint *hint);
188 static const char *JoinMethodHintParse(Hint *hint, PlanHint *plan, Query *parse, const char *str);
189 static Hint *LeadingHintCreate(char *hint_str, char *keyword);
190 static void LeadingHintDelete(LeadingHint *hint);
191 static const char *LeadingHintParse(Hint *hint, PlanHint *plan, Query *parse, const char *str);
192 static Hint *SetHintCreate(char *hint_str, char *keyword);
193 static void SetHintDelete(SetHint *hint);
194 static const char *SetHintParse(Hint *hint, PlanHint *plan, Query *parse, const char *str);
196 static const char *OrderedHintParse(Hint *hint, PlanHint *plan, Query *parse, const char *str);
199 RelOptInfo *standard_join_search_org(PlannerInfo *root, int levels_needed, List *initial_rels);
200 void pg_hint_plan_join_search_one_level(PlannerInfo *root, int level);
201 static void make_rels_by_clause_joins(PlannerInfo *root, RelOptInfo *old_rel, ListCell *other_rels);
202 static void make_rels_by_clauseless_joins(PlannerInfo *root, RelOptInfo *old_rel, ListCell *other_rels);
203 static bool has_join_restriction(PlannerInfo *root, RelOptInfo *rel);
204 static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte);
208 static bool pg_hint_plan_enable = true;
209 static bool pg_hint_plan_debug_print = false;
210 static int pg_hint_plan_parse_messages = INFO;
212 static const struct config_enum_entry parse_messages_level_options[] = {
213 {"debug", DEBUG2, true},
214 {"debug5", DEBUG5, false},
215 {"debug4", DEBUG4, false},
216 {"debug3", DEBUG3, false},
217 {"debug2", DEBUG2, false},
218 {"debug1", DEBUG1, false},
220 {"info", INFO, false},
221 {"notice", NOTICE, false},
222 {"warning", WARNING, false},
223 {"error", ERROR, false},
225 {"fatal", FATAL, true},
226 {"panic", PANIC, true},
231 /* Saved hook values in case of unload */
232 static planner_hook_type prev_planner_hook = NULL;
233 static get_relation_info_hook_type prev_get_relation_info = NULL;
234 static join_search_hook_type prev_join_search = NULL;
236 /* フック関数をまたがって使用する情報を管理する */
237 static PlanHint *global = NULL;
239 static const HintParser parsers[] = {
240 {HINT_SEQSCAN, true, ScanMethodHintCreate},
241 {HINT_INDEXSCAN, true, ScanMethodHintCreate},
242 {HINT_BITMAPSCAN, true, ScanMethodHintCreate},
243 {HINT_TIDSCAN, true, ScanMethodHintCreate},
244 {HINT_NOSEQSCAN, true, ScanMethodHintCreate},
245 {HINT_NOINDEXSCAN, true, ScanMethodHintCreate},
246 {HINT_NOBITMAPSCAN, true, ScanMethodHintCreate},
247 {HINT_NOTIDSCAN, true, ScanMethodHintCreate},
248 #if PG_VERSION_NUM >= 90200
249 {HINT_INDEXONLYSCAN, true, ScanMethodHintCreate},
250 {HINT_NOINDEXONLYSCAN, true, ScanMethodHintCreate},
252 {HINT_NESTLOOP, true, JoinMethodHintCreate},
253 {HINT_MERGEJOIN, true, JoinMethodHintCreate},
254 {HINT_HASHJOIN, true, JoinMethodHintCreate},
255 {HINT_NONESTLOOP, true, JoinMethodHintCreate},
256 {HINT_NOMERGEJOIN, true, JoinMethodHintCreate},
257 {HINT_NOHASHJOIN, true, JoinMethodHintCreate},
258 {HINT_LEADING, true, LeadingHintCreate},
259 {HINT_SET, true, SetHintCreate},
264 * Module load callbacks
269 /* Define custom GUC variables. */
270 DefineCustomBoolVariable("pg_hint_plan.enable",
271 "Instructions or hints to the planner using block comments.",
273 &pg_hint_plan_enable,
281 DefineCustomBoolVariable("pg_hint_plan.debug_print",
282 "Logs each query's parse results of the hint.",
284 &pg_hint_plan_debug_print,
292 DefineCustomEnumVariable("pg_hint_plan.parse_messages",
293 "Messege level of the parse error.",
295 &pg_hint_plan_parse_messages,
297 parse_messages_level_options,
305 prev_planner_hook = planner_hook;
306 planner_hook = pg_hint_plan_planner;
307 prev_get_relation_info = get_relation_info_hook;
308 get_relation_info_hook = pg_hint_plan_get_relation_info;
309 prev_join_search = join_search_hook;
310 join_search_hook = pg_hint_plan_join_search;
314 * Module unload callback
320 /* Uninstall hooks. */
321 planner_hook = prev_planner_hook;
322 get_relation_info_hook = prev_get_relation_info;
323 join_search_hook = prev_join_search;
327 ScanMethodHintCreate(char *hint_str, char *keyword)
329 ScanMethodHint *hint;
331 hint = palloc(sizeof(ScanMethodHint));
332 hint->base.hint_str = hint_str;
333 hint->base.keyword = keyword;
334 hint->base.used = false;
335 hint->base.delete_func = (HintDeleteFunction) ScanMethodHintDelete;
336 hint->base.parser_func = (HintParseFunction) ScanMethodHintParse;
337 hint->relname = NULL;
338 hint->indexnames = NIL;
339 hint->enforce_mask = 0;
341 return (Hint *) hint;
345 ScanMethodHintDelete(ScanMethodHint *hint)
351 pfree(hint->relname);
352 list_free_deep(hint->indexnames);
357 JoinMethodHintCreate(char *hint_str, char *keyword)
359 JoinMethodHint *hint;
361 hint = palloc(sizeof(JoinMethodHint));
362 hint->base.hint_str = hint_str;
363 hint->base.keyword = keyword;
364 hint->base.used = false;
365 hint->base.delete_func = (HintDeleteFunction) JoinMethodHintDelete;
366 hint->base.parser_func = (HintParseFunction) JoinMethodHintParse;
368 hint->relnames = NULL;
369 hint->enforce_mask = 0;
370 hint->joinrelids = NULL;
372 return (Hint *) hint;
376 JoinMethodHintDelete(JoinMethodHint *hint)
385 for (i = 0; i < hint->nrels; i++)
386 pfree(hint->relnames[i]);
387 pfree(hint->relnames);
389 bms_free(hint->joinrelids);
394 LeadingHintCreate(char *hint_str, char *keyword)
398 hint = palloc(sizeof(LeadingHint));
399 hint->base.hint_str = hint_str;
400 hint->base.keyword = keyword;
401 hint->base.used = false;
402 hint->base.delete_func = (HintDeleteFunction)LeadingHintDelete;
403 hint->base.parser_func = (HintParseFunction) LeadingHintParse;
404 hint->relations = NIL;
406 return (Hint *) hint;
410 LeadingHintDelete(LeadingHint *hint)
415 list_free_deep(hint->relations);
420 SetHintCreate(char *hint_str, char *keyword)
424 hint = palloc(sizeof(SetHint));
425 hint->base.hint_str = hint_str;
426 hint->base.keyword = keyword;
427 hint->base.used = false;
428 hint->base.delete_func = (HintDeleteFunction) SetHintDelete;
429 hint->base.parser_func = (HintParseFunction) SetHintParse;
433 return (Hint *) hint;
437 SetHintDelete(SetHint *hint)
454 hint = palloc(sizeof(PlanHint));
455 hint->hint_str = NULL;
456 hint->nscan_hints = 0;
457 hint->max_scan_hints = 0;
458 hint->scan_hints = NULL;
459 hint->njoin_hints = 0;
460 hint->max_join_hints = 0;
461 hint->join_hints = NULL;
463 hint->join_hint_level = NULL;
465 hint->context = superuser() ? PGC_SUSET : PGC_USERSET;
466 hint->set_hints = NIL;
472 PlanHintDelete(PlanHint *hint)
481 pfree(hint->hint_str);
483 for (i = 0; i < hint->nscan_hints; i++)
484 ScanMethodHintDelete(hint->scan_hints[i]);
485 if (hint->scan_hints)
486 pfree(hint->scan_hints);
488 for (i = 0; i < hint->njoin_hints; i++)
489 JoinMethodHintDelete(hint->join_hints[i]);
490 if (hint->join_hints)
491 pfree(hint->join_hints);
493 for (i = 2; i <= hint->nlevel; i++)
494 list_free(hint->join_hint_level[i]);
495 if (hint->join_hint_level)
496 pfree(hint->join_hint_level);
498 list_free_deep(hint->leading);
500 foreach(l, hint->set_hints)
501 SetHintDelete((SetHint *) lfirst(l));
502 list_free(hint->set_hints);
508 PlanHintIsempty(PlanHint *hint)
510 if (hint->nscan_hints == 0 &&
511 hint->njoin_hints == 0 &&
512 hint->leading == NIL &&
513 hint->set_hints == NIL)
519 /* TODO オブジェクト名のクォート処理を追加 */
521 PlanHintDump(PlanHint *hint)
526 bool is_first = true;
530 elog(LOG, "no hint");
534 initStringInfo(&buf);
535 appendStringInfo(&buf, "/*\n");
536 for (i = 0; i < hint->nscan_hints; i++)
538 ScanMethodHint *h = hint->scan_hints[i];
540 switch(h->enforce_mask)
542 case(ENABLE_SEQSCAN):
543 appendStringInfo(&buf, "%s(", HINT_SEQSCAN);
545 case(ENABLE_INDEXSCAN):
546 appendStringInfo(&buf, "%s(", HINT_INDEXSCAN);
548 case(ENABLE_BITMAPSCAN):
549 appendStringInfo(&buf, "%s(", HINT_BITMAPSCAN);
551 case(ENABLE_TIDSCAN):
552 appendStringInfo(&buf, "%s(", HINT_TIDSCAN);
554 case(ENABLE_INDEXSCAN | ENABLE_BITMAPSCAN | ENABLE_TIDSCAN):
555 appendStringInfo(&buf, "%s(", HINT_NOSEQSCAN);
557 case(ENABLE_SEQSCAN | ENABLE_BITMAPSCAN | ENABLE_TIDSCAN):
558 appendStringInfo(&buf, "%s(", HINT_NOINDEXSCAN);
560 case(ENABLE_SEQSCAN | ENABLE_INDEXSCAN | ENABLE_TIDSCAN):
561 appendStringInfo(&buf, "%s(", HINT_NOBITMAPSCAN);
563 case(ENABLE_SEQSCAN | ENABLE_INDEXSCAN | ENABLE_BITMAPSCAN):
564 appendStringInfo(&buf, "%s(", HINT_NOTIDSCAN);
567 appendStringInfoString(&buf, "\?\?\?(");
570 appendStringInfo(&buf, "%s", h->relname);
571 foreach(n, h->indexnames)
572 appendStringInfo(&buf, " %s", (char *) lfirst(n));
573 appendStringInfoString(&buf, ")\n");
576 for (i = 0; i < hint->njoin_hints; i++)
578 JoinMethodHint *h = hint->join_hints[i];
580 switch(h->enforce_mask)
582 case(ENABLE_NESTLOOP):
583 appendStringInfo(&buf, "%s(", HINT_NESTLOOP);
585 case(ENABLE_MERGEJOIN):
586 appendStringInfo(&buf, "%s(", HINT_MERGEJOIN);
588 case(ENABLE_HASHJOIN):
589 appendStringInfo(&buf, "%s(", HINT_HASHJOIN);
591 case(ENABLE_ALL_JOIN ^ ENABLE_NESTLOOP):
592 appendStringInfo(&buf, "%s(", HINT_NONESTLOOP);
594 case(ENABLE_ALL_JOIN ^ ENABLE_MERGEJOIN):
595 appendStringInfo(&buf, "%s(", HINT_NOMERGEJOIN);
597 case(ENABLE_ALL_JOIN ^ ENABLE_HASHJOIN):
598 appendStringInfo(&buf, "%s(", HINT_NOHASHJOIN);
600 case(ENABLE_ALL_JOIN):
603 appendStringInfoString(&buf, "\?\?\?(");
606 appendStringInfo(&buf, "%s", h->relnames[0]);
607 for (i = 1; i < h->nrels; i++)
608 appendStringInfo(&buf, " %s", h->relnames[i]);
609 appendStringInfoString(&buf, ")\n");
612 foreach(l, hint->set_hints)
614 SetHint *h = (SetHint *) lfirst(l);
615 appendStringInfo(&buf, "%s(%s %s)\n", HINT_SET, h->name, h->value);
618 foreach(l, hint->leading)
622 appendStringInfo(&buf, "%s(%s", HINT_LEADING, (char *)lfirst(l));
626 appendStringInfo(&buf, " %s", (char *)lfirst(l));
629 appendStringInfoString(&buf, ")\n");
631 appendStringInfoString(&buf, "*/");
633 elog(LOG, "%s", buf.data);
639 RelnameCmp(const void *a, const void *b)
641 const char *relnamea = *((const char **) a);
642 const char *relnameb = *((const char **) b);
644 return strcmp(relnamea, relnameb);
648 ScanMethodHintCmp(const void *a, const void *b, bool order)
650 const ScanMethodHint *hinta = *((const ScanMethodHint **) a);
651 const ScanMethodHint *hintb = *((const ScanMethodHint **) b);
654 if ((result = RelnameCmp(&hinta->relname, &hintb->relname)) != 0)
659 return hinta->base.hint_str - hintb->base.hint_str;
665 ScanMethodHintCmpIsOrder(const void *a, const void *b)
667 return ScanMethodHintCmp(a, b, true);
671 JoinMethodHintCmp(const void *a, const void *b, bool order)
673 const JoinMethodHint *hinta = *((const JoinMethodHint **) a);
674 const JoinMethodHint *hintb = *((const JoinMethodHint **) b);
676 if (hinta->nrels == hintb->nrels)
679 for (i = 0; i < hinta->nrels; i++)
682 if ((result = RelnameCmp(&hinta->relnames[i], &hintb->relnames[i])) != 0)
688 return hinta->base.hint_str - hintb->base.hint_str;
693 return hinta->nrels - hintb->nrels;
697 JoinMethodHintCmpIsOrder(const void *a, const void *b)
699 return JoinMethodHintCmp(a, b, true);
702 #if PG_VERSION_NUM < 90200
704 set_config_option_wrapper(const char *name, const char *value,
705 GucContext context, GucSource source,
706 GucAction action, bool changeVal, int elevel)
709 MemoryContext ccxt = CurrentMemoryContext;
713 result = set_config_option(name, value, context, source,
724 ecxt = MemoryContextSwitchTo(ccxt);
725 errdata = CopyErrorData();
726 ereport(elevel, (errcode(errdata->sqlerrcode),
727 errmsg("%s", errdata->message),
728 errdata->detail ? errdetail("%s", errdata->detail) : 0,
729 errdata->hint ? errhint("%s", errdata->hint) : 0));
730 FreeErrorData(errdata);
732 MemoryContextSwitchTo(ecxt);
739 #define set_config_option(name, value, context, source, \
740 action, changeVal, elevel) \
741 set_config_option_wrapper(name, value, context, source, \
742 action, changeVal, elevel)
746 set_config_options(List *options, GucContext context)
752 save_nestlevel = NewGUCNestLevel();
756 SetHint *hint = (SetHint *) lfirst(l);
759 result = set_config_option(hint->name, hint->value, context,
760 PGC_S_SESSION, GUC_ACTION_SAVE, true,
761 pg_hint_plan_parse_messages);
764 return save_nestlevel;
767 #define SET_CONFIG_OPTION(name, enforce_mask, type_bits) \
768 set_config_option((name), \
769 ((enforce_mask) & (type_bits)) ? "true" : "false", \
770 context, PGC_S_SESSION, GUC_ACTION_SAVE, true, ERROR)
773 set_join_config_options(unsigned char enforce_mask, GucContext context)
775 SET_CONFIG_OPTION("enable_nestloop", enforce_mask, ENABLE_NESTLOOP);
776 SET_CONFIG_OPTION("enable_mergejoin", enforce_mask, ENABLE_MERGEJOIN);
777 SET_CONFIG_OPTION("enable_hashjoin", enforce_mask, ENABLE_HASHJOIN);
781 set_scan_config_options(unsigned char enforce_mask, GucContext context)
783 SET_CONFIG_OPTION("enable_seqscan", enforce_mask, ENABLE_SEQSCAN);
784 SET_CONFIG_OPTION("enable_indexscan", enforce_mask, ENABLE_INDEXSCAN);
785 SET_CONFIG_OPTION("enable_bitmapscan", enforce_mask, ENABLE_BITMAPSCAN);
786 SET_CONFIG_OPTION("enable_tidscan", enforce_mask, ENABLE_TIDSCAN);
787 #if PG_VERSION_NUM >= 90200
788 SET_CONFIG_OPTION("enable_indexonlyscan", enforce_mask, ENABLE_INDEXSCAN);
797 parse_keyword(const char *str, StringInfo buf)
801 while (!isspace(*str) && *str != '(' && *str != '\0')
802 appendStringInfoChar(buf, *str++);
808 skip_opened_parenthesis(const char *str)
814 parse_ereport(str, ("Opened parenthesis is necessary."));
824 skip_closed_parenthesis(const char *str)
830 parse_ereport(str, ("Closed parenthesis is necessary."));
840 * 二重引用符で囲まれているかもしれないトークンを読み取り word 引数に palloc
841 * で確保したバッファに格納してそのポインタを返す。
843 * 正常にパースできた場合は残りの文字列の先頭位置を、異常があった場合は NULL を
847 parse_quote_value(const char *str, char **word, char *value_type)
855 initStringInfo(&buf);
868 /* 二重引用符が閉じられていない場合はパース中断 */
872 parse_ereport(str, ("Unterminated quoted %s.", value_type));
877 * エスケープ対象のダブルクウォートをスキップする。
878 * もしブロックコメントの開始文字列や終了文字列もオブジェクト名とし
879 * て使用したい場合は、/ と * もエスケープ対象とすることで使用できる
880 * が、処理対象としていない。もしテーブル名にこれらの文字が含まれる
881 * 場合は、エイリアスを指定する必要がある。
891 if (isspace(*str) || *str == ')' || *str == '\0')
894 appendStringInfoCharMacro(&buf, *str++);
900 parse_ereport(str, ("%s is necessary.", value_type));
910 skip_option_delimiter(const char *str)
918 parse_ereport(str, ("Must be specified space."));
926 parse_hints(PlanHint *plan, Query *parse, const char *str)
931 initStringInfo(&buf);
934 const HintParser *parser;
936 /* in error message, we output the comment including the keyword. */
939 /* parse only the keyword of the hint. */
940 resetStringInfo(&buf);
941 str = parse_keyword(str, &buf);
943 for (parser = parsers; parser->keyword != NULL; parser++)
945 char *keyword = parser->keyword;
948 if (strcasecmp(buf.data, keyword) != 0)
951 hint = parser->create_func(head, keyword);
953 if (parser->have_paren)
955 /* parser of each hint does parse in a parenthesis. */
956 if ((str = skip_opened_parenthesis(str)) == NULL ||
957 (str = hint->parser_func(hint, plan, parse, str)) == NULL ||
958 (str = skip_closed_parenthesis(str)) == NULL)
960 hint->delete_func(hint);
967 if ((str = hint->parser_func(hint, plan, parse, str)) == NULL)
969 hint->delete_func(hint);
975 * 直前のヒントに括弧の指定がなければ次のヒントの間に空白が必要
977 if (!isspace(*str) && *str == '\0')
978 parse_ereport(str, ("Delimiter of the hint is necessary."));
986 if (parser->keyword == NULL)
988 parse_ereport(head, ("Keyword \"%s\" does not exist.", buf.data));
1000 * Do basic parsing of the query head comment.
1003 parse_head_comment(Query *parse)
1012 /* get client-supplied query string. */
1013 p = debug_query_string;
1017 /* extract query head comment. */
1018 len = strlen(HINT_START);
1020 if (strncmp(p, HINT_START, len))
1026 if ((tail = strstr(p, HINT_END)) == NULL)
1028 parse_ereport(debug_query_string, ("unterminated /* comment"));
1032 /* 入れ子にしたブロックコメントはサポートしない */
1033 if ((head = strstr(p, HINT_START)) != NULL && head < tail)
1034 parse_ereport(head, ("block comments nest doesn't supported"));
1038 head = palloc(len + 1);
1039 memcpy(head, p, len);
1043 plan = PlanHintCreate();
1044 plan->hint_str = head;
1046 /* parse each hint. */
1047 if (!parse_hints(plan, parse, p))
1050 /* 重複したScan条件をを除外する */
1051 qsort(plan->scan_hints, plan->nscan_hints, sizeof(ScanMethodHint *), ScanMethodHintCmpIsOrder);
1052 for (i = 0; i < plan->nscan_hints - 1;)
1054 int result = ScanMethodHintCmp(plan->scan_hints + i,
1055 plan->scan_hints + i + 1, false);
1060 /* 後で指定したヒントを有効にする */
1061 plan->nscan_hints--;
1062 memmove(plan->scan_hints + i, plan->scan_hints + i + 1,
1063 sizeof(ScanMethodHint *) * (plan->nscan_hints - i));
1067 /* 重複したJoin条件をを除外する */
1068 qsort(plan->join_hints, plan->njoin_hints, sizeof(JoinMethodHint *), JoinMethodHintCmpIsOrder);
1069 for (i = 0; i < plan->njoin_hints - 1;)
1071 int result = JoinMethodHintCmp(plan->join_hints + i,
1072 plan->join_hints + i + 1, false);
1077 /* 後で指定したヒントを有効にする */
1078 plan->njoin_hints--;
1079 memmove(plan->join_hints + i, plan->join_hints + i + 1,
1080 sizeof(JoinMethodHint *) * (plan->njoin_hints - i));
1088 * スキャン方式ヒントのカッコ内をパースする
1091 ScanMethodHintParse(Hint *base_hint, PlanHint *plan, Query *parse, const char *str)
1093 ScanMethodHint *hint = (ScanMethodHint *) base_hint;
1094 const char *keyword = hint->base.keyword;
1097 * スキャン方式のヒントでリレーション名が読み取れない場合はヒント無効
1099 if ((str = parse_quote_value(str, &hint->relname, "ralation name")) == NULL)
1105 * インデックスリストを受け付けるヒントであれば、インデックス参照をパース
1108 if (strcmp(keyword, HINT_INDEXSCAN) == 0 ||
1109 #if PG_VERSION_NUM >= 90200
1110 strcmp(keyword, HINT_INDEXONLYSCAN) == 0 ||
1112 strcmp(keyword, HINT_BITMAPSCAN) == 0)
1114 while (*str != ')' && *str != '\0')
1118 str = parse_quote_value(str, &indexname, "index name");
1122 hint->indexnames = lappend(hint->indexnames, indexname);
1127 /* カッコが閉じていなければヒント無効。 */
1128 skip_space(str); /* just in case */
1131 parse_ereport(str, ("Closed parenthesis is necessary."));
1136 * ヒントごとに決まっている許容スキャン方式をビットマスクとして設定
1138 if (strcasecmp(keyword, HINT_SEQSCAN) == 0)
1139 hint->enforce_mask = ENABLE_SEQSCAN;
1140 else if (strcasecmp(keyword, HINT_INDEXSCAN) == 0)
1141 hint->enforce_mask = ENABLE_INDEXSCAN;
1142 else if (strcasecmp(keyword, HINT_BITMAPSCAN) == 0)
1143 hint->enforce_mask = ENABLE_BITMAPSCAN;
1144 else if (strcasecmp(keyword, HINT_TIDSCAN) == 0)
1145 hint->enforce_mask = ENABLE_TIDSCAN;
1146 else if (strcasecmp(keyword, HINT_NOSEQSCAN) == 0)
1147 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_SEQSCAN;
1148 else if (strcasecmp(keyword, HINT_NOINDEXSCAN) == 0)
1149 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_INDEXSCAN;
1150 else if (strcasecmp(keyword, HINT_NOBITMAPSCAN) == 0)
1151 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_BITMAPSCAN;
1152 else if (strcasecmp(keyword, HINT_NOTIDSCAN) == 0)
1153 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_TIDSCAN;
1156 parse_ereport(str, ("unrecognized hint keyword \"%s\"", keyword));
1161 * 出来上がったヒント情報を追加。スロットが足りない場合は二倍に拡張する。
1163 if (plan->nscan_hints == 0)
1165 plan->max_scan_hints = HINT_ARRAY_DEFAULT_INITSIZE;
1166 plan->scan_hints = palloc(sizeof(ScanMethodHint *) * plan->max_scan_hints);
1168 else if (plan->nscan_hints == plan->max_scan_hints)
1170 plan->max_scan_hints *= 2;
1171 plan->scan_hints = repalloc(plan->scan_hints,
1172 sizeof(ScanMethodHint *) * plan->max_scan_hints);
1174 plan->scan_hints[plan->nscan_hints] = hint;
1175 plan->nscan_hints++;
1181 JoinMethodHintParse(Hint *base_hint, PlanHint *plan, Query *parse, const char *str)
1184 JoinMethodHint *hint = (JoinMethodHint *) base_hint;
1185 const char *keyword = hint->base.keyword;
1189 hint->relnames = palloc(sizeof(char *));
1191 while ((str = parse_quote_value(str, &relname, "table name")) != NULL)
1194 hint->relnames = repalloc(hint->relnames, sizeof(char *) * hint->nrels);
1195 hint->relnames[hint->nrels - 1] = relname;
1205 /* Join 対象のテーブルは最低でも2つ指定する必要がある */
1206 if (hint->nrels < 2)
1208 parse_ereport(str, ("Specified relation more than two."));
1213 qsort(hint->relnames, hint->nrels, sizeof(char *), RelnameCmp);
1215 if (strcasecmp(keyword, HINT_NESTLOOP) == 0)
1216 hint->enforce_mask = ENABLE_NESTLOOP;
1217 else if (strcasecmp(keyword, HINT_MERGEJOIN) == 0)
1218 hint->enforce_mask = ENABLE_MERGEJOIN;
1219 else if (strcasecmp(keyword, HINT_HASHJOIN) == 0)
1220 hint->enforce_mask = ENABLE_HASHJOIN;
1221 else if (strcasecmp(keyword, HINT_NONESTLOOP) == 0)
1222 hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_NESTLOOP;
1223 else if (strcasecmp(keyword, HINT_NOMERGEJOIN) == 0)
1224 hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_MERGEJOIN;
1225 else if (strcasecmp(keyword, HINT_NOHASHJOIN) == 0)
1226 hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_HASHJOIN;
1229 parse_ereport(str, ("unrecognized hint keyword \"%s\"", keyword));
1233 if (plan->njoin_hints == 0)
1235 plan->max_join_hints = HINT_ARRAY_DEFAULT_INITSIZE;
1236 plan->join_hints = palloc(sizeof(JoinMethodHint *) * plan->max_join_hints);
1238 else if (plan->njoin_hints == plan->max_join_hints)
1240 plan->max_join_hints *= 2;
1241 plan->join_hints = repalloc(plan->join_hints,
1242 sizeof(JoinMethodHint *) * plan->max_join_hints);
1245 plan->join_hints[plan->njoin_hints] = hint;
1246 plan->njoin_hints++;
1252 LeadingHintParse(Hint *hint, PlanHint *plan, Query *parse, const char *str)
1257 * すでに指定済みの場合は、後で指定したヒントが有効にするため、登録済みの
1260 list_free_deep(plan->leading);
1261 plan->leading = NIL;
1263 while ((str = parse_quote_value(str, &relname, "relation name")) != NULL)
1267 plan->leading = lappend(plan->leading, relname);
1276 parse_ereport(str, ("Must be specified space."));
1281 /* テーブル指定が1つのみの場合は、ヒントを無効にし、パースを続ける */
1282 if (list_length(plan->leading) == 1)
1284 parse_ereport(str, ("In %s hint, specified relation name 2 or more.", HINT_LEADING));
1285 list_free_deep(plan->leading);
1286 plan->leading = NIL;
1293 SetHintParse(Hint *base_hint, PlanHint *plan, Query *parse, const char *str)
1295 SetHint *hint = (SetHint *) base_hint;
1297 if ((str = parse_quote_value(str, &hint->name, "parameter name")) == NULL ||
1298 (str = skip_option_delimiter(str)) == NULL ||
1299 (str = parse_quote_value(str, &hint->value, "parameter value")) == NULL)
1305 parse_ereport(str, ("Closed parenthesis is necessary."));
1308 plan->set_hints = lappend(plan->set_hints, hint);
1315 * Oracle の ORDERD ヒントの実装
1318 OrderedHintParse(Hint *base_hint, PlanHint *plan, Query *parse, const char *str);
1320 SetHint *hint = (SetHint *) base_hint;
1322 hint->name = pstrdup("join_collapse_limit");
1323 hint->value = pstrdup("1");
1324 plan->set_hints = lappend(plan->set_hints, hint);
1330 static PlannedStmt *
1331 pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
1334 PlannedStmt *result;
1337 * hintが指定されない、または空のhintを指定された場合は通常のparser処理をお
1339 * 他のフック関数で実行されるhint処理をスキップするために、global 変数をNULL
1342 if (!pg_hint_plan_enable ||
1343 (global = parse_head_comment(parse)) == NULL ||
1344 PlanHintIsempty(global))
1346 PlanHintDelete(global);
1349 if (prev_planner_hook)
1350 return (*prev_planner_hook) (parse, cursorOptions, boundParams);
1352 return standard_planner(parse, cursorOptions, boundParams);
1355 /* Set hint で指定されたGUCパラメータを設定する */
1356 save_nestlevel = set_config_options(global->set_hints, global->context);
1358 if (global->leading != NIL)
1359 set_join_config_options(0, global->context);
1362 * TODO ビュー定義で指定したテーブル数が1つの場合にもこのタイミングでGUCを変更する必
1365 if (list_length(parse->rtable) == 1 &&
1366 ((RangeTblEntry *) linitial(parse->rtable))->rtekind == RTE_RELATION)
1369 RangeTblEntry *rte = linitial(parse->rtable);
1371 for (i = 0; i < global->nscan_hints; i++)
1373 ScanMethodHint *hint = global->scan_hints[i];
1375 if (RelnameCmp(&rte->eref->aliasname, &hint->relname) != 0)
1376 parse_ereport(hint->base.hint_str, ("Relation \"%s\" does not exist.", hint->relname));
1378 set_scan_config_options(hint->enforce_mask, global->context);
1382 if (prev_planner_hook)
1383 result = (*prev_planner_hook) (parse, cursorOptions, boundParams);
1385 result = standard_planner(parse, cursorOptions, boundParams);
1388 * Restore the GUC variables we set above.
1390 AtEOXact_GUC(true, save_nestlevel);
1392 if (pg_hint_plan_debug_print)
1394 PlanHintDump(global);
1396 elog_node_display(INFO, "rtable", parse->rtable, true);
1400 PlanHintDelete(global);
1407 * aliasnameと一致するSCANヒントを探す
1409 static ScanMethodHint *
1410 find_scan_hint(RangeTblEntry *rte)
1414 for (i = 0; i < global->nscan_hints; i++)
1416 ScanMethodHint *hint = global->scan_hints[i];
1418 if (RelnameCmp(&rte->eref->aliasname, &hint->relname) == 0)
1426 pg_hint_plan_get_relation_info(PlannerInfo *root, Oid relationObjectId,
1427 bool inhparent, RelOptInfo *rel)
1429 ScanMethodHint *hint;
1434 if (prev_get_relation_info)
1435 (*prev_get_relation_info) (root, relationObjectId, inhparent, rel);
1437 /* 有効なヒントが指定されなかった場合は処理をスキップする。 */
1441 if (rel->reloptkind != RELOPT_BASEREL)
1444 if ((hint = find_scan_hint(root->simple_rte_array[rel->relid])) == NULL)
1447 /* インデックスを全て削除し、スキャンに使えなくする */
1448 if (hint->enforce_mask == ENABLE_SEQSCAN)
1450 list_free_deep(rel->indexlist);
1451 rel->indexlist = NIL;
1456 /* 後でパスを作り直すため、ここではなにもしない */
1457 if (hint->indexnames == NULL)
1460 /* 指定されたインデックスのみをのこす */
1462 for (cell = list_head(rel->indexlist); cell; cell = next)
1464 IndexOptInfo *info = (IndexOptInfo *) lfirst(cell);
1465 char *indexname = get_rel_name(info->indexoid);
1467 bool use_index = false;
1471 foreach(l, hint->indexnames)
1473 if (RelnameCmp(&indexname, &lfirst(l)) == 0)
1481 rel->indexlist = list_delete_cell(rel->indexlist, cell, prev);
1490 scan_relid_aliasname(PlannerInfo *root, char *aliasname, bool check_ambiguous, const char *str)
1492 /* TODO refnameRangeTblEntry を参考 */
1496 for (i = 1; i < root->simple_rel_array_size; i++)
1498 if (root->simple_rel_array[i] == NULL)
1501 Assert(i == root->simple_rel_array[i]->relid);
1503 if (RelnameCmp(&aliasname, &root->simple_rte_array[i]->eref->aliasname)
1507 if (!check_ambiguous)
1511 parse_ereport(str, ("relation name \"%s\" is ambiguous", aliasname));
1520 * relidビットマスクと一致するヒントを探す
1522 static JoinMethodHint *
1523 scan_join_hint(Relids joinrelids)
1528 join_hint = global->join_hint_level[bms_num_members(joinrelids)];
1529 foreach(l, join_hint)
1531 JoinMethodHint *hint = (JoinMethodHint *) lfirst(l);
1532 if (bms_equal(joinrelids, hint->joinrelids))
1540 * 結合方式のヒントを使用しやすい構造に変換する。
1543 transform_join_hints(PlanHint *plan, PlannerInfo *root, int level, List *initial_rels)
1550 plan->nlevel = root->simple_rel_array_size - 1;
1551 plan->join_hint_level = palloc0(sizeof(List *) * (root->simple_rel_array_size));
1552 for (i = 0; i < plan->njoin_hints; i++)
1554 JoinMethodHint *hint = plan->join_hints[i];
1558 for (j = 0; j < hint->nrels; j++)
1560 char *relname = hint->relnames[j];
1562 relid = scan_relid_aliasname(root, relname, true, hint->base.hint_str);
1565 parse_ereport(hint->base.hint_str, ("Relation \"%s\" does not exist.", relname));
1569 hint->joinrelids = bms_add_member(hint->joinrelids, relid);
1575 plan->join_hint_level[hint->nrels] =
1576 lappend(plan->join_hint_level[hint->nrels], hint);
1579 /* Leading hint は、全ての join 方式が有効な hint として登録する */
1582 foreach(l, plan->leading)
1584 char *relname = (char *)lfirst(l);
1585 JoinMethodHint *hint;
1587 i = scan_relid_aliasname(root, relname, true, plan->hint_str);
1590 parse_ereport(plan->hint_str, ("Relation \"%s\" does not exist.", relname));
1591 list_free_deep(plan->leading);
1592 plan->leading = NIL;
1596 joinrelids = bms_add_member(joinrelids, i);
1602 if (njoinrels > plan->nlevel)
1604 parse_ereport(plan->hint_str, ("In %s hint, specified relation name %d or less.", HINT_LEADING, plan->nlevel));
1608 /* Leading で指定した組み合わせ以外の join hint を削除する */
1609 hint = scan_join_hint(joinrelids);
1610 list_free(plan->join_hint_level[njoinrels]);
1612 plan->join_hint_level[njoinrels] = lappend(NIL, hint);
1616 * Here relnames is not set, since Relids bitmap is sufficient to
1617 * control paths of this query afterwards.
1619 // TODO plan->hint_strをLeadingHint構造に変更後修正
1620 hint = (JoinMethodHint *) JoinMethodHintCreate(plan->hint_str, HINT_LEADING);
1621 hint->nrels = njoinrels;
1622 hint->enforce_mask = ENABLE_ALL_JOIN;
1623 hint->joinrelids = bms_copy(joinrelids);
1624 plan->join_hint_level[njoinrels] = lappend(NIL, hint);
1626 if (plan->njoin_hints == 0)
1628 plan->max_join_hints = HINT_ARRAY_DEFAULT_INITSIZE;
1629 plan->join_hints = palloc(sizeof(JoinMethodHint *) * plan->max_join_hints);
1631 else if (plan->njoin_hints == plan->max_join_hints)
1633 plan->max_join_hints *= 2;
1634 plan->join_hints = repalloc(plan->join_hints,
1635 sizeof(JoinMethodHint *) * plan->max_join_hints);
1638 plan->join_hints[plan->njoin_hints] = hint;
1639 plan->njoin_hints++;
1643 bms_free(joinrelids);
1647 rebuild_scan_path(PlanHint *plan, PlannerInfo *root, int level, List *initial_rels)
1650 int save_nestlevel = 0;
1652 for (i = 0; i < plan->nscan_hints; i++)
1654 ScanMethodHint *hint = plan->scan_hints[i];
1657 if (hint->enforce_mask == ENABLE_SEQSCAN)
1660 foreach(l, initial_rels)
1662 RelOptInfo *rel = (RelOptInfo *) lfirst(l);
1663 RangeTblEntry *rte = root->simple_rte_array[rel->relid];
1666 * スキャン方式が選択できるリレーションのみ、スキャンパスを再生成
1669 if (rel->reloptkind != RELOPT_BASEREL ||
1670 rte->rtekind == RTE_VALUES ||
1671 RelnameCmp(&hint->relname, &rte->eref->aliasname) != 0)
1675 * 複数のスキャンヒントが指定されていた場合でも、1つのネストレベルで
1676 * スキャン関連のGUCパラメータを変更する。
1678 if (save_nestlevel == 0)
1679 save_nestlevel = NewGUCNestLevel();
1682 * TODO ヒントで指定されたScan方式が最安価でない場合のみ、Pathを生成
1685 set_scan_config_options(hint->enforce_mask, plan->context);
1687 rel->pathlist = NIL; /* TODO 解放 */
1688 set_plain_rel_pathlist(root, rel, rte);
1695 * Restore the GUC variables we set above.
1697 if (save_nestlevel != 0)
1698 AtEOXact_GUC(true, save_nestlevel);
1702 * src/backend/optimizer/path/joinrels.c
1703 * export make_join_rel() をラップする関数
1705 * ヒントにしたがって、enabele_* パラメータを変更した上で、make_join_rel()を
1709 pg_hint_plan_make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
1712 JoinMethodHint *hint;
1716 joinrelids = bms_union(rel1->relids, rel2->relids);
1717 hint = scan_join_hint(joinrelids);
1718 bms_free(joinrelids);
1721 return make_join_rel(root, rel1, rel2);
1723 save_nestlevel = NewGUCNestLevel();
1725 set_join_config_options(hint->enforce_mask, global->context);
1727 rel = make_join_rel(root, rel1, rel2);
1730 * Restore the GUC variables we set above.
1732 AtEOXact_GUC(true, save_nestlevel);
1738 pg_hint_plan_join_search(PlannerInfo *root, int levels_needed, List *initial_rels)
1741 * pg_hint_planが無効、または有効なヒントが1つも指定されなかった場合は、標準
1746 if (prev_join_search)
1747 return (*prev_join_search) (root, levels_needed, initial_rels);
1748 else if (enable_geqo && levels_needed >= geqo_threshold)
1749 return geqo(root, levels_needed, initial_rels);
1751 return standard_join_search(root, levels_needed, initial_rels);
1754 transform_join_hints(global, root, levels_needed, initial_rels);
1755 rebuild_scan_path(global, root, levels_needed, initial_rels);
1758 * GEQOを使用する条件を満たした場合は、GEQOを用いた結合方式の検索を行う。
1759 * このとき、スキャン方式のヒントとSetヒントのみが有効になり、結合方式や結合
1760 * 順序はヒント句は無効になりGEQOのアルゴリズムで決定される。
1762 if (enable_geqo && levels_needed >= geqo_threshold)
1763 return geqo(root, levels_needed, initial_rels);
1765 return standard_join_search_org(root, levels_needed, initial_rels);
1768 #define standard_join_search standard_join_search_org
1769 #define join_search_one_level pg_hint_plan_join_search_one_level
1770 #define make_join_rel pg_hint_plan_make_join_rel