path->rows = clamp_row_est(path->rows / parallel_divisor);
}
- path->disabled_nodes = enable_seqscan ? 0 : 1;
+ path->disabled_nodes = (baserel->ssa_mask & SSA_SEQSCAN) != 0 ? 0 : 1;
path->startup_cost = startup_cost;
path->total_cost = startup_cost + cpu_run_cost + disk_run_cost;
}
double pages_fetched;
double rand_heap_pages;
double index_pages;
+ bool enabled;
/* Should only be applied to base relations */
Assert(IsA(baserel, RelOptInfo) &&
path->indexclauses);
}
- /* we don't need to check enable_indexonlyscan; indxpath.c does that */
- path->path.disabled_nodes = enable_indexscan ? 0 : 1;
+ /* is this scan type disabled? */
+ if (indexonly)
+ enabled = (baserel->ssa_mask & SSA_INDEXONLYSCAN) ? 1 : 0;
+ else
+ enabled = (baserel->ssa_mask & SSA_INDEXSCAN) ? 1 : 0;
+ path->path.disabled_nodes = enabled ? 0 : 1;
/*
* Call index-access-method-specific code to estimate the processing cost
startup_cost += path->pathtarget->cost.startup;
run_cost += path->pathtarget->cost.per_tuple * path->rows;
- path->disabled_nodes = enable_bitmapscan ? 0 : 1;
+ path->disabled_nodes = (baserel->ssa_mask & SSA_BITMAPSCAN) != 0 ? 0 : 1;
path->startup_cost = startup_cost;
path->total_cost = startup_cost + run_cost;
}
/*
* We must use a TID scan for CurrentOfExpr; in any other case, we
- * should be generating a TID scan only if enable_tidscan=true. Also,
+ * should be generating a TID scan only if TID scans are allowed. Also,
* if CurrentOfExpr is the qual, there should be only one.
*/
- Assert(enable_tidscan || IsA(qual, CurrentOfExpr));
+ Assert((baserel->ssa_mask & SSA_TIDSCAN) != 0 || IsA(qual, CurrentOfExpr));
Assert(list_length(tidquals) == 1 || !IsA(qual, CurrentOfExpr));
if (IsA(qual, ScalarArrayOpExpr))
/*
* There are assertions above verifying that we only reach this function
- * either when enable_tidscan=true or when the TID scan is the only legal
- * path, so it's safe to set disabled_nodes to zero here.
+ * either when baserel->ssa_mask includes SSA_TIDSCAN or when the TID scan
+ * is the only legal path, so it's safe to set disabled_nodes to zero here.
*/
path->disabled_nodes = 0;
path->startup_cost = startup_cost;
startup_cost += path->pathtarget->cost.startup;
run_cost += path->pathtarget->cost.per_tuple * path->rows;
- /* we should not generate this path type when enable_tidscan=false */
- Assert(enable_tidscan);
+ /* we should not generate this path type when TID scans are disabled */
+ Assert((baserel->ssa_mask & SSA_TIDSCAN) != 0);
path->disabled_nodes = 0;
path->startup_cost = startup_cost;
path->total_cost = startup_cost + run_cost;
ListCell *lc;
int i;
- /* Index-only scans must be enabled */
- if (!enable_indexonlyscan)
+ /* If we're not allowed to consider index-only scans, give up now */
+ if ((rel->ssa_mask & SSA_CONSIDER_INDEXONLY) == 0)
return false;
/*
List *tidquals;
List *tidrangequals;
bool isCurrentOf;
+ bool enabled = (rel->ssa_mask & SSA_TIDSCAN) != 0;
/*
* If any suitable quals exist in the rel's baserestrict list, generate a
* plain (unparameterized) TidPath with them.
*
- * We skip this when enable_tidscan = false, except when the qual is
+ * We skip this when TID scans are disabled, except when the qual is
* CurrentOfExpr. In that case, a TID scan is the only correct path.
*/
tidquals = TidQualFromRestrictInfoList(root, rel->baserestrictinfo, rel,
&isCurrentOf);
- if (tidquals != NIL && (enable_tidscan || isCurrentOf))
+ if (tidquals != NIL && (enabled || isCurrentOf))
{
/*
* This path uses no join clauses, but it could still have required
}
/* Skip the rest if TID scans are disabled. */
- if (!enable_tidscan)
+ if (!enabled)
return false;
/*
tuple_fraction = 0.0;
}
+ /*
+ * Compute the initial scan strategy advice mask.
+ *
+ * It may seem surprising that enable_indexscan sets both SSA_INDEXSCAN
+ * and SSA_INDEXONLYSCAN. However, the historical behavior of this GUC
+ * corresponds to this exactly: enable_indexscan=off disables both
+ * index-scan and index-only scan paths, whereas enable_indexonlyscan=off
+ * converts the index-only scan paths that we would have considered into
+ * index scan paths.
+ */
+ glob->default_ssa_mask = 0;
+ if (enable_tidscan)
+ glob->default_ssa_mask |= SSA_TIDSCAN;
+ if (enable_seqscan)
+ glob->default_ssa_mask |= SSA_SEQSCAN;
+ if (enable_indexscan)
+ glob->default_ssa_mask |= SSA_INDEXSCAN | SSA_INDEXONLYSCAN;
+ if (enable_indexonlyscan)
+ glob->default_ssa_mask |= SSA_CONSIDER_INDEXONLY;
+ if (enable_bitmapscan)
+ glob->default_ssa_mask |= SSA_BITMAPSCAN;
+
/* Compute the initial join strategy advice mask. */
glob->default_jsa_mask = JSA_FOREIGN;
if (enable_hashjoin)
* Allow a plugin to editorialize on the info we obtained from the
* catalogs. Actions might include altering the assumed relation size,
* removing an index, or adding a hypothetical index to the indexlist.
+ *
+ * An extension can also modify rel->ssa_mask here to control the scan
+ * strategy.
*/
if (get_relation_info_hook)
(*get_relation_info_hook) (root, relationObjectId, inhparent, rel);
rel->direct_lateral_relids = parent->direct_lateral_relids;
rel->lateral_relids = parent->lateral_relids;
rel->lateral_referencers = parent->lateral_referencers;
+
+ /*
+ * By default, a parent's scan strategy advice is preserved for each
+ * inheritance child.
+ */
+ rel->ssa_mask = parent->ssa_mask;
}
else
{
rel->direct_lateral_relids = NULL;
rel->lateral_relids = NULL;
rel->lateral_referencers = NULL;
+ rel->ssa_mask = root->glob->default_ssa_mask;
}
/* Check type of rtable entry */
/* worst PROPARALLEL hazard level */
char maxParallelHazard;
+ /* default scan strategy advice, except where overrriden by hooks */
+ uint32 default_ssa_mask;
+
/* default join strategy advice, except where overrriden by hooks */
uint32 default_jsa_mask;
int32 *attr_widths pg_node_attr(read_write_ignore);
/* zero-based set containing attnums of NOT NULL columns */
Bitmapset *notnullattnums;
+ /* scan strategy advice */
+ uint32 ssa_mask;
/* relids of outer joins that can null this baserel */
Relids nulling_relids;
/* LATERAL Vars and PHVs referenced by rel */
#include "nodes/pathnodes.h"
+/*
+ * Scan strategy advice.
+ *
+ * If SSA_CONSIDER_INDEXONLY is not set, index-only scan paths will not even
+ * be generated, and we'll generated index-scan paths for the same cases
+ * instead. If any other bit is not set, paths of that type will still be
+ * generated but will be marked as disabled.
+ *
+ * So, if you want to avoid an index-only scan, you can either unset
+ * SSA_CONSIDER_INDEXONLY (in which case you'll get an index-scan instead,
+ * which may end up disabled if you also unset SSA_INDEXSCAN) or you can
+ * unset SSA_INDEXONLYSCAN (in which the index-only scan will be disabled
+ * and the cheapest non-disabled alternative, if any, will be chosen, but
+ * no corresponding index scan will be considered). If, on the other hand,
+ * you want to encourage an index-only scan, you can set just SSA_INDEXONLYSCAN
+ * and SSA_CONSIDER_INDEXONLY and clear all of the other bits.
+ *
+ * A default scan strategy advice mask is stored in the PlannerGlobal object
+ * based on the values of the various enable_* GUCs. This value is propagted
+ * into each RelOptInfo for a baserel, and from baserels to their inheritance
+ * children when partitions are expanded. In either case, the value can be
+ * usefully changed in get_relation_info_hook.
+ */
+#define SSA_TIDSCAN 0x0001
+#define SSA_SEQSCAN 0x0002
+#define SSA_INDEXSCAN 0x0004
+#define SSA_INDEXONLYSCAN 0x0008
+#define SSA_BITMAPSCAN 0x0010
+#define SSA_CONSIDER_INDEXONLY 0x0020
+
/*
* Join strategy advice.
*