postgresql/src/backend/utils/cache/relcache.c

2721 lines
74 KiB
C

/*-------------------------------------------------------------------------
*
* relcache.c
* POSTGRES relation descriptor cache code
*
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.111 2000/09/12 04:49:13 momjian Exp $
*
*-------------------------------------------------------------------------
*/
/*
* INTERFACE ROUTINES
* RelationCacheInitialize - initialize relcache
* RelationCacheInitializePhase2 - finish initializing relcache
* RelationIdCacheGetRelation - get a reldesc from the cache (id)
* RelationNameCacheGetRelation - get a reldesc from the cache (name)
* RelationIdGetRelation - get a reldesc by relation id
* RelationNameGetRelation - get a reldesc by relation name
* RelationClose - close an open relation
*
* NOTES
* The following code contains many undocumented hacks. Please be
* careful....
*
*/
#include <sys/types.h>
#include <errno.h>
#include <sys/file.h>
#include <fcntl.h>
#include <unistd.h>
#include "postgres.h"
#include "utils/builtins.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "access/istrat.h"
#include "catalog/catalog.h"
#include "catalog/catname.h"
#include "catalog/index.h"
#include "catalog/indexing.h"
#include "catalog/pg_attrdef.h"
#include "catalog/pg_index.h"
#include "catalog/pg_log.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_relcheck.h"
#include "catalog/pg_rewrite.h"
#include "catalog/pg_type.h"
#include "catalog/pg_variable.h"
#include "commands/trigger.h"
#include "lib/hasht.h"
#include "miscadmin.h"
#include "storage/smgr.h"
#include "utils/catcache.h"
#include "utils/fmgroids.h"
#include "utils/memutils.h"
#include "utils/relcache.h"
#include "utils/temprel.h"
/* ----------------
* hardcoded tuple descriptors. see lib/backend/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_variable[Natts_pg_variable] = {Schema_pg_variable};
static FormData_pg_attribute Desc_pg_log[Natts_pg_log] = {Schema_pg_log};
/* ----------------
* Hash tables that index the relation cache
*
* Relations are looked up two ways, by name and by id,
* thus there are two hash tables for referencing them.
* ----------------
*/
static HTAB *RelationNameCache;
static HTAB *RelationIdCache;
/*
* newlyCreatedRelns -
* relations created during this transaction. We need to keep track of
* these.
*/
static List *newlyCreatedRelns = NULL;
/*
* 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.
*/
static bool criticalRelcachesBuilt = false;
/* ----------------
* RelationBuildDescInfo exists so code can be shared
* between RelationIdGetRelation() and RelationNameGetRelation()
* ----------------
*/
typedef struct RelationBuildDescInfo
{
int infotype; /* lookup by id or by name */
#define INFO_RELID 1
#define INFO_RELNAME 2
union
{
Oid info_id; /* relation object id */
char *info_name; /* relation name */
} i;
} RelationBuildDescInfo;
typedef struct relidcacheent
{
Oid reloid;
Relation reldesc;
} RelIdCacheEnt;
typedef struct relnamecacheent
{
NameData relname;
Relation reldesc;
} RelNameCacheEnt;
/* -----------------
* macros to manipulate name cache and id cache
* -----------------
*/
#define RelationCacheInsert(RELATION) \
do { \
RelIdCacheEnt *idhentry; RelNameCacheEnt *namehentry; \
char *relname; Oid reloid; bool found; \
relname = RelationGetPhysicalRelationName(RELATION); \
namehentry = (RelNameCacheEnt*)hash_search(RelationNameCache, \
relname, \
HASH_ENTER, \
&found); \
if (namehentry == NULL) \
elog(FATAL, "can't insert into relation descriptor cache"); \
if (found && !IsBootstrapProcessingMode()) \
/* used to give notice -- now just keep quiet */ ; \
namehentry->reldesc = RELATION; \
reloid = RELATION->rd_id; \
idhentry = (RelIdCacheEnt*)hash_search(RelationIdCache, \
(char *)&reloid, \
HASH_ENTER, \
&found); \
if (idhentry == NULL) \
elog(FATAL, "can't insert into relation descriptor cache"); \
if (found && !IsBootstrapProcessingMode()) \
/* used to give notice -- now just keep quiet */ ; \
idhentry->reldesc = RELATION; \
} while(0)
#define RelationNameCacheLookup(NAME, RELATION) \
do { \
RelNameCacheEnt *hentry; bool found; \
hentry = (RelNameCacheEnt*)hash_search(RelationNameCache, \
(char *)NAME,HASH_FIND,&found); \
if (hentry == NULL) \
elog(FATAL, "error in CACHE"); \
if (found) \
RELATION = hentry->reldesc; \
else \
RELATION = NULL; \
} while(0)
#define RelationIdCacheLookup(ID, RELATION) \
do { \
RelIdCacheEnt *hentry; \
bool found; \
hentry = (RelIdCacheEnt*)hash_search(RelationIdCache, \
(char *)&(ID),HASH_FIND, &found); \
if (hentry == NULL) \
elog(FATAL, "error in CACHE"); \
if (found) \
RELATION = hentry->reldesc; \
else \
RELATION = NULL; \
} while(0)
#define RelationCacheDelete(RELATION) \
do { \
RelNameCacheEnt *namehentry; RelIdCacheEnt *idhentry; \
char *relname; Oid reloid; bool found; \
relname = RelationGetPhysicalRelationName(RELATION); \
namehentry = (RelNameCacheEnt*)hash_search(RelationNameCache, \
relname, \
HASH_REMOVE, \
&found); \
if (namehentry == NULL) \
elog(FATAL, "can't delete from relation descriptor cache"); \
if (!found) \
elog(NOTICE, "trying to delete a reldesc that does not exist."); \
reloid = RELATION->rd_id; \
idhentry = (RelIdCacheEnt*)hash_search(RelationIdCache, \
(char *)&reloid, \
HASH_REMOVE, &found); \
if (idhentry == NULL) \
elog(FATAL, "can't delete from relation descriptor cache"); \
if (!found) \
elog(NOTICE, "trying to delete a reldesc that does not exist."); \
} while(0)
/* non-export function prototypes */
static void RelationClearRelation(Relation relation, bool rebuildIt);
static void RelationFlushRelation(Relation *relationPtr,
int skipLocalRelations);
static Relation RelationNameCacheGetRelation(const char *relationName);
static void RelationCacheAbortWalker(Relation *relationPtr, int dummy);
static void init_irels(void);
static void write_irels(void);
static void formrdesc(char *relationName, int natts,
FormData_pg_attribute *att);
static void fixrdesc(char *relationName);
static HeapTuple ScanPgRelation(RelationBuildDescInfo buildinfo);
static HeapTuple scan_pg_rel_seq(RelationBuildDescInfo buildinfo);
static HeapTuple scan_pg_rel_ind(RelationBuildDescInfo buildinfo);
static Relation AllocateRelationDesc(Relation relation, Form_pg_class relp);
static void RelationBuildTupleDesc(RelationBuildDescInfo buildinfo,
Relation relation);
static void build_tupdesc_seq(RelationBuildDescInfo buildinfo,
Relation relation);
static void build_tupdesc_ind(RelationBuildDescInfo buildinfo,
Relation relation);
static Relation RelationBuildDesc(RelationBuildDescInfo buildinfo,
Relation oldrelation);
static void IndexedAccessMethodInitialize(Relation relation);
static void AttrDefaultFetch(Relation relation);
static void RelCheckFetch(Relation relation);
static List *insert_ordered_oid(List *list, Oid datum);
/* ----------------------------------------------------------------
* RelationIdGetRelation() and RelationNameGetRelation()
* support functions
* ----------------------------------------------------------------
*/
/* --------------------------------
* ScanPgRelation
*
* this is used by RelationBuildDesc to find a pg_class
* tuple matching either a relation name or a relation id
* as specified in buildinfo.
*
* NB: the returned tuple has been copied into palloc'd storage
* and must eventually be freed with heap_freetuple.
* --------------------------------
*/
static HeapTuple
ScanPgRelation(RelationBuildDescInfo buildinfo)
{
/*
* If this is bootstrap time (initdb), then we can't use the system
* catalog indices, because they may not exist yet. Otherwise, we
* can, and do.
*/
if (IsIgnoringSystemIndexes() || !criticalRelcachesBuilt)
return scan_pg_rel_seq(buildinfo);
else
return scan_pg_rel_ind(buildinfo);
}
static HeapTuple
scan_pg_rel_seq(RelationBuildDescInfo buildinfo)
{
HeapTuple pg_class_tuple;
HeapTuple return_tuple;
Relation pg_class_desc;
HeapScanDesc pg_class_scan;
ScanKeyData key;
/* ----------------
* form a scan key
* ----------------
*/
switch (buildinfo.infotype)
{
case INFO_RELID:
ScanKeyEntryInitialize(&key, 0,
ObjectIdAttributeNumber,
F_OIDEQ,
ObjectIdGetDatum(buildinfo.i.info_id));
break;
case INFO_RELNAME:
ScanKeyEntryInitialize(&key, 0,
Anum_pg_class_relname,
F_NAMEEQ,
NameGetDatum(buildinfo.i.info_name));
break;
default:
elog(ERROR, "ScanPgRelation: bad buildinfo");
return NULL;
}
/* ----------------
* open pg_class and fetch a tuple
* ----------------
*/
pg_class_desc = heap_openr(RelationRelationName, AccessShareLock);
pg_class_scan = heap_beginscan(pg_class_desc, 0, SnapshotNow, 1, &key);
pg_class_tuple = heap_getnext(pg_class_scan, 0);
/* ----------------
* get set to return tuple
* ----------------
*/
if (!HeapTupleIsValid(pg_class_tuple))
return_tuple = pg_class_tuple;
else
{
/* ------------------
* a satanic bug used to live here: pg_class_tuple used to be
* returned here without having the corresponding buffer pinned.
* so when the buffer gets replaced, all hell breaks loose.
* this bug is discovered and killed by wei on 9/27/91.
* -------------------
*/
return_tuple = heap_copytuple(pg_class_tuple);
}
/* all done */
heap_endscan(pg_class_scan);
heap_close(pg_class_desc, AccessShareLock);
return return_tuple;
}
static HeapTuple
scan_pg_rel_ind(RelationBuildDescInfo buildinfo)
{
Relation pg_class_desc;
HeapTuple return_tuple;
pg_class_desc = heap_openr(RelationRelationName, AccessShareLock);
switch (buildinfo.infotype)
{
case INFO_RELID:
return_tuple = ClassOidIndexScan(pg_class_desc,
ObjectIdGetDatum(buildinfo.i.info_id));
break;
case INFO_RELNAME:
return_tuple = ClassNameIndexScan(pg_class_desc,
PointerGetDatum(buildinfo.i.info_name));
break;
default:
elog(ERROR, "ScanPgRelation: bad buildinfo");
return_tuple = NULL;/* keep compiler quiet */
}
heap_close(pg_class_desc, AccessShareLock);
/* The xxxIndexScan routines will have returned a palloc'd tuple. */
return return_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 Relation
AllocateRelationDesc(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((char *) relation, 0, sizeof(RelationData));
/* make sure relation is marked as having no open file yet */
relation->rd_fd = -1;
/* ----------------
* 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((char *) relationForm, (char *) relp, CLASS_TUPLE_SIZE);
/* initialize relation tuple form */
relation->rd_rel = relationForm;
/* and allocate attribute tuple form storage */
relation->rd_att = CreateTemplateTupleDesc(relationForm->relnatts);
MemoryContextSwitchTo(oldcxt);
return relation;
}
/* --------------------------------
* RelationBuildTupleDesc
*
* Form the relation's tuple descriptor from information in
* the pg_attribute, pg_attrdef & pg_relcheck system cataloges.
* --------------------------------
*/
static void
RelationBuildTupleDesc(RelationBuildDescInfo buildinfo,
Relation relation)
{
/*
* If this is bootstrap time (initdb), then we can't use the system
* catalog indices, because they may not exist yet. Otherwise, we
* can, and do.
*/
if (IsIgnoringSystemIndexes() || !criticalRelcachesBuilt)
build_tupdesc_seq(buildinfo, relation);
else
build_tupdesc_ind(buildinfo, relation);
}
static void
SetConstrOfRelation(Relation relation,
TupleConstr *constr,
int ndef,
AttrDefault *attrdef)
{
if (constr->has_not_null || ndef > 0 || relation->rd_rel->relchecks)
{
relation->rd_att->constr = constr;
if (ndef > 0) /* DEFAULTs */
{
if (ndef < relation->rd_rel->relnatts)
constr->defval = (AttrDefault *)
repalloc(attrdef, ndef * sizeof(AttrDefault));
else
constr->defval = attrdef;
constr->num_defval = ndef;
AttrDefaultFetch(relation);
}
else
constr->num_defval = 0;
if (relation->rd_rel->relchecks > 0) /* CHECKs */
{
constr->num_check = relation->rd_rel->relchecks;
constr->check = (ConstrCheck *)
MemoryContextAlloc(CacheMemoryContext,
constr->num_check * sizeof(ConstrCheck));
MemSet(constr->check, 0, constr->num_check * sizeof(ConstrCheck));
RelCheckFetch(relation);
}
else
constr->num_check = 0;
}
else
{
pfree(constr);
relation->rd_att->constr = NULL;
}
}
static void
build_tupdesc_seq(RelationBuildDescInfo buildinfo,
Relation relation)
{
HeapTuple pg_attribute_tuple;
Relation pg_attribute_desc;
HeapScanDesc pg_attribute_scan;
Form_pg_attribute attp;
ScanKeyData key;
int need;
TupleConstr *constr;
AttrDefault *attrdef = NULL;
int ndef = 0;
constr = (TupleConstr *) MemoryContextAlloc(CacheMemoryContext,
sizeof(TupleConstr));
constr->has_not_null = false;
/* ----------------
* form a scan key
* ----------------
*/
ScanKeyEntryInitialize(&key, 0,
Anum_pg_attribute_attrelid,
F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(relation)));
/* ----------------
* open pg_attribute and begin a scan
* ----------------
*/
pg_attribute_desc = heap_openr(AttributeRelationName, AccessShareLock);
pg_attribute_scan = heap_beginscan(pg_attribute_desc, 0, SnapshotNow, 1, &key);
/* ----------------
* add attribute data to relation->rd_att
* ----------------
*/
need = relation->rd_rel->relnatts;
pg_attribute_tuple = heap_getnext(pg_attribute_scan, 0);
while (HeapTupleIsValid(pg_attribute_tuple) && need > 0)
{
attp = (Form_pg_attribute) GETSTRUCT(pg_attribute_tuple);
if (attp->attnum > 0)
{
relation->rd_att->attrs[attp->attnum - 1] =
(Form_pg_attribute) MemoryContextAlloc(CacheMemoryContext,
ATTRIBUTE_TUPLE_SIZE);
memcpy((char *) (relation->rd_att->attrs[attp->attnum - 1]),
(char *) attp,
ATTRIBUTE_TUPLE_SIZE);
need--;
/* Update if this attribute have a constraint */
if (attp->attnotnull)
constr->has_not_null = true;
if (attp->atthasdef)
{
if (attrdef == NULL)
{
attrdef = (AttrDefault *)
MemoryContextAlloc(CacheMemoryContext,
relation->rd_rel->relnatts *
sizeof(AttrDefault));
MemSet(attrdef, 0,
relation->rd_rel->relnatts * sizeof(AttrDefault));
}
attrdef[ndef].adnum = attp->attnum;
attrdef[ndef].adbin = NULL;
ndef++;
}
}
pg_attribute_tuple = heap_getnext(pg_attribute_scan, 0);
}
if (need > 0)
elog(ERROR, "catalog is missing %d attribute%s for relid %u",
need, (need == 1 ? "" : "s"), RelationGetRelid(relation));
/* ----------------
* end the scan and close the attribute relation
* ----------------
*/
heap_endscan(pg_attribute_scan);
heap_close(pg_attribute_desc, AccessShareLock);
SetConstrOfRelation(relation, constr, ndef, attrdef);
}
static void
build_tupdesc_ind(RelationBuildDescInfo buildinfo,
Relation relation)
{
Relation attrel;
HeapTuple atttup;
Form_pg_attribute attp;
TupleConstr *constr;
AttrDefault *attrdef = NULL;
int ndef = 0;
int i;
constr = (TupleConstr *) MemoryContextAlloc(CacheMemoryContext,
sizeof(TupleConstr));
constr->has_not_null = false;
attrel = heap_openr(AttributeRelationName, AccessShareLock);
for (i = 1; i <= relation->rd_rel->relnatts; i++)
{
#ifdef _DROP_COLUMN_HACK__
bool columnDropped = false;
#endif /* _DROP_COLUMN_HACK__ */
atttup = AttributeRelidNumIndexScan(attrel,
ObjectIdGetDatum(RelationGetRelid(relation)),
Int32GetDatum(i));
if (!HeapTupleIsValid(atttup))
{
#ifdef _DROP_COLUMN_HACK__
atttup = AttributeRelidNumIndexScan(attrel,
ObjectIdGetDatum(RelationGetRelid(relation)),
Int32GetDatum(DROPPED_COLUMN_INDEX(i)));
if (!HeapTupleIsValid(atttup))
#endif /* _DROP_COLUMN_HACK__ */
elog(ERROR, "cannot find attribute %d of relation %s", i,
RelationGetRelationName(relation));
#ifdef _DROP_COLUMN_HACK__
columnDropped = true;
#endif /* _DROP_COLUMN_HACK__ */
}
relation->rd_att->attrs[i - 1] = attp =
(Form_pg_attribute) MemoryContextAlloc(CacheMemoryContext,
ATTRIBUTE_TUPLE_SIZE);
memcpy((char *) attp,
(char *) (Form_pg_attribute) GETSTRUCT(atttup),
ATTRIBUTE_TUPLE_SIZE);
/* don't forget to free the tuple returned from xxxIndexScan */
heap_freetuple(atttup);
#ifdef _DROP_COLUMN_HACK__
if (columnDropped)
continue;
#endif /* _DROP_COLUMN_HACK__ */
/* Update if this attribute have a constraint */
if (attp->attnotnull)
constr->has_not_null = true;
if (attp->atthasdef)
{
if (attrdef == NULL)
{
attrdef = (AttrDefault *)
MemoryContextAlloc(CacheMemoryContext,
relation->rd_rel->relnatts *
sizeof(AttrDefault));
MemSet(attrdef, 0,
relation->rd_rel->relnatts * sizeof(AttrDefault));
}
attrdef[ndef].adnum = i;
attrdef[ndef].adbin = NULL;
ndef++;
}
}
heap_close(attrel, AccessShareLock);
SetConstrOfRelation(relation, constr, ndef, attrdef);
}
/* --------------------------------
* RelationBuildRuleLock
*
* Form the relation's rewrite rules from information in
* the pg_rewrite system catalog.
*
* Note: The rule parsetrees are potentially very complex node structures.
* To allow these trees to be freed when the relcache entry is flushed,
* we make a private memory context to hold the RuleLock information for
* each relcache entry that has associated rules. The context is used
* just for rule info, not for any other subsidiary data of the relcache
* entry, because that keeps the update logic in RelationClearRelation()
* manageable. The other subsidiary data structures are simple enough
* to be easy to free explicitly, anyway.
* --------------------------------
*/
static void
RelationBuildRuleLock(Relation relation)
{
MemoryContext rulescxt;
MemoryContext oldcxt;
HeapTuple pg_rewrite_tuple;
Relation pg_rewrite_desc;
TupleDesc pg_rewrite_tupdesc;
HeapScanDesc pg_rewrite_scan;
ScanKeyData key;
RuleLock *rulelock;
int numlocks;
RewriteRule **rules;
int maxlocks;
/*
* Make the private context. Parameters are set on the assumption
* that it'll probably not contain much data.
*/
rulescxt = AllocSetContextCreate(CacheMemoryContext,
RelationGetRelationName(relation),
0, /* minsize */
1024, /* initsize */
1024); /* maxsize */
relation->rd_rulescxt = rulescxt;
/* ----------------
* form an array to hold the rewrite rules (the array is extended if
* necessary)
* ----------------
*/
maxlocks = 4;
rules = (RewriteRule **)
MemoryContextAlloc(rulescxt, sizeof(RewriteRule *) * maxlocks);
numlocks = 0;
/* ----------------
* form a scan key
* ----------------
*/
ScanKeyEntryInitialize(&key, 0,
Anum_pg_rewrite_ev_class,
F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(relation)));
/* ----------------
* open pg_rewrite and begin a scan
* ----------------
*/
pg_rewrite_desc = heap_openr(RewriteRelationName, AccessShareLock);
pg_rewrite_scan = heap_beginscan(pg_rewrite_desc, 0, SnapshotNow, 1, &key);
pg_rewrite_tupdesc = RelationGetDescr(pg_rewrite_desc);
while (HeapTupleIsValid(pg_rewrite_tuple = heap_getnext(pg_rewrite_scan, 0)))
{
bool isnull;
Datum ruleaction;
Datum rule_evqual;
char *ruleaction_str;
char *rule_evqual_str;
RewriteRule *rule;
rule = (RewriteRule *) MemoryContextAlloc(rulescxt,
sizeof(RewriteRule));
rule->ruleId = pg_rewrite_tuple->t_data->t_oid;
rule->event = DatumGetInt32(heap_getattr(pg_rewrite_tuple,
Anum_pg_rewrite_ev_type,
pg_rewrite_tupdesc,
&isnull)) - 48;
rule->attrno = DatumGetInt16(heap_getattr(pg_rewrite_tuple,
Anum_pg_rewrite_ev_attr,
pg_rewrite_tupdesc,
&isnull));
rule->isInstead = DatumGetBool(heap_getattr(pg_rewrite_tuple,
Anum_pg_rewrite_is_instead,
pg_rewrite_tupdesc,
&isnull));
ruleaction = heap_getattr(pg_rewrite_tuple,
Anum_pg_rewrite_ev_action,
pg_rewrite_tupdesc,
&isnull);
Assert(! isnull);
ruleaction_str = DatumGetCString(DirectFunctionCall1(textout,
ruleaction));
oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
rule->actions = (List *) stringToNode(ruleaction_str);
MemoryContextSwitchTo(oldcxt);
pfree(ruleaction_str);
rule_evqual = heap_getattr(pg_rewrite_tuple,
Anum_pg_rewrite_ev_qual,
pg_rewrite_tupdesc,
&isnull);
Assert(! isnull);
rule_evqual_str = DatumGetCString(DirectFunctionCall1(textout,
rule_evqual));
oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
rule->qual = (Node *) stringToNode(rule_evqual_str);
MemoryContextSwitchTo(oldcxt);
pfree(rule_evqual_str);
if (numlocks >= maxlocks)
{
maxlocks *= 2;
rules = (RewriteRule **)
repalloc(rules, sizeof(RewriteRule *) * maxlocks);
}
rules[numlocks++] = rule;
}
/* ----------------
* end the scan and close the attribute relation
* ----------------
*/
heap_endscan(pg_rewrite_scan);
heap_close(pg_rewrite_desc, AccessShareLock);
/* ----------------
* form a RuleLock and insert into relation
* ----------------
*/
rulelock = (RuleLock *) MemoryContextAlloc(rulescxt, sizeof(RuleLock));
rulelock->numLocks = numlocks;
rulelock->rules = rules;
relation->rd_rules = rulelock;
}
/* --------------------------------
* equalRuleLocks
*
* Determine whether two RuleLocks are equivalent
*
* Probably this should be in the rules code someplace...
* --------------------------------
*/
static bool
equalRuleLocks(RuleLock *rlock1, RuleLock *rlock2)
{
int i,
j;
if (rlock1 != NULL)
{
if (rlock2 == NULL)
return false;
if (rlock1->numLocks != rlock2->numLocks)
return false;
for (i = 0; i < rlock1->numLocks; i++)
{
RewriteRule *rule1 = rlock1->rules[i];
RewriteRule *rule2 = NULL;
/*
* We can't assume that the rules are always read from
* pg_rewrite in the same order; so use the rule OIDs to
* identify the rules to compare. (We assume here that the
* same OID won't appear twice in either ruleset.)
*/
for (j = 0; j < rlock2->numLocks; j++)
{
rule2 = rlock2->rules[j];
if (rule1->ruleId == rule2->ruleId)
break;
}
if (j >= rlock2->numLocks)
return false;
if (rule1->event != rule2->event)
return false;
if (rule1->attrno != rule2->attrno)
return false;
if (rule1->isInstead != rule2->isInstead)
return false;
if (!equal(rule1->qual, rule2->qual))
return false;
if (!equal(rule1->actions, rule2->actions))
return false;
}
}
else if (rlock2 != NULL)
return false;
return true;
}
/* --------------------------------
* RelationBuildDesc
*
* Build a relation descriptor --- either a new one, or by
* recycling the given old relation object. The latter case
* supports rebuilding a relcache entry without invalidating
* pointers to it.
*
* To build a relation descriptor, we have to allocate space,
* open the underlying unix file and initialize the following
* fields:
*
* File rd_fd; open file descriptor
* int rd_nblocks; number of blocks in rel
* it will be set in ambeginscan()
* uint16 rd_refcnt; reference count
* Form_pg_am rd_am; AM tuple
* Form_pg_class rd_rel; RELATION tuple
* Oid rd_id; relation's object id
* LockInfoData rd_lockInfo; lock manager's info
* TupleDesc rd_att; tuple descriptor
*
* Note: rd_ismem (rel is in-memory only) is currently unused
* by any part of the system. someday this will indicate that
* the relation lives only in the main-memory buffer pool
* -cim 2/4/91
* --------------------------------
*/
static Relation
RelationBuildDesc(RelationBuildDescInfo buildinfo,
Relation oldrelation)
{
File fd;
Relation relation;
Oid relid;
Oid relam;
HeapTuple pg_class_tuple;
Form_pg_class relp;
MemoryContext oldcxt;
/* ----------------
* find the tuple in pg_class corresponding to the given relation id
* ----------------
*/
pg_class_tuple = ScanPgRelation(buildinfo);
/* ----------------
* if no such tuple exists, return NULL
* ----------------
*/
if (!HeapTupleIsValid(pg_class_tuple))
return NULL;
/* ----------------
* get information from the pg_class_tuple
* ----------------
*/
relid = pg_class_tuple->t_data->t_oid;
relp = (Form_pg_class) GETSTRUCT(pg_class_tuple);
/* ----------------
* allocate storage for the relation descriptor,
* and copy pg_class_tuple to relation->rd_rel.
* ----------------
*/
relation = AllocateRelationDesc(oldrelation, relp);
/* -------------------
* now we can free the memory allocated for pg_class_tuple
* -------------------
*/
heap_freetuple(pg_class_tuple);
/* ----------------
* initialize the relation's relation id (relation->rd_id)
* ----------------
*/
RelationGetRelid(relation) = relid;
/* ----------------
* initialize relation->rd_refcnt
* ----------------
*/
RelationSetReferenceCount(relation, 1);
/* ----------------
* normal relations are not nailed into the cache
* ----------------
*/
relation->rd_isnailed = false;
/* ----------------
* initialize the access method information (relation->rd_am)
* ----------------
*/
relam = relation->rd_rel->relam;
if (OidIsValid(relam))
relation->rd_am = AccessMethodObjectIdGetForm(relam,
CacheMemoryContext);
/* ----------------
* initialize the tuple descriptor (relation->rd_att).
* ----------------
*/
RelationBuildTupleDesc(buildinfo, relation);
/* ----------------
* Fetch rules and triggers that affect this relation
* ----------------
*/
if (relation->rd_rel->relhasrules)
RelationBuildRuleLock(relation);
else
{
relation->rd_rules = NULL;
relation->rd_rulescxt = NULL;
}
if (relation->rd_rel->reltriggers > 0)
RelationBuildTriggers(relation);
else
relation->trigdesc = NULL;
/* ----------------
* initialize index strategy and support information for this relation
* ----------------
*/
if (OidIsValid(relam))
IndexedAccessMethodInitialize(relation);
/* ----------------
* initialize the relation lock manager information
* ----------------
*/
RelationInitLockInfo(relation); /* see lmgr.c */
/* ----------------
* open the relation and assign the file descriptor returned
* by the storage manager code to rd_fd.
* ----------------
*/
if (relation->rd_rel->relkind != RELKIND_VIEW) {
fd = smgropen(DEFAULT_SMGR, relation);
Assert(fd >= -1);
if (fd == -1)
elog(NOTICE, "RelationBuildDesc: smgropen(%s): %m",
NameStr(relation->rd_rel->relname));
relation->rd_fd = fd;
} else {
relation->rd_fd = -1;
}
/* ----------------
* insert newly created relation into proper relcaches,
* restore memory context and return the new reldesc.
* ----------------
*/
oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
RelationCacheInsert(relation);
MemoryContextSwitchTo(oldcxt);
return relation;
}
static void
IndexedAccessMethodInitialize(Relation relation)
{
IndexStrategy strategy;
RegProcedure *support;
int natts;
Size stratSize;
Size supportSize;
uint16 relamstrategies;
uint16 relamsupport;
natts = relation->rd_rel->relnatts;
relamstrategies = relation->rd_am->amstrategies;
stratSize = AttributeNumberGetIndexStrategySize(natts, relamstrategies);
strategy = (IndexStrategy) MemoryContextAlloc(CacheMemoryContext,
stratSize);
relamsupport = relation->rd_am->amsupport;
if (relamsupport > 0)
{
supportSize = natts * (relamsupport * sizeof(RegProcedure));
support = (RegProcedure *) MemoryContextAlloc(CacheMemoryContext,
supportSize);
}
else
support = (RegProcedure *) NULL;
IndexSupportInitialize(strategy, support,
&relation->rd_uniqueindex,
relation->rd_att->attrs[0]->attrelid,
relation->rd_rel->relam,
relamstrategies, relamsupport, natts);
RelationSetIndexSupport(relation, strategy, support);
}
/* --------------------------------
* formrdesc
*
* This is a special cut-down version of RelationBuildDesc()
* used by RelationCacheInitialize() in initializing the relcache.
* The relation descriptor is built just from the supplied parameters,
* without actually looking at any system table entries.
*
* NOTE: we assume we are already switched into CacheMemoryContext.
* --------------------------------
*/
static void
formrdesc(char *relationName,
int natts,
FormData_pg_attribute *att)
{
Relation relation;
int i;
/* ----------------
* allocate new relation desc
* ----------------
*/
relation = (Relation) palloc(sizeof(RelationData));
MemSet((char *) relation, 0, sizeof(RelationData));
/* ----------------
* don't open the unix file yet..
* ----------------
*/
relation->rd_fd = -1;
/* ----------------
* initialize reference count
* ----------------
*/
RelationSetReferenceCount(relation, 1);
/* ----------------
* all entries built with this routine are nailed-in-cache
* ----------------
*/
relation->rd_isnailed = true;
/* ----------------
* initialize relation tuple form
*
* The data we insert here is pretty incomplete/bogus, but it'll
* serve to get us launched. RelationCacheInitializePhase2() will
* read the real data from pg_class and replace what we've done here.
* ----------------
*/
relation->rd_rel = (Form_pg_class) palloc(CLASS_TUPLE_SIZE);
MemSet(relation->rd_rel, 0, CLASS_TUPLE_SIZE);
strcpy(RelationGetPhysicalRelationName(relation), relationName);
/*
* For debugging purposes, it's important to distinguish between
* shared and non-shared relations, even at bootstrap time. There's
* code in the buffer manager that traces allocations that has to know
* about this.
*/
if (IsSystemRelationName(relationName))
relation->rd_rel->relisshared = IsSharedSystemRelationName(relationName);
else
relation->rd_rel->relisshared = false;
relation->rd_rel->relpages = 1;
relation->rd_rel->reltuples = 1;
relation->rd_rel->relkind = RELKIND_RELATION;
relation->rd_rel->relnatts = (int16) natts;
/* ----------------
* initialize attribute tuple form
* ----------------
*/
relation->rd_att = CreateTemplateTupleDesc(natts);
/* ----------------
* initialize tuple desc info
* ----------------
*/
for (i = 0; i < natts; i++)
{
relation->rd_att->attrs[i] = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
memcpy((char *) relation->rd_att->attrs[i],
(char *) &att[i],
ATTRIBUTE_TUPLE_SIZE);
}
/* ----------------
* initialize relation id
* ----------------
*/
RelationGetRelid(relation) = relation->rd_att->attrs[0]->attrelid;
/* ----------------
* initialize the relation lock manager information
* ----------------
*/
RelationInitLockInfo(relation); /* see lmgr.c */
/* ----------------
* add new reldesc to relcache
* ----------------
*/
RelationCacheInsert(relation);
/*
* Determining this requires a scan on pg_class, but to do the scan
* the rdesc for pg_class must already exist. Therefore we must do
* the check (and possible set) after cache insertion.
*
* XXX I believe the above comment is misguided; we should be running
* in bootstrap or init processing mode here, and CatalogHasIndex
* relies on hard-wired info in those cases.
*/
relation->rd_rel->relhasindex =
CatalogHasIndex(relationName, RelationGetRelid(relation));
}
/* --------------------------------
* fixrdesc
*
* Update the phony data inserted by formrdesc() with real info
* from pg_class.
* --------------------------------
*/
static void
fixrdesc(char *relationName)
{
RelationBuildDescInfo buildinfo;
HeapTuple pg_class_tuple;
Form_pg_class relp;
Relation relation;
/* ----------------
* find the tuple in pg_class corresponding to the given relation name
* ----------------
*/
buildinfo.infotype = INFO_RELNAME;
buildinfo.i.info_name = relationName;
pg_class_tuple = ScanPgRelation(buildinfo);
if (!HeapTupleIsValid(pg_class_tuple))
elog(FATAL, "fixrdesc: no pg_class entry for %s",
relationName);
relp = (Form_pg_class) GETSTRUCT(pg_class_tuple);
/* ----------------
* find the pre-made relcache entry (better be there!)
* ----------------
*/
relation = RelationNameCacheGetRelation(relationName);
if (!RelationIsValid(relation))
elog(FATAL, "fixrdesc: no existing relcache entry for %s",
relationName);
/* ----------------
* and copy pg_class_tuple to relation->rd_rel.
* (See notes in AllocateRelationDesc())
* ----------------
*/
Assert(relation->rd_rel != NULL);
memcpy((char *) relation->rd_rel, (char *) relp, CLASS_TUPLE_SIZE);
heap_freetuple(pg_class_tuple);
}
/* ----------------------------------------------------------------
* Relation Descriptor Lookup Interface
* ----------------------------------------------------------------
*/
/* --------------------------------
* RelationIdCacheGetRelation
*
* Lookup an existing reldesc by OID.
*
* Only try to get the reldesc by looking in the cache,
* do not go to the disk.
*
* NB: relation ref count is incremented if successful.
* Caller should eventually decrement count. (Usually,
* that happens by calling RelationClose().)
* --------------------------------
*/
Relation
RelationIdCacheGetRelation(Oid relationId)
{
Relation rd;
RelationIdCacheLookup(relationId, rd);
if (RelationIsValid(rd))
{
if (rd->rd_fd == -1 && rd->rd_rel->relkind != RELKIND_VIEW)
{
rd->rd_fd = smgropen(DEFAULT_SMGR, rd);
Assert(rd->rd_fd != -1 || rd->rd_unlinked);
}
RelationIncrementReferenceCount(rd);
}
return rd;
}
/* --------------------------------
* RelationNameCacheGetRelation
*
* As above, but lookup by name.
* --------------------------------
*/
static Relation
RelationNameCacheGetRelation(const char *relationName)
{
Relation rd;
NameData name;
/*
* make sure that the name key used for hash lookup is properly
* null-padded
*/
namestrcpy(&name, relationName);
RelationNameCacheLookup(NameStr(name), rd);
if (RelationIsValid(rd))
{
if (rd->rd_fd == -1 && rd->rd_rel->relkind != RELKIND_VIEW)
{
rd->rd_fd = smgropen(DEFAULT_SMGR, rd);
Assert(rd->rd_fd != -1 || rd->rd_unlinked);
}
RelationIncrementReferenceCount(rd);
}
return rd;
}
/* --------------------------------
* RelationIdGetRelation
*
* Lookup a reldesc by OID; make one if not already in cache.
*
* NB: relation ref count is incremented, or set to 1 if new entry.
* Caller should eventually decrement count. (Usually,
* that happens by calling RelationClose().)
* --------------------------------
*/
Relation
RelationIdGetRelation(Oid relationId)
{
Relation rd;
RelationBuildDescInfo buildinfo;
/* ----------------
* increment access statistics
* ----------------
*/
IncrHeapAccessStat(local_RelationIdGetRelation);
IncrHeapAccessStat(global_RelationIdGetRelation);
/* ----------------
* first try and get a reldesc from the cache
* ----------------
*/
rd = RelationIdCacheGetRelation(relationId);
if (RelationIsValid(rd))
return rd;
/* ----------------
* no reldesc in the cache, so have RelationBuildDesc()
* build one and add it.
* ----------------
*/
buildinfo.infotype = INFO_RELID;
buildinfo.i.info_id = relationId;
rd = RelationBuildDesc(buildinfo, NULL);
return rd;
}
/* --------------------------------
* RelationNameGetRelation
*
* As above, but lookup by name.
* --------------------------------
*/
Relation
RelationNameGetRelation(const char *relationName)
{
char *temprelname;
Relation rd;
RelationBuildDescInfo buildinfo;
/* ----------------
* increment access statistics
* ----------------
*/
IncrHeapAccessStat(local_RelationNameGetRelation);
IncrHeapAccessStat(global_RelationNameGetRelation);
/* ----------------
* if caller is looking for a temp relation, substitute its real name;
* we only index temp rels by their real names.
* ----------------
*/
temprelname = get_temp_rel_by_username(relationName);
if (temprelname)
relationName = temprelname;
/* ----------------
* first try and get a reldesc from the cache
* ----------------
*/
rd = RelationNameCacheGetRelation(relationName);
if (RelationIsValid(rd))
return rd;
/* ----------------
* no reldesc in the cache, so have RelationBuildDesc()
* build one and add it.
* ----------------
*/
buildinfo.infotype = INFO_RELNAME;
buildinfo.i.info_name = (char *) relationName;
rd = RelationBuildDesc(buildinfo, NULL);
return rd;
}
/* ----------------------------------------------------------------
* cache invalidation support routines
* ----------------------------------------------------------------
*/
/* --------------------------------
* RelationClose - close an open relation
*
* Actually, we just decrement the refcount.
* --------------------------------
*/
void
RelationClose(Relation relation)
{
/* Note: no locking manipulations needed */
RelationDecrementReferenceCount(relation);
}
/* --------------------------------
* RelationClearRelation
*
* Physically blow away a relation cache entry, or reset it and rebuild
* it from scratch (that is, from catalog entries). The latter path is
* usually used when we are notified of a change to an open relation
* (one with refcount > 0). However, this routine just does whichever
* it's told to do; callers must determine which they want.
*
* If we detect a change in the relation's TupleDesc, rules, or triggers
* while rebuilding, we complain unless refcount is 0.
* --------------------------------
*/
static void
RelationClearRelation(Relation relation, bool rebuildIt)
{
MemoryContext oldcxt;
/*
* Make sure smgr and lower levels close the relation's files, if they
* weren't closed already. We do this unconditionally; if the
* relation is not deleted, the next smgr access should reopen the
* files automatically. This ensures that the low-level file access
* state is updated after, say, a vacuum truncation.
*
* NOTE: this call is a no-op if the relation's smgr file is already
* closed or unlinked.
*/
smgrclose(DEFAULT_SMGR, relation);
/*
* Never, never ever blow away a nailed-in system relation, because
* we'd be unable to recover.
*/
if (relation->rd_isnailed)
return;
/*
* Remove relation from hash tables
*
* Note: we might be reinserting it momentarily, but we must not have it
* visible in the hash tables until it's valid again, so don't try to
* optimize this away...
*/
oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
RelationCacheDelete(relation);
MemoryContextSwitchTo(oldcxt);
/* Clear out catcache's entries for this relation */
SystemCacheRelationFlushed(RelationGetRelid(relation));
/*
* Free all the subsidiary data structures of the relcache entry. We
* cannot free rd_att if we are trying to rebuild the entry, however,
* because pointers to it may be cached in various places. The trigger
* manager might also have pointers into the trigdesc, and the rule
* manager might have pointers into the rewrite rules. So to begin
* with, we can only get rid of these fields:
*/
if (relation->rd_am)
pfree(relation->rd_am);
if (relation->rd_rel)
pfree(relation->rd_rel);
if (relation->rd_istrat)
pfree(relation->rd_istrat);
if (relation->rd_support)
pfree(relation->rd_support);
freeList(relation->rd_indexlist);
/*
* If we're really done with the relcache entry, blow it away. But if
* someone is still using it, reconstruct the whole deal without
* moving the physical RelationData record (so that the someone's
* pointer is still valid).
*/
if (!rebuildIt)
{
/* ok to zap remaining substructure */
FreeTupleDesc(relation->rd_att);
if (relation->rd_rulescxt)
MemoryContextDelete(relation->rd_rulescxt);
FreeTriggerDesc(relation->trigdesc);
pfree(relation);
}
else
{
/*
* When rebuilding an open relcache entry, must preserve ref count
* and myxactonly flag. Also attempt to preserve the tupledesc,
* rewrite rules, and trigger substructures in place. Furthermore
* we save/restore rd_nblocks (in case it is a local relation)
* *and* call RelationGetNumberOfBlocks (in case it isn't).
*/
uint16 old_refcnt = relation->rd_refcnt;
bool old_myxactonly = relation->rd_myxactonly;
TupleDesc old_att = relation->rd_att;
RuleLock *old_rules = relation->rd_rules;
MemoryContext old_rulescxt = relation->rd_rulescxt;
TriggerDesc *old_trigdesc = relation->trigdesc;
int old_nblocks = relation->rd_nblocks;
bool relDescChanged = false;
RelationBuildDescInfo buildinfo;
buildinfo.infotype = INFO_RELID;
buildinfo.i.info_id = RelationGetRelid(relation);
if (RelationBuildDesc(buildinfo, relation) != relation)
{
/* Should only get here if relation was deleted */
FreeTupleDesc(old_att);
if (old_rulescxt)
MemoryContextDelete(old_rulescxt);
FreeTriggerDesc(old_trigdesc);
pfree(relation);
elog(ERROR, "RelationClearRelation: relation %u deleted while still in use",
buildinfo.i.info_id);
}
RelationSetReferenceCount(relation, old_refcnt);
relation->rd_myxactonly = old_myxactonly;
if (equalTupleDescs(old_att, relation->rd_att))
{
FreeTupleDesc(relation->rd_att);
relation->rd_att = old_att;
}
else
{
FreeTupleDesc(old_att);
relDescChanged = true;
}
if (equalRuleLocks(old_rules, relation->rd_rules))
{
if (relation->rd_rulescxt)
MemoryContextDelete(relation->rd_rulescxt);
relation->rd_rules = old_rules;
relation->rd_rulescxt = old_rulescxt;
}
else
{
if (old_rulescxt)
MemoryContextDelete(old_rulescxt);
relDescChanged = true;
}
if (equalTriggerDescs(old_trigdesc, relation->trigdesc))
{
FreeTriggerDesc(relation->trigdesc);
relation->trigdesc = old_trigdesc;
}
else
{
FreeTriggerDesc(old_trigdesc);
relDescChanged = true;
}
relation->rd_nblocks = old_nblocks;
/*
* this is kind of expensive, but I think we must do it in case
* relation has been truncated...
*/
if (relation->rd_unlinked)
relation->rd_nblocks = 0;
else
relation->rd_nblocks = RelationGetNumberOfBlocks(relation);
if (relDescChanged && !RelationHasReferenceCountZero(relation))
elog(ERROR, "RelationClearRelation: relation %u modified while in use",
buildinfo.i.info_id);
}
}
/* --------------------------------
* RelationFlushRelation
*
* Rebuild the relation if it is open (refcount > 0), else blow it away.
* If skipLocalRelations is TRUE, xact-local relations are ignored
* (which is useful when processing SI cache reset, since xact-local
* relations could not be targets of notifications from other backends).
*
* The peculiar calling convention (pointer to pointer to relation)
* is needed so that we can use this routine as a hash table walker.
* --------------------------------
*/
static void
RelationFlushRelation(Relation *relationPtr,
int skipLocalRelations)
{
Relation relation = *relationPtr;
bool rebuildIt;
if (relation->rd_myxactonly)
{
if (skipLocalRelations)
return; /* don't touch local rels if so commanded */
/*
* Local rels should always be rebuilt, not flushed; the relcache
* entry must live until RelationPurgeLocalRelation().
*/
rebuildIt = true;
}
else
{
/*
* Nonlocal rels can be dropped from the relcache if not open.
*/
rebuildIt = !RelationHasReferenceCountZero(relation);
}
RelationClearRelation(relation, rebuildIt);
}
/* --------------------------------
* RelationForgetRelation -
*
* RelationClearRelation + if the relation is myxactonly then
* remove the relation descriptor from the newly created
* relation list.
* --------------------------------
*/
void
RelationForgetRelation(Oid rid)
{
Relation relation;
RelationIdCacheLookup(rid, relation);
if (PointerIsValid(relation))
{
if (relation->rd_myxactonly)
{
List *curr;
List *prev = NIL;
foreach(curr, newlyCreatedRelns)
{
Relation reln = lfirst(curr);
Assert(reln != NULL && reln->rd_myxactonly);
if (RelationGetRelid(reln) == rid)
break;
prev = curr;
}
if (curr == NIL)
elog(FATAL, "Local relation %s not found in list",
RelationGetRelationName(relation));
if (prev == NIL)
newlyCreatedRelns = lnext(newlyCreatedRelns);
else
lnext(prev) = lnext(curr);
pfree(curr);
}
/* Unconditionally destroy the relcache entry */
RelationClearRelation(relation, false);
}
}
/* --------------------------------
* RelationIdInvalidateRelationCacheByRelationId
*
* This routine is invoked for SI cache flush messages.
*
* We used to skip local relations, on the grounds that they could
* not be targets of cross-backend SI update messages; but it seems
* safer to process them, so that our *own* SI update messages will
* have the same effects during CommandCounterIncrement for both
* local and nonlocal relations.
* --------------------------------
*/
void
RelationIdInvalidateRelationCacheByRelationId(Oid relationId)
{
Relation relation;
RelationIdCacheLookup(relationId, relation);
if (PointerIsValid(relation))
RelationFlushRelation(&relation, false);
}
#if NOT_USED
/* only used by RelationIdInvalidateRelationCacheByAccessMethodId,
* which is dead code.
*/
static void
RelationFlushIndexes(Relation *r,
Oid accessMethodId)
{
Relation relation = *r;
if (!RelationIsValid(relation))
{
elog(NOTICE, "inval call to RFI");
return;
}
if (relation->rd_rel->relkind == RELKIND_INDEX && /* XXX style */
(!OidIsValid(accessMethodId) ||
relation->rd_rel->relam == accessMethodId))
RelationFlushRelation(&relation, false);
}
#endif
#if NOT_USED
void
RelationIdInvalidateRelationCacheByAccessMethodId(Oid accessMethodId)
{
/*
* 25 aug 1992: mao commented out the ht walk below. it should be
* doing the right thing, in theory, but flushing reldescs for index
* relations apparently doesn't work. we want to cut 4.0.1, and i
* don't want to introduce new bugs. this code never executed before,
* so i'm turning it off for now. after the release is cut, i'll fix
* this up.
*
* 20 nov 1999: this code has still never done anything, so I'm cutting
* the routine out of the system entirely. tgl
*/
HashTableWalk(RelationNameCache, (HashtFunc) RelationFlushIndexes,
accessMethodId);
}
#endif
/*
* RelationCacheInvalidate
* Blow away cached relation descriptors that have zero reference counts,
* and rebuild those with positive reference counts.
*
* This is currently used only to recover from SI message buffer overflow,
* so we do not touch transaction-local relations; they cannot be targets
* of cross-backend SI updates (and our own updates now go through a
* separate linked list that isn't limited by the SI message buffer size).
*/
void
RelationCacheInvalidate(void)
{
HashTableWalk(RelationNameCache, (HashtFunc) RelationFlushRelation,
(int) true);
}
/*
* RelationCacheAbort
*
* Clean up the relcache at transaction abort.
*
* What we need to do here is reset relcache entry ref counts to
* their normal not-in-a-transaction state. A ref count may be
* too high because some routine was exited by elog() between
* incrementing and decrementing the count.
*
* XXX Maybe we should do this at transaction commit, too, in case
* someone forgets to decrement a refcount in a non-error path?
*/
void
RelationCacheAbort(void)
{
HashTableWalk(RelationNameCache, (HashtFunc) RelationCacheAbortWalker,
0);
}
static void
RelationCacheAbortWalker(Relation *relationPtr, int dummy)
{
Relation relation = *relationPtr;
if (relation->rd_isnailed)
RelationSetReferenceCount(relation, 1);
else
RelationSetReferenceCount(relation, 0);
}
/* --------------------------------
* RelationRegisterRelation -
* register the Relation descriptor of a newly created relation
* with the relation descriptor Cache.
* --------------------------------
*/
void
RelationRegisterRelation(Relation relation)
{
MemoryContext oldcxt;
RelationInitLockInfo(relation);
oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
RelationCacheInsert(relation);
/*
* we've just created the relation. It is invisible to anyone else
* before the transaction is committed. Setting rd_myxactonly allows
* us to use the local buffer manager for select/insert/etc before the
* end of transaction. (We also need to keep track of relations
* created during a transaction and does the necessary clean up at the
* end of the transaction.) - ay 3/95
*/
relation->rd_myxactonly = TRUE;
newlyCreatedRelns = lcons(relation, newlyCreatedRelns);
MemoryContextSwitchTo(oldcxt);
}
/*
* RelationPurgeLocalRelation -
* find all the Relation descriptors marked rd_myxactonly and reset them.
* This should be called at the end of a transaction (commit/abort) when
* the "local" relations will become visible to others and the multi-user
* buffer pool should be used.
*/
void
RelationPurgeLocalRelation(bool xactCommitted)
{
if (newlyCreatedRelns == NULL)
return;
while (newlyCreatedRelns)
{
List *l = newlyCreatedRelns;
Relation reln = lfirst(l);
Assert(reln != NULL && reln->rd_myxactonly);
reln->rd_myxactonly = false; /* mark it not on list anymore */
newlyCreatedRelns = lnext(newlyCreatedRelns);
pfree(l);
if (!xactCommitted)
{
/*
* remove the file if we abort. This is so that files for
* tables created inside a transaction block get removed.
*/
if (! reln->rd_unlinked)
{
smgrunlink(DEFAULT_SMGR, reln);
reln->rd_unlinked = true;
}
}
if (!IsBootstrapProcessingMode())
RelationClearRelation(reln, false);
}
}
/* --------------------------------
* RelationCacheInitialize
*
* This initializes the relation descriptor cache.
* --------------------------------
*/
#define INITRELCACHESIZE 400
void
RelationCacheInitialize(void)
{
MemoryContext oldcxt;
HASHCTL ctl;
/* ----------------
* switch to cache memory context
* ----------------
*/
if (!CacheMemoryContext)
CreateCacheMemoryContext();
oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
/* ----------------
* create global caches
* ----------------
*/
MemSet(&ctl, 0, (int) sizeof(ctl));
ctl.keysize = sizeof(NameData);
ctl.datasize = sizeof(Relation);
RelationNameCache = hash_create(INITRELCACHESIZE, &ctl, HASH_ELEM);
ctl.keysize = sizeof(Oid);
ctl.hash = tag_hash;
RelationIdCache = hash_create(INITRELCACHESIZE, &ctl,
HASH_ELEM | HASH_FUNCTION);
/* ----------------
* initialize the cache with pre-made relation descriptors
* for some of the more important system relations. These
* relations should always be in the cache.
*
* NB: see also the list in RelationCacheInitializePhase2().
* ----------------
*/
formrdesc(RelationRelationName, Natts_pg_class, Desc_pg_class);
formrdesc(AttributeRelationName, Natts_pg_attribute, Desc_pg_attribute);
formrdesc(ProcedureRelationName, Natts_pg_proc, Desc_pg_proc);
formrdesc(TypeRelationName, Natts_pg_type, Desc_pg_type);
formrdesc(VariableRelationName, Natts_pg_variable, Desc_pg_variable);
formrdesc(LogRelationName, Natts_pg_log, Desc_pg_log);
/*
* If this isn't initdb time, then we want to initialize some index
* relation descriptors, as well. The descriptors are for
* pg_attnumind (to make building relation descriptors fast) and
* possibly others, as they're added.
*/
if (!IsBootstrapProcessingMode())
init_irels();
MemoryContextSwitchTo(oldcxt);
}
/* --------------------------------
* RelationCacheInitializePhase2
*
* This completes initialization of the relcache after catcache
* is functional and we are able to actually load data from pg_class.
* --------------------------------
*/
void
RelationCacheInitializePhase2(void)
{
/*
* Get the real pg_class tuple for each nailed-in-cache relcache entry
* that was made by RelationCacheInitialize(), and replace the phony
* rd_rel entry made by formrdesc(). This is necessary so that we have,
* for example, the correct toast-table info for tables that have such.
*/
if (!IsBootstrapProcessingMode())
{
fixrdesc(RelationRelationName);
fixrdesc(AttributeRelationName);
fixrdesc(ProcedureRelationName);
fixrdesc(TypeRelationName);
/* We don't bother to update the entries for pg_variable or pg_log. */
}
}
static void
AttrDefaultFetch(Relation relation)
{
AttrDefault *attrdef = relation->rd_att->constr->defval;
int ndef = relation->rd_att->constr->num_defval;
Relation adrel;
Relation irel = (Relation) NULL;
ScanKeyData skey;
HeapTupleData tuple;
HeapTuple htup;
Form_pg_attrdef adform;
IndexScanDesc sd = (IndexScanDesc) NULL;
HeapScanDesc adscan = (HeapScanDesc) NULL;
RetrieveIndexResult indexRes;
Datum val;
bool isnull;
int found;
int i;
bool hasindex;
ScanKeyEntryInitialize(&skey,
(bits16) 0x0,
(AttrNumber) 1,
(RegProcedure) F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(relation)));
adrel = heap_openr(AttrDefaultRelationName, AccessShareLock);
hasindex = (adrel->rd_rel->relhasindex && !IsIgnoringSystemIndexes());
if (hasindex)
{
irel = index_openr(AttrDefaultIndex);
sd = index_beginscan(irel, false, 1, &skey);
}
else
adscan = heap_beginscan(adrel, false, SnapshotNow, 1, &skey);
tuple.t_datamcxt = NULL;
tuple.t_data = NULL;
for (found = 0;;)
{
Buffer buffer;
if (hasindex)
{
indexRes = index_getnext(sd, ForwardScanDirection);
if (!indexRes)
break;
tuple.t_self = indexRes->heap_iptr;
heap_fetch(adrel, SnapshotNow, &tuple, &buffer);
pfree(indexRes);
if (tuple.t_data == NULL)
continue;
htup = &tuple;
}
else
{
htup = heap_getnext(adscan, 0);
if (!HeapTupleIsValid(htup))
break;
}
found++;
adform = (Form_pg_attrdef) GETSTRUCT(htup);
for (i = 0; i < ndef; i++)
{
if (adform->adnum != attrdef[i].adnum)
continue;
if (attrdef[i].adbin != NULL)
elog(NOTICE, "AttrDefaultFetch: second record found for attr %s in rel %s",
NameStr(relation->rd_att->attrs[adform->adnum - 1]->attname),
RelationGetRelationName(relation));
val = fastgetattr(htup,
Anum_pg_attrdef_adbin,
adrel->rd_att, &isnull);
if (isnull)
elog(NOTICE, "AttrDefaultFetch: adbin IS NULL for attr %s in rel %s",
NameStr(relation->rd_att->attrs[adform->adnum - 1]->attname),
RelationGetRelationName(relation));
else
attrdef[i].adbin = MemoryContextStrdup(CacheMemoryContext,
DatumGetCString(DirectFunctionCall1(textout,
val)));
break;
}
if (hasindex)
ReleaseBuffer(buffer);
if (i >= ndef)
elog(NOTICE, "AttrDefaultFetch: unexpected record found for attr %d in rel %s",
adform->adnum,
RelationGetRelationName(relation));
}
if (found < ndef)
elog(NOTICE, "AttrDefaultFetch: %d record not found for rel %s",
ndef - found, RelationGetRelationName(relation));
if (hasindex)
{
index_endscan(sd);
index_close(irel);
}
else
heap_endscan(adscan);
heap_close(adrel, AccessShareLock);
}
static void
RelCheckFetch(Relation relation)
{
ConstrCheck *check = relation->rd_att->constr->check;
int ncheck = relation->rd_att->constr->num_check;
Relation rcrel;
Relation irel = (Relation) NULL;
ScanKeyData skey;
HeapTupleData tuple;
HeapTuple htup;
IndexScanDesc sd = (IndexScanDesc) NULL;
HeapScanDesc rcscan = (HeapScanDesc) NULL;
RetrieveIndexResult indexRes;
Name rcname;
Datum val;
bool isnull;
int found;
bool hasindex;
ScanKeyEntryInitialize(&skey,
(bits16) 0x0,
(AttrNumber) 1,
(RegProcedure) F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(relation)));
rcrel = heap_openr(RelCheckRelationName, AccessShareLock);
hasindex = (rcrel->rd_rel->relhasindex && !IsIgnoringSystemIndexes());
if (hasindex)
{
irel = index_openr(RelCheckIndex);
sd = index_beginscan(irel, false, 1, &skey);
}
else
rcscan = heap_beginscan(rcrel, false, SnapshotNow, 1, &skey);
tuple.t_datamcxt = NULL;
tuple.t_data = NULL;
for (found = 0;;)
{
Buffer buffer;
if (hasindex)
{
indexRes = index_getnext(sd, ForwardScanDirection);
if (!indexRes)
break;
tuple.t_self = indexRes->heap_iptr;
heap_fetch(rcrel, SnapshotNow, &tuple, &buffer);
pfree(indexRes);
if (tuple.t_data == NULL)
continue;
htup = &tuple;
}
else
{
htup = heap_getnext(rcscan, 0);
if (!HeapTupleIsValid(htup))
break;
}
if (found == ncheck)
elog(ERROR, "RelCheckFetch: unexpected record found for rel %s",
RelationGetRelationName(relation));
rcname = (Name) fastgetattr(htup,
Anum_pg_relcheck_rcname,
rcrel->rd_att, &isnull);
if (isnull)
elog(ERROR, "RelCheckFetch: rcname IS NULL for rel %s",
RelationGetRelationName(relation));
check[found].ccname = MemoryContextStrdup(CacheMemoryContext,
NameStr(*rcname));
val = fastgetattr(htup,
Anum_pg_relcheck_rcbin,
rcrel->rd_att, &isnull);
if (isnull)
elog(ERROR, "RelCheckFetch: rcbin IS NULL for rel %s",
RelationGetRelationName(relation));
check[found].ccbin = MemoryContextStrdup(CacheMemoryContext,
DatumGetCString(DirectFunctionCall1(textout,
val)));
found++;
if (hasindex)
ReleaseBuffer(buffer);
}
if (found < ncheck)
elog(ERROR, "RelCheckFetch: %d record not found for rel %s",
ncheck - found, RelationGetRelationName(relation));
if (hasindex)
{
index_endscan(sd);
index_close(irel);
}
else
heap_endscan(rcscan);
heap_close(rcrel, AccessShareLock);
}
/*
* RelationGetIndexList -- get a list of OIDs of indexes on this relation
*
* The index list is created only if someone requests it. We scan pg_index
* to find relevant indexes, and add the list to the relcache entry so that
* we won't have to compute it again. Note that shared cache inval of a
* relcache entry will delete the old list and set rd_indexfound to false,
* so that we must recompute the index list on next request. This handles
* creation or deletion of an index.
*
* The returned list is guaranteed to be sorted in order by OID. This is
* needed by the executor, since for index types that we obtain exclusive
* locks on when updating the index, all backends must lock the indexes in
* the same order or we will get deadlocks (see ExecOpenIndices()). Any
* consistent ordering would do, but ordering by OID is easy.
*
* Since shared cache inval causes the relcache's copy of the list to go away,
* we return a copy of the list palloc'd in the caller's context. The caller
* may freeList() the returned list after scanning it. This is necessary
* since the caller will typically be doing syscache lookups on the relevant
* indexes, and syscache lookup could cause SI messages to be processed!
*/
List *
RelationGetIndexList(Relation relation)
{
Relation indrel;
Relation irel = (Relation) NULL;
ScanKeyData skey;
IndexScanDesc sd = (IndexScanDesc) NULL;
HeapScanDesc hscan = (HeapScanDesc) NULL;
bool hasindex;
List *result;
MemoryContext oldcxt;
/* Quick exit if we already computed the list. */
if (relation->rd_indexfound)
return listCopy(relation->rd_indexlist);
/* Prepare to scan pg_index for entries having indrelid = this rel. */
indrel = heap_openr(IndexRelationName, AccessShareLock);
hasindex = (indrel->rd_rel->relhasindex && !IsIgnoringSystemIndexes());
if (hasindex)
{
irel = index_openr(IndexIndrelidIndex);
ScanKeyEntryInitialize(&skey,
(bits16) 0x0,
(AttrNumber) 1,
(RegProcedure) F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(relation)));
sd = index_beginscan(irel, false, 1, &skey);
}
else
{
ScanKeyEntryInitialize(&skey,
(bits16) 0x0,
(AttrNumber) Anum_pg_index_indrelid,
(RegProcedure) F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(relation)));
hscan = heap_beginscan(indrel, false, SnapshotNow, 1, &skey);
}
/*
* We build the list we intend to return (in the caller's context) while
* doing the scan. After successfully completing the scan, we copy that
* list into the relcache entry. This avoids cache-context memory leakage
* if we get some sort of error partway through.
*/
result = NIL;
for (;;)
{
HeapTupleData tuple;
HeapTuple htup;
Buffer buffer;
Form_pg_index index;
if (hasindex)
{
RetrieveIndexResult indexRes;
indexRes = index_getnext(sd, ForwardScanDirection);
if (!indexRes)
break;
tuple.t_self = indexRes->heap_iptr;
tuple.t_datamcxt = NULL;
tuple.t_data = NULL;
heap_fetch(indrel, SnapshotNow, &tuple, &buffer);
pfree(indexRes);
if (tuple.t_data == NULL)
continue;
htup = &tuple;
}
else
{
htup = heap_getnext(hscan, 0);
if (!HeapTupleIsValid(htup))
break;
}
index = (Form_pg_index) GETSTRUCT(htup);
result = insert_ordered_oid(result, index->indexrelid);
if (hasindex)
ReleaseBuffer(buffer);
}
if (hasindex)
{
index_endscan(sd);
index_close(irel);
}
else
heap_endscan(hscan);
heap_close(indrel, AccessShareLock);
/* Now save a copy of the completed list in the relcache entry. */
oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
relation->rd_indexlist = listCopy(result);
relation->rd_indexfound = true;
MemoryContextSwitchTo(oldcxt);
return result;
}
/*
* insert_ordered_oid
* Insert a new Oid into a sorted list of Oids, preserving ordering
*
* Building the ordered list this way is O(N^2), but with a pretty small
* constant, so for the number of entries we expect it will probably be
* faster than trying to apply qsort(). Most tables don't have very many
* indexes...
*/
static List *
insert_ordered_oid(List *list, Oid datum)
{
List *l;
/* Does the datum belong at the front? */
if (list == NIL || datum < (Oid) lfirsti(list))
return lconsi(datum, list);
/* No, so find the entry it belongs after */
l = list;
for (;;)
{
List *n = lnext(l);
if (n == NIL || datum < (Oid) lfirsti(n))
break; /* it belongs before n */
l = n;
}
/* Insert datum into list after item l */
lnext(l) = lconsi(datum, lnext(l));
return list;
}
/*
* init_irels(), write_irels() -- handle special-case initialization of
* index relation descriptors.
*
* In late 1992, we started regularly having databases with more than
* a thousand classes in them. With this number of classes, it became
* critical to do indexed lookups on the system catalogs.
*
* Bootstrapping these lookups is very hard. We want to be able to
* use an index on pg_attribute, for example, but in order to do so,
* we must have read pg_attribute for the attributes in the index,
* which implies that we need to use the index.
*
* In order to get around the problem, we do the following:
*
* + When the database system is initialized (at initdb time), we
* don't use indices on pg_attribute. We do sequential scans.
*
* + When the backend is started up in normal mode, we load an image
* of the appropriate relation descriptors, in internal format,
* from an initialization file in the data/base/... directory.
*
* + If the initialization file isn't there, then we create the
* relation descriptors using sequential scans and write 'em to
* the initialization file for use by subsequent backends.
*
* We could dispense with the initialization file and just build the
* critical reldescs the hard way on every backend startup, but that
* slows down backend startup noticeably if pg_class is large.
*
* As of v6.5, vacuum.c deletes the initialization file at completion
* of a VACUUM, so that it will be rebuilt at the next backend startup.
* This ensures that vacuum-collected stats for the system indexes
* will eventually get used by the optimizer --- otherwise the relcache
* entries for these indexes will show zero sizes forever, since the
* relcache entries are pinned in memory and will never be reloaded
* from pg_class.
*/
/* pg_attnumind, pg_classnameind, pg_classoidind */
#define Num_indices_bootstrap 3
static void
init_irels(void)
{
Size len;
int nread;
File fd;
Relation irel[Num_indices_bootstrap];
Relation ird;
Form_pg_am am;
Form_pg_class relform;
IndexStrategy strat;
RegProcedure *support;
int i;
int relno;
if ((fd = FileNameOpenFile(RELCACHE_INIT_FILENAME, O_RDONLY | PG_BINARY, 0600)) < 0)
{
write_irels();
return;
}
FileSeek(fd, 0L, SEEK_SET);
for (relno = 0; relno < Num_indices_bootstrap; relno++)
{
/* first read the relation descriptor length */
if ((nread = FileRead(fd, (char *) &len, sizeof(len))) != sizeof(len))
{
write_irels();
return;
}
ird = irel[relno] = (Relation) palloc(len);
MemSet(ird, 0, len);
/* then, read the Relation structure */
if ((nread = FileRead(fd, (char *) ird, len)) != len)
{
write_irels();
return;
}
/* the file descriptor is not yet opened */
ird->rd_fd = -1;
/* next, read the access method tuple form */
if ((nread = FileRead(fd, (char *) &len, sizeof(len))) != sizeof(len))
{
write_irels();
return;
}
am = (Form_pg_am) palloc(len);
if ((nread = FileRead(fd, (char *) am, len)) != len)
{
write_irels();
return;
}
ird->rd_am = am;
/* next read the relation tuple form */
if ((nread = FileRead(fd, (char *) &len, sizeof(len))) != sizeof(len))
{
write_irels();
return;
}
relform = (Form_pg_class) palloc(len);
if ((nread = FileRead(fd, (char *) relform, len)) != len)
{
write_irels();
return;
}
ird->rd_rel = relform;
/* initialize attribute tuple forms */
ird->rd_att = CreateTemplateTupleDesc(relform->relnatts);
/* next read all the attribute tuple form data entries */
len = ATTRIBUTE_TUPLE_SIZE;
for (i = 0; i < relform->relnatts; i++)
{
if ((nread = FileRead(fd, (char *) &len, sizeof(len))) != sizeof(len))
{
write_irels();
return;
}
ird->rd_att->attrs[i] = (Form_pg_attribute) palloc(len);
if ((nread = FileRead(fd, (char *) ird->rd_att->attrs[i], len)) != len)
{
write_irels();
return;
}
}
/* next, read the index strategy map */
if ((nread = FileRead(fd, (char *) &len, sizeof(len))) != sizeof(len))
{
write_irels();
return;
}
strat = (IndexStrategy) palloc(len);
if ((nread = FileRead(fd, (char *) strat, len)) != len)
{
write_irels();
return;
}
/* oh, for god's sake... */
#define SMD(i) strat->strategyMapData[i].entry[0]
/* have to reinit the function pointers in the strategy maps */
for (i = 0; i < am->amstrategies * relform->relnatts; i++)
{
fmgr_info(SMD(i).sk_procedure,
&(SMD(i).sk_func));
SMD(i).sk_nargs = SMD(i).sk_func.fn_nargs;
}
/*
* use a real field called rd_istrat instead of the bogosity of
* hanging invisible fields off the end of a structure - jolly
*/
ird->rd_istrat = strat;
/* finally, read the vector of support procedures */
if ((nread = FileRead(fd, (char *) &len, sizeof(len))) != sizeof(len))
{
write_irels();
return;
}
support = (RegProcedure *) palloc(len);
if ((nread = FileRead(fd, (char *) support, len)) != len)
{
write_irels();
return;
}
ird->rd_support = support;
RelationInitLockInfo(ird);
RelationCacheInsert(ird);
}
criticalRelcachesBuilt = true;
}
static void
write_irels(void)
{
Size len;
int nwritten;
File fd;
Relation irel[Num_indices_bootstrap];
Relation ird;
Form_pg_am am;
Form_pg_class relform;
IndexStrategy strat;
RegProcedure *support;
int i;
int relno;
RelationBuildDescInfo bi;
char tempfilename[MAXPGPATH];
char finalfilename[MAXPGPATH];
/*
* We must write a temporary file and rename it into place. Otherwise,
* another backend starting at about the same time might crash trying
* to read the partially-complete file.
*/
snprintf(tempfilename, sizeof(tempfilename), "%s%c%s.%d",
DatabasePath, SEP_CHAR, RELCACHE_INIT_FILENAME, MyProcPid);
snprintf(finalfilename, sizeof(finalfilename), "%s%c%s",
DatabasePath, SEP_CHAR, RELCACHE_INIT_FILENAME);
fd = PathNameOpenFile(tempfilename, O_WRONLY | O_CREAT | O_TRUNC | PG_BINARY, 0600);
if (fd < 0)
{
/*
* We used to consider this a fatal error, but we might as well
* continue with backend startup ...
*/
elog(NOTICE, "Cannot create init file %s: %m\n\tContinuing anyway, but there's something wrong.", tempfilename);
return;
}
FileSeek(fd, 0L, SEEK_SET);
/*
* Build relation descriptors for the critical system indexes without
* resort to the descriptor cache. In order to do this, we set
* ProcessingMode to Bootstrap. The effect of this is to disable
* indexed relation searches -- a necessary step, since we're trying
* to instantiate the index relation descriptors here. Once we have
* the descriptors, nail them into cache so we never lose them.
*/
/*
* Removed the following ProcessingMode change -- inoue At this point
* 1) Catalog Cache isn't initialized 2) Relation Cache for the
* following critical indexes aren't built oldmode =
* GetProcessingMode(); SetProcessingMode(BootstrapProcessing);
*/
bi.infotype = INFO_RELNAME;
bi.i.info_name = AttributeRelidNumIndex;
irel[0] = RelationBuildDesc(bi, NULL);
irel[0]->rd_isnailed = true;
bi.i.info_name = ClassNameIndex;
irel[1] = RelationBuildDesc(bi, NULL);
irel[1]->rd_isnailed = true;
bi.i.info_name = ClassOidIndex;
irel[2] = RelationBuildDesc(bi, NULL);
irel[2]->rd_isnailed = true;
criticalRelcachesBuilt = true;
/*
* Removed the following ProcessingMode -- inoue
* SetProcessingMode(oldmode);
*/
/*
* Write out the index reldescs to the special cache file.
*/
for (relno = 0; relno < Num_indices_bootstrap; relno++)
{
ird = irel[relno];
/* save the volatile fields in the relation descriptor */
am = ird->rd_am;
ird->rd_am = (Form_pg_am) NULL;
relform = ird->rd_rel;
ird->rd_rel = (Form_pg_class) NULL;
strat = ird->rd_istrat;
support = ird->rd_support;
/*
* first write the relation descriptor , excluding strategy and
* support
*/
len = sizeof(RelationData);
/* first, write the relation descriptor length */
if ((nwritten = FileWrite(fd, (char *) &len, sizeof(len)))
!= sizeof(len))
elog(FATAL, "cannot write init file -- descriptor length");
/* next, write out the Relation structure */
if ((nwritten = FileWrite(fd, (char *) ird, len)) != len)
elog(FATAL, "cannot write init file -- reldesc");
/* next, write the access method tuple form */
len = sizeof(FormData_pg_am);
if ((nwritten = FileWrite(fd, (char *) &len, sizeof(len)))
!= sizeof(len))
elog(FATAL, "cannot write init file -- am tuple form length");
if ((nwritten = FileWrite(fd, (char *) am, len)) != len)
elog(FATAL, "cannot write init file -- am tuple form");
/* next write the relation tuple form */
len = sizeof(FormData_pg_class);
if ((nwritten = FileWrite(fd, (char *) &len, sizeof(len)))
!= sizeof(len))
elog(FATAL, "cannot write init file -- relation tuple form length");
if ((nwritten = FileWrite(fd, (char *) relform, len)) != len)
elog(FATAL, "cannot write init file -- relation tuple form");
/* next, do all the attribute tuple form data entries */
len = ATTRIBUTE_TUPLE_SIZE;
for (i = 0; i < relform->relnatts; i++)
{
if ((nwritten = FileWrite(fd, (char *) &len, sizeof(len)))
!= sizeof(len))
elog(FATAL, "cannot write init file -- length of attdesc %d", i);
if ((nwritten = FileWrite(fd, (char *) ird->rd_att->attrs[i], len))
!= len)
elog(FATAL, "cannot write init file -- attdesc %d", i);
}
/* next, write the index strategy map */
len = AttributeNumberGetIndexStrategySize(relform->relnatts,
am->amstrategies);
if ((nwritten = FileWrite(fd, (char *) &len, sizeof(len)))
!= sizeof(len))
elog(FATAL, "cannot write init file -- strategy map length");
if ((nwritten = FileWrite(fd, (char *) strat, len)) != len)
elog(FATAL, "cannot write init file -- strategy map");
/* finally, write the vector of support procedures */
len = relform->relnatts * (am->amsupport * sizeof(RegProcedure));
if ((nwritten = FileWrite(fd, (char *) &len, sizeof(len)))
!= sizeof(len))
elog(FATAL, "cannot write init file -- support vector length");
if ((nwritten = FileWrite(fd, (char *) support, len)) != len)
elog(FATAL, "cannot write init file -- support vector");
/* restore volatile fields */
ird->rd_am = am;
ird->rd_rel = relform;
}
FileClose(fd);
/*
* And rename the temp file to its final name, deleting any
* previously-existing init file.
*/
if (rename(tempfilename, finalfilename) < 0)
{
elog(NOTICE, "Cannot rename init file %s to %s: %m\n\tContinuing anyway, but there's something wrong.", tempfilename, finalfilename);
}
}