OSDN Git Service

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