OSDN Git Service

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