/*
* Fill in the IndexAmRoutine for an index relation.
*
* relation's rd_amhandler and rd_indexcxt must be valid already.
*/
static void
InitIndexAmRoutine(Relation relation)
{
IndexAmRoutine *cached,
*tmp;
/*
* Call the amhandler in current, short-lived memory context, just in case
* it leaks anything (it probably won't, but let's be paranoid).
*/
tmp = GetIndexAmRoutine(relation->rd_amhandler);
/* OK, now transfer the data into relation's rd_indexcxt. */
cached = (IndexAmRoutine *) MemoryContextAlloc(relation->rd_indexcxt,
sizeof(IndexAmRoutine));
memcpy(cached, tmp, sizeof(IndexAmRoutine));
relation->rd_indam = cached;
pfree(tmp);
}
/*
* Initialize index-access-method support data for an index relation
*/
void
RelationInitIndexAccessInfo(Relation relation)
{
HeapTuple tuple;
Form_pg_am aform;
Datum indcollDatum;
Datum indclassDatum;
Datum indoptionDatum;
bool isnull;
oidvector *indcoll;
oidvector *indclass;
int2vector *indoption;
MemoryContext indexcxt;
MemoryContext oldcontext;
int indnatts;
int indnkeyatts;
uint16 amsupport;
/*
* Make a copy of the pg_index entry for the index. Since pg_index
* contains variable-length and possibly-null fields, we have to do this
* honestly rather than just treating it as a Form_pg_index struct.
*/
//从syscache中获取与pg_index相关的记录
tuple = SearchSysCache1(INDEXRELID,
ObjectIdGetDatum(RelationGetRelid(relation)));
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for index %u",
RelationGetRelid(relation));
//切换到到CacheMemroyContext中
oldcontext = MemoryContextSwitchTo(CacheMemoryContext);
//拷贝一份
relation->rd_indextuple = heap_copytuple(tuple);
//获取pg_index结构
relation->rd_index = (Form_pg_index) GETSTRUCT(relation->rd_indextuple);
//切换回MessageContext
MemoryContextSwitchTo(oldcontext);
ReleaseSysCache(tuple);
/*
* Look up the index's access method, save the OID of its handler function
*/
Assert(relation->rd_rel->relam != InvalidOid);
//从syscache中获取pg_am的tuple
tuple = SearchSysCache1(AMOID, ObjectIdGetDatum(relation->rd_rel->relam));
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for access method %u",
relation->rd_rel->relam);
//获取记录,并得到handler
aform = (Form_pg_am) GETSTRUCT(tuple);
relation->rd_amhandler = aform->amhandler;
ReleaseSysCache(tuple);
//pg_class 的relnatts
indnatts = RelationGetNumberOfAttributes(relation);
//IndexRelationGetNumberOfAttributes(relation) 获取pg_index的indnatts
//indnatts包括key和including attr
if (indnatts != IndexRelationGetNumberOfAttributes(relation))
elog(ERROR, "relnatts disagrees with indnatts for index %u",
RelationGetRelid(relation));
//获取pg_index的indnkeyatts值(只包括key)
indnkeyatts = IndexRelationGetNumberOfKeyAttributes(relation);
/*
* Make the private context to hold index access info. The reason we need
* a context, and not just a couple of pallocs, is so that we won't leak
* any subsidiary info attached to fmgr lookup records.
*/
indexcxt = AllocSetContextCreate(CacheMemoryContext,
"index info",
ALLOCSET_SMALL_SIZES);
relation->rd_indexcxt = indexcxt;
// name为 index info ident=pg_index_indrelid_index
MemoryContextCopyAndSetIdentifier(indexcxt,
RelationGetRelationName(relation));
/*
* Now we can fetch the index AM's API struct
*/
InitIndexAmRoutine(relation);
/*
* Allocate arrays to hold data. Opclasses are not used for included
* columns, so allocate them for indnkeyatts only.
*/
//给 rd_opfamily开辟空间
relation->rd_opfamily = (Oid *)
MemoryContextAllocZero(indexcxt, indnkeyatts * sizeof(Oid));
//rd_opcintype开辟存储空间
relation->rd_opcintype = (Oid *)
MemoryContextAllocZero(indexcxt, indnkeyatts * sizeof(Oid));
//支持函数的个数
amsupport = relation->rd_indam->amsupport;
if (amsupport > 0)
{
int nsupport = indnatts * amsupport;
//给函数oid开辟空间
relation->rd_support = (RegProcedure *)
MemoryContextAllocZero(indexcxt, nsupport * sizeof(RegProcedure));
//给函数struct开辟空间
relation->rd_supportinfo = (FmgrInfo *)
MemoryContextAllocZero(indexcxt, nsupport * sizeof(FmgrInfo));
}
else
{
relation->rd_support = NULL;
relation->rd_supportinfo = NULL;
}
//给indcollation开辟空间
relation->rd_indcollation = (Oid *)
MemoryContextAllocZero(indexcxt, indnkeyatts * sizeof(Oid));
relation->rd_indoption = (int16 *)
MemoryContextAllocZero(indexcxt, indnkeyatts * sizeof(int16));
/*
* indcollation cannot be referenced directly through the C struct,
* because it comes after the variable-width indkey field. Must extract
* the datum the hard way...
*/
indcollDatum = fastgetattr(relation->rd_indextuple,
Anum_pg_index_indcollation,
GetPgIndexDescriptor(),
&isnull);
Assert(!isnull);
indcoll = (oidvector *) DatumGetPointer(indcollDatum);
memcpy(relation->rd_indcollation, indcoll->values, indnkeyatts * sizeof(Oid));
/*
* indclass cannot be referenced directly through the C struct, because it
* comes after the variable-width indkey field. Must extract the datum
* the hard way...
*/
//获取pg_index的indclass的指针
indclassDatum = fastgetattr(relation->rd_indextuple,
Anum_pg_index_indclass,
GetPgIndexDescriptor(),
&isnull);
Assert(!isnull);
//获取indclass的值
indclass = (oidvector *) DatumGetPointer(indclassDatum);
/*
* 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_support,
relation->rd_opfamily, relation->rd_opcintype,
amsupport, indnkeyatts);
/*
* Similarly extract indoption and copy it to the cache entry
*/
indoptionDatum = fastgetattr(relation->rd_indextuple,
Anum_pg_index_indoption,
GetPgIndexDescriptor(),
&isnull);
Assert(!isnull);
indoption = (int2vector *) DatumGetPointer(indoptionDatum);
memcpy(relation->rd_indoption, indoption->values, indnkeyatts * sizeof(int16));
(void) RelationGetIndexAttOptions(relation, false);
/*
* expressions, predicate, exclusion caches will be filled later
*/
relation->rd_indexprs = NIL;
relation->rd_indpred = NIL;
relation->rd_exclops = NULL;
relation->rd_exclprocs = NULL;
relation->rd_exclstrats = NULL;
relation->rd_amcache = NULL;
}
代码2:IndexSupportInitialize
* Special cache for opclass-related information
*
* 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 numSupport; /* max # of support procs (from pg_am) */
Oid opcfamily; /* OID of opclass's family */
Oid opcintype; /* OID of opclass's declared input type */
RegProcedure *supportProcs; /* OIDs of support procedures */
} OpClassCacheEnt;
/*
* LookupOpclassInfo
*
* 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.
*
* 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.
*
* Note there is no provision for flushing the cache. This is OK at the
* moment because there is no way to ALTER any interesting properties of an
* existing opclass --- all you can do is drop it, which will result in
* a useless but harmless dead entry in the cache. To support altering
* opclass membership (not the same as opfamily membership!), we'd need to
* be able to flush this cache as well as the contents of relcache entries
* for indexes.
*/
static OpClassCacheEnt *
LookupOpclassInfo(Oid operatorClassOid,
StrategyNumber numSupport)
{
OpClassCacheEnt *opcentry;
bool found;
Relation rel;
SysScanDesc scan;
ScanKeyData skey[3];
HeapTuple htup;
bool indexOK;
if (OpClassCache == NULL)
{
/* First time through: initialize the opclass cache */
HASHCTL ctl;
/* Also make sure CacheMemoryContext exists */
if (!CacheMemoryContext)
CreateCacheMemoryContext();
//key为opclassoid
ctl.keysize = sizeof(Oid);
ctl.entrysize = sizeof(OpClassCacheEnt);
OpClassCache = hash_create("Operator class cache", 64,
&ctl, HASH_ELEM | HASH_BLOBS);
}
opcentry = (OpClassCacheEnt *) hash_search(OpClassCache,
&operatorClassOid,
HASH_ENTER, &found);
if (!found)
{
/* Initialize new entry */
opcentry->valid = false; /* until known OK */
opcentry->numSupport = numSupport;
opcentry->supportProcs = NULL; /* filled below */
}
else
{
Assert(numSupport == opcentry->numSupport);
}
/*
* When aggressively testing cache-flush hazards, we disable the operator
* class cache and force reloading of the info on each call. This models
* no real-world behavior, since the cache entries are never invalidated
* otherwise. However it can be helpful for detecting bugs in the cache
* loading logic itself, such as reliance on a non-nailed index. Given
* the limited use-case and the fact that this adds a great deal of
* expense, we enable it only for high values of debug_discard_caches.
*/
#ifdef DISCARD_CACHES_ENABLED
if (debug_discard_caches > 2)
opcentry->valid = false;
#endif
if (opcentry->valid)
return opcentry;
/*
* Need to fill in new entry. First allocate space, unless we already did
* so in some previous attempt.
*/
if (opcentry->supportProcs == NULL && numSupport > 0)
opcentry->supportProcs = (RegProcedure *)
MemoryContextAllocZero(CacheMemoryContext,
numSupport * sizeof(RegProcedure));
/*
* To avoid infinite recursion during startup, force heap scans if we're
* looking up info for the opclasses used by the indexes we would like to
* reference here.
*/
indexOK = criticalRelcachesBuilt ||
(operatorClassOid != OID_BTREE_OPS_OID &&
operatorClassOid != INT2_BTREE_OPS_OID);
/*
* We have to fetch the pg_opclass row to determine its opfamily and
* 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.
*/
//select opcfamily,opcintype from pg_opclass where oid=operatorClassOid
ScanKeyInit(&skey[0],
Anum_pg_opclass_oid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(operatorClassOid));
rel = table_open(OperatorClassRelationId, AccessShareLock);
scan = systable_beginscan(rel, OpclassOidIndexId, indexOK,
NULL, 1, skey);
if (HeapTupleIsValid(htup = systable_getnext(scan)))
{
Form_pg_opclass opclassform = (Form_pg_opclass) GETSTRUCT(htup);
//获取opcfamily
opcentry->opcfamily = opclassform->opcfamily;
//获取opcintype
opcentry->opcintype = opclassform->opcintype;
}
else
elog(ERROR, "could not find tuple for opclass %u", operatorClassOid);
systable_endscan(scan);
table_close(rel, AccessShareLock);
/*
* Scan pg_amproc to obtain support procs for the opclass. We only fetch
* the default ones (those with lefttype = righttype = opcintype).
*/
//select amproc from pg_amproc where amprocfamily=opcentry->opcfamily and
amlefttype=opcentry->opcintype and amrighttype=opcentry->opcintype
if (numSupport > 0)
{
ScanKeyInit(&skey[0],
Anum_pg_amproc_amprocfamily,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(opcentry->opcfamily));
ScanKeyInit(&skey[1],
Anum_pg_amproc_amproclefttype,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(opcentry->opcintype));
ScanKeyInit(&skey[2],
Anum_pg_amproc_amprocrighttype,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(opcentry->opcintype));
rel = table_open(AccessMethodProcedureRelationId, AccessShareLock);
scan = systable_beginscan(rel, AccessMethodProcedureIndexId, indexOK,
NULL, 3, skey);
while (HeapTupleIsValid(htup = systable_getnext(scan)))
{
Form_pg_amproc amprocform = (Form_pg_amproc) GETSTRUCT(htup);
if (amprocform->amprocnum <= 0 ||
(StrategyNumber) amprocform->amprocnum > numSupport)
elog(ERROR, "invalid amproc number %d for opclass %u",
amprocform->amprocnum, operatorClassOid);
opcentry->supportProcs[amprocform->amprocnum - 1] =
amprocform->amproc;
}
systable_endscan(scan);
table_close(rel, AccessShareLock);
}
//设置为true
opcentry->valid = true;
return opcentry;
}
/*
* IndexSupportInitialize
* Initializes an index's cached opclass information,
* given the index's pg_index.indclass entry.
*
* Data is returned into *indexSupport, *opFamily, and *opcInType,
* which are arrays allocated by the caller.
*
* 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,
RegProcedure *indexSupport,
Oid *opFamily,
Oid *opcInType,
StrategyNumber maxSupportNumber,
AttrNumber maxAttributeNumber)
{
int attIndex;
for (attIndex = 0; attIndex < maxAttributeNumber; attIndex++)
{
OpClassCacheEnt *opcentry;
if (!OidIsValid(indclass->values[attIndex]))
elog(ERROR, "bogus pg_index tuple");
/* look up the info for this opclass, using a cache */
//从hash表中查找,找不到查询
opcentry = LookupOpclassInfo(indclass->values[attIndex],
maxSupportNumber);
/* copy cached data into relcache entry */
opFamily[attIndex] = opcentry->opcfamily;
opcInType[attIndex] = opcentry->opcintype;
if (maxSupportNumber > 0)
memcpy(&indexSupport[attIndex * maxSupportNumber],
opcentry->supportProcs,
maxSupportNumber * sizeof(RegProcedure));
}
}