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