Commit 44878506 authored by Tom Lane's avatar Tom Lane

First step in fixing selectivity-estimation code. eqsel and

neqsel now behave as per my suggestions in pghackers a few days ago.
selectivity for < > <= >= should work OK for integral types as well, but
still need work for nonintegral types.  Since these routines have never
actually executed before :-(, this may result in some significant changes
in the optimizer's choices of execution plans.  Let me know if you see
any serious misbehavior.
CAUTION: THESE CHANGES REQUIRE INITDB.  pg_statistic table has changed.
parent f851c6b0
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.115 1999/07/19 07:07:20 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.116 1999/08/01 04:54:24 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -78,7 +78,7 @@ static void vc_vacpage(Page page, VPageDescr vpd); ...@@ -78,7 +78,7 @@ static void vc_vacpage(Page page, VPageDescr vpd);
static void vc_vaconeind(VPageList vpl, Relation indrel, int num_tuples, int keep_tuples); static void vc_vaconeind(VPageList vpl, Relation indrel, int num_tuples, int keep_tuples);
static void vc_scanoneind(Relation indrel, int num_tuples); static void vc_scanoneind(Relation indrel, int num_tuples);
static void vc_attrstats(Relation onerel, VRelStats *vacrelstats, HeapTuple tuple); static void vc_attrstats(Relation onerel, VRelStats *vacrelstats, HeapTuple tuple);
static void vc_bucketcpy(Form_pg_attribute attr, Datum value, Datum *bucket, int16 *bucket_len); static void vc_bucketcpy(Form_pg_attribute attr, Datum value, Datum *bucket, int *bucket_len);
static void vc_updstats(Oid relid, int num_pages, int num_tuples, bool hasindex, VRelStats *vacrelstats); static void vc_updstats(Oid relid, int num_pages, int num_tuples, bool hasindex, VRelStats *vacrelstats);
static void vc_delhilowstats(Oid relid, int attcnt, int *attnums); static void vc_delhilowstats(Oid relid, int attcnt, int *attnums);
static VPageDescr vc_tidreapped(ItemPointer itemptr, VPageList vpl); static VPageDescr vc_tidreapped(ItemPointer itemptr, VPageList vpl);
...@@ -473,9 +473,13 @@ vc_vacone(Oid relid, bool analyze, List *va_cols) ...@@ -473,9 +473,13 @@ vc_vacone(Oid relid, bool analyze, List *va_cols)
{ {
pgopform = (Form_pg_operator) GETSTRUCT(func_operator); pgopform = (Form_pg_operator) GETSTRUCT(func_operator);
fmgr_info(pgopform->oprcode, &(stats->f_cmplt)); fmgr_info(pgopform->oprcode, &(stats->f_cmplt));
stats->op_cmplt = oprid(func_operator);
} }
else else
{
stats->f_cmplt.fn_addr = NULL; stats->f_cmplt.fn_addr = NULL;
stats->op_cmplt = InvalidOid;
}
func_operator = oper(">", stats->attr->atttypid, stats->attr->atttypid, true); func_operator = oper(">", stats->attr->atttypid, stats->attr->atttypid, true);
if (func_operator != NULL) if (func_operator != NULL)
...@@ -2200,8 +2204,8 @@ vc_attrstats(Relation onerel, VRelStats *vacrelstats, HeapTuple tuple) ...@@ -2200,8 +2204,8 @@ vc_attrstats(Relation onerel, VRelStats *vacrelstats, HeapTuple tuple)
{ {
swapDatum(stats->guess1, stats->guess2); swapDatum(stats->guess1, stats->guess2);
swapInt(stats->guess1_len, stats->guess2_len); swapInt(stats->guess1_len, stats->guess2_len);
stats->guess1_cnt = stats->guess2_hits;
swapLong(stats->guess1_hits, stats->guess2_hits); swapLong(stats->guess1_hits, stats->guess2_hits);
stats->guess1_cnt = stats->guess1_hits;
} }
if (stats->guess1_cnt > stats->best_cnt) if (stats->guess1_cnt > stats->best_cnt)
{ {
...@@ -2227,7 +2231,7 @@ vc_attrstats(Relation onerel, VRelStats *vacrelstats, HeapTuple tuple) ...@@ -2227,7 +2231,7 @@ vc_attrstats(Relation onerel, VRelStats *vacrelstats, HeapTuple tuple)
* *
*/ */
static void static void
vc_bucketcpy(Form_pg_attribute attr, Datum value, Datum *bucket, int16 *bucket_len) vc_bucketcpy(Form_pg_attribute attr, Datum value, Datum *bucket, int *bucket_len)
{ {
if (attr->attbyval && attr->attlen != -1) if (attr->attbyval && attr->attlen != -1)
*bucket = value; *bucket = value;
...@@ -2340,13 +2344,14 @@ vc_updstats(Oid relid, int num_pages, int num_tuples, bool hasindex, VRelStats * ...@@ -2340,13 +2344,14 @@ vc_updstats(Oid relid, int num_pages, int num_tuples, bool hasindex, VRelStats *
selratio = 0; selratio = 0;
else if (VacAttrStatsLtGtValid(stats) && stats->min_cnt + stats->max_cnt == stats->nonnull_cnt) else if (VacAttrStatsLtGtValid(stats) && stats->min_cnt + stats->max_cnt == stats->nonnull_cnt)
{ {
/* exact result when there are just 1 or 2 values... */
double min_cnt_d = stats->min_cnt, double min_cnt_d = stats->min_cnt,
max_cnt_d = stats->max_cnt, max_cnt_d = stats->max_cnt,
null_cnt_d = stats->null_cnt, null_cnt_d = stats->null_cnt,
nonnullcnt_d = stats->nonnull_cnt; /* prevent overflow */ nonnull_cnt_d = stats->nonnull_cnt; /* prevent overflow */
selratio = (min_cnt_d * min_cnt_d + max_cnt_d * max_cnt_d + null_cnt_d * null_cnt_d) / selratio = (min_cnt_d * min_cnt_d + max_cnt_d * max_cnt_d + null_cnt_d * null_cnt_d) /
(nonnullcnt_d + null_cnt_d) / (nonnullcnt_d + null_cnt_d); (nonnull_cnt_d + null_cnt_d) / (nonnull_cnt_d + null_cnt_d);
} }
else else
{ {
...@@ -2359,7 +2364,9 @@ vc_updstats(Oid relid, int num_pages, int num_tuples, bool hasindex, VRelStats * ...@@ -2359,7 +2364,9 @@ vc_updstats(Oid relid, int num_pages, int num_tuples, bool hasindex, VRelStats *
*/ */
selratio = (most * most + 0.20 * most * (total - most)) / total / total; selratio = (most * most + 0.20 * most * (total - most)) / total / total;
} }
if (selratio > 1.0) if (selratio < 0.0)
selratio = 0.0;
else if (selratio > 1.0)
selratio = 1.0; selratio = 1.0;
attp->attdisbursion = selratio; attp->attdisbursion = selratio;
...@@ -2375,13 +2382,22 @@ vc_updstats(Oid relid, int num_pages, int num_tuples, bool hasindex, VRelStats * ...@@ -2375,13 +2382,22 @@ vc_updstats(Oid relid, int num_pages, int num_tuples, bool hasindex, VRelStats *
* doing system relations, especially pg_statistic is a * doing system relations, especially pg_statistic is a
* problem * problem
*/ */
if (VacAttrStatsLtGtValid(stats) && stats->initialized /* && if (VacAttrStatsLtGtValid(stats) && stats->initialized
* !IsSystemRelationName( /* && !IsSystemRelationName(pgcform->relname.data)
* */ )
pgcform->relname.data) */ )
{ {
float32data nullratio;
float32data bestratio;
FmgrInfo out_function; FmgrInfo out_function;
char *out_string; char *out_string;
double best_cnt_d = stats->best_cnt,
null_cnt_d = stats->null_cnt,
nonnull_cnt_d = stats->nonnull_cnt; /* prevent overflow */
nullratio = null_cnt_d / (nonnull_cnt_d + null_cnt_d);
bestratio = best_cnt_d / (nonnull_cnt_d + null_cnt_d);
fmgr_info(stats->outfunc, &out_function);
for (i = 0; i < Natts_pg_statistic; ++i) for (i = 0; i < Natts_pg_statistic; ++i)
nulls[i] = ' '; nulls[i] = ' ';
...@@ -2391,26 +2407,34 @@ vc_updstats(Oid relid, int num_pages, int num_tuples, bool hasindex, VRelStats * ...@@ -2391,26 +2407,34 @@ vc_updstats(Oid relid, int num_pages, int num_tuples, bool hasindex, VRelStats *
* ---------------- * ----------------
*/ */
i = 0; i = 0;
values[i++] = (Datum) relid; /* 1 */ values[i++] = (Datum) relid; /* starelid */
values[i++] = (Datum) attp->attnum; /* 2 */ values[i++] = (Datum) attp->attnum; /* staattnum */
values[i++] = (Datum) InvalidOid; /* 3 */ values[i++] = (Datum) stats->op_cmplt; /* staop */
fmgr_info(stats->outfunc, &out_function); /* hack: this code knows float4 is pass-by-ref */
out_string = (*fmgr_faddr(&out_function)) (stats->min, stats->attr->atttypid); values[i++] = PointerGetDatum(&nullratio); /* stanullfrac */
values[i++] = (Datum) fmgr(F_TEXTIN, out_string); values[i++] = PointerGetDatum(&bestratio); /* stacommonfrac */
out_string = (*fmgr_faddr(&out_function)) (stats->best, stats->attr->atttypid, stats->attr->atttypmod);
values[i++] = PointerGetDatum(textin(out_string)); /* stacommonval */
pfree(out_string); pfree(out_string);
out_string = (char *) (*fmgr_faddr(&out_function)) (stats->max, stats->attr->atttypid); out_string = (*fmgr_faddr(&out_function)) (stats->min, stats->attr->atttypid, stats->attr->atttypmod);
values[i++] = (Datum) fmgr(F_TEXTIN, out_string); values[i++] = PointerGetDatum(textin(out_string)); /* staloval */
pfree(out_string);
out_string = (char *) (*fmgr_faddr(&out_function)) (stats->max, stats->attr->atttypid, stats->attr->atttypmod);
values[i++] = PointerGetDatum(textin(out_string)); /* stahival */
pfree(out_string); pfree(out_string);
stup = heap_formtuple(sd->rd_att, values, nulls); stup = heap_formtuple(sd->rd_att, values, nulls);
/* ---------------- /* ----------------
* insert the tuple in the relation and get the tuple's oid. * insert the tuple in the relation.
* ---------------- * ----------------
*/ */
heap_insert(sd, stup); heap_insert(sd, stup);
pfree(DatumGetPointer(values[3]));
pfree(DatumGetPointer(values[4])); /* release allocated space */
pfree(DatumGetPointer(values[Anum_pg_statistic_stacommonval-1]));
pfree(DatumGetPointer(values[Anum_pg_statistic_staloval-1]));
pfree(DatumGetPointer(values[Anum_pg_statistic_stahival-1]));
pfree(stup); pfree(stup);
} }
} }
......
...@@ -6,13 +6,11 @@ ...@@ -6,13 +6,11 @@
* These routines are registered in the operator catalog in the * These routines are registered in the operator catalog in the
* "oprrest" and "oprjoin" attributes. * "oprrest" and "oprjoin" attributes.
* *
* XXX check all the functions--I suspect them to be 1-based.
*
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.35 1999/07/17 20:17:59 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.36 1999/08/01 04:54:22 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -21,7 +19,10 @@ ...@@ -21,7 +19,10 @@
#include "access/heapam.h" #include "access/heapam.h"
#include "catalog/catname.h" #include "catalog/catname.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_statistic.h" #include "catalog/pg_statistic.h"
#include "catalog/pg_type.h"
#include "parser/parse_oper.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
#include "utils/syscache.h" #include "utils/syscache.h"
...@@ -29,24 +30,35 @@ ...@@ -29,24 +30,35 @@
/* N is not a valid var/constant or relation id */ /* N is not a valid var/constant or relation id */
#define NONVALUE(N) ((N) == -1) #define NONVALUE(N) ((N) == -1)
/* /* are we looking at a functional index selectivity request? */
* generalize the test for functional index selectivity request #define FunctionalSelectivity(nIndKeys,attNum) ((attNum)==InvalidAttrNumber)
*/
#define FunctionalSelectivity(nIndKeys,attNum) (attNum==InvalidAttrNumber)
static float32data getattdisbursion(Oid relid, AttrNumber attnum); /* default selectivity estimate for inequalities such as "A < b" */
static void gethilokey(Oid relid, AttrNumber attnum, Oid opid, #define DEFAULT_INEQ_SEL (1.0 / 3.0)
char **high, char **low);
static void getattproperties(Oid relid, AttrNumber attnum,
Oid *typid,
int *typlen,
bool *typbyval,
int32 *typmod);
static bool getattstatistics(Oid relid, AttrNumber attnum,
Oid typid, int32 typmod,
double *nullfrac,
double *commonfrac,
Datum *commonval,
Datum *loval,
Datum *hival);
static double getattdisbursion(Oid relid, AttrNumber attnum);
/* /*
* eqsel - Selectivity of "=" for any data type. * eqsel - Selectivity of "=" for any data types.
*/ */
float64 float64
eqsel(Oid opid, eqsel(Oid opid,
Oid relid, Oid relid,
AttrNumber attno, AttrNumber attno,
char *value, Datum value,
int32 flag) int32 flag)
{ {
float64 result; float64 result;
...@@ -55,18 +67,124 @@ eqsel(Oid opid, ...@@ -55,18 +67,124 @@ eqsel(Oid opid,
if (NONVALUE(attno) || NONVALUE(relid)) if (NONVALUE(attno) || NONVALUE(relid))
*result = 0.1; *result = 0.1;
else else
*result = (float64data) getattdisbursion(relid, (int) attno); {
Oid typid;
int typlen;
bool typbyval;
int32 typmod;
double nullfrac;
double commonfrac;
Datum commonval;
double selec;
/* get info about the attribute */
getattproperties(relid, attno,
&typid, &typlen, &typbyval, &typmod);
if (getattstatistics(relid, attno, typid, typmod,
&nullfrac, &commonfrac, &commonval,
NULL, NULL))
{
if (flag & SEL_CONSTANT)
{
/* Is the constant the same as the most common value? */
HeapTuple oprtuple;
Oid ltype,
rtype;
Operator func_operator;
bool mostcommon = false;
/* get left and right datatypes of the operator */
oprtuple = get_operator_tuple(opid);
if (! HeapTupleIsValid(oprtuple))
elog(ERROR, "eqsel: no tuple for operator %u", opid);
ltype = ((Form_pg_operator) GETSTRUCT(oprtuple))->oprleft;
rtype = ((Form_pg_operator) GETSTRUCT(oprtuple))->oprright;
/* and find appropriate equality operator (no, it ain't
* necessarily opid itself...)
*/
func_operator = oper("=", ltype, rtype, true);
if (func_operator != NULL)
{
RegProcedure eqproc = ((Form_pg_operator) GETSTRUCT(func_operator))->oprcode;
if (flag & SEL_RIGHT) /* given value on the right? */
mostcommon = (bool)
DatumGetUInt8(fmgr(eqproc, commonval, value));
else
mostcommon = (bool)
DatumGetUInt8(fmgr(eqproc, value, commonval));
}
if (mostcommon)
{
/* Search is for the most common value. We know the
* selectivity exactly (or as exactly as VACUUM could
* calculate it, anyway).
*/
selec = commonfrac;
}
else
{
/* Comparison is against a constant that is neither the
* most common value nor null. Its selectivity cannot
* be more than this:
*/
selec = 1.0 - commonfrac - nullfrac;
if (selec > commonfrac)
selec = commonfrac;
/* and in fact it's probably less, so apply a fudge
* factor.
*/
selec *= 0.5;
}
}
else
{
/* Search is for a value that we do not know a priori,
* but we will assume it is not NULL. Selectivity
* cannot be more than this:
*/
selec = 1.0 - nullfrac;
if (selec > commonfrac)
selec = commonfrac;
/* and in fact it's probably less, so apply a fudge
* factor.
*/
selec *= 0.5;
}
/* result should be in range, but make sure... */
if (selec < 0.0)
selec = 0.0;
else if (selec > 1.0)
selec = 1.0;
if (! typbyval)
pfree(DatumGetPointer(commonval));
}
else
{
/* No VACUUM ANALYZE stats available, so make a guess using
* the disbursion stat (if we have that, which is unlikely...)
*/
selec = getattdisbursion(relid, attno);
}
*result = (float64data) selec;
}
return result; return result;
} }
/* /*
* neqsel - Selectivity of "!=" for any data type. * neqsel - Selectivity of "!=" for any data types.
*/ */
float64 float64
neqsel(Oid opid, neqsel(Oid opid,
Oid relid, Oid relid,
AttrNumber attno, AttrNumber attno,
char *value, Datum value,
int32 flag) int32 flag)
{ {
float64 result; float64 result;
...@@ -77,96 +195,164 @@ neqsel(Oid opid, ...@@ -77,96 +195,164 @@ neqsel(Oid opid,
} }
/* /*
* intltsel - Selectivity of "<" for integers. * intltsel - Selectivity of "<" (also "<=") for integers.
* Should work for both longs and shorts. * Should work for both longs and shorts.
*/ */
float64 float64
intltsel(Oid opid, intltsel(Oid opid,
Oid relid, Oid relid,
AttrNumber attno, AttrNumber attno,
int32 value, Datum value,
int32 flag) int32 flag)
{ {
float64 result; float64 result;
char *highchar,
*lowchar;
long val,
high,
low,
top,
bottom;
result = (float64) palloc(sizeof(float64data)); result = (float64) palloc(sizeof(float64data));
if (NONVALUE(attno) || NONVALUE(relid)) if (! (flag & SEL_CONSTANT) || NONVALUE(attno) || NONVALUE(relid))
*result = 1.0 / 3; *result = DEFAULT_INEQ_SEL;
else else
{ {
/* XXX val = atol(value); */ HeapTuple oprtuple;
val = value; Oid ltype,
gethilokey(relid, (int) attno, opid, &highchar, &lowchar); rtype;
if (*highchar == 'n' || *lowchar == 'n') Oid typid;
int typlen;
bool typbyval;
int32 typmod;
Datum hival,
loval;
long val,
high,
low,
numerator,
denominator;
/* get left and right datatypes of the operator */
oprtuple = get_operator_tuple(opid);
if (! HeapTupleIsValid(oprtuple))
elog(ERROR, "intltsel: no tuple for operator %u", opid);
ltype = ((Form_pg_operator) GETSTRUCT(oprtuple))->oprleft;
rtype = ((Form_pg_operator) GETSTRUCT(oprtuple))->oprright;
/*
* TEMPORARY HACK: this code is currently getting called for
* a bunch of non-integral types. Give a default estimate if
* either side is not pass-by-val. Need better solution.
*/
if (! get_typbyval(ltype) || ! get_typbyval(rtype))
{ {
*result = 1.0 / 3.0; *result = DEFAULT_INEQ_SEL;
return result; return result;
} }
high = atol(highchar);
low = atol(lowchar); /* Deduce type of the constant, and convert to uniform "long" format.
if ((flag & SEL_RIGHT && val < low) || * Note that constant might well be a different type than attribute.
(!(flag & SEL_RIGHT) && val > high)) * XXX this ought to use a type-specific "convert to double" op.
*/
typid = (flag & SEL_RIGHT) ? rtype : ltype;
switch (get_typlen(typid))
{ {
float32data nvals; case 1:
val = (long) DatumGetUInt8(value);
break;
case 2:
val = (long) DatumGetInt16(value);
break;
case 4:
val = (long) DatumGetInt32(value);
break;
default:
elog(ERROR, "intltsel: unsupported type %u", typid);
*result = DEFAULT_INEQ_SEL;
return result;
}
nvals = getattdisbursion(relid, (int) attno); /* Now get info about the attribute */
if (nvals == 0) getattproperties(relid, attno,
*result = 1.0 / 3.0; &typid, &typlen, &typbyval, &typmod);
else
{ if (! getattstatistics(relid, attno, typid, typmod,
*result = 3.0 * (float64data) nvals; NULL, NULL, NULL,
if (*result > 1.0) &loval, &hival))
*result = 1; {
} *result = DEFAULT_INEQ_SEL;
return result;
}
/*
* Convert loval/hival to common "long int" representation.
*/
switch (typlen)
{
case 1:
low = (long) DatumGetUInt8(loval);
high = (long) DatumGetUInt8(hival);
break;
case 2:
low = (long) DatumGetInt16(loval);
high = (long) DatumGetInt16(hival);
break;
case 4:
low = (long) DatumGetInt32(loval);
high = (long) DatumGetInt32(hival);
break;
default:
elog(ERROR, "intltsel: unsupported type %u", typid);
*result = DEFAULT_INEQ_SEL;
return result;
}
if (val < low || val > high)
{
/* If given value is outside the statistical range,
* assume we have out-of-date stats and return a default guess.
* We could return a small or large value if we trusted the stats
* more. XXX change this eventually.
*/
*result = DEFAULT_INEQ_SEL;
} }
else else
{ {
bottom = high - low; denominator = high - low;
if (bottom == 0) if (denominator <= 0)
++bottom; denominator = 1;
if (flag & SEL_RIGHT) if (flag & SEL_RIGHT)
top = val - low; numerator = val - low;
else else
top = high - val; numerator = high - val;
if (top > bottom) if (numerator <= 0) /* never return a zero estimate! */
numerator = 1;
if (numerator >= denominator)
*result = 1.0; *result = 1.0;
else else
{ *result = (double) numerator / (double) denominator;
if (top == 0) }
++top; if (! typbyval)
*result = ((1.0 * top) / bottom); {
} pfree(DatumGetPointer(hival));
pfree(DatumGetPointer(loval));
} }
} }
return result; return result;
} }
/* /*
* intgtsel - Selectivity of ">" for integers. * intgtsel - Selectivity of ">" (also ">=") for integers.
* Should work for both longs and shorts. * Should work for both longs and shorts.
*/ */
float64 float64
intgtsel(Oid opid, intgtsel(Oid opid,
Oid relid, Oid relid,
AttrNumber attno, AttrNumber attno,
int32 value, Datum value,
int32 flag) int32 flag)
{ {
float64 result; float64 result;
int notflag;
if (flag & 0) /* Compute selectivity of "<", then invert --- but only if we
notflag = flag & ~SEL_RIGHT; * were able to produce a non-default estimate.
else */
notflag = flag | SEL_RIGHT; result = intltsel(opid, relid, attno, value, flag);
result = intltsel(opid, relid, attno, value, (int32) notflag); if (*result != DEFAULT_INEQ_SEL)
*result = 1.0 - *result;
return result; return result;
} }
...@@ -181,7 +367,7 @@ eqjoinsel(Oid opid, ...@@ -181,7 +367,7 @@ eqjoinsel(Oid opid,
AttrNumber attno2) AttrNumber attno2)
{ {
float64 result; float64 result;
float32data num1, float64data num1,
num2, num2,
max; max;
...@@ -191,13 +377,13 @@ eqjoinsel(Oid opid, ...@@ -191,13 +377,13 @@ eqjoinsel(Oid opid,
*result = 0.1; *result = 0.1;
else else
{ {
num1 = getattdisbursion(relid1, (int) attno1); num1 = getattdisbursion(relid1, attno1);
num2 = getattdisbursion(relid2, (int) attno2); num2 = getattdisbursion(relid2, attno2);
max = (num1 > num2) ? num1 : num2; max = (num1 > num2) ? num1 : num2;
if (max == 0) if (max <= 0)
*result = 1.0; *result = 1.0;
else else
*result = (float64data) max; *result = max;
} }
return result; return result;
} }
...@@ -220,7 +406,7 @@ neqjoinsel(Oid opid, ...@@ -220,7 +406,7 @@ neqjoinsel(Oid opid,
} }
/* /*
* intltjoinsel - Join selectivity of "<" * intltjoinsel - Join selectivity of "<" and "<="
*/ */
float64 float64
intltjoinsel(Oid opid, intltjoinsel(Oid opid,
...@@ -232,12 +418,12 @@ intltjoinsel(Oid opid, ...@@ -232,12 +418,12 @@ intltjoinsel(Oid opid,
float64 result; float64 result;
result = (float64) palloc(sizeof(float64data)); result = (float64) palloc(sizeof(float64data));
*result = 1.0 / 3.0; *result = DEFAULT_INEQ_SEL;
return result; return result;
} }
/* /*
* intgtjoinsel - Join selectivity of ">" * intgtjoinsel - Join selectivity of ">" and ">="
*/ */
float64 float64
intgtjoinsel(Oid opid, intgtjoinsel(Oid opid,
...@@ -249,129 +435,230 @@ intgtjoinsel(Oid opid, ...@@ -249,129 +435,230 @@ intgtjoinsel(Oid opid,
float64 result; float64 result;
result = (float64) palloc(sizeof(float64data)); result = (float64) palloc(sizeof(float64data));
*result = 1.0 / 3.0; *result = DEFAULT_INEQ_SEL;
return result; return result;
} }
/* /*
* getattdisbursion - Retrieves the number of values within an attribute. * getattproperties
* * Retrieve pg_attribute properties for an attribute,
* Note: * including type OID, type len, type byval flag, typmod.
* getattdisbursion and gethilokey both currently use keyed
* relation scans and amgetattr. Alternatively,
* the relation scan could be non-keyed and the tuple
* returned could be cast (struct X *) tuple + tuple->t_hoff.
* The first method is good for testing the implementation,
* but the second may ultimately be faster?!? In any case,
* using the cast instead of amgetattr would be
* more efficient. However, the cast will not work
* for gethilokey which accesses stahikey in struct statistic.
*/ */
static float32data static void
getattdisbursion(Oid relid, AttrNumber attnum) getattproperties(Oid relid, AttrNumber attnum,
Oid *typid, int *typlen, bool *typbyval, int32 *typmod)
{ {
HeapTuple atp; HeapTuple atp;
float32data nvals; Form_pg_attribute att_tup;
int32 ntuples;
atp = SearchSysCacheTuple(ATTNUM, atp = SearchSysCacheTuple(ATTNUM,
ObjectIdGetDatum(relid), ObjectIdGetDatum(relid),
Int16GetDatum(attnum), Int16GetDatum(attnum),
0, 0); 0, 0);
if (!HeapTupleIsValid(atp)) if (! HeapTupleIsValid(atp))
{ elog(ERROR, "getattproperties: no attribute tuple %u %d",
elog(ERROR, "getattdisbursion: no attribute tuple %u %d", relid, (int) attnum);
relid, attnum); att_tup = (Form_pg_attribute) GETSTRUCT(atp);
return 0;
} *typid = att_tup->atttypid;
nvals = ((Form_pg_attribute) GETSTRUCT(atp))->attdisbursion; *typlen = att_tup->attlen;
if (nvals > 0) *typbyval = att_tup->attbyval;
return nvals; *typmod = att_tup->atttypmod;
atp = SearchSysCacheTuple(RELOID,
ObjectIdGetDatum(relid),
0, 0, 0);
/*
* XXX -- use number of tuples as number of distinctive values just
* for now, in case number of distinctive values is not cached
*/
if (!HeapTupleIsValid(atp))
{
elog(ERROR, "getattdisbursion: no relation tuple %u", relid);
return 0;
}
ntuples = ((Form_pg_class) GETSTRUCT(atp))->reltuples;
/* Look above how nvals is used. - vadim 04/09/97 */
if (ntuples > 0)
nvals = 1.0 / ntuples;
return nvals;
} }
/* /*
* gethilokey - Returns a pointer to strings containing * getattstatistics
* the high and low keys within an attribute. * Retrieve the pg_statistic data for an attribute.
* Returns 'false' if no stats are available.
*
* Inputs:
* 'relid' and 'attnum' are the relation and attribute number.
* 'typid' and 'typmod' are the type and typmod of the column,
* which the caller must already have looked up.
* *
* Currently returns "0", and "0" in high and low if the statistic * Outputs:
* catalog does not contain the proper tuple. Eventually, the * The available stats are nullfrac, commonfrac, commonval, loval, hival.
* statistic demon should have the tuple maintained, and it should * The caller need not retrieve all five --- pass NULL pointers for the
* elog() if the tuple is missing. * unwanted values.
* *
* XXX Question: is this worth sticking in the catalog caches, * commonval, loval, hival are returned as Datums holding the internal
* or will this get invalidated too often? * representation of the values. (Note that these should be pfree'd
* after use if the data type is not by-value.)
*
* XXX currently, this does a linear search of pg_statistic because there
* is no index nor syscache for pg_statistic. FIX THIS!
*/ */
static void static bool
gethilokey(Oid relid, getattstatistics(Oid relid, AttrNumber attnum, Oid typid, int32 typmod,
AttrNumber attnum, double *nullfrac,
Oid opid, double *commonfrac,
char **high, Datum *commonval,
char **low) Datum *loval,
Datum *hival)
{ {
Relation rel; Relation rel;
HeapScanDesc scan; HeapScanDesc scan;
static ScanKeyData key[3] = { static ScanKeyData key[2] = {
{0, Anum_pg_statistic_starelid, F_OIDEQ, {0, 0, F_OIDEQ}}, {0, Anum_pg_statistic_starelid, F_OIDEQ, {0, 0, F_OIDEQ}},
{0, Anum_pg_statistic_staattnum, F_INT2EQ, {0, 0, F_INT2EQ}}, {0, Anum_pg_statistic_staattnum, F_INT2EQ, {0, 0, F_INT2EQ}}
{0, Anum_pg_statistic_staop, F_OIDEQ, {0, 0, F_OIDEQ}}
}; };
bool isnull; bool isnull;
HeapTuple tuple; HeapTuple tuple;
HeapTuple typeTuple;
FmgrInfo inputproc;
rel = heap_openr(StatisticRelationName); rel = heap_openr(StatisticRelationName);
key[0].sk_argument = ObjectIdGetDatum(relid); key[0].sk_argument = ObjectIdGetDatum(relid);
key[1].sk_argument = Int16GetDatum((int16) attnum); key[1].sk_argument = Int16GetDatum((int16) attnum);
key[2].sk_argument = ObjectIdGetDatum(opid);
scan = heap_beginscan(rel, 0, SnapshotNow, 3, key); scan = heap_beginscan(rel, 0, SnapshotNow, 2, key);
tuple = heap_getnext(scan, 0); tuple = heap_getnext(scan, 0);
if (!HeapTupleIsValid(tuple)) if (!HeapTupleIsValid(tuple))
{ {
*high = "n"; /* no such stats entry */
*low = "n"; heap_endscan(scan);
heap_close(rel);
return false;
}
/* /* We assume that there will only be one entry in pg_statistic
* XXX elog(ERROR, "gethilokey: statistic tuple not * for the given rel/att. Someday, VACUUM might store more than one...
* found"); */
*/ if (nullfrac)
return; *nullfrac = ((Form_pg_statistic) GETSTRUCT(tuple))->stanullfrac;
if (commonfrac)
*commonfrac = ((Form_pg_statistic) GETSTRUCT(tuple))->stacommonfrac;
/* Get the type input proc for the column datatype */
typeTuple = SearchSysCacheTuple(TYPOID,
ObjectIdGetDatum(typid),
0, 0, 0);
if (! HeapTupleIsValid(typeTuple))
elog(ERROR, "getattstatistics: Cache lookup failed for type %u",
typid);
fmgr_info(((Form_pg_type) GETSTRUCT(typeTuple))->typinput, &inputproc);
/* Values are variable-length fields, so cannot access as struct fields.
* Must do it the hard way with heap_getattr.
*/
if (commonval)
{
text *val = (text *) heap_getattr(tuple,
Anum_pg_statistic_stacommonval,
RelationGetDescr(rel),
&isnull);
if (isnull)
{
elog(DEBUG, "getattstatistics: stacommonval is null");
*commonval = PointerGetDatum(NULL);
}
else
{
char *strval = textout(val);
*commonval = (Datum)
(*fmgr_faddr(&inputproc)) (strval, typid, typmod);
pfree(strval);
}
} }
*high = textout((struct varlena *)
heap_getattr(tuple, if (loval)
Anum_pg_statistic_stahikey, {
RelationGetDescr(rel), text *val = (text *) heap_getattr(tuple,
&isnull)); Anum_pg_statistic_staloval,
if (isnull) RelationGetDescr(rel),
elog(DEBUG, "gethilokey: high key is null"); &isnull);
*low = textout((struct varlena *) if (isnull)
heap_getattr(tuple, {
Anum_pg_statistic_stalokey, elog(DEBUG, "getattstatistics: staloval is null");
RelationGetDescr(rel), *loval = PointerGetDatum(NULL);
&isnull)); }
if (isnull) else
elog(DEBUG, "gethilokey: low key is null"); {
char *strval = textout(val);
*loval = (Datum)
(*fmgr_faddr(&inputproc)) (strval, typid, typmod);
pfree(strval);
}
}
if (hival)
{
text *val = (text *) heap_getattr(tuple,
Anum_pg_statistic_stahival,
RelationGetDescr(rel),
&isnull);
if (isnull)
{
elog(DEBUG, "getattstatistics: stahival is null");
*hival = PointerGetDatum(NULL);
}
else
{
char *strval = textout(val);
*hival = (Datum)
(*fmgr_faddr(&inputproc)) (strval, typid, typmod);
pfree(strval);
}
}
heap_endscan(scan); heap_endscan(scan);
heap_close(rel); heap_close(rel);
return true;
}
/*
* getattdisbursion
* Retrieve the disbursion statistic for an attribute,
* or produce an estimate if no info is available.
*/
static double
getattdisbursion(Oid relid, AttrNumber attnum)
{
HeapTuple atp;
double disbursion;
int32 ntuples;
atp = SearchSysCacheTuple(ATTNUM,
ObjectIdGetDatum(relid),
Int16GetDatum(attnum),
0, 0);
if (!HeapTupleIsValid(atp))
{
/* this should not happen */
elog(ERROR, "getattdisbursion: no attribute tuple %u %d",
relid, attnum);
return 0.1;
}
disbursion = ((Form_pg_attribute) GETSTRUCT(atp))->attdisbursion;
if (disbursion > 0.0)
return disbursion;
/* VACUUM ANALYZE has not stored a disbursion statistic for us.
* Produce an estimate = 1/numtuples. This may produce
* unreasonably small estimates for large tables, so limit
* the estimate to no less than 0.01.
*/
atp = SearchSysCacheTuple(RELOID,
ObjectIdGetDatum(relid),
0, 0, 0);
if (!HeapTupleIsValid(atp))
{
/* this should not happen */
elog(ERROR, "getattdisbursion: no relation tuple %u", relid);
return 0.1;
}
ntuples = ((Form_pg_class) GETSTRUCT(atp))->reltuples;
if (ntuples > 0)
disbursion = 1.0 / (double) ntuples;
if (disbursion < 0.01)
disbursion = 0.01;
return disbursion;
} }
float64 float64
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* $Id: pg_statistic.h,v 1.6 1999/02/13 23:21:15 momjian Exp $ * $Id: pg_statistic.h,v 1.7 1999/08/01 04:54:21 tgl Exp $
* *
* NOTES * NOTES
* the genbki.sh script reads this file and generates .bki * the genbki.sh script reads this file and generates .bki
...@@ -32,11 +32,32 @@ ...@@ -32,11 +32,32 @@
*/ */
CATALOG(pg_statistic) CATALOG(pg_statistic)
{ {
Oid starelid; /* These fields form the unique key for the entry: */
int2 staattnum; Oid starelid; /* relation containing attribute */
Oid staop; int2 staattnum; /* attribute (column) stats are for */
text stalokey; /* VARIABLE LENGTH FIELD */ Oid staop; /* '<' comparison op used for lo/hi vals */
text stahikey; /* VARIABLE LENGTH FIELD */ /* Note: the current VACUUM code will never produce more than one entry
* per column, but in theory there could be multiple entries if a datatype
* has more than one useful ordering operator. Also, the current code
* will not write an entry unless it found at least one non-NULL value
* in the column; so the remaining fields will never be NULL.
*/
/* These fields contain the stats about the column indicated by the key */
float4 stanullfrac; /* the fraction of the entries that are NULL */
float4 stacommonfrac; /* the fraction that are the most common val */
/* THE REST OF THESE ARE VARIABLE LENGTH FIELDS.
* They cannot be accessed as C struct entries; you have to use the
* full field access machinery (heap_getattr) for them.
*
* All three of these are text representations of data values of the
* column's data type. To re-create the actual Datum, do
* datatypein(textout(givenvalue)).
*/
text stacommonval; /* most common non-null value in column */
text staloval; /* smallest non-null value in column */
text stahival; /* largest non-null value in column */
} FormData_pg_statistic; } FormData_pg_statistic;
/* ---------------- /* ----------------
...@@ -50,11 +71,14 @@ typedef FormData_pg_statistic *Form_pg_statistic; ...@@ -50,11 +71,14 @@ typedef FormData_pg_statistic *Form_pg_statistic;
* compiler constants for pg_statistic * compiler constants for pg_statistic
* ---------------- * ----------------
*/ */
#define Natts_pg_statistic 5 #define Natts_pg_statistic 8
#define Anum_pg_statistic_starelid 1 #define Anum_pg_statistic_starelid 1
#define Anum_pg_statistic_staattnum 2 #define Anum_pg_statistic_staattnum 2
#define Anum_pg_statistic_staop 3 #define Anum_pg_statistic_staop 3
#define Anum_pg_statistic_stalokey 4 #define Anum_pg_statistic_stanullfrac 4
#define Anum_pg_statistic_stahikey 5 #define Anum_pg_statistic_stacommonfrac 5
#define Anum_pg_statistic_stacommonval 6
#define Anum_pg_statistic_staloval 7
#define Anum_pg_statistic_stahival 8
#endif /* PG_STATISTIC_H */ #endif /* PG_STATISTIC_H */
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* $Id: vacuum.h,v 1.22 1999/07/15 15:21:03 momjian Exp $ * $Id: vacuum.h,v 1.23 1999/08/01 04:54:25 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -67,22 +67,23 @@ typedef struct ...@@ -67,22 +67,23 @@ typedef struct
guess2, guess2,
max, max,
min; min;
int16 best_len, int best_len,
guess1_len, guess1_len,
guess2_len, guess2_len,
max_len, max_len,
min_len; min_len;
int32 best_cnt, long best_cnt,
guess1_cnt, guess1_cnt,
guess1_hits, guess1_hits,
guess2_hits, guess2_hits,
null_cnt, null_cnt,
nonnull_cnt; nonnull_cnt,
int32 max_cnt, max_cnt,
min_cnt; min_cnt;
FmgrInfo f_cmpeq, FmgrInfo f_cmpeq,
f_cmplt, f_cmplt,
f_cmpgt; f_cmpgt;
Oid op_cmplt;
regproc outfunc; regproc outfunc;
bool initialized; bool initialized;
} VacAttrStats; } VacAttrStats;
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* $Id: builtins.h,v 1.84 1999/07/16 17:07:39 momjian Exp $ * $Id: builtins.h,v 1.85 1999/08/01 04:54:20 tgl Exp $
* *
* NOTES * NOTES
* This should normally only be included by fmgr.h. * This should normally only be included by fmgr.h.
...@@ -372,10 +372,10 @@ extern Oid regproctooid(RegProcedure rp); ...@@ -372,10 +372,10 @@ extern Oid regproctooid(RegProcedure rp);
#define RegprocToOid(rp) regproctooid(rp) #define RegprocToOid(rp) regproctooid(rp)
/* selfuncs.c */ /* selfuncs.c */
extern float64 eqsel(Oid opid, Oid relid, AttrNumber attno, char *value, int32 flag); extern float64 eqsel(Oid opid, Oid relid, AttrNumber attno, Datum value, int32 flag);
extern float64 neqsel(Oid opid, Oid relid, AttrNumber attno, char *value, int32 flag); extern float64 neqsel(Oid opid, Oid relid, AttrNumber attno, Datum value, int32 flag);
extern float64 intltsel(Oid opid, Oid relid, AttrNumber attno, int32 value, int32 flag); extern float64 intltsel(Oid opid, Oid relid, AttrNumber attno, Datum value, int32 flag);
extern float64 intgtsel(Oid opid, Oid relid, AttrNumber attno, int32 value, int32 flag); extern float64 intgtsel(Oid opid, Oid relid, AttrNumber attno, Datum value, int32 flag);
extern float64 eqjoinsel(Oid opid, Oid relid1, AttrNumber attno1, Oid relid2, AttrNumber attno2); extern float64 eqjoinsel(Oid opid, Oid relid1, AttrNumber attno1, Oid relid2, AttrNumber attno2);
extern float64 neqjoinsel(Oid opid, Oid relid1, AttrNumber attno1, Oid relid2, AttrNumber attno2); extern float64 neqjoinsel(Oid opid, Oid relid1, AttrNumber attno1, Oid relid2, AttrNumber attno2);
extern float64 intltjoinsel(Oid opid, Oid relid1, AttrNumber attno1, Oid relid2, AttrNumber attno2); extern float64 intltjoinsel(Oid opid, Oid relid1, AttrNumber attno1, Oid relid2, AttrNumber attno2);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment