Vadim B. Mikheev fa2629b7ea Fix (hack) IndexSelectivity():
use sum(npages)/((nkeys == 1) ? 1 : nkeys + 1) as expected index page
estimation for multi-key quals - instead of sum(npages).
In old code npages for x > 10 and x < 20 is twice as for x > 10 - cool ?
1997-04-09 01:52:04 +00:00

598 lines
17 KiB
C

/*-------------------------------------------------------------------------
*
* plancat.c--
* routines for accessing the system catalogs
*
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.5 1997/04/09 01:52:04 vadim Exp $
*
*-------------------------------------------------------------------------
*/
#include <stdio.h>
#include "postgres.h"
#include "access/heapam.h"
#include "access/genam.h"
#include "access/htup.h"
#include "access/itup.h"
#include "catalog/catname.h"
#include "catalog/pg_amop.h"
#include "catalog/pg_index.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_version.h"
#include "parser/parsetree.h" /* for getrelid() */
#include "fmgr.h"
#include "optimizer/internal.h"
#include "optimizer/plancat.h"
#include "utils/syscache.h"
#ifndef HAVE_MEMMOVE
# include <regex/utils.h>
#else
# include <string.h>
#endif
static void IndexSelectivity(Oid indexrelid, Oid indrelid, int32 nIndexKeys,
Oid AccessMethodOperatorClasses[], Oid operatorObjectIds[],
int32 varAttributeNumbers[], char *constValues[], int32 constFlags[],
float *idxPages, float *idxSelec);
/*
* relation-info -
* Retrieves catalog information for a given relation. Given the oid of
* the relation, return the following information:
* whether the relation has secondary indices
* number of pages
* number of tuples
*/
void
relation_info(Query *root, Index relid,
bool *hasindex, int *pages, int *tuples)
{
HeapTuple relationTuple;
Form_pg_class relation;
Oid relationObjectId;
relationObjectId = getrelid(relid, root->rtable);
relationTuple = SearchSysCacheTuple(RELOID,
ObjectIdGetDatum(relationObjectId),
0,0,0);
if (HeapTupleIsValid(relationTuple)) {
relation = (Form_pg_class)GETSTRUCT(relationTuple);
*hasindex = (relation->relhasindex) ? TRUE : FALSE;
*pages = relation->relpages;
*tuples = relation->reltuples;
} else {
elog(WARN, "RelationCatalogInformation: Relation %d not found",
relationObjectId);
}
return;
}
/*
* index-info--
* Retrieves catalog information on an index on a given relation.
*
* The index relation is opened on the first invocation. The current
* retrieves the next index relation within the catalog that has not
* already been retrieved by a previous call. The index catalog
* is closed when no more indices for 'relid' can be found.
*
* 'first' is 1 if this is the first call
*
* Returns true if successful and false otherwise. Index info is returned
* via the transient data structure 'info'.
*
*/
bool
index_info(Query *root, bool first, int relid, IdxInfoRetval *info)
{
register i;
HeapTuple indexTuple, amopTuple;
IndexTupleForm index;
Relation indexRelation;
uint16 amstrategy;
Oid relam;
Oid indrelid;
static Relation relation = (Relation) NULL;
static HeapScanDesc scan = (HeapScanDesc) NULL;
static ScanKeyData indexKey;
/* find the oid of the indexed relation */
indrelid = getrelid(relid, root->rtable);
memset(info, 0, sizeof(IdxInfoRetval));
/*
* the maximum number of elements in each of the following arrays is
* 8. We allocate one more for a terminating 0 to indicate the end
* of the array.
*/
info->indexkeys = (int *)palloc(sizeof(int)*9);
memset(info->indexkeys, 0, sizeof(int)*9);
info->orderOprs = (Oid *)palloc(sizeof(Oid)*9);
memset(info->orderOprs, 0, sizeof(Oid)*9);
info->classlist = (Oid *)palloc(sizeof(Oid)*9);
memset(info->classlist, 0, sizeof(Oid)*9);
/* Find an index on the given relation */
if (first) {
if (RelationIsValid(relation))
heap_close(relation);
if (HeapScanIsValid(scan))
heap_endscan(scan);
ScanKeyEntryInitialize(&indexKey, 0,
Anum_pg_index_indrelid,
F_OIDEQ,
ObjectIdGetDatum(indrelid));
relation = heap_openr(IndexRelationName);
scan = heap_beginscan(relation, 0, NowTimeQual,
1, &indexKey);
}
if (!HeapScanIsValid(scan))
elog(WARN, "index_info: scan not started");
indexTuple = heap_getnext(scan, 0, (Buffer *) NULL);
if (!HeapTupleIsValid(indexTuple)) {
heap_endscan(scan);
heap_close(relation);
scan = (HeapScanDesc) NULL;
relation = (Relation) NULL;
return(0);
}
/* Extract info from the index tuple */
index = (IndexTupleForm)GETSTRUCT(indexTuple);
info->relid = index->indexrelid; /* index relation */
for (i = 0; i < 8; i++)
info->indexkeys[i] = index->indkey[i];
for (i = 0; i < 8; i++)
info->classlist[i] = index->indclass[i];
info->indproc = index->indproc; /* functional index ?? */
/* partial index ?? */
if (VARSIZE(&index->indpred) != 0) {
/*
* The memory allocated here for the predicate (in lispReadString)
* only needs to stay around until it's used in find_index_paths,
* which is all within a command, so the automatic pfree at end
* of transaction should be ok.
*/
char *predString;
predString = fmgr(F_TEXTOUT, &index->indpred);
info->indpred = (Node*)stringToNode(predString);
pfree(predString);
}
/* Extract info from the relation descriptor for the index */
indexRelation = index_open(index->indexrelid);
#ifdef notdef
/* XXX should iterate through strategies -- but how? use #1 for now */
amstrategy = indexRelation->rd_am->amstrategies;
#endif /* notdef */
amstrategy = 1;
relam = indexRelation->rd_rel->relam;
info->relam = relam;
info->pages = indexRelation->rd_rel->relpages;
info->tuples = indexRelation->rd_rel->reltuples;
heap_close(indexRelation);
/*
* Find the index ordering keys
*
* Must use indclass to know when to stop looking since with
* functional indices there could be several keys (args) for
* one opclass. -mer 27 Sept 1991
*/
for (i = 0; i < 8 && index->indclass[i]; ++i) {
amopTuple = SearchSysCacheTuple(AMOPSTRATEGY,
ObjectIdGetDatum(relam),
ObjectIdGetDatum(index->indclass[i]),
UInt16GetDatum(amstrategy),
0);
if (!HeapTupleIsValid(amopTuple))
elog(WARN, "index_info: no amop %d %d %d",
relam, index->indclass[i], amstrategy);
info->orderOprs[i] =
((Form_pg_amop)GETSTRUCT(amopTuple))->amopopr;
}
return(TRUE);
}
/*
* index-selectivity--
*
* Call util/plancat.c:IndexSelectivity with the indicated arguments.
*
* 'indid' is the index OID
* 'classes' is a list of index key classes
* 'opnos' is a list of index key operator OIDs
* 'relid' is the OID of the relation indexed
* 'attnos' is a list of the relation attnos which the index keys over
* 'values' is a list of the values of the clause's constants
* 'flags' is a list of fixnums which describe the constants
* 'nkeys' is the number of index keys
*
* Returns two floats: index pages and index selectivity in 'idxPages' and
* 'idxSelec'.
*
*/
void
index_selectivity(Oid indid,
Oid *classes,
List *opnos,
Oid relid,
List *attnos,
List *values,
List *flags,
int32 nkeys,
float *idxPages,
float *idxSelec)
{
Oid *opno_array;
int *attno_array, *flag_array;
char **value_array;
int i = 0;
List *xopno, *xattno, *value, *flag;
if (length(opnos)!=nkeys || length(attnos)!=nkeys ||
length(values)!=nkeys || length(flags)!=nkeys) {
*idxPages = 0.0;
*idxSelec = 1.0;
return;
}
opno_array = (Oid *)palloc(nkeys*sizeof(Oid));
attno_array = (int *)palloc(nkeys*sizeof(int32));
value_array = (char **)palloc(nkeys*sizeof(char *));
flag_array = (int *)palloc(nkeys*sizeof(int32));
i = 0;
foreach(xopno, opnos) {
opno_array[i++] = lfirsti(xopno);
}
i = 0;
foreach(xattno,attnos) {
attno_array[i++] = lfirsti(xattno);
}
i = 0;
foreach(value, values) {
value_array[i++] = (char *)lfirst(value);
}
i = 0;
foreach(flag,flags) {
flag_array[i++] = lfirsti(flag);
}
IndexSelectivity(indid,
relid,
nkeys,
classes, /* not used */
opno_array,
attno_array,
value_array,
flag_array,
idxPages,
idxSelec);
return;
}
/*
* restriction_selectivity in lisp system.--
*
* NOTE: The routine is now merged with RestrictionClauseSelectivity
* as defined in plancat.c
*
* Returns the selectivity of a specified operator.
* This code executes registered procedures stored in the
* operator relation, by calling the function manager.
*
* XXX The assumption in the selectivity procedures is that if the
* relation OIDs or attribute numbers are -1, then the clause
* isn't of the form (op var const).
*/
Cost
restriction_selectivity(Oid functionObjectId,
Oid operatorObjectId,
Oid relationObjectId,
AttrNumber attributeNumber,
char *constValue,
int32 constFlag)
{
float64 result;
result = (float64) fmgr(functionObjectId,
(char *) operatorObjectId,
(char *) relationObjectId,
(char *) (int)attributeNumber,
(char *) constValue,
(char *) constFlag,
NULL);
if (!PointerIsValid(result))
elog(WARN, "RestrictionClauseSelectivity: bad pointer");
if (*result < 0.0 || *result > 1.0)
elog(WARN, "RestrictionClauseSelectivity: bad value %lf",
*result);
return ((Cost)*result);
}
/*
* join_selectivity--
* Similarly, this routine is merged with JoinClauseSelectivity in
* plancat.c
*
* Returns the selectivity of an operator, given the join clause
* information.
*
* XXX The assumption in the selectivity procedures is that if the
* relation OIDs or attribute numbers are -1, then the clause
* isn't of the form (op var var).
*/
Cost
join_selectivity (Oid functionObjectId,
Oid operatorObjectId,
Oid relationObjectId1,
AttrNumber attributeNumber1,
Oid relationObjectId2,
AttrNumber attributeNumber2)
{
float64 result;
result = (float64) fmgr(functionObjectId,
(char *) operatorObjectId,
(char *) relationObjectId1,
(char *) (int)attributeNumber1,
(char *) relationObjectId2,
(char *) (int)attributeNumber2,
NULL);
if (!PointerIsValid(result))
elog(WARN, "JoinClauseSelectivity: bad pointer");
if (*result < 0.0 || *result > 1.0)
elog(WARN, "JoinClauseSelectivity: bad value %lf",
*result);
return((Cost)*result);
}
/*
* find_all_inheritors--
*
* Returns a LISP list containing the OIDs of all relations which
* inherits from the relation with OID 'inhparent'.
*/
List *
find_inheritance_children(Oid inhparent)
{
static ScanKeyData key[1] = {
{ 0, Anum_pg_inherits_inhparent, F_OIDEQ }
};
HeapTuple inheritsTuple;
Relation relation;
HeapScanDesc scan;
List *list = NIL;
Oid inhrelid;
fmgr_info(F_OIDEQ, &key[0].sk_func, &key[0].sk_nargs);
key[0].sk_argument = ObjectIdGetDatum((Oid)inhparent);
relation = heap_openr(InheritsRelationName);
scan = heap_beginscan(relation, 0, NowTimeQual, 1, key);
while (HeapTupleIsValid(inheritsTuple =
heap_getnext(scan, 0,
(Buffer *) NULL))) {
inhrelid = ((InheritsTupleForm)GETSTRUCT(inheritsTuple))->inhrel;
list = lappendi(list, inhrelid);
}
heap_endscan(scan);
heap_close(relation);
return(list);
}
/*
* VersionGetParents--
*
* Returns a LISP list containing the OIDs of all relations which are
* base relations of the relation with OID 'verrelid'.
*/
List *
VersionGetParents(Oid verrelid)
{
static ScanKeyData key[1] = {
{ 0, Anum_pg_version_verrelid, F_OIDEQ }
};
HeapTuple versionTuple;
Relation relation;
HeapScanDesc scan;
Oid verbaseid;
List *list= NIL;
fmgr_info(F_OIDEQ, &key[0].sk_func, &key[0].sk_nargs);
relation = heap_openr(VersionRelationName);
key[0].sk_argument = ObjectIdGetDatum(verrelid);
scan = heap_beginscan(relation, 0, NowTimeQual, 1, key);
for (;;) {
versionTuple = heap_getnext(scan, 0,
(Buffer *) NULL);
if (!HeapTupleIsValid(versionTuple))
break;
verbaseid = ((VersionTupleForm)
GETSTRUCT(versionTuple))->verbaseid;
list = lconsi(verbaseid, list);
key[0].sk_argument = ObjectIdGetDatum(verbaseid);
heap_rescan(scan, 0, key);
}
heap_endscan(scan);
heap_close(relation);
return(list);
}
/*****************************************************************************
*
*****************************************************************************/
/*
* IdexSelectivity--
*
* Retrieves the 'amopnpages' and 'amopselect' parameters for each
* AM operator when a given index (specified by 'indexrelid') is used.
* These two parameters are returned by copying them to into an array of
* floats.
*
* Assumption: the attribute numbers and operator ObjectIds are in order
* WRT to each other (otherwise, you have no way of knowing which
* AM operator class or attribute number corresponds to which operator.
*
* 'varAttributeNumbers' contains attribute numbers for variables
* 'constValues' contains the constant values
* 'constFlags' describes how to treat the constants in each clause
* 'nIndexKeys' describes how many keys the index actually has
*
* Returns 'selectivityInfo' filled with the sum of all pages touched
* and the product of each clause's selectivity.
*
*/
static void
IndexSelectivity(Oid indexrelid,
Oid indrelid,
int32 nIndexKeys,
Oid AccessMethodOperatorClasses[], /* XXX not used? */
Oid operatorObjectIds[],
int32 varAttributeNumbers[],
char *constValues[],
int32 constFlags[],
float *idxPages,
float *idxSelec)
{
register i, n;
HeapTuple indexTuple, amopTuple, indRel;
IndexTupleForm index;
Form_pg_amop amop;
Oid indclass;
float64data npages, select;
float64 amopnpages, amopselect;
Oid relam;
indRel = SearchSysCacheTuple(RELOID,
ObjectIdGetDatum(indexrelid),
0,0,0);
if (!HeapTupleIsValid(indRel))
elog(WARN, "IndexSelectivity: index %d not found",
indexrelid);
relam = ((Form_pg_class)GETSTRUCT(indRel))->relam;
indexTuple = SearchSysCacheTuple(INDEXRELID,
ObjectIdGetDatum(indexrelid),
0,0,0);
if (!HeapTupleIsValid(indexTuple))
elog(WARN, "IndexSelectivity: index %d not found",
indexrelid);
index = (IndexTupleForm)GETSTRUCT(indexTuple);
npages = 0.0;
select = 1.0;
for (n = 0; n < nIndexKeys; ++n) {
/*
* Find the AM class for this key.
*
* If the first attribute number is invalid then we have a
* functional index, and AM class is the first one defined
* since functional indices have exactly one key.
*/
indclass = (varAttributeNumbers[0] == InvalidAttrNumber) ?
index->indclass[0] : InvalidOid;
i = 0;
while ((i < nIndexKeys) && (indclass == InvalidOid)) {
if (varAttributeNumbers[n] == index->indkey[i]) {
indclass = index->indclass[i];
break;
}
i++;
}
if (!OidIsValid(indclass)) {
/*
* Presumably this means that we are using a functional
* index clause and so had no variable to match to
* the index key ... if not we are in trouble.
*/
elog(NOTICE, "IndexSelectivity: no key %d in index %d",
varAttributeNumbers[n], indexrelid);
continue;
}
amopTuple = SearchSysCacheTuple(AMOPOPID,
ObjectIdGetDatum(indclass),
ObjectIdGetDatum(operatorObjectIds[n]),
ObjectIdGetDatum(relam),
0);
if (!HeapTupleIsValid(amopTuple))
elog(WARN, "IndexSelectivity: no amop %d %d",
indclass, operatorObjectIds[n]);
amop = (Form_pg_amop)GETSTRUCT(amopTuple);
amopnpages = (float64) fmgr(amop->amopnpages,
(char *) operatorObjectIds[n],
(char *) indrelid,
(char *) varAttributeNumbers[n],
(char *) constValues[n],
(char *) constFlags[n],
(char *) nIndexKeys,
(char *) indexrelid);
#if 0
/*
* So cool guys! Npages for x > 10 and x < 20 is twice as
* npages for x > 10! - vadim 04/09/97
*/
npages += PointerIsValid(amopnpages) ? *amopnpages : 0.0;
if ((i = npages) < npages) /* ceil(npages)? */
npages += 1.0;
#endif
npages += PointerIsValid(amopnpages) ? *amopnpages : 0.0;
amopselect = (float64) fmgr(amop->amopselect,
(char *) operatorObjectIds[n],
(char *) indrelid,
(char *) varAttributeNumbers[n],
(char *) constValues[n],
(char *) constFlags[n],
(char *) nIndexKeys,
(char *) indexrelid);
select *= PointerIsValid(amopselect) ? *amopselect : 1.0;
}
/*
* Estimation of npages below is hack of course, but it's
* better than it was before. - vadim 04/09/97
*/
if ( nIndexKeys > 1 )
npages = npages / (1.0 + nIndexKeys);
*idxPages = ceil ((double)(npages/nIndexKeys));
*idxSelec = select;
}