OSDN Git Service

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