diff --git a/src/backend/optimizer/path/clausesel.c b/src/backend/optimizer/path/clausesel.c index f4717942c3..1b9ae2e901 100644 --- a/src/backend/optimizer/path/clausesel.c +++ b/src/backend/optimizer/path/clausesel.c @@ -37,10 +37,13 @@ typedef struct RangeQueryClause bool have_hibound; /* found a high-bound clause yet? */ Selectivity lobound; /* Selectivity of a var > something clause */ Selectivity hibound; /* Selectivity of a var < something clause */ + bool lobound_isdefault; /* lobound is a default selectivity? */ + bool hibound_isdefault; /* hibound is a default selectivity? */ } RangeQueryClause; static void addRangeClause(RangeQueryClause **rqlist, Node *clause, - bool varonleft, bool isLTsel, Selectivity s2); + bool varonleft, bool isLTsel, Selectivity s2, + bool isdefault); static RelOptInfo *find_single_rel_for_clauses(PlannerInfo *root, List *clauses); @@ -108,6 +111,7 @@ clauselist_selectivity(PlannerInfo *root, RangeQueryClause *rqlist = NULL; ListCell *l; int listidx; + bool isdefault; /* * If there's exactly one clause, just go directly to @@ -115,7 +119,7 @@ clauselist_selectivity(PlannerInfo *root, */ if (list_length(clauses) == 1) return clause_selectivity(root, (Node *) linitial(clauses), - varRelid, jointype, sjinfo); + varRelid, jointype, sjinfo, &isdefault); /* * Determine if these clauses reference a single relation. If so, and if @@ -165,7 +169,8 @@ clauselist_selectivity(PlannerInfo *root, continue; /* Always compute the selectivity using clause_selectivity */ - s2 = clause_selectivity(root, clause, varRelid, jointype, sjinfo); + s2 = clause_selectivity(root, clause, varRelid, jointype, + sjinfo, &isdefault); /* * Check for being passed a RestrictInfo. @@ -227,12 +232,12 @@ clauselist_selectivity(PlannerInfo *root, case F_SCALARLTSEL: case F_SCALARLESEL: addRangeClause(&rqlist, clause, - varonleft, true, s2); + varonleft, true, s2, isdefault); break; case F_SCALARGTSEL: case F_SCALARGESEL: addRangeClause(&rqlist, clause, - varonleft, false, s2); + varonleft, false, s2, isdefault); break; default: /* Just merge the selectivity in generically */ @@ -260,12 +265,11 @@ clauselist_selectivity(PlannerInfo *root, Selectivity s2; /* - * Exact equality to the default value probably means the - * selectivity function punted. This is not airtight but should - * be good enough. + * If either selectivity is default, we use a default estimate. + * This is not airtight but should be good enough. */ - if (rqlist->hibound == DEFAULT_INEQ_SEL || - rqlist->lobound == DEFAULT_INEQ_SEL) + if (rqlist->hibound_isdefault || + rqlist->lobound_isdefault) { s2 = DEFAULT_RANGE_INEQ_SEL; } @@ -332,7 +336,8 @@ clauselist_selectivity(PlannerInfo *root, */ static void addRangeClause(RangeQueryClause **rqlist, Node *clause, - bool varonleft, bool isLTsel, Selectivity s2) + bool varonleft, bool isLTsel, Selectivity s2, + bool isdefault) { RangeQueryClause *rqelem; Node *var; @@ -377,6 +382,8 @@ addRangeClause(RangeQueryClause **rqlist, Node *clause, if (rqelem->lobound > s2) rqelem->lobound = s2; } + if (isdefault) + rqelem->lobound_isdefault = true; } else { @@ -397,6 +404,8 @@ addRangeClause(RangeQueryClause **rqlist, Node *clause, if (rqelem->hibound > s2) rqelem->hibound = s2; } + if (isdefault) + rqelem->hibound_isdefault = true; } return; } @@ -404,17 +413,24 @@ addRangeClause(RangeQueryClause **rqlist, Node *clause, /* No matching var found, so make a new clause-pair data structure */ rqelem = (RangeQueryClause *) palloc(sizeof(RangeQueryClause)); rqelem->var = var; + rqelem->lobound_isdefault = false; + rqelem->hibound_isdefault = false; + if (is_lobound) { rqelem->have_lobound = true; rqelem->have_hibound = false; rqelem->lobound = s2; + if (isdefault) + rqelem->lobound_isdefault = true; } else { rqelem->have_lobound = false; rqelem->have_hibound = true; rqelem->hibound = s2; + if (isdefault) + rqelem->lobound_isdefault = true; } rqelem->next = *rqlist; *rqlist = rqelem; @@ -575,7 +591,8 @@ clause_selectivity(PlannerInfo *root, Node *clause, int varRelid, JoinType jointype, - SpecialJoinInfo *sjinfo) + SpecialJoinInfo *sjinfo, + bool *isdefault) { Selectivity s1 = 0.5; /* default for any unhandled clause type */ RestrictInfo *rinfo = NULL; @@ -695,7 +712,7 @@ clause_selectivity(PlannerInfo *root, (Node *) get_notclausearg((Expr *) clause), varRelid, jointype, - sjinfo); + sjinfo, isdefault); } else if (and_clause(clause)) { @@ -723,7 +740,7 @@ clause_selectivity(PlannerInfo *root, (Node *) lfirst(arg), varRelid, jointype, - sjinfo); + sjinfo, isdefault); s1 = s1 + s2 - s1 * s2; } @@ -748,7 +765,7 @@ clause_selectivity(PlannerInfo *root, s1 = restriction_selectivity(root, opno, opclause->args, opclause->inputcollid, - varRelid); + varRelid, isdefault); } /* @@ -816,7 +833,7 @@ clause_selectivity(PlannerInfo *root, (Node *) ((RelabelType *) clause)->arg, varRelid, jointype, - sjinfo); + sjinfo, isdefault); } else if (IsA(clause, CoerceToDomain)) { @@ -825,7 +842,7 @@ clause_selectivity(PlannerInfo *root, (Node *) ((CoerceToDomain *) clause)->arg, varRelid, jointype, - sjinfo); + sjinfo, isdefault); } else { diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index 480fd250e9..c181ef0ea6 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -4246,6 +4246,7 @@ approx_tuple_count(PlannerInfo *root, JoinPath *path, List *quals) SpecialJoinInfo sjinfo; Selectivity selec = 1.0; ListCell *l; + bool isdefault; /* * Make up a SpecialJoinInfo for JOIN_INNER semantics. @@ -4270,7 +4271,7 @@ approx_tuple_count(PlannerInfo *root, JoinPath *path, List *quals) Node *qual = (Node *) lfirst(l); /* Note that clause_selectivity will be able to cache its result */ - selec *= clause_selectivity(root, qual, 0, JOIN_INNER, &sjinfo); + selec *= clause_selectivity(root, qual, 0, JOIN_INNER, &sjinfo, &isdefault); } /* Apply it to the input relation sizes */ diff --git a/src/backend/optimizer/util/orclauses.c b/src/backend/optimizer/util/orclauses.c index 1e78028abe..c92eeae824 100644 --- a/src/backend/optimizer/util/orclauses.c +++ b/src/backend/optimizer/util/orclauses.c @@ -260,7 +260,7 @@ consider_new_or_clause(PlannerInfo *root, RelOptInfo *rel, RestrictInfo *or_rinfo; Selectivity or_selec, orig_selec; - + bool isdefault; /* * Build a RestrictInfo from the new OR clause. We can assume it's valid * as a base restriction clause. @@ -280,7 +280,7 @@ consider_new_or_clause(PlannerInfo *root, RelOptInfo *rel, * saving work later.) */ or_selec = clause_selectivity(root, (Node *) or_rinfo, - 0, JOIN_INNER, NULL); + 0, JOIN_INNER, NULL, &isdefault); /* * The clause is only worth adding to the query if it rejects a useful @@ -344,7 +344,7 @@ consider_new_or_clause(PlannerInfo *root, RelOptInfo *rel, /* Compute inner-join size */ orig_selec = clause_selectivity(root, (Node *) join_or_rinfo, - 0, JOIN_INNER, &sjinfo); + 0, JOIN_INNER, &sjinfo, &isdefault); /* And hack cached selectivity so join size remains the same */ join_or_rinfo->norm_selec = orig_selec / or_selec; diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index a570ac0aab..cb7595e69b 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -1740,7 +1740,8 @@ restriction_selectivity(PlannerInfo *root, Oid operatorid, List *args, Oid inputcollid, - int varRelid) + int varRelid, + bool *isdefault) { RegProcedure oprrest = get_oprrest(operatorid); float8 result; @@ -1752,12 +1753,13 @@ restriction_selectivity(PlannerInfo *root, if (!oprrest) return (Selectivity) 0.5; - result = DatumGetFloat8(OidFunctionCall4Coll(oprrest, + result = DatumGetFloat8(OidFunctionCall5Coll(oprrest, inputcollid, PointerGetDatum(root), ObjectIdGetDatum(operatorid), PointerGetDatum(args), - Int32GetDatum(varRelid))); + Int32GetDatum(varRelid), + PointerGetDatum(isdefault))); if (result < 0.0 || result > 1.0) elog(ERROR, "invalid restriction selectivity: %f", result); diff --git a/src/backend/statistics/dependencies.c b/src/backend/statistics/dependencies.c index 58d0df20f6..1cee5d213a 100644 --- a/src/backend/statistics/dependencies.c +++ b/src/backend/statistics/dependencies.c @@ -954,6 +954,7 @@ dependencies_clauselist_selectivity(PlannerInfo *root, MVDependencies *dependencies; AttrNumber *list_attnums; int listidx; + bool isdefault; /* initialize output argument */ *estimatedclauses = NULL; @@ -1068,7 +1069,7 @@ dependencies_clauselist_selectivity(PlannerInfo *root, clause = (Node *) lfirst(l); s2 = clause_selectivity(root, clause, varRelid, jointype, - sjinfo); + sjinfo, &isdefault); /* mark this one as done, so we don't touch it again. */ *estimatedclauses = bms_add_member(*estimatedclauses, listidx); diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index c3db9ea070..378619b8c2 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -573,7 +573,8 @@ neqsel(PG_FUNCTION_ARGS) */ static double scalarineqsel(PlannerInfo *root, Oid operator, bool isgt, bool iseq, - VariableStatData *vardata, Datum constval, Oid consttype) + VariableStatData *vardata, Datum constval, Oid consttype, + bool *isdefault) { Form_pg_statistic stats; FmgrInfo opproc; @@ -582,8 +583,11 @@ scalarineqsel(PlannerInfo *root, Oid operator, bool isgt, bool iseq, sumcommon; double selec; + *isdefault = false; + if (!HeapTupleIsValid(vardata->statsTuple)) { + *isdefault = true; /* no stats available, so default result */ return DEFAULT_INEQ_SEL; } @@ -1101,6 +1105,7 @@ scalarineqsel_wrapper(PG_FUNCTION_ARGS, bool isgt, bool iseq) Oid operator = PG_GETARG_OID(1); List *args = (List *) PG_GETARG_POINTER(2); int varRelid = PG_GETARG_INT32(3); + bool *isdefault = (bool *) PG_GETARG_POINTER(4); VariableStatData vardata; Node *other; bool varonleft; @@ -1114,13 +1119,17 @@ scalarineqsel_wrapper(PG_FUNCTION_ARGS, bool isgt, bool iseq) */ if (!get_restriction_variable(root, args, varRelid, &vardata, &other, &varonleft)) + { + *isdefault = true; PG_RETURN_FLOAT8(DEFAULT_INEQ_SEL); + } /* * Can't do anything useful if the something is not a constant, either. */ if (!IsA(other, Const)) { + *isdefault = true; ReleaseVariableStats(vardata); PG_RETURN_FLOAT8(DEFAULT_INEQ_SEL); } @@ -1145,6 +1154,7 @@ scalarineqsel_wrapper(PG_FUNCTION_ARGS, bool isgt, bool iseq) operator = get_commutator(operator); if (!operator) { + *isdefault = true; /* Use default selectivity (should we raise an error instead?) */ ReleaseVariableStats(vardata); PG_RETURN_FLOAT8(DEFAULT_INEQ_SEL); @@ -1154,7 +1164,7 @@ scalarineqsel_wrapper(PG_FUNCTION_ARGS, bool isgt, bool iseq) /* The rest of the work is done by scalarineqsel(). */ selec = scalarineqsel(root, operator, isgt, iseq, - &vardata, constval, consttype); + &vardata, constval, consttype, isdefault); ReleaseVariableStats(vardata); @@ -1607,6 +1617,7 @@ booltestsel(PlannerInfo *root, BoolTestType booltesttype, Node *arg, { VariableStatData vardata; double selec; + bool isdefault; examine_variable(root, arg, varRelid, &vardata); @@ -1731,14 +1742,14 @@ booltestsel(PlannerInfo *root, BoolTestType booltesttype, Node *arg, case IS_TRUE: case IS_NOT_FALSE: selec = (double) clause_selectivity(root, arg, - varRelid, - jointype, sjinfo); + varRelid, jointype, + sjinfo, &isdefault); break; case IS_FALSE: case IS_NOT_TRUE: selec = 1.0 - (double) clause_selectivity(root, arg, - varRelid, - jointype, sjinfo); + varRelid, jointype, + sjinfo, &isdefault); break; default: elog(ERROR, "unrecognized booltesttype: %d", @@ -2235,6 +2246,7 @@ rowcomparesel(PlannerInfo *root, Oid inputcollid = linitial_oid(clause->inputcollids); List *opargs; bool is_join_clause; + bool isdefault; /* Build equivalent arg list for single operator */ opargs = list_make2(linitial(clause->largs), linitial(clause->rargs)); @@ -2283,7 +2295,7 @@ rowcomparesel(PlannerInfo *root, s1 = restriction_selectivity(root, opno, opargs, inputcollid, - varRelid); + varRelid, &isdefault); } return s1; @@ -3039,6 +3051,7 @@ mergejoinscansel(PlannerInfo *root, Node *clause, rightmin, rightmax; double selec; + bool isdefault; /* Set default results if we can't figure anything out. */ /* XXX should default "start" fraction be a bit more than 0? */ @@ -3206,13 +3219,13 @@ mergejoinscansel(PlannerInfo *root, Node *clause, * non-default estimates, else stick with our 1.0. */ selec = scalarineqsel(root, leop, isgt, true, &leftvar, - rightmax, op_righttype); + rightmax, op_righttype, &isdefault); if (selec != DEFAULT_INEQ_SEL) *leftend = selec; /* And similarly for the right variable. */ selec = scalarineqsel(root, revleop, isgt, true, &rightvar, - leftmax, op_lefttype); + leftmax, op_lefttype, &isdefault); if (selec != DEFAULT_INEQ_SEL) *rightend = selec; @@ -3236,13 +3249,13 @@ mergejoinscansel(PlannerInfo *root, Node *clause, * our own default. */ selec = scalarineqsel(root, ltop, isgt, false, &leftvar, - rightmin, op_righttype); + rightmin, op_righttype, &isdefault); if (selec != DEFAULT_INEQ_SEL) *leftstart = selec; /* And similarly for the right variable. */ selec = scalarineqsel(root, revltop, isgt, false, &rightvar, - leftmin, op_lefttype); + leftmin, op_lefttype, &isdefault); if (selec != DEFAULT_INEQ_SEL) *rightstart = selec; diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h index 77ca7ff837..19382b8a28 100644 --- a/src/include/optimizer/cost.h +++ b/src/include/optimizer/cost.h @@ -214,7 +214,8 @@ extern Selectivity clause_selectivity(PlannerInfo *root, Node *clause, int varRelid, JoinType jointype, - SpecialJoinInfo *sjinfo); + SpecialJoinInfo *sjinfo, + bool *isdefault); extern void cost_gather_merge(GatherMergePath *path, PlannerInfo *root, RelOptInfo *rel, ParamPathInfo *param_info, Cost input_startup_cost, Cost input_total_cost, diff --git a/src/include/optimizer/plancat.h b/src/include/optimizer/plancat.h index 7d53cbbb87..2cb077e6ff 100644 --- a/src/include/optimizer/plancat.h +++ b/src/include/optimizer/plancat.h @@ -46,7 +46,8 @@ extern Selectivity restriction_selectivity(PlannerInfo *root, Oid operatorid, List *args, Oid inputcollid, - int varRelid); + int varRelid, + bool *isdefault); extern Selectivity join_selectivity(PlannerInfo *root, Oid operatorid,