OSDN Git Service

Leadingヒント機能を追加
[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 != 90200
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 #define HINT_NESTLOOP           "NestLoop"
43 #define HINT_MERGEJOIN          "MergeJoin"
44 #define HINT_HASHJOIN           "HashJoin"
45 #define HINT_NONESTLOOP         "NoNestLoop"
46 #define HINT_NOMERGEJOIN        "NoMergeJoin"
47 #define HINT_NOHASHJOIN         "NoHashJoin"
48 #define HINT_LEADING            "Leading"
49 #define HINT_SET                        "Set"
50
51 #if PG_VERSION_NUM >= 90200
52 #define HINT_INDEXONLYSCAN              "IndexonlyScan"
53 #define HINT_NOINDEXONLYSCAN    "NoIndexonlyScan"
54 #endif
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 typedef struct PlanHint
109 {
110         char       *hint_str;
111
112         int                     nscan_hints;
113         int                     max_scan_hints;
114         ScanHint  **scan_hints;
115
116         int                     njoin_hints;
117         int                     max_join_hints;
118         JoinHint  **join_hints;
119
120         int                     nlevel;
121         List      **join_hint_level;
122
123         List       *leading;
124
125         GucContext      context;
126         List       *set_hints;
127 } PlanHint;
128
129 typedef const char *(*HintParserFunction) (PlanHint *plan, Query *parse, char *keyword, const char *str);
130
131 typedef struct HintParser
132 {
133         char   *keyword;
134         bool    have_paren;
135         HintParserFunction      hint_parser;
136 } HintParser;
137
138 /* Module callbacks */
139 void            _PG_init(void);
140 void            _PG_fini(void);
141
142 static PlannedStmt *pg_hint_plan_planner(Query *parse, int cursorOptions,
143                                                            ParamListInfo boundParams);
144 static void pg_hint_plan_get_relation_info(PlannerInfo *root, Oid relationObjectId,
145                                                                  bool inhparent, RelOptInfo *rel);
146 static RelOptInfo *pg_hint_plan_join_search(PlannerInfo *root, int levels_needed,
147                                                                   List *initial_rels);
148
149 static const char *ParseScanMethod(PlanHint *plan, Query *parse, char *keyword, const char *str);
150 static const char *ParseIndexScanMethod(PlanHint *plan, Query *parse, char *keyword, const char *str);
151 static const char *ParseJoinMethod(PlanHint *plan, Query *parse, char *keyword, const char *str);
152 static const char *ParseLeading(PlanHint *plan, Query *parse, char *keyword, const char *str);
153 static const char *ParseSet(PlanHint *plan, Query *parse, char *keyword, const char *str);
154 #ifdef NOT_USED
155 static const char *Ordered(PlanHint *plan, Query *parse, char *keyword, const char *str);
156 #endif
157
158 static void my_join_search_one_level(PlannerInfo *root, int level);
159 static void make_rels_by_clause_joins(PlannerInfo *root, RelOptInfo *old_rel, ListCell *other_rels);
160 static void make_rels_by_clauseless_joins(PlannerInfo *root, RelOptInfo *old_rel, ListCell *other_rels);
161 static bool has_join_restriction(PlannerInfo *root, RelOptInfo *rel);
162 static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte);
163
164
165 /* GUC variables */
166 static bool pg_hint_plan_enable = true;
167 static bool pg_hint_plan_debug_print = false;
168 static int pg_hint_plan_parse_message = INFO;
169
170 static const struct config_enum_entry parse_message_level_options[] = {
171         {"debug", DEBUG2, true},
172         {"debug5", DEBUG5, false},
173         {"debug4", DEBUG4, false},
174         {"debug3", DEBUG3, false},
175         {"debug2", DEBUG2, false},
176         {"debug1", DEBUG1, false},
177         {"log", LOG, false},
178         {"info", INFO, false},
179         {"notice", NOTICE, false},
180         {"warning", WARNING, false},
181         {"error", ERROR, false},
182         /*
183         {"fatal", FATAL, true},
184         {"panic", PANIC, true},
185          */
186         {NULL, 0, false}
187 };
188
189 /* Saved hook values in case of unload */
190 static planner_hook_type prev_planner_hook = NULL;
191 static get_relation_info_hook_type prev_get_relation_info = NULL;
192 static join_search_hook_type prev_join_search = NULL;
193
194 /* フック関数をまたがって使用する情報を管理する */
195 static PlanHint *global = NULL;
196
197 static const HintParser parsers[] = {
198         {HINT_SEQSCAN, true, ParseScanMethod},
199         {HINT_INDEXSCAN, true, ParseIndexScanMethod},
200         {HINT_BITMAPSCAN, true, ParseIndexScanMethod},
201         {HINT_TIDSCAN, true, ParseScanMethod},
202         {HINT_NOSEQSCAN, true, ParseScanMethod},
203         {HINT_NOINDEXSCAN, true, ParseScanMethod},
204         {HINT_NOBITMAPSCAN, true, ParseScanMethod},
205         {HINT_NOTIDSCAN, true, ParseScanMethod},
206 #if PG_VERSION_NUM >= 90200
207         {HINT_INDEXONLYSCAN, true, ParseIndexScanMethod},
208         {HINT_NOINDEXONLYSCAN, true, ParseIndexScanMethod},
209 #endif
210         {HINT_NESTLOOP, true, ParseJoinMethod},
211         {HINT_MERGEJOIN, true, ParseJoinMethod},
212         {HINT_HASHJOIN, true, ParseJoinMethod},
213         {HINT_NONESTLOOP, true, ParseJoinMethod},
214         {HINT_NOMERGEJOIN, true, ParseJoinMethod},
215         {HINT_NOHASHJOIN, true, ParseJoinMethod},
216         {HINT_LEADING, true, ParseLeading},
217         {HINT_SET, true, ParseSet},
218         {NULL, false, NULL},
219 };
220
221 /*
222  * Module load callbacks
223  */
224 void
225 _PG_init(void)
226 {
227         /* Define custom GUC variables. */
228         DefineCustomBoolVariable("pg_hint_plan.enable",
229                          "Instructions or hints to the planner using block comments.",
230                                                          NULL,
231                                                          &pg_hint_plan_enable,
232                                                          true,
233                                                          PGC_USERSET,
234                                                          0,
235                                                          NULL,
236                                                          NULL,
237                                                          NULL);
238
239         DefineCustomBoolVariable("pg_hint_plan.debug_print",
240                                                          "Logs each query's parse results of the hint.",
241                                                          NULL,
242                                                          &pg_hint_plan_debug_print,
243                                                          false,
244                                                          PGC_USERSET,
245                                                          0,
246                                                          NULL,
247                                                          NULL,
248                                                          NULL);
249
250         DefineCustomEnumVariable("pg_hint_plan.parse_message",
251                                                          "Messege level of the parse error.",
252                                                          NULL,
253                                                          &pg_hint_plan_parse_message,
254                                                          INFO,
255                                                          parse_message_level_options,
256                                                          PGC_USERSET,
257                                                          0,
258                                                          NULL,
259                                                          NULL,
260                                                          NULL);
261
262         /* Install hooks. */
263         prev_planner_hook = planner_hook;
264         planner_hook = pg_hint_plan_planner;
265         prev_get_relation_info = get_relation_info_hook;
266         get_relation_info_hook = pg_hint_plan_get_relation_info;
267         prev_join_search = join_search_hook;
268         join_search_hook = pg_hint_plan_join_search;
269 }
270
271 /*
272  * Module unload callback
273  */
274 void
275 _PG_fini(void)
276 {
277         /* Uninstall hooks. */
278         planner_hook = prev_planner_hook;
279         get_relation_info_hook = prev_get_relation_info;
280         join_search_hook = prev_join_search;
281 }
282
283 static ScanHint *
284 ScanHintCreate(void)
285 {
286         ScanHint           *hint;
287
288         hint = palloc(sizeof(ScanHint));
289         hint->opt_str = NULL;
290         hint->relname = NULL;
291         hint->indexnames = NIL;
292         hint->enforce_mask = 0;
293
294         return hint;
295 }
296
297 static void
298 ScanHintDelete(ScanHint *hint)
299 {
300         if (!hint)
301                 return;
302
303         if (hint->relname)
304                 pfree(hint->relname);
305         list_free_deep(hint->indexnames);
306         pfree(hint);
307 }
308
309 static JoinHint *
310 JoinHintCreate(void)
311 {
312         JoinHint   *hint;
313
314         hint = palloc(sizeof(JoinHint));
315         hint->opt_str = NULL;
316         hint->nrels = 0;
317         hint->relnames = NULL;
318         hint->enforce_mask = 0;
319         hint->joinrelids = NULL;
320
321         return hint;
322 }
323
324 static void
325 JoinHintDelete(JoinHint *hint)
326 {
327         if (!hint)
328                 return;
329
330         if (hint->relnames)
331         {
332                 int     i;
333
334                 for (i = 0; i < hint->nrels; i++)
335                         pfree(hint->relnames[i]);
336                 pfree(hint->relnames);
337         }
338         bms_free(hint->joinrelids);
339         pfree(hint);
340 }
341
342 static SetHint *
343 SetHintCreate(void)
344 {
345         SetHint    *hint;
346
347         hint = palloc(sizeof(SetHint));
348         hint->name = NULL;
349         hint->value = NULL;
350
351         return hint;
352 }
353
354 static void
355 SetHintDelete(SetHint *hint)
356 {
357         if (!hint)
358                 return;
359
360         if (hint->name)
361                 pfree(hint->name);
362         if (hint->value)
363                 pfree(hint->value);
364         pfree(hint);
365 }
366
367 static PlanHint *
368 PlanHintCreate(void)
369 {
370         PlanHint   *hint;
371
372         hint = palloc(sizeof(PlanHint));
373         hint->hint_str = NULL;
374         hint->nscan_hints = 0;
375         hint->max_scan_hints = 0;
376         hint->scan_hints = NULL;
377         hint->njoin_hints = 0;
378         hint->max_join_hints = 0;
379         hint->join_hints = NULL;
380         hint->nlevel = 0;
381         hint->join_hint_level = NULL;
382         hint->leading = NIL;
383         hint->context = superuser() ? PGC_SUSET : PGC_USERSET;
384         hint->set_hints = NIL;
385
386         return hint;
387 }
388
389 static void
390 PlanHintDelete(PlanHint *hint)
391 {
392         ListCell   *l;
393         int                     i;
394
395         if (!hint)
396                 return;
397
398         if (hint->hint_str)
399                 pfree(hint->hint_str);
400
401         for (i = 0; i < hint->nscan_hints; i++)
402                 ScanHintDelete(hint->scan_hints[i]);
403         if (hint->scan_hints)
404                 pfree(hint->scan_hints);
405
406         for (i = 0; i < hint->njoin_hints; i++)
407                 JoinHintDelete(hint->join_hints[i]);
408         if (hint->join_hints)
409                 pfree(hint->join_hints);
410
411         for (i = 2; i <= hint->nlevel; i++)
412                 list_free(hint->join_hint_level[i]);
413         if (hint->join_hint_level)
414                 pfree(hint->join_hint_level);
415
416         list_free_deep(hint->leading);
417
418         foreach(l, hint->set_hints)
419                 SetHintDelete((SetHint *) lfirst(l));
420         list_free(hint->set_hints);
421
422         pfree(hint);
423 }
424
425 static bool
426 PlanHintIsempty(PlanHint *hint)
427 {
428         if (hint->nscan_hints == 0 &&
429                 hint->njoin_hints == 0 &&
430                 hint->leading == NIL &&
431                 hint->set_hints == NIL)
432                 return true;
433
434         return false;
435 }
436
437 // TODO オブジェクト名のクォート処理を追加
438 static void
439 PlanHintDump(PlanHint *hint)
440 {
441         StringInfoData  buf;
442         ListCell           *l;
443         int                             i;
444         bool                    is_first = true;
445
446         if (!hint)
447         {
448                 elog(INFO, "no hint");
449                 return;
450         }
451
452         initStringInfo(&buf);
453         appendStringInfo(&buf, "/*\n");
454         for (i = 0; i < hint->nscan_hints; i++)
455         {
456                 ScanHint   *h = hint->scan_hints[i];
457                 ListCell   *n;
458                 switch(h->enforce_mask)
459                 {
460                         case(ENABLE_SEQSCAN):
461                                 appendStringInfo(&buf, "%s(", HINT_SEQSCAN);
462                                 break;
463                         case(ENABLE_INDEXSCAN):
464                                 appendStringInfo(&buf, "%s(", HINT_INDEXSCAN);
465                                 break;
466                         case(ENABLE_BITMAPSCAN):
467                                 appendStringInfo(&buf, "%s(", HINT_BITMAPSCAN);
468                                 break;
469                         case(ENABLE_TIDSCAN):
470                                 appendStringInfo(&buf, "%s(", HINT_TIDSCAN);
471                                 break;
472                         case(ENABLE_INDEXSCAN | ENABLE_BITMAPSCAN | ENABLE_TIDSCAN):
473                                 appendStringInfo(&buf, "%s(", HINT_NOSEQSCAN);
474                                 break;
475                         case(ENABLE_SEQSCAN | ENABLE_BITMAPSCAN | ENABLE_TIDSCAN):
476                                 appendStringInfo(&buf, "%s(", HINT_NOINDEXSCAN);
477                                 break;
478                         case(ENABLE_SEQSCAN | ENABLE_INDEXSCAN | ENABLE_TIDSCAN):
479                                 appendStringInfo(&buf, "%s(", HINT_NOBITMAPSCAN);
480                                 break;
481                         case(ENABLE_SEQSCAN | ENABLE_INDEXSCAN | ENABLE_BITMAPSCAN):
482                                 appendStringInfo(&buf, "%s(", HINT_NOTIDSCAN);
483                                 break;
484                         default:
485                                 appendStringInfoString(&buf, "\?\?\?(");
486                                 break;
487                 }
488                 appendStringInfo(&buf, "%s", h->relname);
489                 foreach(n, h->indexnames)
490                         appendStringInfo(&buf, " %s", (char *) lfirst(n));
491                 appendStringInfoString(&buf, ")\n");
492         }
493
494         for (i = 0; i < hint->njoin_hints; i++)
495         {
496                 JoinHint   *h = hint->join_hints[i];
497                 int                     i;
498                 switch(h->enforce_mask)
499                 {
500                         case(ENABLE_NESTLOOP):
501                                 appendStringInfo(&buf, "%s(", HINT_NESTLOOP);
502                                 break;
503                         case(ENABLE_MERGEJOIN):
504                                 appendStringInfo(&buf, "%s(", HINT_MERGEJOIN);
505                                 break;
506                         case(ENABLE_HASHJOIN):
507                                 appendStringInfo(&buf, "%s(", HINT_HASHJOIN);
508                                 break;
509                         case(ENABLE_ALL_JOIN ^ ENABLE_NESTLOOP):
510                                 appendStringInfo(&buf, "%s(", HINT_NONESTLOOP);
511                                 break;
512                         case(ENABLE_ALL_JOIN ^ ENABLE_MERGEJOIN):
513                                 appendStringInfo(&buf, "%s(", HINT_NOMERGEJOIN);
514                                 break;
515                         case(ENABLE_ALL_JOIN ^ ENABLE_HASHJOIN):
516                                 appendStringInfo(&buf, "%s(", HINT_NOHASHJOIN);
517                                 break;
518                         case(ENABLE_ALL_JOIN):
519                                 continue;
520                         default:
521                                 appendStringInfoString(&buf, "\?\?\?(");
522                                 break;
523                 }
524                 appendStringInfo(&buf, "%s", h->relnames[0]);
525                 for (i = 1; i < h->nrels; i++)
526                         appendStringInfo(&buf, " %s", h->relnames[i]);
527                 appendStringInfoString(&buf, ")\n");
528         }
529
530         foreach(l, hint->set_hints)
531         {
532                 SetHint    *h = (SetHint *) lfirst(l);
533                 appendStringInfo(&buf, "%s(%s %s)\n", HINT_SET, h->name, h->value);
534         }
535
536         foreach(l, hint->leading)
537         {
538                 if (is_first)
539                 {
540                         appendStringInfo(&buf, "%s(%s", HINT_LEADING, (char *)lfirst(l));
541                         is_first = false;
542                 }
543                 else
544                         appendStringInfo(&buf, " %s", (char *)lfirst(l));
545         }
546         if (!is_first)
547                 appendStringInfoString(&buf, ")\n");
548
549         appendStringInfoString(&buf, "*/");
550
551         elog(INFO, "%s", buf.data);
552
553         pfree(buf.data);
554 }
555
556 static int
557 RelnameCmp(const void *a, const void *b)
558 {
559         const char *relnamea = *((const char **) a);
560         const char *relnameb = *((const char **) b);
561
562         return strcmp(relnamea, relnameb);
563 }
564
565 static int
566 ScanHintCmp(const void *a, const void *b, bool order)
567 {
568         const ScanHint     *hinta = *((const ScanHint **) a);
569         const ScanHint     *hintb = *((const ScanHint **) b);
570         int                                     result;
571
572         if ((result = strcmp(hinta->relname, hintb->relname)) != 0)
573                 return result;
574
575         /* ヒント句で指定した順を返す */
576         if (order)
577                 return hinta->opt_str - hintb->opt_str;
578         else
579                 return 0;
580 }
581
582 static int
583 ScanHintCmpIsOrder(const void *a, const void *b)
584 {
585         return ScanHintCmp(a, b, true);
586 }
587
588 static int
589 JoinHintCmp(const void *a, const void *b, bool order)
590 {
591         const JoinHint     *hinta = *((const JoinHint **) a);
592         const JoinHint     *hintb = *((const JoinHint **) b);
593
594         if (hinta->nrels == hintb->nrels)
595         {
596                 int     i;
597                 for (i = 0; i < hinta->nrels; i++)
598                 {
599                         int     result;
600                         if ((result = strcmp(hinta->relnames[i], hintb->relnames[i])) != 0)
601                                 return result;
602                 }
603
604                 /* ヒント句で指定した順を返す */
605                 if (order)
606                         return hinta->opt_str - hintb->opt_str;
607                 else
608                         return 0;
609         }
610
611         return hinta->nrels - hintb->nrels;
612 }
613
614 static int
615 JoinHintCmpIsOrder(const void *a, const void *b)
616 {
617         return JoinHintCmp(a, b, true);
618 }
619
620 static int
621 set_config_options(List *options, GucContext context)
622 {
623         ListCell   *l;
624         int                     save_nestlevel;
625         int                     result = 1;
626
627         save_nestlevel = NewGUCNestLevel();
628
629         foreach(l, options)
630         {
631                 SetHint    *hint = (SetHint *) lfirst(l);
632
633                 if (result > 0)
634                         result = set_config_option(hint->name, hint->value, context,
635                                                 PGC_S_SESSION, GUC_ACTION_SAVE, true,
636                                                 pg_hint_plan_parse_message);
637         }
638
639         return save_nestlevel;
640 }
641
642 #define SET_CONFIG_OPTION(name, enforce_mask, type_bits) \
643         set_config_option((name), \
644                 ((enforce_mask) & (type_bits)) ? "true" : "false", \
645                 context, PGC_S_SESSION, GUC_ACTION_SAVE, true, ERROR)
646
647 static void
648 set_join_config_options(unsigned char enforce_mask, GucContext context)
649 {
650         SET_CONFIG_OPTION("enable_nestloop", enforce_mask, ENABLE_NESTLOOP);
651         SET_CONFIG_OPTION("enable_mergejoin", enforce_mask, ENABLE_MERGEJOIN);
652         SET_CONFIG_OPTION("enable_hashjoin", enforce_mask, ENABLE_HASHJOIN);
653 }
654
655 static void
656 set_scan_config_options(unsigned char enforce_mask, GucContext context)
657 {
658         SET_CONFIG_OPTION("enable_seqscan", enforce_mask, ENABLE_SEQSCAN);
659         SET_CONFIG_OPTION("enable_indexscan", enforce_mask, ENABLE_INDEXSCAN);
660         SET_CONFIG_OPTION("enable_bitmapscan", enforce_mask, ENABLE_BITMAPSCAN);
661         SET_CONFIG_OPTION("enable_tidscan", enforce_mask, ENABLE_TIDSCAN);
662 #if PG_VERSION_NUM >= 90200
663         SET_CONFIG_OPTION("enable_indexonlyscan", enforce_mask, ENABLE_INDEXSCAN);
664 #endif
665 }
666
667 /*
668  * parse functions
669  */
670
671 static const char *
672 parse_keyword(const char *str, StringInfo buf)
673 {
674         skip_space(str);
675
676         while (!isspace(*str) && *str != '(' && *str != '\0')
677                 appendStringInfoChar(buf, *str++);
678
679         return str;
680 }
681
682 static const char *
683 skip_opened_parenthesis(const char *str)
684 {
685         skip_space(str);
686
687         if (*str != '(')
688         {
689                 parse_ereport(str, ("Opened parenthesis is necessary."));
690                 return NULL;
691         }
692
693         str++;
694
695         return str;
696 }
697
698 static const char *
699 skip_closed_parenthesis(const char *str)
700 {
701         skip_space(str);
702
703         if (*str != ')')
704         {
705                 parse_ereport(str, ("Closed parenthesis is necessary."));
706                 return NULL;
707         }
708
709         str++;
710
711         return str;
712 }
713
714 static const char *
715 parse_quote_value(const char *str, char **word, char *value_type)
716 {
717         StringInfoData  buf;
718         bool                    in_quote;
719
720         skip_space(str);
721
722         initStringInfo(&buf);
723         if (*str == '"')
724         {
725                 str++;
726                 in_quote = true;
727         }
728         else
729         {
730                 /*
731                  * 1文字目以降の制限の適用
732                  */
733                 if (!isalpha(*str) && *str != '_')
734                 {
735                         pfree(buf.data);
736                         parse_ereport(str, ("Need for %s to be quoted.", value_type));
737                         return NULL;
738                 }
739
740                 in_quote = false;
741                 appendStringInfoCharMacro(&buf, *str++);
742         }
743
744         while (true)
745         {
746                 if (in_quote)
747                 {
748                         if (*str == '\0')
749                         {
750                                 pfree(buf.data);
751                                 parse_ereport(str, ("Unterminated quoted %s.", value_type));
752                                 return NULL;
753                         }
754
755                         /*
756                          * エスケープ対象をスキップする。
757                          * TODO エスケープ対象の仕様にあわせた処理を行う。
758                          */
759                         if(*str == '"')
760                         {
761                                 str++;
762                                 if (*str != '"')
763                                         break;
764                         }
765                 }
766                 else
767                 {
768                         /*
769                          * 2文字目以降の制限の適用
770                          */
771                         if (!isalnum(*str) && *str != '_' && *str != '$')
772                                 break;
773                 }
774
775                 appendStringInfoCharMacro(&buf, *str++);
776         }
777
778         if (buf.len == 0)
779         {
780                 pfree(buf.data);
781                 parse_ereport(str, ("%s is necessary.", value_type));
782                 return NULL;
783         }
784
785         *word = buf.data;
786
787         return str;
788 }
789
790 static const char *
791 skip_option_delimiter(const char *str)
792 {
793         const char *p = str;
794
795         skip_space(str);
796
797         if (str == p)
798         {
799                 parse_ereport(str, ("Must be specified space."));
800                 return NULL;
801         }
802
803         return str;
804 }
805
806 static bool
807 parse_hints(PlanHint *plan, Query *parse, const char *str)
808 {
809         StringInfoData  buf;
810         char               *head;
811
812         initStringInfo(&buf);
813         while (*str != '\0')
814         {
815                 const HintParser *parser;
816
817                 /* in error message, we output the comment including the keyword. */
818                 head = (char *) str;
819
820                 /* parse only the keyword of the hint. */
821                 resetStringInfo(&buf);
822                 str = parse_keyword(str, &buf);
823
824                 for (parser = parsers; parser->keyword != NULL; parser++)
825                 {
826                         char   *keyword = parser->keyword;
827
828                         if (strcasecmp(buf.data, keyword) != 0)
829                                 continue;
830
831                         if (parser->have_paren)
832                         {
833                                 /* parser of each hint does parse in a parenthesis. */
834                                 if ((str = skip_opened_parenthesis(str)) == NULL ||
835                                         (str = parser->hint_parser(plan, parse, keyword, str)) == NULL ||
836                                         (str = skip_closed_parenthesis(str)) == NULL)
837                                 {
838                                         pfree(buf.data);
839                                         return false;
840                                 }
841                         }
842                         else
843                         {
844                                 if ((str = parser->hint_parser(plan, parse, keyword, str)) == NULL)
845                                 {
846                                         pfree(buf.data);
847                                         return false;
848                                 }
849
850                                 /*
851                                  * 直前のヒントに括弧の指定がなければ次のヒントの間に空白が必要
852                                  */
853                                 if (!isspace(*str) && *str == '\0')
854                                         parse_ereport(str, ("Delimiter of the hint is necessary."));
855                         }
856
857                         skip_space(str);
858
859                         break;
860                 }
861
862                 if (parser->keyword == NULL)
863                 {
864                         parse_ereport(head, ("Keyword \"%s\" does not exist.", buf.data));
865                         pfree(buf.data);
866                         return false;
867                 }
868         }
869
870         pfree(buf.data);
871
872         return true;
873 }
874
875 /*
876  * Do basic parsing of the query head comment.
877  */
878 static PlanHint *
879 parse_head_comment(Query *parse)
880 {
881         const char         *p;
882         char               *head;
883         char               *tail;
884         int                             len;
885         int                             i;
886         PlanHint           *plan;
887
888         /* get client-supplied query string. */
889         p = debug_query_string;
890         if (p == NULL)
891                 return NULL;
892
893
894         /* extract query head comment. */
895         len = strlen(HINT_START);
896         skip_space(p);
897         if (strncmp(p, HINT_START, len))
898                 return NULL;
899
900         p += len;
901         skip_space(p);
902
903         if ((tail = strstr(p, HINT_END)) == NULL)
904                 elog(ERROR, "unterminated /* comment at or near \"%s\"",
905                          debug_query_string);
906
907         /* 入れ子にしたブロックコメントはサポートしない */
908         if ((head = strstr(p, HINT_START)) != NULL && head < tail)
909                 parse_ereport(head, ("block comments nest doesn't supported"));
910
911         /* ヒント句部分を切り出す */
912         len = tail - p;
913         head = palloc(len + 1);
914         memcpy(head, p, len);
915         head[len] = '\0';
916         p = head;
917
918         plan = PlanHintCreate();
919         plan->hint_str = head;
920
921         /* parse each hint. */
922         if (!parse_hints(plan, parse, p))
923                 return plan;
924
925         /* 重複したScan条件をを除外する */
926         qsort(plan->scan_hints, plan->nscan_hints, sizeof(ScanHint *), ScanHintCmpIsOrder);
927         for (i = 0; i < plan->nscan_hints - 1;)
928         {
929                 int     result = ScanHintCmp(plan->scan_hints + i,
930                                                 plan->scan_hints + i + 1, false);
931                 if (result != 0)
932                         i++;
933                 else
934                 {
935                         /* 後で指定したヒントを有効にする */
936                         plan->nscan_hints--;
937                         memmove(plan->scan_hints + i, plan->scan_hints + i + 1,
938                                         sizeof(ScanHint *) * (plan->nscan_hints - i));
939                 }
940         }
941
942         /* 重複したJoin条件をを除外する */
943         qsort(plan->join_hints, plan->njoin_hints, sizeof(JoinHint *), JoinHintCmpIsOrder);
944         for (i = 0; i < plan->njoin_hints - 1;)
945         {
946                 int     result = JoinHintCmp(plan->join_hints + i,
947                                                 plan->join_hints + i + 1, false);
948                 if (result != 0)
949                         i++;
950                 else
951                 {
952                         /* 後で指定したヒントを有効にする */
953                         plan->njoin_hints--;
954                         memmove(plan->join_hints + i, plan->join_hints + i + 1,
955                                         sizeof(JoinHint *) * (plan->njoin_hints - i));
956                 }
957         }
958
959         return plan;
960 }
961
962 static const char *
963 parse_scan_method(PlanHint *plan, Query *parse, char *keyword, const char *str)
964 {
965         ScanHint   *hint;
966
967         hint = ScanHintCreate();
968         hint->opt_str = str;
969
970         if ((str = parse_quote_value(str, &hint->relname, "ralation name")) == NULL)
971         {
972                 ScanHintDelete(hint);
973                 return NULL;
974         }
975
976         if (strcasecmp(keyword, HINT_SEQSCAN) == 0)
977                 hint->enforce_mask = ENABLE_SEQSCAN;
978         else if (strcasecmp(keyword, HINT_INDEXSCAN) == 0)
979                 hint->enforce_mask = ENABLE_INDEXSCAN;
980         else if (strcasecmp(keyword, HINT_BITMAPSCAN) == 0)
981                 hint->enforce_mask = ENABLE_BITMAPSCAN;
982         else if (strcasecmp(keyword, HINT_TIDSCAN) == 0)
983                 hint->enforce_mask = ENABLE_TIDSCAN;
984         else if (strcasecmp(keyword, HINT_NOSEQSCAN) == 0)
985                 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_SEQSCAN;
986         else if (strcasecmp(keyword, HINT_NOINDEXSCAN) == 0)
987                 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_INDEXSCAN;
988         else if (strcasecmp(keyword, HINT_NOBITMAPSCAN) == 0)
989                 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_BITMAPSCAN;
990         else if (strcasecmp(keyword, HINT_NOTIDSCAN) == 0)
991                 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_TIDSCAN;
992         else
993         {
994                 elog(ERROR, "unrecognized hint keyword \"%s\"", keyword);
995                 return NULL;
996         }
997
998         if (plan->nscan_hints == 0)
999         {
1000                 plan->max_scan_hints = HINT_ARRAY_DEFAULT_INITSIZE;
1001                 plan->scan_hints = palloc(sizeof(JoinHint *) * plan->max_scan_hints);
1002         }
1003         else if (plan->nscan_hints == plan->max_scan_hints)
1004         {
1005                 plan->max_scan_hints *= 2;
1006                 plan->scan_hints = repalloc(plan->scan_hints,
1007                                                                 sizeof(JoinHint *) * plan->max_scan_hints);
1008         }
1009
1010         plan->scan_hints[plan->nscan_hints] = hint;
1011         plan->nscan_hints++;
1012
1013         return str;
1014 }
1015
1016 static const char *
1017 ParseScanMethod(PlanHint *plan, Query *parse, char *keyword, const char *str)
1018 {
1019         ScanHint   *hint;
1020
1021         if ((str = parse_scan_method(plan, parse, keyword, str)) == NULL)
1022                 return NULL;
1023
1024         hint = plan->scan_hints[plan->nscan_hints - 1];
1025
1026         skip_space(str);
1027         if (*str != ')')
1028         {
1029                 parse_ereport(str, ("Closed parenthesis is necessary."));
1030                 plan->nscan_hints--;
1031                 ScanHintDelete(hint);
1032                 return NULL;
1033         }
1034
1035         return str;
1036 }
1037
1038 static const char *
1039 ParseIndexScanMethod(PlanHint *plan, Query *parse, char *keyword, const char *str)
1040 {
1041         char       *indexname;
1042         ScanHint   *hint;
1043
1044         if ((str = parse_scan_method(plan, parse, keyword, str)) == NULL)
1045                 return NULL;
1046
1047         hint = plan->scan_hints[plan->nscan_hints - 1];
1048
1049         /* インデックス参照をパースする。 */
1050         while (true)
1051         {
1052                 // TODO 直前のオブジェクト名がクウォート処理されていた場合の処理を実装
1053                 skip_space(str);
1054                 if (*str == ')')
1055                         break;
1056
1057                 if ((str = parse_quote_value(str, &indexname, "index name")) == NULL)
1058                 {
1059                         plan->nscan_hints--;
1060                         ScanHintDelete(hint);
1061                         return NULL;
1062                 }
1063
1064                 hint->indexnames = lappend(hint->indexnames, indexname);
1065         }
1066
1067         return str;
1068 }
1069
1070 static const char *
1071 ParseJoinMethod(PlanHint *plan, Query *parse, char *keyword, const char *str)
1072 {
1073         char       *relname;
1074         JoinHint   *hint;
1075
1076         skip_space(str);
1077
1078         hint = JoinHintCreate();
1079         hint->opt_str = str;
1080         hint->relnames = palloc(sizeof(char *));
1081
1082         while ((str = parse_quote_value(str, &relname, "table name")) != NULL)
1083         {
1084                 hint->nrels++;
1085                 hint->relnames = repalloc(hint->relnames, sizeof(char *) * hint->nrels);
1086                 hint->relnames[hint->nrels - 1] = relname;
1087
1088                 skip_space(str);
1089                 if (*str == ')')
1090                         break;
1091         }
1092
1093         if (str == NULL)
1094         {
1095                 JoinHintDelete(hint);
1096                 return NULL;
1097         }
1098
1099         /* Join 対象のテーブルは最低でも2つ指定する必要がある */
1100         if (hint->nrels < 2)
1101         {
1102                 JoinHintDelete(hint);
1103                 parse_ereport(str, ("Specified relation more than two."));
1104                 return NULL;
1105         }
1106
1107         /* テーブル名順にソートする */
1108         qsort(hint->relnames, hint->nrels, sizeof(char *), RelnameCmp);
1109
1110         if (strcasecmp(keyword, HINT_NESTLOOP) == 0)
1111                 hint->enforce_mask = ENABLE_NESTLOOP;
1112         else if (strcasecmp(keyword, HINT_MERGEJOIN) == 0)
1113                 hint->enforce_mask = ENABLE_MERGEJOIN;
1114         else if (strcasecmp(keyword, HINT_HASHJOIN) == 0)
1115                 hint->enforce_mask = ENABLE_HASHJOIN;
1116         else if (strcasecmp(keyword, HINT_NONESTLOOP) == 0)
1117                 hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_NESTLOOP;
1118         else if (strcasecmp(keyword, HINT_NOMERGEJOIN) == 0)
1119                 hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_MERGEJOIN;
1120         else if (strcasecmp(keyword, HINT_NOHASHJOIN) == 0)
1121                 hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_HASHJOIN;
1122         else
1123                 elog(ERROR, "unrecognized hint keyword \"%s\"", keyword);
1124
1125         if (plan->njoin_hints == 0)
1126         {
1127                 plan->max_join_hints = HINT_ARRAY_DEFAULT_INITSIZE;
1128                 plan->join_hints = palloc(sizeof(JoinHint *) * plan->max_join_hints);
1129         }
1130         else if (plan->njoin_hints == plan->max_join_hints)
1131         {
1132                 plan->max_join_hints *= 2;
1133                 plan->join_hints = repalloc(plan->join_hints,
1134                                                                 sizeof(JoinHint *) * plan->max_join_hints);
1135         }
1136
1137         plan->join_hints[plan->njoin_hints] = hint;
1138         plan->njoin_hints++;
1139
1140         return str;
1141 }
1142
1143 static const char *
1144 ParseLeading(PlanHint *plan, Query *parse, char *keyword, const char *str)
1145 {
1146         char   *relname;
1147
1148         /*
1149          * すでに指定済みの場合は、後で指定したヒントが有効にするため、登録済みの
1150          * 情報を削除する
1151          */
1152         list_free_deep(plan->leading);
1153         plan->leading = NIL;
1154
1155         while ((str = parse_quote_value(str, &relname, "relation name")) != NULL)
1156         {
1157                 const char *p;
1158
1159                 plan->leading = lappend(plan->leading, relname);
1160
1161                 p = str;
1162                 skip_space(str);
1163                 if (*str == ')')
1164                         break;
1165
1166                 if (p == str)
1167                 {
1168                         parse_ereport(str, ("Must be specified space."));
1169                         break;
1170                 }
1171         }
1172
1173         /* テーブル指定が1つのみの場合は、ヒントを無効にし、パースを続ける */
1174         if (list_length(plan->leading) == 1)
1175         {
1176                 parse_ereport(str, ("In %s hint, specified relation name 2 or more.", HINT_LEADING));
1177                 list_free_deep(plan->leading);
1178                 plan->leading = NIL;
1179         }
1180
1181         return str;
1182 }
1183
1184 static const char *
1185 ParseSet(PlanHint *plan, Query *parse, char *keyword, const char *str)
1186 {
1187         SetHint    *hint;
1188
1189         hint = SetHintCreate();
1190
1191         if ((str = parse_quote_value(str, &hint->name, "parameter name")) == NULL ||
1192                 (str = skip_option_delimiter(str)) == NULL ||
1193                 (str = parse_quote_value(str, &hint->value, "parameter value")) == NULL)
1194         {
1195                 SetHintDelete(hint);
1196                 return NULL;
1197         }
1198
1199         skip_space(str);
1200         if (*str != ')')
1201         {
1202                 parse_ereport(str, ("Closed parenthesis is necessary."));
1203                 SetHintDelete(hint);
1204                 return NULL;
1205         }
1206         plan->set_hints = lappend(plan->set_hints, hint);
1207
1208         return str;
1209 }
1210
1211 #ifdef NOT_USED
1212 /*
1213  * Oracle の ORDERD ヒントの実装
1214  */
1215 static const char *
1216 Ordered(PlanHint *plan, Query *parse, char *keyword, const char *str)
1217 {
1218         SetHint    *hint;
1219
1220         hint = SetHintCreate();
1221         hint->name = pstrdup("join_collapse_limit");
1222         hint->value = pstrdup("1");
1223         plan->set_hints = lappend(plan->set_hints, hint);
1224
1225         return str;
1226 }
1227 #endif
1228
1229 static PlannedStmt *
1230 pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
1231 {
1232         int                             save_nestlevel;
1233         PlannedStmt        *result;
1234
1235         /*
1236          * hintが指定されない、または空のhintを指定された場合は通常のparser処理をお
1237          * こなう。
1238          * 他のフック関数で実行されるhint処理をスキップするために、global 変数をNULL
1239          * に設定しておく。
1240          */
1241         if (!pg_hint_plan_enable ||
1242                 (global = parse_head_comment(parse)) == NULL ||
1243                 PlanHintIsempty(global))
1244         {
1245                 PlanHintDelete(global);
1246                 global = NULL;
1247
1248                 if (prev_planner_hook)
1249                         return (*prev_planner_hook) (parse, cursorOptions, boundParams);
1250                 else
1251                         return standard_planner(parse, cursorOptions, boundParams);
1252         }
1253
1254         /* Set hint で指定されたGUCパラメータを設定する */
1255         save_nestlevel = set_config_options(global->set_hints, global->context);
1256
1257         if (global->leading != NIL)
1258                 set_join_config_options(0, global->context);
1259
1260         /*
1261          * TODO ビュー定義で指定したテーブル数が1つの場合にもこのタイミングでGUCを変更する必
1262          * 要がある。
1263          */
1264         if (list_length(parse->rtable) == 1 &&
1265                 ((RangeTblEntry *) linitial(parse->rtable))->rtekind == RTE_RELATION)
1266         {
1267                 int     i;
1268                 RangeTblEntry  *rte = linitial(parse->rtable);
1269                 
1270                 for (i = 0; i < global->nscan_hints; i++)
1271                 {
1272                         ScanHint           *hint = global->scan_hints[i];
1273
1274                         if (strcmp(hint->relname, rte->eref->aliasname) != 0)
1275                                 parse_ereport(hint->opt_str, ("Relation \"%s\" does not exist.", hint->relname));
1276
1277                         set_scan_config_options(hint->enforce_mask, global->context);
1278                 }
1279         }
1280
1281         if (prev_planner_hook)
1282                 result = (*prev_planner_hook) (parse, cursorOptions, boundParams);
1283         else
1284                 result = standard_planner(parse, cursorOptions, boundParams);
1285
1286         /*
1287          * Restore the GUC variables we set above.
1288          */
1289         AtEOXact_GUC(true, save_nestlevel);
1290
1291         if (pg_hint_plan_debug_print)
1292         {
1293                 PlanHintDump(global);
1294                 //elog_node_display(INFO, "rtable", parse->rtable, true);
1295         }
1296
1297         PlanHintDelete(global);
1298         global = NULL;
1299
1300         return result;
1301 }
1302
1303 /*
1304  * aliasnameと一致するSCANヒントを探す
1305  */
1306 static ScanHint *
1307 find_scan_hint(RangeTblEntry *rte)
1308 {
1309         int     i;
1310
1311         for (i = 0; i < global->nscan_hints; i++)
1312         {
1313                 ScanHint   *hint = global->scan_hints[i];
1314
1315                 if (strcmp(rte->eref->aliasname, hint->relname) == 0)
1316                         return hint;
1317         }
1318
1319         return NULL;
1320 }
1321
1322 static void
1323 pg_hint_plan_get_relation_info(PlannerInfo *root, Oid relationObjectId,
1324                                                                  bool inhparent, RelOptInfo *rel)
1325 {
1326         ScanHint   *hint;
1327         ListCell   *cell;
1328         ListCell   *prev;
1329         ListCell   *next;
1330
1331         if (prev_get_relation_info)
1332                 (*prev_get_relation_info) (root, relationObjectId, inhparent, rel);
1333
1334         /* 有効なヒントが指定されなかった場合は処理をスキップする。 */
1335         if (!global)
1336                 return;
1337
1338         if (rel->reloptkind != RELOPT_BASEREL)
1339                 return;
1340
1341         if ((hint = find_scan_hint(root->simple_rte_array[rel->relid])) == NULL)
1342                 return;
1343
1344         /* インデックスを全て削除し、スキャンに使えなくする */
1345         if (hint->enforce_mask == ENABLE_SEQSCAN)
1346         {
1347                 list_free_deep(rel->indexlist);
1348                 rel->indexlist = NIL;
1349
1350                 return;
1351         }
1352
1353         /* 後でパスを作り直すため、ここではなにもしない */
1354         if (hint->indexnames == NULL)
1355                 return;
1356
1357         /* 指定されたインデックスのみをのこす */
1358         prev = NULL;
1359         for (cell = list_head(rel->indexlist); cell; cell = next)
1360         {
1361                 IndexOptInfo   *info = (IndexOptInfo *) lfirst(cell);
1362                 char               *indexname = get_rel_name(info->indexoid);
1363                 ListCell           *l;
1364                 bool                    use_index = false;
1365
1366                 next = lnext(cell);
1367
1368                 foreach(l, hint->indexnames)
1369                 {
1370                         if (strcmp(indexname, lfirst(l)) == 0)
1371                         {
1372                                 use_index = true;
1373                                 break;
1374                         }
1375                 }
1376
1377                 if (!use_index)
1378                         rel->indexlist = list_delete_cell(rel->indexlist, cell, prev);
1379                 else
1380                         prev = cell;
1381
1382                 pfree(indexname);
1383         }
1384 }
1385
1386 static Index
1387 scan_relid_aliasname(PlannerInfo *root, char *aliasname, bool check_ambiguous, const char *str)
1388 {
1389         // TODO refnameRangeTblEntry を参考
1390         int             i;
1391         Index   find = 0;
1392
1393         for (i = 1; i < root->simple_rel_array_size; i++)
1394         {
1395                 if (root->simple_rel_array[i] == NULL)
1396                         continue;
1397
1398                 Assert(i == root->simple_rel_array[i]->relid);
1399
1400                 if (strcmp(aliasname, root->simple_rte_array[i]->eref->aliasname) != 0)
1401                         continue;
1402
1403                 if (!check_ambiguous)
1404                         return i;
1405
1406                 if (find)
1407                         parse_ereport(str, ("relation name \"%s\" is ambiguous", aliasname));
1408
1409                 find = i;
1410         }
1411
1412         return find;
1413 }
1414
1415 /*
1416  * relidビットマスクと一致するヒントを探す
1417  */
1418 static JoinHint *
1419 scan_join_hint(Relids joinrelids)
1420 {
1421         List       *join_hint;
1422         ListCell   *l;
1423
1424         join_hint = global->join_hint_level[bms_num_members(joinrelids)];
1425         foreach(l, join_hint)
1426         {
1427                 JoinHint   *hint = (JoinHint *) lfirst(l);
1428                 if (bms_equal(joinrelids, hint->joinrelids))
1429                         return hint;
1430         }
1431
1432         return NULL;
1433 }
1434
1435 /*
1436  * ヒントを使用しやすい構造に変換する。
1437  */
1438 static void
1439 rebuild_join_hints(PlanHint *plan, PlannerInfo *root, int level, List *initial_rels)
1440 {
1441         int                     i;
1442         ListCell   *l;
1443         Relids          joinrelids;
1444         int                     njoinrels;
1445
1446         plan->nlevel = root->simple_rel_array_size - 1;
1447         plan->join_hint_level = palloc0(sizeof(List *) * (root->simple_rel_array_size));
1448         for (i = 0; i < plan->njoin_hints; i++)
1449         {
1450                 JoinHint   *hint = plan->join_hints[i];
1451                 int                     j;
1452                 Index           relid = 0;
1453
1454                 for (j = 0; j < hint->nrels; j++)
1455                 {
1456                         char   *relname = hint->relnames[j];
1457
1458                         relid = scan_relid_aliasname(root, relname, true, hint->opt_str);
1459                         if (relid == 0)
1460                         {
1461                                 parse_ereport(hint->opt_str, ("Relation \"%s\" does not exist.", relname));
1462                                 break;
1463                         }
1464
1465                         hint->joinrelids = bms_add_member(hint->joinrelids, relid);
1466                 }
1467
1468                 if (relid == 0)
1469                         continue;
1470
1471                 plan->join_hint_level[hint->nrels] =
1472                         lappend(plan->join_hint_level[hint->nrels], hint);
1473         }
1474
1475         /* Leading hint は、全ての join 方式が有効な hint として登録する */
1476         joinrelids = NULL;
1477         njoinrels = 0;
1478         foreach(l, plan->leading)
1479         {
1480                 char       *relname = (char *)lfirst(l);
1481                 JoinHint   *hint;
1482
1483                 i = scan_relid_aliasname(root, relname, true, plan->hint_str);
1484                 if (i == 0)
1485                 {
1486                         parse_ereport(plan->hint_str, ("Relation \"%s\" does not exist.", relname));
1487                         list_free_deep(plan->leading);
1488                         plan->leading = NIL;
1489                         break;
1490                 }
1491
1492                 joinrelids = bms_add_member(joinrelids, i);
1493                 njoinrels++;
1494
1495                 if (njoinrels < 2)
1496                         continue;
1497
1498                 if (njoinrels > plan->nlevel)
1499                 {
1500                         parse_ereport(plan->hint_str, ("In %s hint, specified relation name %d or less.", HINT_LEADING, plan->nlevel));
1501                         break;
1502                 }
1503
1504                 /* Leading で指定した組み合わせ以外の join hint を削除する */
1505                 hint = scan_join_hint(joinrelids);
1506                 list_free(plan->join_hint_level[njoinrels]);
1507                 if (hint)
1508                         plan->join_hint_level[njoinrels] = lappend(NIL, hint);
1509                 else
1510                 {
1511                         hint = JoinHintCreate();
1512                         hint->nrels = njoinrels;
1513                         hint->enforce_mask = ENABLE_ALL_JOIN;
1514                         hint->joinrelids = bms_copy(joinrelids);
1515                         plan->join_hint_level[njoinrels] = lappend(NIL, hint);
1516
1517                         if (plan->njoin_hints == 0)
1518                         {
1519                                 plan->max_join_hints = HINT_ARRAY_DEFAULT_INITSIZE;
1520                                 plan->join_hints = palloc(sizeof(JoinHint *) * plan->max_join_hints);
1521                         }
1522                         else if (plan->njoin_hints == plan->max_join_hints)
1523                         {
1524                                 plan->max_join_hints *= 2;
1525                                 plan->join_hints = repalloc(plan->join_hints,
1526                                                                         sizeof(JoinHint *) * plan->max_join_hints);
1527                         }
1528
1529                         plan->join_hints[plan->njoin_hints] = hint;
1530                         plan->njoin_hints++;
1531                 }
1532         }
1533
1534         bms_free(joinrelids);
1535 }
1536
1537 static void
1538 rebuild_scan_path(PlanHint *plan, PlannerInfo *root, int level, List *initial_rels)
1539 {
1540         int     i;
1541         int     save_nestlevel = 0;
1542
1543         for (i = 0; i < plan->nscan_hints; i++)
1544         {
1545                 ScanHint   *hint = plan->scan_hints[i];
1546                 ListCell   *l;
1547
1548                 if (hint->enforce_mask == ENABLE_SEQSCAN)
1549                         continue;
1550
1551                 foreach(l, initial_rels)
1552                 {
1553                         RelOptInfo         *rel = (RelOptInfo *) lfirst(l);
1554                         RangeTblEntry  *rte = root->simple_rte_array[rel->relid];
1555
1556                         if (rel->reloptkind != RELOPT_BASEREL ||
1557                                 strcmp(hint->relname, rte->eref->aliasname) != 0)
1558                                 continue;
1559
1560                         if (save_nestlevel != 0)
1561                                 save_nestlevel = NewGUCNestLevel();
1562
1563                         /*
1564                          * TODO ヒントで指定されたScan方式が最安価でない場合のみ、Pathを生成
1565                          * しなおす
1566                          */
1567                         set_scan_config_options(hint->enforce_mask, plan->context);
1568
1569                         rel->pathlist = NIL;    // TODO 解放
1570                         set_plain_rel_pathlist(root, rel, rte);
1571
1572                         break;
1573                 }
1574         }
1575
1576         /*
1577          * Restore the GUC variables we set above.
1578          */
1579         if (save_nestlevel != 0)
1580                 AtEOXact_GUC(true, save_nestlevel);
1581 }
1582
1583 /*
1584  * src/backend/optimizer/path/joinrels.c
1585  * export make_join_rel() をラップする関数
1586  * 
1587  * ヒントにしたがって、enabele_* パラメータを変更した上で、make_join_rel()を
1588  * 呼び出す。
1589  */
1590 static RelOptInfo *
1591 pg_hint_plan_make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
1592 {
1593         Relids                  joinrelids;
1594         JoinHint           *hint;
1595         RelOptInfo         *rel;
1596         int                             save_nestlevel;
1597
1598         joinrelids = bms_union(rel1->relids, rel2->relids);
1599         hint = scan_join_hint(joinrelids);
1600         bms_free(joinrelids);
1601
1602         if (!hint)
1603                 return make_join_rel(root, rel1, rel2);
1604
1605         save_nestlevel = NewGUCNestLevel();
1606
1607         set_join_config_options(hint->enforce_mask, global->context);
1608
1609         rel = make_join_rel(root, rel1, rel2);
1610
1611         /*
1612          * Restore the GUC variables we set above.
1613          */
1614         AtEOXact_GUC(true, save_nestlevel);
1615
1616         return rel;
1617 }
1618
1619 static RelOptInfo *_standard_join_search(PlannerInfo *root, int levels_needed, List *initial_rels);
1620 static RelOptInfo *
1621 pg_hint_plan_join_search(PlannerInfo *root, int levels_needed, List *initial_rels)
1622 {
1623         /* 有効なヒントが指定されなかった場合は本来の処理を実行する。 */
1624         if (!global)
1625         {
1626                 if (prev_join_search)
1627                         return (*prev_join_search) (root, levels_needed, initial_rels);
1628                 else if (enable_geqo && levels_needed >= geqo_threshold)
1629                         return geqo(root, levels_needed, initial_rels);
1630                 else
1631                         return standard_join_search(root, levels_needed, initial_rels);
1632         }
1633
1634         return _standard_join_search(root, levels_needed, initial_rels);
1635 }
1636
1637 /*
1638  * PostgreSQL 本体から流用した関数
1639  */
1640
1641 /*
1642  * src/backend/optimizer/path/allpaths.c
1643  * export standard_join_search() を流用
1644  * 
1645  * 変更箇所
1646  *  rebuild_join_hints() と rebuild_scan_path() の呼び出しを追加
1647  */
1648 /*
1649  * standard_join_search
1650  *        Find possible joinpaths for a query by successively finding ways
1651  *        to join component relations into join relations.
1652  *
1653  * 'levels_needed' is the number of iterations needed, ie, the number of
1654  *              independent jointree items in the query.  This is > 1.
1655  *
1656  * 'initial_rels' is a list of RelOptInfo nodes for each independent
1657  *              jointree item.  These are the components to be joined together.
1658  *              Note that levels_needed == list_length(initial_rels).
1659  *
1660  * Returns the final level of join relations, i.e., the relation that is
1661  * the result of joining all the original relations together.
1662  * At least one implementation path must be provided for this relation and
1663  * all required sub-relations.
1664  *
1665  * To support loadable plugins that modify planner behavior by changing the
1666  * join searching algorithm, we provide a hook variable that lets a plugin
1667  * replace or supplement this function.  Any such hook must return the same
1668  * final join relation as the standard code would, but it might have a
1669  * different set of implementation paths attached, and only the sub-joinrels
1670  * needed for these paths need have been instantiated.
1671  *
1672  * Note to plugin authors: the functions invoked during standard_join_search()
1673  * modify root->join_rel_list and root->join_rel_hash.  If you want to do more
1674  * than one join-order search, you'll probably need to save and restore the
1675  * original states of those data structures.  See geqo_eval() for an example.
1676  */
1677 static RelOptInfo *
1678 _standard_join_search(PlannerInfo *root, int levels_needed, List *initial_rels)
1679 {
1680         int                     lev;
1681         RelOptInfo *rel;
1682
1683         /*
1684          * This function cannot be invoked recursively within any one planning
1685          * problem, so join_rel_level[] can't be in use already.
1686          */
1687         Assert(root->join_rel_level == NULL);
1688
1689         /*
1690          * We employ a simple "dynamic programming" algorithm: we first find all
1691          * ways to build joins of two jointree items, then all ways to build joins
1692          * of three items (from two-item joins and single items), then four-item
1693          * joins, and so on until we have considered all ways to join all the
1694          * items into one rel.
1695          *
1696          * root->join_rel_level[j] is a list of all the j-item rels.  Initially we
1697          * set root->join_rel_level[1] to represent all the single-jointree-item
1698          * relations.
1699          */
1700         root->join_rel_level = (List **) palloc0((levels_needed + 1) * sizeof(List *));
1701
1702         root->join_rel_level[1] = initial_rels;
1703
1704         rebuild_join_hints(global, root, levels_needed, initial_rels);
1705         rebuild_scan_path(global, root, levels_needed, initial_rels);
1706
1707         for (lev = 2; lev <= levels_needed; lev++)
1708         {
1709                 ListCell   *lc;
1710
1711                 /*
1712                  * Determine all possible pairs of relations to be joined at this
1713                  * level, and build paths for making each one from every available
1714                  * pair of lower-level relations.
1715                  */
1716                 my_join_search_one_level(root, lev);
1717
1718                 /*
1719                  * Do cleanup work on each just-processed rel.
1720                  */
1721                 foreach(lc, root->join_rel_level[lev])
1722                 {
1723                         rel = (RelOptInfo *) lfirst(lc);
1724
1725                         /* Find and save the cheapest paths for this rel */
1726                         set_cheapest(rel);
1727
1728 #ifdef OPTIMIZER_DEBUG
1729                         debug_print_rel(root, rel);
1730 #endif
1731                 }
1732         }
1733
1734         /*
1735          * We should have a single rel at the final level.
1736          */
1737         if (root->join_rel_level[levels_needed] == NIL)
1738                 elog(ERROR, "failed to build any %d-way joins", levels_needed);
1739         Assert(list_length(root->join_rel_level[levels_needed]) == 1);
1740
1741         rel = (RelOptInfo *) linitial(root->join_rel_level[levels_needed]);
1742
1743         root->join_rel_level = NULL;
1744
1745         return rel;
1746 }
1747
1748 /*
1749  * src/backend/optimizer/path/joinrels.c
1750  * join_search_one_level() を流用
1751  * 
1752  * 変更箇所
1753  *  make_join_rel() の呼び出しをラップする、pg_hint_plan_make_join_rel()の呼び出しに変更
1754  */
1755 /*
1756  * join_search_one_level
1757  *        Consider ways to produce join relations containing exactly 'level'
1758  *        jointree items.  (This is one step of the dynamic-programming method
1759  *        embodied in standard_join_search.)  Join rel nodes for each feasible
1760  *        combination of lower-level rels are created and returned in a list.
1761  *        Implementation paths are created for each such joinrel, too.
1762  *
1763  * level: level of rels we want to make this time
1764  * root->join_rel_level[j], 1 <= j < level, is a list of rels containing j items
1765  *
1766  * The result is returned in root->join_rel_level[level].
1767  */
1768 static void
1769 my_join_search_one_level(PlannerInfo *root, int level)
1770 {
1771         List      **joinrels = root->join_rel_level;
1772         ListCell   *r;
1773         int                     k;
1774
1775         Assert(joinrels[level] == NIL);
1776
1777         /* Set join_cur_level so that new joinrels are added to proper list */
1778         root->join_cur_level = level;
1779
1780         /*
1781          * First, consider left-sided and right-sided plans, in which rels of
1782          * exactly level-1 member relations are joined against initial relations.
1783          * We prefer to join using join clauses, but if we find a rel of level-1
1784          * members that has no join clauses, we will generate Cartesian-product
1785          * joins against all initial rels not already contained in it.
1786          *
1787          * In the first pass (level == 2), we try to join each initial rel to each
1788          * initial rel that appears later in joinrels[1].  (The mirror-image joins
1789          * are handled automatically by make_join_rel.)  In later passes, we try
1790          * to join rels of size level-1 from joinrels[level-1] to each initial rel
1791          * in joinrels[1].
1792          */
1793         foreach(r, joinrels[level - 1])
1794         {
1795                 RelOptInfo *old_rel = (RelOptInfo *) lfirst(r);
1796                 ListCell   *other_rels;
1797
1798                 if (level == 2)
1799                         other_rels = lnext(r);          /* only consider remaining initial
1800                                                                                  * rels */
1801                 else
1802                         other_rels = list_head(joinrels[1]);            /* consider all initial
1803                                                                                                                  * rels */
1804
1805                 if (old_rel->joininfo != NIL || old_rel->has_eclass_joins ||
1806                         has_join_restriction(root, old_rel))
1807                 {
1808                         /*
1809                          * Note that if all available join clauses for this rel require
1810                          * more than one other rel, we will fail to make any joins against
1811                          * it here.  In most cases that's OK; it'll be considered by
1812                          * "bushy plan" join code in a higher-level pass where we have
1813                          * those other rels collected into a join rel.
1814                          *
1815                          * See also the last-ditch case below.
1816                          */
1817                         make_rels_by_clause_joins(root,
1818                                                                           old_rel,
1819                                                                           other_rels);
1820                 }
1821                 else
1822                 {
1823                         /*
1824                          * Oops, we have a relation that is not joined to any other
1825                          * relation, either directly or by join-order restrictions.
1826                          * Cartesian product time.
1827                          */
1828                         make_rels_by_clauseless_joins(root,
1829                                                                                   old_rel,
1830                                                                                   other_rels);
1831                 }
1832         }
1833
1834         /*
1835          * Now, consider "bushy plans" in which relations of k initial rels are
1836          * joined to relations of level-k initial rels, for 2 <= k <= level-2.
1837          *
1838          * We only consider bushy-plan joins for pairs of rels where there is a
1839          * suitable join clause (or join order restriction), in order to avoid
1840          * unreasonable growth of planning time.
1841          */
1842         for (k = 2;; k++)
1843         {
1844                 int                     other_level = level - k;
1845
1846                 /*
1847                  * Since make_join_rel(x, y) handles both x,y and y,x cases, we only
1848                  * need to go as far as the halfway point.
1849                  */
1850                 if (k > other_level)
1851                         break;
1852
1853                 foreach(r, joinrels[k])
1854                 {
1855                         RelOptInfo *old_rel = (RelOptInfo *) lfirst(r);
1856                         ListCell   *other_rels;
1857                         ListCell   *r2;
1858
1859                         /*
1860                          * We can ignore clauseless joins here, *except* when they
1861                          * participate in join-order restrictions --- then we might have
1862                          * to force a bushy join plan.
1863                          */
1864                         if (old_rel->joininfo == NIL && !old_rel->has_eclass_joins &&
1865                                 !has_join_restriction(root, old_rel))
1866                                 continue;
1867
1868                         if (k == other_level)
1869                                 other_rels = lnext(r);  /* only consider remaining rels */
1870                         else
1871                                 other_rels = list_head(joinrels[other_level]);
1872
1873                         for_each_cell(r2, other_rels)
1874                         {
1875                                 RelOptInfo *new_rel = (RelOptInfo *) lfirst(r2);
1876
1877                                 if (!bms_overlap(old_rel->relids, new_rel->relids))
1878                                 {
1879                                         /*
1880                                          * OK, we can build a rel of the right level from this
1881                                          * pair of rels.  Do so if there is at least one usable
1882                                          * join clause or a relevant join restriction.
1883                                          */
1884                                         if (have_relevant_joinclause(root, old_rel, new_rel) ||
1885                                                 have_join_order_restriction(root, old_rel, new_rel))
1886                                         {
1887                                                 (void) pg_hint_plan_make_join_rel(root, old_rel, new_rel);
1888                                         }
1889                                 }
1890                         }
1891                 }
1892         }
1893
1894         /*
1895          * Last-ditch effort: if we failed to find any usable joins so far, force
1896          * a set of cartesian-product joins to be generated.  This handles the
1897          * special case where all the available rels have join clauses but we
1898          * cannot use any of those clauses yet.  An example is
1899          *
1900          * SELECT * FROM a,b,c WHERE (a.f1 + b.f2 + c.f3) = 0;
1901          *
1902          * The join clause will be usable at level 3, but at level 2 we have no
1903          * choice but to make cartesian joins.  We consider only left-sided and
1904          * right-sided cartesian joins in this case (no bushy).
1905          */
1906         if (joinrels[level] == NIL)
1907         {
1908                 /*
1909                  * This loop is just like the first one, except we always call
1910                  * make_rels_by_clauseless_joins().
1911                  */
1912                 foreach(r, joinrels[level - 1])
1913                 {
1914                         RelOptInfo *old_rel = (RelOptInfo *) lfirst(r);
1915                         ListCell   *other_rels;
1916
1917                         if (level == 2)
1918                                 other_rels = lnext(r);  /* only consider remaining initial
1919                                                                                  * rels */
1920                         else
1921                                 other_rels = list_head(joinrels[1]);    /* consider all initial
1922                                                                                                                  * rels */
1923
1924                         make_rels_by_clauseless_joins(root,
1925                                                                                   old_rel,
1926                                                                                   other_rels);
1927                 }
1928
1929                 /*----------
1930                  * When special joins are involved, there may be no legal way
1931                  * to make an N-way join for some values of N.  For example consider
1932                  *
1933                  * SELECT ... FROM t1 WHERE
1934                  *       x IN (SELECT ... FROM t2,t3 WHERE ...) AND
1935                  *       y IN (SELECT ... FROM t4,t5 WHERE ...)
1936                  *
1937                  * We will flatten this query to a 5-way join problem, but there are
1938                  * no 4-way joins that join_is_legal() will consider legal.  We have
1939                  * to accept failure at level 4 and go on to discover a workable
1940                  * bushy plan at level 5.
1941                  *
1942                  * However, if there are no special joins then join_is_legal() should
1943                  * never fail, and so the following sanity check is useful.
1944                  *----------
1945                  */
1946                 if (joinrels[level] == NIL && root->join_info_list == NIL)
1947                         elog(ERROR, "failed to build any %d-way joins", level);
1948         }
1949 }
1950
1951 /*
1952  * src/backend/optimizer/path/joinrels.c
1953  * static make_rels_by_clause_joins() を流用
1954  * 
1955  * 変更箇所
1956  *  make_join_rel() の呼び出しをラップする、pg_hint_plan_make_join_rel()の呼び出しに変更
1957  */
1958 /*
1959  * make_rels_by_clause_joins
1960  *        Build joins between the given relation 'old_rel' and other relations
1961  *        that participate in join clauses that 'old_rel' also participates in
1962  *        (or participate in join-order restrictions with it).
1963  *        The join rels are returned in root->join_rel_level[join_cur_level].
1964  *
1965  * Note: at levels above 2 we will generate the same joined relation in
1966  * multiple ways --- for example (a join b) join c is the same RelOptInfo as
1967  * (b join c) join a, though the second case will add a different set of Paths
1968  * to it.  This is the reason for using the join_rel_level mechanism, which
1969  * automatically ensures that each new joinrel is only added to the list once.
1970  *
1971  * 'old_rel' is the relation entry for the relation to be joined
1972  * 'other_rels': the first cell in a linked list containing the other
1973  * rels to be considered for joining
1974  *
1975  * Currently, this is only used with initial rels in other_rels, but it
1976  * will work for joining to joinrels too.
1977  */
1978 static void
1979 make_rels_by_clause_joins(PlannerInfo *root,
1980                                                   RelOptInfo *old_rel,
1981                                                   ListCell *other_rels)
1982 {
1983         ListCell   *l;
1984
1985         for_each_cell(l, other_rels)
1986         {
1987                 RelOptInfo *other_rel = (RelOptInfo *) lfirst(l);
1988
1989                 if (!bms_overlap(old_rel->relids, other_rel->relids) &&
1990                         (have_relevant_joinclause(root, old_rel, other_rel) ||
1991                          have_join_order_restriction(root, old_rel, other_rel)))
1992                 {
1993                         (void) pg_hint_plan_make_join_rel(root, old_rel, other_rel);
1994                 }
1995         }
1996 }
1997
1998 /*
1999  * src/backend/optimizer/path/joinrels.c
2000  * static make_rels_by_clauseless_joins() を流用
2001  * 
2002  * 変更箇所
2003  *  make_join_rel() の呼び出しをラップする、pg_hint_plan_make_join_rel()の呼び出しに変更
2004  */
2005 /*
2006  * make_rels_by_clauseless_joins
2007  *        Given a relation 'old_rel' and a list of other relations
2008  *        'other_rels', create a join relation between 'old_rel' and each
2009  *        member of 'other_rels' that isn't already included in 'old_rel'.
2010  *        The join rels are returned in root->join_rel_level[join_cur_level].
2011  *
2012  * 'old_rel' is the relation entry for the relation to be joined
2013  * 'other_rels': the first cell of a linked list containing the
2014  * other rels to be considered for joining
2015  *
2016  * Currently, this is only used with initial rels in other_rels, but it would
2017  * work for joining to joinrels too.
2018  */
2019 static void
2020 make_rels_by_clauseless_joins(PlannerInfo *root,
2021                                                           RelOptInfo *old_rel,
2022                                                           ListCell *other_rels)
2023 {
2024         ListCell   *l;
2025
2026         for_each_cell(l, other_rels)
2027         {
2028                 RelOptInfo *other_rel = (RelOptInfo *) lfirst(l);
2029
2030                 if (!bms_overlap(other_rel->relids, old_rel->relids))
2031                 {
2032                         (void) pg_hint_plan_make_join_rel(root, old_rel, other_rel);
2033                 }
2034         }
2035 }
2036
2037 /*
2038  * src/backend/optimizer/path/joinrels.c
2039  * static has_join_restriction() を流用
2040  * 
2041  * 変更箇所
2042  *  なし
2043  */
2044 /*
2045  * has_join_restriction
2046  *              Detect whether the specified relation has join-order restrictions
2047  *              due to being inside an outer join or an IN (sub-SELECT).
2048  *
2049  * Essentially, this tests whether have_join_order_restriction() could
2050  * succeed with this rel and some other one.  It's OK if we sometimes
2051  * say "true" incorrectly.      (Therefore, we don't bother with the relatively
2052  * expensive has_legal_joinclause test.)
2053  */
2054 static bool
2055 has_join_restriction(PlannerInfo *root, RelOptInfo *rel)
2056 {
2057         ListCell   *l;
2058
2059         foreach(l, root->join_info_list)
2060         {
2061                 SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) lfirst(l);
2062
2063                 /* ignore full joins --- other mechanisms preserve their ordering */
2064                 if (sjinfo->jointype == JOIN_FULL)
2065                         continue;
2066
2067                 /* ignore if SJ is already contained in rel */
2068                 if (bms_is_subset(sjinfo->min_lefthand, rel->relids) &&
2069                         bms_is_subset(sjinfo->min_righthand, rel->relids))
2070                         continue;
2071
2072                 /* restricted if it overlaps LHS or RHS, but doesn't contain SJ */
2073                 if (bms_overlap(sjinfo->min_lefthand, rel->relids) ||
2074                         bms_overlap(sjinfo->min_righthand, rel->relids))
2075                         return true;
2076         }
2077
2078         return false;
2079 }
2080
2081 /*
2082  * src/backend/optimizer/path/allpaths.c
2083  * static set_plain_rel_pathlist() を流用
2084  * 
2085  * 変更箇所
2086  *  なし
2087  */
2088 /*
2089  * set_plain_rel_pathlist
2090  *        Build access paths for a plain relation (no subquery, no inheritance)
2091  */
2092 static void
2093 set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
2094 {
2095         /* Consider sequential scan */
2096         add_path(rel, create_seqscan_path(root, rel));
2097
2098         /* Consider index scans */
2099         create_index_paths(root, rel);
2100
2101         /* Consider TID scans */
2102         create_tidscan_paths(root, rel);
2103
2104         /* Now find the cheapest of the paths for this rel */
2105         set_cheapest(rel);
2106 }