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,
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)
{
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)
{
}
static int
-ScanMethodHintCmp(const ScanMethodHint *a, const ScanMethodHint *b)
-{
- return RelnameCmp(&a->relname, &b->relname);
-}
-
-static int
-JoinMethodHintCmp(const JoinMethodHint *a, const JoinMethodHint *b)
-{
- int i;
-
- if (a->nrels != b->nrels)
- return a->nrels - b->nrels;
-
- for (i = 0; i < a->nrels; i++)
- {
- int result;
- if ((result = RelnameCmp(&a->relnames[i], &b->relnames[i])) != 0)
- return result;
- }
-
- return 0;
-}
-
-static int
-LeadingHintCmp(const LeadingHint *a, const LeadingHint *b)
-{
- return 0;
-}
-
-static int
-SetHintCmp(const SetHint *a, const SetHint *b)
-{
- return strcmp(a->name, b->name);
-}
-
-static int
-AllHintCmp(const void *a, const void *b, bool order)
-{
- const Hint *hinta = *((const Hint **) a);
- const Hint *hintb = *((const Hint **) b);
- int result = 0;
-
- if (hinta->type != hintb->type)
- return hinta->type - hintb->type;
-
- if ((result = hinta->cmp_func(hinta, hintb)) != 0 || !order)
- return result;
-
- /* ヒント句で指定した順を返す */
- return hinta->hint_str - hintb->hint_str;
-}
-
-static int
-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
+ScanMethodHintCmp(const ScanMethodHint *a, const ScanMethodHint *b)
+{
+ return RelnameCmp(&a->relname, &b->relname);
+}
static int
-set_config_options(SetHint **options, int noptions, GucContext context)
+JoinMethodHintCmp(const JoinMethodHint *a, const JoinMethodHint *b)
{
int i;
- int save_nestlevel;
- save_nestlevel = NewGUCNestLevel();
+ if (a->nrels != b->nrels)
+ return a->nrels - b->nrels;
- for (i = 0; i < noptions; i++)
+ for (i = 0; i < a->nrels; 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;
+ int result;
+ if ((result = RelnameCmp(&a->relnames[i], &b->relnames[i])) != 0)
+ return result;
}
- return save_nestlevel;
+ return 0;
}
-#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 int
+LeadingHintCmp(const LeadingHint *a, const LeadingHint *b)
+{
+ return 0;
+}
-static void
-set_scan_config_options(unsigned char enforce_mask, GucContext context)
+static int
+SetHintCmp(const SetHint *a, const SetHint *b)
{
- 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_INDEXONLYSCAN);
-#endif
+ return strcmp(a->name, b->name);
}
-static void
-set_join_config_options(unsigned char enforce_mask, GucContext context)
+static int
+AllHintCmp(const void *a, const void *b, bool order)
{
- SET_CONFIG_OPTION("enable_nestloop", ENABLE_NESTLOOP);
- SET_CONFIG_OPTION("enable_mergejoin", ENABLE_MERGEJOIN);
- SET_CONFIG_OPTION("enable_hashjoin", ENABLE_HASHJOIN);
+ const Hint *hinta = *((const Hint **) a);
+ const Hint *hintb = *((const Hint **) b);
+ int result = 0;
+
+ if (hinta->type != hintb->type)
+ return hinta->type - hintb->type;
+
+ if ((result = hinta->cmp_func(hinta, hintb)) != 0 || !order)
+ return result;
+
+ /* ヒント句で指定した順を返す */
+ return hinta->hint_str - hintb->hint_str;
+}
+
+static int
+AllHintCmpIsOrder(const void *a, const void *b)
+{
+ return AllHintCmp(a, b, true);
}
/*
return str;
}
+/*
+ * set GUC parameter functions
+ */
+
+#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_INDEXONLYSCAN);
+#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);
+}
+
+/*
+ * pg_hint_plan hook functions
+ */
+
+static void
+ProcessUtility_hook_error_callback(void *arg)
+{
+ stmt_name = NULL;
+}
+
+static 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;
+ }
+}
+
static PlannedStmt *
pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
{