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