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 "commands/prepare.h"
13 #include "miscadmin.h"
14 #include "nodes/nodeFuncs.h"
15 #include "optimizer/clauses.h"
16 #include "optimizer/cost.h"
17 #include "optimizer/geqo.h"
18 #include "optimizer/joininfo.h"
19 #include "optimizer/pathnode.h"
20 #include "optimizer/paths.h"
21 #include "optimizer/plancat.h"
22 #include "optimizer/planner.h"
23 #include "optimizer/prep.h"
24 #include "optimizer/restrictinfo.h"
25 #include "tcop/tcopprot.h"
26 #include "tcop/utility.h"
27 #include "utils/lsyscache.h"
28 #include "utils/memutils.h"
30 #ifdef PG_MODULE_MAGIC
34 #if PG_VERSION_NUM < 90100
35 #error unsupported PostgreSQL version
38 #define BLOCK_COMMENT_START "/*"
39 #define BLOCK_COMMENT_END "*/"
40 #define HINT_COMMENT_KEYWORD "+"
41 #define HINT_START BLOCK_COMMENT_START HINT_COMMENT_KEYWORD
42 #define HINT_END BLOCK_COMMENT_END
45 #define HINT_SEQSCAN "SeqScan"
46 #define HINT_INDEXSCAN "IndexScan"
47 #define HINT_BITMAPSCAN "BitmapScan"
48 #define HINT_TIDSCAN "TidScan"
49 #define HINT_NOSEQSCAN "NoSeqScan"
50 #define HINT_NOINDEXSCAN "NoIndexScan"
51 #define HINT_NOBITMAPSCAN "NoBitmapScan"
52 #define HINT_NOTIDSCAN "NoTidScan"
53 #if PG_VERSION_NUM >= 90200
54 #define HINT_INDEXONLYSCAN "IndexonlyScan"
55 #define HINT_NOINDEXONLYSCAN "NoIndexonlyScan"
57 #define HINT_NESTLOOP "NestLoop"
58 #define HINT_MERGEJOIN "MergeJoin"
59 #define HINT_HASHJOIN "HashJoin"
60 #define HINT_NONESTLOOP "NoNestLoop"
61 #define HINT_NOMERGEJOIN "NoMergeJoin"
62 #define HINT_NOHASHJOIN "NoHashJoin"
63 #define HINT_LEADING "Leading"
64 #define HINT_SET "Set"
67 #define HINT_ARRAY_DEFAULT_INITSIZE 8
69 #define parse_ereport(str, detail) \
70 ereport(pg_hint_plan_parse_messages, \
71 (errmsg("hint syntax error at or near \"%s\"", (str)), \
74 #define skip_space(str) \
75 while (isspace(*str)) \
80 ENABLE_SEQSCAN = 0x01,
81 ENABLE_INDEXSCAN = 0x02,
82 ENABLE_BITMAPSCAN = 0x04,
83 ENABLE_TIDSCAN = 0x08,
84 #if PG_VERSION_NUM >= 90200
85 ENABLE_INDEXONLYSCAN = 0x10,
87 ENABLE_NESTLOOP = 0x20,
88 ENABLE_MERGEJOIN = 0x40,
89 ENABLE_HASHJOIN = 0x80
92 #define ENABLE_ALL_SCAN (ENABLE_SEQSCAN | ENABLE_INDEXSCAN | ENABLE_BITMAPSCAN \
94 #if PG_VERSION_NUM >= 90200
95 #define ENABLE_ALL_SCAN (ENABLE_SEQSCAN | ENABLE_INDEXSCAN | ENABLE_BITMAPSCAN \
96 | ENABLE_TIDSCAN | ENABLE_INDEXONLYSCAN)
98 #define ENABLE_ALL_JOIN (ENABLE_NESTLOOP | ENABLE_MERGEJOIN | ENABLE_HASHJOIN)
99 #define DISABLE_ALL_SCAN 0
100 #define DISABLE_ALL_JOIN 0
102 typedef struct Hint Hint;
103 typedef struct PlanHint PlanHint;
105 typedef Hint *(*HintCreateFunction) (const char *hint_str, const char *keyword);
106 typedef void (*HintDeleteFunction) (Hint *hint);
107 typedef const char *(*HintParseFunction) (Hint *hint, PlanHint *plan, Query *parse, const char *str);
110 typedef enum HintStatus
112 HINT_STATE_NOTUSED = 0, /* specified relation not used in query */
113 HINT_STATE_USED, /* hint is used */
114 HINT_STATE_DUPLICATION, /* specified hint duplication */
115 /* execute error (parse error does not include it) */
119 #define hint_state_enabled(hint) ((hint)->base.state == HINT_STATE_NOTUSED || \
120 (hint)->base.state == HINT_STATE_USED)
122 /* common data for all hints. */
125 const char *hint_str; /* must not do pfree */
126 const char *keyword; /* must not do pfree */
128 HintDeleteFunction delete_func;
129 HintParseFunction parser_func;
132 /* scan method hints */
133 typedef struct ScanMethodHint
138 unsigned char enforce_mask;
141 /* join method hints */
142 typedef struct JoinMethodHint
147 unsigned char enforce_mask;
151 /* join order hints */
152 typedef struct LeadingHint
155 List *relations; /* relation names specified in Leading hint */
158 /* change a run-time parameter hints */
159 typedef struct SetHint
162 char *name; /* name of variable */
167 * Describes a context of hint processing.
171 char *hint_str; /* original hint string */
173 /* for scan method hints */
174 int nscan_hints; /* # of valid scan hints */
175 int max_scan_hints; /* # of slots for scan hints */
176 ScanMethodHint **scan_hints; /* parsed scan hints */
177 int init_scan_mask; /* initial value scan parameter */
178 Index parent_relid; /* inherit parent table relid */
180 /* for join method hints */
181 int njoin_hints; /* # of valid join hints */
182 int max_join_hints; /* # of slots for join hints */
183 JoinMethodHint **join_hints; /* parsed join hints */
184 int init_join_mask; /* initial value join parameter */
186 List **join_hint_level;
188 /* for Leading hints */
189 int nleading_hints; /* # of valid leading hints */
190 int max_leading_hints; /* # of slots for leading hints */
191 LeadingHint **leading_hints; /* parsed Leading hints */
194 GucContext context; /* which GUC parameters can we set? */
195 int nset_hints; /* # of valid set hints */
196 int max_set_hints; /* # of slots for set hints */
197 SetHint **set_hints; /* parsed Set hints */
201 * Describes a hint parser module which is bound with particular hint keyword.
203 typedef struct HintParser
207 HintCreateFunction create_func;
210 /* Module callbacks */
214 void pg_hint_plan_ProcessUtility(Node *parsetree, const char *queryString,
215 ParamListInfo params, bool isTopLevel,
216 DestReceiver *dest, char *completionTag);
217 static PlannedStmt *pg_hint_plan_planner(Query *parse, int cursorOptions,
218 ParamListInfo boundParams);
219 static void pg_hint_plan_get_relation_info(PlannerInfo *root, Oid relationObjectId,
220 bool inhparent, RelOptInfo *rel);
221 static RelOptInfo *pg_hint_plan_join_search(PlannerInfo *root, int levels_needed,
224 static Hint *ScanMethodHintCreate(const char *hint_str, const char *keyword);
225 static void ScanMethodHintDelete(ScanMethodHint *hint);
226 static const char *ScanMethodHintParse(ScanMethodHint *hint, PlanHint *plan, Query *parse, const char *str);
227 static Hint *JoinMethodHintCreate(const char *hint_str, const char *keyword);
228 static void JoinMethodHintDelete(JoinMethodHint *hint);
229 static const char *JoinMethodHintParse(JoinMethodHint *hint, PlanHint *plan, Query *parse, const char *str);
230 static Hint *LeadingHintCreate(const char *hint_str, const char *keyword);
231 static void LeadingHintDelete(LeadingHint *hint);
232 static const char *LeadingHintParse(LeadingHint *hint, PlanHint *plan, Query *parse, const char *str);
233 static Hint *SetHintCreate(const char *hint_str, const char *keyword);
234 static void SetHintDelete(SetHint *hint);
235 static const char *SetHintParse(SetHint *hint, PlanHint *plan, Query *parse, const char *str);
237 RelOptInfo *pg_hint_plan_standard_join_search(PlannerInfo *root, int levels_needed, List *initial_rels);
238 void pg_hint_plan_join_search_one_level(PlannerInfo *root, int level);
239 static void make_rels_by_clause_joins(PlannerInfo *root, RelOptInfo *old_rel, ListCell *other_rels);
240 static void make_rels_by_clauseless_joins(PlannerInfo *root, RelOptInfo *old_rel, ListCell *other_rels);
241 static bool has_join_restriction(PlannerInfo *root, RelOptInfo *rel);
242 static void set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
243 Index rti, RangeTblEntry *rte);
244 static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
246 static void set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
247 Index rti, RangeTblEntry *rte);
248 static List *accumulate_append_subpath(List *subpaths, Path *path);
249 static void set_dummy_rel_pathlist(RelOptInfo *rel);
250 RelOptInfo *pg_hint_plan_make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2);
254 static bool pg_hint_plan_enable = true;
255 static bool pg_hint_plan_debug_print = false;
256 static int pg_hint_plan_parse_messages = INFO;
258 static const struct config_enum_entry parse_messages_level_options[] = {
259 {"debug", DEBUG2, true},
260 {"debug5", DEBUG5, false},
261 {"debug4", DEBUG4, false},
262 {"debug3", DEBUG3, false},
263 {"debug2", DEBUG2, false},
264 {"debug1", DEBUG1, false},
266 {"info", INFO, false},
267 {"notice", NOTICE, false},
268 {"warning", WARNING, false},
269 {"error", ERROR, false},
271 {"fatal", FATAL, true},
272 {"panic", PANIC, true},
277 /* Saved hook values in case of unload */
278 static ProcessUtility_hook_type prev_ProcessUtility = NULL;
279 static planner_hook_type prev_planner = NULL;
280 static get_relation_info_hook_type prev_get_relation_info = NULL;
281 static join_search_hook_type prev_join_search = NULL;
283 /* フック関数をまたがって使用する情報を管理する */
284 static PlanHint *global = NULL;
287 * EXECUTEコマンド実行時に、ステートメント名を格納する。
288 * その他のコマンドの場合は、NULLに設定る。
290 static char *stmt_name = NULL;
292 static const HintParser parsers[] = {
293 {HINT_SEQSCAN, true, ScanMethodHintCreate},
294 {HINT_INDEXSCAN, true, ScanMethodHintCreate},
295 {HINT_BITMAPSCAN, true, ScanMethodHintCreate},
296 {HINT_TIDSCAN, true, ScanMethodHintCreate},
297 {HINT_NOSEQSCAN, true, ScanMethodHintCreate},
298 {HINT_NOINDEXSCAN, true, ScanMethodHintCreate},
299 {HINT_NOBITMAPSCAN, true, ScanMethodHintCreate},
300 {HINT_NOTIDSCAN, true, ScanMethodHintCreate},
301 #if PG_VERSION_NUM >= 90200
302 {HINT_INDEXONLYSCAN, true, ScanMethodHintCreate},
303 {HINT_NOINDEXONLYSCAN, true, ScanMethodHintCreate},
305 {HINT_NESTLOOP, true, JoinMethodHintCreate},
306 {HINT_MERGEJOIN, true, JoinMethodHintCreate},
307 {HINT_HASHJOIN, true, JoinMethodHintCreate},
308 {HINT_NONESTLOOP, true, JoinMethodHintCreate},
309 {HINT_NOMERGEJOIN, true, JoinMethodHintCreate},
310 {HINT_NOHASHJOIN, true, JoinMethodHintCreate},
311 {HINT_LEADING, true, LeadingHintCreate},
312 {HINT_SET, true, SetHintCreate},
317 * Module load callbacks
322 /* Define custom GUC variables. */
323 DefineCustomBoolVariable("pg_hint_plan.enable",
324 "Instructions or hints to the planner using block comments.",
326 &pg_hint_plan_enable,
334 DefineCustomBoolVariable("pg_hint_plan.debug_print",
335 "Logs each query's parse results of the hint.",
337 &pg_hint_plan_debug_print,
345 DefineCustomEnumVariable("pg_hint_plan.parse_messages",
346 "Messege level of the parse error.",
348 &pg_hint_plan_parse_messages,
350 parse_messages_level_options,
358 prev_ProcessUtility = ProcessUtility_hook;
359 ProcessUtility_hook = pg_hint_plan_ProcessUtility;
360 prev_planner = planner_hook;
361 planner_hook = pg_hint_plan_planner;
362 prev_get_relation_info = get_relation_info_hook;
363 get_relation_info_hook = pg_hint_plan_get_relation_info;
364 prev_join_search = join_search_hook;
365 join_search_hook = pg_hint_plan_join_search;
369 * Module unload callback
375 /* Uninstall hooks. */
376 ProcessUtility_hook = prev_ProcessUtility;
377 planner_hook = prev_planner;
378 get_relation_info_hook = prev_get_relation_info;
379 join_search_hook = prev_join_search;
383 pg_hint_plan_ProcessUtility(Node *parsetree, const char *queryString,
384 ParamListInfo params, bool isTopLevel,
385 DestReceiver *dest, char *completionTag)
389 if (!pg_hint_plan_enable)
391 if (prev_ProcessUtility)
392 (*prev_ProcessUtility) (parsetree, queryString, params,
393 isTopLevel, dest, completionTag);
395 standard_ProcessUtility(parsetree, queryString, params,
396 isTopLevel, dest, completionTag);
402 if (IsA(node , ExplainStmt))
405 * EXPLAIN対象のクエリのパースツリーを取得する
410 stmt = (ExplainStmt *) node;
412 Assert(IsA(stmt->query, Query));
413 query = (Query *) stmt->query;
415 if (query->commandType == CMD_UTILITY && query->utilityStmt != NULL)
416 node = query->utilityStmt;
420 * EXECUTEコマンドならば、PREPARE時に指定されたクエリ文字列を取得し、ヒント
423 if (IsA(node , ExecuteStmt))
427 stmt = (ExecuteStmt *) node;
428 stmt_name = stmt->name;
433 if (prev_ProcessUtility)
434 (*prev_ProcessUtility) (parsetree, queryString, params,
435 isTopLevel, dest, completionTag);
437 standard_ProcessUtility(parsetree, queryString, params,
438 isTopLevel, dest, completionTag);
444 ScanMethodHintCreate(const char *hint_str, const char *keyword)
446 ScanMethodHint *hint;
448 hint = palloc(sizeof(ScanMethodHint));
449 hint->base.hint_str = hint_str;
450 hint->base.keyword = keyword;
451 hint->base.state = HINT_STATE_NOTUSED;
452 hint->base.delete_func = (HintDeleteFunction) ScanMethodHintDelete;
453 hint->base.parser_func = (HintParseFunction) ScanMethodHintParse;
454 hint->relname = NULL;
455 hint->indexnames = NIL;
456 hint->enforce_mask = 0;
458 return (Hint *) hint;
462 ScanMethodHintDelete(ScanMethodHint *hint)
468 pfree(hint->relname);
469 list_free_deep(hint->indexnames);
474 JoinMethodHintCreate(const char *hint_str, const char *keyword)
476 JoinMethodHint *hint;
478 hint = palloc(sizeof(JoinMethodHint));
479 hint->base.hint_str = hint_str;
480 hint->base.keyword = keyword;
481 hint->base.state = HINT_STATE_NOTUSED;
482 hint->base.delete_func = (HintDeleteFunction) JoinMethodHintDelete;
483 hint->base.parser_func = (HintParseFunction) JoinMethodHintParse;
485 hint->relnames = NULL;
486 hint->enforce_mask = 0;
487 hint->joinrelids = NULL;
489 return (Hint *) hint;
493 JoinMethodHintDelete(JoinMethodHint *hint)
502 for (i = 0; i < hint->nrels; i++)
503 pfree(hint->relnames[i]);
504 pfree(hint->relnames);
506 bms_free(hint->joinrelids);
511 LeadingHintCreate(const char *hint_str, const char *keyword)
515 hint = palloc(sizeof(LeadingHint));
516 hint->base.hint_str = hint_str;
517 hint->base.keyword = keyword;
518 hint->base.state = HINT_STATE_NOTUSED;
519 hint->base.delete_func = (HintDeleteFunction)LeadingHintDelete;
520 hint->base.parser_func = (HintParseFunction) LeadingHintParse;
521 hint->relations = NIL;
523 return (Hint *) hint;
527 LeadingHintDelete(LeadingHint *hint)
532 list_free_deep(hint->relations);
537 SetHintCreate(const char *hint_str, const char *keyword)
541 hint = palloc(sizeof(SetHint));
542 hint->base.hint_str = hint_str;
543 hint->base.keyword = keyword;
544 hint->base.state = HINT_STATE_NOTUSED;
545 hint->base.delete_func = (HintDeleteFunction) SetHintDelete;
546 hint->base.parser_func = (HintParseFunction) SetHintParse;
550 return (Hint *) hint;
554 SetHintDelete(SetHint *hint)
571 hint = palloc(sizeof(PlanHint));
572 hint->hint_str = NULL;
573 hint->nscan_hints = 0;
574 hint->max_scan_hints = 0;
575 hint->scan_hints = NULL;
576 hint->init_scan_mask = 0;
577 hint->parent_relid = 0;
578 hint->njoin_hints = 0;
579 hint->max_join_hints = 0;
580 hint->join_hints = NULL;
581 hint->init_join_mask = 0;
582 hint->join_hint_level = NULL;
583 hint->nleading_hints = 0;
584 hint->max_leading_hints = 0;
585 hint->leading_hints = NULL;
586 hint->context = superuser() ? PGC_SUSET : PGC_USERSET;
587 hint->nset_hints = 0;
588 hint->max_set_hints = 0;
589 hint->set_hints = NULL;
595 PlanHintDelete(PlanHint *hint)
603 pfree(hint->hint_str);
605 for (i = 0; i < hint->nscan_hints; i++)
606 ScanMethodHintDelete(hint->scan_hints[i]);
607 if (hint->scan_hints)
608 pfree(hint->scan_hints);
610 for (i = 0; i < hint->njoin_hints; i++)
611 JoinMethodHintDelete(hint->join_hints[i]);
612 if (hint->join_hints)
613 pfree(hint->join_hints);
615 for (i = 0; i < hint->nleading_hints; i++)
616 LeadingHintDelete(hint->leading_hints[i]);
617 if (hint->leading_hints)
618 pfree(hint->leading_hints);
620 for (i = 0; i < hint->nset_hints; i++)
621 SetHintDelete(hint->set_hints[i]);
623 pfree(hint->set_hints);
629 PlanHintIsEmpty(PlanHint *hint)
631 if (hint->nscan_hints == 0 &&
632 hint->njoin_hints == 0 &&
633 hint->nleading_hints == 0 &&
634 hint->nset_hints == 0)
641 dump_quote_value(StringInfo buf, const char *value)
643 bool need_quote = false;
646 for (str = value; *str != '\0'; str++)
648 if (isspace(*str) || *str == ')' || *str == '"' || *str == '\0')
651 appendStringInfoCharMacro(buf, '"');
656 for (str = value; *str != '\0'; str++)
659 appendStringInfoCharMacro(buf, '"');
661 appendStringInfoCharMacro(buf, *str);
665 appendStringInfoCharMacro(buf, '"');
669 all_hint_dump(PlanHint *hint, StringInfo buf, const char *title, HintStatus state)
674 appendStringInfo(buf, "%s:\n", title);
675 for (i = 0; i < hint->nscan_hints; i++)
677 ScanMethodHint *h = hint->scan_hints[i];
679 if (h->base.state != state)
682 appendStringInfo(buf, "%s(", h->base.keyword);
683 dump_quote_value(buf, h->relname);
684 foreach(l, h->indexnames)
686 appendStringInfoCharMacro(buf, ' ');
687 dump_quote_value(buf, (char *) lfirst(l));
689 appendStringInfoString(buf, ")\n");
692 for (i = 0; i < hint->njoin_hints; i++)
694 JoinMethodHint *h = hint->join_hints[i];
697 if (h->base.state != state)
700 appendStringInfo(buf, "%s(", h->base.keyword);
701 dump_quote_value(buf, h->relnames[0]);
702 for (j = 1; j < h->nrels; j++)
704 appendStringInfoCharMacro(buf, ' ');
705 dump_quote_value(buf, h->relnames[j]);
707 appendStringInfoString(buf, ")\n");
710 for (i = 0; i < hint->nset_hints; i++)
712 SetHint *h = hint->set_hints[i];
714 if (h->base.state != state)
717 appendStringInfo(buf, "%s(", HINT_SET);
718 dump_quote_value(buf, h->name);
719 appendStringInfoCharMacro(buf, ' ');
720 dump_quote_value(buf, h->value);
721 appendStringInfo(buf, ")\n");
724 for (i = 0; i < hint->nleading_hints; i++)
726 LeadingHint *h = hint->leading_hints[i];
729 if (h->base.state != state)
732 appendStringInfo(buf, "%s(", HINT_LEADING);
734 foreach(l, h->relations)
739 appendStringInfoCharMacro(buf, ' ');
741 dump_quote_value(buf, (char *)lfirst(l));
744 appendStringInfoString(buf, ")\n");
749 PlanHintDump(PlanHint *hint)
755 elog(LOG, "pg_hint_plan:\nno hint");
759 initStringInfo(&buf);
761 appendStringInfoString(&buf, "pg_hint_plan:\n");
762 all_hint_dump(hint, &buf, "used hint", HINT_STATE_USED);
763 all_hint_dump(hint, &buf, "not used hint", HINT_STATE_NOTUSED);
764 all_hint_dump(hint, &buf, "duplication hint", HINT_STATE_DUPLICATION);
765 all_hint_dump(hint, &buf, "error hint", HINT_STATE_ERROR);
767 elog(LOG, "%s", buf.data);
773 RelnameCmp(const void *a, const void *b)
775 const char *relnamea = *((const char **) a);
776 const char *relnameb = *((const char **) b);
778 return strcmp(relnamea, relnameb);
782 ScanMethodHintCmp(const void *a, const void *b, bool order)
784 const ScanMethodHint *hinta = *((const ScanMethodHint **) a);
785 const ScanMethodHint *hintb = *((const ScanMethodHint **) b);
788 if ((result = RelnameCmp(&hinta->relname, &hintb->relname)) != 0)
793 return hinta->base.hint_str - hintb->base.hint_str;
799 ScanMethodHintCmpIsOrder(const void *a, const void *b)
801 return ScanMethodHintCmp(a, b, true);
805 JoinMethodHintCmp(const void *a, const void *b, bool order)
807 const JoinMethodHint *hinta = *((const JoinMethodHint **) a);
808 const JoinMethodHint *hintb = *((const JoinMethodHint **) b);
811 if (hinta->nrels != hintb->nrels)
812 return hinta->nrels - hintb->nrels;
814 for (i = 0; i < hinta->nrels; i++)
817 if ((result = RelnameCmp(&hinta->relnames[i], &hintb->relnames[i])) != 0)
823 return hinta->base.hint_str - hintb->base.hint_str;
829 JoinMethodHintCmpIsOrder(const void *a, const void *b)
831 return JoinMethodHintCmp(a, b, true);
835 LeadingHintCmp(const void *a, const void *b, bool order)
837 const LeadingHint *hinta = *((const LeadingHint **) a);
838 const LeadingHint *hintb = *((const LeadingHint **) b);
842 return hinta->base.hint_str - hintb->base.hint_str;
849 LeadingHintCmpIsOrder(const void *a, const void *b)
851 return LeadingHintCmp(a, b, true);
856 SetHintCmp(const void *a, const void *b, bool order)
858 const SetHint *hinta = *((const SetHint **) a);
859 const SetHint *hintb = *((const SetHint **) b);
862 if ((result = strcmp(hinta->name, hintb->name)) != 0)
867 return hinta->base.hint_str - hintb->base.hint_str;
873 SetHintCmpIsOrder(const void *a, const void *b)
875 return SetHintCmp(a, b, true);
878 #if PG_VERSION_NUM < 90200
880 set_config_option_wrapper(const char *name, const char *value,
881 GucContext context, GucSource source,
882 GucAction action, bool changeVal, int elevel)
885 MemoryContext ccxt = CurrentMemoryContext;
889 result = set_config_option(name, value, context, source,
900 ecxt = MemoryContextSwitchTo(ccxt);
901 errdata = CopyErrorData();
902 ereport(elevel, (errcode(errdata->sqlerrcode),
903 errmsg("%s", errdata->message),
904 errdata->detail ? errdetail("%s", errdata->detail) : 0,
905 errdata->hint ? errhint("%s", errdata->hint) : 0));
906 FreeErrorData(errdata);
908 MemoryContextSwitchTo(ecxt);
915 #define set_config_option(name, value, context, source, \
916 action, changeVal, elevel) \
917 set_config_option_wrapper(name, value, context, source, \
918 action, changeVal, elevel)
922 set_config_options(SetHint **options, int noptions, GucContext context)
927 save_nestlevel = NewGUCNestLevel();
929 for (i = 0; i < noptions; i++)
931 SetHint *hint = options[i];
934 if (!hint_state_enabled(hint))
937 result = set_config_option(hint->name, hint->value, context,
938 PGC_S_SESSION, GUC_ACTION_SAVE, true,
939 pg_hint_plan_parse_messages);
941 hint->base.state = HINT_STATE_USED;
943 hint->base.state = HINT_STATE_ERROR;
946 return save_nestlevel;
949 #define SET_CONFIG_OPTION(name, type_bits) \
950 set_config_option((name), \
951 (enforce_mask & (type_bits)) ? "true" : "false", \
952 context, PGC_S_SESSION, GUC_ACTION_SAVE, true, ERROR)
955 set_scan_config_options(unsigned char enforce_mask, GucContext context)
957 SET_CONFIG_OPTION("enable_seqscan", ENABLE_SEQSCAN);
958 SET_CONFIG_OPTION("enable_indexscan", ENABLE_INDEXSCAN);
959 SET_CONFIG_OPTION("enable_bitmapscan", ENABLE_BITMAPSCAN);
960 SET_CONFIG_OPTION("enable_tidscan", ENABLE_TIDSCAN);
961 #if PG_VERSION_NUM >= 90200
962 SET_CONFIG_OPTION("enable_indexonlyscan", ENABLE_INDEXSCAN);
967 set_join_config_options(unsigned char enforce_mask, GucContext context)
969 SET_CONFIG_OPTION("enable_nestloop", ENABLE_NESTLOOP);
970 SET_CONFIG_OPTION("enable_mergejoin", ENABLE_MERGEJOIN);
971 SET_CONFIG_OPTION("enable_hashjoin", ENABLE_HASHJOIN);
979 parse_keyword(const char *str, StringInfo buf)
983 while (!isspace(*str) && *str != '(' && *str != '\0')
984 appendStringInfoCharMacro(buf, *str++);
990 skip_opened_parenthesis(const char *str)
996 parse_ereport(str, ("Opened parenthesis is necessary."));
1006 skip_closed_parenthesis(const char *str)
1012 parse_ereport(str, ("Closed parenthesis is necessary."));
1022 * 二重引用符で囲まれているかもしれないトークンを読み取り word 引数に palloc
1023 * で確保したバッファに格納してそのポインタを返す。
1025 * 正常にパースできた場合は残りの文字列の先頭位置を、異常があった場合は NULL を
1029 parse_quote_value(const char *str, char **word, char *value_type)
1034 /* 先頭のスペースは読み飛ばす。 */
1037 initStringInfo(&buf);
1050 /* 二重引用符が閉じられていない場合はパース中断 */
1054 parse_ereport(str, ("Unterminated quoted %s.", value_type));
1059 * エスケープ対象のダブルクウォートをスキップする。
1060 * もしブロックコメントの開始文字列や終了文字列もオブジェクト名とし
1061 * て使用したい場合は、/ と * もエスケープ対象とすることで使用できる
1062 * が、処理対象としていない。もしテーブル名にこれらの文字が含まれる
1063 * 場合は、エイリアスを指定する必要がある。
1073 if (isspace(*str) || *str == ')' || *str == '"' || *str == '\0')
1076 appendStringInfoCharMacro(&buf, *str++);
1082 parse_ereport(str, ("%s is necessary.", value_type));
1092 parse_hints(PlanHint *plan, Query *parse, const char *str)
1097 initStringInfo(&buf);
1098 while (*str != '\0')
1100 const HintParser *parser;
1102 /* in error message, we output the comment including the keyword. */
1103 head = (char *) str;
1105 /* parse only the keyword of the hint. */
1106 resetStringInfo(&buf);
1107 str = parse_keyword(str, &buf);
1109 for (parser = parsers; parser->keyword != NULL; parser++)
1111 char *keyword = parser->keyword;
1114 if (strcasecmp(buf.data, keyword) != 0)
1117 hint = parser->create_func(head, keyword);
1119 if (parser->have_paren)
1121 /* parser of each hint does parse in a parenthesis. */
1122 if ((str = skip_opened_parenthesis(str)) == NULL ||
1123 (str = hint->parser_func(hint, plan, parse, str)) == NULL ||
1124 (str = skip_closed_parenthesis(str)) == NULL)
1126 hint->delete_func(hint);
1133 if ((str = hint->parser_func(hint, plan, parse, str)) == NULL)
1135 hint->delete_func(hint);
1141 * 直前のヒントに括弧の指定がなければ次のヒントの間に空白が必要
1143 if (!isspace(*str) && *str == '\0')
1144 parse_ereport(str, ("Delimiter of the hint is necessary."));
1152 if (parser->keyword == NULL)
1154 parse_ereport(head, ("Keyword \"%s\" does not exist.", buf.data));
1164 * Do basic parsing of the query head comment.
1167 parse_head_comment(Query *parse)
1176 /* get client-supplied query string. */
1179 PreparedStatement *entry;
1181 entry = FetchPreparedStatement(stmt_name, true);
1182 p = entry->plansource->query_string;
1185 p = debug_query_string;
1190 /* extract query head comment. */
1191 len = strlen(HINT_START);
1193 if (strncmp(p, HINT_START, len))
1200 /* find hint end keyword. */
1201 if ((tail = strstr(p, HINT_END)) == NULL)
1203 parse_ereport(head, ("Unterminated block comment."));
1207 /* 入れ子にしたブロックコメントはサポートしない */
1208 if ((head = strstr(p, BLOCK_COMMENT_START)) != NULL && head < tail)
1209 parse_ereport(head, ("Block comments nest doesn't supported."));
1213 head = palloc(len + 1);
1214 memcpy(head, p, len);
1218 plan = PlanHintCreate();
1219 plan->hint_str = head;
1221 /* parse each hint. */
1222 parse_hints(plan, parse, p);
1224 /* When nothing specified a hint, we free PlanHint and returns NULL. */
1225 if (PlanHintIsEmpty(plan))
1227 PlanHintDelete(plan);
1231 /* 重複したscan method hintを検索する */
1232 qsort(plan->scan_hints, plan->nscan_hints, sizeof(ScanMethodHint *), ScanMethodHintCmpIsOrder);
1233 for (i = 0; i < plan->nscan_hints - 1; i++)
1235 if (ScanMethodHintCmp(plan->scan_hints + i,
1236 plan->scan_hints + i + 1, false) == 0)
1238 parse_ereport(plan->scan_hints[i]->base.hint_str,
1239 ("Conflict scan method hint."));
1240 plan->scan_hints[i]->base.state = HINT_STATE_DUPLICATION;
1244 /* 重複したjoin method hintを検索する */
1245 qsort(plan->join_hints, plan->njoin_hints, sizeof(JoinMethodHint *), JoinMethodHintCmpIsOrder);
1246 for (i = 0; i < plan->njoin_hints - 1; i++)
1248 if (JoinMethodHintCmp(plan->join_hints + i,
1249 plan->join_hints + i + 1, false) == 0)
1251 parse_ereport(plan->join_hints[i]->base.hint_str,
1252 ("Duplicate join method hint."));
1253 plan->join_hints[i]->base.state = HINT_STATE_DUPLICATION;
1257 /* 重複したSet hintを検索する */
1258 qsort(plan->set_hints, plan->nset_hints, sizeof(SetHint *), SetHintCmpIsOrder);
1259 for (i = 0; i < plan->nset_hints - 1; i++)
1261 if (SetHintCmp(plan->set_hints + i,
1262 plan->set_hints + i + 1, false) == 0)
1264 parse_ereport(plan->set_hints[i]->base.hint_str,
1265 ("Duplicate set hint."));
1266 plan->set_hints[i]->base.state = HINT_STATE_DUPLICATION;
1270 /* 重複したLeading hintを検索する */
1271 for (i = 0; i < plan->nleading_hints - 1; i++)
1273 if (LeadingHintCmp(plan->leading_hints + i,
1274 plan->leading_hints + i + 1, false) == 0)
1276 parse_ereport(plan->leading_hints[i]->base.hint_str,
1277 ("Duplicate leading hint."));
1278 plan->leading_hints[i]->base.state = HINT_STATE_DUPLICATION;
1286 * スキャン方式ヒントのカッコ内をパースする
1289 ScanMethodHintParse(ScanMethodHint *hint, PlanHint *plan, Query *parse, const char *str)
1291 const char *keyword = hint->base.keyword;
1294 * スキャン方式のヒントでリレーション名が読み取れない場合はヒント無効
1296 if ((str = parse_quote_value(str, &hint->relname, "ralation name")) == NULL)
1302 * インデックスリストを受け付けるヒントであれば、インデックス参照をパース
1305 if (strcmp(keyword, HINT_INDEXSCAN) == 0 ||
1306 #if PG_VERSION_NUM >= 90200
1307 strcmp(keyword, HINT_INDEXONLYSCAN) == 0 ||
1309 strcmp(keyword, HINT_BITMAPSCAN) == 0)
1311 while (*str != ')' && *str != '\0')
1315 str = parse_quote_value(str, &indexname, "index name");
1319 hint->indexnames = lappend(hint->indexnames, indexname);
1324 /* カッコが閉じていなければヒント無効。 */
1325 skip_space(str); /* just in case */
1328 parse_ereport(str, ("Closed parenthesis is necessary."));
1333 * ヒントごとに決まっている許容スキャン方式をビットマスクとして設定
1335 if (strcasecmp(keyword, HINT_SEQSCAN) == 0)
1336 hint->enforce_mask = ENABLE_SEQSCAN;
1337 else if (strcasecmp(keyword, HINT_INDEXSCAN) == 0)
1338 hint->enforce_mask = ENABLE_INDEXSCAN;
1339 else if (strcasecmp(keyword, HINT_BITMAPSCAN) == 0)
1340 hint->enforce_mask = ENABLE_BITMAPSCAN;
1341 else if (strcasecmp(keyword, HINT_TIDSCAN) == 0)
1342 hint->enforce_mask = ENABLE_TIDSCAN;
1343 else if (strcasecmp(keyword, HINT_NOSEQSCAN) == 0)
1344 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_SEQSCAN;
1345 else if (strcasecmp(keyword, HINT_NOINDEXSCAN) == 0)
1346 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_INDEXSCAN;
1347 else if (strcasecmp(keyword, HINT_NOBITMAPSCAN) == 0)
1348 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_BITMAPSCAN;
1349 else if (strcasecmp(keyword, HINT_NOTIDSCAN) == 0)
1350 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_TIDSCAN;
1353 parse_ereport(str, ("Unrecognized hint keyword \"%s\".", keyword));
1358 * 出来上がったヒント情報を追加。スロットが足りない場合は二倍に拡張する。
1360 if (plan->nscan_hints == 0)
1362 plan->max_scan_hints = HINT_ARRAY_DEFAULT_INITSIZE;
1363 plan->scan_hints = palloc(sizeof(ScanMethodHint *) * plan->max_scan_hints);
1365 else if (plan->nscan_hints == plan->max_scan_hints)
1367 plan->max_scan_hints *= 2;
1368 plan->scan_hints = repalloc(plan->scan_hints,
1369 sizeof(ScanMethodHint *) * plan->max_scan_hints);
1372 plan->scan_hints[plan->nscan_hints] = hint;
1373 plan->nscan_hints++;
1379 JoinMethodHintParse(JoinMethodHint *hint, PlanHint *plan, Query *parse, const char *str)
1382 const char *keyword = hint->base.keyword;
1386 hint->relnames = palloc(sizeof(char *));
1388 while ((str = parse_quote_value(str, &relname, "table name")) != NULL)
1391 hint->relnames = repalloc(hint->relnames, sizeof(char *) * hint->nrels);
1392 hint->relnames[hint->nrels - 1] = relname;
1402 /* Join 対象のテーブルは最低でも2つ指定する必要がある */
1403 if (hint->nrels < 2)
1405 parse_ereport(str, ("Specified relation more than two."));
1410 qsort(hint->relnames, hint->nrels, sizeof(char *), RelnameCmp);
1412 if (strcasecmp(keyword, HINT_NESTLOOP) == 0)
1413 hint->enforce_mask = ENABLE_NESTLOOP;
1414 else if (strcasecmp(keyword, HINT_MERGEJOIN) == 0)
1415 hint->enforce_mask = ENABLE_MERGEJOIN;
1416 else if (strcasecmp(keyword, HINT_HASHJOIN) == 0)
1417 hint->enforce_mask = ENABLE_HASHJOIN;
1418 else if (strcasecmp(keyword, HINT_NONESTLOOP) == 0)
1419 hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_NESTLOOP;
1420 else if (strcasecmp(keyword, HINT_NOMERGEJOIN) == 0)
1421 hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_MERGEJOIN;
1422 else if (strcasecmp(keyword, HINT_NOHASHJOIN) == 0)
1423 hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_HASHJOIN;
1426 parse_ereport(str, ("Unrecognized hint keyword \"%s\".", keyword));
1430 if (plan->njoin_hints == 0)
1432 plan->max_join_hints = HINT_ARRAY_DEFAULT_INITSIZE;
1433 plan->join_hints = palloc(sizeof(JoinMethodHint *) * plan->max_join_hints);
1435 else if (plan->njoin_hints == plan->max_join_hints)
1437 plan->max_join_hints *= 2;
1438 plan->join_hints = repalloc(plan->join_hints,
1439 sizeof(JoinMethodHint *) * plan->max_join_hints);
1442 plan->join_hints[plan->njoin_hints] = hint;
1443 plan->njoin_hints++;
1449 LeadingHintParse(LeadingHint *hint, PlanHint *plan, Query *parse, const char *str)
1457 if ((str = parse_quote_value(str, &relname, "relation name")) == NULL)
1460 hint->relations = lappend(hint->relations, relname);
1465 /* テーブル指定が2つ未満の場合は、Leading ヒントはエラーとする */
1466 if (list_length(hint->relations) < 2)
1468 parse_ereport(hint->base.hint_str,
1469 ("In %s hint, specified relation name 2 or more.", HINT_LEADING));
1470 hint->base.state = HINT_STATE_ERROR;
1473 if (plan->nleading_hints == 0)
1475 plan->max_leading_hints = HINT_ARRAY_DEFAULT_INITSIZE;
1476 plan->leading_hints = palloc(sizeof(LeadingHint *) * plan->max_leading_hints);
1478 else if (plan->nleading_hints == plan->max_leading_hints)
1480 plan->max_leading_hints *= 2;
1481 plan->leading_hints = repalloc(plan->leading_hints,
1482 sizeof(LeadingHint *) * plan->max_leading_hints);
1485 plan->leading_hints[plan->nleading_hints] = hint;
1486 plan->nleading_hints++;
1492 SetHintParse(SetHint *hint, PlanHint *plan, Query *parse, const char *str)
1494 if ((str = parse_quote_value(str, &hint->name, "parameter name")) == NULL ||
1495 (str = parse_quote_value(str, &hint->value, "parameter value")) == NULL)
1501 parse_ereport(str, ("Closed parenthesis is necessary."));
1505 if (plan->nset_hints == 0)
1507 plan->max_set_hints = HINT_ARRAY_DEFAULT_INITSIZE;
1508 plan->set_hints = palloc(sizeof(SetHint *) * plan->max_set_hints);
1510 else if (plan->nset_hints == plan->max_set_hints)
1512 plan->max_set_hints *= 2;
1513 plan->set_hints = repalloc(plan->set_hints,
1514 sizeof(SetHint *) * plan->max_set_hints);
1517 plan->set_hints[plan->nset_hints] = hint;
1523 static PlannedStmt *
1524 pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
1527 PlannedStmt *result;
1530 * hintが指定されない、または空のhintを指定された場合は通常のparser処理をお
1532 * 他のフック関数で実行されるhint処理をスキップするために、global 変数をNULL
1535 if (!pg_hint_plan_enable || (global = parse_head_comment(parse)) == NULL)
1540 return (*prev_planner) (parse, cursorOptions, boundParams);
1542 return standard_planner(parse, cursorOptions, boundParams);
1545 /* Set hint で指定されたGUCパラメータを設定する */
1546 save_nestlevel = set_config_options(global->set_hints, global->nset_hints,
1550 global->init_scan_mask |= ENABLE_SEQSCAN;
1551 if (enable_indexscan)
1552 global->init_scan_mask |= ENABLE_INDEXSCAN;
1553 if (enable_bitmapscan)
1554 global->init_scan_mask |= ENABLE_BITMAPSCAN;
1556 global->init_scan_mask |= ENABLE_TIDSCAN;
1557 #if PG_VERSION_NUM >= 90200
1558 if (enable_indexonlyscan)
1559 global->init_scan_mask |= ENABLE_INDEXONLYSCAN;
1561 if (enable_nestloop)
1562 global->init_join_mask |= ENABLE_NESTLOOP;
1563 if (enable_mergejoin)
1564 global->init_join_mask |= ENABLE_MERGEJOIN;
1565 if (enable_hashjoin)
1566 global->init_join_mask |= ENABLE_HASHJOIN;
1569 result = (*prev_planner) (parse, cursorOptions, boundParams);
1571 result = standard_planner(parse, cursorOptions, boundParams);
1574 * Restore the GUC variables we set above.
1576 AtEOXact_GUC(true, save_nestlevel);
1579 * Print hint if debugging.
1581 if (pg_hint_plan_debug_print)
1583 PlanHintDump(global);
1585 elog_node_display(INFO, "rtable", parse->rtable, true);
1589 PlanHintDelete(global);
1596 * aliasnameと一致するSCANヒントを探す
1598 static ScanMethodHint *
1599 find_scan_hint(PlannerInfo *root, RelOptInfo *rel)
1605 * RELOPT_BASEREL でなければ、scan method ヒントが適用しない。
1606 * 子テーブルの場合はRELOPT_OTHER_MEMBER_RELとなるが、サポート対象外とする。
1607 * また、通常のリレーション以外は、スキャン方式を選択できない。
1609 if (rel->reloptkind != RELOPT_BASEREL || rel->rtekind != RTE_RELATION)
1612 rte = root->simple_rte_array[rel->relid];
1614 /* 外部表はスキャン方式が選択できない。 */
1615 if (rte->relkind == RELKIND_FOREIGN_TABLE)
1618 for (i = 0; i < global->nscan_hints; i++)
1620 ScanMethodHint *hint = global->scan_hints[i];
1622 if (!hint_state_enabled(hint))
1625 if (RelnameCmp(&rte->eref->aliasname, &hint->relname) == 0)
1633 pg_hint_plan_get_relation_info(PlannerInfo *root, Oid relationObjectId,
1634 bool inhparent, RelOptInfo *rel)
1636 ScanMethodHint *hint;
1641 if (prev_get_relation_info)
1642 (*prev_get_relation_info) (root, relationObjectId, inhparent, rel);
1644 /* 有効なヒントが指定されなかった場合は処理をスキップする。 */
1650 /* cache does relids of parent table */
1651 global->parent_relid = rel->relid;
1653 else if (global->parent_relid != 0)
1655 /* If this rel is an appendrel child, */
1658 /* append_rel_list contains all append rels; ignore others */
1659 foreach(l, root->append_rel_list)
1661 AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
1663 /* This rel is child table. */
1664 if (appinfo->parent_relid == global->parent_relid)
1668 /* This rel is not inherit table. */
1669 global->parent_relid = 0;
1672 /* scan hint が指定されない場合は、GUCパラメータをリセットする。 */
1673 if ((hint = find_scan_hint(root, rel)) == NULL)
1675 set_scan_config_options(global->init_scan_mask, global->context);
1678 set_scan_config_options(hint->enforce_mask, global->context);
1679 hint->base.state = HINT_STATE_USED;
1681 /* インデックスを全て削除し、スキャンに使えなくする */
1682 if (hint->enforce_mask == ENABLE_SEQSCAN ||
1683 hint->enforce_mask == ENABLE_TIDSCAN)
1685 list_free_deep(rel->indexlist);
1686 rel->indexlist = NIL;
1687 hint->base.state = HINT_STATE_USED;
1692 /* 後でパスを作り直すため、ここではなにもしない */
1693 if (hint->indexnames == NULL)
1696 /* 指定されたインデックスのみをのこす */
1698 for (cell = list_head(rel->indexlist); cell; cell = next)
1700 IndexOptInfo *info = (IndexOptInfo *) lfirst(cell);
1701 char *indexname = get_rel_name(info->indexoid);
1703 bool use_index = false;
1707 foreach(l, hint->indexnames)
1709 if (RelnameCmp(&indexname, &lfirst(l)) == 0)
1717 rel->indexlist = list_delete_cell(rel->indexlist, cell, prev);
1726 * aliasnameがクエリ中に指定した別名と一致する場合は、そのインデックスを返し、一致
1728 * aliasnameがクエリ中に複数回指定された場合は、-1を返す。
1731 find_relid_aliasname(PlannerInfo *root, char *aliasname, List *initial_rels,
1737 for (i = 1; i < root->simple_rel_array_size; i++)
1741 if (root->simple_rel_array[i] == NULL)
1744 Assert(i == root->simple_rel_array[i]->relid);
1746 if (RelnameCmp(&aliasname, &root->simple_rte_array[i]->eref->aliasname)
1750 foreach(l, initial_rels)
1752 RelOptInfo *rel = (RelOptInfo *) lfirst(l);
1754 if (rel->reloptkind == RELOPT_BASEREL)
1756 if (rel->relid != i)
1761 Assert(rel->reloptkind == RELOPT_JOINREL);
1763 if (!bms_is_member(i, rel->relids))
1769 parse_ereport(str, ("Relation name \"%s\" is ambiguous.", aliasname));
1783 * relidビットマスクと一致するヒントを探す
1785 static JoinMethodHint *
1786 find_join_hint(Relids joinrelids)
1791 join_hint = global->join_hint_level[bms_num_members(joinrelids)];
1792 foreach(l, join_hint)
1794 JoinMethodHint *hint = (JoinMethodHint *) lfirst(l);
1795 if (bms_equal(joinrelids, hint->joinrelids))
1803 * 結合方式のヒントを使用しやすい構造に変換する。
1806 transform_join_hints(PlanHint *plan, PlannerInfo *root, int nbaserel,
1807 List *initial_rels, JoinMethodHint **join_method_hints)
1816 for (i = 0; i < plan->njoin_hints; i++)
1818 JoinMethodHint *hint = plan->join_hints[i];
1821 if (!hint_state_enabled(hint) || hint->nrels > nbaserel)
1824 bms_free(hint->joinrelids);
1825 hint->joinrelids = NULL;
1827 for (j = 0; j < hint->nrels; j++)
1829 char *relname = hint->relnames[j];
1831 relid = find_relid_aliasname(root, relname, initial_rels,
1832 hint->base.hint_str);
1835 hint->base.state = HINT_STATE_ERROR;
1840 if (bms_is_member(relid, hint->joinrelids))
1842 parse_ereport(hint->base.hint_str,
1843 ("Relation name \"%s\" is duplicate.", relname));
1844 hint->base.state = HINT_STATE_ERROR;
1848 hint->joinrelids = bms_add_member(hint->joinrelids, relid);
1851 if (relid <= 0 || hint->base.state == HINT_STATE_ERROR)
1854 plan->join_hint_level[hint->nrels] =
1855 lappend(plan->join_hint_level[hint->nrels], hint);
1859 * 有効なLeading ヒントが指定されている場合は、結合順にあわせてjoin method hint
1862 if (plan->nleading_hints == 0)
1865 lhint = plan->leading_hints[plan->nleading_hints - 1];
1866 if (!hint_state_enabled(lhint))
1869 /* Leading hint は、全ての join 方式が有効な hint として登録する */
1872 foreach(l, lhint->relations)
1874 char *relname = (char *)lfirst(l);
1875 JoinMethodHint *hint;
1877 i = find_relid_aliasname(root, relname, initial_rels, plan->hint_str);
1881 bms_free(joinrelids);
1888 if (bms_is_member(i, joinrelids))
1890 parse_ereport(lhint->base.hint_str,
1891 ("Relation name \"%s\" is duplicate.", relname));
1892 lhint->base.state = HINT_STATE_ERROR;
1893 bms_free(joinrelids);
1897 joinrelids = bms_add_member(joinrelids, i);
1903 hint = find_join_hint(joinrelids);
1907 * Here relnames is not set, since Relids bitmap is sufficient to
1908 * control paths of this query afterwards.
1910 hint = (JoinMethodHint *) JoinMethodHintCreate(lhint->base.hint_str,
1912 hint->base.state = HINT_STATE_USED;
1913 hint->nrels = njoinrels;
1914 hint->enforce_mask = ENABLE_ALL_JOIN;
1915 hint->joinrelids = bms_copy(joinrelids);
1918 join_method_hints[njoinrels] = hint;
1920 if (njoinrels >= nbaserel)
1924 bms_free(joinrelids);
1929 for (i = 2; i <= njoinrels; i++)
1931 /* Leading で指定した組み合わせ以外の join hint を削除する */
1932 list_free(plan->join_hint_level[i]);
1934 plan->join_hint_level[i] =
1935 lappend(NIL, join_method_hints[i]);
1938 if (hint_state_enabled(lhint))
1939 set_join_config_options(DISABLE_ALL_JOIN, global->context);
1941 lhint->base.state = HINT_STATE_USED;
1946 rebuild_scan_path(PlanHint *plan, PlannerInfo *root, int level, List *initial_rels)
1950 foreach(l, initial_rels)
1952 RelOptInfo *rel = (RelOptInfo *) lfirst(l);
1954 ScanMethodHint *hint;
1957 * スキャン方式が選択できるリレーションのみ、スキャンパスを再生成
1960 if (rel->reloptkind != RELOPT_BASEREL || rel->rtekind != RTE_RELATION)
1963 rte = root->simple_rte_array[rel->relid];
1965 /* 外部表はスキャン方式が選択できない。 */
1966 if (rte->relkind == RELKIND_FOREIGN_TABLE)
1970 * scan method hint が指定されていなければ、初期値のGUCパラメータでscan
1973 if ((hint = find_scan_hint(root, rel)) == NULL)
1974 set_scan_config_options(plan->init_scan_mask, plan->context);
1977 set_scan_config_options(hint->enforce_mask, plan->context);
1978 hint->base.state = HINT_STATE_USED;
1981 list_free_deep(rel->pathlist);
1982 rel->pathlist = NIL;
1985 /* It's an "append relation", process accordingly */
1986 set_append_rel_pathlist(root, rel, rel->relid, rte);
1990 set_plain_rel_pathlist(root, rel, rte);
1995 * Restore the GUC variables we set above.
1997 set_scan_config_options(plan->init_scan_mask, plan->context);
2001 * make_join_rel() をラップする関数
2003 * ヒントにしたがって、enabele_* パラメータを変更した上で、make_join_rel()を
2007 make_join_rel_wrapper(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
2010 JoinMethodHint *hint;
2014 joinrelids = bms_union(rel1->relids, rel2->relids);
2015 hint = find_join_hint(joinrelids);
2016 bms_free(joinrelids);
2019 return pg_hint_plan_make_join_rel(root, rel1, rel2);
2021 save_nestlevel = NewGUCNestLevel();
2023 set_join_config_options(hint->enforce_mask, global->context);
2025 rel = pg_hint_plan_make_join_rel(root, rel1, rel2);
2026 hint->base.state = HINT_STATE_USED;
2029 * Restore the GUC variables we set above.
2031 AtEOXact_GUC(true, save_nestlevel);
2037 get_num_baserels(List *initial_rels)
2042 foreach(l, initial_rels)
2044 RelOptInfo *rel = (RelOptInfo *) lfirst(l);
2046 if (rel->reloptkind == RELOPT_BASEREL)
2048 else if (rel->reloptkind ==RELOPT_JOINREL)
2049 nbaserel+= bms_num_members(rel->relids);
2052 /* other values not expected here */
2053 elog(ERROR, "unrecognized reloptkind type: %d", rel->reloptkind);
2061 pg_hint_plan_join_search(PlannerInfo *root, int levels_needed, List *initial_rels)
2063 JoinMethodHint **join_method_hints;
2069 * pg_hint_planが無効、または有効なヒントが1つも指定されなかった場合は、標準
2074 if (prev_join_search)
2075 return (*prev_join_search) (root, levels_needed, initial_rels);
2076 else if (enable_geqo && levels_needed >= geqo_threshold)
2077 return geqo(root, levels_needed, initial_rels);
2079 return standard_join_search(root, levels_needed, initial_rels);
2082 /* We apply scan method hint rebuild scan path. */
2083 rebuild_scan_path(global, root, levels_needed, initial_rels);
2086 * GEQOを使用する条件を満たした場合は、GEQOを用いた結合方式の検索を行う。
2087 * このとき、スキャン方式のヒントとSetヒントのみが有効になり、結合方式や結合
2088 * 順序はヒント句は無効になりGEQOのアルゴリズムで決定される。
2090 if (enable_geqo && levels_needed >= geqo_threshold)
2091 return geqo(root, levels_needed, initial_rels);
2093 nbaserel = get_num_baserels(initial_rels);
2094 global->join_hint_level = palloc0(sizeof(List *) * (nbaserel + 1));
2095 join_method_hints = palloc0(sizeof(JoinMethodHint *) * (nbaserel + 1));
2097 transform_join_hints(global, root, nbaserel, initial_rels, join_method_hints);
2099 rel = pg_hint_plan_standard_join_search(root, levels_needed, initial_rels);
2101 for (i = 2; i <= nbaserel; i++)
2103 list_free(global->join_hint_level[i]);
2105 /* free Leading hint only */
2106 if (join_method_hints[i] != NULL &&
2107 join_method_hints[i]->enforce_mask == ENABLE_ALL_JOIN)
2108 JoinMethodHintDelete(join_method_hints[i]);
2110 pfree(global->join_hint_level);
2111 pfree(join_method_hints);
2113 if (global->nleading_hints > 0 &&
2114 hint_state_enabled(global->leading_hints[global->nleading_hints - 1]))
2115 set_join_config_options(global->init_join_mask, global->context);
2122 * Build access paths for a base relation
2125 set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
2126 Index rti, RangeTblEntry *rte)
2130 /* It's an "append relation", process accordingly */
2131 set_append_rel_pathlist(root, rel, rti, rte);
2135 if (rel->rtekind == RTE_RELATION)
2137 if (rte->relkind == RELKIND_RELATION)
2139 /* Plain relation */
2140 set_plain_rel_pathlist(root, rel, rte);
2143 elog(ERROR, "unexpected relkind: %c", rte->relkind);
2146 elog(ERROR, "unexpected rtekind: %d", (int) rel->rtekind);
2150 #define standard_join_search pg_hint_plan_standard_join_search
2151 #define join_search_one_level pg_hint_plan_join_search_one_level
2152 #define make_join_rel make_join_rel_wrapper
2155 #undef make_join_rel
2156 #define make_join_rel pg_hint_plan_make_join_rel
2157 #define add_paths_to_joinrel(root, joinrel, outerrel, innerrel, jointype, sjinfo, restrictlist) \
2159 ScanMethodHint *hint = NULL; \
2160 if ((hint = find_scan_hint((root), (innerrel))) != NULL) \
2162 set_scan_config_options(hint->enforce_mask, global->context); \
2163 hint->base.state = HINT_STATE_USED; \
2165 add_paths_to_joinrel((root), (joinrel), (outerrel), (innerrel), (jointype), (sjinfo), (restrictlist)); \
2167 set_scan_config_options(global->init_scan_mask, global->context); \
2169 #include "make_join_rel.c"