OSDN Git Service

直前のコミットの修正漏れを修正した。
[pghintplan/pg_hint_plan.git] / pg_hint_plan.c
1 /*-------------------------------------------------------------------------
2  *
3  * pg_hint_plan.c
4  *                do instructions or hints to the planner using C-style block comments
5  *                of the SQL.
6  *
7  * Copyright (c) 2012, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
8  *
9  *-------------------------------------------------------------------------
10  */
11 #include "postgres.h"
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"
28
29 #ifdef PG_MODULE_MAGIC
30 PG_MODULE_MAGIC;
31 #endif
32
33 #if PG_VERSION_NUM < 90100
34 #error unsupported PostgreSQL version
35 #endif
36
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
42
43 /* hint keywords */
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"
55 #endif
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"
64
65
66 #define HINT_ARRAY_DEFAULT_INITSIZE 8
67
68 #define parse_ereport(str, detail) \
69         ereport(pg_hint_plan_parse_messages, \
70                         (errmsg("hint syntax error at or near \"%s\"", (str)), \
71                          errdetail detail))
72
73 #define skip_space(str) \
74         while (isspace(*str)) \
75                 str++;
76
77 enum
78 {
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
85 #endif
86 } SCAN_TYPE_BITS;
87
88 enum
89 {
90         ENABLE_NESTLOOP = 0x01,
91         ENABLE_MERGEJOIN = 0x02,
92         ENABLE_HASHJOIN = 0x04
93 } JOIN_TYPE_BITS;
94
95 #define ENABLE_ALL_SCAN (ENABLE_SEQSCAN | ENABLE_INDEXSCAN | ENABLE_BITMAPSCAN \
96                                                 | ENABLE_TIDSCAN)
97 #if PG_VERSION_NUM >= 90200
98 #define ENABLE_ALL_SCAN (ENABLE_SEQSCAN | ENABLE_INDEXSCAN | ENABLE_BITMAPSCAN \
99                                                 | ENABLE_TIDSCAN | ENABLE_INDEXONLYSCAN)
100 #endif
101 #define ENABLE_ALL_JOIN (ENABLE_NESTLOOP | ENABLE_MERGEJOIN | ENABLE_HASHJOIN)
102 #define DISABLE_ALL_SCAN 0
103 #define DISABLE_ALL_JOIN 0
104
105 typedef struct Hint Hint;
106 typedef struct PlanHint PlanHint;
107
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);
115
116 /* hint types */
117 #define NUM_HINT_TYPE   4
118 typedef enum HintType
119 {
120         HINT_TYPE_SCAN_METHOD,
121         HINT_TYPE_JOIN_METHOD,
122         HINT_TYPE_LEADING,
123         HINT_TYPE_SET
124 } HintType;
125
126 /* hint status */
127 typedef enum HintStatus
128 {
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
133                                                                  * it) */
134 } HintStatus;
135
136 #define hint_state_enabled(hint) ((hint)->base.state == HINT_STATE_NOTUSED || \
137                                                                   (hint)->base.state == HINT_STATE_USED)
138
139 /* common data for all hints. */
140 struct Hint
141 {
142         const char                 *hint_str;           /* must not do pfree */
143         const char                 *keyword;            /* must not do pfree */
144         HintType                        type;
145         HintStatus                      state;
146         HintDeleteFunction      delete_func;
147         HintDumpFunction        dump_func;
148         HintCmpFunction         cmp_func;
149         HintParseFunction       parser_func;
150 };
151
152 /* scan method hints */
153 typedef struct ScanMethodHint
154 {
155         Hint                    base;
156         char               *relname;
157         List               *indexnames;
158         unsigned char   enforce_mask;
159 } ScanMethodHint;
160
161 /* join method hints */
162 typedef struct JoinMethodHint
163 {
164         Hint                    base;
165         int                             nrels;
166         char              **relnames;
167         unsigned char   enforce_mask;
168         Relids                  joinrelids;
169 } JoinMethodHint;
170
171 /* join order hints */
172 typedef struct LeadingHint
173 {
174         Hint    base;
175         List   *relations;              /* relation names specified in Leading hint */
176 } LeadingHint;
177
178 /* change a run-time parameter hints */
179 typedef struct SetHint
180 {
181         Hint    base;
182         char   *name;                           /* name of variable */
183         char   *value;
184 } SetHint;
185
186 /*
187  * Describes a context of hint processing.
188  */
189 struct PlanHint
190 {
191         char               *hint_str;                   /* original hint string */
192
193         /* all hint */
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 */
197
198         /* # of each hints */
199         int                             num_hints[NUM_HINT_TYPE];
200
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 */
205
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;
210
211         /* for Leading hints */
212         LeadingHint       **leading_hints;              /* parsed Leading hints */
213
214         /* for Set hints */
215         SetHint           **set_hints;                  /* parsed Set hints */
216         GucContext              context;                        /* which GUC parameters can we set? */
217 };
218
219 /*
220  * Describes a hint parser module which is bound with particular hint keyword.
221  */
222 typedef struct HintParser
223 {
224         char                       *keyword;
225         HintCreateFunction      create_func;
226 } HintParser;
227
228 /* Module callbacks */
229 void            _PG_init(void);
230 void            _PG_fini(void);
231
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,
241                                                                                         int levels_needed,
242                                                                                         List *initial_rels);
243
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,
267                                                                 const char *str);
268
269 RelOptInfo *pg_hint_plan_standard_join_search(PlannerInfo *root,
270                                                                                           int levels_needed,
271                                                                                           List *initial_rels);
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,
276                                                                                   RelOptInfo *old_rel,
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,
282                                                                    RangeTblEntry *rte);
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,
288                                                                            RelOptInfo *rel2);
289
290
291 /* GUC variables */
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;
295
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},
303         {"log", LOG, false},
304         {"info", INFO, false},
305         {"notice", NOTICE, false},
306         {"warning", WARNING, false},
307         {"error", ERROR, false},
308         /*
309          * {"fatal", FATAL, true},
310          * {"panic", PANIC, true},
311          */
312         {NULL, 0, false}
313 };
314
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;
320
321 /* フック関数をまたがって使用する情報を管理する */
322 static PlanHint *global = NULL;
323
324 /*
325  * EXECUTEコマンド実行時に、ステートメント名を格納する。
326  * その他のコマンドの場合は、NULLに設定する。
327  */
328 static char        *stmt_name = NULL;
329
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},
342 #endif
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},
351         {NULL, NULL}
352 };
353
354 /*
355  * Module load callbacks
356  */
357 void
358 _PG_init(void)
359 {
360         /* Define custom GUC variables. */
361         DefineCustomBoolVariable("pg_hint_plan.enable",
362                          "Instructions or hints to the planner using block comments.",
363                                                          NULL,
364                                                          &pg_hint_plan_enable,
365                                                          true,
366                                                          PGC_USERSET,
367                                                          0,
368                                                          NULL,
369                                                          NULL,
370                                                          NULL);
371
372         DefineCustomBoolVariable("pg_hint_plan.debug_print",
373                                                          "Logs each query's parse results of the hint.",
374                                                          NULL,
375                                                          &pg_hint_plan_debug_print,
376                                                          false,
377                                                          PGC_USERSET,
378                                                          0,
379                                                          NULL,
380                                                          NULL,
381                                                          NULL);
382
383         DefineCustomEnumVariable("pg_hint_plan.parse_messages",
384                                                          "Messege level of the parse error.",
385                                                          NULL,
386                                                          &pg_hint_plan_parse_messages,
387                                                          INFO,
388                                                          parse_messages_level_options,
389                                                          PGC_USERSET,
390                                                          0,
391                                                          NULL,
392                                                          NULL,
393                                                          NULL);
394
395         /* Install hooks. */
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;
404 }
405
406 /*
407  * Module unload callback
408  * XXX never called
409  */
410 void
411 _PG_fini(void)
412 {
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;
418 }
419
420 static void
421 ProcessUtility_hook_error_callback(void *arg)
422 {
423         stmt_name = NULL;
424 }
425
426 void
427 pg_hint_plan_ProcessUtility(Node *parsetree, const char *queryString,
428                                                         ParamListInfo params, bool isTopLevel,
429                                                         DestReceiver *dest, char *completionTag)
430 {
431         Node                               *node;
432         ErrorContextCallback    errcontext;
433
434         if (!pg_hint_plan_enable)
435         {
436                 if (prev_ProcessUtility)
437                         (*prev_ProcessUtility) (parsetree, queryString, params,
438                                                                         isTopLevel, dest, completionTag);
439                 else
440                         standard_ProcessUtility(parsetree, queryString, params,
441                                                                         isTopLevel, dest, completionTag);
442
443                 return;
444         }
445
446         node = parsetree;
447         if (IsA(node, ExplainStmt))
448         {
449                 /*
450                  * EXPLAIN対象のクエリのパースツリーを取得する
451                  */
452                 ExplainStmt        *stmt;
453                 Query              *query;
454
455                 stmt = (ExplainStmt *) node;
456
457                 Assert(IsA(stmt->query, Query));
458                 query = (Query *) stmt->query;
459
460                 if (query->commandType == CMD_UTILITY && query->utilityStmt != NULL)
461                         node = query->utilityStmt;
462         }
463
464         /*
465          * EXECUTEコマンドならば、PREPARE時に指定されたクエリ文字列を取得し、ヒント
466          * 句の候補として設定する
467          */
468         if (IsA(node, ExecuteStmt))
469         {
470                 ExecuteStmt        *stmt;
471
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;
477
478                 stmt = (ExecuteStmt *) node;
479                 stmt_name = stmt->name;
480         }
481
482         if (prev_ProcessUtility)
483                 (*prev_ProcessUtility) (parsetree, queryString, params,
484                                                                 isTopLevel, dest, completionTag);
485         else
486                 standard_ProcessUtility(parsetree, queryString, params,
487                                                                 isTopLevel, dest, completionTag);
488
489         if (stmt_name)
490         {
491                 stmt_name = NULL;
492
493                 /* Remove error callback. */
494                 error_context_stack = errcontext.previous;
495         }
496 }
497
498 static Hint *
499 ScanMethodHintCreate(const char *hint_str, const char *keyword)
500 {
501         ScanMethodHint *hint;
502
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;
515
516         return (Hint *) hint;
517 }
518
519 static void
520 ScanMethodHintDelete(ScanMethodHint *hint)
521 {
522         if (!hint)
523                 return;
524
525         if (hint->relname)
526                 pfree(hint->relname);
527         list_free_deep(hint->indexnames);
528         pfree(hint);
529 }
530
531 static void
532 dump_quote_value(StringInfo buf, const char *value)
533 {
534         bool            need_quote = false;
535         const char *str;
536
537         for (str = value; *str != '\0'; str++)
538         {
539                 if (isspace(*str) || *str == ')' || *str == '"')
540                 {
541                         need_quote = true;
542                         appendStringInfoCharMacro(buf, '"');
543                         break;
544                 }
545         }
546
547         for (str = value; *str != '\0'; str++)
548         {
549                 if (*str == '"')
550                         appendStringInfoCharMacro(buf, '"');
551
552                 appendStringInfoCharMacro(buf, *str);
553         }
554
555         if (need_quote)
556                 appendStringInfoCharMacro(buf, '"');
557 }
558
559 static void
560 ScanMethodHintDump(ScanMethodHint *hint, StringInfo buf)
561 {
562         ListCell   *l;
563
564         appendStringInfo(buf, "%s(", hint->base.keyword);
565         dump_quote_value(buf, hint->relname);
566         foreach(l, hint->indexnames)
567         {
568                 appendStringInfoCharMacro(buf, ' ');
569                 dump_quote_value(buf, (char *) lfirst(l));
570         }
571         appendStringInfoString(buf, ")\n");
572 }
573
574 static Hint *
575 JoinMethodHintCreate(const char *hint_str, const char *keyword)
576 {
577         JoinMethodHint *hint;
578
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;
588         hint->nrels = 0;
589         hint->relnames = NULL;
590         hint->enforce_mask = 0;
591         hint->joinrelids = NULL;
592
593         return (Hint *) hint;
594 }
595
596 static void
597 JoinMethodHintDelete(JoinMethodHint *hint)
598 {
599         if (!hint)
600                 return;
601
602         if (hint->relnames)
603         {
604                 int     i;
605
606                 for (i = 0; i < hint->nrels; i++)
607                         pfree(hint->relnames[i]);
608                 pfree(hint->relnames);
609         }
610         bms_free(hint->joinrelids);
611         pfree(hint);
612 }
613
614 static void
615 JoinMethodHintDump(JoinMethodHint *hint, StringInfo buf)
616 {
617         int     i;
618
619         appendStringInfo(buf, "%s(", hint->base.keyword);
620         dump_quote_value(buf, hint->relnames[0]);
621         for (i = 1; i < hint->nrels; i++)
622         {
623                 appendStringInfoCharMacro(buf, ' ');
624                 dump_quote_value(buf, hint->relnames[i]);
625         }
626         appendStringInfoString(buf, ")\n");
627
628 }
629
630 static Hint *
631 LeadingHintCreate(const char *hint_str, const char *keyword)
632 {
633         LeadingHint        *hint;
634
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;
645
646         return (Hint *) hint;
647 }
648
649 static void
650 LeadingHintDelete(LeadingHint *hint)
651 {
652         if (!hint)
653                 return;
654
655         list_free_deep(hint->relations);
656         pfree(hint);
657 }
658
659 static void
660 LeadingHintDump(LeadingHint *hint, StringInfo buf)
661 {
662         bool            is_first;
663         ListCell   *l;
664
665         appendStringInfo(buf, "%s(", HINT_LEADING);
666         is_first = true;
667         foreach(l, hint->relations)
668         {
669                 if (is_first)
670                         is_first = false;
671                 else
672                         appendStringInfoCharMacro(buf, ' ');
673
674                 dump_quote_value(buf, (char *) lfirst(l));
675         }
676
677         appendStringInfoString(buf, ")\n");
678 }
679
680 static Hint *
681 SetHintCreate(const char *hint_str, const char *keyword)
682 {
683         SetHint    *hint;
684
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;
694         hint->name = NULL;
695         hint->value = NULL;
696
697         return (Hint *) hint;
698 }
699
700 static void
701 SetHintDelete(SetHint *hint)
702 {
703         if (!hint)
704                 return;
705
706         if (hint->name)
707                 pfree(hint->name);
708         if (hint->value)
709                 pfree(hint->value);
710         pfree(hint);
711 }
712
713 static void
714 SetHintDump(SetHint *hint, StringInfo buf)
715 {
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");
721 }
722
723 static PlanHint *
724 PlanHintCreate(void)
725 {
726         PlanHint   *hint;
727
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;
743
744         return hint;
745 }
746
747 static void
748 PlanHintDelete(PlanHint *hint)
749 {
750         int                     i;
751
752         if (!hint)
753                 return;
754
755         if (hint->hint_str)
756                 pfree(hint->hint_str);
757
758         for (i = 0; i < hint->num_hints[HINT_TYPE_SCAN_METHOD]; i++)
759                 hint->all_hints[i]->delete_func(hint->all_hints[i]);
760         if (hint->all_hints)
761                 pfree(hint->all_hints);
762 }
763
764 static void
765 all_hint_dump(PlanHint *hint, StringInfo buf, const char *title,
766                           HintStatus state)
767 {
768         int     i;
769
770         appendStringInfo(buf, "%s:\n", title);
771         for (i = 0; i < hint->nall_hints; i++)
772         {
773                 if (hint->all_hints[i]->state != state)
774                         continue;
775
776                 hint->all_hints[i]->dump_func(hint->all_hints[i], buf);
777         }
778 }
779
780 static void
781 PlanHintDump(PlanHint *hint)
782 {
783         StringInfoData  buf;
784
785         if (!hint)
786         {
787                 elog(LOG, "pg_hint_plan:\nno hint");
788                 return;
789         }
790
791         initStringInfo(&buf);
792
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);
798
799         elog(LOG, "%s", buf.data);
800
801         pfree(buf.data);
802 }
803
804 static int
805 RelnameCmp(const void *a, const void *b)
806 {
807         const char *relnamea = *((const char **) a);
808         const char *relnameb = *((const char **) b);
809
810         return strcmp(relnamea, relnameb);
811 }
812
813 static int
814 ScanMethodHintCmp(const ScanMethodHint *a, const ScanMethodHint *b)
815 {
816         return RelnameCmp(&a->relname, &b->relname);
817 }
818
819 static int
820 JoinMethodHintCmp(const JoinMethodHint *a, const JoinMethodHint *b)
821 {
822         int     i;
823
824         if (a->nrels != b->nrels)
825                 return a->nrels - b->nrels;
826
827         for (i = 0; i < a->nrels; i++)
828         {
829                 int     result;
830                 if ((result = RelnameCmp(&a->relnames[i], &b->relnames[i])) != 0)
831                         return result;
832         }
833
834         return 0;
835 }
836
837 static int
838 LeadingHintCmp(const LeadingHint *a, const LeadingHint *b)
839 {
840         return 0;
841 }
842
843 static int
844 SetHintCmp(const SetHint *a, const SetHint *b)
845 {
846         return strcmp(a->name, b->name);
847 }
848
849 static int
850 AllHintCmp(const void *a, const void *b, bool order)
851 {
852         const Hint *hinta = *((const Hint **) a);
853         const Hint *hintb = *((const Hint **) b);
854         int                     result = 0;
855
856         if (hinta->type != hintb->type)
857                 return hinta->type - hintb->type;
858
859         if ((result = hinta->cmp_func(hinta, hintb)) != 0 || !order)
860                 return result;
861
862         /* ヒント句で指定した順を返す */
863         return hinta->hint_str - hintb->hint_str;
864 }
865
866 static int
867 AllHintCmpIsOrder(const void *a, const void *b)
868 {
869         return AllHintCmp(a, b, true);
870 }
871
872 #if PG_VERSION_NUM < 90200
873 static int
874 set_config_option_wrapper(const char *name, const char *value,
875                                                   GucContext context, GucSource source,
876                                                   GucAction action, bool changeVal, int elevel)
877 {
878         int                             result = 0;
879         MemoryContext   ccxt = CurrentMemoryContext;
880
881         PG_TRY();
882         {
883                 result = set_config_option(name, value, context, source,
884                                                                    action, changeVal);
885         }
886         PG_CATCH();
887         {
888                 ErrorData          *errdata;
889                 MemoryContext   ecxt;
890
891                 if (elevel >= ERROR)
892                         PG_RE_THROW();
893
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);
901
902                 MemoryContextSwitchTo(ecxt);
903         }
904         PG_END_TRY();
905
906         return result;
907 }
908
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)
913 #endif
914
915 static int
916 set_config_options(SetHint **options, int noptions, GucContext context)
917 {
918         int     i;
919         int     save_nestlevel;
920
921         save_nestlevel = NewGUCNestLevel();
922
923         for (i = 0; i < noptions; i++)
924         {
925                 SetHint    *hint = options[i];
926                 int                     result;
927
928                 if (!hint_state_enabled(hint))
929                         continue;
930
931                 result = set_config_option(hint->name, hint->value, context,
932                                                                    PGC_S_SESSION, GUC_ACTION_SAVE, true,
933                                                                    pg_hint_plan_parse_messages);
934                 if (result != 0)
935                         hint->base.state = HINT_STATE_USED;
936                 else
937                         hint->base.state = HINT_STATE_ERROR;
938         }
939
940         return save_nestlevel;
941 }
942
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)
947
948 static void
949 set_scan_config_options(unsigned char enforce_mask, GucContext context)
950 {
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);
957 #endif
958 }
959
960 static void
961 set_join_config_options(unsigned char enforce_mask, GucContext context)
962 {
963         SET_CONFIG_OPTION("enable_nestloop", ENABLE_NESTLOOP);
964         SET_CONFIG_OPTION("enable_mergejoin", ENABLE_MERGEJOIN);
965         SET_CONFIG_OPTION("enable_hashjoin", ENABLE_HASHJOIN);
966 }
967
968 /*
969  * parse functions
970  */
971
972 static const char *
973 parse_keyword(const char *str, StringInfo buf)
974 {
975         skip_space(str);
976
977         while (!isspace(*str) && *str != '(' && *str != '\0')
978                 appendStringInfoCharMacro(buf, *str++);
979
980         return str;
981 }
982
983 static const char *
984 skip_opened_parenthesis(const char *str)
985 {
986         skip_space(str);
987
988         if (*str != '(')
989         {
990                 parse_ereport(str, ("Opened parenthesis is necessary."));
991                 return NULL;
992         }
993
994         str++;
995
996         return str;
997 }
998
999 static const char *
1000 skip_closed_parenthesis(const char *str)
1001 {
1002         skip_space(str);
1003
1004         if (*str != ')')
1005         {
1006                 parse_ereport(str, ("Closed parenthesis is necessary."));
1007                 return NULL;
1008         }
1009
1010         str++;
1011
1012         return str;
1013 }
1014
1015 /*
1016  * 二重引用符で囲まれているかもしれないトークンを読み取り word 引数に palloc
1017  * で確保したバッファに格納してそのポインタを返す。
1018  *
1019  * 正常にパースできた場合は残りの文字列の先頭位置を、異常があった場合は NULL を
1020  * 返す。
1021  */
1022 static const char *
1023 parse_quote_value(const char *str, char **word, char *value_type)
1024 {
1025         StringInfoData  buf;
1026         bool                    in_quote;
1027
1028         /* 先頭のスペースは読み飛ばす。 */
1029         skip_space(str);
1030
1031         initStringInfo(&buf);
1032         if (*str == '"')
1033         {
1034                 str++;
1035                 in_quote = true;
1036         }
1037         else
1038                 in_quote = false;
1039
1040         while (true)
1041         {
1042                 if (in_quote)
1043                 {
1044                         /* 二重引用符が閉じられていない場合はパース中断 */
1045                         if (*str == '\0')
1046                         {
1047                                 pfree(buf.data);
1048                                 parse_ereport(str, ("Unterminated quoted %s.", value_type));
1049                                 return NULL;
1050                         }
1051
1052                         /*
1053                          * エスケープ対象のダブルクウォートをスキップする。
1054                          * もしブロックコメントの開始文字列や終了文字列もオブジェクト名とし
1055                          * て使用したい場合は、/ と * もエスケープ対象とすることで使用できる
1056                          * が、処理対象としていない。もしテーブル名にこれらの文字が含まれる
1057                          * 場合は、エイリアスを指定する必要がある。
1058                          */
1059                         if (*str == '"')
1060                         {
1061                                 str++;
1062                                 if (*str != '"')
1063                                         break;
1064                         }
1065                 }
1066                 else if (isspace(*str) || *str == ')' || *str == '"' || *str == '\0')
1067                         break;
1068
1069                 appendStringInfoCharMacro(&buf, *str++);
1070         }
1071
1072         if (buf.len == 0)
1073         {
1074                 pfree(buf.data);
1075                 parse_ereport(str, ("%s is necessary.", value_type));
1076                 return NULL;
1077         }
1078
1079         *word = buf.data;
1080
1081         return str;
1082 }
1083
1084 static void
1085 parse_hints(PlanHint *plan, Query *parse, const char *str)
1086 {
1087         StringInfoData  buf;
1088         char               *head;
1089
1090         initStringInfo(&buf);
1091         while (*str != '\0')
1092         {
1093                 const HintParser *parser;
1094
1095                 /* in error message, we output the comment including the keyword. */
1096                 head = (char *) str;
1097
1098                 /* parse only the keyword of the hint. */
1099                 resetStringInfo(&buf);
1100                 str = parse_keyword(str, &buf);
1101
1102                 for (parser = parsers; parser->keyword != NULL; parser++)
1103                 {
1104                         char   *keyword = parser->keyword;
1105                         Hint   *hint;
1106
1107                         if (strcasecmp(buf.data, keyword) != 0)
1108                                 continue;
1109
1110                         hint = parser->create_func(head, keyword);
1111
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)
1116                         {
1117                                 hint->delete_func(hint);
1118                                 pfree(buf.data);
1119                                 return;
1120                         }
1121
1122                         /*
1123                          * 出来上がったヒント情報を追加。スロットが足りない場合は二倍に拡張する。
1124                          */
1125                         if (plan->nall_hints == 0)
1126                         {
1127                                 plan->max_all_hints = HINT_ARRAY_DEFAULT_INITSIZE;
1128                                 plan->all_hints = palloc(sizeof(Hint *) * plan->max_all_hints);
1129                         }
1130                         else if (plan->nall_hints == plan->max_all_hints)
1131                         {
1132                                 plan->max_all_hints *= 2;
1133                                 plan->all_hints = repalloc(plan->all_hints,
1134                                                                                 sizeof(Hint *) * plan->max_all_hints);
1135                         }
1136
1137                         plan->all_hints[plan->nall_hints] = hint;
1138                         plan->nall_hints++;
1139
1140                         skip_space(str);
1141
1142                         break;
1143                 }
1144
1145                 if (parser->keyword == NULL)
1146                 {
1147                         parse_ereport(head, ("Keyword \"%s\" does not exist.", buf.data));
1148                         pfree(buf.data);
1149                         return;
1150                 }
1151         }
1152
1153         pfree(buf.data);
1154 }
1155
1156 /*
1157  * Do basic parsing of the query head comment.
1158  */
1159 static PlanHint *
1160 parse_head_comment(Query *parse)
1161 {
1162         const char *p;
1163         char       *head;
1164         char       *tail;
1165         int                     len;
1166         int                     i;
1167         PlanHint   *plan;
1168
1169         /* get client-supplied query string. */
1170         if (stmt_name)
1171         {
1172                 PreparedStatement  *entry;
1173
1174                 entry = FetchPreparedStatement(stmt_name, true);
1175                 p = entry->plansource->query_string;
1176         }
1177         else
1178                 p = debug_query_string;
1179
1180         if (p == NULL)
1181                 return NULL;
1182
1183         /* extract query head comment. */
1184         len = strlen(HINT_START);
1185         skip_space(p);
1186         if (strncmp(p, HINT_START, len))
1187                 return NULL;
1188
1189         head = (char *) p;
1190         p += len;
1191         skip_space(p);
1192
1193         /* find hint end keyword. */
1194         if ((tail = strstr(p, HINT_END)) == NULL)
1195         {
1196                 parse_ereport(head, ("Unterminated block comment."));
1197                 return NULL;
1198         }
1199
1200         /* 入れ子にしたブロックコメントはサポートしない */
1201         if ((head = strstr(p, BLOCK_COMMENT_START)) != NULL && head < tail)
1202                 parse_ereport(head, ("Block comments nest doesn't supported."));
1203
1204         /* ヒント句部分を切り出す */
1205         len = tail - p;
1206         head = palloc(len + 1);
1207         memcpy(head, p, len);
1208         head[len] = '\0';
1209         p = head;
1210
1211         plan = PlanHintCreate();
1212         plan->hint_str = head;
1213
1214         /* parse each hint. */
1215         parse_hints(plan, parse, p);
1216
1217         /* When nothing specified a hint, we free PlanHint and returns NULL. */
1218         if (plan->nall_hints == 0)
1219         {
1220                 PlanHintDelete(plan);
1221                 return NULL;
1222         }
1223
1224         /* パースしたヒントを並び替える */
1225         qsort(plan->all_hints, plan->nall_hints, sizeof(Hint *), AllHintCmpIsOrder);
1226
1227         /* 重複したヒントを検索する */
1228         for (i = 0; i < plan->nall_hints; i++)
1229         {
1230                 Hint   *hint = plan->all_hints[i];
1231
1232                 plan->num_hints[hint->type]++;
1233
1234                 if (i + 1 >= plan->nall_hints)
1235                         break;
1236
1237                 if (AllHintCmp(plan->all_hints + i, plan->all_hints + i + 1, false) ==
1238                         0)
1239                 {
1240                         const char *HintTypeName[] = {"scan method", "join method",
1241                                                                                   "leading", "set"};
1242
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;
1246                 }
1247         }
1248
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];
1259
1260         return plan;
1261 }
1262
1263 /*
1264  * スキャン方式ヒントのカッコ内をパースする
1265  */
1266 static const char *
1267 ScanMethodHintParse(ScanMethodHint *hint, PlanHint *plan, Query *parse,
1268                                         const char *str)
1269 {
1270         const char *keyword = hint->base.keyword;
1271
1272         /*
1273          * スキャン方式のヒントでリレーション名が読み取れない場合はヒント無効
1274          */
1275         if ((str = parse_quote_value(str, &hint->relname, "ralation name")) == NULL)
1276                 return NULL;
1277
1278         skip_space(str);
1279
1280         /*
1281          * インデックスリストを受け付けるヒントであれば、インデックス参照をパース
1282          * する。
1283          */
1284         if (strcmp(keyword, HINT_INDEXSCAN) == 0 ||
1285 #if PG_VERSION_NUM >= 90200
1286                 strcmp(keyword, HINT_INDEXONLYSCAN) == 0 ||
1287 #endif
1288                 strcmp(keyword, HINT_BITMAPSCAN) == 0)
1289         {
1290                 while (*str != ')' && *str != '\0')
1291                 {
1292                         char       *indexname;
1293
1294                         str = parse_quote_value(str, &indexname, "index name");
1295                         if (str == NULL)
1296                                 return NULL;
1297
1298                         hint->indexnames = lappend(hint->indexnames, indexname);
1299                         skip_space(str);
1300                 }
1301         }
1302
1303         /*
1304          * ヒントごとに決まっている許容スキャン方式をビットマスクとして設定
1305          */
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;
1322         else
1323         {
1324                 parse_ereport(str, ("Unrecognized hint keyword \"%s\".", keyword));
1325                 return NULL;
1326         }
1327
1328         return str;
1329 }
1330
1331 static const char *
1332 JoinMethodHintParse(JoinMethodHint *hint, PlanHint *plan, Query *parse,
1333                                         const char *str)
1334 {
1335         char       *relname;
1336         const char *keyword = hint->base.keyword;
1337
1338         skip_space(str);
1339
1340         hint->relnames = palloc(sizeof(char *));
1341
1342         while ((str = parse_quote_value(str, &relname, "table name")) != NULL)
1343         {
1344                 hint->nrels++;
1345                 hint->relnames = repalloc(hint->relnames, sizeof(char *) * hint->nrels);
1346                 hint->relnames[hint->nrels - 1] = relname;
1347
1348                 skip_space(str);
1349                 if (*str == ')')
1350                         break;
1351         }
1352
1353         if (str == NULL)
1354                 return NULL;
1355
1356         /* Join 対象のテーブルは最低でも2つ指定する必要がある */
1357         if (hint->nrels < 2)
1358         {
1359                 parse_ereport(str, ("Specified relation more than two."));
1360                 hint->base.state = HINT_STATE_ERROR;
1361         }
1362
1363         /* テーブル名順にソートする */
1364         qsort(hint->relnames, hint->nrels, sizeof(char *), RelnameCmp);
1365
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;
1378         else
1379         {
1380                 parse_ereport(str, ("Unrecognized hint keyword \"%s\".", keyword));
1381                 return NULL;
1382         }
1383
1384         return str;
1385 }
1386
1387 static const char *
1388 LeadingHintParse(LeadingHint *hint, PlanHint *plan, Query *parse,
1389                                  const char *str)
1390 {
1391         skip_space(str);
1392
1393         while (*str != ')')
1394         {
1395                 char   *relname;
1396
1397                 if ((str = parse_quote_value(str, &relname, "relation name")) == NULL)
1398                         return NULL;
1399
1400                 hint->relations = lappend(hint->relations, relname);
1401
1402                 skip_space(str);
1403         }
1404
1405         /* テーブル指定が2つ未満の場合は、Leading ヒントはエラーとする */
1406         if (list_length(hint->relations) < 2)
1407         {
1408                 parse_ereport(hint->base.hint_str,
1409                                           ("In %s hint, specified relation name 2 or more.",
1410                                            HINT_LEADING));
1411                 hint->base.state = HINT_STATE_ERROR;
1412         }
1413
1414         return str;
1415 }
1416
1417 static const char *
1418 SetHintParse(SetHint *hint, PlanHint *plan, Query *parse, const char *str)
1419 {
1420         if ((str = parse_quote_value(str, &hint->name, "parameter name")) == NULL ||
1421                 (str = parse_quote_value(str, &hint->value, "parameter value")) == NULL)
1422                 return NULL;
1423
1424         return str;
1425 }
1426
1427 static PlannedStmt *
1428 pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
1429 {
1430         int                             save_nestlevel;
1431         PlannedStmt        *result;
1432
1433         /*
1434          * hintが指定されない、または空のhintを指定された場合は通常のparser処理をお
1435          * こなう。
1436          * 他のフック関数で実行されるhint処理をスキップするために、global 変数をNULL
1437          * に設定しておく。
1438          */
1439         if (!pg_hint_plan_enable || (global = parse_head_comment(parse)) == NULL)
1440         {
1441                 global = NULL;
1442
1443                 if (prev_planner)
1444                         return (*prev_planner) (parse, cursorOptions, boundParams);
1445                 else
1446                         return standard_planner(parse, cursorOptions, boundParams);
1447         }
1448
1449         /* Set hint で指定されたGUCパラメータを設定する */
1450         save_nestlevel = set_config_options(global->set_hints,
1451                                                                                 global->num_hints[HINT_TYPE_SET],
1452                                                                                 global->context);
1453
1454         if (enable_seqscan)
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;
1460         if (enable_tidscan)
1461                 global->init_scan_mask |= ENABLE_TIDSCAN;
1462 #if PG_VERSION_NUM >= 90200
1463         if (enable_indexonlyscan)
1464                 global->init_scan_mask |= ENABLE_INDEXONLYSCAN;
1465 #endif
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;
1472
1473         if (prev_planner)
1474                 result = (*prev_planner) (parse, cursorOptions, boundParams);
1475         else
1476                 result = standard_planner(parse, cursorOptions, boundParams);
1477
1478         /*
1479          * Restore the GUC variables we set above.
1480          */
1481         AtEOXact_GUC(true, save_nestlevel);
1482
1483         /*
1484          * Print hint if debugging.
1485          */
1486         if (pg_hint_plan_debug_print)
1487                 PlanHintDump(global);
1488
1489         PlanHintDelete(global);
1490         global = NULL;
1491
1492         return result;
1493 }
1494
1495 /*
1496  * aliasnameと一致するSCANヒントを探す
1497  */
1498 static ScanMethodHint *
1499 find_scan_hint(PlannerInfo *root, RelOptInfo *rel)
1500 {
1501         RangeTblEntry  *rte;
1502         int                             i;
1503
1504         /*
1505          * RELOPT_BASEREL でなければ、scan method ヒントが適用しない。
1506          * 子テーブルの場合はRELOPT_OTHER_MEMBER_RELとなるが、サポート対象外とする。
1507          * また、通常のリレーション以外は、スキャン方式を選択できない。
1508          */
1509         if (rel->reloptkind != RELOPT_BASEREL || rel->rtekind != RTE_RELATION)
1510                 return NULL;
1511
1512         rte = root->simple_rte_array[rel->relid];
1513
1514         /* 外部表はスキャン方式が選択できない。 */
1515         if (rte->relkind == RELKIND_FOREIGN_TABLE)
1516                 return NULL;
1517
1518         /*
1519          * スキャン方式のヒントのリストから、検索対象のリレーションと名称が一致する
1520          * ヒントを検索する。
1521          */
1522         for (i = 0; i < global->num_hints[HINT_TYPE_SCAN_METHOD]; i++)
1523         {
1524                 ScanMethodHint *hint = global->scan_hints[i];
1525
1526                 /* すでに無効となっているヒントは検索対象にしない。 */
1527                 if (!hint_state_enabled(hint))
1528                         continue;
1529
1530                 if (RelnameCmp(&rte->eref->aliasname, &hint->relname) == 0)
1531                         return hint;
1532         }
1533
1534         return NULL;
1535 }
1536
1537 static void
1538 pg_hint_plan_get_relation_info(PlannerInfo *root, Oid relationObjectId,
1539                                                            bool inhparent, RelOptInfo *rel)
1540 {
1541         ScanMethodHint *hint;
1542         ListCell           *cell;
1543         ListCell           *prev;
1544         ListCell           *next;
1545
1546         if (prev_get_relation_info)
1547                 (*prev_get_relation_info) (root, relationObjectId, inhparent, rel);
1548
1549         /* 有効なヒントが指定されなかった場合は処理をスキップする。 */
1550         if (!global)
1551                 return;
1552
1553         if (inhparent)
1554         {
1555                 /* cache does relids of parent table */
1556                 global->parent_relid = rel->relid;
1557         }
1558         else if (global->parent_relid != 0)
1559         {
1560                 /* If this rel is an appendrel child, */
1561                 ListCell   *l;
1562
1563                 /* append_rel_list contains all append rels; ignore others */
1564                 foreach(l, root->append_rel_list)
1565                 {
1566                         AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
1567
1568                         /* This rel is child table. */
1569                         if (appinfo->parent_relid == global->parent_relid)
1570                                 return;
1571                 }
1572
1573                 /* This rel is not inherit table. */
1574                 global->parent_relid = 0;
1575         }
1576
1577         /* scan hint が指定されない場合は、GUCパラメータをリセットする。 */
1578         if ((hint = find_scan_hint(root, rel)) == NULL)
1579         {
1580                 set_scan_config_options(global->init_scan_mask, global->context);
1581                 return;
1582         }
1583         set_scan_config_options(hint->enforce_mask, global->context);
1584         hint->base.state = HINT_STATE_USED;
1585
1586         /* インデックスを全て削除し、スキャンに使えなくする */
1587         if (hint->enforce_mask == ENABLE_SEQSCAN ||
1588                 hint->enforce_mask == ENABLE_TIDSCAN)
1589         {
1590                 list_free_deep(rel->indexlist);
1591                 rel->indexlist = NIL;
1592                 hint->base.state = HINT_STATE_USED;
1593
1594                 return;
1595         }
1596
1597         /* 後でパスを作り直すため、ここではなにもしない */
1598         if (hint->indexnames == NULL)
1599                 return;
1600
1601         /* 指定されたインデックスのみをのこす */
1602         prev = NULL;
1603         for (cell = list_head(rel->indexlist); cell; cell = next)
1604         {
1605                 IndexOptInfo   *info = (IndexOptInfo *) lfirst(cell);
1606                 char               *indexname = get_rel_name(info->indexoid);
1607                 ListCell           *l;
1608                 bool                    use_index = false;
1609
1610                 next = lnext(cell);
1611
1612                 foreach(l, hint->indexnames)
1613                 {
1614                         if (RelnameCmp(&indexname, &lfirst(l)) == 0)
1615                         {
1616                                 use_index = true;
1617                                 break;
1618                         }
1619                 }
1620
1621                 if (!use_index)
1622                         rel->indexlist = list_delete_cell(rel->indexlist, cell, prev);
1623                 else
1624                         prev = cell;
1625
1626                 pfree(indexname);
1627         }
1628 }
1629
1630 /*
1631  * aliasnameがクエリ中に指定した別名と一致する場合は、そのインデックスを返し、一致
1632  * する別名がなければ0を返す。
1633  * aliasnameがクエリ中に複数回指定された場合は、-1を返す。
1634  */
1635 static int
1636 find_relid_aliasname(PlannerInfo *root, char *aliasname, List *initial_rels,
1637                                          const char *str)
1638 {
1639         int     i;
1640         int     found = 0;
1641
1642         for (i = 1; i < root->simple_rel_array_size; i++)
1643         {
1644                 ListCell   *l;
1645
1646                 if (root->simple_rel_array[i] == NULL)
1647                         continue;
1648
1649                 Assert(i == root->simple_rel_array[i]->relid);
1650
1651                 if (RelnameCmp(&aliasname, &root->simple_rte_array[i]->eref->aliasname)
1652                         != 0)
1653                         continue;
1654
1655                 foreach(l, initial_rels)
1656                 {
1657                         RelOptInfo *rel = (RelOptInfo *) lfirst(l);
1658
1659                         if (rel->reloptkind == RELOPT_BASEREL)
1660                         {
1661                                 if (rel->relid != i)
1662                                         continue;
1663                         }
1664                         else
1665                         {
1666                                 Assert(rel->reloptkind == RELOPT_JOINREL);
1667
1668                                 if (!bms_is_member(i, rel->relids))
1669                                         continue;
1670                         }
1671
1672                         if (found != 0)
1673                         {
1674                                 parse_ereport(str,
1675                                                           ("Relation name \"%s\" is ambiguous.",
1676                                                            aliasname));
1677                                 return -1;
1678                         }
1679
1680                         found = i;
1681                         break;
1682                 }
1683
1684         }
1685
1686         return found;
1687 }
1688
1689 /*
1690  * relidビットマスクと一致するヒントを探す
1691  */
1692 static JoinMethodHint *
1693 find_join_hint(Relids joinrelids)
1694 {
1695         List       *join_hint;
1696         ListCell   *l;
1697
1698         join_hint = global->join_hint_level[bms_num_members(joinrelids)];
1699
1700         foreach(l, join_hint)
1701         {
1702                 JoinMethodHint *hint = (JoinMethodHint *) lfirst(l);
1703
1704                 if (bms_equal(joinrelids, hint->joinrelids))
1705                         return hint;
1706         }
1707
1708         return NULL;
1709 }
1710
1711 /*
1712  * 結合方式のヒントを使用しやすい構造に変換する。
1713  */
1714 static void
1715 transform_join_hints(PlanHint *plan, PlannerInfo *root, int nbaserel,
1716                 List *initial_rels, JoinMethodHint **join_method_hints)
1717 {
1718         int                             i;
1719         int                             relid;
1720         LeadingHint        *lhint;
1721         Relids                  joinrelids;
1722         int                             njoinrels;
1723         ListCell           *l;
1724
1725         for (i = 0; i < plan->num_hints[HINT_TYPE_JOIN_METHOD]; i++)
1726         {
1727                 JoinMethodHint *hint = plan->join_hints[i];
1728                 int     j;
1729
1730                 if (!hint_state_enabled(hint) || hint->nrels > nbaserel)
1731                         continue;
1732
1733                 bms_free(hint->joinrelids);
1734                 hint->joinrelids = NULL;
1735                 relid = 0;
1736                 for (j = 0; j < hint->nrels; j++)
1737                 {
1738                         char   *relname = hint->relnames[j];
1739
1740                         relid = find_relid_aliasname(root, relname, initial_rels,
1741                                                                                  hint->base.hint_str);
1742
1743                         if (relid == -1)
1744                                 hint->base.state = HINT_STATE_ERROR;
1745
1746                         if (relid <= 0)
1747                                 break;
1748
1749                         if (bms_is_member(relid, hint->joinrelids))
1750                         {
1751                                 parse_ereport(hint->base.hint_str,
1752                                                           ("Relation name \"%s\" is duplicate.", relname));
1753                                 hint->base.state = HINT_STATE_ERROR;
1754                                 break;
1755                         }
1756
1757                         hint->joinrelids = bms_add_member(hint->joinrelids, relid);
1758                 }
1759
1760                 if (relid <= 0 || hint->base.state == HINT_STATE_ERROR)
1761                         continue;
1762
1763                 plan->join_hint_level[hint->nrels] =
1764                         lappend(plan->join_hint_level[hint->nrels], hint);
1765         }
1766
1767         /*
1768          * 有効なLeading ヒントが指定されている場合は、結合順にあわせてjoin method hint
1769          * のフォーマットに変換する。
1770          */
1771         if (plan->num_hints[HINT_TYPE_LEADING] == 0)
1772                 return;
1773
1774         lhint = plan->leading_hints[plan->num_hints[HINT_TYPE_LEADING] - 1];
1775         if (!hint_state_enabled(lhint))
1776                 return;
1777
1778         /* Leading hint は、全ての join 方式が有効な hint として登録する */
1779         joinrelids = NULL;
1780         njoinrels = 0;
1781         foreach(l, lhint->relations)
1782         {
1783                 char   *relname = (char *)lfirst(l);
1784                 JoinMethodHint *hint;
1785
1786                 relid =
1787                         find_relid_aliasname(root, relname, initial_rels, plan->hint_str);
1788
1789                 if (relid == -1)
1790                 {
1791                         bms_free(joinrelids);
1792                         return;
1793                 }
1794
1795                 if (relid == 0)
1796                         continue;
1797
1798                 if (bms_is_member(relid, joinrelids))
1799                 {
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);
1804                         return;
1805                 }
1806
1807                 joinrelids = bms_add_member(joinrelids, relid);
1808                 njoinrels++;
1809
1810                 if (njoinrels < 2)
1811                         continue;
1812
1813                 hint = find_join_hint(joinrelids);
1814                 if (hint == NULL)
1815                 {
1816                         /*
1817                          * Here relnames is not set, since Relids bitmap is sufficient to
1818                          * control paths of this query afterwards.
1819                          */
1820                         hint = (JoinMethodHint *) JoinMethodHintCreate(lhint->base.hint_str,
1821                                                                                                                    HINT_LEADING);
1822                         hint->base.state = HINT_STATE_USED;
1823                         hint->nrels = njoinrels;
1824                         hint->enforce_mask = ENABLE_ALL_JOIN;
1825                         hint->joinrelids = bms_copy(joinrelids);
1826                 }
1827
1828                 join_method_hints[njoinrels] = hint;
1829
1830                 if (njoinrels >= nbaserel)
1831                         break;
1832         }
1833
1834         bms_free(joinrelids);
1835
1836         if (njoinrels < 2)
1837                 return;
1838
1839         for (i = 2; i <= njoinrels; i++)
1840         {
1841                 /* Leading で指定した組み合わせ以外の join hint を削除する */
1842                 list_free(plan->join_hint_level[i]);
1843
1844                 plan->join_hint_level[i] = lappend(NIL, join_method_hints[i]);
1845         }
1846
1847         if (hint_state_enabled(lhint))
1848                 set_join_config_options(DISABLE_ALL_JOIN, global->context);
1849
1850         lhint->base.state = HINT_STATE_USED;
1851
1852 }
1853
1854 static void
1855 rebuild_scan_path(PlanHint *plan, PlannerInfo *root, int level,
1856                                   List *initial_rels)
1857 {
1858         ListCell   *l;
1859
1860         foreach(l, initial_rels)
1861         {
1862                 RelOptInfo         *rel = (RelOptInfo *) lfirst(l);
1863                 RangeTblEntry  *rte;
1864                 ScanMethodHint *hint;
1865
1866                 /*
1867                  * スキャン方式が選択できるリレーションのみ、スキャンパスを再生成
1868                  * する。
1869                  */
1870                 if (rel->reloptkind != RELOPT_BASEREL || rel->rtekind != RTE_RELATION)
1871                         continue;
1872
1873                 rte = root->simple_rte_array[rel->relid];
1874
1875                 /* 外部表はスキャン方式が選択できない。 */
1876                 if (rte->relkind == RELKIND_FOREIGN_TABLE)
1877                         continue;
1878
1879                 /*
1880                  * scan method hint が指定されていなければ、初期値のGUCパラメータでscan
1881                  * path を再生成する。
1882                  */
1883                 if ((hint = find_scan_hint(root, rel)) == NULL)
1884                         set_scan_config_options(plan->init_scan_mask, plan->context);
1885                 else
1886                 {
1887                         set_scan_config_options(hint->enforce_mask, plan->context);
1888                         hint->base.state = HINT_STATE_USED;
1889                 }
1890
1891                 list_free_deep(rel->pathlist);
1892                 rel->pathlist = NIL;
1893                 if (rte->inh)
1894                 {
1895                         /* It's an "append relation", process accordingly */
1896                         set_append_rel_pathlist(root, rel, rel->relid, rte);
1897                 }
1898                 else
1899                 {
1900                         set_plain_rel_pathlist(root, rel, rte);
1901                 }
1902         }
1903
1904         /*
1905          * Restore the GUC variables we set above.
1906          */
1907         set_scan_config_options(plan->init_scan_mask, plan->context);
1908 }
1909
1910 /*
1911  * make_join_rel() をラップする関数
1912  *
1913  * ヒントにしたがって、enabele_* パラメータを変更した上で、make_join_rel()を
1914  * 呼び出す。
1915  */
1916 static RelOptInfo *
1917 make_join_rel_wrapper(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
1918 {
1919         Relids                  joinrelids;
1920         JoinMethodHint *hint;
1921         RelOptInfo         *rel;
1922         int                             save_nestlevel;
1923
1924         joinrelids = bms_union(rel1->relids, rel2->relids);
1925         hint = find_join_hint(joinrelids);
1926         bms_free(joinrelids);
1927
1928         if (!hint)
1929                 return pg_hint_plan_make_join_rel(root, rel1, rel2);
1930
1931         save_nestlevel = NewGUCNestLevel();
1932
1933         set_join_config_options(hint->enforce_mask, global->context);
1934
1935         rel = pg_hint_plan_make_join_rel(root, rel1, rel2);
1936         hint->base.state = HINT_STATE_USED;
1937
1938         /*
1939          * Restore the GUC variables we set above.
1940          */
1941         AtEOXact_GUC(true, save_nestlevel);
1942
1943         return rel;
1944 }
1945
1946 static int
1947 get_num_baserels(List *initial_rels)
1948 {
1949         int                     nbaserel = 0;
1950         ListCell   *l;
1951
1952         foreach(l, initial_rels)
1953         {
1954                 RelOptInfo *rel = (RelOptInfo *) lfirst(l);
1955
1956                 if (rel->reloptkind == RELOPT_BASEREL)
1957                         nbaserel++;
1958                 else if (rel->reloptkind ==RELOPT_JOINREL)
1959                         nbaserel+= bms_num_members(rel->relids);
1960                 else
1961                 {
1962                         /* other values not expected here */
1963                         elog(ERROR, "unrecognized reloptkind type: %d", rel->reloptkind);
1964                 }
1965         }
1966
1967         return nbaserel;
1968 }
1969
1970 static RelOptInfo *
1971 pg_hint_plan_join_search(PlannerInfo *root, int levels_needed,
1972                                                  List *initial_rels)
1973 {
1974         JoinMethodHint **join_method_hints;
1975         int                     nbaserel;
1976         RelOptInfo *rel;
1977         int                     i;
1978
1979         /*
1980          * pg_hint_planが無効、または有効なヒントが1つも指定されなかった場合は、標準
1981          * の処理を行う。
1982          */
1983         if (!global)
1984         {
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);
1989                 else
1990                         return standard_join_search(root, levels_needed, initial_rels);
1991         }
1992
1993         /* We apply scan method hint rebuild scan path. */
1994         rebuild_scan_path(global, root, levels_needed, initial_rels);
1995
1996         /*
1997          * GEQOを使用する条件を満たした場合は、GEQOを用いた結合方式の検索を行う。
1998          * このとき、スキャン方式のヒントとSetヒントのみが有効になり、結合方式や結合
1999          * 順序はヒント句は無効になりGEQOのアルゴリズムで決定される。
2000          */
2001         if (enable_geqo && levels_needed >= geqo_threshold)
2002                 return geqo(root, levels_needed, initial_rels);
2003
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));
2007
2008         transform_join_hints(global, root, nbaserel, initial_rels,
2009                                                  join_method_hints);
2010
2011         rel = pg_hint_plan_standard_join_search(root, levels_needed, initial_rels);
2012
2013         for (i = 2; i <= nbaserel; i++)
2014         {
2015                 list_free(global->join_hint_level[i]);
2016
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]);
2021         }
2022         pfree(global->join_hint_level);
2023         pfree(join_method_hints);
2024
2025         if (global->num_hints[HINT_TYPE_LEADING] > 0 &&
2026                 hint_state_enabled(
2027                         global->leading_hints[global->num_hints[HINT_TYPE_LEADING] - 1]))
2028                 set_join_config_options(global->init_join_mask, global->context);
2029
2030         return rel;
2031 }
2032
2033 /*
2034  * set_rel_pathlist
2035  *        Build access paths for a base relation
2036  */
2037 static void
2038 set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
2039                                  Index rti, RangeTblEntry *rte)
2040 {
2041         if (rte->inh)
2042         {
2043                 /* It's an "append relation", process accordingly */
2044                 set_append_rel_pathlist(root, rel, rti, rte);
2045         }
2046         else
2047         {
2048                 if (rel->rtekind == RTE_RELATION)
2049                 {
2050                         if (rte->relkind == RELKIND_RELATION)
2051                         {
2052                                 /* Plain relation */
2053                                 set_plain_rel_pathlist(root, rel, rte);
2054                         }
2055                         else
2056                                 elog(ERROR, "unexpected relkind: %c", rte->relkind);
2057                 }
2058                 else
2059                         elog(ERROR, "unexpected rtekind: %d", (int) rel->rtekind);
2060         }
2061 }
2062
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
2066 #include "core.c"
2067
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) \
2071 do { \
2072         ScanMethodHint *hint = NULL; \
2073         if ((hint = find_scan_hint((root), (innerrel))) != NULL) \
2074         { \
2075                 set_scan_config_options(hint->enforce_mask, global->context); \
2076                 hint->base.state = HINT_STATE_USED; \
2077         } \
2078         add_paths_to_joinrel((root), (joinrel), (outerrel), (innerrel), (jointype), (sjinfo), (restrictlist)); \
2079         if (hint != NULL) \
2080                 set_scan_config_options(global->init_scan_mask, global->context); \
2081 } while(0)
2082 #include "make_join_rel.c"