/** RelationBuildTupleDesc** Form the relation's tuple descriptor from information in* the pg_attribute, pg_attrdef & pg_constraint system catalogs.*///从pg_attribute,pg_attrdef和pg_constraint 获取字段信息以填充relation->rd_att
static void
//用到的结构体
/* This structure contains constraints of a tuple */
typedef struct TupleConstr
{AttrDefault *defval; /* array */ConstrCheck *check; /* array */struct AttrMissing *missing; /* missing attributes values, NULL if none */uint16 num_defval;uint16 num_check;bool has_not_null;bool has_generated_stored;
} TupleConstr;RelationBuildTupleDesc(Relation relation)
{HeapTuple pg_attribute_tuple;Relation pg_attribute_desc;SysScanDesc pg_attribute_scan;ScanKeyData skey[2];int need;TupleConstr *constr;AttrMissing *attrmiss = NULL;int ndef = 0;/* fill rd_att's type ID fields (compare heap.c's AddNewRelationTuple) *///初始化赋予了RECORDOID,也就是2249relation->rd_att->tdtypeid =relation->rd_rel->reltype ? relation->rd_rel->reltype : RECORDOID;relation->rd_att->tdtypmod = -1; /* just to be sure */constr = (TupleConstr *) MemoryContextAllocZero(CacheMemoryContext,sizeof(TupleConstr));constr->has_not_null = false;constr->has_generated_stored = 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.)*///类似执行select * from pg_attribute where attrelid='poly100w'::regclass and attnum>0; //初始化查询条件 attrelid='poly100w'::regclassScanKeyInit(&skey[0],Anum_pg_attribute_attrelid,BTEqualStrategyNumber, F_OIDEQ,ObjectIdGetDatum(RelationGetRelid(relation)));// attnum>0ScanKeyInit(&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 = table_open(AttributeRelationId, AccessShareLock);pg_attribute_scan = systable_beginscan(pg_attribute_desc,AttributeRelidNumIndexId,criticalRelcachesBuilt,NULL,2, skey);/** add attribute data to relation->rd_att*///从pg_class的relnatts字段获取到的值need = RelationGetNumberOfAttributes(relation);while (HeapTupleIsValid(pg_attribute_tuple = systable_getnext(pg_attribute_scan))){Form_pg_attribute attp;int attnum;//获取pg_attrubte获取一条记录attp = (Form_pg_attribute) GETSTRUCT(pg_attribute_tuple);attnum = attp->attnum;if (attnum <= 0 || attnum > RelationGetNumberOfAttributes(relation))elog(ERROR, "invalid attribute number %d for relation \"%s\"",attp->attnum, RelationGetRelationName(relation));#define ATTRIBUTE_FIXED_PART_SIZE \(offsetof(FormData_pg_attribute,attcollation) + sizeof(Oid))//拷贝固定大小部分到relation->rd_att[attnum-1]中memcpy(TupleDescAttr(relation->rd_att, attnum - 1),attp,ATTRIBUTE_FIXED_PART_SIZE);/* Update constraint/default info */if (attp->attnotnull) //not null限制constr->has_not_null = true;if (attp->attgenerated == ATTRIBUTE_GENERATED_STORED) //generated限制constr->has_generated_stored = true;if (attp->atthasdef) //默认值ndef++;/* If the column has a "missing" value, put it in the attrmiss array *///是否有missing value,可以参考https://zhuanlan.zhihu.com/p/671012588if (attp->atthasmissing){Datum missingval;bool missingNull;/* Do we have a missing value? */missingval = heap_getattr(pg_attribute_tuple,Anum_pg_attribute_attmissingval,pg_attribute_desc->rd_att,&missingNull);if (!missingNull){/* Yes, fetch from the array */MemoryContext oldcxt;bool is_null;int one = 1;Datum missval;if (attrmiss == NULL)attrmiss = (AttrMissing *)MemoryContextAllocZero(CacheMemoryContext,relation->rd_rel->relnatts *sizeof(AttrMissing));missval = array_get_element(missingval,1,&one,-1,attp->attlen,attp->attbyval,attp->attalign,&is_null);Assert(!is_null);if (attp->attbyval){/* for copy by val just copy the datum direct */attrmiss[attnum - 1].am_value = missval;}else{/* otherwise copy in the correct context */oldcxt = MemoryContextSwitchTo(CacheMemoryContext);attrmiss[attnum - 1].am_value = datumCopy(missval,attp->attbyval,attp->attlen);MemoryContextSwitchTo(oldcxt);}attrmiss[attnum - 1].am_present = true;}}need--;if (need == 0)break;}/** end the scan and close the attribute relation*/systable_endscan(pg_attribute_scan);table_close(pg_attribute_desc, AccessShareLock);if (need != 0)elog(ERROR, "pg_attribute catalog is missing %d attribute(s) for relation OID %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 < RelationGetNumberOfAttributes(relation); i++)Assert(TupleDescAttr(relation->rd_att, 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 (RelationGetNumberOfAttributes(relation) > 0)TupleDescAttr(relation->rd_att, 0)->attcacheoff = 0;/** Set up constraint/default info*/if (constr->has_not_null ||constr->has_generated_stored ||ndef > 0 ||attrmiss ||relation->rd_rel->relchecks > 0){relation->rd_att->constr = constr;//如果有default值,从pg_attrdef中获取//看代码2部分if (ndef > 0) /* DEFAULTs */AttrDefaultFetch(relation, ndef);elseconstr->num_defval = 0;constr->missing = attrmiss;//如果有check(pg_class的relchecks的值),从pg_constraint中获取,具体见代码3if (relation->rd_rel->relchecks > 0) /* CHECKs */CheckConstraintFetch(relation);elseconstr->num_check = 0;}else{pfree(constr);relation->rd_att->constr = NULL;}
}
代码2:AttrDefaultFetch
/** Load any default attribute value definitions for the relation.** ndef is the number of attributes that were marked atthasdef.** Note: we don't make it a hard error to be missing some pg_attrdef records.* We can limp along as long as nothing needs to use the default value. Code* that fails to find an expected AttrDefault record should throw an error.*/typedef struct AttrDefault
{AttrNumber adnum;char *adbin; /* nodeToString representation of expr */
} AttrDefault;static void
AttrDefaultFetch(Relation relation, int ndef)
{AttrDefault *attrdef;Relation adrel;SysScanDesc adscan;ScanKeyData skey;HeapTuple htup;int found = 0;/* Allocate array with room for as many entries as expected */attrdef = (AttrDefault *)MemoryContextAllocZero(CacheMemoryContext,ndef * sizeof(AttrDefault));/* Search pg_attrdef for relevant entries *///设置查询条件 adrelid='poly100w'::regclass;ScanKeyInit(&skey,Anum_pg_attrdef_adrelid,BTEqualStrategyNumber, F_OIDEQ,ObjectIdGetDatum(RelationGetRelid(relation)));adrel = table_open(AttrDefaultRelationId, AccessShareLock);adscan = systable_beginscan(adrel, AttrDefaultIndexId, true,NULL, 1, &skey);while (HeapTupleIsValid(htup = systable_getnext(adscan))){Form_pg_attrdef adform = (Form_pg_attrdef) GETSTRUCT(htup);Datum val;bool isnull;/* protect limited size of array */if (found >= ndef){elog(WARNING, "unexpected pg_attrdef record found for attribute %d of relation \"%s\"",adform->adnum, RelationGetRelationName(relation));break;}val = fastgetattr(htup,Anum_pg_attrdef_adbin,adrel->rd_att, &isnull);if (isnull)elog(WARNING, "null adbin for attribute %d of relation \"%s\"",adform->adnum, RelationGetRelationName(relation));else{/* detoast and convert to cstring in caller's context */char *s = TextDatumGetCString(val);//获取default的表达式attrdef[found].adnum = adform->adnum;attrdef[found].adbin = MemoryContextStrdup(CacheMemoryContext, s);pfree(s);found++;}}systable_endscan(adscan);table_close(adrel, AccessShareLock);if (found != ndef)elog(WARNING, "%d pg_attrdef record(s) missing for relation \"%s\"",ndef - found, RelationGetRelationName(relation));/** Sort the AttrDefault entries by adnum, for the convenience of* equalTupleDescs(). (Usually, they already will be in order, but this* might not be so if systable_getnext isn't using an index.)*/if (found > 1)qsort(attrdef, found, sizeof(AttrDefault), AttrDefaultCmp);/* Install array only after it's fully valid *///赋值rd_att->const->defvalrelation->rd_att->constr->defval = attrdef;relation->rd_att->constr->num_defval = found;
}
代码3:
/** Load any check constraints for the relation.** As with defaults, if we don't find the expected number of them, just warn* here. The executor should throw an error if an INSERT/UPDATE is attempted.*/typedef struct ConstrCheck
{char *ccname;char *ccbin; /* nodeToString representation of expr */bool ccvalid;bool ccnoinherit; /* this is a non-inheritable constraint */
} ConstrCheck;static void
CheckConstraintFetch(Relation relation)
{ConstrCheck *check;int ncheck = relation->rd_rel->relchecks;Relation conrel;SysScanDesc conscan;ScanKeyData skey[1];HeapTuple htup;int found = 0;/* Allocate array with room for as many entries as expected */check = (ConstrCheck *)MemoryContextAllocZero(CacheMemoryContext,ncheck * sizeof(ConstrCheck));/* Search pg_constraint for relevant entries */// select * from pg_constraint where conrelid='poly100w'::regclass;ScanKeyInit(&skey[0],Anum_pg_constraint_conrelid,BTEqualStrategyNumber, F_OIDEQ,ObjectIdGetDatum(RelationGetRelid(relation)));conrel = table_open(ConstraintRelationId, AccessShareLock);conscan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId, true,NULL, 1, skey);while (HeapTupleIsValid(htup = systable_getnext(conscan))){Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(htup);Datum val;bool isnull;/* We want check constraints only */if (conform->contype != CONSTRAINT_CHECK)continue;/* protect limited size of array */if (found >= ncheck){elog(WARNING, "unexpected pg_constraint record found for relation \"%s\"",RelationGetRelationName(relation));break;}check[found].ccvalid = conform->convalidated;check[found].ccnoinherit = conform->connoinherit;check[found].ccname = MemoryContextStrdup(CacheMemoryContext,NameStr(conform->conname));/* Grab and test conbin is actually set */val = fastgetattr(htup,Anum_pg_constraint_conbin,conrel->rd_att, &isnull);if (isnull)elog(WARNING, "null conbin for relation \"%s\"",RelationGetRelationName(relation));else{/* detoast and convert to cstring in caller's context */char *s = TextDatumGetCString(val);check[found].ccbin = MemoryContextStrdup(CacheMemoryContext, s);pfree(s);found++;}}systable_endscan(conscan);table_close(conrel, AccessShareLock);if (found != ncheck)elog(WARNING, "%d pg_constraint record(s) missing for relation \"%s\"",ncheck - found, RelationGetRelationName(relation));/** Sort the records by name. This ensures that CHECKs are applied in a* deterministic order, and it also makes equalTupleDescs() faster.*/if (found > 1)qsort(check, found, sizeof(ConstrCheck), CheckConstraintCmp);/* Install array only after it's fully valid */relation->rd_att->constr->check = check;relation->rd_att->constr->num_check = found;
}