OSDN Git Service

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