Simplify and speed up mapping of index opfamilies to pathkeys.

Formerly we looked up the operators associated with each index (caching
them in relcache) and then the planner looked up the btree opfamily
containing such operators in order to build the btree-centric pathkey
representation that describes the index's sort order.  This is quite
pointless for btree indexes: we might as well just use the index's opfamily
information directly.  That saves syscache lookup cycles during planning,
and furthermore allows us to eliminate the relcache's caching of operators
altogether, which may help in reducing backend startup time.

I added code to plancat.c to perform the same type of double lookup
on-the-fly if it's ever faced with a non-btree amcanorder index AM.
If such a thing actually becomes interesting for production, we should
replace that logic with some more-direct method for identifying the
corresponding btree opfamily; but it's not worth spending effort on now.

There is considerably more to do pursuant to my recent proposal to get rid
of sort-operator-based representations of sort orderings, but this patch
grabs some of the low-hanging fruit.  I'll look at the remainder of that
work after the current commitfest.
This commit is contained in:
Tom Lane 2010-11-29 12:29:42 -05:00
parent 3c42efceb2
commit c0b5fac701
7 changed files with 218 additions and 259 deletions

View File

@ -380,7 +380,7 @@ find_usable_indexes(PlannerInfo *root, RelOptInfo *rel,
* how many of them are actually useful for this query. This is not
* relevant unless we are at top level.
*/
index_is_ordered = OidIsValid(index->fwdsortop[0]);
index_is_ordered = (index->sortopfamily != NULL);
if (index_is_ordered && possibly_useful_pathkeys &&
istoplevel && outer_rel == NULL)
{

View File

@ -36,12 +36,6 @@ static PathKey *make_canonical_pathkey(PlannerInfo *root,
EquivalenceClass *eclass, Oid opfamily,
int strategy, bool nulls_first);
static bool pathkey_is_redundant(PathKey *new_pathkey, List *pathkeys);
static PathKey *make_pathkey_from_sortinfo(PlannerInfo *root,
Expr *expr, Oid ordering_op,
bool nulls_first,
Index sortref,
bool create_it,
bool canonicalize);
static Var *find_indexkey_var(PlannerInfo *root, RelOptInfo *rel,
AttrNumber varattno);
static bool right_merge_direction(PlannerInfo *root, PathKey *pathkey);
@ -224,9 +218,9 @@ canonicalize_pathkeys(PlannerInfo *root, List *pathkeys)
/*
* make_pathkey_from_sortinfo
* Given an expression, a sortop, and a nulls-first flag, create
* a PathKey. If canonicalize = true, the result is a "canonical"
* PathKey, otherwise not. (But note it might be redundant anyway.)
* Given an expression and sort-order information, create a PathKey.
* If canonicalize = true, the result is a "canonical" PathKey,
* otherwise not. (But note it might be redundant anyway.)
*
* If the PathKey is being generated from a SortGroupClause, sortref should be
* the SortGroupClause's SortGroupRef; otherwise zero.
@ -240,46 +234,39 @@ canonicalize_pathkeys(PlannerInfo *root, List *pathkeys)
*/
static PathKey *
make_pathkey_from_sortinfo(PlannerInfo *root,
Expr *expr, Oid ordering_op,
Expr *expr,
Oid opfamily,
Oid opcintype,
bool reverse_sort,
bool nulls_first,
Index sortref,
bool create_it,
bool canonicalize)
{
Oid opfamily,
opcintype;
int16 strategy;
Oid equality_op;
List *opfamilies;
EquivalenceClass *eclass;
/*
* An ordering operator fully determines the behavior of its opfamily, so
* could only meaningfully appear in one family --- or perhaps two if one
* builds a reverse-sort opfamily, but there's not much point in that
* anymore. But EquivalenceClasses need to contain opfamily lists based
* on the family membership of equality operators, which could easily be
* bigger. So, look up the equality operator that goes with the ordering
* operator (this should be unique) and get its membership.
*/
strategy = reverse_sort ? BTGreaterStrategyNumber : BTLessStrategyNumber;
/* Find the operator in pg_amop --- failure shouldn't happen */
if (!get_ordering_op_properties(ordering_op,
&opfamily, &opcintype, &strategy))
elog(ERROR, "operator %u is not a valid ordering operator",
ordering_op);
/* Get matching equality operator */
/*
* EquivalenceClasses need to contain opfamily lists based on the family
* membership of mergejoinable equality operators, which could belong to
* more than one opfamily. So we have to look up the opfamily's equality
* operator and get its membership.
*/
equality_op = get_opfamily_member(opfamily,
opcintype,
opcintype,
BTEqualStrategyNumber);
if (!OidIsValid(equality_op)) /* shouldn't happen */
elog(ERROR, "could not find equality operator for ordering operator %u",
ordering_op);
elog(ERROR, "could not find equality operator for opfamily %u",
opfamily);
opfamilies = get_mergejoin_opfamilies(equality_op);
if (!opfamilies) /* certainly should find some */
elog(ERROR, "could not find opfamilies for ordering operator %u",
ordering_op);
elog(ERROR, "could not find opfamilies for equality operator %u",
equality_op);
/*
* When dealing with binary-compatible opclasses, we have to ensure that
@ -322,6 +309,42 @@ make_pathkey_from_sortinfo(PlannerInfo *root,
return makePathKey(eclass, opfamily, strategy, nulls_first);
}
/*
* make_pathkey_from_sortop
* Like make_pathkey_from_sortinfo, but work from a sort operator.
*
* This should eventually go away, but we need to restructure SortGroupClause
* first.
*/
static PathKey *
make_pathkey_from_sortop(PlannerInfo *root,
Expr *expr,
Oid ordering_op,
bool nulls_first,
Index sortref,
bool create_it,
bool canonicalize)
{
Oid opfamily,
opcintype;
int16 strategy;
/* Find the operator in pg_amop --- failure shouldn't happen */
if (!get_ordering_op_properties(ordering_op,
&opfamily, &opcintype, &strategy))
elog(ERROR, "operator %u is not a valid ordering operator",
ordering_op);
return make_pathkey_from_sortinfo(root,
expr,
opfamily,
opcintype,
(strategy == BTGreaterStrategyNumber),
nulls_first,
sortref,
create_it,
canonicalize);
}
/****************************************************************************
* PATHKEY COMPARISONS
@ -479,11 +502,10 @@ get_cheapest_fractional_path_for_pathkeys(List *paths,
* build_index_pathkeys
* Build a pathkeys list that describes the ordering induced by an index
* scan using the given index. (Note that an unordered index doesn't
* induce any ordering; such an index will have no sortop OIDS in
* its sortops arrays, and we will return NIL.)
* induce any ordering, so we return NIL.)
*
* If 'scandir' is BackwardScanDirection, attempt to build pathkeys
* representing a backwards scan of the index. Return NIL if can't do it.
* If 'scandir' is BackwardScanDirection, build pathkeys representing a
* backwards scan of the index.
*
* The result is canonical, meaning that redundant pathkeys are removed;
* it may therefore have fewer entries than there are index columns.
@ -500,12 +522,16 @@ build_index_pathkeys(PlannerInfo *root,
ScanDirection scandir)
{
List *retval = NIL;
ListCell *indexprs_item = list_head(index->indexprs);
ListCell *indexprs_item;
int i;
if (index->sortopfamily == NULL)
return NIL; /* non-orderable index */
indexprs_item = list_head(index->indexprs);
for (i = 0; i < index->ncolumns; i++)
{
Oid sortop;
bool reverse_sort;
bool nulls_first;
int ikey;
Expr *indexkey;
@ -513,18 +539,15 @@ build_index_pathkeys(PlannerInfo *root,
if (ScanDirectionIsBackward(scandir))
{
sortop = index->revsortop[i];
reverse_sort = !index->reverse_sort[i];
nulls_first = !index->nulls_first[i];
}
else
{
sortop = index->fwdsortop[i];
reverse_sort = index->reverse_sort[i];
nulls_first = index->nulls_first[i];
}
if (!OidIsValid(sortop))
break; /* no more orderable columns */
ikey = index->indexkeys[i];
if (ikey != 0)
{
@ -543,7 +566,9 @@ build_index_pathkeys(PlannerInfo *root,
/* OK, try to make a canonical pathkey for this sort key */
cpathkey = make_pathkey_from_sortinfo(root,
indexkey,
sortop,
index->sortopfamily[i],
index->opcintype[i],
reverse_sort,
nulls_first,
0,
false,
@ -892,13 +917,13 @@ make_pathkeys_for_sortclauses(PlannerInfo *root,
sortkey = (Expr *) get_sortgroupclause_expr(sortcl, tlist);
Assert(OidIsValid(sortcl->sortop));
pathkey = make_pathkey_from_sortinfo(root,
sortkey,
sortcl->sortop,
sortcl->nulls_first,
sortcl->tleSortGroupRef,
true,
canonicalize);
pathkey = make_pathkey_from_sortop(root,
sortkey,
sortcl->sortop,
sortcl->nulls_first,
sortcl->tleSortGroupRef,
true,
canonicalize);
/* Canonical form eliminates redundant ordering keys */
if (canonicalize)
@ -935,13 +960,13 @@ make_pathkeys_for_aggregate(PlannerInfo *root,
* We arbitrarily set nulls_first to false. Actually, a MIN/MAX agg can
* use either nulls ordering option, but that is dealt with elsewhere.
*/
pathkey = make_pathkey_from_sortinfo(root,
aggtarget,
aggsortop,
false, /* nulls_first */
0,
true,
false);
pathkey = make_pathkey_from_sortop(root,
aggtarget,
aggsortop,
false, /* nulls_first */
0,
true,
false);
return list_make1(pathkey);
}

View File

@ -189,19 +189,9 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
RelationGetForm(indexRelation)->reltablespace;
info->rel = rel;
info->ncolumns = ncolumns = index->indnatts;
/*
* Allocate per-column info arrays. To save a few palloc cycles
* we allocate all the Oid-type arrays in one request. We must
* pre-zero the sortop and nulls_first arrays in case the index is
* unordered.
*/
info->indexkeys = (int *) palloc(sizeof(int) * ncolumns);
info->opfamily = (Oid *) palloc0(sizeof(Oid) * (4 * ncolumns));
info->opcintype = info->opfamily + ncolumns;
info->fwdsortop = info->opcintype + ncolumns;
info->revsortop = info->fwdsortop + ncolumns;
info->nulls_first = (bool *) palloc0(sizeof(bool) * ncolumns);
info->opfamily = (Oid *) palloc(sizeof(Oid) * ncolumns);
info->opcintype = (Oid *) palloc(sizeof(Oid) * ncolumns);
for (i = 0; i < ncolumns; i++)
{
@ -219,49 +209,90 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
info->amhasgetbitmap = OidIsValid(indexRelation->rd_am->amgetbitmap);
/*
* Fetch the ordering operators associated with the index, if any.
* We expect that all ordering-capable indexes use btree's
* strategy numbers for the ordering operators.
* Fetch the ordering information for the index, if any.
*/
if (indexRelation->rd_am->amcanorder)
if (info->relam == BTREE_AM_OID)
{
int nstrat = indexRelation->rd_am->amstrategies;
/*
* If it's a btree index, we can use its opfamily OIDs
* directly as the sort ordering opfamily OIDs.
*/
Assert(indexRelation->rd_am->amcanorder);
info->sortopfamily = info->opfamily;
info->reverse_sort = (bool *) palloc(sizeof(bool) * ncolumns);
info->nulls_first = (bool *) palloc(sizeof(bool) * ncolumns);
for (i = 0; i < ncolumns; i++)
{
int16 opt = indexRelation->rd_indoption[i];
int fwdstrat;
int revstrat;
if (opt & INDOPTION_DESC)
info->reverse_sort[i] = (opt & INDOPTION_DESC) != 0;
info->nulls_first[i] = (opt & INDOPTION_NULLS_FIRST) != 0;
}
}
else if (indexRelation->rd_am->amcanorder)
{
/*
* Otherwise, identify the corresponding btree opfamilies by
* trying to map this index's "<" operators into btree. Since
* "<" uniquely defines the behavior of a sort order, this is
* a sufficient test.
*
* XXX This method is rather slow and also requires the
* undesirable assumption that the other index AM numbers its
* strategies the same as btree. It'd be better to have a way
* to explicitly declare the corresponding btree opfamily for
* each opfamily of the other index type. But given the lack
* of current or foreseeable amcanorder index types, it's not
* worth expending more effort on now.
*/
info->sortopfamily = (Oid *) palloc(sizeof(Oid) * ncolumns);
info->reverse_sort = (bool *) palloc(sizeof(bool) * ncolumns);
info->nulls_first = (bool *) palloc(sizeof(bool) * ncolumns);
for (i = 0; i < ncolumns; i++)
{
int16 opt = indexRelation->rd_indoption[i];
Oid ltopr;
Oid btopfamily;
Oid btopcintype;
int16 btstrategy;
info->reverse_sort[i] = (opt & INDOPTION_DESC) != 0;
info->nulls_first[i] = (opt & INDOPTION_NULLS_FIRST) != 0;
ltopr = get_opfamily_member(info->opfamily[i],
info->opcintype[i],
info->opcintype[i],
BTLessStrategyNumber);
if (OidIsValid(ltopr) &&
get_ordering_op_properties(ltopr,
&btopfamily,
&btopcintype,
&btstrategy) &&
btopcintype == info->opcintype[i] &&
btstrategy == BTLessStrategyNumber)
{
fwdstrat = BTGreaterStrategyNumber;
revstrat = BTLessStrategyNumber;
/* Successful mapping */
info->sortopfamily[i] = btopfamily;
}
else
{
fwdstrat = BTLessStrategyNumber;
revstrat = BTGreaterStrategyNumber;
/* Fail ... quietly treat index as unordered */
info->sortopfamily = NULL;
info->reverse_sort = NULL;
info->nulls_first = NULL;
break;
}
/*
* Index AM must have a fixed set of strategies for it to
* make sense to specify amcanorder, so we need not allow
* the case amstrategies == 0.
*/
if (fwdstrat > 0)
{
Assert(fwdstrat <= nstrat);
info->fwdsortop[i] = indexRelation->rd_operator[i * nstrat + fwdstrat - 1];
}
if (revstrat > 0)
{
Assert(revstrat <= nstrat);
info->revsortop[i] = indexRelation->rd_operator[i * nstrat + revstrat - 1];
}
info->nulls_first[i] = (opt & INDOPTION_NULLS_FIRST) != 0;
}
}
else
{
info->sortopfamily = NULL;
info->reverse_sort = NULL;
info->nulls_first = NULL;
}
/*
* Fetch the index expressions and predicate, if any. We must

View File

@ -4567,14 +4567,26 @@ get_actual_variable_range(PlannerInfo *root, VariableStatData *vardata,
* The first index column must match the desired variable and sort
* operator --- but we can use a descending-order index.
*/
if (sortop == index->fwdsortop[0])
indexscandir = ForwardScanDirection;
else if (sortop == index->revsortop[0])
indexscandir = BackwardScanDirection;
else
continue;
if (!match_index_to_operand(vardata->var, 0, index))
continue;
switch (get_op_opfamily_strategy(sortop, index->sortopfamily[0]))
{
case BTLessStrategyNumber:
if (index->reverse_sort[0])
indexscandir = BackwardScanDirection;
else
indexscandir = ForwardScanDirection;
break;
case BTGreaterStrategyNumber:
if (index->reverse_sort[0])
indexscandir = ForwardScanDirection;
else
indexscandir = BackwardScanDirection;
break;
default:
/* index doesn't match the sortop */
continue;
}
/*
* Found a suitable index to extract data from. We'll need an EState
@ -6150,12 +6162,18 @@ btcostestimate(PG_FUNCTION_ARGS)
if (HeapTupleIsValid(vardata.statsTuple))
{
Oid sortop;
float4 *numbers;
int nnumbers;
if (get_attstatsslot(vardata.statsTuple, InvalidOid, 0,
sortop = get_opfamily_member(index->opfamily[0],
index->opcintype[0],
index->opcintype[0],
BTLessStrategyNumber);
if (OidIsValid(sortop) &&
get_attstatsslot(vardata.statsTuple, InvalidOid, 0,
STATISTIC_KIND_CORRELATION,
index->fwdsortop[0],
sortop,
NULL,
NULL, NULL,
&numbers, &nnumbers))
@ -6165,6 +6183,9 @@ btcostestimate(PG_FUNCTION_ARGS)
Assert(nnumbers == 1);
varCorrelation = numbers[0];
if (index->reverse_sort[0])
varCorrelation = -varCorrelation;
if (index->ncolumns > 1)
*indexCorrelation = varCorrelation * 0.75;
else
@ -6172,25 +6193,6 @@ btcostestimate(PG_FUNCTION_ARGS)
free_attstatsslot(InvalidOid, NULL, 0, numbers, nnumbers);
}
else if (get_attstatsslot(vardata.statsTuple, InvalidOid, 0,
STATISTIC_KIND_CORRELATION,
index->revsortop[0],
NULL,
NULL, NULL,
&numbers, &nnumbers))
{
double varCorrelation;
Assert(nnumbers == 1);
varCorrelation = numbers[0];
if (index->ncolumns > 1)
*indexCorrelation = -varCorrelation * 0.75;
else
*indexCorrelation = -varCorrelation;
free_attstatsslot(InvalidOid, NULL, 0, numbers, nnumbers);
}
}
ReleaseVariableStats(vardata);

View File

@ -39,7 +39,6 @@
#include "catalog/index.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/pg_amop.h"
#include "catalog/pg_amproc.h"
#include "catalog/pg_attrdef.h"
#include "catalog/pg_authid.h"
@ -48,7 +47,6 @@
#include "catalog/pg_database.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_rewrite.h"
#include "catalog/pg_tablespace.h"
@ -84,10 +82,10 @@
*/
#define RELCACHE_INIT_FILENAME "pg_internal.init"
#define RELCACHE_INIT_FILEMAGIC 0x573265 /* version ID value */
#define RELCACHE_INIT_FILEMAGIC 0x573266 /* version ID value */
/*
* hardcoded tuple descriptors, generated by genbki.pl
* hardcoded tuple descriptors, contents generated by genbki.pl
*/
static const FormData_pg_attribute Desc_pg_class[Natts_pg_class] = {Schema_pg_class};
static const FormData_pg_attribute Desc_pg_attribute[Natts_pg_attribute] = {Schema_pg_attribute};
@ -185,19 +183,17 @@ do { \
/*
* Special cache for opclass-related information
*
* Note: only default operators and support procs get cached, ie, those with
* Note: only default support procs get cached, ie, those with
* lefttype = righttype = opcintype.
*/
typedef struct opclasscacheent
{
Oid opclassoid; /* lookup key: OID of opclass */
bool valid; /* set TRUE after successful fill-in */
StrategyNumber numStrats; /* max # of strategies (from pg_am) */
StrategyNumber numSupport; /* max # of support procs (from pg_am) */
Oid opcfamily; /* OID of opclass's family */
Oid opcintype; /* OID of opclass's declared input type */
Oid *operatorOids; /* strategy operators' OIDs */
RegProcedure *supportProcs; /* support procs */
RegProcedure *supportProcs; /* OIDs of support procedures */
} OpClassCacheEnt;
static HTAB *OpClassCache = NULL;
@ -231,15 +227,12 @@ static void AttrDefaultFetch(Relation relation);
static void CheckConstraintFetch(Relation relation);
static List *insert_ordered_oid(List *list, Oid datum);
static void IndexSupportInitialize(oidvector *indclass,
Oid *indexOperator,
RegProcedure *indexSupport,
Oid *opFamily,
Oid *opcInType,
StrategyNumber maxStrategyNumber,
StrategyNumber maxSupportNumber,
AttrNumber maxAttributeNumber);
static OpClassCacheEnt *LookupOpclassInfo(Oid operatorClassOid,
StrategyNumber numStrats,
StrategyNumber numSupport);
static void RelationCacheInitFileRemoveInDir(const char *tblspcpath);
static void unlink_initfile(const char *initfilename);
@ -980,7 +973,6 @@ RelationInitIndexAccessInfo(Relation relation)
MemoryContext indexcxt;
MemoryContext oldcontext;
int natts;
uint16 amstrategies;
uint16 amsupport;
/*
@ -1015,7 +1007,6 @@ RelationInitIndexAccessInfo(Relation relation)
if (natts != relation->rd_index->indnatts)
elog(ERROR, "relnatts disagrees with indnatts for index %u",
RelationGetRelid(relation));
amstrategies = aform->amstrategies;
amsupport = aform->amsupport;
/*
@ -1044,13 +1035,6 @@ RelationInitIndexAccessInfo(Relation relation)
relation->rd_opcintype = (Oid *)
MemoryContextAllocZero(indexcxt, natts * sizeof(Oid));
if (amstrategies > 0)
relation->rd_operator = (Oid *)
MemoryContextAllocZero(indexcxt,
natts * amstrategies * sizeof(Oid));
else
relation->rd_operator = NULL;
if (amsupport > 0)
{
int nsupport = natts * amsupport;
@ -1082,14 +1066,13 @@ RelationInitIndexAccessInfo(Relation relation)
indclass = (oidvector *) DatumGetPointer(indclassDatum);
/*
* Fill the operator and support procedure OID arrays, as well as the info
* about opfamilies and opclass input types. (aminfo and supportinfo are
* left as zeroes, and are filled on-the-fly when used)
* Fill the support procedure OID array, as well as the info about
* opfamilies and opclass input types. (aminfo and supportinfo are left
* as zeroes, and are filled on-the-fly when used)
*/
IndexSupportInitialize(indclass,
relation->rd_operator, relation->rd_support,
IndexSupportInitialize(indclass, relation->rd_support,
relation->rd_opfamily, relation->rd_opcintype,
amstrategies, amsupport, natts);
amsupport, natts);
/*
* Similarly extract indoption and copy it to the cache entry
@ -1118,22 +1101,19 @@ RelationInitIndexAccessInfo(Relation relation)
* Initializes an index's cached opclass information,
* given the index's pg_index.indclass entry.
*
* Data is returned into *indexOperator, *indexSupport, *opFamily, and
* *opcInType, which are arrays allocated by the caller.
* Data is returned into *indexSupport, *opFamily, and *opcInType,
* which are arrays allocated by the caller.
*
* The caller also passes maxStrategyNumber, maxSupportNumber, and
* maxAttributeNumber, since these indicate the size of the arrays
* it has allocated --- but in practice these numbers must always match
* those obtainable from the system catalog entries for the index and
* access method.
* The caller also passes maxSupportNumber and maxAttributeNumber, since these
* indicate the size of the arrays it has allocated --- but in practice these
* numbers must always match those obtainable from the system catalog entries
* for the index and access method.
*/
static void
IndexSupportInitialize(oidvector *indclass,
Oid *indexOperator,
RegProcedure *indexSupport,
Oid *opFamily,
Oid *opcInType,
StrategyNumber maxStrategyNumber,
StrategyNumber maxSupportNumber,
AttrNumber maxAttributeNumber)
{
@ -1148,16 +1128,11 @@ IndexSupportInitialize(oidvector *indclass,
/* look up the info for this opclass, using a cache */
opcentry = LookupOpclassInfo(indclass->values[attIndex],
maxStrategyNumber,
maxSupportNumber);
/* copy cached data into relcache entry */
opFamily[attIndex] = opcentry->opcfamily;
opcInType[attIndex] = opcentry->opcintype;
if (maxStrategyNumber > 0)
memcpy(&indexOperator[attIndex * maxStrategyNumber],
opcentry->operatorOids,
maxStrategyNumber * sizeof(Oid));
if (maxSupportNumber > 0)
memcpy(&indexSupport[attIndex * maxSupportNumber],
opcentry->supportProcs,
@ -1171,9 +1146,9 @@ IndexSupportInitialize(oidvector *indclass,
* This routine maintains a per-opclass cache of the information needed
* by IndexSupportInitialize(). This is more efficient than relying on
* the catalog cache, because we can load all the info about a particular
* opclass in a single indexscan of pg_amproc or pg_amop.
* opclass in a single indexscan of pg_amproc.
*
* The information from pg_am about expected range of strategy and support
* The information from pg_am about expected range of support function
* numbers is passed in, rather than being looked up, mainly because the
* caller will have it already.
*
@ -1187,7 +1162,6 @@ IndexSupportInitialize(oidvector *indclass,
*/
static OpClassCacheEnt *
LookupOpclassInfo(Oid operatorClassOid,
StrategyNumber numStrats,
StrategyNumber numSupport)
{
OpClassCacheEnt *opcentry;
@ -1223,16 +1197,8 @@ LookupOpclassInfo(Oid operatorClassOid,
{
/* Need to allocate memory for new entry */
opcentry->valid = false; /* until known OK */
opcentry->numStrats = numStrats;
opcentry->numSupport = numSupport;
if (numStrats > 0)
opcentry->operatorOids = (Oid *)
MemoryContextAllocZero(CacheMemoryContext,
numStrats * sizeof(Oid));
else
opcentry->operatorOids = NULL;
if (numSupport > 0)
opcentry->supportProcs = (RegProcedure *)
MemoryContextAllocZero(CacheMemoryContext,
@ -1242,7 +1208,6 @@ LookupOpclassInfo(Oid operatorClassOid,
}
else
{
Assert(numStrats == opcentry->numStrats);
Assert(numSupport == opcentry->numSupport);
}
@ -1273,7 +1238,7 @@ LookupOpclassInfo(Oid operatorClassOid,
/*
* We have to fetch the pg_opclass row to determine its opfamily and
* opcintype, which are needed to look up the operators and functions.
* opcintype, which are needed to look up related operators and functions.
* It'd be convenient to use the syscache here, but that probably doesn't
* work while bootstrapping.
*/
@ -1298,45 +1263,6 @@ LookupOpclassInfo(Oid operatorClassOid,
systable_endscan(scan);
heap_close(rel, AccessShareLock);
/*
* Scan pg_amop to obtain operators for the opclass. We only fetch the
* default ones (those with lefttype = righttype = opcintype).
*/
if (numStrats > 0)
{
ScanKeyInit(&skey[0],
Anum_pg_amop_amopfamily,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(opcentry->opcfamily));
ScanKeyInit(&skey[1],
Anum_pg_amop_amoplefttype,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(opcentry->opcintype));
ScanKeyInit(&skey[2],
Anum_pg_amop_amoprighttype,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(opcentry->opcintype));
rel = heap_open(AccessMethodOperatorRelationId, AccessShareLock);
scan = systable_beginscan(rel, AccessMethodStrategyIndexId, indexOK,
SnapshotNow, 3, skey);
while (HeapTupleIsValid(htup = systable_getnext(scan)))
{
Form_pg_amop amopform = (Form_pg_amop) GETSTRUCT(htup);
if (amopform->amopstrategy <= 0 ||
(StrategyNumber) amopform->amopstrategy > numStrats)
elog(ERROR, "invalid amopstrategy number %d for opclass %u",
amopform->amopstrategy, operatorClassOid);
opcentry->operatorOids[amopform->amopstrategy - 1] =
amopform->amopopr;
}
systable_endscan(scan);
heap_close(rel, AccessShareLock);
}
/*
* Scan pg_amproc to obtain support procs for the opclass. We only fetch
* the default ones (those with lefttype = righttype = opcintype).
@ -2907,18 +2833,14 @@ RelationCacheInitializePhase3(void)
IndexRelationId);
load_critical_index(OpclassOidIndexId,
OperatorClassRelationId);
load_critical_index(AccessMethodStrategyIndexId,
AccessMethodOperatorRelationId);
load_critical_index(AccessMethodProcedureIndexId,
AccessMethodProcedureRelationId);
load_critical_index(OperatorOidIndexId,
OperatorRelationId);
load_critical_index(RewriteRelRulenameIndexId,
RewriteRelationId);
load_critical_index(TriggerRelidNameIndexId,
TriggerRelationId);
#define NUM_CRITICAL_LOCAL_INDEXES 9 /* fix if you change list above */
#define NUM_CRITICAL_LOCAL_INDEXES 7 /* fix if you change list above */
criticalRelcachesBuilt = true;
}
@ -4044,7 +3966,6 @@ load_relcache_init_file(bool shared)
MemoryContext indexcxt;
Oid *opfamily;
Oid *opcintype;
Oid *operator;
RegProcedure *support;
int nsupport;
int16 *indoption;
@ -4105,17 +4026,7 @@ load_relcache_init_file(bool shared)
rel->rd_opcintype = opcintype;
/* next, read the vector of operator OIDs */
if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
goto read_failed;
operator = (Oid *) MemoryContextAlloc(indexcxt, len);
if (fread(operator, 1, len, fp) != len)
goto read_failed;
rel->rd_operator = operator;
/* next, read the vector of support procedures */
/* next, read the vector of support procedure OIDs */
if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
goto read_failed;
support = (RegProcedure *) MemoryContextAlloc(indexcxt, len);
@ -4154,7 +4065,6 @@ load_relcache_init_file(bool shared)
Assert(rel->rd_aminfo == NULL);
Assert(rel->rd_opfamily == NULL);
Assert(rel->rd_opcintype == NULL);
Assert(rel->rd_operator == NULL);
Assert(rel->rd_support == NULL);
Assert(rel->rd_supportinfo == NULL);
Assert(rel->rd_indoption == NULL);
@ -4371,12 +4281,7 @@ write_relcache_init_file(bool shared)
relform->relnatts * sizeof(Oid),
fp);
/* next, write the vector of operator OIDs */
write_item(rel->rd_operator,
relform->relnatts * (am->amstrategies * sizeof(Oid)),
fp);
/* next, write the vector of support procedures */
/* next, write the vector of support procedure OIDs */
write_item(rel->rd_support,
relform->relnatts * (am->amsupport * sizeof(RegProcedure)),
fp);

View File

@ -421,20 +421,17 @@ typedef struct RelOptInfo
* IndexOptInfo
* Per-index information for planning/optimization
*
* Prior to Postgres 7.0, RelOptInfo was used to describe both relations
* and indexes, but that created confusion without actually doing anything
* useful. So now we have a separate IndexOptInfo struct for indexes.
*
* opfamily[], indexkeys[], opcintype[], fwdsortop[], revsortop[],
* and nulls_first[] each have ncolumns entries.
* opfamily[], indexkeys[], and opcintype[] each have ncolumns entries.
* sortopfamily[], reverse_sort[], and nulls_first[] likewise have
* ncolumns entries, if the index is ordered; but if it is unordered,
* those pointers are NULL.
*
* Zeroes in the indexkeys[] array indicate index columns that are
* expressions; there is one element in indexprs for each such column.
*
* For an unordered index, the sortop arrays contains zeroes. Note that
* fwdsortop[] and nulls_first[] describe the sort ordering of a forward
* indexscan; we can also consider a backward indexscan, which will
* generate sort order described by revsortop/!nulls_first.
* For an ordered index, reverse_sort[] and nulls_first[] describe the
* sort ordering of a forward indexscan; we can also consider a backward
* indexscan, which will generate the reverse ordering.
*
* The indexprs and indpred expressions have been run through
* prepqual.c and eval_const_expressions() for ease of matching to
@ -457,8 +454,8 @@ typedef struct IndexOptInfo
Oid *opfamily; /* OIDs of operator families for columns */
int *indexkeys; /* column numbers of index's keys, or 0 */
Oid *opcintype; /* OIDs of opclass declared input data types */
Oid *fwdsortop; /* OIDs of sort operators for each column */
Oid *revsortop; /* OIDs of sort operators for backward scan */
Oid *sortopfamily; /* OIDs of btree opfamilies, if orderable */
bool *reverse_sort; /* is sort order descending? */
bool *nulls_first; /* do NULLs come first in the sort order? */
Oid relam; /* OID of the access method (in pg_am) */

View File

@ -178,10 +178,10 @@ typedef struct RelationData
/*
* index access support info (used only for an index relation)
*
* Note: only default operators and support procs for each opclass are
* cached, namely those with lefttype and righttype equal to the opclass's
* opcintype. The arrays are indexed by strategy or support number, which
* is a sufficient identifier given that restriction.
* Note: only default support procs for each opclass are cached, namely
* those with lefttype and righttype equal to the opclass's opcintype.
* The arrays are indexed by support function number, which is a
* sufficient identifier given that restriction.
*
* Note: rd_amcache is available for index AMs to cache private data about
* an index. This must be just a cache since it may get reset at any time
@ -194,7 +194,6 @@ typedef struct RelationData
RelationAmInfo *rd_aminfo; /* lookup info for funcs found in pg_am */
Oid *rd_opfamily; /* OIDs of op families for each index col */
Oid *rd_opcintype; /* OIDs of opclass declared input data types */
Oid *rd_operator; /* OIDs of index operators */
RegProcedure *rd_support; /* OIDs of support procedures */
FmgrInfo *rd_supportinfo; /* lookup info for support procedures */
int16 *rd_indoption; /* per-column AM-specific flags */