#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"
#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"
ENABLE_INDEXSCAN = 0x02,
ENABLE_BITMAPSCAN = 0x04,
ENABLE_TIDSCAN = 0x08,
-#if PG_VERSION_NUM >= 90200
- ENABLE_INDEXONLYSCAN = 0x10
-#endif
} SCAN_TYPE_BITS;
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
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 */
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,
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);
{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},
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)
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)
{
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)
{
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)
{
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)
{
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;
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)
pfree(buf.data);
}
+/*
+ * compare functions
+ */
+
static int
RelnameCmp(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
*/
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;
}
/* 入れ子にしたブロックコメントはサポートしない */
if ((head = strstr(p, BLOCK_COMMENT_START)) != NULL && head < tail)
+ {
parse_ereport(head, ("Block comments nest doesn't supported."));
+ return NULL;
+ }
/* ヒント句部分を切り出す */
len = tail - p;
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] +
/*
* スキャン方式のヒントでリレーション名が読み取れない場合はヒント無効
*/
- 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);
* する。
*/
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')
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 *
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)
}
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)
{
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)
{
}
}
+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を返す。
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++)
{
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;
}
+/*
+ * 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)
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;
/*
* 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,