OSDN Git Service

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