diff options
Diffstat (limited to 'src/backend/utils/adt/selfuncs.c')
-rw-r--r-- | src/backend/utils/adt/selfuncs.c | 585 |
1 files changed, 585 insertions, 0 deletions
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c new file mode 100644 index 00000000000..8ee93b768d3 --- /dev/null +++ b/src/backend/utils/adt/selfuncs.c @@ -0,0 +1,585 @@ +/*------------------------------------------------------------------------- + * + * selfuncs.c-- + * Selectivity functions for system catalogs and builtin types + * + * These routines are registered in the operator catalog in the + * "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 + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.1.1.1 1996/07/09 06:22:05 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <stdio.h> +#include <string.h> + +#include "access/heapam.h" +#include "utils/tqual.h" /* for NowTimeQual */ +#include "fmgr.h" +#include "utils/builtins.h" /* for textout() prototype + and where the declarations go */ +#include "utils/elog.h" +#include "utils/palloc.h" + +#include "catalog/catname.h" +#include "utils/syscache.h" +#include "utils/lsyscache.h" /* for get_oprrest() */ +#include "catalog/pg_statistic.h" + + +/* N is not a valid var/constant or relation id */ +#define NONVALUE(N) ((N) == -1) + +/* + * generalize the test for functional index selectivity request + */ +#define FunctionalSelectivity(nIndKeys,attNum) (attNum==InvalidAttrNumber) + +static int32 getattnvals(Oid relid, AttrNumber attnum); +static void gethilokey(Oid relid, AttrNumber attnum, Oid opid, + char **high, char **low); + + +/* + * eqsel - Selectivity of "=" for any data type. + */ +float64 +eqsel(Oid opid, + Oid relid, + AttrNumber attno, + char *value, + int32 flag) +{ + int32 nvals; + float64 result; + + result = (float64) palloc(sizeof(float64data)); + if (NONVALUE(attno) || NONVALUE(relid)) + *result = 0.1; + else { + nvals = getattnvals(relid, (int) attno); + if (nvals == 0) + *result = 0.0; + else + *result = 1.0 / nvals; + } + return(result); +} + +/* + * neqsel - Selectivity of "!=" for any data type. + */ +float64 +neqsel(Oid opid, + Oid relid, + AttrNumber attno, + char *value, + int32 flag) +{ + float64 result; + + result = eqsel(opid, relid, attno, value, flag); + *result = 1.0 - *result; + return(result); +} + +/* + * intltsel - Selectivity of "<" for integers. + * Should work for both longs and shorts. + */ +float64 +intltsel(Oid opid, + Oid relid, + AttrNumber attno, + int32 value, + int32 flag) +{ + float64 result; + char *highchar, *lowchar; + long val, high, low, top, bottom; + + result = (float64) palloc(sizeof(float64data)); + if (NONVALUE(attno) || NONVALUE(relid)) + *result = 1.0 / 3; + else { + /* XXX val = atol(value);*/ + val = value; + gethilokey(relid, (int) attno, opid, &highchar, &lowchar); + if (*highchar == 'n' || *lowchar == 'n') { + *result = 1.0/3.0; + return (result); + } + high = atol(highchar); + low = atol(lowchar); + if ((flag & SEL_RIGHT && val < low) || + (!(flag & SEL_RIGHT) && val > high)) { + int nvals; + nvals = getattnvals(relid, (int) attno); + if (nvals == 0) + *result = 1.0 / 3.0; + else + *result = 3.0 / nvals; + }else { + bottom = high - low; + if (bottom == 0) + ++bottom; + if (flag & SEL_RIGHT) + top = val - low; + else + top = high - val; + if (top > bottom) + *result = 1.0; + else { + if (top == 0) + ++top; + *result = ((1.0 * top) / bottom); + } + } + } + return(result); +} + +/* + * intgtsel - Selectivity of ">" for integers. + * Should work for both longs and shorts. + */ +float64 +intgtsel(Oid opid, + Oid relid, + AttrNumber attno, + int32 value, + int32 flag) +{ + float64 result; + int notflag; + + if (flag & 0) + notflag = flag & ~SEL_RIGHT; + else + notflag = flag | SEL_RIGHT; + result = intltsel(opid, relid, attno, value, (int32) notflag); + return(result); +} + +/* + * eqjoinsel - Join selectivity of "=" + */ +float64 +eqjoinsel(Oid opid, + Oid relid1, + AttrNumber attno1, + Oid relid2, + AttrNumber attno2) +{ + float64 result; + int32 num1, num2, max; + + result = (float64) palloc(sizeof(float64data)); + if (NONVALUE(attno1) || NONVALUE(relid1) || + NONVALUE(attno2) || NONVALUE(relid2)) + *result = 0.1; + else { + num1 = getattnvals(relid1, (int) attno1); + num2 = getattnvals(relid2, (int) attno2); + max = (num1 > num2) ? num1 : num2; + if (max == 0) + *result = 1.0; + else + *result = 1.0 / max; + } + return(result); +} + +/* + * neqjoinsel - Join selectivity of "!=" + */ +float64 +neqjoinsel(Oid opid, + Oid relid1, + AttrNumber attno1, + Oid relid2, + AttrNumber attno2) +{ + float64 result; + + result = eqjoinsel(opid, relid1, attno1, relid2, attno2); + *result = 1.0 - *result; + return(result); +} + +/* + * intltjoinsel - Join selectivity of "<" + */ +float64 +intltjoinsel(Oid opid, + Oid relid1, + AttrNumber attno1, + Oid relid2, + AttrNumber attno2) +{ + float64 result; + + result = (float64) palloc(sizeof(float64data)); + *result = 1.0 / 3.0; + return(result); +} + +/* + * intgtjoinsel - Join selectivity of ">" + */ +float64 +intgtjoinsel(Oid opid, + Oid relid1, + AttrNumber attno1, + Oid relid2, + AttrNumber attno2) +{ + float64 result; + + result = (float64) palloc(sizeof(float64data)); + *result = 1.0 / 3.0; + return(result); +} + +/* + * getattnvals - Retrieves the number of values within an attribute. + * + * Note: + * getattnvals 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 int32 +getattnvals(Oid relid, AttrNumber attnum) +{ + HeapTuple atp; + int nvals; + + atp = SearchSysCacheTuple(ATTNUM, + ObjectIdGetDatum(relid), + Int16GetDatum(attnum), + 0,0); + if (!HeapTupleIsValid(atp)) { + elog(WARN, "getattnvals: no attribute tuple %d %d", + relid, attnum); + return(0); + } + nvals = ((AttributeTupleForm ) GETSTRUCT(atp))->attnvals; + if (nvals > 0) return(nvals); + + 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(WARN, "getattnvals: no relation tuple %d", relid); + return(0); + } + nvals = ((Form_pg_class) GETSTRUCT(atp))->reltuples; + return(nvals); +} + +/* + * gethilokey - Returns a pointer to strings containing + * the high and low keys within an attribute. + * + * Currently returns "0", and "0" in high and low if the statistic + * catalog does not contain the proper tuple. Eventually, the + * statistic demon should have the tuple maintained, and it should + * elog() if the tuple is missing. + * + * XXX Question: is this worth sticking in the catalog caches, + * or will this get invalidated too often? + */ +static void +gethilokey(Oid relid, + AttrNumber attnum, + Oid opid, + char **high, + char **low) +{ + register Relation rdesc; + register HeapScanDesc sdesc; + static ScanKeyData key[3] = { + { 0, Anum_pg_statistic_starelid, F_OIDEQ }, + { 0, Anum_pg_statistic_staattnum, F_INT2EQ }, + { 0, Anum_pg_statistic_staop, F_OIDEQ } + }; + bool isnull; + HeapTuple tuple; + + rdesc = heap_openr(StatisticRelationName); + + key[0].sk_argument = ObjectIdGetDatum(relid); + key[1].sk_argument = Int16GetDatum((int16) attnum); + key[2].sk_argument = ObjectIdGetDatum(opid); + sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 3, key); + tuple = heap_getnext(sdesc, 0, (Buffer *) NULL); + if (!HeapTupleIsValid(tuple)) { + *high = "n"; + *low = "n"; + /* XXX elog(WARN, "gethilokey: statistic tuple not found");*/ + return; + } + *high = textout((struct varlena *) + heap_getattr(tuple, + InvalidBuffer, + Anum_pg_statistic_stahikey, + RelationGetTupleDescriptor(rdesc), + &isnull)); + if (isnull) + elog(DEBUG, "gethilokey: high key is null"); + *low = textout((struct varlena *) + heap_getattr(tuple, + InvalidBuffer, + Anum_pg_statistic_stalokey, + RelationGetTupleDescriptor(rdesc), + &isnull)); + if (isnull) + elog(DEBUG, "gethilokey: low key is null"); + heap_endscan(sdesc); + heap_close(rdesc); +} + +float64 +btreesel(Oid operatorObjectId, + Oid indrelid, + AttrNumber attributeNumber, + char *constValue, + int32 constFlag, + int32 nIndexKeys, + Oid indexrelid) +{ + float64 result; + float64data resultData; + + if (FunctionalSelectivity(nIndexKeys, attributeNumber)) { + /* + * Need to call the functions selectivity + * function here. For now simply assume it's + * 1/3 since functions don't currently + * have selectivity functions + */ + resultData = 1.0 / 3.0; + result = &resultData; + } + else { + result = (float64)fmgr(get_oprrest (operatorObjectId), + (char*)operatorObjectId, + (char*)indrelid, + (char*)attributeNumber, + (char*)constValue, + (char*)constFlag, + NULL); + } + + if (!PointerIsValid(result)) + elog(WARN, "Btree Selectivity: bad pointer"); + if (*result < 0.0 || *result > 1.0) + elog(WARN, "Btree Selectivity: bad value %lf", *result); + + return(result); +} + +float64 +btreenpage(Oid operatorObjectId, + Oid indrelid, + AttrNumber attributeNumber, + char *constValue, + int32 constFlag, + int32 nIndexKeys, + Oid indexrelid) +{ + float64 temp, result; + float64data tempData; + HeapTuple atp; + int npage; + + if (FunctionalSelectivity(nIndexKeys, attributeNumber)) { + /* + * Need to call the functions selectivity + * function here. For now simply assume it's + * 1/3 since functions don't currently + * have selectivity functions + */ + tempData = 1.0 / 3.0; + temp = &tempData; + } + else { + temp = (float64)fmgr(get_oprrest (operatorObjectId), + (char*)operatorObjectId, + (char*)indrelid, + (char*)attributeNumber, + (char*)constValue, + (char*)constFlag, + NULL); + } + atp = SearchSysCacheTuple(RELOID, + ObjectIdGetDatum(indexrelid), + 0,0,0); + if (!HeapTupleIsValid(atp)) { + elog(WARN, "btreenpage: no index tuple %d", indexrelid); + return(0); + } + + npage = ((Form_pg_class) GETSTRUCT(atp))->relpages; + result = (float64)palloc(sizeof(float64data)); + *result = *temp * npage; + return(result); +} + +float64 +hashsel(Oid operatorObjectId, + Oid indrelid, + AttrNumber attributeNumber, + char *constValue, + int32 constFlag, + int32 nIndexKeys, + Oid indexrelid) +{ + + float64 result; + float64data resultData; + HeapTuple atp; + int ntuples; + + if (FunctionalSelectivity(nIndexKeys, attributeNumber)) { + /* + * Need to call the functions selectivity + * function here. For now simply use 1/Number of Tuples + * since functions don't currently + * have selectivity functions + */ + + atp = SearchSysCacheTuple(RELOID, ObjectIdGetDatum(indexrelid), + 0,0,0); + if (!HeapTupleIsValid(atp)) { + elog(WARN, "hashsel: no index tuple %d", indexrelid); + return(0); + } + ntuples = ((Form_pg_class) GETSTRUCT(atp))->reltuples; + if (ntuples > 0) { + resultData = 1.0 / (float64data) ntuples; + } + else { + resultData = (float64data) (1.0 / 100.0); + } + result = &resultData; + + } + else { + result = (float64)fmgr(get_oprrest (operatorObjectId), + (char*)operatorObjectId, + (char*)indrelid, + (char*)attributeNumber, + (char*)constValue, + (char*)constFlag, + NULL); + } + + if (!PointerIsValid(result)) + elog(WARN, "Hash Table Selectivity: bad pointer"); + if (*result < 0.0 || *result > 1.0) + elog(WARN, "Hash Table Selectivity: bad value %lf", *result); + + return(result); + + +} + +float64 +hashnpage(Oid operatorObjectId, + Oid indrelid, + AttrNumber attributeNumber, + char *constValue, + int32 constFlag, + int32 nIndexKeys, + Oid indexrelid) +{ + float64 temp, result; + float64data tempData; + HeapTuple atp; + int npage; + int ntuples; + + atp = SearchSysCacheTuple(RELOID, ObjectIdGetDatum(indexrelid), + 0,0,0); + if (!HeapTupleIsValid(atp)) { + elog(WARN, "hashsel: no index tuple %d", indexrelid); + return(0); + } + + + if (FunctionalSelectivity(nIndexKeys, attributeNumber)) { + /* + * Need to call the functions selectivity + * function here. For now, use 1/Number of Tuples + * since functions don't currently + * have selectivity functions + */ + + ntuples = ((Form_pg_class) GETSTRUCT(atp))->reltuples; + if (ntuples > 0) { + tempData = 1.0 / (float64data) ntuples; + } + else { + tempData = (float64data) (1.0 / 100.0); + } + temp = &tempData; + + } + else { + temp = (float64)fmgr(get_oprrest (operatorObjectId), + (char*)operatorObjectId, + (char*)indrelid, + (char*)attributeNumber, + (char*)constValue, + (char*)constFlag, + NULL); + } + + npage = ((Form_pg_class) GETSTRUCT(atp))->relpages; + result = (float64)palloc(sizeof(float64data)); + *result = *temp * npage; + return(result); +} + + +float64 +rtsel(Oid operatorObjectId, + Oid indrelid, + AttrNumber attributeNumber, + char *constValue, + int32 constFlag, + int32 nIndexKeys, + Oid indexrelid) +{ + return (btreesel(operatorObjectId, indrelid, attributeNumber, + constValue, constFlag, nIndexKeys, indexrelid)); +} + +float64 +rtnpage(Oid operatorObjectId, + Oid indrelid, + AttrNumber attributeNumber, + char *constValue, + int32 constFlag, + int32 nIndexKeys, + Oid indexrelid) +{ + return (btreenpage(operatorObjectId, indrelid, attributeNumber, + constValue, constFlag, nIndexKeys, indexrelid)); +} |