OSDN Git Service

現在のヒントを指すポインタをスタックからではなく直接得るようにした。
[pghintplan/pg_hint_plan.git] / pg_hint_plan.c
1 /*-------------------------------------------------------------------------
2  *
3  * pg_hint_plan.c
4  *                do instructions or hints to the planner using C-style block comments
5  *                of the SQL.
6  *
7  * Copyright (c) 2012, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
8  *
9  *-------------------------------------------------------------------------
10  */
11 #include "postgres.h"
12 #include "commands/prepare.h"
13 #include "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 | 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 push_hint(PlanHint *plan);
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 *plan,
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 *plan,
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 *plan,
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 *plan, 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   *hint;
553
554         hint = palloc(sizeof(PlanHint));
555         hint->hint_str = NULL;
556         hint->nall_hints = 0;
557         hint->max_all_hints = 0;
558         hint->all_hints = NULL;
559         memset(hint->num_hints, 0, sizeof(hint->num_hints));
560         hint->scan_hints = NULL;
561         hint->init_scan_mask = 0;
562         hint->parent_relid = 0;
563         hint->parent_hint = NULL;
564         hint->join_hints = NULL;
565         hint->init_join_mask = 0;
566         hint->join_hint_level = NULL;
567         hint->leading_hint = NULL;
568         hint->context = superuser() ? PGC_SUSET : PGC_USERSET;
569         hint->set_hints = NULL;
570
571         return hint;
572 }
573
574 static void
575 PlanHintDelete(PlanHint *hint)
576 {
577         int                     i;
578
579         if (!hint)
580                 return;
581
582         if (hint->hint_str)
583                 pfree(hint->hint_str);
584
585         for (i = 0; i < hint->num_hints[HINT_TYPE_SCAN_METHOD]; i++)
586                 hint->all_hints[i]->delete_func(hint->all_hints[i]);
587         if (hint->all_hints)
588                 pfree(hint->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 *hint, StringInfo buf, const char *title,
687                           HintStatus state)
688 {
689         int     i;
690
691         appendStringInfo(buf, "%s:\n", title);
692         for (i = 0; i < hint->nall_hints; i++)
693         {
694                 if (hint->all_hints[i]->state != state)
695                         continue;
696
697                 hint->all_hints[i]->dump_func(hint->all_hints[i], buf);
698         }
699 }
700
701 static void
702 PlanHintDump(PlanHint *hint)
703 {
704         StringInfoData  buf;
705
706         if (!hint)
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(hint, &buf, "used hint", HINT_STATE_USED);
716         all_hint_dump(hint, &buf, "not used hint", HINT_STATE_NOTUSED);
717         all_hint_dump(hint, &buf, "duplication hint", HINT_STATE_DUPLICATION);
718         all_hint_dump(hint, &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 *plan, 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, plan, 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 (plan->nall_hints == 0)
968                         {
969                                 plan->max_all_hints = HINT_ARRAY_DEFAULT_INITSIZE;
970                                 plan->all_hints = palloc(sizeof(Hint *) * plan->max_all_hints);
971                         }
972                         else if (plan->nall_hints == plan->max_all_hints)
973                         {
974                                 plan->max_all_hints *= 2;
975                                 plan->all_hints = repalloc(plan->all_hints,
976                                                                                 sizeof(Hint *) * plan->max_all_hints);
977                         }
978
979                         plan->all_hints[plan->nall_hints] = hint;
980                         plan->nall_hints++;
981
982                         skip_space(str);
983
984                         break;
985                 }
986
987                 if (parser->keyword == NULL)
988                 {
989                         parse_ereport(head, ("Unrecognized hint keyword \"%s\".",
990                                                                  buf.data));
991                         pfree(buf.data);
992                         return;
993                 }
994         }
995
996         pfree(buf.data);
997 }
998
999 /*
1000  * Do basic parsing of the query head comment.
1001  */
1002 static PlanHint *
1003 parse_head_comment(Query *parse)
1004 {
1005         const char *p;
1006         char       *head;
1007         char       *tail;
1008         int                     len;
1009         int                     i;
1010         PlanHint   *plan;
1011
1012         /* get client-supplied query string. */
1013         if (stmt_name)
1014         {
1015                 PreparedStatement  *entry;
1016
1017                 entry = FetchPreparedStatement(stmt_name, true);
1018                 p = entry->plansource->query_string;
1019         }
1020         else
1021                 p = debug_query_string;
1022
1023         if (p == NULL)
1024                 return NULL;
1025
1026         /* extract query head comment. */
1027         len = strlen(HINT_START);
1028         skip_space(p);
1029         if (strncmp(p, HINT_START, len))
1030                 return NULL;
1031
1032         head = (char *) p;
1033         p += len;
1034         skip_space(p);
1035
1036         /* find hint end keyword. */
1037         if ((tail = strstr(p, HINT_END)) == NULL)
1038         {
1039                 parse_ereport(head, ("Unterminated block comment."));
1040                 return NULL;
1041         }
1042
1043         /* 入れ子にしたブロックコメントはサポートしない */
1044         if ((head = strstr(p, BLOCK_COMMENT_START)) != NULL && head < tail)
1045         {
1046                 parse_ereport(head, ("Nested block comments are not supported."));
1047                 return NULL;
1048         }
1049
1050         /* ヒント句部分を切り出す */
1051         len = tail - p;
1052         head = palloc(len + 1);
1053         memcpy(head, p, len);
1054         head[len] = '\0';
1055         p = head;
1056
1057         plan = PlanHintCreate();
1058         plan->hint_str = head;
1059
1060         /* parse each hint. */
1061         parse_hints(plan, parse, p);
1062
1063         /* When nothing specified a hint, we free PlanHint and returns NULL. */
1064         if (plan->nall_hints == 0)
1065         {
1066                 PlanHintDelete(plan);
1067                 return NULL;
1068         }
1069
1070         /* パースしたヒントを並び替える */
1071         qsort(plan->all_hints, plan->nall_hints, sizeof(Hint *), AllHintCmpIsOrder);
1072
1073         /* 重複したヒントを検索する */
1074         for (i = 0; i < plan->nall_hints; i++)
1075         {
1076                 Hint   *hint = plan->all_hints[i];
1077
1078                 plan->num_hints[hint->type]++;
1079
1080                 if (i + 1 >= plan->nall_hints)
1081                         break;
1082
1083                 if (AllHintCmp(plan->all_hints + i, plan->all_hints + i + 1, false)
1084                         == 0)
1085                 {
1086                         const char *HintTypeName[] = {"scan method", "join method",
1087                                                                                   "leading", "set"};
1088
1089                         parse_ereport(plan->all_hints[i]->hint_str,
1090                                                   ("Conflict %s hint.", HintTypeName[hint->type]));
1091                         plan->all_hints[i]->state = HINT_STATE_DUPLICATION;
1092                 }
1093         }
1094
1095         plan->scan_hints = (ScanMethodHint **) plan->all_hints;
1096         plan->join_hints = (JoinMethodHint **) plan->all_hints +
1097                 plan->num_hints[HINT_TYPE_SCAN_METHOD];
1098         plan->leading_hint = (LeadingHint *) plan->all_hints[
1099                 plan->num_hints[HINT_TYPE_SCAN_METHOD] +
1100                 plan->num_hints[HINT_TYPE_JOIN_METHOD] +
1101                 plan->num_hints[HINT_TYPE_LEADING] - 1];
1102         plan->set_hints = (SetHint **) plan->all_hints +
1103                 plan->num_hints[HINT_TYPE_SCAN_METHOD] +
1104                 plan->num_hints[HINT_TYPE_JOIN_METHOD] +
1105                 plan->num_hints[HINT_TYPE_LEADING];
1106
1107         return plan;
1108 }
1109
1110 /*
1111  * スキャン方式ヒントのカッコ内をパースする
1112  */
1113 static const char *
1114 ScanMethodHintParse(ScanMethodHint *hint, PlanHint *plan, Query *parse,
1115                                         const char *str)
1116 {
1117         const char *keyword = hint->base.keyword;
1118
1119         /*
1120          * スキャン方式のヒントでリレーション名が読み取れない場合はヒント無効
1121          */
1122         if ((str = parse_quote_value(str, &hint->relname, "relation name", true))
1123                 == NULL)
1124                 return NULL;
1125
1126         skip_space(str);
1127
1128         /*
1129          * インデックスリストを受け付けるヒントであれば、インデックス参照をパース
1130          * する。
1131          */
1132         if (strcmp(keyword, HINT_INDEXSCAN) == 0 ||
1133                 strcmp(keyword, HINT_BITMAPSCAN) == 0)
1134         {
1135                 while (*str != ')' && *str != '\0')
1136                 {
1137                         char       *indexname;
1138
1139                         str = parse_quote_value(str, &indexname, "index name", true);
1140                         if (str == NULL)
1141                                 return NULL;
1142
1143                         hint->indexnames = lappend(hint->indexnames, indexname);
1144                         skip_space(str);
1145                 }
1146         }
1147
1148         /*
1149          * ヒントごとに決まっている許容スキャン方式をビットマスクとして設定
1150          */
1151         if (strcasecmp(keyword, HINT_SEQSCAN) == 0)
1152                 hint->enforce_mask = ENABLE_SEQSCAN;
1153         else if (strcasecmp(keyword, HINT_INDEXSCAN) == 0)
1154                 hint->enforce_mask = ENABLE_INDEXSCAN;
1155         else if (strcasecmp(keyword, HINT_BITMAPSCAN) == 0)
1156                 hint->enforce_mask = ENABLE_BITMAPSCAN;
1157         else if (strcasecmp(keyword, HINT_TIDSCAN) == 0)
1158                 hint->enforce_mask = ENABLE_TIDSCAN;
1159         else if (strcasecmp(keyword, HINT_NOSEQSCAN) == 0)
1160                 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_SEQSCAN;
1161         else if (strcasecmp(keyword, HINT_NOINDEXSCAN) == 0)
1162                 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_INDEXSCAN;
1163         else if (strcasecmp(keyword, HINT_NOBITMAPSCAN) == 0)
1164                 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_BITMAPSCAN;
1165         else if (strcasecmp(keyword, HINT_NOTIDSCAN) == 0)
1166                 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_TIDSCAN;
1167         else
1168         {
1169                 parse_ereport(str, ("Unrecognized hint keyword \"%s\".", keyword));
1170                 return NULL;
1171         }
1172
1173         return str;
1174 }
1175
1176 static const char *
1177 JoinMethodHintParse(JoinMethodHint *hint, PlanHint *plan, Query *parse,
1178                                         const char *str)
1179 {
1180         char       *relname;
1181         const char *keyword = hint->base.keyword;
1182
1183         skip_space(str);
1184
1185         hint->relnames = palloc(sizeof(char *));
1186
1187         while ((str = parse_quote_value(str, &relname, "relation name", true))
1188                    != NULL)
1189         {
1190                 hint->nrels++;
1191                 hint->relnames = repalloc(hint->relnames, sizeof(char *) * hint->nrels);
1192                 hint->relnames[hint->nrels - 1] = relname;
1193
1194                 skip_space(str);
1195                 if (*str == ')')
1196                         break;
1197         }
1198
1199         if (str == NULL)
1200                 return NULL;
1201
1202         /* Join 対象のテーブルは最低でも2つ指定する必要がある */
1203         if (hint->nrels < 2)
1204         {
1205                 parse_ereport(str,
1206                                           ("%s hint requires at least two relations.",
1207                                            hint->base.keyword));
1208                 hint->base.state = HINT_STATE_ERROR;
1209         }
1210
1211         /* テーブル名順にソートする */
1212         qsort(hint->relnames, hint->nrels, sizeof(char *), RelnameCmp);
1213
1214         if (strcasecmp(keyword, HINT_NESTLOOP) == 0)
1215                 hint->enforce_mask = ENABLE_NESTLOOP;
1216         else if (strcasecmp(keyword, HINT_MERGEJOIN) == 0)
1217                 hint->enforce_mask = ENABLE_MERGEJOIN;
1218         else if (strcasecmp(keyword, HINT_HASHJOIN) == 0)
1219                 hint->enforce_mask = ENABLE_HASHJOIN;
1220         else if (strcasecmp(keyword, HINT_NONESTLOOP) == 0)
1221                 hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_NESTLOOP;
1222         else if (strcasecmp(keyword, HINT_NOMERGEJOIN) == 0)
1223                 hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_MERGEJOIN;
1224         else if (strcasecmp(keyword, HINT_NOHASHJOIN) == 0)
1225                 hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_HASHJOIN;
1226         else
1227         {
1228                 parse_ereport(str, ("Unrecognized hint keyword \"%s\".", keyword));
1229                 return NULL;
1230         }
1231
1232         return str;
1233 }
1234
1235 static const char *
1236 LeadingHintParse(LeadingHint *hint, PlanHint *plan, Query *parse,
1237                                  const char *str)
1238 {
1239         skip_space(str);
1240
1241         while (*str != ')')
1242         {
1243                 char   *relname;
1244
1245                 if ((str = parse_quote_value(str, &relname, "relation name", true))
1246                         == NULL)
1247                         return NULL;
1248
1249                 hint->relations = lappend(hint->relations, relname);
1250
1251                 skip_space(str);
1252         }
1253
1254         /* テーブル指定が2つ未満の場合は、Leading ヒントはエラーとする */
1255         if (list_length(hint->relations) < 2)
1256         {
1257                 parse_ereport(hint->base.hint_str,
1258                                           ("%s hint requires at least two relations.",
1259                                            HINT_LEADING));
1260                 hint->base.state = HINT_STATE_ERROR;
1261         }
1262
1263         return str;
1264 }
1265
1266 static const char *
1267 SetHintParse(SetHint *hint, PlanHint *plan, Query *parse, const char *str)
1268 {
1269         if ((str = parse_quote_value(str, &hint->name, "parameter name", true))
1270                 == NULL ||
1271                 (str = parse_quote_value(str, &hint->value, "parameter value", false))
1272                 == NULL)
1273                 return NULL;
1274
1275         return str;
1276 }
1277
1278 /*
1279  * set GUC parameter functions
1280  */
1281
1282 static int
1283 set_config_option_wrapper(const char *name, const char *value,
1284                                                   GucContext context, GucSource source,
1285                                                   GucAction action, bool changeVal, int elevel)
1286 {
1287         int                             result = 0;
1288         MemoryContext   ccxt = CurrentMemoryContext;
1289
1290         PG_TRY();
1291         {
1292                 result = set_config_option(name, value, context, source,
1293                                                                    action, changeVal);
1294         }
1295         PG_CATCH();
1296         {
1297                 ErrorData          *errdata;
1298
1299                 /* Save error info */
1300                 MemoryContextSwitchTo(ccxt);
1301                 errdata = CopyErrorData();
1302                 FlushErrorState();
1303
1304                 ereport(elevel, (errcode(errdata->sqlerrcode),
1305                                 errmsg("%s", errdata->message),
1306                                 errdata->detail ? errdetail("%s", errdata->detail) : 0,
1307                                 errdata->hint ? errhint("%s", errdata->hint) : 0));
1308                 FreeErrorData(errdata);
1309         }
1310         PG_END_TRY();
1311
1312         return result;
1313 }
1314
1315 static int
1316 set_config_options(SetHint **options, int noptions, GucContext context)
1317 {
1318         int     i;
1319         int     save_nestlevel;
1320
1321         save_nestlevel = NewGUCNestLevel();
1322
1323         for (i = 0; i < noptions; i++)
1324         {
1325                 SetHint    *hint = options[i];
1326                 int                     result;
1327
1328                 if (!hint_state_enabled(hint))
1329                         continue;
1330
1331                 result = set_config_option_wrapper(hint->name, hint->value, context,
1332                                                                                    PGC_S_SESSION, GUC_ACTION_SAVE, true,
1333                                                                                    pg_hint_plan_parse_messages);
1334                 if (result != 0)
1335                         hint->base.state = HINT_STATE_USED;
1336                 else
1337                         hint->base.state = HINT_STATE_ERROR;
1338         }
1339
1340         return save_nestlevel;
1341 }
1342
1343 #define SET_CONFIG_OPTION(name, type_bits) \
1344         set_config_option_wrapper((name), \
1345                 (mask & (type_bits)) ? "true" : "false", \
1346                 context, PGC_S_SESSION, GUC_ACTION_SAVE, true, ERROR)
1347
1348 static void
1349 set_scan_config_options(unsigned char enforce_mask, GucContext context)
1350 {
1351         unsigned char   mask;
1352
1353         if (enforce_mask == ENABLE_SEQSCAN || enforce_mask == ENABLE_INDEXSCAN ||
1354                 enforce_mask == ENABLE_BITMAPSCAN || enforce_mask == ENABLE_TIDSCAN
1355                 )
1356                 mask = enforce_mask;
1357         else
1358                 mask = enforce_mask & current_hint->init_scan_mask;
1359
1360         SET_CONFIG_OPTION("enable_seqscan", ENABLE_SEQSCAN);
1361         SET_CONFIG_OPTION("enable_indexscan", ENABLE_INDEXSCAN);
1362         SET_CONFIG_OPTION("enable_bitmapscan", ENABLE_BITMAPSCAN);
1363         SET_CONFIG_OPTION("enable_tidscan", ENABLE_TIDSCAN);
1364 }
1365
1366 static void
1367 set_join_config_options(unsigned char enforce_mask, GucContext context)
1368 {
1369         unsigned char   mask;
1370
1371         if (enforce_mask == ENABLE_NESTLOOP || enforce_mask == ENABLE_MERGEJOIN ||
1372                 enforce_mask == ENABLE_HASHJOIN)
1373                 mask = enforce_mask;
1374         else
1375                 mask = enforce_mask & current_hint->init_join_mask;
1376
1377         SET_CONFIG_OPTION("enable_nestloop", ENABLE_NESTLOOP);
1378         SET_CONFIG_OPTION("enable_mergejoin", ENABLE_MERGEJOIN);
1379         SET_CONFIG_OPTION("enable_hashjoin", ENABLE_HASHJOIN);
1380 }
1381
1382 /*
1383  * pg_hint_plan hook functions
1384  */
1385
1386 static void
1387 pg_hint_plan_ProcessUtility(Node *parsetree, const char *queryString,
1388                                                         ParamListInfo params, bool isTopLevel,
1389                                                         DestReceiver *dest, char *completionTag)
1390 {
1391         Node                               *node;
1392
1393         if (!pg_hint_plan_enable)
1394         {
1395                 if (prev_ProcessUtility)
1396                         (*prev_ProcessUtility) (parsetree, queryString, params,
1397                                                                         isTopLevel, dest, completionTag);
1398                 else
1399                         standard_ProcessUtility(parsetree, queryString, params,
1400                                                                         isTopLevel, dest, completionTag);
1401
1402                 return;
1403         }
1404
1405         node = parsetree;
1406         if (IsA(node, ExplainStmt))
1407         {
1408                 /*
1409                  * EXPLAIN対象のクエリのパースツリーを取得する
1410                  */
1411                 ExplainStmt        *stmt;
1412                 Query              *query;
1413
1414                 stmt = (ExplainStmt *) node;
1415
1416                 Assert(IsA(stmt->query, Query));
1417                 query = (Query *) stmt->query;
1418
1419                 if (query->commandType == CMD_UTILITY && query->utilityStmt != NULL)
1420                         node = query->utilityStmt;
1421         }
1422
1423         /*
1424          * EXECUTEコマンドならば、PREPARE時に指定されたクエリ文字列を取得し、ヒント
1425          * 句の候補として設定する
1426          */
1427         if (IsA(node, ExecuteStmt))
1428         {
1429                 ExecuteStmt        *stmt;
1430
1431                 stmt = (ExecuteStmt *) node;
1432                 stmt_name = stmt->name;
1433         }
1434
1435         if (stmt_name)
1436         {
1437                 PG_TRY();
1438                 {
1439                         if (prev_ProcessUtility)
1440                                 (*prev_ProcessUtility) (parsetree, queryString, params,
1441                                                                                 isTopLevel, dest, completionTag);
1442                         else
1443                                 standard_ProcessUtility(parsetree, queryString, params,
1444                                                                                 isTopLevel, dest, completionTag);
1445                 }
1446                 PG_CATCH();
1447                 {
1448                         stmt_name = NULL;
1449                         PG_RE_THROW();
1450                 }
1451                 PG_END_TRY();
1452
1453                 stmt_name = NULL;
1454
1455                 return;
1456         }
1457
1458         if (prev_ProcessUtility)
1459                 (*prev_ProcessUtility) (parsetree, queryString, params,
1460                                                                 isTopLevel, dest, completionTag);
1461         else
1462                 standard_ProcessUtility(parsetree, queryString, params,
1463                                                                 isTopLevel, dest, completionTag);
1464 }
1465
1466 /*
1467  * ヒント用スタック構造にヒントをプッシュする。なお、List構造体でヒント用スタッ
1468  * ク構造を実装していて、リストの先頭がスタックの一番上に該当する。
1469  */
1470 static void
1471 push_hint(PlanHint *plan)
1472 {
1473         /* 新しいヒントをスタックに積む。 */
1474         PlanHintStack = lcons(plan, PlanHintStack);
1475
1476         /*
1477          * 先ほどスタックに積んだヒントを現在のヒントとしてcurrent_hintに格納する。
1478          */
1479         current_hint = plan;
1480 }
1481
1482 static void
1483 pop_hint(void)
1484 {
1485         /* ヒントのスタックが空の場合はエラーを返す */
1486         if(PlanHintStack == NIL)
1487                 elog(ERROR, "hint stack is empty");
1488
1489         /*
1490          * ヒントのスタックから一番上のものを取り出して解放する。 current_hint
1491          * 常に最上段ヒントを指すが、スタックが空の場合はNULLにする。
1492          */
1493         PlanHintStack = list_delete_first(PlanHintStack);
1494         PlanHintDelete(current_hint);
1495         if(PlanHintStack == NIL)
1496                 current_hint = NULL;
1497         else
1498                 current_hint = (PlanHint *) lfirst(list_head(PlanHintStack));
1499 }
1500
1501 static PlannedStmt *
1502 pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
1503 {
1504         int                             save_nestlevel;
1505         PlannedStmt        *result;
1506         PlanHint           *plan;
1507
1508         /*
1509          * pg_hint_planが無効である場合は通常のparser処理をおこなう。
1510          * 他のフック関数で実行されるhint処理をスキップするために、current_hint 変数
1511          * をNULLに設定しておく。
1512          */
1513         if (!pg_hint_plan_enable)
1514         {
1515                 current_hint = NULL;
1516
1517                 if (prev_planner)
1518                         return (*prev_planner) (parse, cursorOptions, boundParams);
1519                 else
1520                         return standard_planner(parse, cursorOptions, boundParams);
1521         }
1522
1523         /* 有効なヒント句を保存する。 */
1524         plan = parse_head_comment(parse);
1525
1526         /*
1527          * hintが指定されない、または空のhintを指定された場合は通常のparser処理をお
1528          * こなう。
1529          * 他のフック関数で実行されるhint処理をスキップするために、current_hint 変数
1530          * をNULLに設定しておく。
1531          */
1532         if (!plan)
1533         {
1534                 current_hint = NULL;
1535
1536                 if (prev_planner)
1537                         return (*prev_planner) (parse, cursorOptions, boundParams);
1538                 else
1539                         return standard_planner(parse, cursorOptions, boundParams);
1540         }
1541
1542         /* 現在のヒントをスタックに積む。 */
1543         push_hint(plan);
1544
1545         /* Set hint で指定されたGUCパラメータを設定する */
1546         save_nestlevel = set_config_options(current_hint->set_hints,
1547                                                                                 current_hint->num_hints[HINT_TYPE_SET],
1548                                                                                 current_hint->context);
1549
1550         if (enable_seqscan)
1551                 current_hint->init_scan_mask |= ENABLE_SEQSCAN;
1552         if (enable_indexscan)
1553                 current_hint->init_scan_mask |= ENABLE_INDEXSCAN;
1554         if (enable_bitmapscan)
1555                 current_hint->init_scan_mask |= ENABLE_BITMAPSCAN;
1556         if (enable_tidscan)
1557                 current_hint->init_scan_mask |= ENABLE_TIDSCAN;
1558         if (enable_nestloop)
1559                 current_hint->init_join_mask |= ENABLE_NESTLOOP;
1560         if (enable_mergejoin)
1561                 current_hint->init_join_mask |= ENABLE_MERGEJOIN;
1562         if (enable_hashjoin)
1563                 current_hint->init_join_mask |= ENABLE_HASHJOIN;
1564
1565         /*
1566          * プラン作成中にエラーとなった場合、GUCパラメータと current_hintを
1567          * pg_hint_plan_planner 関数の実行前の状態に戻す。
1568          */
1569         PG_TRY();
1570         {
1571                 if (prev_planner)
1572                         result = (*prev_planner) (parse, cursorOptions, boundParams);
1573                 else
1574                         result = standard_planner(parse, cursorOptions, boundParams);
1575         }
1576         PG_CATCH();
1577         {
1578                 /*
1579                  * プランナ起動前の状態に戻すため、GUCパラメータを復元し、ヒント情報を
1580                  * 一つ削除する。
1581                  */
1582                 AtEOXact_GUC(true, save_nestlevel);
1583                 pop_hint();
1584                 PG_RE_THROW();
1585         }
1586         PG_END_TRY();
1587
1588         /*
1589          * Print hint if debugging.
1590          */
1591         if (pg_hint_plan_debug_print)
1592                 PlanHintDump(current_hint);
1593
1594         /*
1595          * プランナ起動前の状態に戻すため、GUCパラメータを復元し、ヒント情報を一つ
1596          * 削除する。
1597          */
1598         AtEOXact_GUC(true, save_nestlevel);
1599         pop_hint();
1600
1601         return result;
1602 }
1603
1604 /*
1605  * aliasnameと一致するSCANヒントを探す
1606  */
1607 static ScanMethodHint *
1608 find_scan_hint(PlannerInfo *root, RelOptInfo *rel)
1609 {
1610         RangeTblEntry  *rte;
1611         int                             i;
1612
1613         /*
1614          * RELOPT_BASEREL でなければ、scan method ヒントが適用しない。
1615          * 子テーブルの場合はRELOPT_OTHER_MEMBER_RELとなるが、サポート対象外とする。
1616          * また、通常のリレーション以外は、スキャン方式を選択できない。
1617          */
1618         if (rel->reloptkind != RELOPT_BASEREL || rel->rtekind != RTE_RELATION)
1619                 return NULL;
1620
1621         rte = root->simple_rte_array[rel->relid];
1622
1623         /* 外部表はスキャン方式が選択できない。 */
1624         if (rte->relkind == RELKIND_FOREIGN_TABLE)
1625                 return NULL;
1626
1627         /*
1628          * スキャン方式のヒントのリストから、検索対象のリレーションと名称が一致する
1629          * ヒントを検索する。
1630          */
1631         for (i = 0; i < current_hint->num_hints[HINT_TYPE_SCAN_METHOD]; i++)
1632         {
1633                 ScanMethodHint *hint = current_hint->scan_hints[i];
1634
1635                 /* すでに無効となっているヒントは検索対象にしない。 */
1636                 if (!hint_state_enabled(hint))
1637                         continue;
1638
1639                 if (RelnameCmp(&rte->eref->aliasname, &hint->relname) == 0)
1640                         return hint;
1641         }
1642
1643         return NULL;
1644 }
1645
1646 static void
1647 delete_indexes(ScanMethodHint *hint, RelOptInfo *rel)
1648 {
1649         ListCell           *cell;
1650         ListCell           *prev;
1651         ListCell           *next;
1652
1653         /*
1654          * We delete all the IndexOptInfo list and prevent you from being usable by
1655          * a scan.
1656          */
1657         if (hint->enforce_mask == ENABLE_SEQSCAN ||
1658                 hint->enforce_mask == ENABLE_TIDSCAN)
1659         {
1660                 list_free_deep(rel->indexlist);
1661                 rel->indexlist = NIL;
1662                 hint->base.state = HINT_STATE_USED;
1663
1664                 return;
1665         }
1666
1667         /*
1668          * When a list of indexes is not specified, we just use all indexes.
1669          */
1670         if (hint->indexnames == NIL)
1671                 return;
1672
1673         /*
1674          * Leaving only an specified index, we delete it from a IndexOptInfo list
1675          * other than it.
1676          */
1677         prev = NULL;
1678         for (cell = list_head(rel->indexlist); cell; cell = next)
1679         {
1680                 IndexOptInfo   *info = (IndexOptInfo *) lfirst(cell);
1681                 char               *indexname = get_rel_name(info->indexoid);
1682                 ListCell           *l;
1683                 bool                    use_index = false;
1684
1685                 next = lnext(cell);
1686
1687                 foreach(l, hint->indexnames)
1688                 {
1689                         if (RelnameCmp(&indexname, &lfirst(l)) == 0)
1690                         {
1691                                 use_index = true;
1692                                 break;
1693                         }
1694                 }
1695
1696                 if (!use_index)
1697                         rel->indexlist = list_delete_cell(rel->indexlist, cell, prev);
1698                 else
1699                         prev = cell;
1700
1701                 pfree(indexname);
1702         }
1703 }
1704
1705 static void
1706 pg_hint_plan_get_relation_info(PlannerInfo *root, Oid relationObjectId,
1707                                                            bool inhparent, RelOptInfo *rel)
1708 {
1709         ScanMethodHint *hint;
1710
1711         if (prev_get_relation_info)
1712                 (*prev_get_relation_info) (root, relationObjectId, inhparent, rel);
1713
1714         /* 有効なヒントが指定されなかった場合は処理をスキップする。 */
1715         if (!current_hint)
1716                 return;
1717
1718         if (inhparent)
1719         {
1720                 /* store does relids of parent table. */
1721                 current_hint->parent_relid = rel->relid;
1722         }
1723         else if (current_hint->parent_relid != 0)
1724         {
1725                 /*
1726                  * We use the same GUC parameter if this table is the child table of a
1727                  * table called pg_hint_plan_get_relation_info just before that.
1728                  */
1729                 ListCell   *l;
1730
1731                 /* append_rel_list contains all append rels; ignore others */
1732                 foreach(l, root->append_rel_list)
1733                 {
1734                         AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
1735
1736                         /* This rel is child table. */
1737                         if (appinfo->parent_relid == current_hint->parent_relid &&
1738                                 appinfo->child_relid == rel->relid)
1739                         {
1740                                 if (current_hint->parent_hint)
1741                                         delete_indexes(current_hint->parent_hint, rel);
1742
1743                                 return;
1744                         }
1745                 }
1746
1747                 /* This rel is not inherit table. */
1748                 current_hint->parent_relid = 0;
1749                 current_hint->parent_hint = NULL;
1750         }
1751
1752         /* scan hint が指定されない場合は、GUCパラメータをリセットする。 */
1753         if ((hint = find_scan_hint(root, rel)) == NULL)
1754         {
1755                 set_scan_config_options(current_hint->init_scan_mask,
1756                                                                 current_hint->context);
1757                 return;
1758         }
1759         set_scan_config_options(hint->enforce_mask, current_hint->context);
1760         hint->base.state = HINT_STATE_USED;
1761         if (inhparent)
1762                 current_hint->parent_hint = hint;
1763
1764         delete_indexes(hint, rel);
1765 }
1766
1767 /*
1768  * aliasnameがクエリ中に指定した別名と一致する場合は、そのインデックスを返し、一
1769  * 致する別名がなければ0を返す。
1770  * aliasnameがクエリ中に複数回指定された場合は、-1を返す。
1771  */
1772 static int
1773 find_relid_aliasname(PlannerInfo *root, char *aliasname, List *initial_rels,
1774                                          const char *str)
1775 {
1776         int             i;
1777         Index   found = 0;
1778
1779         for (i = 1; i < root->simple_rel_array_size; i++)
1780         {
1781                 ListCell   *l;
1782
1783                 if (root->simple_rel_array[i] == NULL)
1784                         continue;
1785
1786                 Assert(i == root->simple_rel_array[i]->relid);
1787
1788                 if (RelnameCmp(&aliasname, &root->simple_rte_array[i]->eref->aliasname)
1789                         != 0)
1790                         continue;
1791
1792                 foreach(l, initial_rels)
1793                 {
1794                         RelOptInfo *rel = (RelOptInfo *) lfirst(l);
1795
1796                         if (rel->reloptkind == RELOPT_BASEREL)
1797                         {
1798                                 if (rel->relid != i)
1799                                         continue;
1800                         }
1801                         else
1802                         {
1803                                 Assert(rel->reloptkind == RELOPT_JOINREL);
1804
1805                                 if (!bms_is_member(i, rel->relids))
1806                                         continue;
1807                         }
1808
1809                         if (found != 0)
1810                         {
1811                                 parse_ereport(str,
1812                                                           ("Relation name \"%s\" is ambiguous.",
1813                                                            aliasname));
1814                                 return -1;
1815                         }
1816
1817                         found = i;
1818                         break;
1819                 }
1820
1821         }
1822
1823         return found;
1824 }
1825
1826 /*
1827  * relidビットマスクと一致するヒントを探す
1828  */
1829 static JoinMethodHint *
1830 find_join_hint(Relids joinrelids)
1831 {
1832         List       *join_hint;
1833         ListCell   *l;
1834
1835         join_hint = current_hint->join_hint_level[bms_num_members(joinrelids)];
1836
1837         foreach(l, join_hint)
1838         {
1839                 JoinMethodHint *hint = (JoinMethodHint *) lfirst(l);
1840
1841                 if (bms_equal(joinrelids, hint->joinrelids))
1842                         return hint;
1843         }
1844
1845         return NULL;
1846 }
1847
1848 /*
1849  * 結合方式のヒントを使用しやすい構造に変換する。
1850  */
1851 static void
1852 transform_join_hints(PlanHint *plan, PlannerInfo *root, int nbaserel,
1853                 List *initial_rels, JoinMethodHint **join_method_hints)
1854 {
1855         int                             i;
1856         int                             relid;
1857         LeadingHint        *lhint;
1858         Relids                  joinrelids;
1859         int                             njoinrels;
1860         ListCell           *l;
1861
1862         for (i = 0; i < plan->num_hints[HINT_TYPE_JOIN_METHOD]; i++)
1863         {
1864                 JoinMethodHint *hint = plan->join_hints[i];
1865                 int     j;
1866
1867                 if (!hint_state_enabled(hint) || hint->nrels > nbaserel)
1868                         continue;
1869
1870                 bms_free(hint->joinrelids);
1871                 hint->joinrelids = NULL;
1872                 relid = 0;
1873                 for (j = 0; j < hint->nrels; j++)
1874                 {
1875                         char   *relname = hint->relnames[j];
1876
1877                         relid = find_relid_aliasname(root, relname, initial_rels,
1878                                                                                  hint->base.hint_str);
1879
1880                         if (relid == -1)
1881                                 hint->base.state = HINT_STATE_ERROR;
1882
1883                         if (relid <= 0)
1884                                 break;
1885
1886                         if (bms_is_member(relid, hint->joinrelids))
1887                         {
1888                                 parse_ereport(hint->base.hint_str,
1889                                                           ("Relation name \"%s\" is duplicated.", relname));
1890                                 hint->base.state = HINT_STATE_ERROR;
1891                                 break;
1892                         }
1893
1894                         hint->joinrelids = bms_add_member(hint->joinrelids, relid);
1895                 }
1896
1897                 if (relid <= 0 || hint->base.state == HINT_STATE_ERROR)
1898                         continue;
1899
1900                 plan->join_hint_level[hint->nrels] =
1901                         lappend(plan->join_hint_level[hint->nrels], hint);
1902         }
1903
1904         /*
1905          * 有効なLeading ヒントが指定されている場合は、結合順にあわせて
1906          * join method hint のフォーマットに変換する。
1907          */
1908         if (plan->num_hints[HINT_TYPE_LEADING] == 0)
1909                 return;
1910
1911         lhint = plan->leading_hint;
1912         if (!hint_state_enabled(lhint))
1913                 return;
1914
1915         /* Leading hint は、全ての join 方式が有効な hint として登録する */
1916         joinrelids = NULL;
1917         njoinrels = 0;
1918         foreach(l, lhint->relations)
1919         {
1920                 char   *relname = (char *)lfirst(l);
1921                 JoinMethodHint *hint;
1922
1923                 relid =
1924                         find_relid_aliasname(root, relname, initial_rels, plan->hint_str);
1925
1926                 if (relid == -1)
1927                 {
1928                         bms_free(joinrelids);
1929                         return;
1930                 }
1931
1932                 if (relid == 0)
1933                         continue;
1934
1935                 if (bms_is_member(relid, joinrelids))
1936                 {
1937                         parse_ereport(lhint->base.hint_str,
1938                                                   ("Relation name \"%s\" is duplicated.", relname));
1939                         lhint->base.state = HINT_STATE_ERROR;
1940                         bms_free(joinrelids);
1941                         return;
1942                 }
1943
1944                 joinrelids = bms_add_member(joinrelids, relid);
1945                 njoinrels++;
1946
1947                 if (njoinrels < 2)
1948                         continue;
1949
1950                 hint = find_join_hint(joinrelids);
1951                 if (hint == NULL)
1952                 {
1953                         /*
1954                          * Here relnames is not set, since Relids bitmap is sufficient to
1955                          * control paths of this query afterwards.
1956                          */
1957                         hint = (JoinMethodHint *) JoinMethodHintCreate(lhint->base.hint_str,
1958                                                                                                                    HINT_LEADING);
1959                         hint->base.state = HINT_STATE_USED;
1960                         hint->nrels = njoinrels;
1961                         hint->enforce_mask = ENABLE_ALL_JOIN;
1962                         hint->joinrelids = bms_copy(joinrelids);
1963                 }
1964
1965                 join_method_hints[njoinrels] = hint;
1966
1967                 if (njoinrels >= nbaserel)
1968                         break;
1969         }
1970
1971         bms_free(joinrelids);
1972
1973         if (njoinrels < 2)
1974                 return;
1975
1976         for (i = 2; i <= njoinrels; i++)
1977         {
1978                 /* Leading で指定した組み合わせ以外の join hint を削除する */
1979                 list_free(plan->join_hint_level[i]);
1980
1981                 plan->join_hint_level[i] = lappend(NIL, join_method_hints[i]);
1982         }
1983
1984         if (hint_state_enabled(lhint))
1985                 set_join_config_options(DISABLE_ALL_JOIN, current_hint->context);
1986
1987         lhint->base.state = HINT_STATE_USED;
1988
1989 }
1990
1991 /*
1992  * set_plain_rel_pathlist
1993  *        Build access paths for a plain relation (no subquery, no inheritance)
1994  *
1995  * This function was copied and edited from set_plain_rel_pathlist() in
1996  * src/backend/optimizer/path/allpaths.c
1997  */
1998 static void
1999 set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
2000 {
2001         /* Consider sequential scan */
2002         add_path(rel, create_seqscan_path(root, rel));
2003
2004         /* Consider index scans */
2005         create_index_paths(root, rel);
2006
2007         /* Consider TID scans */
2008         create_tidscan_paths(root, rel);
2009
2010         /* Now find the cheapest of the paths for this rel */
2011         set_cheapest(rel);
2012 }
2013
2014 static void
2015 rebuild_scan_path(PlanHint *plan, PlannerInfo *root, int level,
2016                                   List *initial_rels)
2017 {
2018         ListCell   *l;
2019
2020         foreach(l, initial_rels)
2021         {
2022                 RelOptInfo         *rel = (RelOptInfo *) lfirst(l);
2023                 RangeTblEntry  *rte;
2024                 ScanMethodHint *hint;
2025
2026                 /*
2027                  * スキャン方式が選択できるリレーションのみ、スキャンパスを再生成する。
2028                  */
2029                 if (rel->reloptkind != RELOPT_BASEREL || rel->rtekind != RTE_RELATION)
2030                         continue;
2031
2032                 rte = root->simple_rte_array[rel->relid];
2033
2034                 /* 外部表はスキャン方式が選択できない。 */
2035                 if (rte->relkind == RELKIND_FOREIGN_TABLE)
2036                         continue;
2037
2038                 /*
2039                  * scan method hint が指定されていなければ、初期値のGUCパラメータでscan
2040                  * path を再生成する。
2041                  */
2042                 if ((hint = find_scan_hint(root, rel)) == NULL)
2043                         set_scan_config_options(plan->init_scan_mask, plan->context);
2044                 else
2045                 {
2046                         set_scan_config_options(hint->enforce_mask, plan->context);
2047                         hint->base.state = HINT_STATE_USED;
2048                 }
2049
2050                 list_free_deep(rel->pathlist);
2051                 rel->pathlist = NIL;
2052                 if (rte->inh)
2053                 {
2054                         /* It's an "append relation", process accordingly */
2055                         set_append_rel_pathlist(root, rel, rel->relid, rte);
2056                 }
2057                 else
2058                 {
2059                         set_plain_rel_pathlist(root, rel, rte);
2060                 }
2061         }
2062
2063         /*
2064          * Restore the GUC variables we set above.
2065          */
2066         set_scan_config_options(plan->init_scan_mask, plan->context);
2067 }
2068
2069 /*
2070  * make_join_rel() をラップする関数
2071  *
2072  * ヒントにしたがって、enabele_* パラメータを変更した上で、make_join_rel()を
2073  * 呼び出す。
2074  */
2075 static RelOptInfo *
2076 make_join_rel_wrapper(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
2077 {
2078         Relids                  joinrelids;
2079         JoinMethodHint *hint;
2080         RelOptInfo         *rel;
2081         int                             save_nestlevel;
2082
2083         joinrelids = bms_union(rel1->relids, rel2->relids);
2084         hint = find_join_hint(joinrelids);
2085         bms_free(joinrelids);
2086
2087         if (!hint)
2088                 return pg_hint_plan_make_join_rel(root, rel1, rel2);
2089
2090         save_nestlevel = NewGUCNestLevel();
2091
2092         set_join_config_options(hint->enforce_mask, current_hint->context);
2093
2094         rel = pg_hint_plan_make_join_rel(root, rel1, rel2);
2095         hint->base.state = HINT_STATE_USED;
2096
2097         /*
2098          * Restore the GUC variables we set above.
2099          */
2100         AtEOXact_GUC(true, save_nestlevel);
2101
2102         return rel;
2103 }
2104
2105 static int
2106 get_num_baserels(List *initial_rels)
2107 {
2108         int                     nbaserel = 0;
2109         ListCell   *l;
2110
2111         foreach(l, initial_rels)
2112         {
2113                 RelOptInfo *rel = (RelOptInfo *) lfirst(l);
2114
2115                 if (rel->reloptkind == RELOPT_BASEREL)
2116                         nbaserel++;
2117                 else if (rel->reloptkind ==RELOPT_JOINREL)
2118                         nbaserel+= bms_num_members(rel->relids);
2119                 else
2120                 {
2121                         /* other values not expected here */
2122                         elog(ERROR, "Unrecognized reloptkind type: %d", rel->reloptkind);
2123                 }
2124         }
2125
2126         return nbaserel;
2127 }
2128
2129 static RelOptInfo *
2130 pg_hint_plan_join_search(PlannerInfo *root, int levels_needed,
2131                                                  List *initial_rels)
2132 {
2133         JoinMethodHint **join_method_hints;
2134         int                     nbaserel;
2135         RelOptInfo *rel;
2136         int                     i;
2137
2138         /*
2139          * pg_hint_planが無効、または有効なヒントが1つも指定されなかった場合は、標準
2140          * の処理を行う。
2141          */
2142         if (!current_hint)
2143         {
2144                 if (prev_join_search)
2145                         return (*prev_join_search) (root, levels_needed, initial_rels);
2146                 else if (enable_geqo && levels_needed >= geqo_threshold)
2147                         return geqo(root, levels_needed, initial_rels);
2148                 else
2149                         return standard_join_search(root, levels_needed, initial_rels);
2150         }
2151
2152         /* We apply scan method hint rebuild scan path. */
2153         rebuild_scan_path(current_hint, root, levels_needed, initial_rels);
2154
2155         /*
2156          * GEQOを使用する条件を満たした場合は、GEQOを用いた結合方式の検索を行う。
2157          * このとき、スキャン方式のヒントとSetヒントのみが有効になり、結合方式や結合
2158          * 順序はヒント句は無効になりGEQOのアルゴリズムで決定される。
2159          */
2160         if (enable_geqo && levels_needed >= geqo_threshold)
2161                 return geqo(root, levels_needed, initial_rels);
2162
2163         nbaserel = get_num_baserels(initial_rels);
2164         current_hint->join_hint_level = palloc0(sizeof(List *) * (nbaserel + 1));
2165         join_method_hints = palloc0(sizeof(JoinMethodHint *) * (nbaserel + 1));
2166
2167         transform_join_hints(current_hint, root, nbaserel, initial_rels,
2168                                                  join_method_hints);
2169
2170         rel = pg_hint_plan_standard_join_search(root, levels_needed, initial_rels);
2171
2172         for (i = 2; i <= nbaserel; i++)
2173         {
2174                 list_free(current_hint->join_hint_level[i]);
2175
2176                 /* free Leading hint only */
2177                 if (join_method_hints[i] != NULL &&
2178                         join_method_hints[i]->enforce_mask == ENABLE_ALL_JOIN)
2179                         JoinMethodHintDelete(join_method_hints[i]);
2180         }
2181         pfree(current_hint->join_hint_level);
2182         pfree(join_method_hints);
2183
2184         if (current_hint->num_hints[HINT_TYPE_LEADING] > 0 &&
2185                 hint_state_enabled(current_hint->leading_hint))
2186                 set_join_config_options(current_hint->init_join_mask, current_hint->context);
2187
2188         return rel;
2189 }
2190
2191 /*
2192  * set_rel_pathlist
2193  *        Build access paths for a base relation
2194  *
2195  * This function was copied and edited from set_rel_pathlist() in
2196  * src/backend/optimizer/path/allpaths.c
2197  */
2198 static void
2199 set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
2200                                  Index rti, RangeTblEntry *rte)
2201 {
2202         if (rte->inh)
2203         {
2204                 /* It's an "append relation", process accordingly */
2205                 set_append_rel_pathlist(root, rel, rti, rte);
2206         }
2207         else
2208         {
2209                 if (rel->rtekind == RTE_RELATION)
2210                 {
2211                         if (rte->relkind == RELKIND_RELATION)
2212                         {
2213                                 /* Plain relation */
2214                                 set_plain_rel_pathlist(root, rel, rte);
2215                         }
2216                         else
2217                                 elog(ERROR, "Unexpected relkind: %c", rte->relkind);
2218                 }
2219                 else
2220                         elog(ERROR, "Unexpected rtekind: %d", (int) rel->rtekind);
2221         }
2222 }
2223
2224 #define standard_join_search pg_hint_plan_standard_join_search
2225 #define join_search_one_level pg_hint_plan_join_search_one_level
2226 #define make_join_rel make_join_rel_wrapper
2227 #include "core.c"
2228
2229 #undef make_join_rel
2230 #define make_join_rel pg_hint_plan_make_join_rel
2231 #define add_paths_to_joinrel(root, joinrel, outerrel, innerrel, jointype, sjinfo, restrictlist) \
2232 do { \
2233         ScanMethodHint *hint = NULL; \
2234         if ((hint = find_scan_hint((root), (innerrel))) != NULL) \
2235         { \
2236                 set_scan_config_options(hint->enforce_mask, current_hint->context); \
2237                 hint->base.state = HINT_STATE_USED; \
2238         } \
2239         add_paths_to_joinrel((root), (joinrel), (outerrel), (innerrel), (jointype), (sjinfo), (restrictlist)); \
2240         if (hint != NULL) \
2241                 set_scan_config_options(current_hint->init_scan_mask, current_hint->context); \
2242 } while(0)
2243 #include "make_join_rel.c"