OSDN Git Service

Don't call elog(ERROR) during parsing hints.
[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 = RelnameCmp(&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 = RelnameCmp(&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         /* extract query head comment. */
943         len = strlen(HINT_START);
944         skip_space(p);
945         if (strncmp(p, HINT_START, len))
946                 return NULL;
947
948         p += len;
949         skip_space(p);
950
951         if ((tail = strstr(p, HINT_END)) == NULL)
952         {
953                 parse_ereport(debug_query_string, ("unterminated /* comment"));
954                 return NULL;
955         }
956
957         /* 入れ子にしたブロックコメントはサポートしない */
958         if ((head = strstr(p, HINT_START)) != NULL && head < tail)
959                 parse_ereport(head, ("block comments nest doesn't supported"));
960
961         /* ヒント句部分を切り出す */
962         len = tail - p;
963         head = palloc(len + 1);
964         memcpy(head, p, len);
965         head[len] = '\0';
966         p = head;
967
968         plan = PlanHintCreate();
969         plan->hint_str = head;
970
971         /* parse each hint. */
972         if (!parse_hints(plan, parse, p))
973                 return plan;
974
975         /* 重複したScan条件をを除外する */
976         qsort(plan->scan_hints, plan->nscan_hints, sizeof(ScanHint *), ScanHintCmpIsOrder);
977         for (i = 0; i < plan->nscan_hints - 1;)
978         {
979                 int     result = ScanHintCmp(plan->scan_hints + i,
980                                                 plan->scan_hints + i + 1, false);
981                 if (result != 0)
982                         i++;
983                 else
984                 {
985                         /* 後で指定したヒントを有効にする */
986                         plan->nscan_hints--;
987                         memmove(plan->scan_hints + i, plan->scan_hints + i + 1,
988                                         sizeof(ScanHint *) * (plan->nscan_hints - i));
989                 }
990         }
991
992         /* 重複したJoin条件をを除外する */
993         qsort(plan->join_hints, plan->njoin_hints, sizeof(JoinHint *), JoinHintCmpIsOrder);
994         for (i = 0; i < plan->njoin_hints - 1;)
995         {
996                 int     result = JoinHintCmp(plan->join_hints + i,
997                                                 plan->join_hints + i + 1, false);
998                 if (result != 0)
999                         i++;
1000                 else
1001                 {
1002                         /* 後で指定したヒントを有効にする */
1003                         plan->njoin_hints--;
1004                         memmove(plan->join_hints + i, plan->join_hints + i + 1,
1005                                         sizeof(JoinHint *) * (plan->njoin_hints - i));
1006                 }
1007         }
1008
1009         return plan;
1010 }
1011
1012 /*
1013  * スキャン方式ヒントのパースでの共通処理
1014  */
1015 static const char *
1016 parse_scan_method(PlanHint *plan, Query *parse, char *keyword, const char *str)
1017 {
1018         ScanHint   *hint;
1019
1020         hint = ScanHintCreate();
1021         hint->opt_str = str;
1022
1023         /*
1024          * スキャン方式のヒントでリレーション名が読み取れない場合はヒント無効
1025          */
1026         if ((str = parse_quote_value(str, &hint->relname, "ralation name")) == NULL)
1027         {
1028                 ScanHintDelete(hint);
1029                 return NULL;
1030         }
1031
1032         /*
1033          * ヒントごとに決まっている許容スキャン方式をビットマスクとして設定
1034          */
1035         if (strcasecmp(keyword, HINT_SEQSCAN) == 0)
1036                 hint->enforce_mask = ENABLE_SEQSCAN;
1037         else if (strcasecmp(keyword, HINT_INDEXSCAN) == 0)
1038                 hint->enforce_mask = ENABLE_INDEXSCAN;
1039         else if (strcasecmp(keyword, HINT_BITMAPSCAN) == 0)
1040                 hint->enforce_mask = ENABLE_BITMAPSCAN;
1041         else if (strcasecmp(keyword, HINT_TIDSCAN) == 0)
1042                 hint->enforce_mask = ENABLE_TIDSCAN;
1043         else if (strcasecmp(keyword, HINT_NOSEQSCAN) == 0)
1044                 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_SEQSCAN;
1045         else if (strcasecmp(keyword, HINT_NOINDEXSCAN) == 0)
1046                 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_INDEXSCAN;
1047         else if (strcasecmp(keyword, HINT_NOBITMAPSCAN) == 0)
1048                 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_BITMAPSCAN;
1049         else if (strcasecmp(keyword, HINT_NOTIDSCAN) == 0)
1050                 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_TIDSCAN;
1051         else
1052         {
1053                 ScanHintDelete(hint);
1054                 parse_ereport(str, ("unrecognized hint keyword \"%s\"", keyword));
1055                 return NULL;
1056         }
1057
1058         /*
1059          * 出来上がったヒント情報を追加。スロットが足りない場合は二倍に拡張する。
1060          */
1061         if (plan->nscan_hints == 0)
1062         {
1063                 plan->max_scan_hints = HINT_ARRAY_DEFAULT_INITSIZE;
1064                 plan->scan_hints = palloc(sizeof(JoinHint *) * plan->max_scan_hints);
1065         }
1066         else if (plan->nscan_hints == plan->max_scan_hints)
1067         {
1068                 plan->max_scan_hints *= 2;
1069                 plan->scan_hints = repalloc(plan->scan_hints,
1070                                                                 sizeof(JoinHint *) * plan->max_scan_hints);
1071         }
1072
1073         plan->scan_hints[plan->nscan_hints] = hint;
1074         plan->nscan_hints++;
1075
1076         return str;
1077 }
1078
1079 static const char *
1080 ParseScanMethod(PlanHint *plan, Query *parse, char *keyword, const char *str)
1081 {
1082         ScanHint   *hint;
1083
1084         if ((str = parse_scan_method(plan, parse, keyword, str)) == NULL)
1085                 return NULL;
1086
1087         hint = plan->scan_hints[plan->nscan_hints - 1];
1088
1089         skip_space(str);
1090         if (*str != ')')
1091         {
1092                 parse_ereport(str, ("Closed parenthesis is necessary."));
1093                 plan->nscan_hints--;
1094                 ScanHintDelete(hint);
1095                 return NULL;
1096         }
1097
1098         return str;
1099 }
1100
1101 static const char *
1102 ParseIndexScanMethod(PlanHint *plan, Query *parse, char *keyword, const char *str)
1103 {
1104         char       *indexname;
1105         ScanHint   *hint;
1106
1107         if ((str = parse_scan_method(plan, parse, keyword, str)) == NULL)
1108                 return NULL;
1109
1110         hint = plan->scan_hints[plan->nscan_hints - 1];
1111
1112         /* インデックス参照をパースする。 */
1113         while (true)
1114         {
1115                 /*
1116                  * TODO 直前のオブジェクト名がクウォート処理されていた場合の処理を実装
1117                  */
1118                 skip_space(str);
1119                 if (*str == ')')
1120                         break;
1121
1122                 if ((str = parse_quote_value(str, &indexname, "index name")) == NULL)
1123                 {
1124                         plan->nscan_hints--;
1125                         ScanHintDelete(hint);
1126                         return NULL;
1127                 }
1128
1129                 hint->indexnames = lappend(hint->indexnames, indexname);
1130         }
1131
1132         return str;
1133 }
1134
1135 static const char *
1136 ParseJoinMethod(PlanHint *plan, Query *parse, char *keyword, const char *str)
1137 {
1138         char       *relname;
1139         JoinHint   *hint;
1140
1141         skip_space(str);
1142
1143         hint = JoinHintCreate();
1144         hint->opt_str = str;
1145         hint->relnames = palloc(sizeof(char *));
1146
1147         while ((str = parse_quote_value(str, &relname, "table name")) != NULL)
1148         {
1149                 hint->nrels++;
1150                 hint->relnames = repalloc(hint->relnames, sizeof(char *) * hint->nrels);
1151                 hint->relnames[hint->nrels - 1] = relname;
1152
1153                 skip_space(str);
1154                 if (*str == ')')
1155                         break;
1156         }
1157
1158         if (str == NULL)
1159         {
1160                 JoinHintDelete(hint);
1161                 return NULL;
1162         }
1163
1164         /* Join 対象のテーブルは最低でも2つ指定する必要がある */
1165         if (hint->nrels < 2)
1166         {
1167                 JoinHintDelete(hint);
1168                 parse_ereport(str, ("Specified relation more than two."));
1169                 return NULL;
1170         }
1171
1172         /* テーブル名順にソートする */
1173         qsort(hint->relnames, hint->nrels, sizeof(char *), RelnameCmp);
1174
1175         if (strcasecmp(keyword, HINT_NESTLOOP) == 0)
1176                 hint->enforce_mask = ENABLE_NESTLOOP;
1177         else if (strcasecmp(keyword, HINT_MERGEJOIN) == 0)
1178                 hint->enforce_mask = ENABLE_MERGEJOIN;
1179         else if (strcasecmp(keyword, HINT_HASHJOIN) == 0)
1180                 hint->enforce_mask = ENABLE_HASHJOIN;
1181         else if (strcasecmp(keyword, HINT_NONESTLOOP) == 0)
1182                 hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_NESTLOOP;
1183         else if (strcasecmp(keyword, HINT_NOMERGEJOIN) == 0)
1184                 hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_MERGEJOIN;
1185         else if (strcasecmp(keyword, HINT_NOHASHJOIN) == 0)
1186                 hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_HASHJOIN;
1187         else
1188         {
1189                 JoinHintDelete(hint);
1190                 parse_ereport(str, ("unrecognized hint keyword \"%s\"", keyword));
1191                 return NULL;
1192         }
1193
1194         if (plan->njoin_hints == 0)
1195         {
1196                 plan->max_join_hints = HINT_ARRAY_DEFAULT_INITSIZE;
1197                 plan->join_hints = palloc(sizeof(JoinHint *) * plan->max_join_hints);
1198         }
1199         else if (plan->njoin_hints == plan->max_join_hints)
1200         {
1201                 plan->max_join_hints *= 2;
1202                 plan->join_hints = repalloc(plan->join_hints,
1203                                                                 sizeof(JoinHint *) * plan->max_join_hints);
1204         }
1205
1206         plan->join_hints[plan->njoin_hints] = hint;
1207         plan->njoin_hints++;
1208
1209         return str;
1210 }
1211
1212 static const char *
1213 ParseLeading(PlanHint *plan, Query *parse, char *keyword, const char *str)
1214 {
1215         char   *relname;
1216
1217         /*
1218          * すでに指定済みの場合は、後で指定したヒントが有効にするため、登録済みの
1219          * 情報を削除する
1220          */
1221         list_free_deep(plan->leading);
1222         plan->leading = NIL;
1223
1224         while ((str = parse_quote_value(str, &relname, "relation name")) != NULL)
1225         {
1226                 const char *p;
1227
1228                 plan->leading = lappend(plan->leading, relname);
1229
1230                 p = str;
1231                 skip_space(str);
1232                 if (*str == ')')
1233                         break;
1234
1235                 if (p == str)
1236                 {
1237                         parse_ereport(str, ("Must be specified space."));
1238                         break;
1239                 }
1240         }
1241
1242         /* テーブル指定が1つのみの場合は、ヒントを無効にし、パースを続ける */
1243         if (list_length(plan->leading) == 1)
1244         {
1245                 parse_ereport(str, ("In %s hint, specified relation name 2 or more.", HINT_LEADING));
1246                 list_free_deep(plan->leading);
1247                 plan->leading = NIL;
1248         }
1249
1250         return str;
1251 }
1252
1253 static const char *
1254 ParseSet(PlanHint *plan, Query *parse, char *keyword, const char *str)
1255 {
1256         SetHint    *hint;
1257
1258         hint = SetHintCreate();
1259
1260         if ((str = parse_quote_value(str, &hint->name, "parameter name")) == NULL ||
1261                 (str = skip_option_delimiter(str)) == NULL ||
1262                 (str = parse_quote_value(str, &hint->value, "parameter value")) == NULL)
1263         {
1264                 SetHintDelete(hint);
1265                 return NULL;
1266         }
1267
1268         skip_space(str);
1269         if (*str != ')')
1270         {
1271                 parse_ereport(str, ("Closed parenthesis is necessary."));
1272                 SetHintDelete(hint);
1273                 return NULL;
1274         }
1275         plan->set_hints = lappend(plan->set_hints, hint);
1276
1277         return str;
1278 }
1279
1280 #ifdef NOT_USED
1281 /*
1282  * Oracle の ORDERD ヒントの実装
1283  */
1284 static const char *
1285 Ordered(PlanHint *plan, Query *parse, char *keyword, const char *str)
1286 {
1287         SetHint    *hint;
1288
1289         hint = SetHintCreate();
1290         hint->name = pstrdup("join_collapse_limit");
1291         hint->value = pstrdup("1");
1292         plan->set_hints = lappend(plan->set_hints, hint);
1293
1294         return str;
1295 }
1296 #endif
1297
1298 static PlannedStmt *
1299 pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
1300 {
1301         int                             save_nestlevel;
1302         PlannedStmt        *result;
1303
1304         /*
1305          * hintが指定されない、または空のhintを指定された場合は通常のparser処理をお
1306          * こなう。
1307          * 他のフック関数で実行されるhint処理をスキップするために、global 変数をNULL
1308          * に設定しておく。
1309          */
1310         if (!pg_hint_plan_enable ||
1311                 (global = parse_head_comment(parse)) == NULL ||
1312                 PlanHintIsempty(global))
1313         {
1314                 PlanHintDelete(global);
1315                 global = NULL;
1316
1317                 if (prev_planner_hook)
1318                         return (*prev_planner_hook) (parse, cursorOptions, boundParams);
1319                 else
1320                         return standard_planner(parse, cursorOptions, boundParams);
1321         }
1322
1323         /* Set hint で指定されたGUCパラメータを設定する */
1324         save_nestlevel = set_config_options(global->set_hints, global->context);
1325
1326         if (global->leading != NIL)
1327                 set_join_config_options(0, global->context);
1328
1329         /*
1330          * TODO ビュー定義で指定したテーブル数が1つの場合にもこのタイミングでGUCを変更する必
1331          * 要がある。
1332          */
1333         if (list_length(parse->rtable) == 1 &&
1334                 ((RangeTblEntry *) linitial(parse->rtable))->rtekind == RTE_RELATION)
1335         {
1336                 int     i;
1337                 RangeTblEntry  *rte = linitial(parse->rtable);
1338                 
1339                 for (i = 0; i < global->nscan_hints; i++)
1340                 {
1341                         ScanHint           *hint = global->scan_hints[i];
1342
1343                         if (RelnameCmp(&rte->eref->aliasname, &hint->relname) != 0)
1344                                 parse_ereport(hint->opt_str, ("Relation \"%s\" does not exist.", hint->relname));
1345
1346                         set_scan_config_options(hint->enforce_mask, global->context);
1347                 }
1348         }
1349
1350         if (prev_planner_hook)
1351                 result = (*prev_planner_hook) (parse, cursorOptions, boundParams);
1352         else
1353                 result = standard_planner(parse, cursorOptions, boundParams);
1354
1355         /*
1356          * Restore the GUC variables we set above.
1357          */
1358         AtEOXact_GUC(true, save_nestlevel);
1359
1360         if (pg_hint_plan_debug_print)
1361         {
1362                 PlanHintDump(global);
1363 #ifdef NOT_USED
1364                 elog_node_display(INFO, "rtable", parse->rtable, true);
1365 #endif
1366         }
1367
1368         PlanHintDelete(global);
1369         global = NULL;
1370
1371         return result;
1372 }
1373
1374 /*
1375  * aliasnameと一致するSCANヒントを探す
1376  */
1377 static ScanHint *
1378 find_scan_hint(RangeTblEntry *rte)
1379 {
1380         int     i;
1381
1382         for (i = 0; i < global->nscan_hints; i++)
1383         {
1384                 ScanHint   *hint = global->scan_hints[i];
1385
1386                 if (RelnameCmp(&rte->eref->aliasname, &hint->relname) == 0)
1387                         return hint;
1388         }
1389
1390         return NULL;
1391 }
1392
1393 static void
1394 pg_hint_plan_get_relation_info(PlannerInfo *root, Oid relationObjectId,
1395                                                                  bool inhparent, RelOptInfo *rel)
1396 {
1397         ScanHint   *hint;
1398         ListCell   *cell;
1399         ListCell   *prev;
1400         ListCell   *next;
1401
1402         if (prev_get_relation_info)
1403                 (*prev_get_relation_info) (root, relationObjectId, inhparent, rel);
1404
1405         /* 有効なヒントが指定されなかった場合は処理をスキップする。 */
1406         if (!global)
1407                 return;
1408
1409         if (rel->reloptkind != RELOPT_BASEREL)
1410                 return;
1411
1412         if ((hint = find_scan_hint(root->simple_rte_array[rel->relid])) == NULL)
1413                 return;
1414
1415         /* インデックスを全て削除し、スキャンに使えなくする */
1416         if (hint->enforce_mask == ENABLE_SEQSCAN)
1417         {
1418                 list_free_deep(rel->indexlist);
1419                 rel->indexlist = NIL;
1420
1421                 return;
1422         }
1423
1424         /* 後でパスを作り直すため、ここではなにもしない */
1425         if (hint->indexnames == NULL)
1426                 return;
1427
1428         /* 指定されたインデックスのみをのこす */
1429         prev = NULL;
1430         for (cell = list_head(rel->indexlist); cell; cell = next)
1431         {
1432                 IndexOptInfo   *info = (IndexOptInfo *) lfirst(cell);
1433                 char               *indexname = get_rel_name(info->indexoid);
1434                 ListCell           *l;
1435                 bool                    use_index = false;
1436
1437                 next = lnext(cell);
1438
1439                 foreach(l, hint->indexnames)
1440                 {
1441                         if (RelnameCmp(&indexname, &lfirst(l)) == 0)
1442                         {
1443                                 use_index = true;
1444                                 break;
1445                         }
1446                 }
1447
1448                 if (!use_index)
1449                         rel->indexlist = list_delete_cell(rel->indexlist, cell, prev);
1450                 else
1451                         prev = cell;
1452
1453                 pfree(indexname);
1454         }
1455 }
1456
1457 static Index
1458 scan_relid_aliasname(PlannerInfo *root, char *aliasname, bool check_ambiguous, const char *str)
1459 {
1460         /* TODO refnameRangeTblEntry を参考 */
1461         int             i;
1462         Index   find = 0;
1463
1464         for (i = 1; i < root->simple_rel_array_size; i++)
1465         {
1466                 if (root->simple_rel_array[i] == NULL)
1467                         continue;
1468
1469                 Assert(i == root->simple_rel_array[i]->relid);
1470
1471                 if (RelnameCmp(&aliasname, &root->simple_rte_array[i]->eref->aliasname)
1472                                 != 0)
1473                         continue;
1474
1475                 if (!check_ambiguous)
1476                         return i;
1477
1478                 if (find)
1479                         parse_ereport(str, ("relation name \"%s\" is ambiguous", aliasname));
1480
1481                 find = i;
1482         }
1483
1484         return find;
1485 }
1486
1487 /*
1488  * relidビットマスクと一致するヒントを探す
1489  */
1490 static JoinHint *
1491 scan_join_hint(Relids joinrelids)
1492 {
1493         List       *join_hint;
1494         ListCell   *l;
1495
1496         join_hint = global->join_hint_level[bms_num_members(joinrelids)];
1497         foreach(l, join_hint)
1498         {
1499                 JoinHint   *hint = (JoinHint *) lfirst(l);
1500                 if (bms_equal(joinrelids, hint->joinrelids))
1501                         return hint;
1502         }
1503
1504         return NULL;
1505 }
1506
1507 /*
1508  * ヒントを使用しやすい構造に変換する。
1509  */
1510 static void
1511 rebuild_join_hints(PlanHint *plan, PlannerInfo *root, int level, List *initial_rels)
1512 {
1513         int                     i;
1514         ListCell   *l;
1515         Relids          joinrelids;
1516         int                     njoinrels;
1517
1518         plan->nlevel = root->simple_rel_array_size - 1;
1519         plan->join_hint_level = palloc0(sizeof(List *) * (root->simple_rel_array_size));
1520         for (i = 0; i < plan->njoin_hints; i++)
1521         {
1522                 JoinHint   *hint = plan->join_hints[i];
1523                 int                     j;
1524                 Index           relid = 0;
1525
1526                 for (j = 0; j < hint->nrels; j++)
1527                 {
1528                         char   *relname = hint->relnames[j];
1529
1530                         relid = scan_relid_aliasname(root, relname, true, hint->opt_str);
1531                         if (relid == 0)
1532                         {
1533                                 parse_ereport(hint->opt_str, ("Relation \"%s\" does not exist.", relname));
1534                                 break;
1535                         }
1536
1537                         hint->joinrelids = bms_add_member(hint->joinrelids, relid);
1538                 }
1539
1540                 if (relid == 0)
1541                         continue;
1542
1543                 plan->join_hint_level[hint->nrels] =
1544                         lappend(plan->join_hint_level[hint->nrels], hint);
1545         }
1546
1547         /* Leading hint は、全ての join 方式が有効な hint として登録する */
1548         joinrelids = NULL;
1549         njoinrels = 0;
1550         foreach(l, plan->leading)
1551         {
1552                 char       *relname = (char *)lfirst(l);
1553                 JoinHint   *hint;
1554
1555                 i = scan_relid_aliasname(root, relname, true, plan->hint_str);
1556                 if (i == 0)
1557                 {
1558                         parse_ereport(plan->hint_str, ("Relation \"%s\" does not exist.", relname));
1559                         list_free_deep(plan->leading);
1560                         plan->leading = NIL;
1561                         break;
1562                 }
1563
1564                 joinrelids = bms_add_member(joinrelids, i);
1565                 njoinrels++;
1566
1567                 if (njoinrels < 2)
1568                         continue;
1569
1570                 if (njoinrels > plan->nlevel)
1571                 {
1572                         parse_ereport(plan->hint_str, ("In %s hint, specified relation name %d or less.", HINT_LEADING, plan->nlevel));
1573                         break;
1574                 }
1575
1576                 /* Leading で指定した組み合わせ以外の join hint を削除する */
1577                 hint = scan_join_hint(joinrelids);
1578                 list_free(plan->join_hint_level[njoinrels]);
1579                 if (hint)
1580                         plan->join_hint_level[njoinrels] = lappend(NIL, hint);
1581                 else
1582                 {
1583                         /*
1584                          * Here relnames is not set, since Relids bitmap is sufficient to
1585                          * control paths of this query afterwards.
1586                          */
1587                         hint = JoinHintCreate();
1588                         hint->nrels = njoinrels;
1589                         hint->enforce_mask = ENABLE_ALL_JOIN;
1590                         hint->joinrelids = bms_copy(joinrelids);
1591                         plan->join_hint_level[njoinrels] = lappend(NIL, hint);
1592
1593                         if (plan->njoin_hints == 0)
1594                         {
1595                                 plan->max_join_hints = HINT_ARRAY_DEFAULT_INITSIZE;
1596                                 plan->join_hints = palloc(sizeof(JoinHint *) * plan->max_join_hints);
1597                         }
1598                         else if (plan->njoin_hints == plan->max_join_hints)
1599                         {
1600                                 plan->max_join_hints *= 2;
1601                                 plan->join_hints = repalloc(plan->join_hints,
1602                                                                         sizeof(JoinHint *) * plan->max_join_hints);
1603                         }
1604
1605                         plan->join_hints[plan->njoin_hints] = hint;
1606                         plan->njoin_hints++;
1607                 }
1608         }
1609
1610         bms_free(joinrelids);
1611 }
1612
1613 static void
1614 rebuild_scan_path(PlanHint *plan, PlannerInfo *root, int level, List *initial_rels)
1615 {
1616         int     i;
1617         int     save_nestlevel = 0;
1618
1619         for (i = 0; i < plan->nscan_hints; i++)
1620         {
1621                 ScanHint   *hint = plan->scan_hints[i];
1622                 ListCell   *l;
1623
1624                 if (hint->enforce_mask == ENABLE_SEQSCAN)
1625                         continue;
1626
1627                 foreach(l, initial_rels)
1628                 {
1629                         RelOptInfo         *rel = (RelOptInfo *) lfirst(l);
1630                         RangeTblEntry  *rte = root->simple_rte_array[rel->relid];
1631
1632                         if (rel->reloptkind != RELOPT_BASEREL ||
1633                                 RelnameCmp(&hint->relname, &rte->eref->aliasname) != 0)
1634                                 continue;
1635
1636                         if (save_nestlevel != 0)
1637                                 save_nestlevel = NewGUCNestLevel();
1638
1639                         /*
1640                          * TODO ヒントで指定されたScan方式が最安価でない場合のみ、Pathを生成
1641                          * しなおす
1642                          */
1643                         set_scan_config_options(hint->enforce_mask, plan->context);
1644
1645                         rel->pathlist = NIL;    /* TODO 解放 */
1646                         set_plain_rel_pathlist(root, rel, rte);
1647
1648                         break;
1649                 }
1650         }
1651
1652         /*
1653          * Restore the GUC variables we set above.
1654          */
1655         if (save_nestlevel != 0)
1656                 AtEOXact_GUC(true, save_nestlevel);
1657 }
1658
1659 /*
1660  * src/backend/optimizer/path/joinrels.c
1661  * export make_join_rel() をラップする関数
1662  * 
1663  * ヒントにしたがって、enabele_* パラメータを変更した上で、make_join_rel()を
1664  * 呼び出す。
1665  */
1666 static RelOptInfo *
1667 pg_hint_plan_make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
1668 {
1669         Relids                  joinrelids;
1670         JoinHint           *hint;
1671         RelOptInfo         *rel;
1672         int                             save_nestlevel;
1673
1674         joinrelids = bms_union(rel1->relids, rel2->relids);
1675         hint = scan_join_hint(joinrelids);
1676         bms_free(joinrelids);
1677
1678         if (!hint)
1679                 return make_join_rel(root, rel1, rel2);
1680
1681         save_nestlevel = NewGUCNestLevel();
1682
1683         set_join_config_options(hint->enforce_mask, global->context);
1684
1685         rel = make_join_rel(root, rel1, rel2);
1686
1687         /*
1688          * Restore the GUC variables we set above.
1689          */
1690         AtEOXact_GUC(true, save_nestlevel);
1691
1692         return rel;
1693 }
1694
1695 static RelOptInfo *
1696 pg_hint_plan_join_search(PlannerInfo *root, int levels_needed, List *initial_rels)
1697 {
1698         /*
1699          * pg_hint_planが無効、または有効なヒントが1つも指定されなかった場合は、標準
1700          * の処理を行う。
1701          */
1702         if (!global)
1703         {
1704                 if (prev_join_search)
1705                         return (*prev_join_search) (root, levels_needed, initial_rels);
1706                 else if (enable_geqo && levels_needed >= geqo_threshold)
1707                         return geqo(root, levels_needed, initial_rels);
1708                 else
1709                         return standard_join_search(root, levels_needed, initial_rels);
1710         }
1711
1712         rebuild_join_hints(global, root, levels_needed, initial_rels);
1713         rebuild_scan_path(global, root, levels_needed, initial_rels);
1714
1715         /*
1716          * GEQOを使用する条件を満たした場合は、GEQOを用いた結合方式の検索を行う。
1717          * このとき、スキャン方式のヒントとSetヒントのみが有効になり、結合方式や結合
1718          * 順序はヒント句は無効になりGEQOのアルゴリズムで決定される。
1719          */
1720         if (enable_geqo && levels_needed >= geqo_threshold)
1721                 return geqo(root, levels_needed, initial_rels);
1722
1723         return standard_join_search_org(root, levels_needed, initial_rels);
1724 }
1725
1726 #define standard_join_search standard_join_search_org
1727 #define join_search_one_level pg_hint_plan_join_search_one_level
1728 #define make_join_rel pg_hint_plan_make_join_rel
1729 #include "core.c"