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_message, \
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 /* scan method hints */
83 typedef struct ScanHint
85 const char *opt_str; /* must not do pfree */
88 unsigned char enforce_mask;
91 /* join method hints */
92 typedef struct JoinHint
94 const char *opt_str; /* must not do pfree */
97 unsigned char enforce_mask;
101 /* change a run-time parameter hints */
102 typedef struct SetHint
104 char *name; /* name of variable */
109 * Describes a context of hint processing.
111 typedef struct PlanHint
113 char *hint_str; /* original hint string */
115 /* for scan method hints */
116 int nscan_hints; /* # of valid scan hints */
117 int max_scan_hints; /* # of slots for scan hints */
118 ScanHint **scan_hints; /* parsed scan hints */
120 /* for join method hints */
121 int njoin_hints; /* # of valid join hints */
122 int max_join_hints; /* # of slots for join hints */
123 JoinHint **join_hints; /* parsed join hints */
125 int nlevel; /* # of relations to be joined */
126 List **join_hint_level;
128 /* for Leading hints */
129 List *leading; /* relation names specified in Leading hint */
132 GucContext context; /* which GUC parameters can we set? */
133 List *set_hints; /* parsed Set hints */
136 typedef const char *(*HintParserFunction) (PlanHint *plan, Query *parse, char *keyword, const char *str);
139 * Describes a hint parser module which is bound with particular hint keyword.
141 typedef struct HintParser
145 HintParserFunction hint_parser;
148 /* Module callbacks */
152 static PlannedStmt *pg_hint_plan_planner(Query *parse, int cursorOptions,
153 ParamListInfo boundParams);
154 static void pg_hint_plan_get_relation_info(PlannerInfo *root, Oid relationObjectId,
155 bool inhparent, RelOptInfo *rel);
156 static RelOptInfo *pg_hint_plan_join_search(PlannerInfo *root, int levels_needed,
159 static const char *ParseScanMethod(PlanHint *plan, Query *parse, char *keyword, const char *str);
160 static const char *ParseIndexScanMethod(PlanHint *plan, Query *parse, char *keyword, const char *str);
161 static const char *ParseJoinMethod(PlanHint *plan, Query *parse, char *keyword, const char *str);
162 static const char *ParseLeading(PlanHint *plan, Query *parse, char *keyword, const char *str);
163 static const char *ParseSet(PlanHint *plan, Query *parse, char *keyword, const char *str);
165 static const char *Ordered(PlanHint *plan, Query *parse, char *keyword, const char *str);
168 RelOptInfo *standard_join_search_org(PlannerInfo *root, int levels_needed, List *initial_rels);
169 void pg_hint_plan_join_search_one_level(PlannerInfo *root, int level);
170 static void make_rels_by_clause_joins(PlannerInfo *root, RelOptInfo *old_rel, ListCell *other_rels);
171 static void make_rels_by_clauseless_joins(PlannerInfo *root, RelOptInfo *old_rel, ListCell *other_rels);
172 static bool has_join_restriction(PlannerInfo *root, RelOptInfo *rel);
173 static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte);
177 static bool pg_hint_plan_enable = true;
178 static bool pg_hint_plan_debug_print = false;
179 static int pg_hint_plan_parse_message = INFO;
181 static const struct config_enum_entry parse_message_level_options[] = {
182 {"debug", DEBUG2, true},
183 {"debug5", DEBUG5, false},
184 {"debug4", DEBUG4, false},
185 {"debug3", DEBUG3, false},
186 {"debug2", DEBUG2, false},
187 {"debug1", DEBUG1, false},
189 {"info", INFO, false},
190 {"notice", NOTICE, false},
191 {"warning", WARNING, false},
192 {"error", ERROR, false},
194 {"fatal", FATAL, true},
195 {"panic", PANIC, true},
200 /* Saved hook values in case of unload */
201 static planner_hook_type prev_planner_hook = NULL;
202 static get_relation_info_hook_type prev_get_relation_info = NULL;
203 static join_search_hook_type prev_join_search = NULL;
205 /* フック関数をまたがって使用する情報を管理する */
206 static PlanHint *global = NULL;
208 static const HintParser parsers[] = {
209 {HINT_SEQSCAN, true, ParseScanMethod},
210 {HINT_INDEXSCAN, true, ParseIndexScanMethod},
211 {HINT_BITMAPSCAN, true, ParseIndexScanMethod},
212 {HINT_TIDSCAN, true, ParseScanMethod},
213 {HINT_NOSEQSCAN, true, ParseScanMethod},
214 {HINT_NOINDEXSCAN, true, ParseScanMethod},
215 {HINT_NOBITMAPSCAN, true, ParseScanMethod},
216 {HINT_NOTIDSCAN, true, ParseScanMethod},
217 #if PG_VERSION_NUM >= 90200
218 {HINT_INDEXONLYSCAN, true, ParseIndexScanMethod},
219 {HINT_NOINDEXONLYSCAN, true, ParseIndexScanMethod},
221 {HINT_NESTLOOP, true, ParseJoinMethod},
222 {HINT_MERGEJOIN, true, ParseJoinMethod},
223 {HINT_HASHJOIN, true, ParseJoinMethod},
224 {HINT_NONESTLOOP, true, ParseJoinMethod},
225 {HINT_NOMERGEJOIN, true, ParseJoinMethod},
226 {HINT_NOHASHJOIN, true, ParseJoinMethod},
227 {HINT_LEADING, true, ParseLeading},
228 {HINT_SET, true, ParseSet},
233 * Module load callbacks
238 /* Define custom GUC variables. */
239 DefineCustomBoolVariable("pg_hint_plan.enable",
240 "Instructions or hints to the planner using block comments.",
242 &pg_hint_plan_enable,
250 DefineCustomBoolVariable("pg_hint_plan.debug_print",
251 "Logs each query's parse results of the hint.",
253 &pg_hint_plan_debug_print,
261 DefineCustomEnumVariable("pg_hint_plan.parse_message",
262 "Messege level of the parse error.",
264 &pg_hint_plan_parse_message,
266 parse_message_level_options,
274 prev_planner_hook = planner_hook;
275 planner_hook = pg_hint_plan_planner;
276 prev_get_relation_info = get_relation_info_hook;
277 get_relation_info_hook = pg_hint_plan_get_relation_info;
278 prev_join_search = join_search_hook;
279 join_search_hook = pg_hint_plan_join_search;
283 * Module unload callback
289 /* Uninstall hooks. */
290 planner_hook = prev_planner_hook;
291 get_relation_info_hook = prev_get_relation_info;
292 join_search_hook = prev_join_search;
300 hint = palloc(sizeof(ScanHint));
301 hint->opt_str = NULL;
302 hint->relname = NULL;
303 hint->indexnames = NIL;
304 hint->enforce_mask = 0;
310 ScanHintDelete(ScanHint *hint)
316 pfree(hint->relname);
317 list_free_deep(hint->indexnames);
326 hint = palloc(sizeof(JoinHint));
327 hint->opt_str = NULL;
329 hint->relnames = NULL;
330 hint->enforce_mask = 0;
331 hint->joinrelids = NULL;
337 JoinHintDelete(JoinHint *hint)
346 for (i = 0; i < hint->nrels; i++)
347 pfree(hint->relnames[i]);
348 pfree(hint->relnames);
350 bms_free(hint->joinrelids);
359 hint = palloc(sizeof(SetHint));
367 SetHintDelete(SetHint *hint)
384 hint = palloc(sizeof(PlanHint));
385 hint->hint_str = NULL;
386 hint->nscan_hints = 0;
387 hint->max_scan_hints = 0;
388 hint->scan_hints = NULL;
389 hint->njoin_hints = 0;
390 hint->max_join_hints = 0;
391 hint->join_hints = NULL;
393 hint->join_hint_level = NULL;
395 hint->context = superuser() ? PGC_SUSET : PGC_USERSET;
396 hint->set_hints = NIL;
402 PlanHintDelete(PlanHint *hint)
411 pfree(hint->hint_str);
413 for (i = 0; i < hint->nscan_hints; i++)
414 ScanHintDelete(hint->scan_hints[i]);
415 if (hint->scan_hints)
416 pfree(hint->scan_hints);
418 for (i = 0; i < hint->njoin_hints; i++)
419 JoinHintDelete(hint->join_hints[i]);
420 if (hint->join_hints)
421 pfree(hint->join_hints);
423 for (i = 2; i <= hint->nlevel; i++)
424 list_free(hint->join_hint_level[i]);
425 if (hint->join_hint_level)
426 pfree(hint->join_hint_level);
428 list_free_deep(hint->leading);
430 foreach(l, hint->set_hints)
431 SetHintDelete((SetHint *) lfirst(l));
432 list_free(hint->set_hints);
438 PlanHintIsempty(PlanHint *hint)
440 if (hint->nscan_hints == 0 &&
441 hint->njoin_hints == 0 &&
442 hint->leading == NIL &&
443 hint->set_hints == NIL)
449 /* TODO オブジェクト名のクォート処理を追加 */
451 PlanHintDump(PlanHint *hint)
456 bool is_first = true;
460 elog(INFO, "no hint");
464 initStringInfo(&buf);
465 appendStringInfo(&buf, "/*\n");
466 for (i = 0; i < hint->nscan_hints; i++)
468 ScanHint *h = hint->scan_hints[i];
470 switch(h->enforce_mask)
472 case(ENABLE_SEQSCAN):
473 appendStringInfo(&buf, "%s(", HINT_SEQSCAN);
475 case(ENABLE_INDEXSCAN):
476 appendStringInfo(&buf, "%s(", HINT_INDEXSCAN);
478 case(ENABLE_BITMAPSCAN):
479 appendStringInfo(&buf, "%s(", HINT_BITMAPSCAN);
481 case(ENABLE_TIDSCAN):
482 appendStringInfo(&buf, "%s(", HINT_TIDSCAN);
484 case(ENABLE_INDEXSCAN | ENABLE_BITMAPSCAN | ENABLE_TIDSCAN):
485 appendStringInfo(&buf, "%s(", HINT_NOSEQSCAN);
487 case(ENABLE_SEQSCAN | ENABLE_BITMAPSCAN | ENABLE_TIDSCAN):
488 appendStringInfo(&buf, "%s(", HINT_NOINDEXSCAN);
490 case(ENABLE_SEQSCAN | ENABLE_INDEXSCAN | ENABLE_TIDSCAN):
491 appendStringInfo(&buf, "%s(", HINT_NOBITMAPSCAN);
493 case(ENABLE_SEQSCAN | ENABLE_INDEXSCAN | ENABLE_BITMAPSCAN):
494 appendStringInfo(&buf, "%s(", HINT_NOTIDSCAN);
497 appendStringInfoString(&buf, "\?\?\?(");
500 appendStringInfo(&buf, "%s", h->relname);
501 foreach(n, h->indexnames)
502 appendStringInfo(&buf, " %s", (char *) lfirst(n));
503 appendStringInfoString(&buf, ")\n");
506 for (i = 0; i < hint->njoin_hints; i++)
508 JoinHint *h = hint->join_hints[i];
510 switch(h->enforce_mask)
512 case(ENABLE_NESTLOOP):
513 appendStringInfo(&buf, "%s(", HINT_NESTLOOP);
515 case(ENABLE_MERGEJOIN):
516 appendStringInfo(&buf, "%s(", HINT_MERGEJOIN);
518 case(ENABLE_HASHJOIN):
519 appendStringInfo(&buf, "%s(", HINT_HASHJOIN);
521 case(ENABLE_ALL_JOIN ^ ENABLE_NESTLOOP):
522 appendStringInfo(&buf, "%s(", HINT_NONESTLOOP);
524 case(ENABLE_ALL_JOIN ^ ENABLE_MERGEJOIN):
525 appendStringInfo(&buf, "%s(", HINT_NOMERGEJOIN);
527 case(ENABLE_ALL_JOIN ^ ENABLE_HASHJOIN):
528 appendStringInfo(&buf, "%s(", HINT_NOHASHJOIN);
530 case(ENABLE_ALL_JOIN):
533 appendStringInfoString(&buf, "\?\?\?(");
536 appendStringInfo(&buf, "%s", h->relnames[0]);
537 for (i = 1; i < h->nrels; i++)
538 appendStringInfo(&buf, " %s", h->relnames[i]);
539 appendStringInfoString(&buf, ")\n");
542 foreach(l, hint->set_hints)
544 SetHint *h = (SetHint *) lfirst(l);
545 appendStringInfo(&buf, "%s(%s %s)\n", HINT_SET, h->name, h->value);
548 foreach(l, hint->leading)
552 appendStringInfo(&buf, "%s(%s", HINT_LEADING, (char *)lfirst(l));
556 appendStringInfo(&buf, " %s", (char *)lfirst(l));
559 appendStringInfoString(&buf, ")\n");
561 appendStringInfoString(&buf, "*/");
563 elog(INFO, "%s", buf.data);
569 RelnameCmp(const void *a, const void *b)
571 const char *relnamea = *((const char **) a);
572 const char *relnameb = *((const char **) b);
574 return strcmp(relnamea, relnameb);
578 ScanHintCmp(const void *a, const void *b, bool order)
580 const ScanHint *hinta = *((const ScanHint **) a);
581 const ScanHint *hintb = *((const ScanHint **) b);
584 if ((result = RelnameCmp(&hinta->relname, &hintb->relname)) != 0)
589 return hinta->opt_str - hintb->opt_str;
595 ScanHintCmpIsOrder(const void *a, const void *b)
597 return ScanHintCmp(a, b, true);
601 JoinHintCmp(const void *a, const void *b, bool order)
603 const JoinHint *hinta = *((const JoinHint **) a);
604 const JoinHint *hintb = *((const JoinHint **) b);
606 if (hinta->nrels == hintb->nrels)
609 for (i = 0; i < hinta->nrels; i++)
612 if ((result = RelnameCmp(&hinta->relnames[i], &hintb->relnames[i])) != 0)
618 return hinta->opt_str - hintb->opt_str;
623 return hinta->nrels - hintb->nrels;
627 JoinHintCmpIsOrder(const void *a, const void *b)
629 return JoinHintCmp(a, b, true);
632 #if PG_VERSION_NUM < 90200
634 set_config_option_wrapper(const char *name, const char *value,
635 GucContext context, GucSource source,
636 GucAction action, bool changeVal, int elevel)
639 MemoryContext ccxt = CurrentMemoryContext;
643 result = set_config_option(name, value, context, source,
654 ecxt = MemoryContextSwitchTo(ccxt);
655 errdata = CopyErrorData();
656 ereport(elevel, (errcode(errdata->sqlerrcode),
657 errmsg("%s", errdata->message),
658 errdata->detail ? errdetail("%s", errdata->detail) : 0,
659 errdata->hint ? errhint("%s", errdata->hint) : 0));
660 FreeErrorData(errdata);
662 MemoryContextSwitchTo(ecxt);
669 #define set_config_option(name, value, context, source, \
670 action, changeVal, elevel) \
671 set_config_option_wrapper(name, value, context, source, \
672 action, changeVal, elevel)
676 set_config_options(List *options, GucContext context)
682 save_nestlevel = NewGUCNestLevel();
686 SetHint *hint = (SetHint *) lfirst(l);
689 result = set_config_option(hint->name, hint->value, context,
690 PGC_S_SESSION, GUC_ACTION_SAVE, true,
691 pg_hint_plan_parse_message);
694 return save_nestlevel;
697 #define SET_CONFIG_OPTION(name, enforce_mask, type_bits) \
698 set_config_option((name), \
699 ((enforce_mask) & (type_bits)) ? "true" : "false", \
700 context, PGC_S_SESSION, GUC_ACTION_SAVE, true, ERROR)
703 set_join_config_options(unsigned char enforce_mask, GucContext context)
705 SET_CONFIG_OPTION("enable_nestloop", enforce_mask, ENABLE_NESTLOOP);
706 SET_CONFIG_OPTION("enable_mergejoin", enforce_mask, ENABLE_MERGEJOIN);
707 SET_CONFIG_OPTION("enable_hashjoin", enforce_mask, ENABLE_HASHJOIN);
711 set_scan_config_options(unsigned char enforce_mask, GucContext context)
713 SET_CONFIG_OPTION("enable_seqscan", enforce_mask, ENABLE_SEQSCAN);
714 SET_CONFIG_OPTION("enable_indexscan", enforce_mask, ENABLE_INDEXSCAN);
715 SET_CONFIG_OPTION("enable_bitmapscan", enforce_mask, ENABLE_BITMAPSCAN);
716 SET_CONFIG_OPTION("enable_tidscan", enforce_mask, ENABLE_TIDSCAN);
717 #if PG_VERSION_NUM >= 90200
718 SET_CONFIG_OPTION("enable_indexonlyscan", enforce_mask, ENABLE_INDEXSCAN);
727 parse_keyword(const char *str, StringInfo buf)
731 while (!isspace(*str) && *str != '(' && *str != '\0')
732 appendStringInfoChar(buf, *str++);
738 skip_opened_parenthesis(const char *str)
744 parse_ereport(str, ("Opened parenthesis is necessary."));
754 skip_closed_parenthesis(const char *str)
760 parse_ereport(str, ("Closed parenthesis is necessary."));
770 * 二重引用符で囲まれているかもしれないトークンを読み取り word 引数に palloc
771 * で確保したバッファに格納してそのポインタを返す。
773 * 正常にパースできた場合は残りの文字列の先頭位置を、異常があった場合は NULL を
777 parse_quote_value(const char *str, char **word, char *value_type)
785 initStringInfo(&buf);
798 /* 二重引用符が閉じられていない場合はパース中断 */
802 parse_ereport(str, ("Unterminated quoted %s.", value_type));
807 * エスケープ対象のダブルクウォートをスキップする。
808 * もしブロックコメントの開始文字列や終了文字列もオブジェクト名とし
809 * て使用したい場合は、/ と * もエスケープ対象とすることで使用できる
810 * が、処理対象としていない。もしテーブル名にこれらの文字が含まれる
811 * 場合は、エイリアスを指定する必要がある。
821 if (isspace(*str) || *str == ')' || *str == '\0')
824 appendStringInfoCharMacro(&buf, *str++);
830 parse_ereport(str, ("%s is necessary.", value_type));
840 skip_option_delimiter(const char *str)
848 parse_ereport(str, ("Must be specified space."));
856 parse_hints(PlanHint *plan, Query *parse, const char *str)
861 initStringInfo(&buf);
864 const HintParser *parser;
866 /* in error message, we output the comment including the keyword. */
869 /* parse only the keyword of the hint. */
870 resetStringInfo(&buf);
871 str = parse_keyword(str, &buf);
873 for (parser = parsers; parser->keyword != NULL; parser++)
875 char *keyword = parser->keyword;
877 if (strcasecmp(buf.data, keyword) != 0)
880 if (parser->have_paren)
882 /* parser of each hint does parse in a parenthesis. */
883 if ((str = skip_opened_parenthesis(str)) == NULL ||
884 (str = parser->hint_parser(plan, parse, keyword, str)) == NULL ||
885 (str = skip_closed_parenthesis(str)) == NULL)
893 if ((str = parser->hint_parser(plan, parse, keyword, str)) == NULL)
900 * 直前のヒントに括弧の指定がなければ次のヒントの間に空白が必要
902 if (!isspace(*str) && *str == '\0')
903 parse_ereport(str, ("Delimiter of the hint is necessary."));
911 if (parser->keyword == NULL)
913 parse_ereport(head, ("Keyword \"%s\" does not exist.", buf.data));
925 * Do basic parsing of the query head comment.
928 parse_head_comment(Query *parse)
937 /* get client-supplied query string. */
938 p = debug_query_string;
942 /* extract query head comment. */
943 len = strlen(HINT_START);
945 if (strncmp(p, HINT_START, len))
951 if ((tail = strstr(p, HINT_END)) == NULL)
953 parse_ereport(debug_query_string, ("unterminated /* comment"));
957 /* 入れ子にしたブロックコメントはサポートしない */
958 if ((head = strstr(p, HINT_START)) != NULL && head < tail)
959 parse_ereport(head, ("block comments nest doesn't supported"));
963 head = palloc(len + 1);
964 memcpy(head, p, len);
968 plan = PlanHintCreate();
969 plan->hint_str = head;
971 /* parse each hint. */
972 if (!parse_hints(plan, parse, p))
975 /* 重複したScan条件をを除外する */
976 qsort(plan->scan_hints, plan->nscan_hints, sizeof(ScanHint *), ScanHintCmpIsOrder);
977 for (i = 0; i < plan->nscan_hints - 1;)
979 int result = ScanHintCmp(plan->scan_hints + i,
980 plan->scan_hints + i + 1, false);
985 /* 後で指定したヒントを有効にする */
987 memmove(plan->scan_hints + i, plan->scan_hints + i + 1,
988 sizeof(ScanHint *) * (plan->nscan_hints - i));
992 /* 重複したJoin条件をを除外する */
993 qsort(plan->join_hints, plan->njoin_hints, sizeof(JoinHint *), JoinHintCmpIsOrder);
994 for (i = 0; i < plan->njoin_hints - 1;)
996 int result = JoinHintCmp(plan->join_hints + i,
997 plan->join_hints + i + 1, false);
1002 /* 後で指定したヒントを有効にする */
1003 plan->njoin_hints--;
1004 memmove(plan->join_hints + i, plan->join_hints + i + 1,
1005 sizeof(JoinHint *) * (plan->njoin_hints - i));
1013 * スキャン方式ヒントのパースでの共通処理
1016 parse_scan_method(PlanHint *plan, Query *parse, char *keyword, const char *str)
1020 hint = ScanHintCreate();
1021 hint->opt_str = str;
1024 * スキャン方式のヒントでリレーション名が読み取れない場合はヒント無効
1026 if ((str = parse_quote_value(str, &hint->relname, "ralation name")) == NULL)
1028 ScanHintDelete(hint);
1033 * ヒントごとに決まっている許容スキャン方式をビットマスクとして設定
1035 if (strcasecmp(keyword, HINT_SEQSCAN) == 0)
1036 hint->enforce_mask = ENABLE_SEQSCAN;
1037 else if (strcasecmp(keyword, HINT_INDEXSCAN) == 0)
1038 hint->enforce_mask = ENABLE_INDEXSCAN;
1039 else if (strcasecmp(keyword, HINT_BITMAPSCAN) == 0)
1040 hint->enforce_mask = ENABLE_BITMAPSCAN;
1041 else if (strcasecmp(keyword, HINT_TIDSCAN) == 0)
1042 hint->enforce_mask = ENABLE_TIDSCAN;
1043 else if (strcasecmp(keyword, HINT_NOSEQSCAN) == 0)
1044 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_SEQSCAN;
1045 else if (strcasecmp(keyword, HINT_NOINDEXSCAN) == 0)
1046 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_INDEXSCAN;
1047 else if (strcasecmp(keyword, HINT_NOBITMAPSCAN) == 0)
1048 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_BITMAPSCAN;
1049 else if (strcasecmp(keyword, HINT_NOTIDSCAN) == 0)
1050 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_TIDSCAN;
1053 ScanHintDelete(hint);
1054 parse_ereport(str, ("unrecognized hint keyword \"%s\"", keyword));
1059 * 出来上がったヒント情報を追加。スロットが足りない場合は二倍に拡張する。
1061 if (plan->nscan_hints == 0)
1063 plan->max_scan_hints = HINT_ARRAY_DEFAULT_INITSIZE;
1064 plan->scan_hints = palloc(sizeof(JoinHint *) * plan->max_scan_hints);
1066 else if (plan->nscan_hints == plan->max_scan_hints)
1068 plan->max_scan_hints *= 2;
1069 plan->scan_hints = repalloc(plan->scan_hints,
1070 sizeof(JoinHint *) * plan->max_scan_hints);
1073 plan->scan_hints[plan->nscan_hints] = hint;
1074 plan->nscan_hints++;
1080 ParseScanMethod(PlanHint *plan, Query *parse, char *keyword, const char *str)
1084 if ((str = parse_scan_method(plan, parse, keyword, str)) == NULL)
1087 hint = plan->scan_hints[plan->nscan_hints - 1];
1092 parse_ereport(str, ("Closed parenthesis is necessary."));
1093 plan->nscan_hints--;
1094 ScanHintDelete(hint);
1102 ParseIndexScanMethod(PlanHint *plan, Query *parse, char *keyword, const char *str)
1107 if ((str = parse_scan_method(plan, parse, keyword, str)) == NULL)
1110 hint = plan->scan_hints[plan->nscan_hints - 1];
1112 /* インデックス参照をパースする。 */
1116 * TODO 直前のオブジェクト名がクウォート処理されていた場合の処理を実装
1122 if ((str = parse_quote_value(str, &indexname, "index name")) == NULL)
1124 plan->nscan_hints--;
1125 ScanHintDelete(hint);
1129 hint->indexnames = lappend(hint->indexnames, indexname);
1136 ParseJoinMethod(PlanHint *plan, Query *parse, char *keyword, const char *str)
1143 hint = JoinHintCreate();
1144 hint->opt_str = str;
1145 hint->relnames = palloc(sizeof(char *));
1147 while ((str = parse_quote_value(str, &relname, "table name")) != NULL)
1150 hint->relnames = repalloc(hint->relnames, sizeof(char *) * hint->nrels);
1151 hint->relnames[hint->nrels - 1] = relname;
1160 JoinHintDelete(hint);
1164 /* Join 対象のテーブルは最低でも2つ指定する必要がある */
1165 if (hint->nrels < 2)
1167 JoinHintDelete(hint);
1168 parse_ereport(str, ("Specified relation more than two."));
1173 qsort(hint->relnames, hint->nrels, sizeof(char *), RelnameCmp);
1175 if (strcasecmp(keyword, HINT_NESTLOOP) == 0)
1176 hint->enforce_mask = ENABLE_NESTLOOP;
1177 else if (strcasecmp(keyword, HINT_MERGEJOIN) == 0)
1178 hint->enforce_mask = ENABLE_MERGEJOIN;
1179 else if (strcasecmp(keyword, HINT_HASHJOIN) == 0)
1180 hint->enforce_mask = ENABLE_HASHJOIN;
1181 else if (strcasecmp(keyword, HINT_NONESTLOOP) == 0)
1182 hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_NESTLOOP;
1183 else if (strcasecmp(keyword, HINT_NOMERGEJOIN) == 0)
1184 hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_MERGEJOIN;
1185 else if (strcasecmp(keyword, HINT_NOHASHJOIN) == 0)
1186 hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_HASHJOIN;
1189 JoinHintDelete(hint);
1190 parse_ereport(str, ("unrecognized hint keyword \"%s\"", keyword));
1194 if (plan->njoin_hints == 0)
1196 plan->max_join_hints = HINT_ARRAY_DEFAULT_INITSIZE;
1197 plan->join_hints = palloc(sizeof(JoinHint *) * plan->max_join_hints);
1199 else if (plan->njoin_hints == plan->max_join_hints)
1201 plan->max_join_hints *= 2;
1202 plan->join_hints = repalloc(plan->join_hints,
1203 sizeof(JoinHint *) * plan->max_join_hints);
1206 plan->join_hints[plan->njoin_hints] = hint;
1207 plan->njoin_hints++;
1213 ParseLeading(PlanHint *plan, Query *parse, char *keyword, const char *str)
1218 * すでに指定済みの場合は、後で指定したヒントが有効にするため、登録済みの
1221 list_free_deep(plan->leading);
1222 plan->leading = NIL;
1224 while ((str = parse_quote_value(str, &relname, "relation name")) != NULL)
1228 plan->leading = lappend(plan->leading, relname);
1237 parse_ereport(str, ("Must be specified space."));
1242 /* テーブル指定が1つのみの場合は、ヒントを無効にし、パースを続ける */
1243 if (list_length(plan->leading) == 1)
1245 parse_ereport(str, ("In %s hint, specified relation name 2 or more.", HINT_LEADING));
1246 list_free_deep(plan->leading);
1247 plan->leading = NIL;
1254 ParseSet(PlanHint *plan, Query *parse, char *keyword, const char *str)
1258 hint = SetHintCreate();
1260 if ((str = parse_quote_value(str, &hint->name, "parameter name")) == NULL ||
1261 (str = skip_option_delimiter(str)) == NULL ||
1262 (str = parse_quote_value(str, &hint->value, "parameter value")) == NULL)
1264 SetHintDelete(hint);
1271 parse_ereport(str, ("Closed parenthesis is necessary."));
1272 SetHintDelete(hint);
1275 plan->set_hints = lappend(plan->set_hints, hint);
1282 * Oracle の ORDERD ヒントの実装
1285 Ordered(PlanHint *plan, Query *parse, char *keyword, const char *str)
1289 hint = SetHintCreate();
1290 hint->name = pstrdup("join_collapse_limit");
1291 hint->value = pstrdup("1");
1292 plan->set_hints = lappend(plan->set_hints, hint);
1298 static PlannedStmt *
1299 pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
1302 PlannedStmt *result;
1305 * hintが指定されない、または空のhintを指定された場合は通常のparser処理をお
1307 * 他のフック関数で実行されるhint処理をスキップするために、global 変数をNULL
1310 if (!pg_hint_plan_enable ||
1311 (global = parse_head_comment(parse)) == NULL ||
1312 PlanHintIsempty(global))
1314 PlanHintDelete(global);
1317 if (prev_planner_hook)
1318 return (*prev_planner_hook) (parse, cursorOptions, boundParams);
1320 return standard_planner(parse, cursorOptions, boundParams);
1323 /* Set hint で指定されたGUCパラメータを設定する */
1324 save_nestlevel = set_config_options(global->set_hints, global->context);
1326 if (global->leading != NIL)
1327 set_join_config_options(0, global->context);
1330 * TODO ビュー定義で指定したテーブル数が1つの場合にもこのタイミングでGUCを変更する必
1333 if (list_length(parse->rtable) == 1 &&
1334 ((RangeTblEntry *) linitial(parse->rtable))->rtekind == RTE_RELATION)
1337 RangeTblEntry *rte = linitial(parse->rtable);
1339 for (i = 0; i < global->nscan_hints; i++)
1341 ScanHint *hint = global->scan_hints[i];
1343 if (RelnameCmp(&rte->eref->aliasname, &hint->relname) != 0)
1344 parse_ereport(hint->opt_str, ("Relation \"%s\" does not exist.", hint->relname));
1346 set_scan_config_options(hint->enforce_mask, global->context);
1350 if (prev_planner_hook)
1351 result = (*prev_planner_hook) (parse, cursorOptions, boundParams);
1353 result = standard_planner(parse, cursorOptions, boundParams);
1356 * Restore the GUC variables we set above.
1358 AtEOXact_GUC(true, save_nestlevel);
1360 if (pg_hint_plan_debug_print)
1362 PlanHintDump(global);
1364 elog_node_display(INFO, "rtable", parse->rtable, true);
1368 PlanHintDelete(global);
1375 * aliasnameと一致するSCANヒントを探す
1378 find_scan_hint(RangeTblEntry *rte)
1382 for (i = 0; i < global->nscan_hints; i++)
1384 ScanHint *hint = global->scan_hints[i];
1386 if (RelnameCmp(&rte->eref->aliasname, &hint->relname) == 0)
1394 pg_hint_plan_get_relation_info(PlannerInfo *root, Oid relationObjectId,
1395 bool inhparent, RelOptInfo *rel)
1402 if (prev_get_relation_info)
1403 (*prev_get_relation_info) (root, relationObjectId, inhparent, rel);
1405 /* 有効なヒントが指定されなかった場合は処理をスキップする。 */
1409 if (rel->reloptkind != RELOPT_BASEREL)
1412 if ((hint = find_scan_hint(root->simple_rte_array[rel->relid])) == NULL)
1415 /* インデックスを全て削除し、スキャンに使えなくする */
1416 if (hint->enforce_mask == ENABLE_SEQSCAN)
1418 list_free_deep(rel->indexlist);
1419 rel->indexlist = NIL;
1424 /* 後でパスを作り直すため、ここではなにもしない */
1425 if (hint->indexnames == NULL)
1428 /* 指定されたインデックスのみをのこす */
1430 for (cell = list_head(rel->indexlist); cell; cell = next)
1432 IndexOptInfo *info = (IndexOptInfo *) lfirst(cell);
1433 char *indexname = get_rel_name(info->indexoid);
1435 bool use_index = false;
1439 foreach(l, hint->indexnames)
1441 if (RelnameCmp(&indexname, &lfirst(l)) == 0)
1449 rel->indexlist = list_delete_cell(rel->indexlist, cell, prev);
1458 scan_relid_aliasname(PlannerInfo *root, char *aliasname, bool check_ambiguous, const char *str)
1460 /* TODO refnameRangeTblEntry を参考 */
1464 for (i = 1; i < root->simple_rel_array_size; i++)
1466 if (root->simple_rel_array[i] == NULL)
1469 Assert(i == root->simple_rel_array[i]->relid);
1471 if (RelnameCmp(&aliasname, &root->simple_rte_array[i]->eref->aliasname)
1475 if (!check_ambiguous)
1479 parse_ereport(str, ("relation name \"%s\" is ambiguous", aliasname));
1488 * relidビットマスクと一致するヒントを探す
1491 scan_join_hint(Relids joinrelids)
1496 join_hint = global->join_hint_level[bms_num_members(joinrelids)];
1497 foreach(l, join_hint)
1499 JoinHint *hint = (JoinHint *) lfirst(l);
1500 if (bms_equal(joinrelids, hint->joinrelids))
1508 * ヒントを使用しやすい構造に変換する。
1511 rebuild_join_hints(PlanHint *plan, PlannerInfo *root, int level, List *initial_rels)
1518 plan->nlevel = root->simple_rel_array_size - 1;
1519 plan->join_hint_level = palloc0(sizeof(List *) * (root->simple_rel_array_size));
1520 for (i = 0; i < plan->njoin_hints; i++)
1522 JoinHint *hint = plan->join_hints[i];
1526 for (j = 0; j < hint->nrels; j++)
1528 char *relname = hint->relnames[j];
1530 relid = scan_relid_aliasname(root, relname, true, hint->opt_str);
1533 parse_ereport(hint->opt_str, ("Relation \"%s\" does not exist.", relname));
1537 hint->joinrelids = bms_add_member(hint->joinrelids, relid);
1543 plan->join_hint_level[hint->nrels] =
1544 lappend(plan->join_hint_level[hint->nrels], hint);
1547 /* Leading hint は、全ての join 方式が有効な hint として登録する */
1550 foreach(l, plan->leading)
1552 char *relname = (char *)lfirst(l);
1555 i = scan_relid_aliasname(root, relname, true, plan->hint_str);
1558 parse_ereport(plan->hint_str, ("Relation \"%s\" does not exist.", relname));
1559 list_free_deep(plan->leading);
1560 plan->leading = NIL;
1564 joinrelids = bms_add_member(joinrelids, i);
1570 if (njoinrels > plan->nlevel)
1572 parse_ereport(plan->hint_str, ("In %s hint, specified relation name %d or less.", HINT_LEADING, plan->nlevel));
1576 /* Leading で指定した組み合わせ以外の join hint を削除する */
1577 hint = scan_join_hint(joinrelids);
1578 list_free(plan->join_hint_level[njoinrels]);
1580 plan->join_hint_level[njoinrels] = lappend(NIL, hint);
1584 * Here relnames is not set, since Relids bitmap is sufficient to
1585 * control paths of this query afterwards.
1587 hint = JoinHintCreate();
1588 hint->nrels = njoinrels;
1589 hint->enforce_mask = ENABLE_ALL_JOIN;
1590 hint->joinrelids = bms_copy(joinrelids);
1591 plan->join_hint_level[njoinrels] = lappend(NIL, hint);
1593 if (plan->njoin_hints == 0)
1595 plan->max_join_hints = HINT_ARRAY_DEFAULT_INITSIZE;
1596 plan->join_hints = palloc(sizeof(JoinHint *) * plan->max_join_hints);
1598 else if (plan->njoin_hints == plan->max_join_hints)
1600 plan->max_join_hints *= 2;
1601 plan->join_hints = repalloc(plan->join_hints,
1602 sizeof(JoinHint *) * plan->max_join_hints);
1605 plan->join_hints[plan->njoin_hints] = hint;
1606 plan->njoin_hints++;
1610 bms_free(joinrelids);
1614 rebuild_scan_path(PlanHint *plan, PlannerInfo *root, int level, List *initial_rels)
1617 int save_nestlevel = 0;
1619 for (i = 0; i < plan->nscan_hints; i++)
1621 ScanHint *hint = plan->scan_hints[i];
1624 if (hint->enforce_mask == ENABLE_SEQSCAN)
1627 foreach(l, initial_rels)
1629 RelOptInfo *rel = (RelOptInfo *) lfirst(l);
1630 RangeTblEntry *rte = root->simple_rte_array[rel->relid];
1632 if (rel->reloptkind != RELOPT_BASEREL ||
1633 RelnameCmp(&hint->relname, &rte->eref->aliasname) != 0)
1636 if (save_nestlevel != 0)
1637 save_nestlevel = NewGUCNestLevel();
1640 * TODO ヒントで指定されたScan方式が最安価でない場合のみ、Pathを生成
1643 set_scan_config_options(hint->enforce_mask, plan->context);
1645 rel->pathlist = NIL; /* TODO 解放 */
1646 set_plain_rel_pathlist(root, rel, rte);
1653 * Restore the GUC variables we set above.
1655 if (save_nestlevel != 0)
1656 AtEOXact_GUC(true, save_nestlevel);
1660 * src/backend/optimizer/path/joinrels.c
1661 * export make_join_rel() をラップする関数
1663 * ヒントにしたがって、enabele_* パラメータを変更した上で、make_join_rel()を
1667 pg_hint_plan_make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
1674 joinrelids = bms_union(rel1->relids, rel2->relids);
1675 hint = scan_join_hint(joinrelids);
1676 bms_free(joinrelids);
1679 return make_join_rel(root, rel1, rel2);
1681 save_nestlevel = NewGUCNestLevel();
1683 set_join_config_options(hint->enforce_mask, global->context);
1685 rel = make_join_rel(root, rel1, rel2);
1688 * Restore the GUC variables we set above.
1690 AtEOXact_GUC(true, save_nestlevel);
1696 pg_hint_plan_join_search(PlannerInfo *root, int levels_needed, List *initial_rels)
1699 * pg_hint_planが無効、または有効なヒントが1つも指定されなかった場合は、標準
1704 if (prev_join_search)
1705 return (*prev_join_search) (root, levels_needed, initial_rels);
1706 else if (enable_geqo && levels_needed >= geqo_threshold)
1707 return geqo(root, levels_needed, initial_rels);
1709 return standard_join_search(root, levels_needed, initial_rels);
1712 rebuild_join_hints(global, root, levels_needed, initial_rels);
1713 rebuild_scan_path(global, root, levels_needed, initial_rels);
1716 * GEQOを使用する条件を満たした場合は、GEQOを用いた結合方式の検索を行う。
1717 * このとき、スキャン方式のヒントとSetヒントのみが有効になり、結合方式や結合
1718 * 順序はヒント句は無効になりGEQOのアルゴリズムで決定される。
1720 if (enable_geqo && levels_needed >= geqo_threshold)
1721 return geqo(root, levels_needed, initial_rels);
1723 return standard_join_search_org(root, levels_needed, initial_rels);
1726 #define standard_join_search standard_join_search_org
1727 #define join_search_one_level pg_hint_plan_join_search_one_level
1728 #define make_join_rel pg_hint_plan_make_join_rel