> 技术文档 > PostgreSQL RelationInitIndexAccessInfo

PostgreSQL RelationInitIndexAccessInfo

/* * Fill in the IndexAmRoutine for an index relation. * * relation\'s rd_amhandler and rd_indexcxt must be valid already. */static voidInitIndexAmRoutine(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 */voidRelationInitIndexAccessInfo(Relation relation){HeapTupletuple;Form_pg_amaform;DatumindcollDatum;DatumindclassDatum;DatumindoptionDatum;boolisnull;oidvector *indcoll;oidvector *indclass;int2vector *indoption;MemoryContext indexcxt;MemoryContext oldcontext;intindnatts;intindnkeyatts;uint16amsupport;/* * 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); //切换回MessageContextMemoryContextSwitchTo(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的tupletuple = SearchSysCache1(AMOID, ObjectIdGetDatum(relation->rd_rel->relam));if (!HeapTupleIsValid(tuple))elog(ERROR, \"cache lookup failed for access method %u\", relation->rd_rel->relam); //获取记录,并得到handleraform = (Form_pg_am) GETSTRUCT(tuple);relation->rd_amhandler = aform->amhandler;ReleaseSysCache(tuple); //pg_class 的relnattsindnatts = RelationGetNumberOfAttributes(relation); //IndexRelationGetNumberOfAttributes(relation) 获取pg_index的indnatts //indnatts包括key和including attrif (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_indexMemoryContextCopyAndSetIdentifier(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){intnsupport = 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{Oidopclassoid;/* lookup key: OID of opclass */boolvalid;/* set true after successful fill-in */StrategyNumber numSupport;/* max # of support procs (from pg_am) */Oidopcfamily;/* OID of opclass\'s family */Oidopcintype;/* 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;boolfound;Relationrel;SysScanDesc scan;ScanKeyData skey[3];HeapTuplehtup;boolindexOK; if (OpClassCache == NULL){/* First time through: initialize the opclass cache */HASHCTLctl;/* Also make sure CacheMemoryContext exists */if (!CacheMemoryContext)CreateCacheMemoryContext(); //key为opclassoidctl.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_ENABLEDif (debug_discard_caches > 2)opcentry->valid = false;#endifif (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=operatorClassOidScanKeyInit(&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); //获取opcfamilyopcentry->opcfamily = opclassform->opcfamily; //获取opcintypeopcentry->opcintype = opclassform->opcintype;}elseelog(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->opcintypeif (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 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);} //设置为trueopcentry->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 voidIndexSupportInitialize(oidvector *indclass, RegProcedure *indexSupport, Oid *opFamily, Oid *opcInType, StrategyNumber maxSupportNumber, AttrNumber maxAttributeNumber){intattIndex;for (attIndex = 0; attIndex 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));}}