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/utility.h"
26 #include "utils/lsyscache.h"
27 #include "utils/memutils.h"
29 #ifdef PG_MODULE_MAGIC
33 #if PG_VERSION_NUM < 90100
34 #error unsupported PostgreSQL version
37 #define BLOCK_COMMENT_START "/*"
38 #define BLOCK_COMMENT_END "*/"
39 #define HINT_COMMENT_KEYWORD "+"
40 #define HINT_START BLOCK_COMMENT_START HINT_COMMENT_KEYWORD
41 #define HINT_END BLOCK_COMMENT_END
44 #define HINT_SEQSCAN "SeqScan"
45 #define HINT_INDEXSCAN "IndexScan"
46 #define HINT_BITMAPSCAN "BitmapScan"
47 #define HINT_TIDSCAN "TidScan"
48 #define HINT_NOSEQSCAN "NoSeqScan"
49 #define HINT_NOINDEXSCAN "NoIndexScan"
50 #define HINT_NOBITMAPSCAN "NoBitmapScan"
51 #define HINT_NOTIDSCAN "NoTidScan"
52 #if PG_VERSION_NUM >= 90200
53 #define HINT_INDEXONLYSCAN "IndexonlyScan"
54 #define HINT_NOINDEXONLYSCAN "NoIndexonlyScan"
56 #define HINT_NESTLOOP "NestLoop"
57 #define HINT_MERGEJOIN "MergeJoin"
58 #define HINT_HASHJOIN "HashJoin"
59 #define HINT_NONESTLOOP "NoNestLoop"
60 #define HINT_NOMERGEJOIN "NoMergeJoin"
61 #define HINT_NOHASHJOIN "NoHashJoin"
62 #define HINT_LEADING "Leading"
63 #define HINT_SET "Set"
66 #define HINT_ARRAY_DEFAULT_INITSIZE 8
68 #define parse_ereport(str, detail) \
69 ereport(pg_hint_plan_parse_messages, \
70 (errmsg("hint syntax error at or near \"%s\"", (str)), \
73 #define skip_space(str) \
74 while (isspace(*str)) \
79 ENABLE_SEQSCAN = 0x01,
80 ENABLE_INDEXSCAN = 0x02,
81 ENABLE_BITMAPSCAN = 0x04,
82 ENABLE_TIDSCAN = 0x08,
83 #if PG_VERSION_NUM >= 90200
84 ENABLE_INDEXONLYSCAN = 0x10
90 ENABLE_NESTLOOP = 0x01,
91 ENABLE_MERGEJOIN = 0x02,
92 ENABLE_HASHJOIN = 0x04
95 #define ENABLE_ALL_SCAN (ENABLE_SEQSCAN | ENABLE_INDEXSCAN | ENABLE_BITMAPSCAN \
97 #if PG_VERSION_NUM >= 90200
98 #define ENABLE_ALL_SCAN (ENABLE_SEQSCAN | ENABLE_INDEXSCAN | ENABLE_BITMAPSCAN \
99 | ENABLE_TIDSCAN | ENABLE_INDEXONLYSCAN)
101 #define ENABLE_ALL_JOIN (ENABLE_NESTLOOP | ENABLE_MERGEJOIN | ENABLE_HASHJOIN)
102 #define DISABLE_ALL_SCAN 0
103 #define DISABLE_ALL_JOIN 0
105 typedef struct Hint Hint;
106 typedef struct PlanHint PlanHint;
108 typedef Hint *(*HintCreateFunction) (const char *hint_str,
109 const char *keyword);
110 typedef void (*HintDeleteFunction) (Hint *hint);
111 typedef void (*HintDumpFunction) (Hint *hint, StringInfo buf);
112 typedef int (*HintCmpFunction) (const Hint *a, const Hint *b);
113 typedef const char *(*HintParseFunction) (Hint *hint, PlanHint *plan,
114 Query *parse, const char *str);
117 #define NUM_HINT_TYPE 4
118 typedef enum HintType
120 HINT_TYPE_SCAN_METHOD,
121 HINT_TYPE_JOIN_METHOD,
127 typedef enum HintStatus
129 HINT_STATE_NOTUSED = 0, /* specified relation not used in query */
130 HINT_STATE_USED, /* hint is used */
131 HINT_STATE_DUPLICATION, /* specified hint duplication */
132 HINT_STATE_ERROR /* execute error (parse error does not include
136 #define hint_state_enabled(hint) ((hint)->base.state == HINT_STATE_NOTUSED || \
137 (hint)->base.state == HINT_STATE_USED)
139 /* common data for all hints. */
142 const char *hint_str; /* must not do pfree */
143 const char *keyword; /* must not do pfree */
146 HintDeleteFunction delete_func;
147 HintDumpFunction dump_func;
148 HintCmpFunction cmp_func;
149 HintParseFunction parser_func;
152 /* scan method hints */
153 typedef struct ScanMethodHint
158 unsigned char enforce_mask;
161 /* join method hints */
162 typedef struct JoinMethodHint
167 unsigned char enforce_mask;
171 /* join order hints */
172 typedef struct LeadingHint
175 List *relations; /* relation names specified in Leading hint */
178 /* change a run-time parameter hints */
179 typedef struct SetHint
182 char *name; /* name of variable */
187 * Describes a context of hint processing.
191 char *hint_str; /* original hint string */
194 int nall_hints; /* # of valid all hints */
195 int max_all_hints; /* # of slots for all hints */
196 Hint **all_hints; /* parsed all hints */
198 /* # of each hints */
199 int num_hints[NUM_HINT_TYPE];
201 /* for scan method hints */
202 ScanMethodHint **scan_hints; /* parsed scan hints */
203 int init_scan_mask; /* initial value scan parameter */
204 Index parent_relid; /* inherit parent table relid */
206 /* for join method hints */
207 JoinMethodHint **join_hints; /* parsed join hints */
208 int init_join_mask; /* initial value join parameter */
209 List **join_hint_level;
211 /* for Leading hints */
212 LeadingHint **leading_hints; /* parsed Leading hints */
215 SetHint **set_hints; /* parsed Set hints */
216 GucContext context; /* which GUC parameters can we set? */
220 * Describes a hint parser module which is bound with particular hint keyword.
222 typedef struct HintParser
225 HintCreateFunction create_func;
228 /* Module callbacks */
232 void pg_hint_plan_ProcessUtility(Node *parsetree, const char *queryString,
233 ParamListInfo params, bool isTopLevel,
234 DestReceiver *dest, char *completionTag);
235 static PlannedStmt *pg_hint_plan_planner(Query *parse, int cursorOptions,
236 ParamListInfo boundParams);
237 static void pg_hint_plan_get_relation_info(PlannerInfo *root,
238 Oid relationObjectId,
239 bool inhparent, RelOptInfo *rel);
240 static RelOptInfo *pg_hint_plan_join_search(PlannerInfo *root,
244 static Hint *ScanMethodHintCreate(const char *hint_str, const char *keyword);
245 static void ScanMethodHintDelete(ScanMethodHint *hint);
246 static void ScanMethodHintDump(ScanMethodHint *hint, StringInfo buf);
247 static int ScanMethodHintCmp(const ScanMethodHint *a, const ScanMethodHint *b);
248 static const char *ScanMethodHintParse(ScanMethodHint *hint, PlanHint *plan,
249 Query *parse, const char *str);
250 static Hint *JoinMethodHintCreate(const char *hint_str, const char *keyword);
251 static void JoinMethodHintDelete(JoinMethodHint *hint);
252 static void JoinMethodHintDump(JoinMethodHint *hint, StringInfo buf);
253 static int JoinMethodHintCmp(const JoinMethodHint *a, const JoinMethodHint *b);
254 static const char *JoinMethodHintParse(JoinMethodHint *hint, PlanHint *plan,
255 Query *parse, const char *str);
256 static Hint *LeadingHintCreate(const char *hint_str, const char *keyword);
257 static void LeadingHintDelete(LeadingHint *hint);
258 static void LeadingHintDump(LeadingHint *hint, StringInfo buf);
259 static int LeadingHintCmp(const LeadingHint *a, const LeadingHint *b);
260 static const char *LeadingHintParse(LeadingHint *hint, PlanHint *plan,
261 Query *parse, const char *str);
262 static Hint *SetHintCreate(const char *hint_str, const char *keyword);
263 static void SetHintDelete(SetHint *hint);
264 static void SetHintDump(SetHint *hint, StringInfo buf);
265 static int SetHintCmp(const SetHint *a, const SetHint *b);
266 static const char *SetHintParse(SetHint *hint, PlanHint *plan, Query *parse,
269 RelOptInfo *pg_hint_plan_standard_join_search(PlannerInfo *root,
272 void pg_hint_plan_join_search_one_level(PlannerInfo *root, int level);
273 static void make_rels_by_clause_joins(PlannerInfo *root, RelOptInfo *old_rel,
274 ListCell *other_rels);
275 static void make_rels_by_clauseless_joins(PlannerInfo *root,
277 ListCell *other_rels);
278 static bool has_join_restriction(PlannerInfo *root, RelOptInfo *rel);
279 static void set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
280 Index rti, RangeTblEntry *rte);
281 static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
283 static void set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
284 Index rti, RangeTblEntry *rte);
285 static List *accumulate_append_subpath(List *subpaths, Path *path);
286 static void set_dummy_rel_pathlist(RelOptInfo *rel);
287 RelOptInfo *pg_hint_plan_make_join_rel(PlannerInfo *root, RelOptInfo *rel1,
292 static bool pg_hint_plan_enable = true;
293 static bool pg_hint_plan_debug_print = false;
294 static int pg_hint_plan_parse_messages = INFO;
296 static const struct config_enum_entry parse_messages_level_options[] = {
297 {"debug", DEBUG2, true},
298 {"debug5", DEBUG5, false},
299 {"debug4", DEBUG4, false},
300 {"debug3", DEBUG3, false},
301 {"debug2", DEBUG2, false},
302 {"debug1", DEBUG1, false},
304 {"info", INFO, false},
305 {"notice", NOTICE, false},
306 {"warning", WARNING, false},
307 {"error", ERROR, false},
309 * {"fatal", FATAL, true},
310 * {"panic", PANIC, true},
315 /* Saved hook values in case of unload */
316 static ProcessUtility_hook_type prev_ProcessUtility = NULL;
317 static planner_hook_type prev_planner = NULL;
318 static get_relation_info_hook_type prev_get_relation_info = NULL;
319 static join_search_hook_type prev_join_search = NULL;
321 /* フック関数をまたがって使用する情報を管理する */
322 static PlanHint *global = NULL;
325 * EXECUTEコマンド実行時に、ステートメント名を格納する。
326 * その他のコマンドの場合は、NULLに設定する。
328 static char *stmt_name = NULL;
330 static const HintParser parsers[] = {
331 {HINT_SEQSCAN, ScanMethodHintCreate},
332 {HINT_INDEXSCAN, ScanMethodHintCreate},
333 {HINT_BITMAPSCAN, ScanMethodHintCreate},
334 {HINT_TIDSCAN, ScanMethodHintCreate},
335 {HINT_NOSEQSCAN, ScanMethodHintCreate},
336 {HINT_NOINDEXSCAN, ScanMethodHintCreate},
337 {HINT_NOBITMAPSCAN, ScanMethodHintCreate},
338 {HINT_NOTIDSCAN, ScanMethodHintCreate},
339 #if PG_VERSION_NUM >= 90200
340 {HINT_INDEXONLYSCAN, ScanMethodHintCreate},
341 {HINT_NOINDEXONLYSCAN, ScanMethodHintCreate},
343 {HINT_NESTLOOP, JoinMethodHintCreate},
344 {HINT_MERGEJOIN, JoinMethodHintCreate},
345 {HINT_HASHJOIN, JoinMethodHintCreate},
346 {HINT_NONESTLOOP, JoinMethodHintCreate},
347 {HINT_NOMERGEJOIN, JoinMethodHintCreate},
348 {HINT_NOHASHJOIN, JoinMethodHintCreate},
349 {HINT_LEADING, LeadingHintCreate},
350 {HINT_SET, SetHintCreate},
355 * Module load callbacks
360 /* Define custom GUC variables. */
361 DefineCustomBoolVariable("pg_hint_plan.enable",
362 "Instructions or hints to the planner using block comments.",
364 &pg_hint_plan_enable,
372 DefineCustomBoolVariable("pg_hint_plan.debug_print",
373 "Logs each query's parse results of the hint.",
375 &pg_hint_plan_debug_print,
383 DefineCustomEnumVariable("pg_hint_plan.parse_messages",
384 "Messege level of the parse error.",
386 &pg_hint_plan_parse_messages,
388 parse_messages_level_options,
396 prev_ProcessUtility = ProcessUtility_hook;
397 ProcessUtility_hook = pg_hint_plan_ProcessUtility;
398 prev_planner = planner_hook;
399 planner_hook = pg_hint_plan_planner;
400 prev_get_relation_info = get_relation_info_hook;
401 get_relation_info_hook = pg_hint_plan_get_relation_info;
402 prev_join_search = join_search_hook;
403 join_search_hook = pg_hint_plan_join_search;
407 * Module unload callback
413 /* Uninstall hooks. */
414 ProcessUtility_hook = prev_ProcessUtility;
415 planner_hook = prev_planner;
416 get_relation_info_hook = prev_get_relation_info;
417 join_search_hook = prev_join_search;
421 ProcessUtility_hook_error_callback(void *arg)
427 pg_hint_plan_ProcessUtility(Node *parsetree, const char *queryString,
428 ParamListInfo params, bool isTopLevel,
429 DestReceiver *dest, char *completionTag)
432 ErrorContextCallback errcontext;
434 if (!pg_hint_plan_enable)
436 if (prev_ProcessUtility)
437 (*prev_ProcessUtility) (parsetree, queryString, params,
438 isTopLevel, dest, completionTag);
440 standard_ProcessUtility(parsetree, queryString, params,
441 isTopLevel, dest, completionTag);
447 if (IsA(node, ExplainStmt))
450 * EXPLAIN対象のクエリのパースツリーを取得する
455 stmt = (ExplainStmt *) node;
457 Assert(IsA(stmt->query, Query));
458 query = (Query *) stmt->query;
460 if (query->commandType == CMD_UTILITY && query->utilityStmt != NULL)
461 node = query->utilityStmt;
465 * EXECUTEコマンドならば、PREPARE時に指定されたクエリ文字列を取得し、ヒント
468 if (IsA(node, ExecuteStmt))
472 /* Set up callback to statement name reset. */
473 errcontext.callback = ProcessUtility_hook_error_callback;
474 errcontext.arg = NULL;
475 errcontext.previous = error_context_stack;
476 error_context_stack = &errcontext;
478 stmt = (ExecuteStmt *) node;
479 stmt_name = stmt->name;
482 if (prev_ProcessUtility)
483 (*prev_ProcessUtility) (parsetree, queryString, params,
484 isTopLevel, dest, completionTag);
486 standard_ProcessUtility(parsetree, queryString, params,
487 isTopLevel, dest, completionTag);
493 /* Remove error callback. */
494 error_context_stack = errcontext.previous;
499 ScanMethodHintCreate(const char *hint_str, const char *keyword)
501 ScanMethodHint *hint;
503 hint = palloc(sizeof(ScanMethodHint));
504 hint->base.hint_str = hint_str;
505 hint->base.keyword = keyword;
506 hint->base.type = HINT_TYPE_SCAN_METHOD;
507 hint->base.state = HINT_STATE_NOTUSED;
508 hint->base.delete_func = (HintDeleteFunction) ScanMethodHintDelete;
509 hint->base.dump_func = (HintDumpFunction) ScanMethodHintDump;
510 hint->base.cmp_func = (HintCmpFunction) ScanMethodHintCmp;
511 hint->base.parser_func = (HintParseFunction) ScanMethodHintParse;
512 hint->relname = NULL;
513 hint->indexnames = NIL;
514 hint->enforce_mask = 0;
516 return (Hint *) hint;
520 ScanMethodHintDelete(ScanMethodHint *hint)
526 pfree(hint->relname);
527 list_free_deep(hint->indexnames);
532 dump_quote_value(StringInfo buf, const char *value)
534 bool need_quote = false;
537 for (str = value; *str != '\0'; str++)
539 if (isspace(*str) || *str == ')' || *str == '"')
542 appendStringInfoCharMacro(buf, '"');
547 for (str = value; *str != '\0'; str++)
550 appendStringInfoCharMacro(buf, '"');
552 appendStringInfoCharMacro(buf, *str);
556 appendStringInfoCharMacro(buf, '"');
560 ScanMethodHintDump(ScanMethodHint *hint, StringInfo buf)
564 appendStringInfo(buf, "%s(", hint->base.keyword);
565 dump_quote_value(buf, hint->relname);
566 foreach(l, hint->indexnames)
568 appendStringInfoCharMacro(buf, ' ');
569 dump_quote_value(buf, (char *) lfirst(l));
571 appendStringInfoString(buf, ")\n");
575 JoinMethodHintCreate(const char *hint_str, const char *keyword)
577 JoinMethodHint *hint;
579 hint = palloc(sizeof(JoinMethodHint));
580 hint->base.hint_str = hint_str;
581 hint->base.keyword = keyword;
582 hint->base.type = HINT_TYPE_JOIN_METHOD;
583 hint->base.state = HINT_STATE_NOTUSED;
584 hint->base.delete_func = (HintDeleteFunction) JoinMethodHintDelete;
585 hint->base.dump_func = (HintDumpFunction) JoinMethodHintDump;
586 hint->base.cmp_func = (HintCmpFunction) JoinMethodHintCmp;
587 hint->base.parser_func = (HintParseFunction) JoinMethodHintParse;
589 hint->relnames = NULL;
590 hint->enforce_mask = 0;
591 hint->joinrelids = NULL;
593 return (Hint *) hint;
597 JoinMethodHintDelete(JoinMethodHint *hint)
606 for (i = 0; i < hint->nrels; i++)
607 pfree(hint->relnames[i]);
608 pfree(hint->relnames);
610 bms_free(hint->joinrelids);
615 JoinMethodHintDump(JoinMethodHint *hint, StringInfo buf)
619 appendStringInfo(buf, "%s(", hint->base.keyword);
620 dump_quote_value(buf, hint->relnames[0]);
621 for (i = 1; i < hint->nrels; i++)
623 appendStringInfoCharMacro(buf, ' ');
624 dump_quote_value(buf, hint->relnames[i]);
626 appendStringInfoString(buf, ")\n");
631 LeadingHintCreate(const char *hint_str, const char *keyword)
635 hint = palloc(sizeof(LeadingHint));
636 hint->base.hint_str = hint_str;
637 hint->base.keyword = keyword;
638 hint->base.type = HINT_TYPE_LEADING;
639 hint->base.state = HINT_STATE_NOTUSED;
640 hint->base.delete_func = (HintDeleteFunction)LeadingHintDelete;
641 hint->base.dump_func = (HintDumpFunction) LeadingHintDump;
642 hint->base.cmp_func = (HintCmpFunction) LeadingHintCmp;
643 hint->base.parser_func = (HintParseFunction) LeadingHintParse;
644 hint->relations = NIL;
646 return (Hint *) hint;
650 LeadingHintDelete(LeadingHint *hint)
655 list_free_deep(hint->relations);
660 LeadingHintDump(LeadingHint *hint, StringInfo buf)
665 appendStringInfo(buf, "%s(", HINT_LEADING);
667 foreach(l, hint->relations)
672 appendStringInfoCharMacro(buf, ' ');
674 dump_quote_value(buf, (char *) lfirst(l));
677 appendStringInfoString(buf, ")\n");
681 SetHintCreate(const char *hint_str, const char *keyword)
685 hint = palloc(sizeof(SetHint));
686 hint->base.hint_str = hint_str;
687 hint->base.keyword = keyword;
688 hint->base.type = HINT_TYPE_SET;
689 hint->base.state = HINT_STATE_NOTUSED;
690 hint->base.delete_func = (HintDeleteFunction) SetHintDelete;
691 hint->base.dump_func = (HintDumpFunction) SetHintDump;
692 hint->base.cmp_func = (HintCmpFunction) SetHintCmp;
693 hint->base.parser_func = (HintParseFunction) SetHintParse;
697 return (Hint *) hint;
701 SetHintDelete(SetHint *hint)
714 SetHintDump(SetHint *hint, StringInfo buf)
716 appendStringInfo(buf, "%s(", HINT_SET);
717 dump_quote_value(buf, hint->name);
718 appendStringInfoCharMacro(buf, ' ');
719 dump_quote_value(buf, hint->value);
720 appendStringInfo(buf, ")\n");
728 hint = palloc(sizeof(PlanHint));
729 hint->hint_str = NULL;
730 hint->nall_hints = 0;
731 hint->max_all_hints = 0;
732 hint->all_hints = NULL;
733 memset(hint->num_hints, 0, sizeof(hint->num_hints));
734 hint->scan_hints = NULL;
735 hint->init_scan_mask = 0;
736 hint->parent_relid = 0;
737 hint->join_hints = NULL;
738 hint->init_join_mask = 0;
739 hint->join_hint_level = NULL;
740 hint->leading_hints = NULL;
741 hint->context = superuser() ? PGC_SUSET : PGC_USERSET;
742 hint->set_hints = NULL;
748 PlanHintDelete(PlanHint *hint)
756 pfree(hint->hint_str);
758 for (i = 0; i < hint->num_hints[HINT_TYPE_SCAN_METHOD]; i++)
759 hint->all_hints[i]->delete_func(hint->all_hints[i]);
761 pfree(hint->all_hints);
765 all_hint_dump(PlanHint *hint, StringInfo buf, const char *title,
770 appendStringInfo(buf, "%s:\n", title);
771 for (i = 0; i < hint->nall_hints; i++)
773 if (hint->all_hints[i]->state != state)
776 hint->all_hints[i]->dump_func(hint->all_hints[i], buf);
781 PlanHintDump(PlanHint *hint)
787 elog(LOG, "pg_hint_plan:\nno hint");
791 initStringInfo(&buf);
793 appendStringInfoString(&buf, "pg_hint_plan:\n");
794 all_hint_dump(hint, &buf, "used hint", HINT_STATE_USED);
795 all_hint_dump(hint, &buf, "not used hint", HINT_STATE_NOTUSED);
796 all_hint_dump(hint, &buf, "duplication hint", HINT_STATE_DUPLICATION);
797 all_hint_dump(hint, &buf, "error hint", HINT_STATE_ERROR);
799 elog(LOG, "%s", buf.data);
805 RelnameCmp(const void *a, const void *b)
807 const char *relnamea = *((const char **) a);
808 const char *relnameb = *((const char **) b);
810 return strcmp(relnamea, relnameb);
814 ScanMethodHintCmp(const ScanMethodHint *a, const ScanMethodHint *b)
816 return RelnameCmp(&a->relname, &b->relname);
820 JoinMethodHintCmp(const JoinMethodHint *a, const JoinMethodHint *b)
824 if (a->nrels != b->nrels)
825 return a->nrels - b->nrels;
827 for (i = 0; i < a->nrels; i++)
830 if ((result = RelnameCmp(&a->relnames[i], &b->relnames[i])) != 0)
838 LeadingHintCmp(const LeadingHint *a, const LeadingHint *b)
844 SetHintCmp(const SetHint *a, const SetHint *b)
846 return strcmp(a->name, b->name);
850 AllHintCmp(const void *a, const void *b, bool order)
852 const Hint *hinta = *((const Hint **) a);
853 const Hint *hintb = *((const Hint **) b);
856 if (hinta->type != hintb->type)
857 return hinta->type - hintb->type;
859 if ((result = hinta->cmp_func(hinta, hintb)) != 0 || !order)
863 return hinta->hint_str - hintb->hint_str;
867 AllHintCmpIsOrder(const void *a, const void *b)
869 return AllHintCmp(a, b, true);
872 #if PG_VERSION_NUM < 90200
874 set_config_option_wrapper(const char *name, const char *value,
875 GucContext context, GucSource source,
876 GucAction action, bool changeVal, int elevel)
879 MemoryContext ccxt = CurrentMemoryContext;
883 result = set_config_option(name, value, context, source,
894 ecxt = MemoryContextSwitchTo(ccxt);
895 errdata = CopyErrorData();
896 ereport(elevel, (errcode(errdata->sqlerrcode),
897 errmsg("%s", errdata->message),
898 errdata->detail ? errdetail("%s", errdata->detail) : 0,
899 errdata->hint ? errhint("%s", errdata->hint) : 0));
900 FreeErrorData(errdata);
902 MemoryContextSwitchTo(ecxt);
909 #define set_config_option(name, value, context, source, \
910 action, changeVal, elevel) \
911 set_config_option_wrapper(name, value, context, source, \
912 action, changeVal, elevel)
916 set_config_options(SetHint **options, int noptions, GucContext context)
921 save_nestlevel = NewGUCNestLevel();
923 for (i = 0; i < noptions; i++)
925 SetHint *hint = options[i];
928 if (!hint_state_enabled(hint))
931 result = set_config_option(hint->name, hint->value, context,
932 PGC_S_SESSION, GUC_ACTION_SAVE, true,
933 pg_hint_plan_parse_messages);
935 hint->base.state = HINT_STATE_USED;
937 hint->base.state = HINT_STATE_ERROR;
940 return save_nestlevel;
943 #define SET_CONFIG_OPTION(name, type_bits) \
944 set_config_option((name), \
945 (enforce_mask & (type_bits)) ? "true" : "false", \
946 context, PGC_S_SESSION, GUC_ACTION_SAVE, true, ERROR)
949 set_scan_config_options(unsigned char enforce_mask, GucContext context)
951 SET_CONFIG_OPTION("enable_seqscan", ENABLE_SEQSCAN);
952 SET_CONFIG_OPTION("enable_indexscan", ENABLE_INDEXSCAN);
953 SET_CONFIG_OPTION("enable_bitmapscan", ENABLE_BITMAPSCAN);
954 SET_CONFIG_OPTION("enable_tidscan", ENABLE_TIDSCAN);
955 #if PG_VERSION_NUM >= 90200
956 SET_CONFIG_OPTION("enable_indexonlyscan", ENABLE_INDEXSCAN);
961 set_join_config_options(unsigned char enforce_mask, GucContext context)
963 SET_CONFIG_OPTION("enable_nestloop", ENABLE_NESTLOOP);
964 SET_CONFIG_OPTION("enable_mergejoin", ENABLE_MERGEJOIN);
965 SET_CONFIG_OPTION("enable_hashjoin", ENABLE_HASHJOIN);
973 parse_keyword(const char *str, StringInfo buf)
977 while (!isspace(*str) && *str != '(' && *str != '\0')
978 appendStringInfoCharMacro(buf, *str++);
984 skip_opened_parenthesis(const char *str)
990 parse_ereport(str, ("Opened parenthesis is necessary."));
1000 skip_closed_parenthesis(const char *str)
1006 parse_ereport(str, ("Closed parenthesis is necessary."));
1016 * 二重引用符で囲まれているかもしれないトークンを読み取り word 引数に palloc
1017 * で確保したバッファに格納してそのポインタを返す。
1019 * 正常にパースできた場合は残りの文字列の先頭位置を、異常があった場合は NULL を
1023 parse_quote_value(const char *str, char **word, char *value_type)
1028 /* 先頭のスペースは読み飛ばす。 */
1031 initStringInfo(&buf);
1044 /* 二重引用符が閉じられていない場合はパース中断 */
1048 parse_ereport(str, ("Unterminated quoted %s.", value_type));
1053 * エスケープ対象のダブルクウォートをスキップする。
1054 * もしブロックコメントの開始文字列や終了文字列もオブジェクト名とし
1055 * て使用したい場合は、/ と * もエスケープ対象とすることで使用できる
1056 * が、処理対象としていない。もしテーブル名にこれらの文字が含まれる
1057 * 場合は、エイリアスを指定する必要がある。
1066 else if (isspace(*str) || *str == ')' || *str == '"' || *str == '\0')
1069 appendStringInfoCharMacro(&buf, *str++);
1075 parse_ereport(str, ("%s is necessary.", value_type));
1085 parse_hints(PlanHint *plan, Query *parse, const char *str)
1090 initStringInfo(&buf);
1091 while (*str != '\0')
1093 const HintParser *parser;
1095 /* in error message, we output the comment including the keyword. */
1096 head = (char *) str;
1098 /* parse only the keyword of the hint. */
1099 resetStringInfo(&buf);
1100 str = parse_keyword(str, &buf);
1102 for (parser = parsers; parser->keyword != NULL; parser++)
1104 char *keyword = parser->keyword;
1107 if (strcasecmp(buf.data, keyword) != 0)
1110 hint = parser->create_func(head, keyword);
1112 /* parser of each hint does parse in a parenthesis. */
1113 if ((str = skip_opened_parenthesis(str)) == NULL ||
1114 (str = hint->parser_func(hint, plan, parse, str)) == NULL ||
1115 (str = skip_closed_parenthesis(str)) == NULL)
1117 hint->delete_func(hint);
1123 * 出来上がったヒント情報を追加。スロットが足りない場合は二倍に拡張する。
1125 if (plan->nall_hints == 0)
1127 plan->max_all_hints = HINT_ARRAY_DEFAULT_INITSIZE;
1128 plan->all_hints = palloc(sizeof(Hint *) * plan->max_all_hints);
1130 else if (plan->nall_hints == plan->max_all_hints)
1132 plan->max_all_hints *= 2;
1133 plan->all_hints = repalloc(plan->all_hints,
1134 sizeof(Hint *) * plan->max_all_hints);
1137 plan->all_hints[plan->nall_hints] = hint;
1145 if (parser->keyword == NULL)
1147 parse_ereport(head, ("Keyword \"%s\" does not exist.", buf.data));
1157 * Do basic parsing of the query head comment.
1160 parse_head_comment(Query *parse)
1169 /* get client-supplied query string. */
1172 PreparedStatement *entry;
1174 entry = FetchPreparedStatement(stmt_name, true);
1175 p = entry->plansource->query_string;
1178 p = debug_query_string;
1183 /* extract query head comment. */
1184 len = strlen(HINT_START);
1186 if (strncmp(p, HINT_START, len))
1193 /* find hint end keyword. */
1194 if ((tail = strstr(p, HINT_END)) == NULL)
1196 parse_ereport(head, ("Unterminated block comment."));
1200 /* 入れ子にしたブロックコメントはサポートしない */
1201 if ((head = strstr(p, BLOCK_COMMENT_START)) != NULL && head < tail)
1202 parse_ereport(head, ("Block comments nest doesn't supported."));
1206 head = palloc(len + 1);
1207 memcpy(head, p, len);
1211 plan = PlanHintCreate();
1212 plan->hint_str = head;
1214 /* parse each hint. */
1215 parse_hints(plan, parse, p);
1217 /* When nothing specified a hint, we free PlanHint and returns NULL. */
1218 if (plan->nall_hints == 0)
1220 PlanHintDelete(plan);
1224 /* パースしたヒントを並び替える */
1225 qsort(plan->all_hints, plan->nall_hints, sizeof(Hint *), AllHintCmpIsOrder);
1228 for (i = 0; i < plan->nall_hints; i++)
1230 Hint *hint = plan->all_hints[i];
1232 plan->num_hints[hint->type]++;
1234 if (i + 1 >= plan->nall_hints)
1237 if (AllHintCmp(plan->all_hints + i, plan->all_hints + i + 1, false) ==
1240 const char *HintTypeName[] = {"scan method", "join method",
1243 parse_ereport(plan->all_hints[i]->hint_str,
1244 ("Conflict %s hint.", HintTypeName[hint->type]));
1245 plan->all_hints[i]->state = HINT_STATE_DUPLICATION;
1249 plan->scan_hints = (ScanMethodHint **) plan->all_hints;
1250 plan->join_hints = (JoinMethodHint **) plan->all_hints +
1251 plan->num_hints[HINT_TYPE_SCAN_METHOD];
1252 plan->leading_hints = (LeadingHint **) plan->all_hints +
1253 plan->num_hints[HINT_TYPE_SCAN_METHOD] +
1254 plan->num_hints[HINT_TYPE_JOIN_METHOD];
1255 plan->set_hints = (SetHint **) plan->all_hints +
1256 plan->num_hints[HINT_TYPE_SCAN_METHOD] +
1257 plan->num_hints[HINT_TYPE_JOIN_METHOD] +
1258 plan->num_hints[HINT_TYPE_LEADING];
1264 * スキャン方式ヒントのカッコ内をパースする
1267 ScanMethodHintParse(ScanMethodHint *hint, PlanHint *plan, Query *parse,
1270 const char *keyword = hint->base.keyword;
1273 * スキャン方式のヒントでリレーション名が読み取れない場合はヒント無効
1275 if ((str = parse_quote_value(str, &hint->relname, "ralation name")) == NULL)
1281 * インデックスリストを受け付けるヒントであれば、インデックス参照をパース
1284 if (strcmp(keyword, HINT_INDEXSCAN) == 0 ||
1285 #if PG_VERSION_NUM >= 90200
1286 strcmp(keyword, HINT_INDEXONLYSCAN) == 0 ||
1288 strcmp(keyword, HINT_BITMAPSCAN) == 0)
1290 while (*str != ')' && *str != '\0')
1294 str = parse_quote_value(str, &indexname, "index name");
1298 hint->indexnames = lappend(hint->indexnames, indexname);
1304 * ヒントごとに決まっている許容スキャン方式をビットマスクとして設定
1306 if (strcasecmp(keyword, HINT_SEQSCAN) == 0)
1307 hint->enforce_mask = ENABLE_SEQSCAN;
1308 else if (strcasecmp(keyword, HINT_INDEXSCAN) == 0)
1309 hint->enforce_mask = ENABLE_INDEXSCAN;
1310 else if (strcasecmp(keyword, HINT_BITMAPSCAN) == 0)
1311 hint->enforce_mask = ENABLE_BITMAPSCAN;
1312 else if (strcasecmp(keyword, HINT_TIDSCAN) == 0)
1313 hint->enforce_mask = ENABLE_TIDSCAN;
1314 else if (strcasecmp(keyword, HINT_NOSEQSCAN) == 0)
1315 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_SEQSCAN;
1316 else if (strcasecmp(keyword, HINT_NOINDEXSCAN) == 0)
1317 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_INDEXSCAN;
1318 else if (strcasecmp(keyword, HINT_NOBITMAPSCAN) == 0)
1319 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_BITMAPSCAN;
1320 else if (strcasecmp(keyword, HINT_NOTIDSCAN) == 0)
1321 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_TIDSCAN;
1324 parse_ereport(str, ("Unrecognized hint keyword \"%s\".", keyword));
1332 JoinMethodHintParse(JoinMethodHint *hint, PlanHint *plan, Query *parse,
1336 const char *keyword = hint->base.keyword;
1340 hint->relnames = palloc(sizeof(char *));
1342 while ((str = parse_quote_value(str, &relname, "table name")) != NULL)
1345 hint->relnames = repalloc(hint->relnames, sizeof(char *) * hint->nrels);
1346 hint->relnames[hint->nrels - 1] = relname;
1356 /* Join 対象のテーブルは最低でも2つ指定する必要がある */
1357 if (hint->nrels < 2)
1359 parse_ereport(str, ("Specified relation more than two."));
1360 hint->base.state = HINT_STATE_ERROR;
1364 qsort(hint->relnames, hint->nrels, sizeof(char *), RelnameCmp);
1366 if (strcasecmp(keyword, HINT_NESTLOOP) == 0)
1367 hint->enforce_mask = ENABLE_NESTLOOP;
1368 else if (strcasecmp(keyword, HINT_MERGEJOIN) == 0)
1369 hint->enforce_mask = ENABLE_MERGEJOIN;
1370 else if (strcasecmp(keyword, HINT_HASHJOIN) == 0)
1371 hint->enforce_mask = ENABLE_HASHJOIN;
1372 else if (strcasecmp(keyword, HINT_NONESTLOOP) == 0)
1373 hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_NESTLOOP;
1374 else if (strcasecmp(keyword, HINT_NOMERGEJOIN) == 0)
1375 hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_MERGEJOIN;
1376 else if (strcasecmp(keyword, HINT_NOHASHJOIN) == 0)
1377 hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_HASHJOIN;
1380 parse_ereport(str, ("Unrecognized hint keyword \"%s\".", keyword));
1388 LeadingHintParse(LeadingHint *hint, PlanHint *plan, Query *parse,
1397 if ((str = parse_quote_value(str, &relname, "relation name")) == NULL)
1400 hint->relations = lappend(hint->relations, relname);
1405 /* テーブル指定が2つ未満の場合は、Leading ヒントはエラーとする */
1406 if (list_length(hint->relations) < 2)
1408 parse_ereport(hint->base.hint_str,
1409 ("In %s hint, specified relation name 2 or more.",
1411 hint->base.state = HINT_STATE_ERROR;
1418 SetHintParse(SetHint *hint, PlanHint *plan, Query *parse, const char *str)
1420 if ((str = parse_quote_value(str, &hint->name, "parameter name")) == NULL ||
1421 (str = parse_quote_value(str, &hint->value, "parameter value")) == NULL)
1427 static PlannedStmt *
1428 pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
1431 PlannedStmt *result;
1434 * hintが指定されない、または空のhintを指定された場合は通常のparser処理をお
1436 * 他のフック関数で実行されるhint処理をスキップするために、global 変数をNULL
1439 if (!pg_hint_plan_enable || (global = parse_head_comment(parse)) == NULL)
1444 return (*prev_planner) (parse, cursorOptions, boundParams);
1446 return standard_planner(parse, cursorOptions, boundParams);
1449 /* Set hint で指定されたGUCパラメータを設定する */
1450 save_nestlevel = set_config_options(global->set_hints,
1451 global->num_hints[HINT_TYPE_SET],
1455 global->init_scan_mask |= ENABLE_SEQSCAN;
1456 if (enable_indexscan)
1457 global->init_scan_mask |= ENABLE_INDEXSCAN;
1458 if (enable_bitmapscan)
1459 global->init_scan_mask |= ENABLE_BITMAPSCAN;
1461 global->init_scan_mask |= ENABLE_TIDSCAN;
1462 #if PG_VERSION_NUM >= 90200
1463 if (enable_indexonlyscan)
1464 global->init_scan_mask |= ENABLE_INDEXONLYSCAN;
1466 if (enable_nestloop)
1467 global->init_join_mask |= ENABLE_NESTLOOP;
1468 if (enable_mergejoin)
1469 global->init_join_mask |= ENABLE_MERGEJOIN;
1470 if (enable_hashjoin)
1471 global->init_join_mask |= ENABLE_HASHJOIN;
1474 result = (*prev_planner) (parse, cursorOptions, boundParams);
1476 result = standard_planner(parse, cursorOptions, boundParams);
1479 * Restore the GUC variables we set above.
1481 AtEOXact_GUC(true, save_nestlevel);
1484 * Print hint if debugging.
1486 if (pg_hint_plan_debug_print)
1487 PlanHintDump(global);
1489 PlanHintDelete(global);
1496 * aliasnameと一致するSCANヒントを探す
1498 static ScanMethodHint *
1499 find_scan_hint(PlannerInfo *root, RelOptInfo *rel)
1505 * RELOPT_BASEREL でなければ、scan method ヒントが適用しない。
1506 * 子テーブルの場合はRELOPT_OTHER_MEMBER_RELとなるが、サポート対象外とする。
1507 * また、通常のリレーション以外は、スキャン方式を選択できない。
1509 if (rel->reloptkind != RELOPT_BASEREL || rel->rtekind != RTE_RELATION)
1512 rte = root->simple_rte_array[rel->relid];
1514 /* 外部表はスキャン方式が選択できない。 */
1515 if (rte->relkind == RELKIND_FOREIGN_TABLE)
1519 * スキャン方式のヒントのリストから、検索対象のリレーションと名称が一致する
1522 for (i = 0; i < global->num_hints[HINT_TYPE_SCAN_METHOD]; i++)
1524 ScanMethodHint *hint = global->scan_hints[i];
1526 /* すでに無効となっているヒントは検索対象にしない。 */
1527 if (!hint_state_enabled(hint))
1530 if (RelnameCmp(&rte->eref->aliasname, &hint->relname) == 0)
1538 pg_hint_plan_get_relation_info(PlannerInfo *root, Oid relationObjectId,
1539 bool inhparent, RelOptInfo *rel)
1541 ScanMethodHint *hint;
1546 if (prev_get_relation_info)
1547 (*prev_get_relation_info) (root, relationObjectId, inhparent, rel);
1549 /* 有効なヒントが指定されなかった場合は処理をスキップする。 */
1555 /* cache does relids of parent table */
1556 global->parent_relid = rel->relid;
1558 else if (global->parent_relid != 0)
1560 /* If this rel is an appendrel child, */
1563 /* append_rel_list contains all append rels; ignore others */
1564 foreach(l, root->append_rel_list)
1566 AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
1568 /* This rel is child table. */
1569 if (appinfo->parent_relid == global->parent_relid)
1573 /* This rel is not inherit table. */
1574 global->parent_relid = 0;
1577 /* scan hint が指定されない場合は、GUCパラメータをリセットする。 */
1578 if ((hint = find_scan_hint(root, rel)) == NULL)
1580 set_scan_config_options(global->init_scan_mask, global->context);
1583 set_scan_config_options(hint->enforce_mask, global->context);
1584 hint->base.state = HINT_STATE_USED;
1586 /* インデックスを全て削除し、スキャンに使えなくする */
1587 if (hint->enforce_mask == ENABLE_SEQSCAN ||
1588 hint->enforce_mask == ENABLE_TIDSCAN)
1590 list_free_deep(rel->indexlist);
1591 rel->indexlist = NIL;
1592 hint->base.state = HINT_STATE_USED;
1597 /* 後でパスを作り直すため、ここではなにもしない */
1598 if (hint->indexnames == NULL)
1601 /* 指定されたインデックスのみをのこす */
1603 for (cell = list_head(rel->indexlist); cell; cell = next)
1605 IndexOptInfo *info = (IndexOptInfo *) lfirst(cell);
1606 char *indexname = get_rel_name(info->indexoid);
1608 bool use_index = false;
1612 foreach(l, hint->indexnames)
1614 if (RelnameCmp(&indexname, &lfirst(l)) == 0)
1622 rel->indexlist = list_delete_cell(rel->indexlist, cell, prev);
1631 * aliasnameがクエリ中に指定した別名と一致する場合は、そのインデックスを返し、一致
1633 * aliasnameがクエリ中に複数回指定された場合は、-1を返す。
1636 find_relid_aliasname(PlannerInfo *root, char *aliasname, List *initial_rels,
1642 for (i = 1; i < root->simple_rel_array_size; i++)
1646 if (root->simple_rel_array[i] == NULL)
1649 Assert(i == root->simple_rel_array[i]->relid);
1651 if (RelnameCmp(&aliasname, &root->simple_rte_array[i]->eref->aliasname)
1655 foreach(l, initial_rels)
1657 RelOptInfo *rel = (RelOptInfo *) lfirst(l);
1659 if (rel->reloptkind == RELOPT_BASEREL)
1661 if (rel->relid != i)
1666 Assert(rel->reloptkind == RELOPT_JOINREL);
1668 if (!bms_is_member(i, rel->relids))
1675 ("Relation name \"%s\" is ambiguous.",
1690 * relidビットマスクと一致するヒントを探す
1692 static JoinMethodHint *
1693 find_join_hint(Relids joinrelids)
1698 join_hint = global->join_hint_level[bms_num_members(joinrelids)];
1700 foreach(l, join_hint)
1702 JoinMethodHint *hint = (JoinMethodHint *) lfirst(l);
1704 if (bms_equal(joinrelids, hint->joinrelids))
1712 * 結合方式のヒントを使用しやすい構造に変換する。
1715 transform_join_hints(PlanHint *plan, PlannerInfo *root, int nbaserel,
1716 List *initial_rels, JoinMethodHint **join_method_hints)
1725 for (i = 0; i < plan->num_hints[HINT_TYPE_JOIN_METHOD]; i++)
1727 JoinMethodHint *hint = plan->join_hints[i];
1730 if (!hint_state_enabled(hint) || hint->nrels > nbaserel)
1733 bms_free(hint->joinrelids);
1734 hint->joinrelids = NULL;
1736 for (j = 0; j < hint->nrels; j++)
1738 char *relname = hint->relnames[j];
1740 relid = find_relid_aliasname(root, relname, initial_rels,
1741 hint->base.hint_str);
1744 hint->base.state = HINT_STATE_ERROR;
1749 if (bms_is_member(relid, hint->joinrelids))
1751 parse_ereport(hint->base.hint_str,
1752 ("Relation name \"%s\" is duplicate.", relname));
1753 hint->base.state = HINT_STATE_ERROR;
1757 hint->joinrelids = bms_add_member(hint->joinrelids, relid);
1760 if (relid <= 0 || hint->base.state == HINT_STATE_ERROR)
1763 plan->join_hint_level[hint->nrels] =
1764 lappend(plan->join_hint_level[hint->nrels], hint);
1768 * 有効なLeading ヒントが指定されている場合は、結合順にあわせてjoin method hint
1771 if (plan->num_hints[HINT_TYPE_LEADING] == 0)
1774 lhint = plan->leading_hints[plan->num_hints[HINT_TYPE_LEADING] - 1];
1775 if (!hint_state_enabled(lhint))
1778 /* Leading hint は、全ての join 方式が有効な hint として登録する */
1781 foreach(l, lhint->relations)
1783 char *relname = (char *)lfirst(l);
1784 JoinMethodHint *hint;
1787 find_relid_aliasname(root, relname, initial_rels, plan->hint_str);
1791 bms_free(joinrelids);
1798 if (bms_is_member(relid, joinrelids))
1800 parse_ereport(lhint->base.hint_str,
1801 ("Relation name \"%s\" is duplicate.", relname));
1802 lhint->base.state = HINT_STATE_ERROR;
1803 bms_free(joinrelids);
1807 joinrelids = bms_add_member(joinrelids, relid);
1813 hint = find_join_hint(joinrelids);
1817 * Here relnames is not set, since Relids bitmap is sufficient to
1818 * control paths of this query afterwards.
1820 hint = (JoinMethodHint *) JoinMethodHintCreate(lhint->base.hint_str,
1822 hint->base.state = HINT_STATE_USED;
1823 hint->nrels = njoinrels;
1824 hint->enforce_mask = ENABLE_ALL_JOIN;
1825 hint->joinrelids = bms_copy(joinrelids);
1828 join_method_hints[njoinrels] = hint;
1830 if (njoinrels >= nbaserel)
1834 bms_free(joinrelids);
1839 for (i = 2; i <= njoinrels; i++)
1841 /* Leading で指定した組み合わせ以外の join hint を削除する */
1842 list_free(plan->join_hint_level[i]);
1844 plan->join_hint_level[i] = lappend(NIL, join_method_hints[i]);
1847 if (hint_state_enabled(lhint))
1848 set_join_config_options(DISABLE_ALL_JOIN, global->context);
1850 lhint->base.state = HINT_STATE_USED;
1855 rebuild_scan_path(PlanHint *plan, PlannerInfo *root, int level,
1860 foreach(l, initial_rels)
1862 RelOptInfo *rel = (RelOptInfo *) lfirst(l);
1864 ScanMethodHint *hint;
1867 * スキャン方式が選択できるリレーションのみ、スキャンパスを再生成
1870 if (rel->reloptkind != RELOPT_BASEREL || rel->rtekind != RTE_RELATION)
1873 rte = root->simple_rte_array[rel->relid];
1875 /* 外部表はスキャン方式が選択できない。 */
1876 if (rte->relkind == RELKIND_FOREIGN_TABLE)
1880 * scan method hint が指定されていなければ、初期値のGUCパラメータでscan
1883 if ((hint = find_scan_hint(root, rel)) == NULL)
1884 set_scan_config_options(plan->init_scan_mask, plan->context);
1887 set_scan_config_options(hint->enforce_mask, plan->context);
1888 hint->base.state = HINT_STATE_USED;
1891 list_free_deep(rel->pathlist);
1892 rel->pathlist = NIL;
1895 /* It's an "append relation", process accordingly */
1896 set_append_rel_pathlist(root, rel, rel->relid, rte);
1900 set_plain_rel_pathlist(root, rel, rte);
1905 * Restore the GUC variables we set above.
1907 set_scan_config_options(plan->init_scan_mask, plan->context);
1911 * make_join_rel() をラップする関数
1913 * ヒントにしたがって、enabele_* パラメータを変更した上で、make_join_rel()を
1917 make_join_rel_wrapper(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
1920 JoinMethodHint *hint;
1924 joinrelids = bms_union(rel1->relids, rel2->relids);
1925 hint = find_join_hint(joinrelids);
1926 bms_free(joinrelids);
1929 return pg_hint_plan_make_join_rel(root, rel1, rel2);
1931 save_nestlevel = NewGUCNestLevel();
1933 set_join_config_options(hint->enforce_mask, global->context);
1935 rel = pg_hint_plan_make_join_rel(root, rel1, rel2);
1936 hint->base.state = HINT_STATE_USED;
1939 * Restore the GUC variables we set above.
1941 AtEOXact_GUC(true, save_nestlevel);
1947 get_num_baserels(List *initial_rels)
1952 foreach(l, initial_rels)
1954 RelOptInfo *rel = (RelOptInfo *) lfirst(l);
1956 if (rel->reloptkind == RELOPT_BASEREL)
1958 else if (rel->reloptkind ==RELOPT_JOINREL)
1959 nbaserel+= bms_num_members(rel->relids);
1962 /* other values not expected here */
1963 elog(ERROR, "unrecognized reloptkind type: %d", rel->reloptkind);
1971 pg_hint_plan_join_search(PlannerInfo *root, int levels_needed,
1974 JoinMethodHint **join_method_hints;
1980 * pg_hint_planが無効、または有効なヒントが1つも指定されなかった場合は、標準
1985 if (prev_join_search)
1986 return (*prev_join_search) (root, levels_needed, initial_rels);
1987 else if (enable_geqo && levels_needed >= geqo_threshold)
1988 return geqo(root, levels_needed, initial_rels);
1990 return standard_join_search(root, levels_needed, initial_rels);
1993 /* We apply scan method hint rebuild scan path. */
1994 rebuild_scan_path(global, root, levels_needed, initial_rels);
1997 * GEQOを使用する条件を満たした場合は、GEQOを用いた結合方式の検索を行う。
1998 * このとき、スキャン方式のヒントとSetヒントのみが有効になり、結合方式や結合
1999 * 順序はヒント句は無効になりGEQOのアルゴリズムで決定される。
2001 if (enable_geqo && levels_needed >= geqo_threshold)
2002 return geqo(root, levels_needed, initial_rels);
2004 nbaserel = get_num_baserels(initial_rels);
2005 global->join_hint_level = palloc0(sizeof(List *) * (nbaserel + 1));
2006 join_method_hints = palloc0(sizeof(JoinMethodHint *) * (nbaserel + 1));
2008 transform_join_hints(global, root, nbaserel, initial_rels,
2011 rel = pg_hint_plan_standard_join_search(root, levels_needed, initial_rels);
2013 for (i = 2; i <= nbaserel; i++)
2015 list_free(global->join_hint_level[i]);
2017 /* free Leading hint only */
2018 if (join_method_hints[i] != NULL &&
2019 join_method_hints[i]->enforce_mask == ENABLE_ALL_JOIN)
2020 JoinMethodHintDelete(join_method_hints[i]);
2022 pfree(global->join_hint_level);
2023 pfree(join_method_hints);
2025 if (global->num_hints[HINT_TYPE_LEADING] > 0 &&
2027 global->leading_hints[global->num_hints[HINT_TYPE_LEADING] - 1]))
2028 set_join_config_options(global->init_join_mask, global->context);
2035 * Build access paths for a base relation
2038 set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
2039 Index rti, RangeTblEntry *rte)
2043 /* It's an "append relation", process accordingly */
2044 set_append_rel_pathlist(root, rel, rti, rte);
2048 if (rel->rtekind == RTE_RELATION)
2050 if (rte->relkind == RELKIND_RELATION)
2052 /* Plain relation */
2053 set_plain_rel_pathlist(root, rel, rte);
2056 elog(ERROR, "unexpected relkind: %c", rte->relkind);
2059 elog(ERROR, "unexpected rtekind: %d", (int) rel->rtekind);
2063 #define standard_join_search pg_hint_plan_standard_join_search
2064 #define join_search_one_level pg_hint_plan_join_search_one_level
2065 #define make_join_rel make_join_rel_wrapper
2068 #undef make_join_rel
2069 #define make_join_rel pg_hint_plan_make_join_rel
2070 #define add_paths_to_joinrel(root, joinrel, outerrel, innerrel, jointype, sjinfo, restrictlist) \
2072 ScanMethodHint *hint = NULL; \
2073 if ((hint = find_scan_hint((root), (innerrel))) != NULL) \
2075 set_scan_config_options(hint->enforce_mask, global->context); \
2076 hint->base.state = HINT_STATE_USED; \
2078 add_paths_to_joinrel((root), (joinrel), (outerrel), (innerrel), (jointype), (sjinfo), (restrictlist)); \
2080 set_scan_config_options(global->init_scan_mask, global->context); \
2082 #include "make_join_rel.c"