OSDN Git Service

Get rid of code for 9.2 features.
[pghintplan/pg_hint_plan.git] / pg_hint_plan.c
index 5a84058..2897c0a 100644 (file)
@@ -37,8 +37,8 @@ PG_MODULE_MAGIC;
 #define BLOCK_COMMENT_START            "/*"
 #define BLOCK_COMMENT_END              "*/"
 #define HINT_COMMENT_KEYWORD   "+"
-#define HINT_START             BLOCK_COMMENT_START HINT_COMMENT_KEYWORD
-#define HINT_END               BLOCK_COMMENT_END
+#define HINT_START                             BLOCK_COMMENT_START HINT_COMMENT_KEYWORD
+#define HINT_END                               BLOCK_COMMENT_END
 
 /* hint keywords */
 #define HINT_SEQSCAN                   "SeqScan"
@@ -49,10 +49,6 @@ PG_MODULE_MAGIC;
 #define HINT_NOINDEXSCAN               "NoIndexScan"
 #define HINT_NOBITMAPSCAN              "NoBitmapScan"
 #define HINT_NOTIDSCAN                 "NoTidScan"
-#if PG_VERSION_NUM >= 90200
-#define HINT_INDEXONLYSCAN             "IndexonlyScan"
-#define HINT_NOINDEXONLYSCAN   "NoIndexonlyScan"
-#endif
 #define HINT_NESTLOOP                  "NestLoop"
 #define HINT_MERGEJOIN                 "MergeJoin"
 #define HINT_HASHJOIN                  "HashJoin"
@@ -80,9 +76,6 @@ enum
        ENABLE_INDEXSCAN = 0x02,
        ENABLE_BITMAPSCAN = 0x04,
        ENABLE_TIDSCAN = 0x08,
-#if PG_VERSION_NUM >= 90200
-       ENABLE_INDEXONLYSCAN = 0x10
-#endif
 } SCAN_TYPE_BITS;
 
 enum
@@ -94,10 +87,6 @@ enum
 
 #define ENABLE_ALL_SCAN (ENABLE_SEQSCAN | ENABLE_INDEXSCAN | ENABLE_BITMAPSCAN \
                                                | ENABLE_TIDSCAN)
-#if PG_VERSION_NUM >= 90200
-#define ENABLE_ALL_SCAN (ENABLE_SEQSCAN | ENABLE_INDEXSCAN | ENABLE_BITMAPSCAN \
-                                               | ENABLE_TIDSCAN | ENABLE_INDEXONLYSCAN)
-#endif
 #define ENABLE_ALL_JOIN (ENABLE_NESTLOOP | ENABLE_MERGEJOIN | ENABLE_HASHJOIN)
 #define DISABLE_ALL_SCAN 0
 #define DISABLE_ALL_JOIN 0
@@ -202,14 +191,15 @@ struct PlanHint
        ScanMethodHint **scan_hints;            /* parsed scan hints */
        int                             init_scan_mask;         /* initial value scan parameter */
        Index                   parent_relid;           /* inherit parent table relid */
+       ScanMethodHint *parent_hint;            /* inherit parent table scan hint */
 
        /* for join method hints */
        JoinMethodHint **join_hints;            /* parsed join hints */
        int                             init_join_mask;         /* initial value join parameter */
        List              **join_hint_level;
 
-       /* for Leading hints */
-       LeadingHint       **leading_hints;              /* parsed Leading hints */
+       /* for Leading hint */
+       LeadingHint        *leading_hint;               /* parsed last specified Leading hint */
 
        /* for Set hints */
        SetHint           **set_hints;                  /* parsed Set hints */
@@ -229,9 +219,11 @@ typedef struct HintParser
 void           _PG_init(void);
 void           _PG_fini(void);
 
-void pg_hint_plan_ProcessUtility(Node *parsetree, const char *queryString,
-                                                                ParamListInfo params, bool isTopLevel,
-                                                                DestReceiver *dest, char *completionTag);
+static void pg_hint_plan_ProcessUtility(Node *parsetree,
+                                                                               const char *queryString,
+                                                                               ParamListInfo params, bool isTopLevel,
+                                                                               DestReceiver *dest,
+                                                                               char *completionTag);
 static PlannedStmt *pg_hint_plan_planner(Query *parse, int cursorOptions,
                                                                                 ParamListInfo boundParams);
 static void pg_hint_plan_get_relation_info(PlannerInfo *root,
@@ -276,10 +268,6 @@ static void make_rels_by_clauseless_joins(PlannerInfo *root,
                                                                                  RelOptInfo *old_rel,
                                                                                  ListCell *other_rels);
 static bool has_join_restriction(PlannerInfo *root, RelOptInfo *rel);
-static void set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
-                                                        Index rti, RangeTblEntry *rte);
-static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
-                                                                  RangeTblEntry *rte);
 static void set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
                                                                        Index rti, RangeTblEntry *rte);
 static List *accumulate_append_subpath(List *subpaths, Path *path);
