OSDN Git Service

コメントの空白除去などのクリーンアップをした。
[pghintplan/pg_hint_plan.git] / pg_hint_plan.c
1 /*-------------------------------------------------------------------------
2  *
3  * pg_hint_plan.c
4  *                do instructions or hints to the planner using C-style block comments
5  *                of the SQL.
6  *
7  * Copyright (c) 2012, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
8  *
9  *-------------------------------------------------------------------------
10  */
11 #include "postgres.h"
12 #include "commands/prepare.h"
13 #include "miscadmin.h"
14 #include "nodes/nodeFuncs.h"
15 #include "optimizer/clauses.h"
16 #include "optimizer/cost.h"
17 #include "optimizer/geqo.h"
18 #include "optimizer/joininfo.h"
19 #include "optimizer/pathnode.h"
20 #include "optimizer/paths.h"
21 #include "optimizer/plancat.h"
22 #include "optimizer/planner.h"
23 #include "optimizer/prep.h"
24 #include "optimizer/restrictinfo.h"
25 #include "tcop/tcopprot.h"
26 #include "tcop/utility.h"
27 #include "utils/lsyscache.h"
28 #include "utils/memutils.h"
29
30 #ifdef PG_MODULE_MAGIC
31 PG_MODULE_MAGIC;
32 #endif
33
34 #if PG_VERSION_NUM < 90100
35 #error unsupported PostgreSQL version
36 #endif
37
38 #define BLOCK_COMMENT_START             "/*"
39 #define BLOCK_COMMENT_END               "*/"
40 #define HINT_COMMENT_KEYWORD    "+"
41 #define HINT_START              BLOCK_COMMENT_START HINT_COMMENT_KEYWORD
42 #define HINT_END                BLOCK_COMMENT_END
43
44 /* hint keywords */
45 #define HINT_SEQSCAN                    "SeqScan"
46 #define HINT_INDEXSCAN                  "IndexScan"
47 #define HINT_BITMAPSCAN                 "BitmapScan"
48 #define HINT_TIDSCAN                    "TidScan"
49 #define HINT_NOSEQSCAN                  "NoSeqScan"
50 #define HINT_NOINDEXSCAN                "NoIndexScan"
51 #define HINT_NOBITMAPSCAN               "NoBitmapScan"
52 #define HINT_NOTIDSCAN                  "NoTidScan"
53 #if PG_VERSION_NUM >= 90200
54 #define HINT_INDEXONLYSCAN              "IndexonlyScan"
55 #define HINT_NOINDEXONLYSCAN    "NoIndexonlyScan"
56 #endif
57 #define HINT_NESTLOOP                   "NestLoop"
58 #define HINT_MERGEJOIN                  "MergeJoin"
59 #define HINT_HASHJOIN                   "HashJoin"
60 #define HINT_NONESTLOOP                 "NoNestLoop"
61 #define HINT_NOMERGEJOIN                "NoMergeJoin"
62 #define HINT_NOHASHJOIN                 "NoHashJoin"
63 #define HINT_LEADING                    "Leading"
64 #define HINT_SET                                "Set"
65
66
67 #define HINT_ARRAY_DEFAULT_INITSIZE 8
68
69 #define parse_ereport(str, detail) \
70         ereport(pg_hint_plan_parse_messages, \
71                         (errmsg("hint syntax error at or near \"%s\"", (str)), \
72                          errdetail detail))
73
74 #define skip_space(str) \
75         while (isspace(*str)) \
76                 str++;
77
78 enum
79 {
80         ENABLE_SEQSCAN                  = 0x01,
81         ENABLE_INDEXSCAN                = 0x02,
82         ENABLE_BITMAPSCAN               = 0x04,
83         ENABLE_TIDSCAN                  = 0x08,
84 #if PG_VERSION_NUM >= 90200
85         ENABLE_INDEXONLYSCAN    = 0x10
86 #endif
87 } SCAN_TYPE_BITS;
88
89 enum
90 {
91         ENABLE_NESTLOOP                 = 0x01,
92         ENABLE_MERGEJOIN                = 0x02,
93         ENABLE_HASHJOIN                 = 0x04
94 } JOIN_TYPE_BITS;
95
96 #define ENABLE_ALL_SCAN (ENABLE_SEQSCAN | ENABLE_INDEXSCAN | ENABLE_BITMAPSCAN \
97                                                 | ENABLE_TIDSCAN)
98 #if PG_VERSION_NUM >= 90200
99 #define ENABLE_ALL_SCAN (ENABLE_SEQSCAN | ENABLE_INDEXSCAN | ENABLE_BITMAPSCAN \
100                                                 | ENABLE_TIDSCAN | ENABLE_INDEXONLYSCAN)
101 #endif
102 #define ENABLE_ALL_JOIN (ENABLE_NESTLOOP | ENABLE_MERGEJOIN | ENABLE_HASHJOIN)
103 #define DISABLE_ALL_SCAN 0
104 #define DISABLE_ALL_JOIN 0
105
106 typedef struct Hint Hint;
107 typedef struct PlanHint PlanHint;
108
109 typedef Hint *(*HintCreateFunction) (const char *hint_str, const char *keyword);
110 typedef void (*HintDeleteFunction) (Hint *hint);
111 typedef void (*HintDumpFunction) (Hint *hint, StringInfo buf);
112 typedef int (*HintCmpFunction) (const Hint *a, const Hint *b);
113 typedef const char *(*HintParseFunction) (Hint *hint, PlanHint *plan, Query *parse, const char *str);
114
115 /* hint types */
116 #define NUM_HINT_TYPE   4
117 typedef enum HintType
118 {
119         HINT_TYPE_SCAN_METHOD,
120         HINT_TYPE_JOIN_METHOD,
121         HINT_TYPE_LEADING,
122         HINT_TYPE_SET
123 } HintType;
124
125 /* hint status */
126 typedef enum HintStatus
127 {
128         HINT_STATE_NOTUSED = 0, /* specified relation not used in query */
129         HINT_STATE_USED,                /* hint is used */
130         HINT_STATE_DUPLICATION, /* specified hint duplication */
131         /* execute error (parse error does not include it) */
132         HINT_STATE_ERROR
133 } HintStatus;
134
135 #define hint_state_enabled(hint) ((hint)->base.state == HINT_STATE_NOTUSED || \
136                                                                   (hint)->base.state == HINT_STATE_USED)
137
138 /* common data for all hints. */
139 struct Hint
140 {
141         const char                 *hint_str;           /* must not do pfree */
142         const char                 *keyword;            /* must not do pfree */
143         HintType                        type;
144         HintStatus                      state;
145         HintDeleteFunction      delete_func;
146         HintDumpFunction        dump_func;
147         HintCmpFunction         cmp_func;
148         HintParseFunction       parser_func;
149 };
150
151 /* scan method hints */
152 typedef struct ScanMethodHint
153 {
154         Hint                    base;
155         char               *relname;
156         List               *indexnames;
157         unsigned char   enforce_mask;
158 } ScanMethodHint;
159
160 /* join method hints */
161 typedef struct JoinMethodHint
162 {
163         Hint                    base;
164         int                             nrels;
165         char              **relnames;
166         unsigned char   enforce_mask;
167         Relids                  joinrelids;
168 } JoinMethodHint;
169
170 /* join order hints */
171 typedef struct LeadingHint
172 {
173         Hint    base;
174         List   *relations;              /* relation names specified in Leading hint */
175 } LeadingHint;
176
177 /* change a run-time parameter hints */
178 typedef struct SetHint
179 {
180         Hint    base;
181         char   *name;                           /* name of variable */
182         char   *value;
183 } SetHint;
184
185 /*
186  * Describes a context of hint processing.
187  */
188 struct PlanHint
189 {
190         char               *hint_str;                   /* original hint string */
191
192         /* all hint */
193         int                             nall_hints;                     /* # of valid all hints */
194         int                             max_all_hints;          /* # of slots for all hints */
195         Hint              **all_hints;                  /* parsed all hints */
196
197         /* # of each hints */
198         int                             num_hints[NUM_HINT_TYPE];
199
200         /* for scan method hints */
201         ScanMethodHint **scan_hints;            /* parsed scan hints */
202         int                             init_scan_mask;         /* initial value scan parameter */
203         Index                   parent_relid;           /* inherit parent table relid */
204
205         /* for join method hints */
206         JoinMethodHint **join_hints;            /* parsed join hints */
207         int                             init_join_mask;         /* initial value join parameter */
208         List              **join_hint_level;
209
210         /* for Leading hints */
211         LeadingHint       **leading_hints;              /* parsed Leading hints */
212
213         /* for Set hints */
214         SetHint           **set_hints;                  /* parsed Set hints */
215         GucContext              context;                        /* which GUC parameters can we set? */
216 };
217
218 /*
219  * Describes a hint parser module which is bound with particular hint keyword.
220  */
221 typedef struct HintParser
222 {
223         char   *keyword;
224         HintCreateFunction      create_func;
225 } HintParser;
226
227 /* Module callbacks */
228 void            _PG_init(void);
229 void            _PG_fini(void);
230
231 void pg_hint_plan_ProcessUtility(Node *parsetree, const char *queryString,
232                                                                  ParamListInfo params, bool isTopLevel,
233                                                                  DestReceiver *dest, char *completionTag);
234 static PlannedStmt *pg_hint_plan_planner(Query *parse, int cursorOptions,
235                                                            ParamListInfo boundParams);
236 static void pg_hint_plan_get_relation_info(PlannerInfo *root, Oid relationObjectId,
237                                                                  bool inhparent, RelOptInfo *rel);
238 static RelOptInfo *pg_hint_plan_join_search(PlannerInfo *root, int levels_needed,
239                                                                   List *initial_rels);
240
241 static Hint *ScanMethodHintCreate(const char *hint_str, const char *keyword);
242 static void ScanMethodHintDelete(ScanMethodHint *hint);
243 static void ScanMethodHintDump(ScanMethodHint *hint, StringInfo buf);
244 static int ScanMethodHintCmp(const ScanMethodHint *a, const ScanMethodHint *b);
245 static const char *ScanMethodHintParse(ScanMethodHint *hint, PlanHint *plan, Query *parse, const char *str);
246 static Hint *JoinMethodHintCreate(const char *hint_str, const char *keyword);
247 static void JoinMethodHintDelete(JoinMethodHint *hint);
248 static void JoinMethodHintDump(JoinMethodHint *hint, StringInfo buf);
249 static int JoinMethodHintCmp(const JoinMethodHint *a, const JoinMethodHint *b);
250 static const char *JoinMethodHintParse(JoinMethodHint *hint, PlanHint *plan, Query *parse, const char *str);
251 static Hint *LeadingHintCreate(const char *hint_str, const char *keyword);
252 static void LeadingHintDelete(LeadingHint *hint);
253 static void LeadingHintDump(LeadingHint *hint, StringInfo buf);
254 static int LeadingHintCmp(const LeadingHint *a, const LeadingHint *b);
255 static const char *LeadingHintParse(LeadingHint *hint, PlanHint *plan, Query *parse, const char *str);
256 static Hint *SetHintCreate(const char *hint_str, const char *keyword);
257 static void SetHintDelete(SetHint *hint);
258 static void SetHintDump(SetHint *hint, StringInfo buf);
259 static int SetHintCmp(const SetHint *a, const SetHint *b);
260 static const char *SetHintParse(SetHint *hint, PlanHint *plan, Query *parse, const char *str);
261
262 RelOptInfo *pg_hint_plan_standard_join_search(PlannerInfo *root, int levels_needed, List *initial_rels);
263 void pg_hint_plan_join_search_one_level(PlannerInfo *root, int level);
264 static void make_rels_by_clause_joins(PlannerInfo *root, RelOptInfo *old_rel, ListCell *other_rels);
265 static void make_rels_by_clauseless_joins(PlannerInfo *root, RelOptInfo *old_rel, ListCell *other_rels);
266 static bool has_join_restriction(PlannerInfo *root, RelOptInfo *rel);
267 static void set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
268                                  Index rti, RangeTblEntry *rte);
269 static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
270                                            RangeTblEntry *rte);
271 static void set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
272                                                 Index rti, RangeTblEntry *rte);
273 static List *accumulate_append_subpath(List *subpaths, Path *path);
274 static void set_dummy_rel_pathlist(RelOptInfo *rel);
275 RelOptInfo *pg_hint_plan_make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2);
276
277
278 /* GUC variables */
279 static bool pg_hint_plan_enable = true;
280 static bool pg_hint_plan_debug_print = false;
281 static int pg_hint_plan_parse_messages = INFO;
282
283 static const struct config_enum_entry parse_messages_level_options[] = {
284         {"debug", DEBUG2, true},
285         {"debug5", DEBUG5, false},
286         {"debug4", DEBUG4, false},
287         {"debug3", DEBUG3, false},
288         {"debug2", DEBUG2, false},
289         {"debug1", DEBUG1, false},
290         {"log", LOG, false},
291         {"info", INFO, false},
292         {"notice", NOTICE, false},
293         {"warning", WARNING, false},
294         {"error", ERROR, false},
295         /*
296         {"fatal", FATAL, true},
297         {"panic", PANIC, true},
298          */
299         {NULL, 0, false}
300 };
301
302 /* Saved hook values in case of unload */
303 static ProcessUtility_hook_type prev_ProcessUtility = NULL;
304 static planner_hook_type prev_planner = NULL;
305 static get_relation_info_hook_type prev_get_relation_info = NULL;
306 static join_search_hook_type prev_join_search = NULL;
307
308 /* フック関数をまたがって使用する情報を管理する */
309 static PlanHint *global = NULL;
310
311 /*
312  * EXECUTEコマンド実行時に、ステートメント名を格納する。
313  * その他のコマンドの場合は、NULLに設定する。
314  */
315 static char        *stmt_name = NULL;
316
317 static const HintParser parsers[] = {
318         {HINT_SEQSCAN, ScanMethodHintCreate},
319         {HINT_INDEXSCAN, ScanMethodHintCreate},
320         {HINT_BITMAPSCAN, ScanMethodHintCreate},
321         {HINT_TIDSCAN, ScanMethodHintCreate},
322         {HINT_NOSEQSCAN, ScanMethodHintCreate},
323         {HINT_NOINDEXSCAN, ScanMethodHintCreate},
324         {HINT_NOBITMAPSCAN, ScanMethodHintCreate},
325         {HINT_NOTIDSCAN, ScanMethodHintCreate},
326 #if PG_VERSION_NUM >= 90200
327         {HINT_INDEXONLYSCAN, ScanMethodHintCreate},
328         {HINT_NOINDEXONLYSCAN, ScanMethodHintCreate},
329 #endif
330         {HINT_NESTLOOP, JoinMethodHintCreate},
331         {HINT_MERGEJOIN, JoinMethodHintCreate},
332         {HINT_HASHJOIN, JoinMethodHintCreate},
333         {HINT_NONESTLOOP, JoinMethodHintCreate},
334         {HINT_NOMERGEJOIN, JoinMethodHintCreate},
335         {HINT_NOHASHJOIN, JoinMethodHintCreate},
336         {HINT_LEADING, LeadingHintCreate},
337         {HINT_SET, SetHintCreate},
338         {NULL, NULL}
339 };
340
341 /*
342  * Module load callbacks
343  */
344 void
345 _PG_init(void)
346 {
347         /* Define custom GUC variables. */
348         DefineCustomBoolVariable("pg_hint_plan.enable",
349                          "Instructions or hints to the planner using block comments.",
350                                                          NULL,
351                                                          &pg_hint_plan_enable,
352                                                          true,
353                                                          PGC_USERSET,
354                                                          0,
355                                                          NULL,
356                                                          NULL,
357                                                          NULL);
358
359         DefineCustomBoolVariable("pg_hint_plan.debug_print",
360                                                          "Logs each query's parse results of the hint.",
361                                                          NULL,
362                                                          &pg_hint_plan_debug_print,
363                                                          false,
364                                                          PGC_USERSET,
365                                                          0,
366                                                          NULL,
367                                                          NULL,
368                                                          NULL);
369
370         DefineCustomEnumVariable("pg_hint_plan.parse_messages",
371                                                          "Messege level of the parse error.",
372                                                          NULL,
373                                                          &pg_hint_plan_parse_messages,
374                                                          INFO,
375                                                          parse_messages_level_options,
376                                                          PGC_USERSET,
377                                                          0,
378                                                          NULL,
379                                                          NULL,
380                                                          NULL);
381
382         /* Install hooks. */
383         prev_ProcessUtility = ProcessUtility_hook;
384         ProcessUtility_hook = pg_hint_plan_ProcessUtility;
385         prev_planner = planner_hook;
386         planner_hook = pg_hint_plan_planner;
387         prev_get_relation_info = get_relation_info_hook;
388         get_relation_info_hook = pg_hint_plan_get_relation_info;
389         prev_join_search = join_search_hook;
390         join_search_hook = pg_hint_plan_join_search;
391 }
392
393 /*
394  * Module unload callback
395  * XXX never called
396  */
397 void
398 _PG_fini(void)
399 {
400         /* Uninstall hooks. */
401         ProcessUtility_hook = prev_ProcessUtility;
402         planner_hook = prev_planner;
403         get_relation_info_hook = prev_get_relation_info;
404         join_search_hook = prev_join_search;
405 }
406
407 static void
408 ProcessUtility_hook_error_callback(void *arg)
409 {
410         stmt_name = NULL;
411 }
412
413 void
414 pg_hint_plan_ProcessUtility(Node *parsetree, const char *queryString,
415                                                         ParamListInfo params, bool isTopLevel,
416                                                         DestReceiver *dest, char *completionTag)
417 {
418         Node                               *node;
419         ErrorContextCallback    errcontext;
420
421         if (!pg_hint_plan_enable)
422         {
423                 if (prev_ProcessUtility)
424                         (*prev_ProcessUtility) (parsetree, queryString, params,
425                                                                         isTopLevel, dest, completionTag);
426                 else
427                         standard_ProcessUtility(parsetree, queryString, params,
428                                                                         isTopLevel, dest, completionTag);
429
430                 return;
431         }
432
433         node = parsetree;
434         if (IsA(node , ExplainStmt))
435         {
436                 /*
437                  * EXPLAIN対象のクエリのパースツリーを取得する
438                  */
439                 ExplainStmt        *stmt;
440                 Query              *query;
441
442                 stmt = (ExplainStmt *) node;
443
444                 Assert(IsA(stmt->query, Query));
445                 query = (Query *) stmt->query;
446
447                 if (query->commandType == CMD_UTILITY && query->utilityStmt != NULL)
448                         node = query->utilityStmt;
449         }
450
451         /*
452          * EXECUTEコマンドならば、PREPARE時に指定されたクエリ文字列を取得し、ヒント
453          * 句の候補として設定する
454          */
455         if (IsA(node , ExecuteStmt))
456         {
457                 ExecuteStmt        *stmt;
458
459                 /* Set up callback to statement name reset. */
460                 errcontext.callback = ProcessUtility_hook_error_callback;
461                 errcontext.arg = NULL;
462                 errcontext.previous = error_context_stack;
463                 error_context_stack = &errcontext;
464
465                 stmt = (ExecuteStmt *) node;
466                 stmt_name = stmt->name;
467         }
468
469         if (prev_ProcessUtility)
470                 (*prev_ProcessUtility) (parsetree, queryString, params,
471                                                                 isTopLevel, dest, completionTag);
472         else
473                 standard_ProcessUtility(parsetree, queryString, params,
474                                                                 isTopLevel, dest, completionTag);
475
476         if (stmt_name)
477         {
478                 stmt_name = NULL;
479
480                 /* Remove error callback. */
481                 error_context_stack = errcontext.previous;
482         }
483 }
484
485 static Hint *
486 ScanMethodHintCreate(const char *hint_str, const char *keyword)
487 {
488         ScanMethodHint *hint;
489
490         hint = palloc(sizeof(ScanMethodHint));
491         hint->base.hint_str = hint_str;
492         hint->base.keyword = keyword;
493         hint->base.type = HINT_TYPE_SCAN_METHOD;
494         hint->base.state = HINT_STATE_NOTUSED;
495         hint->base.delete_func = (HintDeleteFunction) ScanMethodHintDelete;
496         hint->base.dump_func = (HintDumpFunction) ScanMethodHintDump;
497         hint->base.cmp_func = (HintCmpFunction) ScanMethodHintCmp;
498         hint->base.parser_func = (HintParseFunction) ScanMethodHintParse;
499         hint->relname = NULL;
500         hint->indexnames = NIL;
501         hint->enforce_mask = 0;
502
503         return (Hint *) hint;
504 }
505
506 static void
507 ScanMethodHintDelete(ScanMethodHint *hint)
508 {
509         if (!hint)
510                 return;
511
512         if (hint->relname)
513                 pfree(hint->relname);
514         list_free_deep(hint->indexnames);
515         pfree(hint);
516 }
517
518 static void
519 dump_quote_value(StringInfo buf, const char *value)
520 {
521         bool            need_quote = false;
522         const char *str;
523
524         for (str = value; *str != '\0'; str++)
525         {
526                 if (isspace(*str) || *str == ')' || *str == '"')
527                 {
528                         need_quote = true;
529                         appendStringInfoCharMacro(buf, '"');
530                         break;
531                 }
532         }
533
534         for (str = value; *str != '\0'; str++)
535         {
536                 if (*str == '"')
537                         appendStringInfoCharMacro(buf, '"');
538
539                 appendStringInfoCharMacro(buf, *str);
540         }
541
542         if (need_quote)
543                 appendStringInfoCharMacro(buf, '"');
544 }
545
546 static void ScanMethodHintDump(ScanMethodHint *hint, StringInfo buf)
547 {
548         ListCell   *l;
549
550         appendStringInfo(buf, "%s(", hint->base.keyword);
551         dump_quote_value(buf, hint->relname);
552         foreach(l, hint->indexnames)
553         {
554                 appendStringInfoCharMacro(buf, ' ');
555                 dump_quote_value(buf, (char *) lfirst(l));
556         }
557         appendStringInfoString(buf, ")\n");
558 }
559
560 static Hint *
561 JoinMethodHintCreate(const char *hint_str, const char *keyword)
562 {
563         JoinMethodHint *hint;
564
565         hint = palloc(sizeof(JoinMethodHint));
566         hint->base.hint_str = hint_str;
567         hint->base.keyword = keyword;
568         hint->base.type = HINT_TYPE_JOIN_METHOD;
569         hint->base.state = HINT_STATE_NOTUSED;
570         hint->base.delete_func = (HintDeleteFunction) JoinMethodHintDelete;
571         hint->base.dump_func = (HintDumpFunction) JoinMethodHintDump;
572         hint->base.cmp_func = (HintCmpFunction) JoinMethodHintCmp;
573         hint->base.parser_func = (HintParseFunction) JoinMethodHintParse;
574         hint->nrels = 0;
575         hint->relnames = NULL;
576         hint->enforce_mask = 0;
577         hint->joinrelids = NULL;
578
579         return (Hint *) hint;
580 }
581
582 static void
583 JoinMethodHintDelete(JoinMethodHint *hint)
584 {
585         if (!hint)
586                 return;
587
588         if (hint->relnames)
589         {
590                 int     i;
591
592                 for (i = 0; i < hint->nrels; i++)
593                         pfree(hint->relnames[i]);
594                 pfree(hint->relnames);
595         }
596         bms_free(hint->joinrelids);
597         pfree(hint);
598 }
599
600 static void JoinMethodHintDump(JoinMethodHint *hint, StringInfo buf)
601 {
602         int     i;
603
604         appendStringInfo(buf, "%s(", hint->base.keyword);
605         dump_quote_value(buf, hint->relnames[0]);
606         for (i = 1; i < hint->nrels; i++)
607         {
608                 appendStringInfoCharMacro(buf, ' ');
609                 dump_quote_value(buf, hint->relnames[i]);
610         }
611         appendStringInfoString(buf, ")\n");
612
613 }
614
615 static Hint *
616 LeadingHintCreate(const char *hint_str, const char *keyword)
617 {
618         LeadingHint        *hint;
619
620         hint = palloc(sizeof(LeadingHint));
621         hint->base.hint_str = hint_str;
622         hint->base.keyword = keyword;
623         hint->base.type = HINT_TYPE_LEADING;
624         hint->base.state = HINT_STATE_NOTUSED;
625         hint->base.delete_func = (HintDeleteFunction)LeadingHintDelete;
626         hint->base.dump_func = (HintDumpFunction) LeadingHintDump;
627         hint->base.cmp_func = (HintCmpFunction) LeadingHintCmp;
628         hint->base.parser_func = (HintParseFunction) LeadingHintParse;
629         hint->relations = NIL;
630
631         return (Hint *) hint;
632 }
633
634 static void
635 LeadingHintDelete(LeadingHint *hint)
636 {
637         if (!hint)
638                 return;
639
640         list_free_deep(hint->relations);
641         pfree(hint);
642 }
643
644 static void LeadingHintDump(LeadingHint *hint, StringInfo buf)
645 {
646         bool            is_first;
647         ListCell   *l;
648
649         appendStringInfo(buf, "%s(", HINT_LEADING);
650         is_first = true;
651         foreach(l, hint->relations)
652         {
653                 if (is_first)
654                         is_first = false;
655                 else
656                         appendStringInfoCharMacro(buf, ' ');
657
658                 dump_quote_value(buf, (char *)lfirst(l));
659         }
660
661         appendStringInfoString(buf, ")\n");
662 }
663
664 static Hint *
665 SetHintCreate(const char *hint_str, const char *keyword)
666 {
667         SetHint    *hint;
668
669         hint = palloc(sizeof(SetHint));
670         hint->base.hint_str = hint_str;
671         hint->base.keyword = keyword;
672         hint->base.type = HINT_TYPE_SET;
673         hint->base.state = HINT_STATE_NOTUSED;
674         hint->base.delete_func = (HintDeleteFunction) SetHintDelete;
675         hint->base.dump_func = (HintDumpFunction) SetHintDump;
676         hint->base.cmp_func = (HintCmpFunction) SetHintCmp;
677         hint->base.parser_func = (HintParseFunction) SetHintParse;
678         hint->name = NULL;
679         hint->value = NULL;
680
681         return (Hint *) hint;
682 }
683
684 static void
685 SetHintDelete(SetHint *hint)
686 {
687         if (!hint)
688                 return;
689
690         if (hint->name)
691                 pfree(hint->name);
692         if (hint->value)
693                 pfree(hint->value);
694         pfree(hint);
695 }
696
697 static void SetHintDump(SetHint *hint, StringInfo buf)
698 {
699         appendStringInfo(buf, "%s(", HINT_SET);
700         dump_quote_value(buf, hint->name);
701         appendStringInfoCharMacro(buf, ' ');
702         dump_quote_value(buf, hint->value);
703         appendStringInfo(buf, ")\n");
704 }
705
706 static PlanHint *
707 PlanHintCreate(void)
708 {
709         PlanHint   *hint;
710
711         hint = palloc(sizeof(PlanHint));
712         hint->hint_str = NULL;
713         hint->nall_hints = 0;
714         hint->max_all_hints = 0;
715         hint->all_hints = NULL;
716         memset(hint->num_hints, 0, sizeof(hint->num_hints));
717         hint->scan_hints = NULL;
718         hint->init_scan_mask = 0;
719         hint->parent_relid = 0;
720         hint->join_hints = NULL;
721         hint->init_join_mask = 0;
722         hint->join_hint_level = NULL;
723         hint->leading_hints = NULL;
724         hint->context = superuser() ? PGC_SUSET : PGC_USERSET;
725         hint->set_hints = NULL;
726
727         return hint;
728 }
729
730 static void
731 PlanHintDelete(PlanHint *hint)
732 {
733         int                     i;
734
735         if (!hint)
736                 return;
737
738         if (hint->hint_str)
739                 pfree(hint->hint_str);
740
741         for (i = 0; i < hint->num_hints[HINT_TYPE_SCAN_METHOD]; i++)
742                 hint->all_hints[i]->delete_func(hint->all_hints[i]);
743         if (hint->all_hints)
744                 pfree(hint->all_hints);
745 }
746
747 static void
748 all_hint_dump(PlanHint *hint, StringInfo buf, const char *title, HintStatus state)
749 {
750         int     i;
751
752         appendStringInfo(buf, "%s:\n", title);
753         for (i = 0; i < hint->nall_hints; i++)
754         {
755                 if (hint->all_hints[i]->state != state)
756                         continue;
757
758                 hint->all_hints[i]->dump_func(hint->all_hints[i], buf);
759         }
760 }
761
762 static void
763 PlanHintDump(PlanHint *hint)
764 {
765         StringInfoData  buf;
766
767         if (!hint)
768         {
769                 elog(LOG, "pg_hint_plan:\nno hint");
770                 return;
771         }
772
773         initStringInfo(&buf);
774
775         appendStringInfoString(&buf, "pg_hint_plan:\n");
776         all_hint_dump(hint, &buf, "used hint", HINT_STATE_USED);
777         all_hint_dump(hint, &buf, "not used hint", HINT_STATE_NOTUSED);
778         all_hint_dump(hint, &buf, "duplication hint", HINT_STATE_DUPLICATION);
779         all_hint_dump(hint, &buf, "error hint", HINT_STATE_ERROR);
780
781         elog(LOG, "%s", buf.data);
782
783         pfree(buf.data);
784 }
785
786 static int
787 RelnameCmp(const void *a, const void *b)
788 {
789         const char *relnamea = *((const char **) a);
790         const char *relnameb = *((const char **) b);
791
792         return strcmp(relnamea, relnameb);
793 }
794
795 static int
796 ScanMethodHintCmp(const ScanMethodHint *a, const ScanMethodHint *b)
797 {
798         return RelnameCmp(&a->relname, &b->relname);
799 }
800
801 static int
802 JoinMethodHintCmp(const JoinMethodHint *a, const JoinMethodHint *b)
803 {
804         int                                             i;
805
806         if (a->nrels != b->nrels)
807                 return a->nrels - b->nrels;
808
809         for (i = 0; i < a->nrels; i++)
810         {
811                 int     result;
812                 if ((result = RelnameCmp(&a->relnames[i], &b->relnames[i])) != 0)
813                         return result;
814         }
815
816         return 0;
817 }
818
819 static int
820 LeadingHintCmp(const LeadingHint *a, const LeadingHint *b)
821 {
822         return 0;
823 }
824
825 static int
826 SetHintCmp(const SetHint *a, const SetHint *b)
827 {
828         return strcmp(a->name, b->name);
829 }
830
831 static int
832 AllHintCmp(const void *a, const void *b, bool order)
833 {
834         const Hint *hinta = *((const Hint **) a);
835         const Hint *hintb = *((const Hint **) b);
836         int                     result = 0;
837
838         if (hinta->type != hintb->type)
839                 return hinta->type - hintb->type;
840
841         if ((result = hinta->cmp_func(hinta, hintb)) != 0 || !order)
842                 return result;
843
844         /* ヒント句で指定した順を返す */
845         return hinta->hint_str - hintb->hint_str;
846 }
847
848 static int
849 AllHintCmpIsOrder(const void *a, const void *b)
850 {
851         return AllHintCmp(a, b, true);
852 }
853
854 #if PG_VERSION_NUM < 90200
855 static int
856 set_config_option_wrapper(const char *name, const char *value,
857                                  GucContext context, GucSource source,
858                                  GucAction action, bool changeVal, int elevel)
859 {
860         int                             result = 0;
861         MemoryContext   ccxt = CurrentMemoryContext;
862
863         PG_TRY();
864         {
865                 result = set_config_option(name, value, context, source,
866                                                                    action, changeVal);
867         }
868         PG_CATCH();
869         {
870                 ErrorData          *errdata;
871                 MemoryContext   ecxt;
872
873                 if (elevel >= ERROR)
874                         PG_RE_THROW();
875
876                 ecxt = MemoryContextSwitchTo(ccxt);
877                 errdata = CopyErrorData();
878                 ereport(elevel, (errcode(errdata->sqlerrcode),
879                                 errmsg("%s", errdata->message),
880                                 errdata->detail ? errdetail("%s", errdata->detail) : 0,
881                                 errdata->hint ? errhint("%s", errdata->hint) : 0));
882                 FreeErrorData(errdata);
883
884                 MemoryContextSwitchTo(ecxt);
885         }
886         PG_END_TRY();
887
888         return result;
889 }
890
891 #define set_config_option(name, value, context, source, \
892                                                   action, changeVal, elevel) \
893         set_config_option_wrapper(name, value, context, source, \
894                                                           action, changeVal, elevel)
895 #endif
896
897 static int
898 set_config_options(SetHint **options, int noptions, GucContext context)
899 {
900         int     i;
901         int     save_nestlevel;
902
903         save_nestlevel = NewGUCNestLevel();
904
905         for (i = 0; i < noptions; i++)
906         {
907                 SetHint    *hint = options[i];
908                 int                     result;
909
910                 if (!hint_state_enabled(hint))
911                         continue;
912
913                 result = set_config_option(hint->name, hint->value, context,
914                                                 PGC_S_SESSION, GUC_ACTION_SAVE, true,
915                                                 pg_hint_plan_parse_messages);
916                 if (result != 0)
917                         hint->base.state = HINT_STATE_USED;
918                 else
919                         hint->base.state = HINT_STATE_ERROR;
920         }
921
922         return save_nestlevel;
923 }
924
925 #define SET_CONFIG_OPTION(name, type_bits) \
926         set_config_option((name), \
927                 (enforce_mask & (type_bits)) ? "true" : "false", \
928                 context, PGC_S_SESSION, GUC_ACTION_SAVE, true, ERROR)
929
930 static void
931 set_scan_config_options(unsigned char enforce_mask, GucContext context)
932 {
933         SET_CONFIG_OPTION("enable_seqscan", ENABLE_SEQSCAN);
934         SET_CONFIG_OPTION("enable_indexscan", ENABLE_INDEXSCAN);
935         SET_CONFIG_OPTION("enable_bitmapscan", ENABLE_BITMAPSCAN);
936         SET_CONFIG_OPTION("enable_tidscan", ENABLE_TIDSCAN);
937 #if PG_VERSION_NUM >= 90200
938         SET_CONFIG_OPTION("enable_indexonlyscan", ENABLE_INDEXSCAN);
939 #endif
940 }
941
942 static void
943 set_join_config_options(unsigned char enforce_mask, GucContext context)
944 {
945         SET_CONFIG_OPTION("enable_nestloop", ENABLE_NESTLOOP);
946         SET_CONFIG_OPTION("enable_mergejoin", ENABLE_MERGEJOIN);
947         SET_CONFIG_OPTION("enable_hashjoin", ENABLE_HASHJOIN);
948 }
949
950 /*
951  * parse functions
952  */
953
954 static const char *
955 parse_keyword(const char *str, StringInfo buf)
956 {
957         skip_space(str);
958
959         while (!isspace(*str) && *str != '(' && *str != '\0')
960                 appendStringInfoCharMacro(buf, *str++);
961
962         return str;
963 }
964
965 static const char *
966 skip_opened_parenthesis(const char *str)
967 {
968         skip_space(str);
969
970         if (*str != '(')
971         {
972                 parse_ereport(str, ("Opened parenthesis is necessary."));
973                 return NULL;
974         }
975
976         str++;
977
978         return str;
979 }
980
981 static const char *
982 skip_closed_parenthesis(const char *str)
983 {
984         skip_space(str);
985
986         if (*str != ')')
987         {
988                 parse_ereport(str, ("Closed parenthesis is necessary."));
989                 return NULL;
990         }
991
992         str++;
993
994         return str;
995 }
996
997 /*
998  * 二重引用符で囲まれているかもしれないトークンを読み取り word 引数に palloc
999  * で確保したバッファに格納してそのポインタを返す。
1000  *
1001  * 正常にパースできた場合は残りの文字列の先頭位置を、異常があった場合は NULL を
1002  * 返す。
1003  */
1004 static const char *
1005 parse_quote_value(const char *str, char **word, char *value_type)
1006 {
1007         StringInfoData  buf;
1008         bool                    in_quote;
1009
1010         /* 先頭のスペースは読み飛ばす。 */
1011         skip_space(str);
1012
1013         initStringInfo(&buf);
1014         if (*str == '"')
1015         {
1016                 str++;
1017                 in_quote = true;
1018         }
1019         else
1020                 in_quote = false;
1021
1022         while (true)
1023         {
1024                 if (in_quote)
1025                 {
1026                         /* 二重引用符が閉じられていない場合はパース中断 */
1027                         if (*str == '\0')
1028                         {
1029                                 pfree(buf.data);
1030                                 parse_ereport(str, ("Unterminated quoted %s.", value_type));
1031                                 return NULL;
1032                         }
1033
1034                         /*
1035                          * エスケープ対象のダブルクウォートをスキップする。
1036                          * もしブロックコメントの開始文字列や終了文字列もオブジェクト名とし
1037                          * て使用したい場合は、/ と * もエスケープ対象とすることで使用できる
1038                          * が、処理対象としていない。もしテーブル名にこれらの文字が含まれる
1039                          * 場合は、エイリアスを指定する必要がある。
1040                          */
1041                         if(*str == '"')
1042                         {
1043                                 str++;
1044                                 if (*str != '"')
1045                                         break;
1046                         }
1047                 }
1048                 else
1049                         if (isspace(*str) || *str == ')' || *str == '"' || *str == '\0')
1050                                 break;
1051
1052                 appendStringInfoCharMacro(&buf, *str++);
1053         }
1054
1055         if (buf.len == 0)
1056         {
1057                 pfree(buf.data);
1058                 parse_ereport(str, ("%s is necessary.", value_type));
1059                 return NULL;
1060         }
1061
1062         *word = buf.data;
1063
1064         return str;
1065 }
1066
1067 static void
1068 parse_hints(PlanHint *plan, Query *parse, const char *str)
1069 {
1070         StringInfoData  buf;
1071         char               *head;
1072
1073         initStringInfo(&buf);
1074         while (*str != '\0')
1075         {
1076                 const HintParser *parser;
1077
1078                 /* in error message, we output the comment including the keyword. */
1079                 head = (char *) str;
1080
1081                 /* parse only the keyword of the hint. */
1082                 resetStringInfo(&buf);
1083                 str = parse_keyword(str, &buf);
1084
1085                 for (parser = parsers; parser->keyword != NULL; parser++)
1086                 {
1087                         char   *keyword = parser->keyword;
1088                         Hint   *hint;
1089
1090                         if (strcasecmp(buf.data, keyword) != 0)
1091                                 continue;
1092
1093                         hint = parser->create_func(head, keyword);
1094
1095                         /* parser of each hint does parse in a parenthesis. */
1096                         if ((str = skip_opened_parenthesis(str)) == NULL ||
1097                                 (str = hint->parser_func(hint, plan, parse, str)) == NULL ||
1098                                 (str = skip_closed_parenthesis(str)) == NULL)
1099                         {
1100                                 hint->delete_func(hint);
1101                                 pfree(buf.data);
1102                                 return;
1103                         }
1104
1105                         /*
1106                          * 出来上がったヒント情報を追加。スロットが足りない場合は二倍に拡張する。
1107                          */
1108                         if (plan->nall_hints == 0)
1109                         {
1110                                 plan->max_all_hints = HINT_ARRAY_DEFAULT_INITSIZE;
1111                                 plan->all_hints = palloc(sizeof(Hint *) * plan->max_all_hints);
1112                         }
1113                         else if (plan->nall_hints == plan->max_all_hints)
1114                         {
1115                                 plan->max_all_hints *= 2;
1116                                 plan->all_hints = repalloc(plan->all_hints,
1117                                                                                 sizeof(Hint *) * plan->max_all_hints);
1118                         }
1119
1120                         plan->all_hints[plan->nall_hints] = hint;
1121                         plan->nall_hints++;
1122
1123                         skip_space(str);
1124
1125                         break;
1126                 }
1127
1128                 if (parser->keyword == NULL)
1129                 {
1130                         parse_ereport(head, ("Keyword \"%s\" does not exist.", buf.data));
1131                         pfree(buf.data);
1132                         return;
1133                 }
1134         }
1135
1136         pfree(buf.data);
1137 }
1138
1139 /*
1140  * Do basic parsing of the query head comment.
1141  */
1142 static PlanHint *
1143 parse_head_comment(Query *parse)
1144 {
1145         const char         *p;
1146         char               *head;
1147         char               *tail;
1148         int                             len;
1149         int                             i;
1150         PlanHint           *plan;
1151
1152         /* get client-supplied query string. */
1153         if (stmt_name)
1154         {
1155                 PreparedStatement  *entry;
1156
1157                 entry = FetchPreparedStatement(stmt_name, true);
1158                 p = entry->plansource->query_string;
1159         }
1160         else
1161                 p = debug_query_string;
1162
1163         if (p == NULL)
1164                 return NULL;
1165
1166         /* extract query head comment. */
1167         len = strlen(HINT_START);
1168         skip_space(p);
1169         if (strncmp(p, HINT_START, len))
1170                 return NULL;
1171
1172         head = (char *) p;
1173         p += len;
1174         skip_space(p);
1175
1176         /* find hint end keyword. */
1177         if ((tail = strstr(p, HINT_END)) == NULL)
1178         {
1179                 parse_ereport(head, ("Unterminated block comment."));
1180                 return NULL;
1181         }
1182
1183         /* 入れ子にしたブロックコメントはサポートしない */
1184         if ((head = strstr(p, BLOCK_COMMENT_START)) != NULL && head < tail)
1185                 parse_ereport(head, ("Block comments nest doesn't supported."));
1186
1187         /* ヒント句部分を切り出す */
1188         len = tail - p;
1189         head = palloc(len + 1);
1190         memcpy(head, p, len);
1191         head[len] = '\0';
1192         p = head;
1193
1194         plan = PlanHintCreate();
1195         plan->hint_str = head;
1196
1197         /* parse each hint. */
1198         parse_hints(plan, parse, p);
1199
1200         /* When nothing specified a hint, we free PlanHint and returns NULL. */
1201         if (plan->nall_hints == 0)
1202         {
1203                 PlanHintDelete(plan);
1204                 return NULL;
1205         }
1206
1207         /* パースしたヒントを並び替える */
1208         qsort(plan->all_hints, plan->nall_hints, sizeof(Hint *), AllHintCmpIsOrder);
1209
1210         /* 重複したヒントを検索する */
1211         for (i = 0; i < plan->nall_hints; i++)
1212         {
1213                 Hint   *hint = plan->all_hints[i];
1214
1215                 plan->num_hints[hint->type]++;
1216
1217                 if (i + 1 >= plan->nall_hints)
1218                         break;
1219
1220                 if (AllHintCmp(plan->all_hints + i, plan->all_hints + i + 1, false) == 0)
1221                 {
1222                         const char *HintTypeName[] = {"scan method", "join method",
1223                                                                                   "leading", "set"};
1224
1225                         parse_ereport(plan->all_hints[i]->hint_str,
1226                                 ("Conflict %s hint.", HintTypeName[hint->type]));
1227                         plan->all_hints[i]->state = HINT_STATE_DUPLICATION;
1228                 }
1229         }
1230
1231         plan->scan_hints = (ScanMethodHint **) plan->all_hints;
1232         plan->join_hints = (JoinMethodHint **) plan->all_hints +
1233                                                         plan->num_hints[HINT_TYPE_SCAN_METHOD];
1234         plan->leading_hints = (LeadingHint **) plan->all_hints +
1235                                                         plan->num_hints[HINT_TYPE_SCAN_METHOD] +
1236                                                         plan->num_hints[HINT_TYPE_JOIN_METHOD];
1237         plan->set_hints = (SetHint **) plan->all_hints +
1238                                                         plan->num_hints[HINT_TYPE_SCAN_METHOD] +
1239                                                         plan->num_hints[HINT_TYPE_JOIN_METHOD] +
1240                                                         plan->num_hints[HINT_TYPE_LEADING];
1241
1242         return plan;
1243 }
1244
1245 /*
1246  * スキャン方式ヒントのカッコ内をパースする
1247  */
1248 static const char *
1249 ScanMethodHintParse(ScanMethodHint *hint, PlanHint *plan, Query *parse, const char *str)
1250 {
1251         const char         *keyword = hint->base.keyword;
1252
1253         /*
1254          * スキャン方式のヒントでリレーション名が読み取れない場合はヒント無効
1255          */
1256         if ((str = parse_quote_value(str, &hint->relname, "ralation name")) == NULL)
1257                 return NULL;
1258
1259         skip_space(str);
1260
1261         /*
1262          * インデックスリストを受け付けるヒントであれば、インデックス参照をパース
1263          * する。
1264          */
1265         if (strcmp(keyword, HINT_INDEXSCAN) == 0 ||
1266 #if PG_VERSION_NUM >= 90200
1267                 strcmp(keyword, HINT_INDEXONLYSCAN) == 0 ||
1268 #endif
1269                 strcmp(keyword, HINT_BITMAPSCAN) == 0)
1270         {
1271                 while (*str != ')' && *str != '\0')
1272                 {
1273                         char       *indexname;
1274
1275                         str = parse_quote_value(str, &indexname, "index name");
1276                         if (str == NULL)
1277                                 return NULL;
1278
1279                         hint->indexnames = lappend(hint->indexnames, indexname);
1280                         skip_space(str);
1281                 }
1282         }
1283
1284         /*
1285          * ヒントごとに決まっている許容スキャン方式をビットマスクとして設定
1286          */
1287         if (strcasecmp(keyword, HINT_SEQSCAN) == 0)
1288                 hint->enforce_mask = ENABLE_SEQSCAN;
1289         else if (strcasecmp(keyword, HINT_INDEXSCAN) == 0)
1290                 hint->enforce_mask = ENABLE_INDEXSCAN;
1291         else if (strcasecmp(keyword, HINT_BITMAPSCAN) == 0)
1292                 hint->enforce_mask = ENABLE_BITMAPSCAN;
1293         else if (strcasecmp(keyword, HINT_TIDSCAN) == 0)
1294                 hint->enforce_mask = ENABLE_TIDSCAN;
1295         else if (strcasecmp(keyword, HINT_NOSEQSCAN) == 0)
1296                 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_SEQSCAN;
1297         else if (strcasecmp(keyword, HINT_NOINDEXSCAN) == 0)
1298                 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_INDEXSCAN;
1299         else if (strcasecmp(keyword, HINT_NOBITMAPSCAN) == 0)
1300                 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_BITMAPSCAN;
1301         else if (strcasecmp(keyword, HINT_NOTIDSCAN) == 0)
1302                 hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_TIDSCAN;
1303         else
1304         {
1305                 parse_ereport(str, ("Unrecognized hint keyword \"%s\".", keyword));
1306                 return NULL;
1307         }
1308
1309         return str;
1310 }
1311
1312 static const char *
1313 JoinMethodHintParse(JoinMethodHint *hint, PlanHint *plan, Query *parse, const char *str)
1314 {
1315         char               *relname;
1316         const char         *keyword = hint->base.keyword;
1317
1318         skip_space(str);
1319
1320         hint->relnames = palloc(sizeof(char *));
1321
1322         while ((str = parse_quote_value(str, &relname, "table name")) != NULL)
1323         {
1324                 hint->nrels++;
1325                 hint->relnames = repalloc(hint->relnames, sizeof(char *) * hint->nrels);
1326                 hint->relnames[hint->nrels - 1] = relname;
1327
1328                 skip_space(str);
1329                 if (*str == ')')
1330                         break;
1331         }
1332
1333         if (str == NULL)
1334                 return NULL;
1335
1336         /* Join 対象のテーブルは最低でも2つ指定する必要がある */
1337         if (hint->nrels < 2)
1338         {
1339                 parse_ereport(str, ("Specified relation more than two."));
1340                 hint->base.state = HINT_STATE_ERROR;
1341         }
1342
1343         /* テーブル名順にソートする */
1344         qsort(hint->relnames, hint->nrels, sizeof(char *), RelnameCmp);
1345
1346         if (strcasecmp(keyword, HINT_NESTLOOP) == 0)
1347                 hint->enforce_mask = ENABLE_NESTLOOP;
1348         else if (strcasecmp(keyword, HINT_MERGEJOIN) == 0)
1349                 hint->enforce_mask = ENABLE_MERGEJOIN;
1350         else if (strcasecmp(keyword, HINT_HASHJOIN) == 0)
1351                 hint->enforce_mask = ENABLE_HASHJOIN;
1352         else if (strcasecmp(keyword, HINT_NONESTLOOP) == 0)
1353                 hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_NESTLOOP;
1354         else if (strcasecmp(keyword, HINT_NOMERGEJOIN) == 0)
1355                 hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_MERGEJOIN;
1356         else if (strcasecmp(keyword, HINT_NOHASHJOIN) == 0)
1357                 hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_HASHJOIN;
1358         else
1359         {
1360                 parse_ereport(str, ("Unrecognized hint keyword \"%s\".", keyword));
1361                 return NULL;
1362         }
1363
1364         return str;
1365 }
1366
1367 static const char *
1368 LeadingHintParse(LeadingHint *hint, PlanHint *plan, Query *parse, const char *str)
1369 {
1370         skip_space(str);
1371
1372         while (*str != ')')
1373         {
1374                 char   *relname;
1375
1376                 if ((str = parse_quote_value(str, &relname, "relation name")) == NULL)
1377                         return NULL;
1378
1379                 hint->relations = lappend(hint->relations, relname);
1380
1381                 skip_space(str);
1382         }
1383
1384         /* テーブル指定が2つ未満の場合は、Leading ヒントはエラーとする */
1385         if (list_length(hint->relations) < 2)
1386         {
1387                 parse_ereport(hint->base.hint_str,
1388                         ("In %s hint, specified relation name 2 or more.", HINT_LEADING));
1389                 hint->base.state = HINT_STATE_ERROR;
1390         }
1391
1392         return str;
1393 }
1394
1395 static const char *
1396 SetHintParse(SetHint *hint, PlanHint *plan, Query *parse, const char *str)
1397 {
1398         if ((str = parse_quote_value(str, &hint->name, "parameter name")) == NULL ||
1399                 (str = parse_quote_value(str, &hint->value, "parameter value")) == NULL)
1400                 return NULL;
1401
1402         return str;
1403 }
1404
1405 static PlannedStmt *
1406 pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
1407 {
1408         int                             save_nestlevel;
1409         PlannedStmt        *result;
1410
1411         /*
1412          * hintが指定されない、または空のhintを指定された場合は通常のparser処理をお
1413          * こなう。
1414          * 他のフック関数で実行されるhint処理をスキップするために、global 変数をNULL
1415          * に設定しておく。
1416          */
1417         if (!pg_hint_plan_enable || (global = parse_head_comment(parse)) == NULL)
1418         {
1419                 global = NULL;
1420
1421                 if (prev_planner)
1422                         return (*prev_planner) (parse, cursorOptions, boundParams);
1423                 else
1424                         return standard_planner(parse, cursorOptions, boundParams);
1425         }
1426
1427         /* Set hint で指定されたGUCパラメータを設定する */
1428         save_nestlevel = set_config_options(global->set_hints, global->num_hints[HINT_TYPE_SET],
1429                                                 global->context);
1430
1431         if (enable_seqscan)
1432                 global->init_scan_mask |= ENABLE_SEQSCAN;
1433         if (enable_indexscan)
1434                 global->init_scan_mask |= ENABLE_INDEXSCAN;
1435         if (enable_bitmapscan)
1436                 global->init_scan_mask |= ENABLE_BITMAPSCAN;
1437         if (enable_tidscan)
1438                 global->init_scan_mask |= ENABLE_TIDSCAN;
1439 #if PG_VERSION_NUM >= 90200
1440         if (enable_indexonlyscan)
1441                 global->init_scan_mask |= ENABLE_INDEXONLYSCAN;
1442 #endif
1443         if (enable_nestloop)
1444                 global->init_join_mask |= ENABLE_NESTLOOP;
1445         if (enable_mergejoin)
1446                 global->init_join_mask |= ENABLE_MERGEJOIN;
1447         if (enable_hashjoin)
1448                 global->init_join_mask |= ENABLE_HASHJOIN;
1449
1450         if (prev_planner)
1451                 result = (*prev_planner) (parse, cursorOptions, boundParams);
1452         else
1453                 result = standard_planner(parse, cursorOptions, boundParams);
1454
1455         /*
1456          * Restore the GUC variables we set above.
1457          */
1458         AtEOXact_GUC(true, save_nestlevel);
1459
1460         /*
1461          * Print hint if debugging.
1462          */
1463         if (pg_hint_plan_debug_print)
1464                 PlanHintDump(global);
1465
1466         PlanHintDelete(global);
1467         global = NULL;
1468
1469         return result;
1470 }
1471
1472 /*
1473  * aliasnameと一致するSCANヒントを探す
1474  */
1475 static ScanMethodHint *
1476 find_scan_hint(PlannerInfo *root, RelOptInfo *rel)
1477 {
1478         RangeTblEntry  *rte;
1479         int     i;
1480
1481         /*
1482          * RELOPT_BASEREL でなければ、scan method ヒントが適用しない。
1483          * 子テーブルの場合はRELOPT_OTHER_MEMBER_RELとなるが、サポート対象外とする。
1484          * また、通常のリレーション以外は、スキャン方式を選択できない。
1485          */
1486         if (rel->reloptkind != RELOPT_BASEREL || rel->rtekind != RTE_RELATION)
1487                 return NULL;
1488
1489         rte = root->simple_rte_array[rel->relid];
1490
1491         /* 外部表はスキャン方式が選択できない。 */
1492         if (rte->relkind == RELKIND_FOREIGN_TABLE)
1493                 return NULL;
1494
1495         /*
1496          * スキャン方式のヒントのリストから、検索対象のリレーションと名称が一致する
1497          * ヒントを検索する。
1498          */
1499         for (i = 0; i < global->num_hints[HINT_TYPE_SCAN_METHOD]; i++)
1500         {
1501                 ScanMethodHint *hint = global->scan_hints[i];
1502
1503                 /* すでに無効となっているヒントは検索対象にしない。 */
1504                 if (!hint_state_enabled(hint))
1505                         continue;
1506
1507                 if (RelnameCmp(&rte->eref->aliasname, &hint->relname) == 0)
1508                         return hint;
1509         }
1510
1511         return NULL;
1512 }
1513
1514 static void
1515 pg_hint_plan_get_relation_info(PlannerInfo *root, Oid relationObjectId,
1516                                                                  bool inhparent, RelOptInfo *rel)
1517 {
1518         ScanMethodHint *hint;
1519         ListCell           *cell;
1520         ListCell           *prev;
1521         ListCell           *next;
1522
1523         if (prev_get_relation_info)
1524                 (*prev_get_relation_info) (root, relationObjectId, inhparent, rel);
1525
1526         /* 有効なヒントが指定されなかった場合は処理をスキップする。 */
1527         if (!global)
1528                 return;
1529
1530         if (inhparent)
1531         {
1532                 /* cache does relids of parent table */
1533                 global->parent_relid = rel->relid;
1534         }
1535         else if (global->parent_relid != 0)
1536         {
1537                 /* If this rel is an appendrel child, */
1538                 ListCell   *l;
1539
1540                 /* append_rel_list contains all append rels; ignore others */
1541                 foreach(l, root->append_rel_list)
1542                 {
1543                         AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
1544
1545                         /* This rel is child table. */
1546                         if (appinfo->parent_relid == global->parent_relid)
1547                                 return;
1548                 }
1549
1550                 /* This rel is not inherit table. */
1551                 global->parent_relid = 0;
1552         }
1553
1554         /* scan hint が指定されない場合は、GUCパラメータをリセットする。 */
1555         if ((hint = find_scan_hint(root, rel)) == NULL)
1556         {
1557                 set_scan_config_options(global->init_scan_mask, global->context);
1558                 return;
1559         }
1560         set_scan_config_options(hint->enforce_mask, global->context);
1561         hint->base.state = HINT_STATE_USED;
1562
1563         /* インデックスを全て削除し、スキャンに使えなくする */
1564         if (hint->enforce_mask == ENABLE_SEQSCAN ||
1565                 hint->enforce_mask == ENABLE_TIDSCAN)
1566         {
1567                 list_free_deep(rel->indexlist);
1568                 rel->indexlist = NIL;
1569                 hint->base.state = HINT_STATE_USED;
1570
1571                 return;
1572         }
1573
1574         /* 後でパスを作り直すため、ここではなにもしない */
1575         if (hint->indexnames == NULL)
1576                 return;
1577
1578         /* 指定されたインデックスのみをのこす */
1579         prev = NULL;
1580         for (cell = list_head(rel->indexlist); cell; cell = next)
1581         {
1582                 IndexOptInfo   *info = (IndexOptInfo *) lfirst(cell);
1583                 char               *indexname = get_rel_name(info->indexoid);
1584                 ListCell           *l;
1585                 bool                    use_index = false;
1586
1587                 next = lnext(cell);
1588
1589                 foreach(l, hint->indexnames)
1590                 {
1591                         if (RelnameCmp(&indexname, &lfirst(l)) == 0)
1592                         {
1593                                 use_index = true;
1594                                 break;
1595                         }
1596                 }
1597
1598                 if (!use_index)
1599                         rel->indexlist = list_delete_cell(rel->indexlist, cell, prev);
1600                 else
1601                         prev = cell;
1602
1603                 pfree(indexname);
1604         }
1605 }
1606
1607 /*
1608  * aliasnameがクエリ中に指定した別名と一致する場合は、そのインデックスを返し、一致
1609  * する別名がなければ0を返す。
1610  * aliasnameがクエリ中に複数回指定された場合は、-1を返す。
1611  */
1612 static int
1613 find_relid_aliasname(PlannerInfo *root, char *aliasname, List *initial_rels,
1614                 const char *str)
1615 {
1616         int     i;
1617         int     found = 0;
1618
1619         for (i = 1; i < root->simple_rel_array_size; i++)
1620         {
1621                 ListCell   *l;
1622
1623                 if (root->simple_rel_array[i] == NULL)
1624                         continue;
1625
1626                 Assert(i == root->simple_rel_array[i]->relid);
1627
1628                 if (RelnameCmp(&aliasname, &root->simple_rte_array[i]->eref->aliasname)
1629                                 != 0)
1630                         continue;
1631
1632                 foreach(l, initial_rels)
1633                 {
1634                         RelOptInfo *rel = (RelOptInfo *) lfirst(l);
1635
1636                         if (rel->reloptkind == RELOPT_BASEREL)
1637                         {
1638                                 if (rel->relid != i)
1639                                         continue;
1640                         }
1641                         else
1642                         {
1643                                 Assert(rel->reloptkind == RELOPT_JOINREL);
1644
1645                                 if (!bms_is_member(i, rel->relids))
1646                                         continue;
1647                         }
1648
1649                         if (found != 0)
1650                         {
1651                                 parse_ereport(str, ("Relation name \"%s\" is ambiguous.", aliasname));
1652                                 return -1;
1653                         }
1654
1655                         found = i;
1656                         break;
1657                 }
1658
1659         }
1660
1661         return found;
1662 }
1663
1664 /*
1665  * relidビットマスクと一致するヒントを探す
1666  */
1667 static JoinMethodHint *
1668 find_join_hint(Relids joinrelids)
1669 {
1670         List       *join_hint;
1671         ListCell   *l;
1672
1673         join_hint = global->join_hint_level[bms_num_members(joinrelids)];
1674         foreach(l, join_hint)
1675         {
1676                 JoinMethodHint *hint = (JoinMethodHint *) lfirst(l);
1677                 if (bms_equal(joinrelids, hint->joinrelids))
1678                         return hint;
1679         }
1680
1681         return NULL;
1682 }
1683
1684 /*
1685  * 結合方式のヒントを使用しやすい構造に変換する。
1686  */
1687 static void
1688 transform_join_hints(PlanHint *plan, PlannerInfo *root, int nbaserel,
1689                 List *initial_rels, JoinMethodHint **join_method_hints)
1690 {
1691         int                             i;
1692         int                             relid;
1693         LeadingHint        *lhint;
1694         Relids                  joinrelids;
1695         int                             njoinrels;
1696         ListCell           *l;
1697
1698         for (i = 0; i < plan->num_hints[HINT_TYPE_JOIN_METHOD]; i++)
1699         {
1700                 JoinMethodHint *hint = plan->join_hints[i];
1701                 int                             j;
1702
1703                 if (!hint_state_enabled(hint) || hint->nrels > nbaserel)
1704                         continue;
1705
1706                 bms_free(hint->joinrelids);
1707                 hint->joinrelids = NULL;
1708                 relid = 0;
1709                 for (j = 0; j < hint->nrels; j++)
1710                 {
1711                         char   *relname = hint->relnames[j];
1712
1713                         relid = find_relid_aliasname(root, relname, initial_rels,
1714                                                 hint->base.hint_str);
1715
1716                         if (relid == -1)
1717                                 hint->base.state = HINT_STATE_ERROR;
1718
1719                         if (relid <= 0)
1720                                 break;
1721
1722                         if (bms_is_member(relid, hint->joinrelids))
1723                         {
1724                                 parse_ereport(hint->base.hint_str,
1725                                         ("Relation name \"%s\" is duplicate.", relname));
1726                                 hint->base.state = HINT_STATE_ERROR;
1727                                 break;
1728                         }
1729
1730                         hint->joinrelids = bms_add_member(hint->joinrelids, relid);
1731                 }
1732
1733                 if (relid <= 0 || hint->base.state == HINT_STATE_ERROR)
1734                         continue;
1735
1736                 plan->join_hint_level[hint->nrels] =
1737                         lappend(plan->join_hint_level[hint->nrels], hint);
1738         }
1739
1740         /*
1741          * 有効なLeading ヒントが指定されている場合は、結合順にあわせてjoin method hint
1742          * のフォーマットに変換する。
1743          */
1744         if (plan->num_hints[HINT_TYPE_LEADING] == 0)
1745                 return;
1746
1747         lhint = plan->leading_hints[plan->num_hints[HINT_TYPE_LEADING] - 1];
1748         if (!hint_state_enabled(lhint))
1749                 return;
1750
1751         /* Leading hint は、全ての join 方式が有効な hint として登録する */
1752         joinrelids = NULL;
1753         njoinrels = 0;
1754         foreach(l, lhint->relations)
1755         {
1756                 char               *relname = (char *)lfirst(l);
1757                 JoinMethodHint *hint;
1758
1759                 relid = find_relid_aliasname(root, relname, initial_rels, plan->hint_str);
1760
1761                 if (relid == -1)
1762                 {
1763                         bms_free(joinrelids);
1764                         return;
1765                 }
1766
1767                 if (relid == 0)
1768                         continue;
1769
1770                 if (bms_is_member(relid, joinrelids))
1771                 {
1772                         parse_ereport(lhint->base.hint_str,
1773                                 ("Relation name \"%s\" is duplicate.", relname));
1774                         lhint->base.state = HINT_STATE_ERROR;
1775                         bms_free(joinrelids);
1776                         return;
1777                 }
1778
1779                 joinrelids = bms_add_member(joinrelids, relid);
1780                 njoinrels++;
1781
1782                 if (njoinrels < 2)
1783                         continue;
1784
1785                 hint = find_join_hint(joinrelids);
1786                 if (hint == NULL)
1787                 {
1788                         /*
1789                          * Here relnames is not set, since Relids bitmap is sufficient to
1790                          * control paths of this query afterwards.
1791                          */
1792                         hint = (JoinMethodHint *) JoinMethodHintCreate(lhint->base.hint_str,
1793                                                 HINT_LEADING);
1794                         hint->base.state = HINT_STATE_USED;
1795                         hint->nrels = njoinrels;
1796                         hint->enforce_mask = ENABLE_ALL_JOIN;
1797                         hint->joinrelids = bms_copy(joinrelids);
1798                 }
1799
1800                 join_method_hints[njoinrels] = hint;
1801
1802                 if (njoinrels >= nbaserel)
1803                         break;
1804         }
1805
1806         bms_free(joinrelids);
1807
1808         if (njoinrels < 2)
1809                 return;
1810
1811         for (i = 2; i <= njoinrels; i++)
1812         {
1813                 /* Leading で指定した組み合わせ以外の join hint を削除する */
1814                 list_free(plan->join_hint_level[i]);
1815
1816                 plan->join_hint_level[i] =
1817                         lappend(NIL, join_method_hints[i]);
1818         }
1819
1820         if (hint_state_enabled(lhint))
1821                 set_join_config_options(DISABLE_ALL_JOIN, global->context);
1822
1823         lhint->base.state = HINT_STATE_USED;
1824
1825 }
1826
1827 static void
1828 rebuild_scan_path(PlanHint *plan, PlannerInfo *root, int level, List *initial_rels)
1829 {
1830         ListCell   *l;
1831
1832         foreach(l, initial_rels)
1833         {
1834                 RelOptInfo         *rel = (RelOptInfo *) lfirst(l);
1835                 RangeTblEntry  *rte;
1836                 ScanMethodHint *hint;
1837
1838                 /*
1839                  * スキャン方式が選択できるリレーションのみ、スキャンパスを再生成
1840                  * する。
1841                  */
1842                 if (rel->reloptkind != RELOPT_BASEREL || rel->rtekind != RTE_RELATION)
1843                         continue;
1844
1845                 rte = root->simple_rte_array[rel->relid];
1846
1847                 /* 外部表はスキャン方式が選択できない。 */
1848                 if (rte->relkind == RELKIND_FOREIGN_TABLE)
1849                         continue;
1850
1851                 /*
1852                  * scan method hint が指定されていなければ、初期値のGUCパラメータでscan
1853                  * path を再生成する。
1854                  */
1855                 if ((hint = find_scan_hint(root, rel)) == NULL)
1856                         set_scan_config_options(plan->init_scan_mask, plan->context);
1857                 else
1858                 {
1859                         set_scan_config_options(hint->enforce_mask, plan->context);
1860                         hint->base.state = HINT_STATE_USED;
1861                 }
1862
1863                 list_free_deep(rel->pathlist);
1864                 rel->pathlist = NIL;
1865                 if (rte->inh)
1866                 {
1867                         /* It's an "append relation", process accordingly */
1868                         set_append_rel_pathlist(root, rel, rel->relid, rte);
1869                 }
1870                 else
1871                 {
1872                         set_plain_rel_pathlist(root, rel, rte);
1873                 }
1874         }
1875
1876         /*
1877          * Restore the GUC variables we set above.
1878          */
1879         set_scan_config_options(plan->init_scan_mask, plan->context);
1880 }
1881
1882 /*
1883  * make_join_rel() をラップする関数
1884  *
1885  * ヒントにしたがって、enabele_* パラメータを変更した上で、make_join_rel()を
1886  * 呼び出す。
1887  */
1888 static RelOptInfo *
1889 make_join_rel_wrapper(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
1890 {
1891         Relids                  joinrelids;
1892         JoinMethodHint *hint;
1893         RelOptInfo         *rel;
1894         int                             save_nestlevel;
1895
1896         joinrelids = bms_union(rel1->relids, rel2->relids);
1897         hint = find_join_hint(joinrelids);
1898         bms_free(joinrelids);
1899
1900         if (!hint)
1901                 return pg_hint_plan_make_join_rel(root, rel1, rel2);
1902
1903         save_nestlevel = NewGUCNestLevel();
1904
1905         set_join_config_options(hint->enforce_mask, global->context);
1906
1907         rel = pg_hint_plan_make_join_rel(root, rel1, rel2);
1908         hint->base.state = HINT_STATE_USED;
1909
1910         /*
1911          * Restore the GUC variables we set above.
1912          */
1913         AtEOXact_GUC(true, save_nestlevel);
1914
1915         return rel;
1916 }
1917
1918 static int
1919 get_num_baserels(List *initial_rels)
1920 {
1921         int                     nbaserel = 0;
1922         ListCell   *l;
1923
1924         foreach(l, initial_rels)
1925         {
1926                 RelOptInfo *rel = (RelOptInfo *) lfirst(l);
1927
1928                 if (rel->reloptkind == RELOPT_BASEREL)
1929                         nbaserel++;
1930                 else if (rel->reloptkind ==RELOPT_JOINREL)
1931                         nbaserel+= bms_num_members(rel->relids);
1932                 else
1933                 {
1934                         /* other values not expected here */
1935                         elog(ERROR, "unrecognized reloptkind type: %d", rel->reloptkind);
1936                 }
1937         }
1938
1939         return nbaserel;
1940 }
1941
1942 static RelOptInfo *
1943 pg_hint_plan_join_search(PlannerInfo *root, int levels_needed, List *initial_rels)
1944 {
1945         JoinMethodHint **join_method_hints;
1946         int                             nbaserel;
1947         RelOptInfo         *rel;
1948         int                             i;
1949
1950         /*
1951          * pg_hint_planが無効、または有効なヒントが1つも指定されなかった場合は、標準
1952          * の処理を行う。
1953          */
1954         if (!global)
1955         {
1956                 if (prev_join_search)
1957                         return (*prev_join_search) (root, levels_needed, initial_rels);
1958                 else if (enable_geqo && levels_needed >= geqo_threshold)
1959                         return geqo(root, levels_needed, initial_rels);
1960                 else
1961                         return standard_join_search(root, levels_needed, initial_rels);
1962         }
1963
1964         /* We apply scan method hint rebuild scan path. */
1965         rebuild_scan_path(global, root, levels_needed, initial_rels);
1966
1967         /*
1968          * GEQOを使用する条件を満たした場合は、GEQOを用いた結合方式の検索を行う。
1969          * このとき、スキャン方式のヒントとSetヒントのみが有効になり、結合方式や結合
1970          * 順序はヒント句は無効になりGEQOのアルゴリズムで決定される。
1971          */
1972         if (enable_geqo && levels_needed >= geqo_threshold)
1973                 return geqo(root, levels_needed, initial_rels);
1974
1975         nbaserel = get_num_baserels(initial_rels);
1976         global->join_hint_level = palloc0(sizeof(List *) * (nbaserel + 1));
1977         join_method_hints = palloc0(sizeof(JoinMethodHint *) * (nbaserel + 1));
1978
1979         transform_join_hints(global, root, nbaserel, initial_rels, join_method_hints);
1980
1981         rel = pg_hint_plan_standard_join_search(root, levels_needed, initial_rels);
1982
1983         for (i = 2; i <= nbaserel; i++)
1984         {
1985                 list_free(global->join_hint_level[i]);
1986
1987                 /* free Leading hint only */
1988                 if (join_method_hints[i] != NULL &&
1989                         join_method_hints[i]->enforce_mask == ENABLE_ALL_JOIN)
1990                         JoinMethodHintDelete(join_method_hints[i]);
1991         }
1992         pfree(global->join_hint_level);
1993         pfree(join_method_hints);
1994
1995         if (global->num_hints[HINT_TYPE_LEADING] > 0 &&
1996                 hint_state_enabled(global->leading_hints[global->num_hints[HINT_TYPE_LEADING] - 1]))
1997                 set_join_config_options(global->init_join_mask, global->context);
1998
1999         return rel;
2000 }
2001
2002 /*
2003  * set_rel_pathlist
2004  *        Build access paths for a base relation
2005  */
2006 static void
2007 set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
2008                                  Index rti, RangeTblEntry *rte)
2009 {
2010         if (rte->inh)
2011         {
2012                 /* It's an "append relation", process accordingly */
2013                 set_append_rel_pathlist(root, rel, rti, rte);
2014         }
2015         else
2016         {
2017                 if (rel->rtekind == RTE_RELATION)
2018                 {
2019                         if (rte->relkind == RELKIND_RELATION)
2020                         {
2021                                 /* Plain relation */
2022                                 set_plain_rel_pathlist(root, rel, rte);
2023                         }
2024                         else
2025                                 elog(ERROR, "unexpected relkind: %c", rte->relkind);
2026                 }
2027                 else
2028                         elog(ERROR, "unexpected rtekind: %d", (int) rel->rtekind);
2029         }
2030 }
2031
2032 #define standard_join_search pg_hint_plan_standard_join_search
2033 #define join_search_one_level pg_hint_plan_join_search_one_level
2034 #define make_join_rel make_join_rel_wrapper
2035 #include "core.c"
2036
2037 #undef make_join_rel
2038 #define make_join_rel pg_hint_plan_make_join_rel
2039 #define add_paths_to_joinrel(root, joinrel, outerrel, innerrel, jointype, sjinfo, restrictlist) \
2040 do { \
2041         ScanMethodHint *hint = NULL; \
2042         if ((hint = find_scan_hint((root), (innerrel))) != NULL) \
2043         { \
2044                 set_scan_config_options(hint->enforce_mask, global->context); \
2045                 hint->base.state = HINT_STATE_USED; \
2046         } \
2047         add_paths_to_joinrel((root), (joinrel), (outerrel), (innerrel), (jointype), (sjinfo), (restrictlist)); \
2048         if (hint != NULL) \
2049                 set_scan_config_options(global->init_scan_mask, global->context); \
2050 } while(0)
2051 #include "make_join_rel.c"