OSDN Git Service

オブジェクト名の引用符付けルールを緩めた。
[pghintplan/pg_hint_plan.git] / pg_hint_plan.c
1 /*-------------------------------------------------------------------------
2  *
3  * pg_hint_plan.c
4  *                do instructions or hints to the planner using C-style block comments
5  *                of the SQL.
6  *
7  * Copyright (c) 2012, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
8  *
9  *-------------------------------------------------------------------------
10  */
11 #include "postgres.h"
12 #include "miscadmin.h"
13 #include "optimizer/geqo.h"
14 #include "optimizer/joininfo.h"
15 #include "optimizer/pathnode.h"
16 #include "optimizer/paths.h"
17 #include "optimizer/plancat.h"
18 #include "optimizer/planner.h"
19 #include "tcop/tcopprot.h"
20 #include "utils/lsyscache.h"
21
22 #ifdef PG_MODULE_MAGIC
23 PG_MODULE_MAGIC;
24 #endif
25
26 #if PG_VERSION_NUM < 90100
27 #error unsupported PostgreSQL version
28 #endif
29
30 #define HINT_START      "/*"
31 #define HINT_END        "*/"
32
33 /* hint keywords */
34 #define HINT_SEQSCAN                    "SeqScan"
35 #define HINT_INDEXSCAN                  "IndexScan"
36 #define HINT_BITMAPSCAN                 "BitmapScan"
37 #define HINT_TIDSCAN                    "TidScan"
38 #define HINT_NOSEQSCAN                  "NoSeqScan"
39 #define HINT_NOINDEXSCAN                "NoIndexScan"
40 #define HINT_NOBITMAPSCAN               "NoBitmapScan"
41 #define HINT_NOTIDSCAN                  "NoTidScan"
42 #if PG_VERSION_NUM >= 90200
43 #define HINT_INDEXONLYSCAN              "IndexonlyScan"
44 #define HINT_NOINDEXONLYSCAN    "NoIndexonlyScan"
45 #endif
46 #define HINT_NESTLOOP                   "NestLoop"
47 #define HINT_MERGEJOIN                  "MergeJoin"
48 #define HINT_HASHJOIN                   "HashJoin"
49 #define HINT_NONESTLOOP                 "NoNestLoop"
50 #define HINT_NOMERGEJOIN                "NoMergeJoin"
51 #define HINT_NOHASHJOIN                 "NoHashJoin"
52 #define HINT_LEADING                    "Leading"
53 #define HINT_SET                                "Set"
54
55
56 #define HINT_ARRAY_DEFAULT_INITSIZE 8
57
58 #define parse_ereport(str, detail) \
59         ereport(pg_hint_plan_parse_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 /*
770  * 二重引用符で囲まれているかもしれないトークンを読み取り word 引数に palloc
771  * で確保したバッファに格納してそのポインタを返す。
772  *
773  * 正常にパースできた場合は残りの文字列の先頭位置を、異常があった場合は NULL を
774  * 返す。
775  */
776 static const char *
777 parse_quote_value(const char *str, char **word, char *value_type)
778 {
779         StringInfoData  buf;
780         bool                    in_quote;
781
782         /* 先頭のスペースは読み飛ばす。 */
783         skip_space(str);
784
785         initStringInfo(&buf);
786         if (*str == '"')
787         {
788                 str++;
789                 in_quote = true;
790         }
791         else
792                 in_quote = false;
793
794         while (true)
795         {
796                 if (in_quote)
797                 {
798                         /* 二重引用符が閉じられていない場合はパース中断 */
799                         if (*str == '\0')
800                         {
801                                 pfree(buf.data);
802                                 parse_ereport(str, ("Unterminated quoted %s.", value_type));
803                                 return NULL;
804                         }
805
806                         /*
807                          * エスケープ対象のダブルクウォートをスキップする。
808                          * もしブロックコメントの開始文字列や終了文字列もオブジェクト名とし
809                          * て使用したい場合は、/ と * もエスケープ対象とすることで使用できる
810                          * が、処理対象としていない。もしテーブル名にこれらの文字が含まれる
811                          * 場合は、エイリアスを指定する必要がある。
812                          */
813                         if(*str == '"')
814                         {
815                                 str++;
816                                 if (*str != '"')
817                                         break;
818                         }
819                 }
820                 else
821                         if (isspace(*str) || *str == ')' || *str == '\0')
822                                 break;
823
824                 appendStringInfoCharMacro(&buf, *str++);
825         }
826
827         if (buf.len == 0)
828         {
829                 pfree(buf.data);
830                 parse_ereport(str, ("%s is necessary.", value_type));
831                 return NULL;
832         }
833
834         *word = buf.data;
835
836         return str;
837 }
838
839 static const char *
840 skip_option_delimiter(const char *str)
841 {
842         const char *p = str;
843
844         skip_space(str);
845
846         if (str == p)
847         {
848                 parse_ereport(str, ("Must be specified space."));
849                 return NULL;
850         }
851
852         return str;
853 }
854
855 static bool
856 parse_hints(PlanHint *plan, Query *parse, const char *str)
857 {
858         StringInfoData  buf;
859         char               *head;
860
861         initStringInfo(&buf);
862         while (*str != '\0')
863         {
864                 const HintParser *parser;
865
866                 /* in error message, we output the comment including the keyword. */
867                 head = (char *) str;
868
869                 /* parse only the keyword of the hint. */
870                 resetStringInfo(&buf);
871                 str = parse_keyword(str, &buf);
872
873                 for (parser = parsers; parser->keyword != NULL; parser++)
874                 {
875                         char   *keyword = parser->keyword;
876
877                         if (strcasecmp(buf.data, keyword) != 0)
878                                 continue;
879
880                         if (parser->have_paren)
881                         {
882                                 /* parser of each hint does parse in a parenthesis. */
883                                 if ((str = skip_opened_parenthesis(str)) == NULL ||
884                                         (str = parser->hint_parser(plan, parse, keyword, str)) == NULL ||
885                                         (str = skip_closed_parenthesis(str)) == NULL)
886                                 {
887                                         pfree(buf.data);
888                                         return false;
889                                 }
890                         }
891                         else
892                         {
893                                 if ((str = parser->hint_parser(plan, parse, keyword, str)) == NULL)
894                                 {
895                                         pfree(buf.data);
896                                         return false;
897                                 }
898
899                                 /*
900                                  * 直前のヒントに括弧の指定がなければ次のヒントの間に空白が必要
901                                  */
902                                 if (!isspace(*str) && *str == '\0')
903                                         parse_ereport(str, ("Delimiter of the hint is necessary."));
904                         }
905
906                         skip_space(str);
907
908                         break;
909                 }
910
911                 if (parser->keyword == NULL)
912                 {
913                         parse_ereport(head, ("Keyword \"%s\" does not exist.", buf.data));
914                         pfree(buf.data);
915                         return false;
916                 }
917         }
918
919         pfree(buf.data);
920
921         return true;
922 }
923
924 /*
925  * Do basic parsing of the query head comment.
926  */
927 static PlanHint *
928 parse_head_comment(Query *parse)
929 {
930         const char         *p;
931         char               *head;
932         char               *tail;
933         int                             len;
934         int                             i;
935         PlanHint           *plan;
936
937         /* get client-supplied query string. */
938         p = debug_query_string;
939         if (p == NULL)
940                 return NULL;
941
942
943         /* extract query head comment. */
944         len = strlen(HINT_START);
945         skip_space(p);
946         if (strncmp(p, HINT_START, len))
947                 return NULL;
948
949         p += len;
950         skip_space(p);
951
952         if ((tail = strstr(p, HINT_END)) == NULL)
953                 elog(ERROR, "unterminated /* comment at or near \"%s\"",
954                          debug_query_string);
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 parse_scan_method(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
1031         /*
1032          * ヒントごとに決まっている許容スキャン方式をビットマスクとして設定
1033          */
1034         if (strcasecmp(keyword, HINT_SEQSCAN) == 0)
1035                 hint->enforce_mask = ENABLE_SEQSCAN;
1036         else if (strcasecmp(keyword, HINT_INDEXSCAN) == 0)
1037                 hint->enforce_mask = ENABLE_INDEXSCAN;
1038         else if (strcasecmp(keyword, HINT_BITMAPSCAN) == 0)
1039                 hint->enforce_mask = ENABLE_BITMAPSCAN;
1040         else if (strcasecmp(keyword, HINT_TIDSCAN) == 0)
1041                 hint->enforce_mask = ENABLE_TIDSCAN;
1042         else if (strcasecmp(keyword, HINT_NOSEQSCAN) == 0)
1043                 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_SEQSCAN;
1044         else if (strcasecmp(keyword, HINT_NOINDEXSCAN) == 0)
1045                 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_INDEXSCAN;
1046         else if (strcasecmp(keyword, HINT_NOBITMAPSCAN) == 0)
1047                 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_BITMAPSCAN;
1048         else if (strcasecmp(keyword, HINT_NOTIDSCAN) == 0)
1049                 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_TIDSCAN;
1050         else
1051         {
1052                 elog(ERROR, "unrecognized hint keyword \"%s\"", keyword);
1053                 return NULL;
1054         }
1055
1056         /*
1057          * 出来上がったヒント情報を追加。スロットが足りない場合は二倍に拡張する。
1058          */
1059         if (plan->nscan_hints == 0)
1060         {
1061                 plan->max_scan_hints = HINT_ARRAY_DEFAULT_INITSIZE;
1062                 plan->scan_hints = palloc(sizeof(JoinHint *) * plan->max_scan_hints);
1063         }
1064         else if (plan->nscan_hints == plan->max_scan_hints)
1065         {
1066                 plan->max_scan_hints *= 2;
1067                 plan->scan_hints = repalloc(plan->scan_hints,
1068                                                                 sizeof(JoinHint *) * plan->max_scan_hints);
1069         }
1070
1071         plan->scan_hints[plan->nscan_hints] = hint;
1072         plan->nscan_hints++;
1073
1074         return str;
1075 }
1076
1077 static const char *
1078 ParseScanMethod(PlanHint *plan, Query *parse, char *keyword, const char *str)
1079 {
1080         ScanHint   *hint;
1081
1082         if ((str = parse_scan_method(plan, parse, keyword, str)) == NULL)
1083                 return NULL;
1084
1085         hint = plan->scan_hints[plan->nscan_hints - 1];
1086
1087         skip_space(str);
1088         if (*str != ')')
1089         {
1090                 parse_ereport(str, ("Closed parenthesis is necessary."));
1091                 plan->nscan_hints--;
1092                 ScanHintDelete(hint);
1093                 return NULL;
1094         }
1095
1096         return str;
1097 }
1098
1099 static const char *
1100 ParseIndexScanMethod(PlanHint *plan, Query *parse, char *keyword, const char *str)
1101 {
1102         char       *indexname;
1103         ScanHint   *hint;
1104
1105         if ((str = parse_scan_method(plan, parse, keyword, str)) == NULL)
1106                 return NULL;
1107
1108         hint = plan->scan_hints[plan->nscan_hints - 1];
1109
1110         /* インデックス参照をパースする。 */
1111         while (true)
1112         {
1113                 /*
1114                  * TODO 直前のオブジェクト名がクウォート処理されていた場合の処理を実装
1115                  */
1116                 skip_space(str);
1117                 if (*str == ')')
1118                         break;
1119
1120                 if ((str = parse_quote_value(str, &indexname, "index name")) == NULL)
1121                 {
1122                         plan->nscan_hints--;
1123                         ScanHintDelete(hint);
1124                         return NULL;
1125                 }
1126
1127                 hint->indexnames = lappend(hint->indexnames, indexname);
1128         }
1129
1130         return str;
1131 }
1132
1133 static const char *
1134 ParseJoinMethod(PlanHint *plan, Query *parse, char *keyword, const char *str)
1135 {
1136         char       *relname;
1137         JoinHint   *hint;
1138
1139         skip_space(str);
1140
1141         hint = JoinHintCreate();
1142         hint->opt_str = str;
1143         hint->relnames = palloc(sizeof(char *));
1144
1145         while ((str = parse_quote_value(str, &relname, "table name")) != NULL)
1146         {
1147                 hint->nrels++;
1148                 hint->relnames = repalloc(hint->relnames, sizeof(char *) * hint->nrels);
1149                 hint->relnames[hint->nrels - 1] = relname;
1150
1151                 skip_space(str);
1152                 if (*str == ')')
1153                         break;
1154         }
1155
1156         if (str == NULL)
1157         {
1158                 JoinHintDelete(hint);
1159                 return NULL;
1160         }
1161
1162         /* Join 対象のテーブルは最低でも2つ指定する必要がある */
1163         if (hint->nrels < 2)
1164         {
1165                 JoinHintDelete(hint);
1166                 parse_ereport(str, ("Specified relation more than two."));
1167                 return NULL;
1168         }
1169
1170         /* テーブル名順にソートする */
1171         qsort(hint->relnames, hint->nrels, sizeof(char *), RelnameCmp);
1172
1173         if (strcasecmp(keyword, HINT_NESTLOOP) == 0)
1174                 hint->enforce_mask = ENABLE_NESTLOOP;
1175         else if (strcasecmp(keyword, HINT_MERGEJOIN) == 0)
1176                 hint->enforce_mask = ENABLE_MERGEJOIN;
1177         else if (strcasecmp(keyword, HINT_HASHJOIN) == 0)
1178                 hint->enforce_mask = ENABLE_HASHJOIN;
1179         else if (strcasecmp(keyword, HINT_NONESTLOOP) == 0)
1180                 hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_NESTLOOP;
1181         else if (strcasecmp(keyword, HINT_NOMERGEJOIN) == 0)
1182                 hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_MERGEJOIN;
1183         else if (strcasecmp(keyword, HINT_NOHASHJOIN) == 0)
1184                 hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_HASHJOIN;
1185         else
1186                 elog(ERROR, "unrecognized hint keyword \"%s\"", keyword);
1187
1188         if (plan->njoin_hints == 0)
1189         {
1190                 plan->max_join_hints = HINT_ARRAY_DEFAULT_INITSIZE;
1191                 plan->join_hints = palloc(sizeof(JoinHint *) * plan->max_join_hints);
1192         }
1193         else if (plan->njoin_hints == plan->max_join_hints)
1194         {
1195                 plan->max_join_hints *= 2;
1196                 plan->join_hints = repalloc(plan->join_hints,
1197                                                                 sizeof(JoinHint *) * plan->max_join_hints);
1198         }
1199
1200         plan->join_hints[plan->njoin_hints] = hint;
1201         plan->njoin_hints++;
1202
1203         return str;
1204 }
1205
1206 static const char *
1207 ParseLeading(PlanHint *plan, Query *parse, char *keyword, const char *str)
1208 {
1209         char   *relname;
1210
1211         /*
1212          * すでに指定済みの場合は、後で指定したヒントが有効にするため、登録済みの
1213          * 情報を削除する
1214          */
1215         list_free_deep(plan->leading);
1216         plan->leading = NIL;
1217
1218         while ((str = parse_quote_value(str, &relname, "relation name")) != NULL)
1219         {
1220                 const char *p;
1221
1222                 plan->leading = lappend(plan->leading, relname);
1223
1224                 p = str;
1225                 skip_space(str);
1226                 if (*str == ')')
1227                         break;
1228
1229                 if (p == str)
1230                 {
1231                         parse_ereport(str, ("Must be specified space."));
1232                         break;
1233                 }
1234         }
1235
1236         /* テーブル指定が1つのみの場合は、ヒントを無効にし、パースを続ける */
1237         if (list_length(plan->leading) == 1)
1238         {
1239                 parse_ereport(str, ("In %s hint, specified relation name 2 or more.", HINT_LEADING));
1240                 list_free_deep(plan->leading);
1241                 plan->leading = NIL;
1242         }
1243
1244         return str;
1245 }
1246
1247 static const char *
1248 ParseSet(PlanHint *plan, Query *parse, char *keyword, const char *str)
1249 {
1250         SetHint    *hint;
1251
1252         hint = SetHintCreate();
1253
1254         if ((str = parse_quote_value(str, &hint->name, "parameter name")) == NULL ||
1255                 (str = skip_option_delimiter(str)) == NULL ||
1256                 (str = parse_quote_value(str, &hint->value, "parameter value")) == NULL)
1257         {
1258                 SetHintDelete(hint);
1259                 return NULL;
1260         }
1261
1262         skip_space(str);
1263         if (*str != ')')
1264         {
1265                 parse_ereport(str, ("Closed parenthesis is necessary."));
1266                 SetHintDelete(hint);
1267                 return NULL;
1268         }
1269         plan->set_hints = lappend(plan->set_hints, hint);
1270
1271         return str;
1272 }
1273
1274 #ifdef NOT_USED
1275 /*
1276  * Oracle の ORDERD ヒントの実装
1277  */
1278 static const char *
1279 Ordered(PlanHint *plan, Query *parse, char *keyword, const char *str)
1280 {
1281         SetHint    *hint;
1282
1283         hint = SetHintCreate();
1284         hint->name = pstrdup("join_collapse_limit");
1285         hint->value = pstrdup("1");
1286         plan->set_hints = lappend(plan->set_hints, hint);
1287
1288         return str;
1289 }
1290 #endif
1291
1292 static PlannedStmt *
1293 pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
1294 {
1295         int                             save_nestlevel;
1296         PlannedStmt        *result;
1297
1298         /*
1299          * hintが指定されない、または空のhintを指定された場合は通常のparser処理をお
1300          * こなう。
1301          * 他のフック関数で実行されるhint処理をスキップするために、global 変数をNULL
1302          * に設定しておく。
1303          */
1304         if (!pg_hint_plan_enable ||
1305                 (global = parse_head_comment(parse)) == NULL ||
1306                 PlanHintIsempty(global))
1307         {
1308                 PlanHintDelete(global);
1309                 global = NULL;
1310
1311                 if (prev_planner_hook)
1312                         return (*prev_planner_hook) (parse, cursorOptions, boundParams);
1313                 else
1314                         return standard_planner(parse, cursorOptions, boundParams);
1315         }
1316
1317         /* Set hint で指定されたGUCパラメータを設定する */
1318         save_nestlevel = set_config_options(global->set_hints, global->context);
1319
1320         if (global->leading != NIL)
1321                 set_join_config_options(0, global->context);
1322
1323         /*
1324          * TODO ビュー定義で指定したテーブル数が1つの場合にもこのタイミングでGUCを変更する必
1325          * 要がある。
1326          */
1327         if (list_length(parse->rtable) == 1 &&
1328                 ((RangeTblEntry *) linitial(parse->rtable))->rtekind == RTE_RELATION)
1329         {
1330                 int     i;
1331                 RangeTblEntry  *rte = linitial(parse->rtable);
1332                 
1333                 for (i = 0; i < global->nscan_hints; i++)
1334                 {
1335                         ScanHint           *hint = global->scan_hints[i];
1336
1337                         if (strcmp(hint->relname, rte->eref->aliasname) != 0)
1338                                 parse_ereport(hint->opt_str, ("Relation \"%s\" does not exist.", hint->relname));
1339
1340                         set_scan_config_options(hint->enforce_mask, global->context);
1341                 }
1342         }
1343
1344         if (prev_planner_hook)
1345                 result = (*prev_planner_hook) (parse, cursorOptions, boundParams);
1346         else
1347                 result = standard_planner(parse, cursorOptions, boundParams);
1348
1349         /*
1350          * Restore the GUC variables we set above.
1351          */
1352         AtEOXact_GUC(true, save_nestlevel);
1353
1354         if (pg_hint_plan_debug_print)
1355         {
1356                 PlanHintDump(global);
1357 #ifdef NOT_USED
1358                 elog_node_display(INFO, "rtable", parse->rtable, true);
1359 #endif
1360         }
1361
1362         PlanHintDelete(global);
1363         global = NULL;
1364
1365         return result;
1366 }
1367
1368 /*
1369  * aliasnameと一致するSCANヒントを探す
1370  */
1371 static ScanHint *
1372 find_scan_hint(RangeTblEntry *rte)
1373 {
1374         int     i;
1375
1376         for (i = 0; i < global->nscan_hints; i++)
1377         {
1378                 ScanHint   *hint = global->scan_hints[i];
1379
1380                 if (strcmp(rte->eref->aliasname, hint->relname) == 0)
1381                         return hint;
1382         }
1383
1384         return NULL;
1385 }
1386
1387 static void
1388 pg_hint_plan_get_relation_info(PlannerInfo *root, Oid relationObjectId,
1389                                                                  bool inhparent, RelOptInfo *rel)
1390 {
1391         ScanHint   *hint;
1392         ListCell   *cell;
1393         ListCell   *prev;
1394         ListCell   *next;
1395
1396         if (prev_get_relation_info)
1397                 (*prev_get_relation_info) (root, relationObjectId, inhparent, rel);
1398
1399         /* 有効なヒントが指定されなかった場合は処理をスキップする。 */
1400         if (!global)
1401                 return;
1402
1403         if (rel->reloptkind != RELOPT_BASEREL)
1404                 return;
1405
1406         if ((hint = find_scan_hint(root->simple_rte_array[rel->relid])) == NULL)
1407                 return;
1408
1409         /* インデックスを全て削除し、スキャンに使えなくする */
1410         if (hint->enforce_mask == ENABLE_SEQSCAN)
1411         {
1412                 list_free_deep(rel->indexlist);
1413                 rel->indexlist = NIL;
1414
1415                 return;
1416         }
1417
1418         /* 後でパスを作り直すため、ここではなにもしない */
1419         if (hint->indexnames == NULL)
1420                 return;
1421
1422         /* 指定されたインデックスのみをのこす */
1423         prev = NULL;
1424         for (cell = list_head(rel->indexlist); cell; cell = next)
1425         {
1426                 IndexOptInfo   *info = (IndexOptInfo *) lfirst(cell);
1427                 char               *indexname = get_rel_name(info->indexoid);
1428                 ListCell           *l;
1429                 bool                    use_index = false;
1430
1431                 next = lnext(cell);
1432
1433                 foreach(l, hint->indexnames)
1434                 {
1435                         if (strcmp(indexname, lfirst(l)) == 0)
1436                         {
1437                                 use_index = true;
1438                                 break;
1439                         }
1440                 }
1441
1442                 if (!use_index)
1443                         rel->indexlist = list_delete_cell(rel->indexlist, cell, prev);
1444                 else
1445                         prev = cell;
1446
1447                 pfree(indexname);
1448         }
1449 }
1450
1451 static Index
1452 scan_relid_aliasname(PlannerInfo *root, char *aliasname, bool check_ambiguous, const char *str)
1453 {
1454         /* TODO refnameRangeTblEntry を参考 */
1455         int             i;
1456         Index   find = 0;
1457
1458         for (i = 1; i < root->simple_rel_array_size; i++)
1459         {
1460                 if (root->simple_rel_array[i] == NULL)
1461                         continue;
1462
1463                 Assert(i == root->simple_rel_array[i]->relid);
1464
1465                 if (strcmp(aliasname, root->simple_rte_array[i]->eref->aliasname) != 0)
1466                         continue;
1467
1468                 if (!check_ambiguous)
1469                         return i;
1470
1471                 if (find)
1472                         parse_ereport(str, ("relation name \"%s\" is ambiguous", aliasname));
1473
1474                 find = i;
1475         }
1476
1477         return find;
1478 }
1479
1480 /*
1481  * relidビットマスクと一致するヒントを探す
1482  */
1483 static JoinHint *
1484 scan_join_hint(Relids joinrelids)
1485 {
1486         List       *join_hint;
1487         ListCell   *l;
1488
1489         join_hint = global->join_hint_level[bms_num_members(joinrelids)];
1490         foreach(l, join_hint)
1491         {
1492                 JoinHint   *hint = (JoinHint *) lfirst(l);
1493                 if (bms_equal(joinrelids, hint->joinrelids))
1494                         return hint;
1495         }
1496
1497         return NULL;
1498 }
1499
1500 /*
1501  * ヒントを使用しやすい構造に変換する。
1502  */
1503 static void
1504 rebuild_join_hints(PlanHint *plan, PlannerInfo *root, int level, List *initial_rels)
1505 {
1506         int                     i;
1507         ListCell   *l;
1508         Relids          joinrelids;
1509         int                     njoinrels;
1510
1511         plan->nlevel = root->simple_rel_array_size - 1;
1512         plan->join_hint_level = palloc0(sizeof(List *) * (root->simple_rel_array_size));
1513         for (i = 0; i < plan->njoin_hints; i++)
1514         {
1515                 JoinHint   *hint = plan->join_hints[i];
1516                 int                     j;
1517                 Index           relid = 0;
1518
1519                 for (j = 0; j < hint->nrels; j++)
1520                 {
1521                         char   *relname = hint->relnames[j];
1522
1523                         relid = scan_relid_aliasname(root, relname, true, hint->opt_str);
1524                         if (relid == 0)
1525                         {
1526                                 parse_ereport(hint->opt_str, ("Relation \"%s\" does not exist.", relname));
1527                                 break;
1528                         }
1529
1530                         hint->joinrelids = bms_add_member(hint->joinrelids, relid);
1531                 }
1532
1533                 if (relid == 0)
1534                         continue;
1535
1536                 plan->join_hint_level[hint->nrels] =
1537                         lappend(plan->join_hint_level[hint->nrels], hint);
1538         }
1539
1540         /* Leading hint は、全ての join 方式が有効な hint として登録する */
1541         joinrelids = NULL;
1542         njoinrels = 0;
1543         foreach(l, plan->leading)
1544         {
1545                 char       *relname = (char *)lfirst(l);
1546                 JoinHint   *hint;
1547
1548                 i = scan_relid_aliasname(root, relname, true, plan->hint_str);
1549                 if (i == 0)
1550                 {
1551                         parse_ereport(plan->hint_str, ("Relation \"%s\" does not exist.", relname));
1552                         list_free_deep(plan->leading);
1553                         plan->leading = NIL;
1554                         break;
1555                 }
1556
1557                 joinrelids = bms_add_member(joinrelids, i);
1558                 njoinrels++;
1559
1560                 if (njoinrels < 2)
1561                         continue;
1562
1563                 if (njoinrels > plan->nlevel)
1564                 {
1565                         parse_ereport(plan->hint_str, ("In %s hint, specified relation name %d or less.", HINT_LEADING, plan->nlevel));
1566                         break;
1567                 }
1568
1569                 /* Leading で指定した組み合わせ以外の join hint を削除する */
1570                 hint = scan_join_hint(joinrelids);
1571                 list_free(plan->join_hint_level[njoinrels]);
1572                 if (hint)
1573                         plan->join_hint_level[njoinrels] = lappend(NIL, hint);
1574                 else
1575                 {
1576                         /*
1577                          * Here relnames is not set, since Relids bitmap is sufficient to
1578                          * control paths of this query afterwards.
1579                          */
1580                         hint = JoinHintCreate();
1581                         hint->nrels = njoinrels;
1582                         hint->enforce_mask = ENABLE_ALL_JOIN;
1583                         hint->joinrelids = bms_copy(joinrelids);
1584                         plan->join_hint_level[njoinrels] = lappend(NIL, hint);
1585
1586                         if (plan->njoin_hints == 0)
1587                         {
1588                                 plan->max_join_hints = HINT_ARRAY_DEFAULT_INITSIZE;
1589                                 plan->join_hints = palloc(sizeof(JoinHint *) * plan->max_join_hints);
1590                         }
1591                         else if (plan->njoin_hints == plan->max_join_hints)
1592                         {
1593                                 plan->max_join_hints *= 2;
1594                                 plan->join_hints = repalloc(plan->join_hints,
1595                                                                         sizeof(JoinHint *) * plan->max_join_hints);
1596                         }
1597
1598                         plan->join_hints[plan->njoin_hints] = hint;
1599                         plan->njoin_hints++;
1600                 }
1601         }
1602
1603         bms_free(joinrelids);
1604 }
1605
1606 static void
1607 rebuild_scan_path(PlanHint *plan, PlannerInfo *root, int level, List *initial_rels)
1608 {
1609         int     i;
1610         int     save_nestlevel = 0;
1611
1612         for (i = 0; i < plan->nscan_hints; i++)
1613         {
1614                 ScanHint   *hint = plan->scan_hints[i];
1615                 ListCell   *l;
1616
1617                 if (hint->enforce_mask == ENABLE_SEQSCAN)
1618                         continue;
1619
1620                 foreach(l, initial_rels)
1621                 {
1622                         RelOptInfo         *rel = (RelOptInfo *) lfirst(l);
1623                         RangeTblEntry  *rte = root->simple_rte_array[rel->relid];
1624
1625                         if (rel->reloptkind != RELOPT_BASEREL ||
1626                                 strcmp(hint->relname, rte->eref->aliasname) != 0)
1627                                 continue;
1628
1629                         if (save_nestlevel != 0)
1630                                 save_nestlevel = NewGUCNestLevel();
1631
1632                         /*
1633                          * TODO ヒントで指定されたScan方式が最安価でない場合のみ、Pathを生成
1634                          * しなおす
1635                          */
1636                         set_scan_config_options(hint->enforce_mask, plan->context);
1637
1638                         rel->pathlist = NIL;    /* TODO 解放 */
1639                         set_plain_rel_pathlist(root, rel, rte);
1640
1641                         break;
1642                 }
1643         }
1644
1645         /*
1646          * Restore the GUC variables we set above.
1647          */
1648         if (save_nestlevel != 0)
1649                 AtEOXact_GUC(true, save_nestlevel);
1650 }
1651
1652 /*
1653  * src/backend/optimizer/path/joinrels.c
1654  * export make_join_rel() をラップする関数
1655  * 
1656  * ヒントにしたがって、enabele_* パラメータを変更した上で、make_join_rel()を
1657  * 呼び出す。
1658  */
1659 static RelOptInfo *
1660 pg_hint_plan_make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
1661 {
1662         Relids                  joinrelids;
1663         JoinHint           *hint;
1664         RelOptInfo         *rel;
1665         int                             save_nestlevel;
1666
1667         joinrelids = bms_union(rel1->relids, rel2->relids);
1668         hint = scan_join_hint(joinrelids);
1669         bms_free(joinrelids);
1670
1671         if (!hint)
1672                 return make_join_rel(root, rel1, rel2);
1673
1674         save_nestlevel = NewGUCNestLevel();
1675
1676         set_join_config_options(hint->enforce_mask, global->context);
1677
1678         rel = make_join_rel(root, rel1, rel2);
1679
1680         /*
1681          * Restore the GUC variables we set above.
1682          */
1683         AtEOXact_GUC(true, save_nestlevel);
1684
1685         return rel;
1686 }
1687
1688 static RelOptInfo *
1689 pg_hint_plan_join_search(PlannerInfo *root, int levels_needed, List *initial_rels)
1690 {
1691         /*
1692          * pg_hint_planが無効、または有効なヒントが1つも指定されなかった場合は、標準
1693          * の処理を行う。
1694          */
1695         if (!global)
1696         {
1697                 if (prev_join_search)
1698                         return (*prev_join_search) (root, levels_needed, initial_rels);
1699                 else if (enable_geqo && levels_needed >= geqo_threshold)
1700                         return geqo(root, levels_needed, initial_rels);
1701                 else
1702                         return standard_join_search(root, levels_needed, initial_rels);
1703         }
1704
1705         rebuild_join_hints(global, root, levels_needed, initial_rels);
1706         rebuild_scan_path(global, root, levels_needed, initial_rels);
1707
1708         /*
1709          * GEQOを使用する条件を満たした場合は、GEQOを用いた結合方式の検索を行う。
1710          * このとき、スキャン方式のヒントとSetヒントのみが有効になり、結合方式や結合
1711          * 順序はヒント句は無効になりGEQOのアルゴリズムで決定される。
1712          */
1713         if (enable_geqo && levels_needed >= geqo_threshold)
1714                 return geqo(root, levels_needed, initial_rels);
1715
1716         return standard_join_search_org(root, levels_needed, initial_rels);
1717 }
1718
1719 #define standard_join_search standard_join_search_org
1720 #define join_search_one_level pg_hint_plan_join_search_one_level
1721 #define make_join_rel pg_hint_plan_make_join_rel
1722 #include "core.c"