@@ -336,10 +324,6 @@ static const HintParser parsers[] = {
        {HINT_NOINDEXSCAN, ScanMethodHintCreate},
        {HINT_NOBITMAPSCAN, ScanMethodHintCreate},
        {HINT_NOTIDSCAN, ScanMethodHintCreate},
-#if PG_VERSION_NUM >= 90200
-       {HINT_INDEXONLYSCAN, ScanMethodHintCreate},
-       {HINT_NOINDEXONLYSCAN, ScanMethodHintCreate},
-#endif
        {HINT_NESTLOOP, JoinMethodHintCreate},
        {HINT_MERGEJOIN, JoinMethodHintCreate},
        {HINT_HASHJOIN, JoinMethodHintCreate},
@@ -417,83 +401,9 @@ _PG_fini(void)
        join_search_hook = prev_join_search;
 }
 
-static void
-ProcessUtility_hook_error_callback(void *arg)
-{
-       stmt_name = NULL;
-}
-
-void
-pg_hint_plan_ProcessUtility(Node *parsetree, const char *queryString,
-                                                       ParamListInfo params, bool isTopLevel,
-                                                       DestReceiver *dest, char *completionTag)
-{
-       Node                               *node;
-       ErrorContextCallback    errcontext;
-
-       if (!pg_hint_plan_enable)
-       {
-               if (prev_ProcessUtility)
-                       (*prev_ProcessUtility) (parsetree, queryString, params,
-                                                                       isTopLevel, dest, completionTag);
-               else
-                       standard_ProcessUtility(parsetree, queryString, params,
-                                                                       isTopLevel, dest, completionTag);
-
-               return;
-       }
-
-       node = parsetree;
-       if (IsA(node, ExplainStmt))
-       {
-               /*
-                * EXPLAIN対象のクエリのパースツリーを取得する
-                */
-               ExplainStmt        *stmt;
-               Query              *query;
-
-               stmt = (ExplainStmt *) node;
-
-               Assert(IsA(stmt->query, Query));
-               query = (Query *) stmt->query;
-
-               if (query->commandType == CMD_UTILITY && query->utilityStmt != NULL)
-                       node = query->utilityStmt;
-       }
-
-       /*
-        * EXECUTEコマンドならば、PREPARE時に指定されたクエリ文字列を取得し、ヒント
-        * 句の候補として設定する
-        */
-       if (IsA(node, ExecuteStmt))
-       {
-               ExecuteStmt        *stmt;
-
-               /* Set up callback to statement name reset. */
-               errcontext.callback = ProcessUtility_hook_error_callback;
-               errcontext.arg = NULL;
-               errcontext.previous = error_context_stack;
-               error_context_stack = &errcontext;
-
-               stmt = (ExecuteStmt *) node;
-               stmt_name = stmt->name;
-       }
-
-       if (prev_ProcessUtility)
-               (*prev_ProcessUtility) (parsetree, queryString, params,
-                                                               isTopLevel, dest, completionTag);
-       else
-               standard_ProcessUtility(parsetree, queryString, params,
-                                                               isTopLevel, dest, completionTag);
-
-       if (stmt_name)
-       {
-               stmt_name = NULL;
-
-               /* Remove error callback. */
-               error_context_stack = errcontext.previous;
-       }
-}
+/*
+ * create and delete functions the hint object
+ */
 
 static Hint *
 ScanMethodHintCreate(const char *hint_str, const char *keyword)
@@ -528,49 +438,6 @@ ScanMethodHintDelete(ScanMethodHint *hint)
        pfree(hint);
 }
 
-static void
-dump_quote_value(StringInfo buf, const char *value)
-{
-       bool            need_quote = false;
-       const char *str;
-
-       for (str = value; *str != '\0'; str++)
-       {
-               if (isspace(*str) || *str == ')' || *str == '"')
-               {
-                       need_quote = true;
-                       appendStringInfoCharMacro(buf, '"');
-                       break;
-               }
-       }
-
-       for (str = value; *str != '\0'; str++)
-       {
-               if (*str == '"')
-                       appendStringInfoCharMacro(buf, '"');
-
-               appendStringInfoCharMacro(buf, *str);
-       }
-
-       if (need_quote)
-               appendStringInfoCharMacro(buf, '"');
-}
-
-static void
-ScanMethodHintDump(ScanMethodHint *hint, StringInfo buf)
-{
-       ListCell   *l;
-
-       appendStringInfo(buf, "%s(", hint->base.keyword);
-       dump_quote_value(buf, hint->relname);
-       foreach(l, hint->indexnames)
-       {
-               appendStringInfoCharMacro(buf, ' ');
-               dump_quote_value(buf, (char *) lfirst(l));
-       }
-       appendStringInfoString(buf, ")\n");
-}
-
 static Hint *
 JoinMethodHintCreate(const char *hint_str, const char *keyword)
 {
@@ -611,22 +478,6 @@ JoinMethodHintDelete(JoinMethodHint *hint)
        pfree(hint);
 }
 
-static void
-JoinMethodHintDump(JoinMethodHint *hint, StringInfo buf)
-{
-       int     i;
-
-       appendStringInfo(buf, "%s(", hint->base.keyword);
-       dump_quote_value(buf, hint->relnames[0]);
-       for (i = 1; i < hint->nrels; i++)
-       {
-               appendStringInfoCharMacro(buf, ' ');
-               dump_quote_value(buf, hint->relnames[i]);
-       }
-       appendStringInfoString(buf, ")\n");
-
-}
-
 static Hint *
 LeadingHintCreate(const char *hint_str, const char *keyword)
 {
@@ -656,27 +507,6 @@ LeadingHintDelete(LeadingHint *hint)
        pfree(hint);
 }
 
-static void
-LeadingHintDump(LeadingHint *hint, StringInfo buf)
-{
-       bool            is_first;
-       ListCell   *l;
-
-       appendStringInfo(buf, "%s(", HINT_LEADING);
-       is_first = true;
-       foreach(l, hint->relations)
-       {
-               if (is_first)
-                       is_first = false;
-               else
-                       appendStringInfoCharMacro(buf, ' ');
-
-               dump_quote_value(buf, (char *) lfirst(l));
-       }
-
-       appendStringInfoString(buf, ")\n");
-}
-
 static Hint *
 SetHintCreate(const char *hint_str, const char *keyword)
 {
@@ -710,16 +540,6 @@ SetHintDelete(SetHint *hint)
        pfree(hint);
 }
 
-static void
-SetHintDump(SetHint *hint, StringInfo buf)
-{
-       appendStringInfo(buf, "%s(", HINT_SET);
-       dump_quote_value(buf, hint->name);
-       appendStringInfoCharMacro(buf, ' ');
-       dump_quote_value(buf, hint->value);
-       appendStringInfo(buf, ")\n");
-}
-
 static PlanHint *
 PlanHintCreate(void)
 {
@@ -734,10 +554,11 @@ PlanHintCreate(void)
        hint->scan_hints = NULL;
        hint->init_scan_mask = 0;
        hint->parent_relid = 0;
+       hint->parent_hint = NULL;
        hint->join_hints = NULL;
        hint->init_join_mask = 0;
        hint->join_hint_level = NULL;
-       hint->leading_hints = NULL;
+       hint->leading_hint = NULL;
        hint->context = superuser() ? PGC_SUSET : PGC_USERSET;
        hint->set_hints = NULL;
 
@@ -761,6 +582,100 @@ PlanHintDelete(PlanHint *hint)
                pfree(hint->all_hints);
 }
 
+/*
+ * dump functions
+ */
+
+static void
+dump_quote_value(StringInfo buf, const char *value)
+{
+       bool            need_quote = false;
+       const char *str;
+
+       for (str = value; *str != '\0'; str++)
+       {
+               if (isspace(*str) || *str == ')' || *str == '"')
+               {
+                       need_quote = true;
+                       appendStringInfoCharMacro(buf, '"');
+                       break;
+               }
+       }
+
+       for (str = value; *str != '\0'; str++)
+       {
+               if (*str == '"')
+                       appendStringInfoCharMacro(buf, '"');
+
+               appendStringInfoCharMacro(buf, *str);
+       }
+
+       if (need_quote)
+               appendStringInfoCharMacro(buf, '"');
+}
+
+static void
+ScanMethodHintDump(ScanMethodHint *hint, StringInfo buf)
+{
+       ListCell   *l;
+
+       appendStringInfo(buf, "%s(", hint->base.keyword);
+       dump_quote_value(buf, hint->relname);
+       foreach(l, hint->indexnames)
+       {
+               appendStringInfoCharMacro(buf, ' ');
+               dump_quote_value(buf, (char *) lfirst(l));
+       }
+       appendStringInfoString(buf, ")\n");
+}
+
+static void
+JoinMethodHintDump(JoinMethodHint *hint, StringInfo buf)
+{
+       int     i;
+
+       appendStringInfo(buf, "%s(", hint->base.keyword);
+       dump_quote_value(buf, hint->relnames[0]);
+       for (i = 1; i < hint->nrels; i++)
+       {
+               appendStringInfoCharMacro(buf, ' ');
+               dump_quote_value(buf, hint->relnames[i]);
+       }
+       appendStringInfoString(buf, ")\n");
+
+}
+
+static void
+LeadingHintDump(LeadingHint *hint, StringInfo buf)
+{
+       bool            is_first;
+       ListCell   *l;
+
+       appendStringInfo(buf, "%s(", HINT_LEADING);
+       is_first = true;
+       foreach(l, hint->relations)
+       {
+               if (is_first)
+                       is_first = false;
+               else
+                       appendStringInfoCharMacro(buf, ' ');
+
+               dump_quote_value(buf, (char *) lfirst(l));
+       }
+
+       appendStringInfoString(buf, ")\n");
+}
+
+static void
+SetHintDump(SetHint *hint, StringInfo buf)
+{
+       appendStringInfo(buf, "%s(", HINT_SET);
+       dump_quote_value(buf, hint->name);
+       appendStringInfoCharMacro(buf, ' ');
+       dump_quote_value(buf, hint->value);
+       appendStringInfo(buf, ")\n");
+}
+
 static void
 all_hint_dump(PlanHint *hint, StringInfo buf, const char *title,
                          HintStatus state)
@@ -801,6 +716,10 @@ PlanHintDump(PlanHint *hint)
        pfree(buf.data);
 }
 
+/*
+ * compare functions
+ */
+
 static int
 RelnameCmp(const void *a, const void *b)
 {
@@ -869,102 +788,6 @@ AllHintCmpIsOrder(const void *a, const void *b)
        return AllHintCmp(a, b, true);
 }
 
-#if PG_VERSION_NUM < 90200
-static int
-set_config_option_wrapper(const char *name, const char *value,
-                                                 GucContext context, GucSource source,
-                                                 GucAction action, bool changeVal, int elevel)
-{
-       int                             result = 0;
-       MemoryContext   ccxt = CurrentMemoryContext;
-
-       PG_TRY();
-       {
-               result = set_config_option(name, value, context, source,
-                                                                  action, changeVal);
-       }
-       PG_CATCH();
-       {
-               ErrorData          *errdata;
-               MemoryContext   ecxt;
-
-               if (elevel >= ERROR)
-                       PG_RE_THROW();
-
-               ecxt = MemoryContextSwitchTo(ccxt);
-               errdata = CopyErrorData();
-               ereport(elevel, (errcode(errdata->sqlerrcode),
-                               errmsg("%s", errdata->message),
-                               errdata->detail ? errdetail("%s", errdata->detail) : 0,
-                               errdata->hint ? errhint("%s", errdata->hint) : 0));
-               FreeErrorData(errdata);
-
-               MemoryContextSwitchTo(ecxt);
-       }
-       PG_END_TRY();
-
-       return result;
-}
-
-#define set_config_option(name, value, context, source, \
-                                                 action, changeVal, elevel) \
-       set_config_option_wrapper(name, value, context, source, \
-                                                         action, changeVal, elevel)
-#endif
-
-static int
-set_config_options(SetHint **options, int noptions, GucContext context)
-{
-       int     i;
-       int     save_nestlevel;
-
-       save_nestlevel = NewGUCNestLevel();
-
-       for (i = 0; i < noptions; i++)
-       {
-               SetHint    *hint = options[i];
-               int                     result;
-
-               if (!hint_state_enabled(hint))
-                       continue;
-
-               result = set_config_option(hint->name, hint->value, context,
-                                                                  PGC_S_SESSION, GUC_ACTION_SAVE, true,
-                                                                  pg_hint_plan_parse_messages);
-               if (result != 0)
-                       hint->base.state = HINT_STATE_USED;
-               else
-                       hint->base.state = HINT_STATE_ERROR;
-       }
-
-       return save_nestlevel;
-}
-
-#define SET_CONFIG_OPTION(name, type_bits) \
-       set_config_option((name), \
-               (enforce_mask & (type_bits)) ? "true" : "false", \
-               context, PGC_S_SESSION, GUC_ACTION_SAVE, true, ERROR)
-
-static void
-set_scan_config_options(unsigned char enforce_mask, GucContext context)
-{
-       SET_CONFIG_OPTION("enable_seqscan", ENABLE_SEQSCAN);
-       SET_CONFIG_OPTION("enable_indexscan", ENABLE_INDEXSCAN);
-       SET_CONFIG_OPTION("enable_bitmapscan", ENABLE_BITMAPSCAN);
-       SET_CONFIG_OPTION("enable_tidscan", ENABLE_TIDSCAN);
-#if PG_VERSION_NUM >= 90200
-       SET_CONFIG_OPTION("enable_indexonlyscan", ENABLE_INDEXSCAN);
-#endif
-}
-
-static void
-set_join_config_options(unsigned char enforce_mask, GucContext context)
-{
-       SET_CONFIG_OPTION("enable_nestloop", ENABLE_NESTLOOP);
-       SET_CONFIG_OPTION("enable_mergejoin", ENABLE_MERGEJOIN);
-       SET_CONFIG_OPTION("enable_hashjoin", ENABLE_HASHJOIN);
-}
-
 /*
  * parse functions
  */
@@ -1071,8 +894,15 @@ parse_quote_value(const char *str, char **word, char *value_type)
 
        if (buf.len == 0)
        {
+               char   *type;
+
+               type = pstrdup(value_type);
+               type[0] = toupper(type[0]);
+               parse_ereport(str, ("%s is necessary.", type));
+
                pfree(buf.data);
-               parse_ereport(str, ("%s is necessary.", value_type));
+               pfree(type);
+
                return NULL;
        }
 
@@ -1199,7 +1029,10 @@ parse_head_comment(Query *parse)
 
        /* 入れ子にしたブロックコメントはサポートしない */
        if ((head = strstr(p, BLOCK_COMMENT_START)) != NULL && head < tail)
+       {
                parse_ereport(head, ("Block comments nest doesn't supported."));
+               return NULL;
+       }
 
        /* ヒント句部分を切り出す */
        len = tail - p;
@@ -1249,9 +1082,10 @@ parse_head_comment(Query *parse)
        plan->scan_hints = (ScanMethodHint **) plan->all_hints;
        plan->join_hints = (JoinMethodHint **) plan->all_hints +
                plan->num_hints[HINT_TYPE_SCAN_METHOD];
-       plan->leading_hints = (LeadingHint **) plan->all_hints +
+       plan->leading_hint = (LeadingHint *) plan->all_hints[
                plan->num_hints[HINT_TYPE_SCAN_METHOD] +
-               plan->num_hints[HINT_TYPE_JOIN_METHOD];
+               plan->num_hints[HINT_TYPE_JOIN_METHOD] +
+               plan->num_hints[HINT_TYPE_LEADING] - 1];
        plan->set_hints = (SetHint **) plan->all_hints +
                plan->num_hints[HINT_TYPE_SCAN_METHOD] +
                plan->num_hints[HINT_TYPE_JOIN_METHOD] +
