📄 relcache.c
字号:
/*------------------------------------------------------------------------- * * relcache.c * POSTGRES relation descriptor cache code * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.230.2.3 2006/01/19 20:28:48 tgl Exp $ * *------------------------------------------------------------------------- *//* * INTERFACE ROUTINES * RelationCacheInitialize - initialize relcache * RelationCacheInitializePhase2 - finish initializing relcache * RelationIdGetRelation - get a reldesc by relation id * RelationIdCacheGetRelation - get a cached reldesc by relid * RelationClose - close an open relation * * NOTES * The following code contains many undocumented hacks. Please be * careful.... */#include "postgres.h"#include <sys/file.h>#include <fcntl.h>#include <unistd.h>#include "access/genam.h"#include "access/heapam.h"#include "catalog/catalog.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_attribute.h"#include "catalog/pg_authid.h"#include "catalog/pg_constraint.h"#include "catalog/pg_index.h"#include "catalog/pg_namespace.h"#include "catalog/pg_opclass.h"#include "catalog/pg_proc.h"#include "catalog/pg_rewrite.h"#include "catalog/pg_type.h"#include "commands/trigger.h"#include "miscadmin.h"#include "optimizer/clauses.h"#include "optimizer/planmain.h"#include "optimizer/prep.h"#include "storage/fd.h"#include "storage/smgr.h"#include "utils/builtins.h"#include "utils/catcache.h"#include "utils/fmgroids.h"#include "utils/inval.h"#include "utils/lsyscache.h"#include "utils/memutils.h"#include "utils/relcache.h"#include "utils/resowner.h"#include "utils/syscache.h"#include "utils/typcache.h"/* * name of relcache init file, used to speed up backend startup */#define RELCACHE_INIT_FILENAME "pg_internal.init"#define RELCACHE_INIT_FILEMAGIC 0x573262 /* version ID value *//* * hardcoded tuple descriptors. see include/catalog/pg_attribute.h */static FormData_pg_attribute Desc_pg_class[Natts_pg_class] = {Schema_pg_class};static FormData_pg_attribute Desc_pg_attribute[Natts_pg_attribute] = {Schema_pg_attribute};static FormData_pg_attribute Desc_pg_proc[Natts_pg_proc] = {Schema_pg_proc};static FormData_pg_attribute Desc_pg_type[Natts_pg_type] = {Schema_pg_type};static FormData_pg_attribute Desc_pg_index[Natts_pg_index] = {Schema_pg_index};/* * Hash tables that index the relation cache * * We used to index the cache by both name and OID, but now there * is only an index by OID. */typedef struct relidcacheent{ Oid reloid; Relation reldesc;} RelIdCacheEnt;static HTAB *RelationIdCache;/* * This flag is false until we have prepared the critical relcache entries * that are needed to do indexscans on the tables read by relcache building. */bool criticalRelcachesBuilt = false;/* * This flag is set if we discover that we need to write a new relcache * cache file at the end of startup. */static bool needNewCacheFile = false;/* * This counter counts relcache inval events received since backend startup * (but only for rels that are actually in cache). Presently, we use it only * to detect whether data about to be written by write_relcache_init_file() * might already be obsolete. */static long relcacheInvalsReceived = 0L;/* * This list remembers the OIDs of the relations cached in the relcache * init file. */static List *initFileRelationIds = NIL;/* * This flag lets us optimize away work in AtEO(Sub)Xact_RelationCache(). */static bool need_eoxact_work = false;/* * macros to manipulate the lookup hashtables */#define RelationCacheInsert(RELATION) \do { \ RelIdCacheEnt *idhentry; bool found; \ idhentry = (RelIdCacheEnt*)hash_search(RelationIdCache, \ (void *) &(RELATION->rd_id), \ HASH_ENTER, \ &found); \ /* used to give notice if found -- now just keep quiet */ \ idhentry->reldesc = RELATION; \} while(0)#define RelationIdCacheLookup(ID, RELATION) \do { \ RelIdCacheEnt *hentry; \ hentry = (RelIdCacheEnt*)hash_search(RelationIdCache, \ (void *) &(ID), HASH_FIND,NULL); \ if (hentry) \ RELATION = hentry->reldesc; \ else \ RELATION = NULL; \} while(0)#define RelationCacheDelete(RELATION) \do { \ RelIdCacheEnt *idhentry; \ idhentry = (RelIdCacheEnt*)hash_search(RelationIdCache, \ (void *) &(RELATION->rd_id), \ HASH_REMOVE, NULL); \ if (idhentry == NULL) \ elog(WARNING, "trying to delete a rd_id reldesc that does not exist"); \} while(0)/* * Special cache for opclass-related information * * Note: only default-subtype operators and support procs get cached */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 *operatorOids; /* strategy operators' OIDs */ RegProcedure *supportProcs; /* support procs */} OpClassCacheEnt;static HTAB *OpClassCache = NULL;/* non-export function prototypes */static void RelationClearRelation(Relation relation, bool rebuild);static void RelationReloadClassinfo(Relation relation);static void RelationFlushRelation(Relation relation);static bool load_relcache_init_file(void);static void write_relcache_init_file(void);static void formrdesc(const char *relationName, Oid relationReltype, bool hasoids, int natts, FormData_pg_attribute *att);static HeapTuple ScanPgRelation(Oid targetRelId, bool indexOK);static Relation AllocateRelationDesc(Relation relation, Form_pg_class relp);static void RelationBuildTupleDesc(Relation relation);static Relation RelationBuildDesc(Oid targetRelId, Relation oldrelation);static void RelationInitPhysicalAddr(Relation relation);static TupleDesc GetPgIndexDescriptor(void);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, StrategyNumber maxStrategyNumber, StrategyNumber maxSupportNumber, AttrNumber maxAttributeNumber);static OpClassCacheEnt *LookupOpclassInfo(Oid operatorClassOid, StrategyNumber numStrats, StrategyNumber numSupport);/* * ScanPgRelation * * this is used by RelationBuildDesc to find a pg_class * tuple matching targetRelId. * * NB: the returned tuple has been copied into palloc'd storage * and must eventually be freed with heap_freetuple. */static HeapTupleScanPgRelation(Oid targetRelId, bool indexOK){ HeapTuple pg_class_tuple; Relation pg_class_desc; SysScanDesc pg_class_scan; ScanKeyData key[1]; /* * form a scan key */ ScanKeyInit(&key[0], ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(targetRelId)); /* * Open pg_class and fetch a tuple. Force heap scan if we haven't yet * built the critical relcache entries (this includes initdb and startup * without a pg_internal.init file). The caller can also force a heap * scan by setting indexOK == false. */ pg_class_desc = heap_open(RelationRelationId, AccessShareLock); pg_class_scan = systable_beginscan(pg_class_desc, ClassOidIndexId, indexOK && criticalRelcachesBuilt, SnapshotNow, 1, key); pg_class_tuple = systable_getnext(pg_class_scan); /* * Must copy tuple before releasing buffer. */ if (HeapTupleIsValid(pg_class_tuple)) pg_class_tuple = heap_copytuple(pg_class_tuple); /* all done */ systable_endscan(pg_class_scan); heap_close(pg_class_desc, AccessShareLock); return pg_class_tuple;}/* * AllocateRelationDesc * * This is used to allocate memory for a new relation descriptor * and initialize the rd_rel field. * * If 'relation' is NULL, allocate a new RelationData object. * If not, reuse the given object (that path is taken only when * we have to rebuild a relcache entry during RelationClearRelation). */static RelationAllocateRelationDesc(Relation relation, Form_pg_class relp){ MemoryContext oldcxt; Form_pg_class relationForm; /* Relcache entries must live in CacheMemoryContext */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); /* * allocate space for new relation descriptor, if needed */ if (relation == NULL) relation = (Relation) palloc(sizeof(RelationData)); /* * clear all fields of reldesc */ MemSet(relation, 0, sizeof(RelationData)); relation->rd_targblock = InvalidBlockNumber; /* make sure relation is marked as having no open file yet */ relation->rd_smgr = NULL; /* * Copy the relation tuple form * * We only allocate space for the fixed fields, ie, CLASS_TUPLE_SIZE. * relacl is NOT stored in the relcache --- there'd be little point in it, * since we don't copy the tuple's nullvalues bitmap and hence wouldn't * know if the value is valid ... bottom line is that relacl *cannot* be * retrieved from the relcache. Get it from the syscache if you need it. */ relationForm = (Form_pg_class) palloc(CLASS_TUPLE_SIZE); memcpy(relationForm, relp, CLASS_TUPLE_SIZE); /* initialize relation tuple form */ relation->rd_rel = relationForm; /* and allocate attribute tuple form storage */ relation->rd_att = CreateTemplateTupleDesc(relationForm->relnatts, relationForm->relhasoids); MemoryContextSwitchTo(oldcxt); return relation;}/* * RelationBuildTupleDesc * * Form the relation's tuple descriptor from information in * the pg_attribute, pg_attrdef & pg_constraint system catalogs. */static voidRelationBuildTupleDesc(Relation relation){ HeapTuple pg_attribute_tuple; Relation pg_attribute_desc; SysScanDesc pg_attribute_scan; ScanKeyData skey[2]; int need; TupleConstr *constr; AttrDefault *attrdef = NULL; int ndef = 0; /* copy some fields from pg_class row to rd_att */ relation->rd_att->tdtypeid = relation->rd_rel->reltype; relation->rd_att->tdtypmod = -1; /* unnecessary, but... */ relation->rd_att->tdhasoid = relation->rd_rel->relhasoids; constr = (TupleConstr *) MemoryContextAlloc(CacheMemoryContext, sizeof(TupleConstr)); constr->has_not_null = false; /* * Form a scan key that selects only user attributes (attnum > 0). * (Eliminating system attribute rows at the index level is lots faster * than fetching them.) */ ScanKeyInit(&skey[0], Anum_pg_attribute_attrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(relation))); ScanKeyInit(&skey[1], Anum_pg_attribute_attnum, BTGreaterStrategyNumber, F_INT2GT, Int16GetDatum(0)); /* * Open pg_attribute and begin a scan. Force heap scan if we haven't yet * built the critical relcache entries (this includes initdb and startup * without a pg_internal.init file). */ pg_attribute_desc = heap_open(AttributeRelationId, AccessShareLock); pg_attribute_scan = systable_beginscan(pg_attribute_desc, AttributeRelidNumIndexId, criticalRelcachesBuilt, SnapshotNow, 2, skey); /* * add attribute data to relation->rd_att */ need = relation->rd_rel->relnatts; while (HeapTupleIsValid(pg_attribute_tuple = systable_getnext(pg_attribute_scan))) { Form_pg_attribute attp; attp = (Form_pg_attribute) GETSTRUCT(pg_attribute_tuple); if (attp->attnum <= 0 || attp->attnum > relation->rd_rel->relnatts) elog(ERROR, "invalid attribute number %d for %s", attp->attnum, RelationGetRelationName(relation)); memcpy(relation->rd_att->attrs[attp->attnum - 1], attp, ATTRIBUTE_TUPLE_SIZE); /* Update constraint/default info */ if (attp->attnotnull) constr->has_not_null = true; if (attp->atthasdef) { if (attrdef == NULL) attrdef = (AttrDefault *) MemoryContextAllocZero(CacheMemoryContext, relation->rd_rel->relnatts * sizeof(AttrDefault)); attrdef[ndef].adnum = attp->attnum; attrdef[ndef].adbin = NULL; ndef++; } need--; if (need == 0) break; } /* * end the scan and close the attribute relation */ systable_endscan(pg_attribute_scan); heap_close(pg_attribute_desc, AccessShareLock); if (need != 0) elog(ERROR, "catalog is missing %d attribute(s) for relid %u", need, RelationGetRelid(relation)); /* * The attcacheoff values we read from pg_attribute should all be -1 * ("unknown"). Verify this if assert checking is on. They will be * computed when and if needed during tuple access. */#ifdef USE_ASSERT_CHECKING { int i; for (i = 0; i < relation->rd_rel->relnatts; i++) Assert(relation->rd_att->attrs[i]->attcacheoff == -1); }#endif /* * However, we can easily set the attcacheoff value for the first * attribute: it must be zero. This eliminates the need for special cases * for attnum=1 that used to exist in fastgetattr() and index_getattr(). */ if (relation->rd_rel->relnatts > 0)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -