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 "catalog/pg_index.h"
13 #include "commands/prepare.h"
14 #include "mb/pg_wchar.h"
15 #include "miscadmin.h"
16 #include "nodes/nodeFuncs.h"
17 #include "optimizer/clauses.h"
18 #include "optimizer/cost.h"
19 #include "optimizer/geqo.h"
20 #include "optimizer/joininfo.h"
21 #include "optimizer/pathnode.h"
22 #include "optimizer/paths.h"
23 #include "optimizer/plancat.h"
24 #include "optimizer/planner.h"
25 #include "optimizer/prep.h"
26 #include "optimizer/restrictinfo.h"
27 #include "parser/scansup.h"
28 #include "tcop/utility.h"
29 #include "utils/lsyscache.h"
30 #include "utils/memutils.h"
31 #include "utils/rel.h"
32 #if PG_VERSION_NUM >= 90200
33 #include "catalog/pg_class.h"
36 #ifdef PG_MODULE_MAGIC
40 #if PG_VERSION_NUM < 90100
41 #error unsupported PostgreSQL version
44 #define BLOCK_COMMENT_START "/*"
45 #define BLOCK_COMMENT_END "*/"
46 #define HINT_COMMENT_KEYWORD "+"
47 #define HINT_START BLOCK_COMMENT_START HINT_COMMENT_KEYWORD
48 #define HINT_END BLOCK_COMMENT_END
51 #define HINT_SEQSCAN "SeqScan"
52 #define HINT_INDEXSCAN "IndexScan"
53 #define HINT_BITMAPSCAN "BitmapScan"
54 #define HINT_TIDSCAN "TidScan"
55 #define HINT_NOSEQSCAN "NoSeqScan"
56 #define HINT_NOINDEXSCAN "NoIndexScan"
57 #define HINT_NOBITMAPSCAN "NoBitmapScan"
58 #define HINT_NOTIDSCAN "NoTidScan"
59 #if PG_VERSION_NUM >= 90200
60 #define HINT_INDEXONLYSCAN "IndexOnlyScan"
61 #define HINT_NOINDEXONLYSCAN "NoIndexOnlyScan"
63 #define HINT_NESTLOOP "NestLoop"
64 #define HINT_MERGEJOIN "MergeJoin"
65 #define HINT_HASHJOIN "HashJoin"
66 #define HINT_NONESTLOOP "NoNestLoop"
67 #define HINT_NOMERGEJOIN "NoMergeJoin"
68 #define HINT_NOHASHJOIN "NoHashJoin"
69 #define HINT_LEADING "Leading"
70 #define HINT_SET "Set"
72 #define HINT_ARRAY_DEFAULT_INITSIZE 8
74 #define hint_ereport(str, detail) \
75 ereport(pg_hint_plan_parse_messages, \
76 (errmsg("hint syntax error at or near \"%s\"", (str)), \
79 #define skip_space(str) \
80 while (isspace(*str)) \
85 ENABLE_SEQSCAN = 0x01,
86 ENABLE_INDEXSCAN = 0x02,
87 ENABLE_BITMAPSCAN = 0x04,
88 ENABLE_TIDSCAN = 0x08,
89 #if PG_VERSION_NUM >= 90200
90 ENABLE_INDEXONLYSCAN = 0x10
96 ENABLE_NESTLOOP = 0x01,
97 ENABLE_MERGEJOIN = 0x02,
98 ENABLE_HASHJOIN = 0x04
101 #if PG_VERSION_NUM >= 90200
102 #define ENABLE_ALL_SCAN (ENABLE_SEQSCAN | ENABLE_INDEXSCAN | \
103 ENABLE_BITMAPSCAN | ENABLE_TIDSCAN | \
104 ENABLE_INDEXONLYSCAN)
106 #define ENABLE_ALL_SCAN (ENABLE_SEQSCAN | ENABLE_INDEXSCAN | \
107 ENABLE_BITMAPSCAN | ENABLE_TIDSCAN)
109 #define ENABLE_ALL_JOIN (ENABLE_NESTLOOP | ENABLE_MERGEJOIN | ENABLE_HASHJOIN)
110 #define DISABLE_ALL_SCAN 0
111 #define DISABLE_ALL_JOIN 0
113 /* hint keyword of enum type*/
114 typedef enum HintKeyword
116 HINT_KEYWORD_SEQSCAN,
117 HINT_KEYWORD_INDEXSCAN,
118 HINT_KEYWORD_BITMAPSCAN,
119 HINT_KEYWORD_TIDSCAN,
120 HINT_KEYWORD_NOSEQSCAN,
121 HINT_KEYWORD_NOINDEXSCAN,
122 HINT_KEYWORD_NOBITMAPSCAN,
123 HINT_KEYWORD_NOTIDSCAN,
124 #if PG_VERSION_NUM >= 90200
125 HINT_KEYWORD_INDEXONLYSCAN,
126 HINT_KEYWORD_NOINDEXONLYSCAN,
128 HINT_KEYWORD_NESTLOOP,
129 HINT_KEYWORD_MERGEJOIN,
130 HINT_KEYWORD_HASHJOIN,
131 HINT_KEYWORD_NONESTLOOP,
132 HINT_KEYWORD_NOMERGEJOIN,
133 HINT_KEYWORD_NOHASHJOIN,
134 HINT_KEYWORD_LEADING,
136 HINT_KEYWORD_UNRECOGNIZED
139 typedef struct Hint Hint;
140 typedef struct HintState HintState;
142 typedef Hint *(*HintCreateFunction) (const char *hint_str,
144 HintKeyword hint_keyword);
145 typedef void (*HintDeleteFunction) (Hint *hint);
146 typedef void (*HintDescFunction) (Hint *hint, StringInfo buf);
147 typedef int (*HintCmpFunction) (const Hint *a, const Hint *b);
148 typedef const char *(*HintParseFunction) (Hint *hint, HintState *hstate,
149 Query *parse, const char *str);
152 #define NUM_HINT_TYPE 4
153 typedef enum HintType
155 HINT_TYPE_SCAN_METHOD,
156 HINT_TYPE_JOIN_METHOD,
161 static const char *HintTypeName[] = {
169 typedef enum HintStatus
171 HINT_STATE_NOTUSED = 0, /* specified relation not used in query */
172 HINT_STATE_USED, /* hint is used */
173 HINT_STATE_DUPLICATION, /* specified hint duplication */
174 HINT_STATE_ERROR /* execute error (parse error does not include
178 #define hint_state_enabled(hint) ((hint)->base.state == HINT_STATE_NOTUSED || \
179 (hint)->base.state == HINT_STATE_USED)
181 /* common data for all hints. */
184 const char *hint_str; /* must not do pfree */
185 const char *keyword; /* must not do pfree */
186 HintKeyword hint_keyword;
189 HintDeleteFunction delete_func;
190 HintDescFunction desc_func;
191 HintCmpFunction cmp_func;
192 HintParseFunction parse_func;
195 /* scan method hints */
196 typedef struct ScanMethodHint
201 unsigned char enforce_mask;
204 /* join method hints */
205 typedef struct JoinMethodHint
211 unsigned char enforce_mask;
213 Relids inner_joinrelids;
216 /* join order hints */
217 typedef struct OuterInnerRels
220 List *outer_inner_pair;
223 typedef struct LeadingHint
226 List *relations; /* relation names specified in Leading hint */
227 OuterInnerRels *outer_inner;
230 /* change a run-time parameter hints */
231 typedef struct SetHint
234 char *name; /* name of variable */
240 * Describes a context of hint processing.
244 char *hint_str; /* original hint string */
247 int nall_hints; /* # of valid all hints */
248 int max_all_hints; /* # of slots for all hints */
249 Hint **all_hints; /* parsed all hints */
251 /* # of each hints */
252 int num_hints[NUM_HINT_TYPE];
254 /* for scan method hints */
255 ScanMethodHint **scan_hints; /* parsed scan hints */
256 int init_scan_mask; /* initial value scan parameter */
257 Index parent_relid; /* inherit parent table relid */
258 Oid parent_rel_oid; /* inherit parent table relid */
259 ScanMethodHint *parent_hint; /* inherit parent table scan hint */
260 List *parent_ind_atts; /* attnums of inherit parent table's
263 /* for join method hints */
264 JoinMethodHint **join_hints; /* parsed join hints */
265 int init_join_mask; /* initial value join parameter */
266 List **join_hint_level;
268 /* for Leading hint */
269 LeadingHint **leading_hint; /* parsed Leading hints */
272 SetHint **set_hints; /* parsed Set hints */
273 GucContext context; /* which GUC parameters can we set? */
277 * Describes a hint parser module which is bound with particular hint keyword.
279 typedef struct HintParser
282 HintCreateFunction create_func;
283 HintKeyword hint_keyword;
286 /* Module callbacks */
290 static void push_hint(HintState *hstate);
291 static void pop_hint(void);
293 static void pg_hint_plan_ProcessUtility(Node *parsetree,
294 const char *queryString,
295 ParamListInfo params, bool isTopLevel,
297 char *completionTag);
298 static PlannedStmt *pg_hint_plan_planner(Query *parse, int cursorOptions,
299 ParamListInfo boundParams);
300 static void pg_hint_plan_get_relation_info(PlannerInfo *root,
301 Oid relationObjectId,
302 bool inhparent, RelOptInfo *rel);
303 static RelOptInfo *pg_hint_plan_join_search(PlannerInfo *root,
307 static Hint *ScanMethodHintCreate(const char *hint_str, const char *keyword,
308 HintKeyword hint_keyword);
309 static void ScanMethodHintDelete(ScanMethodHint *hint);
310 static void ScanMethodHintDesc(ScanMethodHint *hint, StringInfo buf);
311 static int ScanMethodHintCmp(const ScanMethodHint *a, const ScanMethodHint *b);
312 static const char *ScanMethodHintParse(ScanMethodHint *hint, HintState *hstate,
313 Query *parse, const char *str);
314 static Hint *JoinMethodHintCreate(const char *hint_str, const char *keyword,
315 HintKeyword hint_keyword);
316 static void JoinMethodHintDelete(JoinMethodHint *hint);
317 static void JoinMethodHintDesc(JoinMethodHint *hint, StringInfo buf);
318 static int JoinMethodHintCmp(const JoinMethodHint *a, const JoinMethodHint *b);
319 static const char *JoinMethodHintParse(JoinMethodHint *hint, HintState *hstate,
320 Query *parse, const char *str);
321 static Hint *LeadingHintCreate(const char *hint_str, const char *keyword,
322 HintKeyword hint_keyword);
323 static void LeadingHintDelete(LeadingHint *hint);
324 static void LeadingHintDesc(LeadingHint *hint, StringInfo buf);
325 static int LeadingHintCmp(const LeadingHint *a, const LeadingHint *b);
326 static const char *LeadingHintParse(LeadingHint *hint, HintState *hstate,
327 Query *parse, const char *str);
328 static Hint *SetHintCreate(const char *hint_str, const char *keyword,
329 HintKeyword hint_keyword);
330 static void SetHintDelete(SetHint *hint);
331 static void SetHintDesc(SetHint *hint, StringInfo buf);
332 static int SetHintCmp(const SetHint *a, const SetHint *b);
333 static const char *SetHintParse(SetHint *hint, HintState *hstate, Query *parse,
336 static void quote_value(StringInfo buf, const char *value);
338 static const char *parse_quoted_value(const char *str, char **word,
341 RelOptInfo *pg_hint_plan_standard_join_search(PlannerInfo *root,
344 void pg_hint_plan_join_search_one_level(PlannerInfo *root, int level);
345 static void make_rels_by_clause_joins(PlannerInfo *root, RelOptInfo *old_rel,
346 ListCell *other_rels);
347 static void make_rels_by_clauseless_joins(PlannerInfo *root,
349 ListCell *other_rels);
350 static bool has_join_restriction(PlannerInfo *root, RelOptInfo *rel);
351 static void set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
352 Index rti, RangeTblEntry *rte);
353 #if PG_VERSION_NUM >= 90200
354 static void generate_mergeappend_paths(PlannerInfo *root, RelOptInfo *rel,
355 List *live_childrels,
356 List *all_child_pathkeys);
358 static List *accumulate_append_subpath(List *subpaths, Path *path);
359 #if PG_VERSION_NUM < 90200
360 static void set_dummy_rel_pathlist(RelOptInfo *rel);
362 RelOptInfo *pg_hint_plan_make_join_rel(PlannerInfo *root, RelOptInfo *rel1,
366 static bool pg_hint_plan_enable_hint = true;
367 static bool pg_hint_plan_debug_print = false;
368 static int pg_hint_plan_parse_messages = INFO;
370 static const struct config_enum_entry parse_messages_level_options[] = {
371 {"debug", DEBUG2, true},
372 {"debug5", DEBUG5, false},
373 {"debug4", DEBUG4, false},
374 {"debug3", DEBUG3, false},
375 {"debug2", DEBUG2, false},
376 {"debug1", DEBUG1, false},
378 {"info", INFO, false},
379 {"notice", NOTICE, false},
380 {"warning", WARNING, false},
381 {"error", ERROR, false},
383 * {"fatal", FATAL, true},
384 * {"panic", PANIC, true},
389 /* Saved hook values in case of unload */
390 static ProcessUtility_hook_type prev_ProcessUtility = NULL;
391 static planner_hook_type prev_planner = NULL;
392 static get_relation_info_hook_type prev_get_relation_info = NULL;
393 static join_search_hook_type prev_join_search = NULL;
395 /* Hold reference to currently active hint */
396 static HintState *current_hint = NULL;
399 * List of hint contexts. We treat the head of the list as the Top of the
400 * context stack, so current_hint always points the first element of this list.
402 static List *HintStateStack = NIL;
405 * Holds statement name during executing EXECUTE command. NULL for other
408 static char *stmt_name = NULL;
410 static const HintParser parsers[] = {
411 {HINT_SEQSCAN, ScanMethodHintCreate, HINT_KEYWORD_SEQSCAN},
412 {HINT_INDEXSCAN, ScanMethodHintCreate, HINT_KEYWORD_INDEXSCAN},
413 {HINT_BITMAPSCAN, ScanMethodHintCreate, HINT_KEYWORD_BITMAPSCAN},
414 {HINT_TIDSCAN, ScanMethodHintCreate, HINT_KEYWORD_TIDSCAN},
415 {HINT_NOSEQSCAN, ScanMethodHintCreate, HINT_KEYWORD_NOSEQSCAN},
416 {HINT_NOINDEXSCAN, ScanMethodHintCreate, HINT_KEYWORD_NOINDEXSCAN},
417 {HINT_NOBITMAPSCAN, ScanMethodHintCreate, HINT_KEYWORD_NOBITMAPSCAN},
418 {HINT_NOTIDSCAN, ScanMethodHintCreate, HINT_KEYWORD_NOTIDSCAN},
419 #if PG_VERSION_NUM >= 90200
420 {HINT_INDEXONLYSCAN, ScanMethodHintCreate, HINT_KEYWORD_INDEXONLYSCAN},
421 {HINT_NOINDEXONLYSCAN, ScanMethodHintCreate, HINT_KEYWORD_NOINDEXONLYSCAN},
423 {HINT_NESTLOOP, JoinMethodHintCreate, HINT_KEYWORD_NESTLOOP},
424 {HINT_MERGEJOIN, JoinMethodHintCreate, HINT_KEYWORD_MERGEJOIN},
425 {HINT_HASHJOIN, JoinMethodHintCreate, HINT_KEYWORD_HASHJOIN},
426 {HINT_NONESTLOOP, JoinMethodHintCreate, HINT_KEYWORD_NONESTLOOP},
427 {HINT_NOMERGEJOIN, JoinMethodHintCreate, HINT_KEYWORD_NOMERGEJOIN},
428 {HINT_NOHASHJOIN, JoinMethodHintCreate, HINT_KEYWORD_NOHASHJOIN},
429 {HINT_LEADING, LeadingHintCreate, HINT_KEYWORD_LEADING},
430 {HINT_SET, SetHintCreate, HINT_KEYWORD_SET},
431 {NULL, NULL, HINT_KEYWORD_UNRECOGNIZED}
435 * Module load callbacks
440 /* Define custom GUC variables. */
441 DefineCustomBoolVariable("pg_hint_plan.enable_hint",
442 "Force planner to use plans specified in the hint comment preceding to the query.",
444 &pg_hint_plan_enable_hint,
452 DefineCustomBoolVariable("pg_hint_plan.debug_print",
453 "Logs results of hint parsing.",
455 &pg_hint_plan_debug_print,
463 DefineCustomEnumVariable("pg_hint_plan.parse_messages",
464 "Message level of parse errors.",
466 &pg_hint_plan_parse_messages,
468 parse_messages_level_options,
476 prev_ProcessUtility = ProcessUtility_hook;
477 ProcessUtility_hook = pg_hint_plan_ProcessUtility;
478 prev_planner = planner_hook;
479 planner_hook = pg_hint_plan_planner;
480 prev_get_relation_info = get_relation_info_hook;
481 get_relation_info_hook = pg_hint_plan_get_relation_info;
482 prev_join_search = join_search_hook;
483 join_search_hook = pg_hint_plan_join_search;
487 * Module unload callback
493 /* Uninstall hooks. */
494 ProcessUtility_hook = prev_ProcessUtility;
495 planner_hook = prev_planner;
496 get_relation_info_hook = prev_get_relation_info;
497 join_search_hook = prev_join_search;
501 * create and delete functions the hint object
505 ScanMethodHintCreate(const char *hint_str, const char *keyword,
506 HintKeyword hint_keyword)
508 ScanMethodHint *hint;
510 hint = palloc(sizeof(ScanMethodHint));
511 hint->base.hint_str = hint_str;
512 hint->base.keyword = keyword;
513 hint->base.hint_keyword = hint_keyword;
514 hint->base.type = HINT_TYPE_SCAN_METHOD;
515 hint->base.state = HINT_STATE_NOTUSED;
516 hint->base.delete_func = (HintDeleteFunction) ScanMethodHintDelete;
517 hint->base.desc_func = (HintDescFunction) ScanMethodHintDesc;
518 hint->base.cmp_func = (HintCmpFunction) ScanMethodHintCmp;
519 hint->base.parse_func = (HintParseFunction) ScanMethodHintParse;
520 hint->relname = NULL;
521 hint->indexnames = NIL;
522 hint->enforce_mask = 0;
524 return (Hint *) hint;
528 ScanMethodHintDelete(ScanMethodHint *hint)
534 pfree(hint->relname);
535 list_free_deep(hint->indexnames);
540 JoinMethodHintCreate(const char *hint_str, const char *keyword,
541 HintKeyword hint_keyword)
543 JoinMethodHint *hint;
545 hint = palloc(sizeof(JoinMethodHint));
546 hint->base.hint_str = hint_str;
547 hint->base.keyword = keyword;
548 hint->base.hint_keyword = hint_keyword;
549 hint->base.type = HINT_TYPE_JOIN_METHOD;
550 hint->base.state = HINT_STATE_NOTUSED;
551 hint->base.delete_func = (HintDeleteFunction) JoinMethodHintDelete;
552 hint->base.desc_func = (HintDescFunction) JoinMethodHintDesc;
553 hint->base.cmp_func = (HintCmpFunction) JoinMethodHintCmp;
554 hint->base.parse_func = (HintParseFunction) JoinMethodHintParse;
556 hint->inner_nrels = 0;
557 hint->relnames = NULL;
558 hint->enforce_mask = 0;
559 hint->joinrelids = NULL;
560 hint->inner_joinrelids = NULL;
562 return (Hint *) hint;
566 JoinMethodHintDelete(JoinMethodHint *hint)
575 for (i = 0; i < hint->nrels; i++)
576 pfree(hint->relnames[i]);
577 pfree(hint->relnames);
580 bms_free(hint->joinrelids);
581 bms_free(hint->inner_joinrelids);
586 LeadingHintCreate(const char *hint_str, const char *keyword,
587 HintKeyword hint_keyword)
591 hint = palloc(sizeof(LeadingHint));
592 hint->base.hint_str = hint_str;
593 hint->base.keyword = keyword;
594 hint->base.hint_keyword = hint_keyword;
595 hint->base.type = HINT_TYPE_LEADING;
596 hint->base.state = HINT_STATE_NOTUSED;
597 hint->base.delete_func = (HintDeleteFunction)LeadingHintDelete;
598 hint->base.desc_func = (HintDescFunction) LeadingHintDesc;
599 hint->base.cmp_func = (HintCmpFunction) LeadingHintCmp;
600 hint->base.parse_func = (HintParseFunction) LeadingHintParse;
601 hint->relations = NIL;
602 hint->outer_inner = NULL;
604 return (Hint *) hint;
608 LeadingHintDelete(LeadingHint *hint)
613 list_free_deep(hint->relations);
614 if (hint->outer_inner)
615 pfree(hint->outer_inner);
620 SetHintCreate(const char *hint_str, const char *keyword,
621 HintKeyword hint_keyword)
625 hint = palloc(sizeof(SetHint));
626 hint->base.hint_str = hint_str;
627 hint->base.keyword = keyword;
628 hint->base.hint_keyword = hint_keyword;
629 hint->base.type = HINT_TYPE_SET;
630 hint->base.state = HINT_STATE_NOTUSED;
631 hint->base.delete_func = (HintDeleteFunction) SetHintDelete;
632 hint->base.desc_func = (HintDescFunction) SetHintDesc;
633 hint->base.cmp_func = (HintCmpFunction) SetHintCmp;
634 hint->base.parse_func = (HintParseFunction) SetHintParse;
639 return (Hint *) hint;
643 SetHintDelete(SetHint *hint)
653 list_free(hint->words);
658 HintStateCreate(void)
662 hstate = palloc(sizeof(HintState));
663 hstate->hint_str = NULL;
664 hstate->nall_hints = 0;
665 hstate->max_all_hints = 0;
666 hstate->all_hints = NULL;
667 memset(hstate->num_hints, 0, sizeof(hstate->num_hints));
668 hstate->scan_hints = NULL;
669 hstate->init_scan_mask = 0;
670 hstate->parent_relid = 0;
671 hstate->parent_rel_oid = InvalidOid;
672 hstate->parent_hint = NULL;
673 hstate->parent_ind_atts = NIL;
674 hstate->join_hints = NULL;
675 hstate->init_join_mask = 0;
676 hstate->join_hint_level = NULL;
677 hstate->leading_hint = NULL;
678 hstate->context = superuser() ? PGC_SUSET : PGC_USERSET;
679 hstate->set_hints = NULL;
685 HintStateDelete(HintState *hstate)
692 if (hstate->hint_str)
693 pfree(hstate->hint_str);
695 for (i = 0; i < hstate->num_hints[HINT_TYPE_SCAN_METHOD]; i++)
696 hstate->all_hints[i]->delete_func(hstate->all_hints[i]);
697 if (hstate->all_hints)
698 pfree(hstate->all_hints);
699 if (hstate->parent_ind_atts)
700 list_free(hstate->parent_ind_atts);
704 * Copy given value into buf, with quoting with '"' if necessary.
707 quote_value(StringInfo buf, const char *value)
709 bool need_quote = false;
712 for (str = value; *str != '\0'; str++)
714 if (isspace(*str) || *str == '(' || *str == ')' || *str == '"')
717 appendStringInfoCharMacro(buf, '"');
722 for (str = value; *str != '\0'; str++)
725 appendStringInfoCharMacro(buf, '"');
727 appendStringInfoCharMacro(buf, *str);
731 appendStringInfoCharMacro(buf, '"');
735 ScanMethodHintDesc(ScanMethodHint *hint, StringInfo buf)
739 appendStringInfo(buf, "%s(", hint->base.keyword);
740 if (hint->relname != NULL)
742 quote_value(buf, hint->relname);
743 foreach(l, hint->indexnames)
745 appendStringInfoCharMacro(buf, ' ');
746 quote_value(buf, (char *) lfirst(l));
749 appendStringInfoString(buf, ")\n");
753 JoinMethodHintDesc(JoinMethodHint *hint, StringInfo buf)
757 appendStringInfo(buf, "%s(", hint->base.keyword);
758 if (hint->relnames != NULL)
760 quote_value(buf, hint->relnames[0]);
761 for (i = 1; i < hint->nrels; i++)
763 appendStringInfoCharMacro(buf, ' ');
764 quote_value(buf, hint->relnames[i]);
767 appendStringInfoString(buf, ")\n");
772 OuterInnerDesc(OuterInnerRels *outer_inner, StringInfo buf)
774 if (outer_inner->relation == NULL)
781 appendStringInfoCharMacro(buf, '(');
782 foreach(l, outer_inner->outer_inner_pair)
787 appendStringInfoCharMacro(buf, ' ');
789 OuterInnerDesc(lfirst(l), buf);
792 appendStringInfoCharMacro(buf, ')');
795 quote_value(buf, outer_inner->relation);
799 LeadingHintDesc(LeadingHint *hint, StringInfo buf)
801 appendStringInfo(buf, "%s(", HINT_LEADING);
802 if (hint->outer_inner == NULL)
809 foreach(l, hint->relations)
814 appendStringInfoCharMacro(buf, ' ');
816 quote_value(buf, (char *) lfirst(l));
820 OuterInnerDesc(hint->outer_inner, buf);
822 appendStringInfoString(buf, ")\n");
826 SetHintDesc(SetHint *hint, StringInfo buf)
828 bool is_first = true;
831 appendStringInfo(buf, "%s(", HINT_SET);
832 foreach(l, hint->words)
837 appendStringInfoCharMacro(buf, ' ');
839 quote_value(buf, (char *) lfirst(l));
841 appendStringInfo(buf, ")\n");
845 * Append string which repserents all hints in a given state to buf, with
846 * preceding title with them.
849 desc_hint_in_state(HintState *hstate, StringInfo buf, const char *title,
854 appendStringInfo(buf, "%s:\n", title);
855 for (i = 0; i < hstate->nall_hints; i++)
857 if (hstate->all_hints[i]->state != state)
860 hstate->all_hints[i]->desc_func(hstate->all_hints[i], buf);
865 * Dump contents of given hstate to server log with log level LOG.
868 HintStateDump(HintState *hstate)
874 elog(LOG, "pg_hint_plan:\nno hint");
878 initStringInfo(&buf);
880 appendStringInfoString(&buf, "pg_hint_plan:\n");
881 desc_hint_in_state(hstate, &buf, "used hint", HINT_STATE_USED);
882 desc_hint_in_state(hstate, &buf, "not used hint", HINT_STATE_NOTUSED);
883 desc_hint_in_state(hstate, &buf, "duplication hint", HINT_STATE_DUPLICATION);
884 desc_hint_in_state(hstate, &buf, "error hint", HINT_STATE_ERROR);
886 elog(LOG, "%s", buf.data);
896 RelnameCmp(const void *a, const void *b)
898 const char *relnamea = *((const char **) a);
899 const char *relnameb = *((const char **) b);
901 return strcmp(relnamea, relnameb);
905 ScanMethodHintCmp(const ScanMethodHint *a, const ScanMethodHint *b)
907 return RelnameCmp(&a->relname, &b->relname);
911 JoinMethodHintCmp(const JoinMethodHint *a, const JoinMethodHint *b)
915 if (a->nrels != b->nrels)
916 return a->nrels - b->nrels;
918 for (i = 0; i < a->nrels; i++)
921 if ((result = RelnameCmp(&a->relnames[i], &b->relnames[i])) != 0)
929 LeadingHintCmp(const LeadingHint *a, const LeadingHint *b)
935 SetHintCmp(const SetHint *a, const SetHint *b)
937 return strcmp(a->name, b->name);
941 HintCmp(const void *a, const void *b)
943 const Hint *hinta = *((const Hint **) a);
944 const Hint *hintb = *((const Hint **) b);
946 if (hinta->type != hintb->type)
947 return hinta->type - hintb->type;
948 if (hinta->state == HINT_STATE_ERROR)
950 if (hintb->state == HINT_STATE_ERROR)
952 return hinta->cmp_func(hinta, hintb);
956 * Returns byte offset of hint b from hint a. If hint a was specified before
957 * b, positive value is returned.
960 HintCmpWithPos(const void *a, const void *b)
962 const Hint *hinta = *((const Hint **) a);
963 const Hint *hintb = *((const Hint **) b);
966 result = HintCmp(a, b);
968 result = hinta->hint_str - hintb->hint_str;
977 parse_keyword(const char *str, StringInfo buf)
981 while (!isspace(*str) && *str != '(' && *str != '\0')
982 appendStringInfoCharMacro(buf, *str++);
988 skip_parenthesis(const char *str, char parenthesis)
992 if (*str != parenthesis)
994 if (parenthesis == '(')
995 hint_ereport(str, ("Opening parenthesis is necessary."));
996 else if (parenthesis == ')')
997 hint_ereport(str, ("Closing parenthesis is necessary."));
1008 * Parse a token from str, and store malloc'd copy into word. A token can be
1009 * quoted with '"'. Return value is pointer to unparsed portion of original
1010 * string, or NULL if an error occurred.
1012 * Parsed token is truncated within NAMEDATALEN-1 bytes, when truncate is true.
1015 parse_quoted_value(const char *str, char **word, bool truncate)
1020 /* Skip leading spaces. */
1023 initStringInfo(&buf);
1036 /* Double quotation must be closed. */
1040 hint_ereport(str, ("Unterminated quoted string."));
1045 * Skip escaped double quotation.
1047 * We don't allow slash-asterisk and asterisk-slash (delimiters of
1048 * block comments) to be an object name, so users must specify
1049 * alias for such object names.
1051 * Those special names can be allowed if we care escaped slashes
1052 * and asterisks, but we don't.
1061 else if (isspace(*str) || *str == '(' || *str == ')' || *str == '"' ||
1065 appendStringInfoCharMacro(&buf, *str++);
1070 hint_ereport(str, ("Zero-length delimited string."));
1077 /* Truncate name if it's too long */
1079 truncate_identifier(buf.data, strlen(buf.data), true);
1086 static OuterInnerRels *
1087 OuterInnerRelsCreate(char *name, List *outer_inner_list)
1089 OuterInnerRels *outer_inner;
1091 outer_inner = palloc(sizeof(OuterInnerRels));
1092 outer_inner->relation = name;
1093 outer_inner->outer_inner_pair = outer_inner_list;
1099 parse_parentheses_Leading_in(const char *str, OuterInnerRels **outer_inner)
1101 List *outer_inner_pair = NIL;
1103 if ((str = skip_parenthesis(str, '(')) == NULL)
1108 /* Store words in parentheses into outer_inner_list. */
1109 while(*str != ')' && *str != '\0')
1111 OuterInnerRels *outer_inner_rels;
1115 str = parse_parentheses_Leading_in(str, &outer_inner_rels);
1123 if ((str = parse_quoted_value(str, &name, true)) == NULL)
1126 outer_inner_rels = OuterInnerRelsCreate(name, NIL);
1129 outer_inner_pair = lappend(outer_inner_pair, outer_inner_rels);
1134 (str = skip_parenthesis(str, ')')) == NULL)
1136 list_free(outer_inner_pair);
1140 *outer_inner = OuterInnerRelsCreate(NULL, outer_inner_pair);
1146 parse_parentheses_Leading(const char *str, List **name_list,
1147 OuterInnerRels **outer_inner)
1150 bool truncate = true;
1152 if ((str = skip_parenthesis(str, '(')) == NULL)
1158 if ((str = parse_parentheses_Leading_in(str, outer_inner)) == NULL)
1163 /* Store words in parentheses into name_list. */
1164 while(*str != ')' && *str != '\0')
1166 if ((str = parse_quoted_value(str, &name, truncate)) == NULL)
1168 list_free(*name_list);
1172 *name_list = lappend(*name_list, name);
1177 if ((str = skip_parenthesis(str, ')')) == NULL)
1183 parse_parentheses(const char *str, List **name_list, HintType type)
1186 bool truncate = true;
1188 if ((str = skip_parenthesis(str, '(')) == NULL)
1193 /* Store words in parentheses into name_list. */
1194 while(*str != ')' && *str != '\0')
1196 if ((str = parse_quoted_value(str, &name, truncate)) == NULL)
1198 list_free(*name_list);
1202 *name_list = lappend(*name_list, name);
1205 if (type == HINT_TYPE_SET)
1211 if ((str = skip_parenthesis(str, ')')) == NULL)
1217 parse_hints(HintState *hstate, Query *parse, const char *str)
1222 initStringInfo(&buf);
1223 while (*str != '\0')
1225 const HintParser *parser;
1227 /* in error message, we output the comment including the keyword. */
1228 head = (char *) str;
1230 /* parse only the keyword of the hint. */
1231 resetStringInfo(&buf);
1232 str = parse_keyword(str, &buf);
1234 for (parser = parsers; parser->keyword != NULL; parser++)
1236 char *keyword = parser->keyword;
1239 if (strcasecmp(buf.data, keyword) != 0)
1242 hint = parser->create_func(head, keyword, parser->hint_keyword);
1244 /* parser of each hint does parse in a parenthesis. */
1245 if ((str = hint->parse_func(hint, hstate, parse, str)) == NULL)
1247 hint->delete_func(hint);
1253 * Add hint information into all_hints array. If we don't have
1254 * enough space, double the array.
1256 if (hstate->nall_hints == 0)
1258 hstate->max_all_hints = HINT_ARRAY_DEFAULT_INITSIZE;
1259 hstate->all_hints = (Hint **)
1260 palloc(sizeof(Hint *) * hstate->max_all_hints);
1262 else if (hstate->nall_hints == hstate->max_all_hints)
1264 hstate->max_all_hints *= 2;
1265 hstate->all_hints = (Hint **)
1266 repalloc(hstate->all_hints,
1267 sizeof(Hint *) * hstate->max_all_hints);
1270 hstate->all_hints[hstate->nall_hints] = hint;
1271 hstate->nall_hints++;
1278 if (parser->keyword == NULL)
1281 ("Unrecognized hint keyword \"%s\".", buf.data));
1291 * Do basic parsing of the query head comment.
1294 parse_head_comment(Query *parse)
1303 /* get client-supplied query string. */
1306 PreparedStatement *entry;
1308 entry = FetchPreparedStatement(stmt_name, true);
1309 p = entry->plansource->query_string;
1312 p = debug_query_string;
1317 /* extract query head comment. */
1318 len = strlen(HINT_START);
1320 if (strncmp(p, HINT_START, len))
1327 /* find hint end keyword. */
1328 if ((tail = strstr(p, HINT_END)) == NULL)
1330 hint_ereport(head, ("Unterminated block comment."));
1334 /* We don't support nested block comments. */
1335 if ((head = strstr(p, BLOCK_COMMENT_START)) != NULL && head < tail)
1337 hint_ereport(head, ("Nested block comments are not supported."));
1341 /* Make a copy of hint. */
1343 head = palloc(len + 1);
1344 memcpy(head, p, len);
1348 hstate = HintStateCreate();
1349 hstate->hint_str = head;
1351 /* parse each hint. */
1352 parse_hints(hstate, parse, p);
1354 /* When nothing specified a hint, we free HintState and returns NULL. */
1355 if (hstate->nall_hints == 0)
1357 HintStateDelete(hstate);
1361 /* Sort hints in order of original position. */
1362 qsort(hstate->all_hints, hstate->nall_hints, sizeof(Hint *),
1365 /* Count number of hints per hint-type. */
1366 for (i = 0; i < hstate->nall_hints; i++)
1368 Hint *cur_hint = hstate->all_hints[i];
1369 hstate->num_hints[cur_hint->type]++;
1373 * If an object (or a set of objects) has multiple hints of same hint-type,
1374 * only the last hint is valid and others are igonred in planning.
1375 * Hints except the last are marked as 'duplicated' to remember the order.
1377 for (i = 0; i < hstate->nall_hints - 1; i++)
1379 Hint *cur_hint = hstate->all_hints[i];
1380 Hint *next_hint = hstate->all_hints[i + 1];
1383 * Leading hint is marked as 'duplicated' in transform_join_hints.
1385 if (cur_hint->type == HINT_TYPE_LEADING &&
1386 next_hint->type == HINT_TYPE_LEADING)
1390 * Note that we need to pass addresses of hint pointers, because
1391 * HintCmp is designed to sort array of Hint* by qsort.
1393 if (HintCmp(&cur_hint, &next_hint) == 0)
1395 hint_ereport(cur_hint->hint_str,
1396 ("Conflict %s hint.", HintTypeName[cur_hint->type]));
1397 cur_hint->state = HINT_STATE_DUPLICATION;
1402 * Make sure that per-type array pointers point proper position in the
1403 * array which consists of all hints.
1405 hstate->scan_hints = (ScanMethodHint **) hstate->all_hints;
1406 hstate->join_hints = (JoinMethodHint **) (hstate->scan_hints +
1407 hstate->num_hints[HINT_TYPE_SCAN_METHOD]);
1408 hstate->leading_hint = (LeadingHint **) (hstate->join_hints +
1409 hstate->num_hints[HINT_TYPE_JOIN_METHOD]);
1410 hstate->set_hints = (SetHint **) (hstate->leading_hint +
1411 hstate->num_hints[HINT_TYPE_LEADING]);
1417 * Parse inside of parentheses of scan-method hints.
1420 ScanMethodHintParse(ScanMethodHint *hint, HintState *hstate, Query *parse,
1423 const char *keyword = hint->base.keyword;
1424 HintKeyword hint_keyword = hint->base.hint_keyword;
1425 List *name_list = NIL;
1428 if ((str = parse_parentheses(str, &name_list, hint->base.type)) == NULL)
1431 /* Parse relation name and index name(s) if given hint accepts. */
1432 length = list_length(name_list);
1435 hint->relname = linitial(name_list);
1436 hint->indexnames = list_delete_first(name_list);
1438 /* check whether the hint accepts index name(s). */
1439 if (hint_keyword != HINT_KEYWORD_INDEXSCAN &&
1440 #if PG_VERSION_NUM >= 90200
1441 hint_keyword != HINT_KEYWORD_INDEXONLYSCAN &&
1443 hint_keyword != HINT_KEYWORD_BITMAPSCAN &&
1447 ("%s hint accepts only one relation.",
1448 hint->base.keyword));
1449 hint->base.state = HINT_STATE_ERROR;
1456 ("%s hint requires a relation.",
1457 hint->base.keyword));
1458 hint->base.state = HINT_STATE_ERROR;
1462 /* Set a bit for specified hint. */
1463 switch (hint_keyword)
1465 case HINT_KEYWORD_SEQSCAN:
1466 hint->enforce_mask = ENABLE_SEQSCAN;
1468 case HINT_KEYWORD_INDEXSCAN:
1469 hint->enforce_mask = ENABLE_INDEXSCAN;
1471 case HINT_KEYWORD_BITMAPSCAN:
1472 hint->enforce_mask = ENABLE_BITMAPSCAN;
1474 case HINT_KEYWORD_TIDSCAN:
1475 hint->enforce_mask = ENABLE_TIDSCAN;
1477 case HINT_KEYWORD_NOSEQSCAN:
1478 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_SEQSCAN;
1480 case HINT_KEYWORD_NOINDEXSCAN:
1481 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_INDEXSCAN;
1483 case HINT_KEYWORD_NOBITMAPSCAN:
1484 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_BITMAPSCAN;
1486 case HINT_KEYWORD_NOTIDSCAN:
1487 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_TIDSCAN;
1489 #if PG_VERSION_NUM >= 90200
1490 case HINT_KEYWORD_INDEXONLYSCAN:
1491 hint->enforce_mask = ENABLE_INDEXSCAN | ENABLE_INDEXONLYSCAN;
1493 case HINT_KEYWORD_NOINDEXONLYSCAN:
1494 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_INDEXONLYSCAN;
1498 hint_ereport(str, ("Unrecognized hint keyword \"%s\".", keyword));
1507 JoinMethodHintParse(JoinMethodHint *hint, HintState *hstate, Query *parse,
1510 const char *keyword = hint->base.keyword;
1511 HintKeyword hint_keyword = hint->base.hint_keyword;
1512 List *name_list = NIL;
1514 if ((str = parse_parentheses(str, &name_list, hint->base.type)) == NULL)
1517 hint->nrels = list_length(name_list);
1519 if (hint->nrels > 0)
1525 * Transform relation names from list to array to sort them with qsort
1528 hint->relnames = palloc(sizeof(char *) * hint->nrels);
1529 foreach (l, name_list)
1531 hint->relnames[i] = lfirst(l);
1536 list_free(name_list);
1538 /* A join hint requires at least two relations */
1539 if (hint->nrels < 2)
1542 ("%s hint requires at least two relations.",
1543 hint->base.keyword));
1544 hint->base.state = HINT_STATE_ERROR;
1548 /* Sort hints in alphabetical order of relation names. */
1549 qsort(hint->relnames, hint->nrels, sizeof(char *), RelnameCmp);
1551 switch (hint_keyword)
1553 case HINT_KEYWORD_NESTLOOP:
1554 hint->enforce_mask = ENABLE_NESTLOOP;
1556 case HINT_KEYWORD_MERGEJOIN:
1557 hint->enforce_mask = ENABLE_MERGEJOIN;
1559 case HINT_KEYWORD_HASHJOIN:
1560 hint->enforce_mask = ENABLE_HASHJOIN;
1562 case HINT_KEYWORD_NONESTLOOP:
1563 hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_NESTLOOP;
1565 case HINT_KEYWORD_NOMERGEJOIN:
1566 hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_MERGEJOIN;
1568 case HINT_KEYWORD_NOHASHJOIN:
1569 hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_HASHJOIN;
1572 hint_ereport(str, ("Unrecognized hint keyword \"%s\".", keyword));
1581 OuterInnerPairCheck(OuterInnerRels *outer_inner)
1584 if (outer_inner->outer_inner_pair == NIL)
1586 if (outer_inner->relation)
1592 if (list_length(outer_inner->outer_inner_pair) == 2)
1594 foreach(l, outer_inner->outer_inner_pair)
1596 if (!OuterInnerPairCheck(lfirst(l)))
1607 OuterInnerList(OuterInnerRels *outer_inner)
1609 List *outer_inner_list = NIL;
1611 OuterInnerRels *outer_inner_rels;
1613 foreach(l, outer_inner->outer_inner_pair)
1615 outer_inner_rels = (OuterInnerRels *)(lfirst(l));
1617 if (outer_inner_rels->relation != NULL)
1618 outer_inner_list = lappend(outer_inner_list,
1619 outer_inner_rels->relation);
1621 outer_inner_list = list_concat(outer_inner_list,
1622 OuterInnerList(outer_inner_rels));
1624 return outer_inner_list;
1628 LeadingHintParse(LeadingHint *hint, HintState *hstate, Query *parse,
1631 List *name_list = NIL;
1632 OuterInnerRels *outer_inner = NULL;
1634 if ((str = parse_parentheses_Leading(str, &name_list, &outer_inner)) ==
1638 if (outer_inner != NULL)
1639 name_list = OuterInnerList(outer_inner);
1641 hint->relations = name_list;
1642 hint->outer_inner = outer_inner;
1644 /* A Leading hint requires at least two relations */
1645 if ( hint->outer_inner == NULL && list_length(hint->relations) < 2)
1647 hint_ereport(hint->base.hint_str,
1648 ("%s hint requires at least two relations.",
1650 hint->base.state = HINT_STATE_ERROR;
1652 else if (hint->outer_inner != NULL &&
1653 !OuterInnerPairCheck(hint->outer_inner))
1655 hint_ereport(hint->base.hint_str,
1656 ("%s hint requires two sets of relations when parentheses nests.",
1658 hint->base.state = HINT_STATE_ERROR;
1665 SetHintParse(SetHint *hint, HintState *hstate, Query *parse, const char *str)
1667 List *name_list = NIL;
1669 if ((str = parse_parentheses(str, &name_list, hint->base.type)) == NULL)
1672 hint->words = name_list;
1674 /* We need both name and value to set GUC parameter. */
1675 if (list_length(name_list) == 2)
1677 hint->name = linitial(name_list);
1678 hint->value = lsecond(name_list);
1682 hint_ereport(hint->base.hint_str,
1683 ("%s hint requires name and value of GUC parameter.",
1685 hint->base.state = HINT_STATE_ERROR;
1692 * set GUC parameter functions
1696 set_config_option_wrapper(const char *name, const char *value,
1697 GucContext context, GucSource source,
1698 GucAction action, bool changeVal, int elevel)
1701 MemoryContext ccxt = CurrentMemoryContext;
1705 #if PG_VERSION_NUM >= 90200
1706 result = set_config_option(name, value, context, source,
1707 action, changeVal, 0);
1709 result = set_config_option(name, value, context, source,
1717 /* Save error info */
1718 MemoryContextSwitchTo(ccxt);
1719 errdata = CopyErrorData();
1722 ereport(elevel, (errcode(errdata->sqlerrcode),
1723 errmsg("%s", errdata->message),
1724 errdata->detail ? errdetail("%s", errdata->detail) : 0,
1725 errdata->hint ? errhint("%s", errdata->hint) : 0));
1726 FreeErrorData(errdata);
1734 set_config_options(SetHint **options, int noptions, GucContext context)
1739 save_nestlevel = NewGUCNestLevel();
1741 for (i = 0; i < noptions; i++)
1743 SetHint *hint = options[i];
1746 if (!hint_state_enabled(hint))
1749 result = set_config_option_wrapper(hint->name, hint->value, context,
1750 PGC_S_SESSION, GUC_ACTION_SAVE, true,
1751 pg_hint_plan_parse_messages);
1753 hint->base.state = HINT_STATE_USED;
1755 hint->base.state = HINT_STATE_ERROR;
1758 return save_nestlevel;
1761 #define SET_CONFIG_OPTION(name, type_bits) \
1762 set_config_option_wrapper((name), \
1763 (mask & (type_bits)) ? "true" : "false", \
1764 context, PGC_S_SESSION, GUC_ACTION_SAVE, true, ERROR)
1767 set_scan_config_options(unsigned char enforce_mask, GucContext context)
1771 if (enforce_mask == ENABLE_SEQSCAN || enforce_mask == ENABLE_INDEXSCAN ||
1772 enforce_mask == ENABLE_BITMAPSCAN || enforce_mask == ENABLE_TIDSCAN
1773 #if PG_VERSION_NUM >= 90200
1774 || enforce_mask == (ENABLE_INDEXSCAN | ENABLE_INDEXONLYSCAN)
1777 mask = enforce_mask;
1779 mask = enforce_mask & current_hint->init_scan_mask;
1781 SET_CONFIG_OPTION("enable_seqscan", ENABLE_SEQSCAN);
1782 SET_CONFIG_OPTION("enable_indexscan", ENABLE_INDEXSCAN);
1783 SET_CONFIG_OPTION("enable_bitmapscan", ENABLE_BITMAPSCAN);
1784 SET_CONFIG_OPTION("enable_tidscan", ENABLE_TIDSCAN);
1785 #if PG_VERSION_NUM >= 90200
1786 SET_CONFIG_OPTION("enable_indexonlyscan", ENABLE_INDEXONLYSCAN);
1791 set_join_config_options(unsigned char enforce_mask, GucContext context)
1795 if (enforce_mask == ENABLE_NESTLOOP || enforce_mask == ENABLE_MERGEJOIN ||
1796 enforce_mask == ENABLE_HASHJOIN)
1797 mask = enforce_mask;
1799 mask = enforce_mask & current_hint->init_join_mask;
1801 SET_CONFIG_OPTION("enable_nestloop", ENABLE_NESTLOOP);
1802 SET_CONFIG_OPTION("enable_mergejoin", ENABLE_MERGEJOIN);
1803 SET_CONFIG_OPTION("enable_hashjoin", ENABLE_HASHJOIN);
1807 * pg_hint_plan hook functions
1811 pg_hint_plan_ProcessUtility(Node *parsetree, const char *queryString,
1812 ParamListInfo params, bool isTopLevel,
1813 DestReceiver *dest, char *completionTag)
1817 if (!pg_hint_plan_enable_hint)
1819 if (prev_ProcessUtility)
1820 (*prev_ProcessUtility) (parsetree, queryString, params,
1821 isTopLevel, dest, completionTag);
1823 standard_ProcessUtility(parsetree, queryString, params,
1824 isTopLevel, dest, completionTag);
1830 if (IsA(node, ExplainStmt))
1833 * Draw out parse tree of actual query from Query struct of EXPLAIN
1839 stmt = (ExplainStmt *) node;
1841 Assert(IsA(stmt->query, Query));
1842 query = (Query *) stmt->query;
1844 if (query->commandType == CMD_UTILITY && query->utilityStmt != NULL)
1845 node = query->utilityStmt;
1849 * If the query was a EXECUTE or CREATE TABLE AS EXECUTE, get query string
1850 * specified to preceding PREPARE command to use it as source of hints.
1852 if (IsA(node, ExecuteStmt))
1856 stmt = (ExecuteStmt *) node;
1857 stmt_name = stmt->name;
1859 #if PG_VERSION_NUM >= 90200
1861 * CREATE AS EXECUTE behavior has changed since 9.2, so we must handle it
1864 if (IsA(node, CreateTableAsStmt))
1866 CreateTableAsStmt *stmt;
1869 stmt = (CreateTableAsStmt *) node;
1870 Assert(IsA(stmt->query, Query));
1871 query = (Query *) stmt->query;
1873 if (query->commandType == CMD_UTILITY &&
1874 IsA(query->utilityStmt, ExecuteStmt))
1876 ExecuteStmt *estmt = (ExecuteStmt *) query->utilityStmt;
1877 stmt_name = estmt->name;
1885 if (prev_ProcessUtility)
1886 (*prev_ProcessUtility) (parsetree, queryString, params,
1887 isTopLevel, dest, completionTag);
1889 standard_ProcessUtility(parsetree, queryString, params,
1890 isTopLevel, dest, completionTag);
1904 if (prev_ProcessUtility)
1905 (*prev_ProcessUtility) (parsetree, queryString, params,
1906 isTopLevel, dest, completionTag);
1908 standard_ProcessUtility(parsetree, queryString, params,
1909 isTopLevel, dest, completionTag);
1913 * Push a hint into hint stack which is implemented with List struct. Head of
1914 * list is top of stack.
1917 push_hint(HintState *hstate)
1919 /* Prepend new hint to the list means pushing to stack. */
1920 HintStateStack = lcons(hstate, HintStateStack);
1922 /* Pushed hint is the one which should be used hereafter. */
1923 current_hint = hstate;
1926 /* Pop a hint from hint stack. Popped hint is automatically discarded. */
1930 /* Hint stack must not be empty. */
1931 if(HintStateStack == NIL)
1932 elog(ERROR, "hint stack is empty");
1935 * Take a hint at the head from the list, and free it. Switch current_hint
1936 * to point new head (NULL if the list is empty).
1938 HintStateStack = list_delete_first(HintStateStack);
1939 HintStateDelete(current_hint);
1940 if(HintStateStack == NIL)
1941 current_hint = NULL;
1943 current_hint = (HintState *) lfirst(list_head(HintStateStack));
1946 static PlannedStmt *
1947 pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
1950 PlannedStmt *result;
1954 * Use standard planner if pg_hint_plan is disabled. Other hook functions
1955 * try to change plan with current_hint if any, so set it to NULL.
1957 if (!pg_hint_plan_enable_hint)
1959 current_hint = NULL;
1962 return (*prev_planner) (parse, cursorOptions, boundParams);
1964 return standard_planner(parse, cursorOptions, boundParams);
1967 /* Create hint struct from parse tree. */
1968 hstate = parse_head_comment(parse);
1971 * Use standard planner if the statement has not valid hint. Other hook
1972 * functions try to change plan with current_hint if any, so set it to
1977 current_hint = NULL;
1980 return (*prev_planner) (parse, cursorOptions, boundParams);
1982 return standard_planner(parse, cursorOptions, boundParams);
1986 * Push new hint struct to the hint stack to disable previous hint context.
1990 /* Set GUC parameters which are specified with Set hint. */
1991 save_nestlevel = set_config_options(current_hint->set_hints,
1992 current_hint->num_hints[HINT_TYPE_SET],
1993 current_hint->context);
1996 current_hint->init_scan_mask |= ENABLE_SEQSCAN;
1997 if (enable_indexscan)
1998 current_hint->init_scan_mask |= ENABLE_INDEXSCAN;
1999 if (enable_bitmapscan)
2000 current_hint->init_scan_mask |= ENABLE_BITMAPSCAN;
2002 current_hint->init_scan_mask |= ENABLE_TIDSCAN;
2003 #if PG_VERSION_NUM >= 90200
2004 if (enable_indexonlyscan)
2005 current_hint->init_scan_mask |= ENABLE_INDEXONLYSCAN;
2007 if (enable_nestloop)
2008 current_hint->init_join_mask |= ENABLE_NESTLOOP;
2009 if (enable_mergejoin)
2010 current_hint->init_join_mask |= ENABLE_MERGEJOIN;
2011 if (enable_hashjoin)
2012 current_hint->init_join_mask |= ENABLE_HASHJOIN;
2015 * Use PG_TRY mechanism to recover GUC parameters and current_hint to the
2016 * state when this planner started when error occurred in planner.
2021 result = (*prev_planner) (parse, cursorOptions, boundParams);
2023 result = standard_planner(parse, cursorOptions, boundParams);
2028 * Rollback changes of GUC parameters, and pop current hint context
2029 * from hint stack to rewind the state.
2031 AtEOXact_GUC(true, save_nestlevel);
2037 /* Print hint in debug mode. */
2038 if (pg_hint_plan_debug_print)
2039 HintStateDump(current_hint);
2042 * Rollback changes of GUC parameters, and pop current hint context from
2043 * hint stack to rewind the state.
2045 AtEOXact_GUC(true, save_nestlevel);
2052 * Return scan method hint which matches given aliasname.
2054 static ScanMethodHint *
2055 find_scan_hint(PlannerInfo *root, RelOptInfo *rel)
2061 * We can't apply scan method hint if the relation is:
2062 * - not a base relation
2063 * - not an ordinary relation (such as join and subquery)
2065 if (rel->reloptkind != RELOPT_BASEREL || rel->rtekind != RTE_RELATION)
2068 rte = root->simple_rte_array[rel->relid];
2070 /* We can't force scan method of foreign tables */
2071 if (rte->relkind == RELKIND_FOREIGN_TABLE)
2074 /* Find scan method hint, which matches given names, from the list. */
2075 for (i = 0; i < current_hint->num_hints[HINT_TYPE_SCAN_METHOD]; i++)
2077 ScanMethodHint *hint = current_hint->scan_hints[i];
2079 /* We ignore disabled hints. */
2080 if (!hint_state_enabled(hint))
2083 if (RelnameCmp(&rte->eref->aliasname, &hint->relname) == 0)
2091 delete_indexes(ScanMethodHint *hint, RelOptInfo *rel, Oid relationObjectId)
2099 * We delete all the IndexOptInfo list and prevent you from being usable by
2102 if (hint->enforce_mask == ENABLE_SEQSCAN ||
2103 hint->enforce_mask == ENABLE_TIDSCAN)
2105 list_free_deep(rel->indexlist);
2106 rel->indexlist = NIL;
2107 hint->base.state = HINT_STATE_USED;
2113 * When a list of indexes is not specified, we just use all indexes.
2115 if (hint->indexnames == NIL)
2119 * Leaving only an specified index, we delete it from a IndexOptInfo list
2123 if (pg_hint_plan_debug_print)
2124 initStringInfo(&buf);
2126 for (cell = list_head(rel->indexlist); cell; cell = next)
2128 IndexOptInfo *info = (IndexOptInfo *) lfirst(cell);
2129 char *indexname = get_rel_name(info->indexoid);
2131 bool use_index = false;
2135 foreach(l, hint->indexnames)
2137 if (RelnameCmp(&indexname, &lfirst(l)) == 0)
2140 if (pg_hint_plan_debug_print)
2142 appendStringInfoCharMacro(&buf, ' ');
2143 quote_value(&buf, indexname);
2150 if (OidIsValid(relationObjectId) && !use_index)
2153 char *child_attname = NULL;
2154 char *parent_attname = NULL;
2156 foreach(l, current_hint->parent_ind_atts)
2158 List *attnums = (List *)lfirst(l);
2160 if ((list_length(attnums)) != info->ncolumns)
2163 for (i = 0; i < info->ncolumns; i++)
2165 child_attname = get_attname(relationObjectId,
2166 info->indexkeys[i]);
2167 parent_attname = get_attname(current_hint->parent_rel_oid,
2168 list_nth_int(attnums, i));
2169 if (strcmp(parent_attname, child_attname) != 0)
2180 if (pg_hint_plan_debug_print)
2182 appendStringInfoCharMacro(&buf, ' ');
2183 quote_value(&buf, indexname);
2191 rel->indexlist = list_delete_cell(rel->indexlist, cell, prev);
2198 if (pg_hint_plan_debug_print)
2201 StringInfoData rel_buf;
2203 if (OidIsValid(relationObjectId))
2204 relname = get_rel_name(relationObjectId);
2206 relname = hint->relname;
2208 initStringInfo(&rel_buf);
2209 quote_value(&rel_buf, relname);
2212 (errmsg("available indexes for %s(%s):%s",
2217 pfree(rel_buf.data);
2222 pg_hint_plan_get_relation_info(PlannerInfo *root, Oid relationObjectId,
2223 bool inhparent, RelOptInfo *rel)
2225 ScanMethodHint *hint;
2227 if (prev_get_relation_info)
2228 (*prev_get_relation_info) (root, relationObjectId, inhparent, rel);
2230 /* Do nothing if we don't have valid hint in this context. */
2236 /* store does relids of parent table. */
2237 current_hint->parent_relid = rel->relid;
2238 current_hint->parent_rel_oid = relationObjectId;
2240 else if (current_hint->parent_relid != 0)
2243 * We use the same GUC parameter if this table is the child table of a
2244 * table called pg_hint_plan_get_relation_info just before that.
2248 /* append_rel_list contains all append rels; ignore others */
2249 foreach(l, root->append_rel_list)
2251 AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
2253 /* This rel is child table. */
2254 if (appinfo->parent_relid == current_hint->parent_relid &&
2255 appinfo->child_relid == rel->relid)
2257 if (current_hint->parent_hint)
2258 delete_indexes(current_hint->parent_hint, rel,
2265 /* This rel is not inherit table. */
2266 current_hint->parent_relid = 0;
2267 current_hint->parent_rel_oid = InvalidOid;
2268 current_hint->parent_hint = NULL;
2272 * If scan method hint was given, reset GUC parameters which control
2273 * planner behavior about choosing scan methods.
2275 if ((hint = find_scan_hint(root, rel)) == NULL)
2277 set_scan_config_options(current_hint->init_scan_mask,
2278 current_hint->context);
2281 set_scan_config_options(hint->enforce_mask, current_hint->context);
2282 hint->base.state = HINT_STATE_USED;
2290 current_hint->parent_hint = hint;
2292 relation = heap_open(relationObjectId, NoLock);
2293 indexoidlist = RelationGetIndexList(relation);
2295 foreach(l, indexoidlist)
2298 Oid indexoid = lfirst_oid(l);
2299 char *indexname = get_rel_name(indexoid);
2300 bool use_index = false;
2301 Relation indexRelation;
2302 Form_pg_index index;
2304 List *attnums = NIL;
2306 foreach(lc, hint->indexnames)
2308 if (RelnameCmp(&indexname, &lfirst(lc)) == 0)
2317 indexRelation = index_open(indexoid, RowExclusiveLock);
2319 index = indexRelation->rd_index;
2321 for (i = 0; i < index->indnatts; i++)
2322 attnums = lappend_int(attnums, index->indkey.values[i]);
2324 current_hint->parent_ind_atts =
2325 lappend(current_hint->parent_ind_atts, attnums);
2327 index_close(indexRelation, NoLock);
2329 heap_close(relation, NoLock);
2332 delete_indexes(hint, rel, InvalidOid);
2336 * Return index of relation which matches given aliasname, or 0 if not found.
2337 * If same aliasname was used multiple times in a query, return -1.
2340 find_relid_aliasname(PlannerInfo *root, char *aliasname, List *initial_rels,
2346 for (i = 1; i < root->simple_rel_array_size; i++)
2350 if (root->simple_rel_array[i] == NULL)
2353 Assert(i == root->simple_rel_array[i]->relid);
2355 if (RelnameCmp(&aliasname,
2356 &root->simple_rte_array[i]->eref->aliasname) != 0)
2359 foreach(l, initial_rels)
2361 RelOptInfo *rel = (RelOptInfo *) lfirst(l);
2363 if (rel->reloptkind == RELOPT_BASEREL)
2365 if (rel->relid != i)
2370 Assert(rel->reloptkind == RELOPT_JOINREL);
2372 if (!bms_is_member(i, rel->relids))
2379 ("Relation name \"%s\" is ambiguous.",
2394 * Return join hint which matches given joinrelids.
2396 static JoinMethodHint *
2397 find_join_hint(Relids joinrelids)
2402 join_hint = current_hint->join_hint_level[bms_num_members(joinrelids)];
2404 foreach(l, join_hint)
2406 JoinMethodHint *hint = (JoinMethodHint *) lfirst(l);
2408 if (bms_equal(joinrelids, hint->joinrelids))
2416 OuterInnerJoinCreate(OuterInnerRels *outer_inner, LeadingHint *leading_hint,
2417 PlannerInfo *root, List *initial_rels, HintState *hstate, int nbaserel)
2419 OuterInnerRels *outer_rels;
2420 OuterInnerRels *inner_rels;
2421 Relids outer_relids;
2422 Relids inner_relids;
2424 JoinMethodHint *hint;
2426 if (outer_inner->relation != NULL)
2428 return bms_make_singleton(
2429 find_relid_aliasname(root, outer_inner->relation,
2431 leading_hint->base.hint_str));
2434 outer_rels = lfirst(outer_inner->outer_inner_pair->head);
2435 inner_rels = lfirst(outer_inner->outer_inner_pair->tail);
2437 outer_relids = OuterInnerJoinCreate(outer_rels,
2443 inner_relids = OuterInnerJoinCreate(inner_rels,
2450 join_relids = bms_add_members(outer_relids, inner_relids);
2452 if (bms_num_members(join_relids) > nbaserel)
2456 * If we don't have join method hint, create new one for the
2457 * join combination with all join methods are enabled.
2459 hint = find_join_hint(join_relids);
2463 * Here relnames is not set, since Relids bitmap is sufficient to
2464 * control paths of this query afterward.
2466 hint = (JoinMethodHint *) JoinMethodHintCreate(
2467 leading_hint->base.hint_str,
2469 HINT_KEYWORD_LEADING);
2470 hint->base.state = HINT_STATE_USED;
2471 hint->nrels = bms_num_members(join_relids);
2472 hint->enforce_mask = ENABLE_ALL_JOIN;
2473 hint->joinrelids = bms_copy(join_relids);
2474 hint->inner_nrels = bms_num_members(inner_relids);
2475 hint->inner_joinrelids = bms_copy(inner_relids);
2477 hstate->join_hint_level[hint->nrels] =
2478 lappend(hstate->join_hint_level[hint->nrels], hint);
2482 hint->inner_nrels = bms_num_members(inner_relids);
2483 hint->inner_joinrelids = bms_copy(inner_relids);
2490 * Transform join method hint into handy form.
2492 * - create bitmap of relids from alias names, to make it easier to check
2493 * whether a join path matches a join method hint.
2494 * - add join method hints which are necessary to enforce join order
2495 * specified by Leading hint
2498 transform_join_hints(HintState *hstate, PlannerInfo *root, int nbaserel,
2499 List *initial_rels, JoinMethodHint **join_method_hints)
2507 LeadingHint *lhint = NULL;
2510 * Create bitmap of relids from alias names for each join method hint.
2511 * Bitmaps are more handy than strings in join searching.
2513 for (i = 0; i < hstate->num_hints[HINT_TYPE_JOIN_METHOD]; i++)
2515 JoinMethodHint *hint = hstate->join_hints[i];
2518 if (!hint_state_enabled(hint) || hint->nrels > nbaserel)
2521 bms_free(hint->joinrelids);
2522 hint->joinrelids = NULL;
2524 for (j = 0; j < hint->nrels; j++)
2526 relname = hint->relnames[j];
2528 relid = find_relid_aliasname(root, relname, initial_rels,
2529 hint->base.hint_str);
2532 hint->base.state = HINT_STATE_ERROR;
2537 if (bms_is_member(relid, hint->joinrelids))
2539 hint_ereport(hint->base.hint_str,
2540 ("Relation name \"%s\" is duplicated.", relname));
2541 hint->base.state = HINT_STATE_ERROR;
2545 hint->joinrelids = bms_add_member(hint->joinrelids, relid);
2548 if (relid <= 0 || hint->base.state == HINT_STATE_ERROR)
2551 hstate->join_hint_level[hint->nrels] =
2552 lappend(hstate->join_hint_level[hint->nrels], hint);
2555 /* Do nothing if no Leading hint was supplied. */
2556 if (hstate->num_hints[HINT_TYPE_LEADING] == 0)
2560 * Decide to use Leading hint。
2562 for (i = 0; i < hstate->num_hints[HINT_TYPE_LEADING]; i++)
2564 LeadingHint *leading_hint = (LeadingHint *)hstate->leading_hint[i];
2567 if (leading_hint->base.state == HINT_STATE_ERROR)
2573 foreach(l, leading_hint->relations)
2575 relname = (char *)lfirst(l);;
2577 relid = find_relid_aliasname(root, relname, initial_rels,
2578 leading_hint->base.hint_str);
2580 leading_hint->base.state = HINT_STATE_ERROR;
2585 if (bms_is_member(relid, relids))
2587 hint_ereport(leading_hint->base.hint_str,
2588 ("Relation name \"%s\" is duplicated.", relname));
2589 leading_hint->base.state = HINT_STATE_ERROR;
2593 relids = bms_add_member(relids, relid);
2596 if (relid <= 0 || leading_hint->base.state == HINT_STATE_ERROR)
2601 hint_ereport(lhint->base.hint_str,
2602 ("Conflict %s hint.", HintTypeName[lhint->base.type]));
2603 lhint->base.state = HINT_STATE_DUPLICATION;
2605 leading_hint->base.state = HINT_STATE_USED;
2606 lhint = leading_hint;
2609 /* check to exist Leading hint marked with 'used'. */
2614 * We need join method hints which fit specified join order in every join
2615 * level. For example, Leading(A B C) virtually requires following join
2616 * method hints, if no join method hint supplied:
2618 * - level 2: NestLoop(A B), MergeJoin(A B), HashJoin(A B)
2619 * - level 3: NestLoop(A B C), MergeJoin(A B C), HashJoin(A B C)
2621 * If we already have join method hint which fits specified join order in
2622 * that join level, we leave it as-is and don't add new hints.
2626 if (lhint->outer_inner == NULL)
2628 foreach(l, lhint->relations)
2630 JoinMethodHint *hint;
2632 relname = (char *)lfirst(l);
2635 * Find relid of the relation which has given name. If we have the
2636 * name given in Leading hint multiple times in the join, nothing to
2639 relid = find_relid_aliasname(root, relname, initial_rels,
2642 /* Create bitmap of relids for current join level. */
2643 joinrelids = bms_add_member(joinrelids, relid);
2646 /* We never have join method hint for single relation. */
2651 * If we don't have join method hint, create new one for the
2652 * join combination with all join methods are enabled.
2654 hint = find_join_hint(joinrelids);
2658 * Here relnames is not set, since Relids bitmap is sufficient
2659 * to control paths of this query afterward.
2661 hint = (JoinMethodHint *) JoinMethodHintCreate(
2662 lhint->base.hint_str,
2664 HINT_KEYWORD_LEADING);
2665 hint->base.state = HINT_STATE_USED;
2666 hint->nrels = njoinrels;
2667 hint->enforce_mask = ENABLE_ALL_JOIN;
2668 hint->joinrelids = bms_copy(joinrelids);
2671 join_method_hints[njoinrels] = hint;
2673 if (njoinrels >= nbaserel)
2676 bms_free(joinrelids);
2682 * Delete all join hints which have different combination from Leading
2685 for (i = 2; i <= njoinrels; i++)
2687 list_free(hstate->join_hint_level[i]);
2689 hstate->join_hint_level[i] = lappend(NIL, join_method_hints[i]);
2694 joinrelids = OuterInnerJoinCreate(lhint->outer_inner,
2701 njoinrels = bms_num_members(joinrelids);
2702 Assert(njoinrels >= 2);
2705 * Delete all join hints which have different combination from Leading
2708 for (i = 2;i <= njoinrels; i++)
2710 if (hstate->join_hint_level[i] != NIL)
2712 ListCell *prev = NULL;
2713 ListCell *next = NULL;
2714 for(l = list_head(hstate->join_hint_level[i]); l; l = next)
2717 JoinMethodHint *hint = (JoinMethodHint *)lfirst(l);
2721 if (hint->inner_nrels == 0 &&
2722 !(bms_intersect(hint->joinrelids, joinrelids) == NULL ||
2723 bms_equal(bms_union(hint->joinrelids, joinrelids),
2726 hstate->join_hint_level[i] =
2727 list_delete_cell(hstate->join_hint_level[i], l,
2736 bms_free(joinrelids);
2739 if (hint_state_enabled(lhint))
2741 set_join_config_options(DISABLE_ALL_JOIN, current_hint->context);
2748 * set_plain_rel_pathlist
2749 * Build access paths for a plain relation (no subquery, no inheritance)
2751 * This function was copied and edited from set_plain_rel_pathlist() in
2752 * src/backend/optimizer/path/allpaths.c
2755 set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
2757 /* Consider sequential scan */
2758 #if PG_VERSION_NUM >= 90200
2759 add_path(rel, create_seqscan_path(root, rel, NULL));
2761 add_path(rel, create_seqscan_path(root, rel));
2764 /* Consider index scans */
2765 create_index_paths(root, rel);
2767 /* Consider TID scans */
2768 create_tidscan_paths(root, rel);
2770 /* Now find the cheapest of the paths for this rel */
2775 rebuild_scan_path(HintState *hstate, PlannerInfo *root, int level,
2780 foreach(l, initial_rels)
2782 RelOptInfo *rel = (RelOptInfo *) lfirst(l);
2784 ScanMethodHint *hint;
2786 /* Skip relations which we can't choose scan method. */
2787 if (rel->reloptkind != RELOPT_BASEREL || rel->rtekind != RTE_RELATION)
2790 rte = root->simple_rte_array[rel->relid];
2792 /* We can't force scan method of foreign tables */
2793 if (rte->relkind == RELKIND_FOREIGN_TABLE)
2797 * Create scan paths with GUC parameters which are at the beginning of
2798 * planner if scan method hint is not specified, otherwise use
2799 * specified hints and mark the hint as used.
2801 if ((hint = find_scan_hint(root, rel)) == NULL)
2802 set_scan_config_options(hstate->init_scan_mask,
2806 set_scan_config_options(hint->enforce_mask, hstate->context);
2807 hint->base.state = HINT_STATE_USED;
2810 list_free_deep(rel->pathlist);
2811 rel->pathlist = NIL;
2814 /* It's an "append relation", process accordingly */
2815 set_append_rel_pathlist(root, rel, rel->relid, rte);
2819 set_plain_rel_pathlist(root, rel, rte);
2824 * Restore the GUC variables we set above.
2826 set_scan_config_options(hstate->init_scan_mask, hstate->context);
2830 * wrapper of make_join_rel()
2832 * call make_join_rel() after changing enable_* parameters according to given
2836 make_join_rel_wrapper(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
2839 JoinMethodHint *hint;
2843 joinrelids = bms_union(rel1->relids, rel2->relids);
2844 hint = find_join_hint(joinrelids);
2845 bms_free(joinrelids);
2848 return pg_hint_plan_make_join_rel(root, rel1, rel2);
2850 if (hint->inner_nrels == 0)
2852 save_nestlevel = NewGUCNestLevel();
2854 set_join_config_options(hint->enforce_mask, current_hint->context);
2856 rel = pg_hint_plan_make_join_rel(root, rel1, rel2);
2857 hint->base.state = HINT_STATE_USED;
2860 * Restore the GUC variables we set above.
2862 AtEOXact_GUC(true, save_nestlevel);
2865 rel = pg_hint_plan_make_join_rel(root, rel1, rel2);
2874 add_paths_to_joinrel_wrapper(PlannerInfo *root,
2875 RelOptInfo *joinrel,
2876 RelOptInfo *outerrel,
2877 RelOptInfo *innerrel,
2879 SpecialJoinInfo *sjinfo,
2882 ScanMethodHint *scan_hint = NULL;
2884 JoinMethodHint *join_hint;
2887 if ((scan_hint = find_scan_hint(root, innerrel)) != NULL)
2889 set_scan_config_options(scan_hint->enforce_mask, current_hint->context);
2890 scan_hint->base.state = HINT_STATE_USED;
2893 joinrelids = bms_union(outerrel->relids, innerrel->relids);
2894 join_hint = find_join_hint(joinrelids);
2895 bms_free(joinrelids);
2897 if (join_hint && join_hint->inner_nrels != 0)
2899 save_nestlevel = NewGUCNestLevel();
2901 if (bms_equal(join_hint->inner_joinrelids, innerrel->relids))
2904 set_join_config_options(join_hint->enforce_mask,
2905 current_hint->context);
2907 add_paths_to_joinrel(root, joinrel, outerrel, innerrel, jointype,
2908 sjinfo, restrictlist);
2909 join_hint->base.state = HINT_STATE_USED;
2913 set_join_config_options(DISABLE_ALL_JOIN, current_hint->context);
2914 add_paths_to_joinrel(root, joinrel, outerrel, innerrel, jointype,
2915 sjinfo, restrictlist);
2919 * Restore the GUC variables we set above.
2921 AtEOXact_GUC(true, save_nestlevel);
2924 add_paths_to_joinrel(root, joinrel, outerrel, innerrel, jointype,
2925 sjinfo, restrictlist);
2927 if (scan_hint != NULL)
2928 set_scan_config_options(current_hint->init_scan_mask,
2929 current_hint->context);
2933 get_num_baserels(List *initial_rels)
2938 foreach(l, initial_rels)
2940 RelOptInfo *rel = (RelOptInfo *) lfirst(l);
2942 if (rel->reloptkind == RELOPT_BASEREL)
2944 else if (rel->reloptkind ==RELOPT_JOINREL)
2945 nbaserel+= bms_num_members(rel->relids);
2948 /* other values not expected here */
2949 elog(ERROR, "unrecognized reloptkind type: %d", rel->reloptkind);
2957 pg_hint_plan_join_search(PlannerInfo *root, int levels_needed,
2960 JoinMethodHint **join_method_hints;
2964 bool leading_hint_enable;
2967 * Use standard planner (or geqo planner) if pg_hint_plan is disabled or no
2968 * valid hint is supplied.
2972 if (prev_join_search)
2973 return (*prev_join_search) (root, levels_needed, initial_rels);
2974 else if (enable_geqo && levels_needed >= geqo_threshold)
2975 return geqo(root, levels_needed, initial_rels);
2977 return standard_join_search(root, levels_needed, initial_rels);
2980 /* We apply scan method hint rebuild scan path. */
2981 rebuild_scan_path(current_hint, root, levels_needed, initial_rels);
2984 * In the case using GEQO, only scan method hints and Set hints have
2985 * effect. Join method and join order is not controllable by hints.
2987 if (enable_geqo && levels_needed >= geqo_threshold)
2988 return geqo(root, levels_needed, initial_rels);
2990 nbaserel = get_num_baserels(initial_rels);
2991 current_hint->join_hint_level = palloc0(sizeof(List *) * (nbaserel + 1));
2992 join_method_hints = palloc0(sizeof(JoinMethodHint *) * (nbaserel + 1));
2994 leading_hint_enable = transform_join_hints(current_hint, root, nbaserel,
2995 initial_rels, join_method_hints);
2997 rel = pg_hint_plan_standard_join_search(root, levels_needed, initial_rels);
2999 for (i = 2; i <= nbaserel; i++)
3001 list_free(current_hint->join_hint_level[i]);
3003 /* free Leading hint only */
3004 if (join_method_hints[i] != NULL &&
3005 join_method_hints[i]->enforce_mask == ENABLE_ALL_JOIN)
3006 JoinMethodHintDelete(join_method_hints[i]);
3008 pfree(current_hint->join_hint_level);
3009 pfree(join_method_hints);
3011 if (leading_hint_enable)
3012 set_join_config_options(current_hint->init_join_mask,
3013 current_hint->context);
3020 * Build access paths for a base relation
3022 * This function was copied and edited from set_rel_pathlist() in
3023 * src/backend/optimizer/path/allpaths.c
3026 set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
3027 Index rti, RangeTblEntry *rte)
3029 #if PG_VERSION_NUM >= 90200
3030 if (IS_DUMMY_REL(rel))
3032 /* We already proved the relation empty, so nothing more to do */
3039 /* It's an "append relation", process accordingly */
3040 set_append_rel_pathlist(root, rel, rti, rte);
3044 if (rel->rtekind == RTE_RELATION)
3046 if (rte->relkind == RELKIND_RELATION)
3048 /* Plain relation */
3049 set_plain_rel_pathlist(root, rel, rte);
3052 elog(ERROR, "unexpected relkind: %c", rte->relkind);
3055 elog(ERROR, "unexpected rtekind: %d", (int) rel->rtekind);
3059 #define standard_join_search pg_hint_plan_standard_join_search
3060 #define join_search_one_level pg_hint_plan_join_search_one_level
3061 #define make_join_rel make_join_rel_wrapper
3064 #undef make_join_rel
3065 #define make_join_rel pg_hint_plan_make_join_rel
3066 #define add_paths_to_joinrel add_paths_to_joinrel_wrapper
3067 #include "make_join_rel.c"