@@ -1272,7 +1106,7 @@ ScanMethodHintParse(ScanMethodHint *hint, PlanHint *plan, Query *parse,
        /*
         * スキャン方式のヒントでリレーション名が読み取れない場合はヒント無効
         */
-       if ((str = parse_quote_value(str, &hint->relname, "ralation name")) == NULL)
+       if ((str = parse_quote_value(str, &hint->relname, "relation name")) == NULL)
                return NULL;
 
        skip_space(str);
@@ -1282,9 +1116,6 @@ ScanMethodHintParse(ScanMethodHint *hint, PlanHint *plan, Query *parse,
         * する。
         */
        if (strcmp(keyword, HINT_INDEXSCAN) == 0 ||
-#if PG_VERSION_NUM >= 90200
-               strcmp(keyword, HINT_INDEXONLYSCAN) == 0 ||
-#endif
                strcmp(keyword, HINT_BITMAPSCAN) == 0)
        {
                while (*str != ')' && *str != '\0')
@@ -1335,93 +1166,282 @@ JoinMethodHintParse(JoinMethodHint *hint, PlanHint *plan, Query *parse,
        char       *relname;
        const char *keyword = hint->base.keyword;
 
-       skip_space(str);
+       skip_space(str);
+
+       hint->relnames = palloc(sizeof(char *));
+
+       while ((str = parse_quote_value(str, &relname, "relation name")) != NULL)
+       {
+               hint->nrels++;
+               hint->relnames = repalloc(hint->relnames, sizeof(char *) * hint->nrels);
+               hint->relnames[hint->nrels - 1] = relname;
+
+               skip_space(str);
+               if (*str == ')')
+                       break;
+       }
+
+       if (str == NULL)
+               return NULL;
+
+       /* Join 対象のテーブルは最低でも2つ指定する必要がある */
+       if (hint->nrels < 2)
+       {
+               parse_ereport(str, ("Specified relation more than two."));
+               hint->base.state = HINT_STATE_ERROR;
+       }
+
+       /* テーブル名順にソートする */
+       qsort(hint->relnames, hint->nrels, sizeof(char *), RelnameCmp);
+
+       if (strcasecmp(keyword, HINT_NESTLOOP) == 0)
+               hint->enforce_mask = ENABLE_NESTLOOP;
+       else if (strcasecmp(keyword, HINT_MERGEJOIN) == 0)
+               hint->enforce_mask = ENABLE_MERGEJOIN;
+       else if (strcasecmp(keyword, HINT_HASHJOIN) == 0)
+               hint->enforce_mask = ENABLE_HASHJOIN;
+       else if (strcasecmp(keyword, HINT_NONESTLOOP) == 0)
+               hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_NESTLOOP;
+       else if (strcasecmp(keyword, HINT_NOMERGEJOIN) == 0)
+               hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_MERGEJOIN;
+       else if (strcasecmp(keyword, HINT_NOHASHJOIN) == 0)
+               hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_HASHJOIN;
+       else
+       {
+               parse_ereport(str, ("Unrecognized hint keyword \"%s\".", keyword));
+               return NULL;
+       }
+
+       return str;
+}
+
+static const char *
+LeadingHintParse(LeadingHint *hint, PlanHint *plan, Query *parse,
+                                const char *str)
+{
+       skip_space(str);
+
+       while (*str != ')')
+       {
+               char   *relname;
+
+               if ((str = parse_quote_value(str, &relname, "relation name")) == NULL)
+                       return NULL;
+
+               hint->relations = lappend(hint->relations, relname);
+
+               skip_space(str);
+       }
+
+       /* テーブル指定が2つ未満の場合は、Leading ヒントはエラーとする */
+       if (list_length(hint->relations) < 2)
+       {
+               parse_ereport(hint->base.hint_str,
+                                         ("In %s hint, specified relation name 2 or more.",
+                                          HINT_LEADING));
+               hint->base.state = HINT_STATE_ERROR;
+       }
+
+       return str;
+}
+
+static const char *
+SetHintParse(SetHint *hint, PlanHint *plan, Query *parse, const char *str)
+{
+       if ((str = parse_quote_value(str, &hint->name, "parameter name")) == NULL ||
+               (str = parse_quote_value(str, &hint->value, "parameter value")) == NULL)
+               return NULL;
+
+       return str;
+}
+
+/*
+ * set GUC parameter functions
+ */
+
+static int
+set_config_option_wrapper(const char *name, const char *value,
+                                                 GucContext context, GucSource source,
+                                                 GucAction action, bool changeVal, int elevel)
+{
+       int                             result = 0;
+       MemoryContext   ccxt = CurrentMemoryContext;
+
+       PG_TRY();
+       {
+               result = set_config_option(name, value, context, source,
+                                                                  action, changeVal);
+       }
+       PG_CATCH();
+       {
+               ErrorData          *errdata;
+               MemoryContext   ecxt;
+
+               ecxt = MemoryContextSwitchTo(ccxt);
+               errdata = CopyErrorData();
+               FlushErrorState();
+               ereport(elevel, (errcode(errdata->sqlerrcode),
+                               errmsg("%s", errdata->message),
+                               errdata->detail ? errdetail("%s", errdata->detail) : 0,
+                               errdata->hint ? errhint("%s", errdata->hint) : 0));
+               FreeErrorData(errdata);
+
+               MemoryContextSwitchTo(ecxt);
+       }
+       PG_END_TRY();
+
+       return result;
+}
+
+static int
+set_config_options(SetHint **options, int noptions, GucContext context)
+{
+       int     i;
+       int     save_nestlevel;
+
+       save_nestlevel = NewGUCNestLevel();
+
+       for (i = 0; i < noptions; i++)
+       {
+               SetHint    *hint = options[i];
+               int                     result;
+
+               if (!hint_state_enabled(hint))
+                       continue;
+
+               result = set_config_option_wrapper(hint->name, hint->value, context,
+                                                                                  PGC_S_SESSION, GUC_ACTION_SAVE, true,
+                                                                                  pg_hint_plan_parse_messages);
+               if (result != 0)
+                       hint->base.state = HINT_STATE_USED;
+               else
+                       hint->base.state = HINT_STATE_ERROR;
+       }
 
-       hint->relnames = palloc(sizeof(char *));
+       return save_nestlevel;
+}
 
-       while ((str = parse_quote_value(str, &relname, "table name")) != NULL)
-       {
-               hint->nrels++;
-               hint->relnames = repalloc(hint->relnames, sizeof(char *) * hint->nrels);
-               hint->relnames[hint->nrels - 1] = relname;
+#define SET_CONFIG_OPTION(name, type_bits) \
+       set_config_option_wrapper((name), \
+               (mask & (type_bits)) ? "true" : "false", \
+               context, PGC_S_SESSION, GUC_ACTION_SAVE, true, ERROR)
 
-               skip_space(str);
-               if (*str == ')')
-                       break;
-       }
+static void
+set_scan_config_options(unsigned char enforce_mask, GucContext context)
+{
+       unsigned char   mask;
 
-       if (str == NULL)
-               return NULL;
+       if (enforce_mask == ENABLE_SEQSCAN || enforce_mask == ENABLE_INDEXSCAN ||
+               enforce_mask == ENABLE_BITMAPSCAN || enforce_mask == ENABLE_TIDSCAN
+               )
+               mask = enforce_mask;
+       else
+               mask = enforce_mask & global->init_scan_mask;
 
-       /* Join 対象のテーブルは最低でも2つ指定する必要がある */
-       if (hint->nrels < 2)
-       {
-               parse_ereport(str, ("Specified relation more than two."));
-               hint->base.state = HINT_STATE_ERROR;
-       }
+       SET_CONFIG_OPTION("enable_seqscan", ENABLE_SEQSCAN);
+       SET_CONFIG_OPTION("enable_indexscan", ENABLE_INDEXSCAN);
+       SET_CONFIG_OPTION("enable_bitmapscan", ENABLE_BITMAPSCAN);
+       SET_CONFIG_OPTION("enable_tidscan", ENABLE_TIDSCAN);
+}
 
-       /* テーブル名順にソートする */
-       qsort(hint->relnames, hint->nrels, sizeof(char *), RelnameCmp);
+static void
+set_join_config_options(unsigned char enforce_mask, GucContext context)
+{
+       unsigned char   mask;
 
-       if (strcasecmp(keyword, HINT_NESTLOOP) == 0)
-               hint->enforce_mask = ENABLE_NESTLOOP;
-       else if (strcasecmp(keyword, HINT_MERGEJOIN) == 0)
-               hint->enforce_mask = ENABLE_MERGEJOIN;
-       else if (strcasecmp(keyword, HINT_HASHJOIN) == 0)
-               hint->enforce_mask = ENABLE_HASHJOIN;
-       else if (strcasecmp(keyword, HINT_NONESTLOOP) == 0)
-               hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_NESTLOOP;
-       else if (strcasecmp(keyword, HINT_NOMERGEJOIN) == 0)
-               hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_MERGEJOIN;
-       else if (strcasecmp(keyword, HINT_NOHASHJOIN) == 0)
-               hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_HASHJOIN;
+       if (enforce_mask == ENABLE_NESTLOOP || enforce_mask == ENABLE_MERGEJOIN ||
+               enforce_mask == ENABLE_HASHJOIN)
+               mask = enforce_mask;
        else
-       {
-               parse_ereport(str, ("Unrecognized hint keyword \"%s\".", keyword));
-               return NULL;
-       }
+               mask = enforce_mask & global->init_join_mask;
 
-       return str;
+       SET_CONFIG_OPTION("enable_nestloop", ENABLE_NESTLOOP);
+       SET_CONFIG_OPTION("enable_mergejoin", ENABLE_MERGEJOIN);
+       SET_CONFIG_OPTION("enable_hashjoin", ENABLE_HASHJOIN);
 }
 
-static const char *
-LeadingHintParse(LeadingHint *hint, PlanHint *plan, Query *parse,
-                                const char *str)
+/*
+ * pg_hint_plan hook functions
+ */
+
+static void
+pg_hint_plan_ProcessUtility(Node *parsetree, const char *queryString,
+                                                       ParamListInfo params, bool isTopLevel,
+                                                       DestReceiver *dest, char *completionTag)
 {
-       skip_space(str);
+       Node                               *node;
 
-       while (*str != ')')
+       if (!pg_hint_plan_enable)
        {
-               char   *relname;
+               if (prev_ProcessUtility)
+                       (*prev_ProcessUtility) (parsetree, queryString, params,
+                                                                       isTopLevel, dest, completionTag);
+               else
+                       standard_ProcessUtility(parsetree, queryString, params,
+                                                                       isTopLevel, dest, completionTag);
 
-               if ((str = parse_quote_value(str, &relname, "relation name")) == NULL)
-                       return NULL;
+               return;
+       }
 
-               hint->relations = lappend(hint->relations, relname);
+       node = parsetree;
+       if (IsA(node, ExplainStmt))
+       {
+               /*
+                * EXPLAIN対象のクエリのパースツリーを取得する
+                */
+               ExplainStmt        *stmt;
+               Query              *query;
 
-               skip_space(str);
+               stmt = (ExplainStmt *) node;
+
+               Assert(IsA(stmt->query, Query));
+               query = (Query *) stmt->query;
+
+               if (query->commandType == CMD_UTILITY && query->utilityStmt != NULL)
+                       node = query->utilityStmt;
        }
 
-       /* テーブル指定が2つ未満の場合は、Leading ヒントはエラーとする */
-       if (list_length(hint->relations) < 2)
+       /*
+        * EXECUTEコマンドならば、PREPARE時に指定されたクエリ文字列を取得し、ヒント
+        * 句の候補として設定する
+        */
+       if (IsA(node, ExecuteStmt))
        {
-               parse_ereport(hint->base.hint_str,
-                                         ("In %s hint, specified relation name 2 or more.",
-                                          HINT_LEADING));
-               hint->base.state = HINT_STATE_ERROR;
+               ExecuteStmt        *stmt;
+
+               stmt = (ExecuteStmt *) node;
+               stmt_name = stmt->name;
        }
 
-       return str;
-}
+       if (stmt_name)
+       {
+               PG_TRY();
+               {
+                       if (prev_ProcessUtility)
+                               (*prev_ProcessUtility) (parsetree, queryString, params,
+                                                                               isTopLevel, dest, completionTag);
+                       else
+                               standard_ProcessUtility(parsetree, queryString, params,
+                                                                               isTopLevel, dest, completionTag);
+               }
+               PG_CATCH();
+               {
+                       stmt_name = NULL;
+                       PG_RE_THROW();
+               }
+               PG_END_TRY();
 
-static const char *
-SetHintParse(SetHint *hint, PlanHint *plan, Query *parse, const char *str)
-{
-       if ((str = parse_quote_value(str, &hint->name, "parameter name")) == NULL ||
-               (str = parse_quote_value(str, &hint->value, "parameter value")) == NULL)
-               return NULL;
+               stmt_name = NULL;
 
-       return str;
+               return;
+       }
+
+       if (prev_ProcessUtility)
+               (*prev_ProcessUtility) (parsetree, queryString, params,
+                                                               isTopLevel, dest, completionTag);
+       else
+               standard_ProcessUtility(parsetree, queryString, params,
+                                                               isTopLevel, dest, completionTag);
 }
 
 static PlannedStmt *
@@ -1459,10 +1479,6 @@ pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
                global->init_scan_mask |= ENABLE_BITMAPSCAN;
        if (enable_tidscan)
                global->init_scan_mask |= ENABLE_TIDSCAN;
-#if PG_VERSION_NUM >= 90200
-       if (enable_indexonlyscan)
-               global->init_scan_mask |= ENABLE_INDEXONLYSCAN;
-#endif
        if (enable_nestloop)
                global->init_join_mask |= ENABLE_NESTLOOP;
        if (enable_mergejoin)
@@ -1535,55 +1551,16 @@ find_scan_hint(PlannerInfo *root, RelOptInfo *rel)
 }
 
 static void
-pg_hint_plan_get_relation_info(PlannerInfo *root, Oid relationObjectId,
-                                                          bool inhparent, RelOptInfo *rel)
+delete_indexes(ScanMethodHint *hint, RelOptInfo *rel)
 {
-       ScanMethodHint *hint;
        ListCell           *cell;
        ListCell           *prev;
        ListCell           *next;
 
-       if (prev_get_relation_info)
-               (*prev_get_relation_info) (root, relationObjectId, inhparent, rel);
-
-       /* 有効なヒントが指定されなかった場合は処理をスキップする。 */
-       if (!global)
-               return;
-
-       if (inhparent)
-       {
-               /* cache does relids of parent table */
-               global->parent_relid = rel->relid;
-       }
-       else if (global->parent_relid != 0)
-       {
-               /* If this rel is an appendrel child, */
-               ListCell   *l;
-
-               /* append_rel_list contains all append rels; ignore others */
-               foreach(l, root->append_rel_list)
-               {
-                       AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
-
-                       /* This rel is child table. */
-                       if (appinfo->parent_relid == global->parent_relid)
-                               return;
-               }
-
-               /* This rel is not inherit table. */
-               global->parent_relid = 0;
-       }
-
-       /* scan hint が指定されない場合は、GUCパラメータをリセットする。 */
-       if ((hint = find_scan_hint(root, rel)) == NULL)
-       {
-               set_scan_config_options(global->init_scan_mask, global->context);
-               return;
-       }
-       set_scan_config_options(hint->enforce_mask, global->context);
-       hint->base.state = HINT_STATE_USED;
-
-       /* インデックスを全て削除し、スキャンに使えなくする */
+       /*
+        * We delete all the IndexOptInfo list and prevent you from being usable by
+        * a scan.
+        */
        if (hint->enforce_mask == ENABLE_SEQSCAN ||
                hint->enforce_mask == ENABLE_TIDSCAN)
        {
@@ -1594,11 +1571,16 @@ pg_hint_plan_get_relation_info(PlannerInfo *root, Oid relationObjectId,
                return;
        }
 
-       /* 後でパスを作り直すため、ここではなにもしない */
-       if (hint->indexnames == NULL)
+       /*
+        * When a list of indexes is not specified, we just use all indexes.
+        */
+       if (hint->indexnames == NIL)
                return;
 
-       /* 指定されたインデックスのみをのこす */
+       /*
+        * Leaving only an specified index, we delete it from a IndexOptInfo list
+        * other than it.
+        */
        prev = NULL;
        for (cell = list_head(rel->indexlist); cell; cell = next)
        {
@@ -1627,6 +1609,67 @@ pg_hint_plan_get_relation_info(PlannerInfo *root, Oid relationObjectId,
        }
 }
 
+static void
+pg_hint_plan_get_relation_info(PlannerInfo *root, Oid relationObjectId,
+                                                          bool inhparent, RelOptInfo *rel)
+{
+       ScanMethodHint *hint;
+
+       if (prev_get_relation_info)
+               (*prev_get_relation_info) (root, relationObjectId, inhparent, rel);
+
+       /* 有効なヒントが指定されなかった場合は処理をスキップする。 */
+       if (!global)
+               return;
+
+       if (inhparent)
+       {
+               /* store does relids of parent table. */
+               global->parent_relid = rel->relid;
+       }
+       else if (global->parent_relid != 0)
+       {
+               /*
+                * We use the same GUC parameter if this table is the child table of a
+                * table called pg_hint_plan_get_relation_info just before that.
+                */
+               ListCell   *l;
+
+               /* append_rel_list contains all append rels; ignore others */
+               foreach(l, root->append_rel_list)
+               {
+                       AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
+
+                       /* This rel is child table. */
+                       if (appinfo->parent_relid == global->parent_relid &&
+                               appinfo->child_relid == rel->relid)
+                       {
+                               if (global->parent_hint)
+                                       delete_indexes(global->parent_hint, rel);
+
+                               return;
+                       }
+               }
+
+               /* This rel is not inherit table. */
+               global->parent_relid = 0;
+               global->parent_hint = NULL;
+       }
+
+       /* scan hint が指定されない場合は、GUCパラメータをリセットする。 */
+       if ((hint = find_scan_hint(root, rel)) == NULL)
+       {
+               set_scan_config_options(global->init_scan_mask, global->context);
+               return;
+       }
+       set_scan_config_options(hint->enforce_mask, global->context);
+       hint->base.state = HINT_STATE_USED;
+       if (inhparent)
+               global->parent_hint = hint;
+
+       delete_indexes(hint, rel);
+}
+
 /*
  * aliasnameがクエリ中に指定した別名と一致する場合は、そのインデックスを返し、一致
  * する別名がなければ0を返す。
@@ -1636,8 +1679,8 @@ static int
 find_relid_aliasname(PlannerInfo *root, char *aliasname, List *initial_rels,
                                         const char *str)
 {
-       int     i;
-       int     found = 0;
+       int             i;
+       Index   found = 0;
 
        for (i = 1; i < root->simple_rel_array_size; i++)
        {
@@ -1771,7 +1814,7 @@ transform_join_hints(PlanHint *plan, PlannerInfo *root, int nbaserel,
        if (plan->num_hints[HINT_TYPE_LEADING] == 0)
                return;
 
-       lhint = plan->leading_hints[plan->num_hints[HINT_TYPE_LEADING] - 1];
+       lhint = plan->leading_hint;
        if (!hint_state_enabled(lhint))
                return;
 
@@ -1851,6 +1894,29 @@ transform_join_hints(PlanHint *plan, PlannerInfo *root, int nbaserel,
 
 }
 
+/*
+ * set_plain_rel_pathlist
+ *       Build access paths for a plain relation (no subquery, no inheritance)
+ *
+ * This function was copied and edited from set_plain_rel_pathlist() in
+ * src/backend/optimizer/path/allpaths.c
+ */
+static void
+set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
+{
+       /* Consider sequential scan */
+       add_path(rel, create_seqscan_path(root, rel));
+
+       /* Consider index scans */
+       create_index_paths(root, rel);
+
+       /* Consider TID scans */
+       create_tidscan_paths(root, rel);
+
+       /* Now find the cheapest of the paths for this rel */
+       set_cheapest(rel);
+}
+
 static void
 rebuild_scan_path(PlanHint *plan, PlannerInfo *root, int level,
                                  List *initial_rels)
@@ -2023,8 +2089,7 @@ pg_hint_plan_join_search(PlannerInfo *root, int levels_needed,
        pfree(join_method_hints);
 
        if (global->num_hints[HINT_TYPE_LEADING] > 0 &&
-               hint_state_enabled(
-                       global->leading_hints[global->num_hints[HINT_TYPE_LEADING] - 1]))
+               hint_state_enabled(global->leading_hint))
                set_join_config_options(global->init_join_mask, global->context);
 
        return rel;
@@ -2033,6 +2098,9 @@ pg_hint_plan_join_search(PlannerInfo *root, int levels_needed,
 /*
  * set_rel_pathlist
  *       Build access paths for a base relation
+ *
+ * This function was copied and edited from set_rel_pathlist() in
+ * src/backend/optimizer/path/allpaths.c
  */
 static void
 set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,