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