#define HINT_END "*/"
/* hint keywords */
-#define HINT_SEQSCAN "SeqScan"
-#define HINT_INDEXSCAN "IndexScan"
-#define HINT_BITMAPSCAN "BitmapScan"
-#define HINT_TIDSCAN "TidScan"
-#define HINT_NOSEQSCAN "NoSeqScan"
-#define HINT_NOINDEXSCAN "NoIndexScan"
-#define HINT_NOBITMAPSCAN "NoBitmapScan"
-#define HINT_NOTIDSCAN "NoTidScan"
-#define HINT_NESTLOOP "NestLoop"
-#define HINT_MERGEJOIN "MergeJoin"
-#define HINT_HASHJOIN "HashJoin"
-#define HINT_NONESTLOOP "NoNestLoop"
-#define HINT_NOMERGEJOIN "NoMergeJoin"
-#define HINT_NOHASHJOIN "NoHashJoin"
-#define HINT_LEADING "Leading"
-#define HINT_SET "Set"
-
+#define HINT_SEQSCAN "SeqScan"
+#define HINT_INDEXSCAN "IndexScan"
+#define HINT_BITMAPSCAN "BitmapScan"
+#define HINT_TIDSCAN "TidScan"
+#define HINT_NOSEQSCAN "NoSeqScan"
+#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"
+#define HINT_INDEXONLYSCAN "IndexonlyScan"
+#define HINT_NOINDEXONLYSCAN "NoIndexonlyScan"
#endif
+#define HINT_NESTLOOP "NestLoop"
+#define HINT_MERGEJOIN "MergeJoin"
+#define HINT_HASHJOIN "HashJoin"
+#define HINT_NONESTLOOP "NoNestLoop"
+#define HINT_NOMERGEJOIN "NoMergeJoin"
+#define HINT_NOHASHJOIN "NoHashJoin"
+#define HINT_LEADING "Leading"
+#define HINT_SET "Set"
+
#define HINT_ARRAY_DEFAULT_INITSIZE 8
char *value;
} SetHint;
+/*
+ * Describes a context of hint processing.
+ */
typedef struct PlanHint
{
- char *hint_str;
+ char *hint_str; /* original hint string */
- int nscan_hints;
- int max_scan_hints;
- ScanHint **scan_hints;
+ /* for scan method hints */
+ int nscan_hints; /* # of valid scan hints */
+ int max_scan_hints; /* # of slots for scan hints */
+ ScanHint **scan_hints; /* parsed scan hints */
- int njoin_hints;
- int max_join_hints;
- JoinHint **join_hints;
+ /* for join method hints */
+ int njoin_hints; /* # of valid join hints */
+ int max_join_hints; /* # of slots for join hints */
+ JoinHint **join_hints; /* parsed join hints */
- int nlevel;
+ int nlevel; /* # of relations to be joined */
List **join_hint_level;
- List *leading;
+ /* for Leading hints */
+ List *leading; /* relation names specified in Leading hint */
- GucContext context;
- List *set_hints;
+ /* for Set hints */
+ GucContext context; /* which GUC parameters can we set? */
+ List *set_hints; /* parsed Set hints */
} PlanHint;
typedef const char *(*HintParserFunction) (PlanHint *plan, Query *parse, char *keyword, const char *str);
+/*
+ * Describes a hint parser module which is bound with particular hint keyword.
+ */
typedef struct HintParser
{
char *keyword;
List *initial_rels);
static const char *ParseScanMethod(PlanHint *plan, Query *parse, char *keyword, const char *str);
-static const char *ParseIndexScanMethod(PlanHint *plan, Query *parse, char *keyword, const char *str);
static const char *ParseJoinMethod(PlanHint *plan, Query *parse, char *keyword, const char *str);
static const char *ParseLeading(PlanHint *plan, Query *parse, char *keyword, const char *str);
static const char *ParseSet(PlanHint *plan, Query *parse, char *keyword, const char *str);
static const HintParser parsers[] = {
{HINT_SEQSCAN, true, ParseScanMethod},
- {HINT_INDEXSCAN, true, ParseIndexScanMethod},
- {HINT_BITMAPSCAN, true, ParseIndexScanMethod},
+ {HINT_INDEXSCAN, true, ParseScanMethod},
+ {HINT_BITMAPSCAN, true, ParseScanMethod},
{HINT_TIDSCAN, true, ParseScanMethod},
{HINT_NOSEQSCAN, true, ParseScanMethod},
{HINT_NOINDEXSCAN, true, ParseScanMethod},
{HINT_NOBITMAPSCAN, true, ParseScanMethod},
{HINT_NOTIDSCAN, true, ParseScanMethod},
#if PG_VERSION_NUM >= 90200
- {HINT_INDEXONLYSCAN, true, ParseIndexScanMethod},
- {HINT_NOINDEXONLYSCAN, true, ParseIndexScanMethod},
+ {HINT_INDEXONLYSCAN, true, ParseScanMethod},
+ {HINT_NOINDEXONLYSCAN, true, ParseScanMethod},
#endif
{HINT_NESTLOOP, true, ParseJoinMethod},
{HINT_MERGEJOIN, true, ParseJoinMethod},
/*
* Module unload callback
+ * XXX never called
*/
void
_PG_fini(void)
return false;
}
-// TODO オブジェクト名のクォート処理を追加
+/* TODO オブジェクト名のクォート処理を追加 */
static void
PlanHintDump(PlanHint *hint)
{
const ScanHint *hintb = *((const ScanHint **) b);
int result;
- if ((result = strcmp(hinta->relname, hintb->relname)) != 0)
+ if ((result = RelnameCmp(&hinta->relname, &hintb->relname)) != 0)
return result;
/* ヒント句で指定した順を返す */
for (i = 0; i < hinta->nrels; i++)
{
int result;
- if ((result = strcmp(hinta->relnames[i], hintb->relnames[i])) != 0)
+ if ((result = RelnameCmp(&hinta->relnames[i], &hintb->relnames[i])) != 0)
return result;
}
return str;
}
+/*
+ * 二重引用符で囲まれているかもしれないトークンを読み取り word 引数に palloc
+ * で確保したバッファに格納してそのポインタを返す。
+ *
+ * 正常にパースできた場合は残りの文字列の先頭位置を、異常があった場合は NULL を
+ * 返す。
+ */
static const char *
parse_quote_value(const char *str, char **word, char *value_type)
{
StringInfoData buf;
bool in_quote;
+ /* 先頭のスペースは読み飛ばす。 */
skip_space(str);
initStringInfo(&buf);
in_quote = true;
}
else
- {
- /*
- * 1文字目以降の制限の適用
- */
- if (!isalpha(*str) && *str != '_')
- {
- pfree(buf.data);
- parse_ereport(str, ("Need for %s to be quoted.", value_type));
- return NULL;
- }
-
in_quote = false;
- appendStringInfoCharMacro(&buf, *str++);
- }
while (true)
{
if (in_quote)
{
+ /* 二重引用符が閉じられていない場合はパース中断 */
if (*str == '\0')
{
pfree(buf.data);
}
/*
- * エスケープ対象をスキップする。
- * TODO エスケープ対象の仕様にあわせた処理を行う。
+ * エスケープ対象のダブルクウォートをスキップする。
+ * もしブロックコメントの開始文字列や終了文字列もオブジェクト名とし
+ * て使用したい場合は、/ と * もエスケープ対象とすることで使用できる
+ * が、処理対象としていない。もしテーブル名にこれらの文字が含まれる
+ * 場合は、エイリアスを指定する必要がある。
*/
if(*str == '"')
{
}
}
else
- {
- /*
- * 2文字目以降の制限の適用
- */
- if (!isalnum(*str) && *str != '_' && *str != '$')
+ if (isspace(*str) || *str == ')' || *str == '\0')
break;
- }
appendStringInfoCharMacro(&buf, *str++);
}
if (p == NULL)
return NULL;
-
/* extract query head comment. */
len = strlen(HINT_START);
skip_space(p);
skip_space(p);
if ((tail = strstr(p, HINT_END)) == NULL)
- elog(ERROR, "unterminated /* comment at or near \"%s\"",
- debug_query_string);
+ {
+ parse_ereport(debug_query_string, ("unterminated /* comment"));
+ return NULL;
+ }
/* 入れ子にしたブロックコメントはサポートしない */
if ((head = strstr(p, HINT_START)) != NULL && head < tail)
return plan;
}
+/*
+ * スキャン方式ヒントのカッコ内をパースする
+ */
static const char *
-parse_scan_method(PlanHint *plan, Query *parse, char *keyword, const char *str)
+ParseScanMethod(PlanHint *plan, Query *parse, char *keyword, const char *str)
{
ScanHint *hint;
hint = ScanHintCreate();
hint->opt_str = str;
+ /*
+ * スキャン方式のヒントでリレーション名が読み取れない場合はヒント無効
+ */
if ((str = parse_quote_value(str, &hint->relname, "ralation name")) == NULL)
{
ScanHintDelete(hint);
return NULL;
}
+ /*
+ * インデックスリストを受け付けるヒントであれば、インデックス参照をパース
+ * する。
+ */
+ if (strcmp(keyword, HINT_INDEXSCAN) == 0 ||
+#if PG_VERSION_NUM >= 90200
+ strcmp(keyword, HINT_INDEXONLYSCAN) == 0 ||
+#endif
+ strcmp(keyword, HINT_BITMAPSCAN) == 0)
+ {
+ skip_space(str);
+ while (*str != ')' && *str != '\0')
+ {
+ char *indexname;
+
+ str = parse_quote_value(str, &indexname, "index name");
+ if (str == NULL)
+ {
+ ScanHintDelete(hint);
+ return NULL;
+ }
+
+ hint->indexnames = lappend(hint->indexnames, indexname);
+ skip_space(str);
+ }
+ }
+
+ /* カッコが閉じていなければヒント無効。 */
+ skip_space(str);
+ if (*str != ')')
+ {
+ parse_ereport(str, ("Closed parenthesis is necessary."));
+ ScanHintDelete(hint);
+ return NULL;
+ }
+
+ /*
+ * ヒントごとに決まっている許容スキャン方式をビットマスクとして設定
+ */
if (strcasecmp(keyword, HINT_SEQSCAN) == 0)
hint->enforce_mask = ENABLE_SEQSCAN;
else if (strcasecmp(keyword, HINT_INDEXSCAN) == 0)
hint->enforce_mask = ENABLE_ALL_SCAN ^ ENABLE_TIDSCAN;
else
{
- elog(ERROR, "unrecognized hint keyword \"%s\"", keyword);
+ ScanHintDelete(hint);
+ parse_ereport(str, ("unrecognized hint keyword \"%s\"", keyword));
return NULL;
}
+ /*
+ * 出来上がったヒント情報を追加。スロットが足りない場合は二倍に拡張する。
+ */
if (plan->nscan_hints == 0)
{
plan->max_scan_hints = HINT_ARRAY_DEFAULT_INITSIZE;
- plan->scan_hints = palloc(sizeof(JoinHint *) * plan->max_scan_hints);
+ plan->scan_hints = palloc(sizeof(ScanHint *) * plan->max_scan_hints);
}
else if (plan->nscan_hints == plan->max_scan_hints)
{
plan->max_scan_hints *= 2;
plan->scan_hints = repalloc(plan->scan_hints,
- sizeof(JoinHint *) * plan->max_scan_hints);
+ sizeof(ScanHint *) * plan->max_scan_hints);
}
-
plan->scan_hints[plan->nscan_hints] = hint;
plan->nscan_hints++;
}
static const char *
-ParseScanMethod(PlanHint *plan, Query *parse, char *keyword, const char *str)
-{
- ScanHint *hint;
-
- if ((str = parse_scan_method(plan, parse, keyword, str)) == NULL)
- return NULL;
-
- hint = plan->scan_hints[plan->nscan_hints - 1];
-
- skip_space(str);
- if (*str != ')')
- {
- parse_ereport(str, ("Closed parenthesis is necessary."));
- plan->nscan_hints--;
- ScanHintDelete(hint);
- return NULL;
- }
-
- return str;
-}
-
-static const char *
-ParseIndexScanMethod(PlanHint *plan, Query *parse, char *keyword, const char *str)
-{
- char *indexname;
- ScanHint *hint;
-
- if ((str = parse_scan_method(plan, parse, keyword, str)) == NULL)
- return NULL;
-
- hint = plan->scan_hints[plan->nscan_hints - 1];
-
- /* インデックス参照をパースする。 */
- while (true)
- {
- // TODO 直前のオブジェクト名がクウォート処理されていた場合の処理を実装
- skip_space(str);
- if (*str == ')')
- break;
-
- if ((str = parse_quote_value(str, &indexname, "index name")) == NULL)
- {
- plan->nscan_hints--;
- ScanHintDelete(hint);
- return NULL;
- }
-
- hint->indexnames = lappend(hint->indexnames, indexname);
- }
-
- return str;
-}
-
-static const char *
ParseJoinMethod(PlanHint *plan, Query *parse, char *keyword, const char *str)
{
char *relname;
else if (strcasecmp(keyword, HINT_NOHASHJOIN) == 0)
hint->enforce_mask = ENABLE_ALL_JOIN ^ ENABLE_HASHJOIN;
else
- elog(ERROR, "unrecognized hint keyword \"%s\"", keyword);
+ {
+ JoinHintDelete(hint);
+ parse_ereport(str, ("unrecognized hint keyword \"%s\"", keyword));
+ return NULL;
+ }
if (plan->njoin_hints == 0)
{
{
ScanHint *hint = global->scan_hints[i];
- if (strcmp(hint->relname, rte->eref->aliasname) != 0)
+ if (RelnameCmp(&rte->eref->aliasname, &hint->relname) != 0)
parse_ereport(hint->opt_str, ("Relation \"%s\" does not exist.", hint->relname));
set_scan_config_options(hint->enforce_mask, global->context);
if (pg_hint_plan_debug_print)
{
PlanHintDump(global);
- //elog_node_display(INFO, "rtable", parse->rtable, true);
+#ifdef NOT_USED
+ elog_node_display(INFO, "rtable", parse->rtable, true);
+#endif
}
PlanHintDelete(global);
{
ScanHint *hint = global->scan_hints[i];
- if (strcmp(rte->eref->aliasname, hint->relname) == 0)
+ if (RelnameCmp(&rte->eref->aliasname, &hint->relname) == 0)
return hint;
}
foreach(l, hint->indexnames)
{
- if (strcmp(indexname, lfirst(l)) == 0)
+ if (RelnameCmp(&indexname, &lfirst(l)) == 0)
{
use_index = true;
break;
static Index
scan_relid_aliasname(PlannerInfo *root, char *aliasname, bool check_ambiguous, const char *str)
{
- // TODO refnameRangeTblEntry を参考
+ /* TODO refnameRangeTblEntry を参考 */
int i;
Index find = 0;
Assert(i == root->simple_rel_array[i]->relid);
- if (strcmp(aliasname, root->simple_rte_array[i]->eref->aliasname) != 0)
+ if (RelnameCmp(&aliasname, &root->simple_rte_array[i]->eref->aliasname)
+ != 0)
continue;
if (!check_ambiguous)
plan->join_hint_level[njoinrels] = lappend(NIL, hint);
else
{
+ /*
+ * Here relnames is not set, since Relids bitmap is sufficient to
+ * control paths of this query afterwards.
+ */
hint = JoinHintCreate();
hint->nrels = njoinrels;
hint->enforce_mask = ENABLE_ALL_JOIN;
RangeTblEntry *rte = root->simple_rte_array[rel->relid];
if (rel->reloptkind != RELOPT_BASEREL ||
- strcmp(hint->relname, rte->eref->aliasname) != 0)
+ RelnameCmp(&hint->relname, &rte->eref->aliasname) != 0)
continue;
if (save_nestlevel != 0)
*/
set_scan_config_options(hint->enforce_mask, plan->context);
- rel->pathlist = NIL; // TODO 解放
+ rel->pathlist = NIL; /* TODO 解放 */
set_plain_rel_pathlist(root, rel, rte);
break;
static RelOptInfo *
pg_hint_plan_join_search(PlannerInfo *root, int levels_needed, List *initial_rels)
{
- /* 有効なヒントが指定されなかった場合は本来の処理を実行する。 */
+ /*
+ * pg_hint_planが無効、または有効なヒントが1つも指定されなかった場合は、標準
+ * の処理を行う。
+ */
if (!global)
{
if (prev_join_search)
rebuild_join_hints(global, root, levels_needed, initial_rels);
rebuild_scan_path(global, root, levels_needed, initial_rels);
+ /*
+ * GEQOを使用する条件を満たした場合は、GEQOを用いた結合方式の検索を行う。
+ * このとき、スキャン方式のヒントとSetヒントのみが有効になり、結合方式や結合
+ * 順序はヒント句は無効になりGEQOのアルゴリズムで決定される。
+ */
+ if (enable_geqo && levels_needed >= geqo_threshold)
+ return geqo(root, levels_needed, initial_rels);
+
return standard_join_search_org(root, levels_needed, initial_rels);
}