* pg_hint_plan.c
* hinting on how to execute a query for PostgreSQL
*
- * Copyright (c) 2012-2019, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
+ * Copyright (c) 2012-2020, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "partitioning/partbounds.h"
#include "tcop/utility.h"
#include "utils/builtins.h"
+#include "utils/float.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/rel.h"
int init_min_para_tablescan_size;
/* min_parallel_index_scan_size*/
int init_min_para_indexscan_size;
- int init_paratup_cost; /* parallel_tuple_cost */
- int init_parasetup_cost;/* parallel_setup_cost */
+ double init_paratup_cost; /* parallel_tuple_cost */
+ double init_parasetup_cost;/* parallel_setup_cost */
PlannerInfo *current_root; /* PlannerInfo for the followings */
Index parent_relid; /* inherit parent of table relid */
const char *queryString,
ProcessUtilityContext context,
ParamListInfo params, QueryEnvironment *queryEnv,
- DestReceiver *dest, char *completionTag);
-static PlannedStmt *pg_hint_plan_planner(Query *parse, int cursorOptions,
+ DestReceiver *dest, QueryCompletion *qc);
+static PlannedStmt *pg_hint_plan_planner(Query *parse, const char *query_string,
+ int cursorOptions,
ParamListInfo boundParams);
static RelOptInfo *pg_hint_plan_join_search(PlannerInfo *root,
int levels_needed,
static void create_plain_partial_paths(PlannerInfo *root,
RelOptInfo *rel);
static void make_rels_by_clause_joins(PlannerInfo *root, RelOptInfo *old_rel,
+ List *other_rels_list,
ListCell *other_rels);
static void make_rels_by_clauseless_joins(PlannerInfo *root,
RelOptInfo *old_rel,
- ListCell *other_rels);
+ List *other_rels);
static bool has_join_restriction(PlannerInfo *root, RelOptInfo *rel);
static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
RangeTblEntry *rte);
HintState *state);
static int set_config_int32_option(const char *name, int32 value,
GucContext context);
+static int set_config_double_option(const char *name, double value,
+ GucContext context);
/* GUC variables */
static bool pg_hint_plan_enable_hint = true;
{
ParallelHint *hint;
- hint = palloc(sizeof(ScanMethodHint));
+ hint = palloc(sizeof(ParallelHint));
hint->base.hint_str = hint_str;
hint->base.keyword = keyword;
hint->base.hint_keyword = hint_keyword;
quote_value(buf, hint->relnames[i]);
}
}
- appendStringInfo(buf, " %s", hint->rows_str);
+ if (hint->rows_str != NULL)
+ appendStringInfo(buf, " %s", hint->rows_str);
appendStringInfoString(buf, ")");
if (!nolf)
appendStringInfoChar(buf, '\n');
* case of DESCRIBE message handling or EXECUTE command. We may still see a
* candidate top-level query in pstate in the case.
*/
- if (!p && pstate)
+ if (pstate && pstate->p_sourcetext)
p = pstate->p_sourcetext;
/* We don't see a query string, return NULL */
PreparedStatement *entry;
entry = FetchPreparedStatement(stmt->name, true);
- p = entry->plansource->query_string;
- target_query = (Query *) linitial (entry->plansource->query_list);
+
+ if (entry->plansource->is_valid)
+ {
+ p = entry->plansource->query_string;
+ target_query = (Query *) linitial (entry->plansource->query_list);
+ }
+ else
+ {
+ /* igonre the hint for EXECUTE if invalidated */
+ p = NULL;
+ target_query = NULL;
+ }
}
/* JumbleQuery accespts only a non-utility Query */
- if (!IsA(target_query, Query) ||
- target_query->utilityStmt != NULL)
+ if (target_query &&
+ (!IsA(target_query, Query) ||
+ target_query->utilityStmt != NULL))
target_query = NULL;
if (jumblequery)
hstate->num_hints[HINT_TYPE_LEADING]);
hstate->rows_hints = (RowsHint **) (hstate->set_hints +
hstate->num_hints[HINT_TYPE_SET]);
- hstate->parallel_hints = (ParallelHint **) (hstate->set_hints +
+ hstate->parallel_hints = (ParallelHint **) (hstate->rows_hints +
hstate->num_hints[HINT_TYPE_ROWS]);
return hstate;
List *name_list = NIL;
char *rows_str;
char *end_ptr;
+ ListCell *l;
+ int i = 0;
if ((str = parse_parentheses(str, &name_list, hint_keyword)) == NULL)
return NULL;
/* Last element must be rows specification */
hint->nrels = list_length(name_list) - 1;
- if (hint->nrels > 0)
+ if (hint->nrels < 1)
{
- ListCell *l;
- int i = 0;
+ hint_ereport(str,
+ ("%s hint needs at least one relation followed by one correction term.",
+ hint->base.keyword));
+ hint->base.state = HINT_STATE_ERROR;
- /*
- * Transform relation names from list to array to sort them with qsort
- * after.
- */
- hint->relnames = palloc(sizeof(char *) * hint->nrels);
- foreach (l, name_list)
- {
- if (hint->nrels <= i)
- break;
- hint->relnames[i] = lfirst(l);
- i++;
- }
+ return str;
+ }
+
+
+ /*
+ * Transform relation names from list to array to sort them with qsort
+ * after.
+ */
+ hint->relnames = palloc(sizeof(char *) * hint->nrels);
+ foreach (l, name_list)
+ {
+ if (hint->nrels <= i)
+ break;
+ hint->relnames[i] = lfirst(l);
+ i++;
}
/* Retieve rows estimation */
pg_hint_plan_parse_message_level);
}
+/*
+ * Sets GUC parameter of double type without throwing exceptions. Returns false
+ * if something wrong.
+ */
+static int
+set_config_double_option(const char *name, double value, GucContext context)
+{
+ char *buf = float8out_internal(value);
+ int result;
+
+ result = set_config_option_noerror(name, buf, context,
+ PGC_S_SESSION, GUC_ACTION_SAVE, true,
+ pg_hint_plan_parse_message_level);
+ pfree(buf);
+ return result;
+}
+
/* setup scan method enforcement according to given options */
static void
setup_guc_enforcement(SetHint **options, int noptions, GucContext context)
/* force means that enforce parallel as far as possible */
if (hint && hint->force_parallel && hint->nworkers > 0)
{
- set_config_int32_option("parallel_tuple_cost", 0, state->context);
- set_config_int32_option("parallel_setup_cost", 0, state->context);
+ set_config_double_option("parallel_tuple_cost", 0.0, state->context);
+ set_config_double_option("parallel_setup_cost", 0.0, state->context);
set_config_int32_option("min_parallel_table_scan_size", 0,
state->context);
set_config_int32_option("min_parallel_index_scan_size", 0,
}
else
{
- set_config_int32_option("parallel_tuple_cost",
+ set_config_double_option("parallel_tuple_cost",
state->init_paratup_cost, state->context);
- set_config_int32_option("parallel_setup_cost",
+ set_config_double_option("parallel_setup_cost",
state->init_parasetup_cost, state->context);
set_config_int32_option("min_parallel_table_scan_size",
state->init_min_para_tablescan_size,
*/
query_len = strlen(query_str) + 1;
normalized_query =
- generate_normalized_query(&jstate, query_str,
- query->stmt_location,
- &query_len,
+ generate_normalized_query(&jstate, query_str, 0, &query_len,
GetDatabaseEncoding());
/*
current_hint_str = get_hints_from_comment(query_str);
MemoryContextSwitchTo(oldcontext);
}
+ else
+ {
+ /*
+ * Failed to get query. We would be in fetching invalidated
+ * plancache. Try the next chance.
+ */
+ current_hint_retrieved = false;
+ }
if (debug_level > 1)
{
pg_hint_plan_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context,
ParamListInfo params, QueryEnvironment *queryEnv,
- DestReceiver *dest, char *completionTag)
+ DestReceiver *dest, QueryCompletion *qc)
{
if (prev_ProcessUtility_hook)
prev_ProcessUtility_hook(pstmt, queryString, context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
else
standard_ProcessUtility(pstmt, queryString, context, params, queryEnv,
- dest, completionTag);
+ dest, qc);
if (plpgsql_recurse_level == 0)
current_hint_retrieved = false;
* Read and set up hint information
*/
static PlannedStmt *
-pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
+pg_hint_plan_planner(Query *parse, const char *query_string, int cursorOptions, ParamListInfo boundParams)
{
int save_nestlevel;
PlannedStmt *result;
HintState *hstate;
- const char *prev_hint_str;
+ const char *prev_hint_str = NULL;
/*
* Use standard planner if pg_hint_plan is disabled or current nesting
* Support for nested plpgsql functions. This is quite ugly but this is the
* only point I could find where I can get the query string.
*/
- if (plpgsql_recurse_level > 0)
+ if (plpgsql_recurse_level > 0 &&
+ error_context_stack && error_context_stack->arg)
{
MemoryContext oldcontext;
*/
recurse_level++;
prev_hint_str = current_hint_str;
+ current_hint_str = NULL;
/*
* Use PG_TRY mechanism to recover GUC parameters and current_hint_state to
PG_TRY();
{
if (prev_planner)
- result = (*prev_planner) (parse, cursorOptions, boundParams);
+ result = (*prev_planner) (parse, query_string,
+ cursorOptions, boundParams);
else
- result = standard_planner(parse, cursorOptions, boundParams);
+ result = standard_planner(parse, query_string,
+ cursorOptions, boundParams);
current_hint_str = prev_hint_str;
recurse_level--;
}
current_hint_state = NULL;
if (prev_planner)
- return (*prev_planner) (parse, cursorOptions, boundParams);
+ result = (*prev_planner) (parse, query_string,
+ cursorOptions, boundParams);
else
- return standard_planner(parse, cursorOptions, boundParams);
+ result = standard_planner(parse, query_string,
+ cursorOptions, boundParams);
+
+ /* The upper-level planner still needs the current hint state */
+ if (HintStateStack != NIL)
+ current_hint_state = (HintState *) lfirst(list_head(HintStateStack));
+
+ return result;
}
/*
bool using_parent_hint)
{
ListCell *cell;
- ListCell *prev;
ListCell *next;
StringInfoData buf;
RangeTblEntry *rte = root->simple_rte_array[rel->relid];
* Leaving only an specified index, we delete it from a IndexOptInfo list
* other than it.
*/
- prev = NULL;
if (debug_level > 0)
initStringInfo(&buf);
ListCell *l;
bool use_index = false;
- next = lnext(cell);
-
+ next = lnext(rel->indexlist, cell);
foreach(l, hint->indexnames)
{
char *hintname = (char *) lfirst(l);
}
if (!use_index)
- rel->indexlist = list_delete_cell(rel->indexlist, cell, prev);
- else
- prev = cell;
+ {
+ rel->indexlist = list_delete_cell(rel->indexlist, cell);
+ /*
+ * the elements after cell moved to the list head by 1 element.
+ * the next iteration should visit the cell at the same address if
+ * any.
+ */
+ if (next)
+ next = cell;
+ }
+
pfree(indexname);
}
return 0;
}
- /* Forget about the parent of another subquery */
- if (root != current_hint_state->current_root)
- current_hint_state->parent_relid = 0;
-
- /* Find the parent for this relation other than the registered parent */
- foreach (l, root->append_rel_list)
+ /*
+ * Forget about the parent of another subquery, but don't forget if the
+ * inhTargetkind of the root is not INHKIND_NONE, which signals the root
+ * contains only appendrel members. See inheritance_planner for details.
+ *
+ * (PG12.0) 428b260f87 added one more planning cycle for updates on
+ * partitioned tables and hints set up in the cycle are overriden by the
+ * second cycle. Since I didn't find no apparent distinction between the
+ * PlannerRoot of the cycle and that of ordinary CMD_SELECT, pg_hint_plan
+ * accepts both cycles and the later one wins. In the second cycle root
+ * doesn't have inheritance information at all so use the parent_relid set
+ * in the first cycle.
+ */
+ if (root->inhTargetKind == INHKIND_NONE)
{
- AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
+ if (root != current_hint_state->current_root)
+ current_hint_state->parent_relid = 0;
- if (appinfo->child_relid == rel->relid)
+ /* Find the parent for this relation other than the registered parent */
+ foreach (l, root->append_rel_list)
{
- if (current_hint_state->parent_relid != appinfo->parent_relid)
+ AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
+
+ if (appinfo->child_relid == rel->relid)
{
- new_parent_relid = appinfo->parent_relid;
- current_hint_state->current_root = root;
+ if (current_hint_state->parent_relid != appinfo->parent_relid)
+ {
+ new_parent_relid = appinfo->parent_relid;
+ current_hint_state->current_root = root;
+ }
+ break;
}
- break;
}
- }
- if (!l)
- {
- /* This relation doesn't have a parent. Cancel current_hint_state. */
- current_hint_state->parent_relid = 0;
- current_hint_state->parent_scan_hint = NULL;
- current_hint_state->parent_parallel_hint = NULL;
+ if (!l)
+ {
+ /*
+ * This relation doesn't have a parent. Cancel
+ * current_hint_state.
+ */
+ current_hint_state->parent_relid = 0;
+ current_hint_state->parent_scan_hint = NULL;
+ current_hint_state->parent_parallel_hint = NULL;
+ }
}
if (new_parent_relid > 0)
parentrel_oid =
root->simple_rte_array[current_hint_state->parent_relid]->relid;
- parent_rel = heap_open(parentrel_oid, NoLock);
+ parent_rel = table_open(parentrel_oid, NoLock);
/* Search the parent relation for indexes match the hint spec */
foreach(l, RelationGetIndexList(parent_rel))
lappend(current_hint_state->parent_index_infos,
parent_index_info);
}
- heap_close(parent_rel, NoLock);
+ table_close(parent_rel, NoLock);
}
}
}
leading_hint->base.hint_str));
}
- outer_rels = lfirst(outer_inner->outer_inner_pair->head);
- inner_rels = lfirst(outer_inner->outer_inner_pair->tail);
+ outer_rels = linitial(outer_inner->outer_inner_pair);
+ inner_rels = llast(outer_inner->outer_inner_pair);
outer_relids = OuterInnerJoinCreate(outer_rels,
leading_hint,
{
if (hstate->join_hint_level[i] != NIL)
{
- ListCell *prev = NULL;
ListCell *next = NULL;
for(l = list_head(hstate->join_hint_level[i]); l; l = next)
{
JoinMethodHint *hint = (JoinMethodHint *)lfirst(l);
- next = lnext(l);
+ next = lnext(hstate->join_hint_level[i], l);
if (hint->inner_nrels == 0 &&
!(bms_intersect(hint->joinrelids, joinrelids) == NULL ||
hint->joinrelids)))
{
hstate->join_hint_level[i] =
- list_delete_cell(hstate->join_hint_level[i], l,
- prev);
+ list_delete_cell(hstate->join_hint_level[i], l);
}
- else
- prev = l;
}
}
}