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 */