OSDN Git Service

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