optimize-cardinality-fully-covered-uk.patch
application/octet-stream
Filename: optimize-cardinality-fully-covered-uk.patch
Type: application/octet-stream
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
| File | + | − |
|---|---|---|
| src/backend/optimizer/path/costsize.c | 75 | 15 |
| src/backend/optimizer/util/relnode.c | 2 | 0 |
| src/backend/utils/adt/selfuncs.c | 132 | 5 |
| src/bin/initdb/initdb.c | 2 | 2 |
| src/include/nodes/pathnodes.h | 3 | 0 |
| src/include/utils/selfuncs.h | 1 | 0 |
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 8335cf5b5c5..92d65b40050 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -5358,16 +5358,28 @@ void
set_baserel_size_estimates(PlannerInfo *root, RelOptInfo *rel)
{
double nrows;
+ bool coveringUniquekey = false;
/* Should only be applied to base relations */
Assert(rel->relid > 0);
- nrows = rel->tuples *
- clauselist_selectivity(root,
- rel->baserestrictinfo,
- 0,
- JOIN_INNER,
- NULL);
+ if (rel->rtekind == RTE_RELATION)
+ {
+ RangeTblEntry *rte = rt_fetch(rel->relid, root->parse->rtable);
+
+ coveringUniquekey = clauses_covering_uniquekey(rte->relid, rel->relid,
+ rel->baserestrictinfo);
+ }
+
+ if (coveringUniquekey)
+ nrows = 1;
+ else
+ nrows = rel->tuples *
+ clauselist_selectivity(root,
+ rel->baserestrictinfo,
+ 0,
+ JOIN_INNER,
+ NULL);
rel->rows = clamp_row_est(nrows);
@@ -5390,6 +5402,7 @@ get_parameterized_baserel_size(PlannerInfo *root, RelOptInfo *rel,
{
List *allclauses;
double nrows;
+ bool coveringUniquekey = false;
/*
* Estimate the number of rows returned by the parameterized scan, knowing
@@ -5398,12 +5411,32 @@ get_parameterized_baserel_size(PlannerInfo *root, RelOptInfo *rel,
* non-join clauses during selectivity estimation.
*/
allclauses = list_concat_copy(param_clauses, rel->baserestrictinfo);
- nrows = rel->tuples *
- clauselist_selectivity(root,
- allclauses,
- rel->relid, /* do not use 0! */
- JOIN_INNER,
- NULL);
+
+ if (rel->rtekind == RTE_RELATION)
+ {
+ RangeTblEntry *rte = rt_fetch(rel->relid, root->parse->rtable);
+
+ coveringUniquekey = clauses_covering_uniquekey(rte->relid, rel->relid,
+ allclauses);
+ }
+
+ if (coveringUniquekey)
+ {
+ nrows = 1;
+
+ if (!rel->adjust_param_clauses)
+ {
+ rel->adjust_rows = nrows;
+ rel->adjust_param_clauses = allclauses;
+ }
+ }
+ else
+ nrows = rel->tuples *
+ clauselist_selectivity(root,
+ allclauses,
+ rel->relid, /* do not use 0! */
+ JOIN_INNER,
+ NULL);
nrows = clamp_row_est(nrows);
/* For safety, make sure result is not more than the base estimate */
if (nrows > rel->rows)
@@ -5440,14 +5473,41 @@ set_joinrel_size_estimates(PlannerInfo *root, RelOptInfo *rel,
SpecialJoinInfo *sjinfo,
List *restrictlist)
{
+ Cardinality outer_rows = outer_rel->rows;
+ Cardinality inner_rows = inner_rel->rows;
+ List *join_clauses = list_copy(restrictlist);
+ List *remove_clauses = NIL;
+ ListCell *l1;
+ ListCell *l2;
+
+ if (outer_rel->adjust_param_clauses)
+ {
+ remove_clauses = outer_rel->adjust_param_clauses;
+ outer_rows = outer_rel->adjust_rows;
+ }
+ else if (inner_rel->adjust_param_clauses)
+ {
+ remove_clauses = inner_rel->adjust_param_clauses;
+ inner_rows = inner_rel->adjust_rows;
+ }
+
+ foreach(l1, remove_clauses)
+ {
+ foreach(l2, join_clauses)
+ {
+ if (lfirst(l1) == lfirst(l2))
+ join_clauses = foreach_delete_current(join_clauses, l2);
+ }
+ }
+
rel->rows = calc_joinrel_size_estimate(root,
rel,
outer_rel,
inner_rel,
- outer_rel->rows,
- inner_rel->rows,
+ outer_rows,
+ inner_rows,
sjinfo,
- restrictlist);
+ join_clauses);
}
/*
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 1158bc194c3..1dbaa34f803 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -304,6 +304,8 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
rel->all_partrels = NULL;
rel->partexprs = NULL;
rel->nullable_partexprs = NULL;
+ rel->adjust_param_clauses = NIL;
+ rel->adjust_rows = 0;
/*
* Pass assorted information down the inheritance hierarchy.
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index 540aa9628d7..cfb4a3fbf81 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -269,6 +269,8 @@ static bool get_actual_variable_endpoint(Relation heapRel,
static RelOptInfo *find_join_input_rel(PlannerInfo *root, Relids relids);
static double btcost_correlation(IndexOptInfo *index,
VariableStatData *vardata);
+static bool clauses_varattnos_covering_index(Oid indexOid, Bitmapset *varattnos);
+static Bitmapset *pull_eq_clauses_varattnos(List *clauses, Index rtIndex);
/* Define support routines for MCV hash tables */
#define SH_PREFIX MCVHashTable
@@ -7333,6 +7335,7 @@ genericcostestimate(PlannerInfo *root,
double qual_arg_cost;
List *selectivityQuals;
ListCell *l;
+ Bitmapset *varattnos;
/*
* If the index is partial, AND the index predicate with the explicitly
@@ -7365,11 +7368,15 @@ genericcostestimate(PlannerInfo *root,
}
}
- /* Estimate the fraction of main-table tuples that will be visited */
- indexSelectivity = clauselist_selectivity(root, selectivityQuals,
- index->rel->relid,
- JOIN_INNER,
- NULL);
+ varattnos = pull_eq_clauses_varattnos(selectivityQuals, index->rel->relid);
+ if (clauses_varattnos_covering_index(index->indexoid, varattnos))
+ indexSelectivity = 1.0 / index->rel->tuples;
+ else
+ /* Estimate the fraction of main-table tuples that will be visited */
+ indexSelectivity = clauselist_selectivity(root, selectivityQuals,
+ index->rel->relid,
+ JOIN_INNER,
+ NULL);
/*
* If caller didn't give us an estimate, estimate the number of index
@@ -9123,3 +9130,123 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
*indexPages = index->pages;
}
+
+bool
+clauses_covering_uniquekey(Oid relid, Index rtIndex, List *clauses)
+{
+ ListCell *l;
+ Bitmapset *varattnos;
+ Relation relation;
+ List *indexOids;
+ bool covered = false;
+
+ varattnos = pull_eq_clauses_varattnos(clauses, rtIndex);
+ if (!varattnos)
+ return false;
+
+ relation = table_open(relid, AccessShareLock);
+ indexOids = RelationGetIndexList(relation);
+
+ foreach(l, indexOids)
+ {
+ if (clauses_varattnos_covering_index(lfirst_oid(l), varattnos))
+ {
+ covered = true;
+ break;
+ }
+ }
+
+ list_free(indexOids);
+ table_close(relation, AccessShareLock);
+
+ return covered;
+}
+
+static bool
+clauses_varattnos_covering_index(Oid indexOid, Bitmapset *varattnos)
+{
+ Relation indexRelation;
+ Bitmapset *index_attrs = NULL;
+ int i;
+ bool covered = false;
+
+ indexRelation = index_open(indexOid, AccessShareLock);
+
+ /* Must is a primary key or unique index */
+ if (!indexRelation->rd_index->indisprimary && !indexRelation->rd_index->indisunique)
+ {
+ index_close(indexRelation, AccessShareLock);
+ return false;
+ }
+
+ for (i = 0; i < indexRelation->rd_index->indnkeyatts; i++)
+ {
+ int attrnum = indexRelation->rd_index->indkey.values[i];
+
+ /*
+ * Collect all key attribute numbers from this index. Skip expression
+ * indexes (attrnum == 0) as they cannot be checked for coverage by
+ * simple attribute numbers.
+ */
+ if (attrnum != 0)
+ index_attrs = bms_add_member(index_attrs, attrnum);
+ }
+
+ /*
+ * Check if varattnos fully covers all attributes of this index. If so,
+ * this means the constraints are sufficient to uniquely identify rows.
+ */
+ if (bms_is_subset(index_attrs, varattnos))
+ covered = true;
+
+ bms_free(index_attrs);
+ index_close(indexRelation, AccessShareLock);
+
+ return covered;
+}
+
+
+static Bitmapset *
+pull_eq_clauses_varattnos(List *clauses, Index rtIndex)
+{
+ ListCell *l;
+ Bitmapset *varattnos = NULL;
+
+ foreach(l, clauses)
+ {
+ Node *clause = (Node *) lfirst(l);
+ RestrictInfo *rinfo;
+
+ if (!IsA(clause, RestrictInfo))
+ continue;
+
+ rinfo = (RestrictInfo *) clause;
+ clause = (Node *) rinfo->clause;
+
+ if (is_opclause(clause) && list_length(((OpExpr *) clause)->args) == 2)
+ {
+ OpExpr *expr = (OpExpr *) clause;
+
+ if (strcmp(get_opname(expr->opno), "=") != 0)
+ continue;
+
+ if (IsA(linitial(expr->args), Var))
+ {
+ Var *var = (Var *) linitial(expr->args);
+
+ if (var->varlevelsup == 0 && var->varno == rtIndex)
+ varattnos = bms_add_member(varattnos, var->varattno);
+ }
+
+ if (IsA(lsecond(expr->args), Var))
+ {
+ Var *var = (Var *) lsecond(expr->args);
+
+ if (var->varlevelsup == 0 && var->varno == rtIndex)
+ varattnos = bms_add_member(varattnos, var->varattno);
+ }
+ }
+ }
+
+ return varattnos;
+}
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 92fe2f531f7..74285e3709b 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -769,8 +769,8 @@ cleanup_directories_atexit(void)
if (made_new_pgdata)
{
pg_log_info("removing data directory \"%s\"", pg_data);
- if (!rmtree(pg_data, true))
- pg_log_error("failed to remove data directory");
+ // if (!rmtree(pg_data, true))
+ // pg_log_error("failed to remove data directory");
}
else if (found_existing_pgdata)
{
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 30d889b54c5..dba1064adc3 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -1122,6 +1122,9 @@ typedef struct RelOptInfo
/* extension state */
void **extension_state pg_node_attr(read_write_ignore);
int extension_state_allocated;
+
+ Cardinality adjust_rows;
+ List *adjust_param_clauses;
} RelOptInfo;
/*
diff --git a/src/include/utils/selfuncs.h b/src/include/utils/selfuncs.h
index fb4fa53363d..b6c0406e5ef 100644
--- a/src/include/utils/selfuncs.h
+++ b/src/include/utils/selfuncs.h
@@ -240,6 +240,7 @@ extern List *add_predicate_to_index_quals(IndexOptInfo *index,
extern void genericcostestimate(PlannerInfo *root, IndexPath *path,
double loop_count,
GenericCosts *costs);
+extern bool clauses_covering_uniquekey(Oid relid, Index rtIndex, List *clauses);
/* Functions in array_selfuncs.c */