2149 lines
54 KiB
C
2149 lines
54 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* relcache.c--
|
|
* POSTGRES relation descriptor cache code
|
|
*
|
|
* Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.54 1999/01/22 16:49:25 momjian Exp $
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
/*
|
|
* INTERFACE ROUTINES
|
|
* RelationInitialize - initialize 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
|
|
* RelationFlushRelation - flush relation information
|
|
*
|
|
* NOTES
|
|
* This file is in the process of being cleaned up
|
|
* before I add system attribute indexing. -cim 1/13/91
|
|
*
|
|
* The following code contains many undocumented hacks. Please be
|
|
* careful....
|
|
*
|
|
*/
|
|
#include <sys/types.h>
|
|
#include <stdio.h> /* for sprintf() */
|
|
#include <errno.h>
|
|
#include <sys/file.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
|
|
#include "postgres.h"
|
|
|
|
#include "access/genam.h"
|
|
#include "access/heapam.h"
|
|
#include "access/htup.h"
|
|
#include "access/istrat.h"
|
|
#include "access/itup.h"
|
|
#include "access/skey.h"
|
|
#include "access/tupdesc.h"
|
|
#include "access/tupmacs.h"
|
|
#include "access/xact.h"
|
|
#include "catalog/catalog.h"
|
|
#include "catalog/catname.h"
|
|
#include "catalog/index.h"
|
|
#include "catalog/indexing.h"
|
|
#include "catalog/pg_aggregate.h"
|
|
#include "catalog/pg_attrdef.h"
|
|
#include "catalog/pg_attribute.h"
|
|
#include "catalog/pg_index.h"
|
|
#include "catalog/pg_proc.h"
|
|
#include "catalog/pg_class.h"
|
|
#include "catalog/pg_log.h"
|
|
#include "catalog/pg_relcheck.h"
|
|
#include "catalog/pg_rewrite.h"
|
|
#include "catalog/pg_type.h"
|
|
#include "catalog/pg_variable.h"
|
|
#include "fmgr.h"
|
|
#include "lib/hasht.h"
|
|
#include "miscadmin.h"
|
|
#include "storage/buf.h"
|
|
#include "storage/bufmgr.h"
|
|
#include "storage/fd.h" /* for SEEK_ */
|
|
#include "storage/lmgr.h"
|
|
#include "storage/smgr.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/catcache.h"
|
|
#include "utils/hsearch.h"
|
|
#include "utils/mcxt.h"
|
|
#include "utils/memutils.h"
|
|
#include "utils/rel.h"
|
|
#include "utils/relcache.h"
|
|
#include "utils/relcache.h"
|
|
#include "utils/syscache.h"
|
|
|
|
|
|
static void RelationFlushRelation(Relation *relationPtr,
|
|
bool onlyFlushReferenceCountZero);
|
|
static Relation RelationNameCacheGetRelation(char *relationName);
|
|
static void init_irels(void);
|
|
static void write_irels(void);
|
|
|
|
/* ----------------
|
|
* defines
|
|
* ----------------
|
|
*/
|
|
#define private static
|
|
#define INIT_FILENAME "pg_internal.init"
|
|
|
|
/* ----------------
|
|
* externs
|
|
* ----------------
|
|
*/
|
|
extern bool AMI_OVERRIDE; /* XXX style */
|
|
extern GlobalMemory CacheCxt; /* from utils/cache/catcache.c */
|
|
|
|
/* ----------------
|
|
* hardcoded tuple descriptors. see lib/backend/catalog/pg_attribute.h
|
|
* ----------------
|
|
*/
|
|
FormData_pg_attribute Desc_pg_class[Natts_pg_class] = {Schema_pg_class};
|
|
FormData_pg_attribute Desc_pg_attribute[Natts_pg_attribute] = {Schema_pg_attribute};
|
|
FormData_pg_attribute Desc_pg_proc[Natts_pg_proc] = {Schema_pg_proc};
|
|
FormData_pg_attribute Desc_pg_type[Natts_pg_type] = {Schema_pg_type};
|
|
FormData_pg_attribute Desc_pg_variable[Natts_pg_variable] = {Schema_pg_variable};
|
|
FormData_pg_attribute Desc_pg_log[Natts_pg_log] = {Schema_pg_log};
|
|
|
|
/* ----------------
|
|
* global variables
|
|
*
|
|
* Relations are cached two ways, by name and by id,
|
|
* thus there are two hash tables for referencing them.
|
|
* ----------------
|
|
*/
|
|
HTAB *RelationNameCache;
|
|
HTAB *RelationIdCache;
|
|
|
|
/* ----------------
|
|
* 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 = (RELATION->rd_rel->relname).data; \
|
|
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 = (RELATION->rd_rel->relname).data; \
|
|
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 formrdesc(char *relationName, u_int natts,
|
|
FormData_pg_attribute *att);
|
|
|
|
#if 0 /* See comments at line 1304 */
|
|
static void RelationFlushIndexes(Relation *r, Oid accessMethodId);
|
|
|
|
#endif
|
|
|
|
static HeapTuple ScanPgRelation(RelationBuildDescInfo buildinfo);
|
|
static HeapTuple scan_pg_rel_seq(RelationBuildDescInfo buildinfo);
|
|
static HeapTuple scan_pg_rel_ind(RelationBuildDescInfo buildinfo);
|
|
static Relation AllocateRelationDesc(u_int natts, Form_pg_class relp);
|
|
static void RelationBuildTupleDesc(RelationBuildDescInfo buildinfo,
|
|
Relation relation, u_int natts);
|
|
static void build_tupdesc_seq(RelationBuildDescInfo buildinfo,
|
|
Relation relation, u_int natts);
|
|
static void build_tupdesc_ind(RelationBuildDescInfo buildinfo,
|
|
Relation relation, u_int natts);
|
|
static Relation RelationBuildDesc(RelationBuildDescInfo buildinfo);
|
|
static void IndexedAccessMethodInitialize(Relation relation);
|
|
static void AttrDefaultFetch(Relation relation);
|
|
static void RelCheckFetch(Relation relation);
|
|
|
|
extern void RelationBuildTriggers(Relation relation);
|
|
extern void FreeTriggerDesc(Relation relation);
|
|
|
|
/*
|
|
* newlyCreatedRelns -
|
|
* relations created during this transaction. We need to keep track of
|
|
* these.
|
|
*/
|
|
static List *newlyCreatedRelns = NULL;
|
|
|
|
/* ----------------------------------------------------------------
|
|
* RelationIdGetRelation() and RelationNameGetRelation()
|
|
* support functions
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
|
|
|
|
#if NOT_USED /* XXX This doesn't seem to be used
|
|
* anywhere */
|
|
/* --------------------------------
|
|
* BuildDescInfoError returns a string appropriate to
|
|
* the buildinfo passed to it
|
|
* --------------------------------
|
|
*/
|
|
static char *
|
|
BuildDescInfoError(RelationBuildDescInfo buildinfo)
|
|
{
|
|
static char errBuf[64];
|
|
|
|
MemSet(errBuf, 0, (int) sizeof(errBuf));
|
|
switch (buildinfo.infotype)
|
|
{
|
|
case INFO_RELID:
|
|
sprintf(errBuf, "(relation id %d)", buildinfo.i.info_id);
|
|
break;
|
|
case INFO_RELNAME:
|
|
sprintf(errBuf, "(relation name %s)", buildinfo.i.info_name);
|
|
break;
|
|
}
|
|
|
|
return errBuf;
|
|
}
|
|
|
|
#endif
|
|
|
|
/* --------------------------------
|
|
* 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.
|
|
* --------------------------------
|
|
*/
|
|
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 (IsBootstrapProcessingMode())
|
|
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);
|
|
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);
|
|
|
|
return return_tuple;
|
|
}
|
|
|
|
static HeapTuple
|
|
scan_pg_rel_ind(RelationBuildDescInfo buildinfo)
|
|
{
|
|
Relation pg_class_desc;
|
|
HeapTuple return_tuple;
|
|
|
|
pg_class_desc = heap_openr(RelationRelationName);
|
|
if (!IsInitProcessingMode())
|
|
LockRelation(pg_class_desc, AccessShareLock);
|
|
|
|
switch (buildinfo.infotype)
|
|
{
|
|
case INFO_RELID:
|
|
return_tuple = ClassOidIndexScan(pg_class_desc, buildinfo.i.info_id);
|
|
break;
|
|
|
|
case INFO_RELNAME:
|
|
return_tuple = ClassNameIndexScan(pg_class_desc,
|
|
buildinfo.i.info_name);
|
|
break;
|
|
|
|
default:
|
|
elog(ERROR, "ScanPgRelation: bad buildinfo");
|
|
|
|
/*
|
|
* XXX I hope this is right. It seems better than returning
|
|
* an uninitialized value
|
|
*/
|
|
return_tuple = NULL;
|
|
}
|
|
|
|
/* all done */
|
|
if (!IsInitProcessingMode())
|
|
UnlockRelation(pg_class_desc, AccessShareLock);
|
|
heap_close(pg_class_desc);
|
|
|
|
return return_tuple;
|
|
}
|
|
|
|
/* ----------------
|
|
* AllocateRelationDesc
|
|
*
|
|
* This is used to allocate memory for a new relation descriptor
|
|
* and initialize the rd_rel field.
|
|
* ----------------
|
|
*/
|
|
static Relation
|
|
AllocateRelationDesc(u_int natts, Form_pg_class relp)
|
|
{
|
|
Relation relation;
|
|
Size len;
|
|
Form_pg_class relationForm;
|
|
|
|
/* ----------------
|
|
* allocate space for the relation tuple form
|
|
* ----------------
|
|
*/
|
|
relationForm = (Form_pg_class)
|
|
palloc((Size) (sizeof(FormData_pg_class)));
|
|
|
|
memmove((char *) relationForm, (char *) relp, CLASS_TUPLE_SIZE);
|
|
|
|
/* ----------------
|
|
* allocate space for new relation descriptor
|
|
*/
|
|
len = sizeof(RelationData) + 10; /* + 10 is voodoo XXX mao */
|
|
|
|
relation = (Relation) palloc(len);
|
|
|
|
/* ----------------
|
|
* clear new reldesc
|
|
* ----------------
|
|
*/
|
|
MemSet((char *) relation, 0, len);
|
|
|
|
/* initialize attribute tuple form */
|
|
relation->rd_att = CreateTemplateTupleDesc(natts);
|
|
|
|
/* and initialize relation tuple form */
|
|
relation->rd_rel = relationForm;
|
|
|
|
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,
|
|
u_int natts)
|
|
{
|
|
|
|
/*
|
|
* 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 (IsBootstrapProcessingMode())
|
|
build_tupdesc_seq(buildinfo, relation, natts);
|
|
else
|
|
build_tupdesc_ind(buildinfo, relation, natts);
|
|
}
|
|
|
|
static void
|
|
build_tupdesc_seq(RelationBuildDescInfo buildinfo,
|
|
Relation relation,
|
|
u_int natts)
|
|
{
|
|
HeapTuple pg_attribute_tuple;
|
|
Relation pg_attribute_desc;
|
|
HeapScanDesc pg_attribute_scan;
|
|
Form_pg_attribute attp;
|
|
ScanKeyData key;
|
|
int need;
|
|
|
|
/* ----------------
|
|
* 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);
|
|
pg_attribute_scan = heap_beginscan(pg_attribute_desc, 0, SnapshotNow, 1, &key);
|
|
|
|
/* ----------------
|
|
* add attribute data to relation->rd_att
|
|
* ----------------
|
|
*/
|
|
need = natts;
|
|
|
|
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) palloc(ATTRIBUTE_TUPLE_SIZE);
|
|
|
|
memmove((char *) (relation->rd_att->attrs[attp->attnum - 1]),
|
|
(char *) attp,
|
|
ATTRIBUTE_TUPLE_SIZE);
|
|
need--;
|
|
}
|
|
pg_attribute_tuple = heap_getnext(pg_attribute_scan, 0);
|
|
}
|
|
|
|
if (need > 0)
|
|
elog(ERROR, "catalog is missing %d attribute%s for relid %d",
|
|
need, (need == 1 ? "" : "s"), RelationGetRelid(relation));
|
|
|
|
/* ----------------
|
|
* end the scan and close the attribute relation
|
|
* ----------------
|
|
*/
|
|
heap_endscan(pg_attribute_scan);
|
|
heap_close(pg_attribute_desc);
|
|
}
|
|
|
|
static void
|
|
build_tupdesc_ind(RelationBuildDescInfo buildinfo,
|
|
Relation relation,
|
|
u_int natts)
|
|
{
|
|
Relation attrel;
|
|
HeapTuple atttup;
|
|
Form_pg_attribute attp;
|
|
TupleConstr *constr = (TupleConstr *) palloc(sizeof(TupleConstr));
|
|
AttrDefault *attrdef = NULL;
|
|
int ndef = 0;
|
|
int i;
|
|
|
|
constr->has_not_null = false;
|
|
|
|
attrel = heap_openr(AttributeRelationName);
|
|
|
|
for (i = 1; i <= relation->rd_rel->relnatts; i++)
|
|
{
|
|
atttup = (HeapTuple) AttributeNumIndexScan(attrel,
|
|
RelationGetRelid(relation), i);
|
|
|
|
if (!HeapTupleIsValid(atttup))
|
|
elog(ERROR, "cannot find attribute %d of relation %s", i,
|
|
relation->rd_rel->relname.data);
|
|
attp = (Form_pg_attribute) GETSTRUCT(atttup);
|
|
|
|
relation->rd_att->attrs[i - 1] =
|
|
(Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
|
|
|
|
memmove((char *) (relation->rd_att->attrs[i - 1]),
|
|
(char *) attp,
|
|
ATTRIBUTE_TUPLE_SIZE);
|
|
|
|
/* Update if this attribute have a constraint */
|
|
if (attp->attnotnull)
|
|
constr->has_not_null = true;
|
|
|
|
if (attp->atthasdef)
|
|
{
|
|
if (attrdef == NULL)
|
|
attrdef = (AttrDefault *) palloc(relation->rd_rel->relnatts *
|
|
sizeof(AttrDefault));
|
|
attrdef[ndef].adnum = i;
|
|
attrdef[ndef].adbin = NULL;
|
|
attrdef[ndef].adsrc = NULL;
|
|
ndef++;
|
|
}
|
|
}
|
|
|
|
heap_close(attrel);
|
|
|
|
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 *) palloc(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;
|
|
}
|
|
|
|
}
|
|
|
|
/* --------------------------------
|
|
* RelationBuildRuleLock
|
|
*
|
|
* Form the relation's rewrite rules from information in
|
|
* the pg_rewrite system catalog.
|
|
* --------------------------------
|
|
*/
|
|
static void
|
|
RelationBuildRuleLock(Relation relation)
|
|
{
|
|
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;
|
|
|
|
/* ----------------
|
|
* form an array to hold the rewrite rules (the array is extended if
|
|
* necessary)
|
|
* ----------------
|
|
*/
|
|
maxlocks = 4;
|
|
rules = (RewriteRule **) palloc(sizeof(RewriteRule *) * maxlocks);
|
|
numlocks = 0;
|
|
|
|
/* ----------------
|
|
* form a scan key
|
|
* ----------------
|
|
*/
|
|
ScanKeyEntryInitialize(&key, 0,
|
|
Anum_pg_rewrite_ev_class,
|
|
F_OIDEQ,
|
|
ObjectIdGetDatum(RelationGetRelid(relation)));
|
|
|
|
/* ----------------
|
|
* open pg_attribute and begin a scan
|
|
* ----------------
|
|
*/
|
|
pg_rewrite_desc = heap_openr(RewriteRelationName);
|
|
pg_rewrite_scan = heap_beginscan(pg_rewrite_desc, 0, SnapshotNow, 1, &key);
|
|
pg_rewrite_tupdesc = RelationGetDescr(pg_rewrite_desc);
|
|
|
|
/* ----------------
|
|
* add attribute data to relation->rd_att
|
|
* ----------------
|
|
*/
|
|
while (HeapTupleIsValid(pg_rewrite_tuple = heap_getnext(pg_rewrite_scan, 0)))
|
|
{
|
|
bool isnull;
|
|
Datum ruleaction;
|
|
Datum rule_evqual_string;
|
|
RewriteRule *rule;
|
|
|
|
rule = (RewriteRule *) palloc(sizeof(RewriteRule));
|
|
|
|
rule->ruleId = pg_rewrite_tuple->t_data->t_oid;
|
|
|
|
rule->event =
|
|
(int) heap_getattr(pg_rewrite_tuple,
|
|
Anum_pg_rewrite_ev_type, pg_rewrite_tupdesc,
|
|
&isnull) - 48;
|
|
rule->attrno =
|
|
(int) heap_getattr(pg_rewrite_tuple,
|
|
Anum_pg_rewrite_ev_attr, pg_rewrite_tupdesc,
|
|
&isnull);
|
|
rule->isInstead =
|
|
!!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);
|
|
rule_evqual_string =
|
|
heap_getattr(pg_rewrite_tuple,
|
|
Anum_pg_rewrite_ev_qual, pg_rewrite_tupdesc,
|
|
&isnull);
|
|
|
|
ruleaction = PointerGetDatum(textout((struct varlena *) DatumGetPointer(ruleaction)));
|
|
rule_evqual_string = PointerGetDatum(textout((struct varlena *) DatumGetPointer(rule_evqual_string)));
|
|
|
|
rule->actions = (List *) stringToNode(DatumGetPointer(ruleaction));
|
|
rule->qual = (Node *) stringToNode(DatumGetPointer(rule_evqual_string));
|
|
|
|
rules[numlocks++] = rule;
|
|
if (numlocks == maxlocks)
|
|
{
|
|
maxlocks *= 2;
|
|
rules =
|
|
(RewriteRule **) repalloc(rules, sizeof(RewriteRule *) * maxlocks);
|
|
}
|
|
}
|
|
|
|
/* ----------------
|
|
* end the scan and close the attribute relation
|
|
* ----------------
|
|
*/
|
|
heap_endscan(pg_rewrite_scan);
|
|
heap_close(pg_rewrite_desc);
|
|
|
|
/* ----------------
|
|
* form a RuleLock and insert into relation
|
|
* ----------------
|
|
*/
|
|
rulelock = (RuleLock *) palloc(sizeof(RuleLock));
|
|
rulelock->numLocks = numlocks;
|
|
rulelock->rules = rules;
|
|
|
|
relation->rd_rules = rulelock;
|
|
return;
|
|
}
|
|
|
|
|
|
/* --------------------------------
|
|
* RelationBuildDesc
|
|
*
|
|
* 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; relations's object id
|
|
* Pointer lockInfo; ptr. to misc. info.
|
|
* TupleDesc rd_att; tuple desciptor
|
|
*
|
|
* 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)
|
|
{
|
|
File fd;
|
|
Relation relation;
|
|
u_int natts;
|
|
Oid relid;
|
|
Oid relam;
|
|
Form_pg_class relp;
|
|
|
|
MemoryContext oldcxt;
|
|
|
|
HeapTuple pg_class_tuple;
|
|
|
|
oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt);
|
|
|
|
/* ----------------
|
|
* 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))
|
|
{
|
|
|
|
MemoryContextSwitchTo(oldcxt);
|
|
|
|
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);
|
|
natts = relp->relnatts;
|
|
|
|
/* ----------------
|
|
* allocate storage for the relation descriptor,
|
|
* initialize relation->rd_rel and get the access method id.
|
|
* ----------------
|
|
*/
|
|
relation = AllocateRelationDesc(natts, relp);
|
|
relam = relation->rd_rel->relam;
|
|
|
|
/* ----------------
|
|
* 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)
|
|
* ----------------
|
|
*/
|
|
if (OidIsValid(relam))
|
|
{
|
|
relation->rd_am = (Form_pg_am)
|
|
AccessMethodObjectIdGetForm(relam);
|
|
}
|
|
|
|
/* ----------------
|
|
* initialize the tuple descriptor (relation->rd_att).
|
|
* remember, rd_att is an array of attribute pointers that lives
|
|
* off the end of the relation descriptor structure so space was
|
|
* already allocated for it by AllocateRelationDesc.
|
|
* ----------------
|
|
*/
|
|
RelationBuildTupleDesc(buildinfo, relation, natts);
|
|
|
|
/* ----------------
|
|
* initialize rules that affect this relation
|
|
* ----------------
|
|
*/
|
|
if (relp->relhasrules)
|
|
RelationBuildRuleLock(relation);
|
|
else
|
|
relation->rd_rules = NULL;
|
|
|
|
/* Triggers */
|
|
if (relp->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.
|
|
* ----------------
|
|
*/
|
|
fd = smgropen(DEFAULT_SMGR, relation);
|
|
|
|
Assert(fd >= -1);
|
|
if (fd == -1)
|
|
elog(NOTICE, "RelationIdBuildRelation: smgropen(%s): %m",
|
|
&relp->relname);
|
|
|
|
relation->rd_fd = fd;
|
|
|
|
/* ----------------
|
|
* insert newly created relation into proper relcaches,
|
|
* restore memory context and return the new reldesc.
|
|
* ----------------
|
|
*/
|
|
|
|
RelationCacheInsert(relation);
|
|
|
|
/* -------------------
|
|
* free the memory allocated for pg_class_tuple
|
|
* and for lock data pointed to by pg_class_tuple
|
|
* -------------------
|
|
*/
|
|
pfree(pg_class_tuple);
|
|
|
|
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) palloc(stratSize);
|
|
relamsupport = relation->rd_am->amsupport;
|
|
|
|
if (relamsupport > 0)
|
|
{
|
|
supportSize = natts * (relamsupport * sizeof(RegProcedure));
|
|
support = (RegProcedure *) palloc(supportSize);
|
|
}
|
|
else
|
|
support = (RegProcedure *) NULL;
|
|
|
|
IndexSupportInitialize(strategy, support,
|
|
relation->rd_att->attrs[0]->attrelid,
|
|
relation->rd_rel->relam,
|
|
relamstrategies, relamsupport, natts);
|
|
|
|
RelationSetIndexSupport(relation, strategy, support);
|
|
}
|
|
|
|
/* --------------------------------
|
|
* formrdesc
|
|
*
|
|
* This is a special version of RelationBuildDesc()
|
|
* used by RelationInitialize() in initializing the
|
|
* relcache. The system relation descriptors built
|
|
* here are all nailed in the descriptor caches, for
|
|
* bootstrapping.
|
|
* --------------------------------
|
|
*/
|
|
static void
|
|
formrdesc(char *relationName,
|
|
u_int natts,
|
|
FormData_pg_attribute *att)
|
|
{
|
|
Relation relation;
|
|
Size len;
|
|
int i;
|
|
|
|
/* ----------------
|
|
* allocate new relation desc
|
|
* ----------------
|
|
*/
|
|
len = sizeof(RelationData);
|
|
relation = (Relation) palloc(len);
|
|
MemSet((char *) relation, 0, len);
|
|
|
|
/* ----------------
|
|
* don't open the unix file yet..
|
|
* ----------------
|
|
*/
|
|
relation->rd_fd = -1;
|
|
|
|
/* ----------------
|
|
* initialize reference count
|
|
* ----------------
|
|
*/
|
|
RelationSetReferenceCount(relation, 1);
|
|
|
|
/* ----------------
|
|
* initialize relation tuple form
|
|
* ----------------
|
|
*/
|
|
relation->rd_rel = (Form_pg_class)
|
|
palloc((Size) (sizeof(*relation->rd_rel)));
|
|
MemSet(relation->rd_rel, 0, sizeof(FormData_pg_class));
|
|
namestrcpy(&relation->rd_rel->relname, relationName);
|
|
|
|
/* ----------------
|
|
initialize attribute tuple form
|
|
*/
|
|
relation->rd_att = CreateTemplateTupleDesc(natts);
|
|
|
|
/*
|
|
* 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->relowner = 6; /* XXX use sym const */
|
|
relation->rd_rel->relisshared =
|
|
IsSharedSystemRelationName(relationName);
|
|
}
|
|
else
|
|
{
|
|
relation->rd_rel->relowner = 0;
|
|
relation->rd_rel->relisshared = false;
|
|
}
|
|
|
|
relation->rd_rel->relpages = 1; /* XXX */
|
|
relation->rd_rel->reltuples = 1; /* XXX */
|
|
relation->rd_rel->relkind = RELKIND_RELATION;
|
|
relation->rd_rel->relnatts = (uint16) natts;
|
|
relation->rd_isnailed = true;
|
|
|
|
/* ----------------
|
|
* initialize tuple desc info
|
|
* ----------------
|
|
*/
|
|
for (i = 0; i < natts; i++)
|
|
{
|
|
relation->rd_att->attrs[i] =
|
|
(Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
|
|
|
|
MemSet((char *) relation->rd_att->attrs[i], 0,
|
|
ATTRIBUTE_TUPLE_SIZE);
|
|
memmove((char *) relation->rd_att->attrs[i],
|
|
(char *) &att[i],
|
|
ATTRIBUTE_TUPLE_SIZE);
|
|
}
|
|
|
|
/* ----------------
|
|
* initialize relation id
|
|
* ----------------
|
|
*/
|
|
RelationGetRelid(relation) = relation->rd_att->attrs[0]->attrelid;
|
|
|
|
/* ----------------
|
|
* add new reldesc to relcache
|
|
* ----------------
|
|
*/
|
|
RelationCacheInsert(relation);
|
|
|
|
RelationInitLockInfo(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.
|
|
*/
|
|
relation->rd_rel->relhasindex =
|
|
CatalogHasIndex(relationName, RelationGetRelid(relation));
|
|
}
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
|
* Relation Descriptor Lookup Interface
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
|
|
/* --------------------------------
|
|
* RelationIdCacheGetRelation
|
|
*
|
|
* only try to get the reldesc by looking up the cache
|
|
* do not go to the disk. this is used by BlockPrepareFile()
|
|
* and RelationIdGetRelation below.
|
|
* --------------------------------
|
|
*/
|
|
Relation
|
|
RelationIdCacheGetRelation(Oid relationId)
|
|
{
|
|
Relation rd;
|
|
|
|
RelationIdCacheLookup(relationId, rd);
|
|
|
|
if (RelationIsValid(rd))
|
|
{
|
|
if (rd->rd_fd == -1)
|
|
{
|
|
rd->rd_fd = smgropen(DEFAULT_SMGR, rd);
|
|
Assert(rd->rd_fd != -1);
|
|
}
|
|
|
|
RelationIncrementReferenceCount(rd);
|
|
|
|
}
|
|
|
|
return rd;
|
|
}
|
|
|
|
/* --------------------------------
|
|
* RelationNameCacheGetRelation
|
|
* --------------------------------
|
|
*/
|
|
static Relation
|
|
RelationNameCacheGetRelation(char *relationName)
|
|
{
|
|
Relation rd;
|
|
NameData name;
|
|
|
|
/*
|
|
* make sure that the name key used for hash lookup is properly
|
|
* null-padded
|
|
*/
|
|
namestrcpy(&name, relationName);
|
|
RelationNameCacheLookup(name.data, rd);
|
|
|
|
if (RelationIsValid(rd))
|
|
{
|
|
if (rd->rd_fd == -1)
|
|
{
|
|
rd->rd_fd = smgropen(DEFAULT_SMGR, rd);
|
|
Assert(rd->rd_fd != -1);
|
|
}
|
|
|
|
RelationIncrementReferenceCount(rd);
|
|
|
|
}
|
|
|
|
return rd;
|
|
}
|
|
|
|
/* --------------------------------
|
|
* RelationIdGetRelation
|
|
*
|
|
* return a relation descriptor based on its id.
|
|
* return a cached value if possible
|
|
* --------------------------------
|
|
*/
|
|
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);
|
|
return
|
|
rd;
|
|
}
|
|
|
|
/* --------------------------------
|
|
* RelationNameGetRelation
|
|
*
|
|
* return a relation descriptor based on its name.
|
|
* return a cached value if possible
|
|
* --------------------------------
|
|
*/
|
|
Relation
|
|
RelationNameGetRelation(char *relationName)
|
|
{
|
|
Relation rd;
|
|
RelationBuildDescInfo buildinfo;
|
|
|
|
/* ----------------
|
|
* increment access statistics
|
|
* ----------------
|
|
*/
|
|
IncrHeapAccessStat(local_RelationNameGetRelation);
|
|
IncrHeapAccessStat(global_RelationNameGetRelation);
|
|
|
|
/* ----------------
|
|
* 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 = relationName;
|
|
|
|
rd = RelationBuildDesc(buildinfo);
|
|
return rd;
|
|
}
|
|
|
|
/* ----------------
|
|
* old "getreldesc" interface.
|
|
* ----------------
|
|
*/
|
|
#ifdef NOT_USED
|
|
Relation
|
|
getreldesc(char *relationName)
|
|
{
|
|
/* ----------------
|
|
* increment access statistics
|
|
* ----------------
|
|
*/
|
|
IncrHeapAccessStat(local_getreldesc);
|
|
IncrHeapAccessStat(global_getreldesc);
|
|
|
|
return RelationNameGetRelation(relationName);
|
|
}
|
|
|
|
#endif
|
|
|
|
/* ----------------------------------------------------------------
|
|
* cache invalidation support routines
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
|
|
/* --------------------------------
|
|
* RelationClose - close an open relation
|
|
* --------------------------------
|
|
*/
|
|
void
|
|
RelationClose(Relation relation)
|
|
{
|
|
/* Note: no locking manipulations needed */
|
|
RelationDecrementReferenceCount(relation);
|
|
}
|
|
|
|
/* --------------------------------
|
|
* RelationFlushRelation
|
|
*
|
|
* Actually blows away a relation... RelationFree doesn't do
|
|
* anything anymore.
|
|
* --------------------------------
|
|
*/
|
|
static void
|
|
RelationFlushRelation(Relation *relationPtr,
|
|
bool onlyFlushReferenceCountZero)
|
|
{
|
|
MemoryContext oldcxt;
|
|
Relation relation = *relationPtr;
|
|
|
|
if (relation->rd_isnailed)
|
|
{
|
|
/* this is a nailed special relation for bootstraping */
|
|
return;
|
|
}
|
|
|
|
if (!onlyFlushReferenceCountZero ||
|
|
RelationHasReferenceCountZero(relation))
|
|
{
|
|
|
|
oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt);
|
|
|
|
RelationCacheDelete(relation);
|
|
|
|
FreeTupleDesc(relation->rd_att);
|
|
SystemCacheRelationFlushed(RelationGetRelid(relation));
|
|
|
|
FreeTriggerDesc(relation);
|
|
|
|
#if 0
|
|
if (relation->rd_rules)
|
|
{
|
|
int j;
|
|
|
|
for (j = 0; j < relation->rd_rules->numLocks; j++)
|
|
pfree(relation->rd_rules->rules[j]);
|
|
pfree(relation->rd_rules->rules);
|
|
pfree(relation->rd_rules);
|
|
}
|
|
#endif
|
|
|
|
pfree(RelationGetLockInfo(relation));
|
|
pfree(RelationGetForm(relation));
|
|
pfree(relation);
|
|
|
|
MemoryContextSwitchTo(oldcxt);
|
|
}
|
|
}
|
|
|
|
/* --------------------------------
|
|
* RelationForgetRelation -
|
|
* RelationFlushRelation + if the relation is local then get rid of
|
|
* the relation descriptor from the newly created relation list.
|
|
* --------------------------------
|
|
*/
|
|
void
|
|
RelationForgetRelation(Oid rid)
|
|
{
|
|
Relation relation;
|
|
|
|
RelationIdCacheLookup(rid, relation);
|
|
Assert(PointerIsValid(relation));
|
|
|
|
if (relation->rd_islocal)
|
|
{
|
|
MemoryContext oldcxt;
|
|
List *curr;
|
|
List *prev = NIL;
|
|
|
|
oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt);
|
|
|
|
foreach(curr, newlyCreatedRelns)
|
|
{
|
|
Relation reln = lfirst(curr);
|
|
|
|
Assert(reln != NULL && reln->rd_islocal);
|
|
if (RelationGetRelid(reln) == rid)
|
|
break;
|
|
prev = curr;
|
|
}
|
|
if (curr == NIL)
|
|
elog(FATAL, "Local relation %s not found in list",
|
|
(RelationGetRelationName(relation))->data);
|
|
if (prev == NIL)
|
|
newlyCreatedRelns = lnext(newlyCreatedRelns);
|
|
else
|
|
lnext(prev) = lnext(curr);
|
|
pfree(curr);
|
|
MemoryContextSwitchTo(oldcxt);
|
|
}
|
|
|
|
RelationFlushRelation(&relation, false);
|
|
}
|
|
|
|
/* --------------------------------
|
|
* RelationIdInvalidateRelationCacheByRelationId
|
|
* --------------------------------
|
|
*/
|
|
void
|
|
RelationIdInvalidateRelationCacheByRelationId(Oid relationId)
|
|
{
|
|
Relation relation;
|
|
|
|
RelationIdCacheLookup(relationId, relation);
|
|
|
|
/*
|
|
* "local" relations are invalidated by RelationPurgeLocalRelation.
|
|
* (This is to make LocalBufferSync's life easier: want the descriptor
|
|
* to hang around for a while. In fact, won't we want this for
|
|
* BufferSync also? But I'll leave it for now since I don't want to
|
|
* break anything.) - ay 3/95
|
|
*/
|
|
if (PointerIsValid(relation) && !relation->rd_islocal)
|
|
{
|
|
|
|
/*
|
|
* The boolean onlyFlushReferenceCountZero in RelationFlushReln()
|
|
* should be set to true when we are incrementing the command
|
|
* counter and to false when we are starting a new xaction. This
|
|
* can be determined by checking the current xaction status.
|
|
*/
|
|
RelationFlushRelation(&relation, CurrentXactInProgress());
|
|
}
|
|
}
|
|
|
|
#if NOT_USED /* See comments at line 1304 */
|
|
/* --------------------------------
|
|
* RelationIdInvalidateRelationCacheByAccessMethodId
|
|
*
|
|
* RelationFlushIndexes is needed for use with HashTableWalk..
|
|
* --------------------------------
|
|
*/
|
|
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
|
|
|
|
|
|
void
|
|
RelationIdInvalidateRelationCacheByAccessMethodId(Oid accessMethodId)
|
|
{
|
|
#if 0
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
HashTableWalk(RelationNameCache, (HashtFunc) RelationFlushIndexes,
|
|
accessMethodId);
|
|
#else
|
|
return;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* RelationCacheInvalidate
|
|
*
|
|
* Will blow away either all the cached relation descriptors or
|
|
* those that have a zero reference count.
|
|
*
|
|
*/
|
|
void
|
|
RelationCacheInvalidate(bool onlyFlushReferenceCountZero)
|
|
{
|
|
HashTableWalk(RelationNameCache, (HashtFunc) RelationFlushRelation,
|
|
onlyFlushReferenceCountZero);
|
|
|
|
/*
|
|
* nailed-in reldescs will still be in the cache... 7 hardwired heaps
|
|
* + 3 hardwired indices == 10 total.
|
|
*/
|
|
if (!onlyFlushReferenceCountZero)
|
|
{
|
|
Assert(RelationNameCache->hctl->nkeys == 10);
|
|
Assert(RelationIdCache->hctl->nkeys == 10);
|
|
}
|
|
}
|
|
|
|
|
|
/* --------------------------------
|
|
* RelationRegisterRelation -
|
|
* register the Relation descriptor of a newly created relation
|
|
* with the relation descriptor Cache.
|
|
* --------------------------------
|
|
*/
|
|
void
|
|
RelationRegisterRelation(Relation relation)
|
|
{
|
|
MemoryContext oldcxt;
|
|
|
|
oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt);
|
|
|
|
if (oldcxt != (MemoryContext) CacheCxt)
|
|
elog(NOIND, "RelationRegisterRelation: WARNING: Context != CacheCxt");
|
|
|
|
RelationCacheInsert(relation);
|
|
|
|
RelationInitLockInfo(relation);
|
|
|
|
/*
|
|
* we've just created the relation. It is invisible to anyone else
|
|
* before the transaction is committed. Setting rd_islocal 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_islocal = TRUE;
|
|
newlyCreatedRelns = lcons(relation, newlyCreatedRelns);
|
|
|
|
MemoryContextSwitchTo(oldcxt);
|
|
}
|
|
|
|
/*
|
|
* RelationPurgeLocalRelation -
|
|
* find all the Relation descriptors marked rd_islocal 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)
|
|
{
|
|
MemoryContext oldcxt;
|
|
|
|
if (newlyCreatedRelns == NULL)
|
|
return;
|
|
|
|
oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt);
|
|
|
|
while (newlyCreatedRelns)
|
|
{
|
|
List *l = newlyCreatedRelns;
|
|
Relation reln = lfirst(l);
|
|
|
|
Assert(reln != NULL && reln->rd_islocal);
|
|
|
|
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_istemp)
|
|
{
|
|
if (!(reln->rd_tmpunlinked))
|
|
{
|
|
smgrunlink(DEFAULT_SMGR, reln);
|
|
reln->rd_tmpunlinked = TRUE;
|
|
}
|
|
}
|
|
else
|
|
smgrunlink(DEFAULT_SMGR, reln);
|
|
}
|
|
else if (!IsBootstrapProcessingMode() && !(reln->rd_istemp))
|
|
|
|
/*
|
|
* RelationFlushRelation () below will flush relation
|
|
* information from the cache. We must call smgrclose to flush
|
|
* relation information from SMGR & FMGR, too. We assume that
|
|
* for temp relations smgrunlink is already called by
|
|
* heap_destroyr and we skip smgrclose for them. -
|
|
* vadim 05/22/97
|
|
*/
|
|
smgrclose(DEFAULT_SMGR, reln);
|
|
|
|
reln->rd_islocal = FALSE;
|
|
|
|
if (!IsBootstrapProcessingMode())
|
|
RelationFlushRelation(&reln, FALSE);
|
|
|
|
newlyCreatedRelns = lnext(newlyCreatedRelns);
|
|
pfree(l);
|
|
}
|
|
|
|
MemoryContextSwitchTo(oldcxt);
|
|
}
|
|
|
|
/* --------------------------------
|
|
* RelationInitialize
|
|
*
|
|
* This initializes the relation descriptor cache.
|
|
* --------------------------------
|
|
*/
|
|
|
|
#define INITRELCACHESIZE 400
|
|
|
|
void
|
|
RelationInitialize(void)
|
|
{
|
|
MemoryContext oldcxt;
|
|
HASHCTL ctl;
|
|
|
|
/* ----------------
|
|
* switch to cache memory context
|
|
* ----------------
|
|
*/
|
|
if (!CacheCxt)
|
|
CacheCxt = CreateGlobalMemory("Cache");
|
|
|
|
oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt);
|
|
|
|
/* ----------------
|
|
* 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.
|
|
* ----------------
|
|
*/
|
|
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);
|
|
}
|
|
|
|
static void
|
|
AttrDefaultFetch(Relation relation)
|
|
{
|
|
AttrDefault *attrdef = relation->rd_att->constr->defval;
|
|
int ndef = relation->rd_att->constr->num_defval;
|
|
Relation adrel;
|
|
Relation irel;
|
|
ScanKeyData skey;
|
|
HeapTupleData tuple;
|
|
Form_pg_attrdef adform;
|
|
IndexScanDesc sd;
|
|
RetrieveIndexResult indexRes;
|
|
struct varlena *val;
|
|
bool isnull;
|
|
int found;
|
|
int i;
|
|
|
|
ScanKeyEntryInitialize(&skey,
|
|
(bits16) 0x0,
|
|
(AttrNumber) 1,
|
|
(RegProcedure) F_OIDEQ,
|
|
ObjectIdGetDatum(RelationGetRelid(relation)));
|
|
|
|
adrel = heap_openr(AttrDefaultRelationName);
|
|
irel = index_openr(AttrDefaultIndex);
|
|
sd = index_beginscan(irel, false, 1, &skey);
|
|
tuple.t_data = NULL;
|
|
|
|
for (found = 0;;)
|
|
{
|
|
Buffer buffer;
|
|
|
|
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;
|
|
found++;
|
|
adform = (Form_pg_attrdef) GETSTRUCT(&tuple);
|
|
for (i = 0; i < ndef; i++)
|
|
{
|
|
if (adform->adnum != attrdef[i].adnum)
|
|
continue;
|
|
if (attrdef[i].adsrc != NULL)
|
|
elog(ERROR, "AttrDefaultFetch: second record found for attr %s in rel %s",
|
|
relation->rd_att->attrs[adform->adnum - 1]->attname.data,
|
|
relation->rd_rel->relname.data);
|
|
|
|
val = (struct varlena *) fastgetattr(&tuple,
|
|
Anum_pg_attrdef_adbin,
|
|
adrel->rd_att, &isnull);
|
|
if (isnull)
|
|
elog(ERROR, "AttrDefaultFetch: adbin IS NULL for attr %s in rel %s",
|
|
relation->rd_att->attrs[adform->adnum - 1]->attname.data,
|
|
relation->rd_rel->relname.data);
|
|
attrdef[i].adbin = textout(val);
|
|
val = (struct varlena *) fastgetattr(&tuple,
|
|
Anum_pg_attrdef_adsrc,
|
|
adrel->rd_att, &isnull);
|
|
if (isnull)
|
|
elog(ERROR, "AttrDefaultFetch: adsrc IS NULL for attr %s in rel %s",
|
|
relation->rd_att->attrs[adform->adnum - 1]->attname.data,
|
|
relation->rd_rel->relname.data);
|
|
attrdef[i].adsrc = textout(val);
|
|
break;
|
|
}
|
|
ReleaseBuffer(buffer);
|
|
|
|
if (i >= ndef)
|
|
elog(ERROR, "AttrDefaultFetch: unexpected record found for attr %d in rel %s",
|
|
adform->adnum,
|
|
relation->rd_rel->relname.data);
|
|
}
|
|
|
|
if (found < ndef)
|
|
elog(ERROR, "AttrDefaultFetch: %d record not found for rel %s",
|
|
ndef - found, relation->rd_rel->relname.data);
|
|
|
|
index_endscan(sd);
|
|
pfree(sd);
|
|
index_close(irel);
|
|
heap_close(adrel);
|
|
}
|
|
|
|
static void
|
|
RelCheckFetch(Relation relation)
|
|
{
|
|
ConstrCheck *check = relation->rd_att->constr->check;
|
|
int ncheck = relation->rd_att->constr->num_check;
|
|
Relation rcrel;
|
|
Relation irel;
|
|
ScanKeyData skey;
|
|
HeapTupleData tuple;
|
|
IndexScanDesc sd;
|
|
RetrieveIndexResult indexRes;
|
|
Name rcname;
|
|
struct varlena *val;
|
|
bool isnull;
|
|
int found;
|
|
|
|
ScanKeyEntryInitialize(&skey,
|
|
(bits16) 0x0,
|
|
(AttrNumber) 1,
|
|
(RegProcedure) F_OIDEQ,
|
|
ObjectIdGetDatum(RelationGetRelid(relation)));
|
|
|
|
rcrel = heap_openr(RelCheckRelationName);
|
|
irel = index_openr(RelCheckIndex);
|
|
sd = index_beginscan(irel, false, 1, &skey);
|
|
tuple.t_data = NULL;
|
|
|
|
for (found = 0;;)
|
|
{
|
|
Buffer buffer;
|
|
|
|
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;
|
|
if (found == ncheck)
|
|
elog(ERROR, "RelCheckFetch: unexpected record found for rel %s",
|
|
relation->rd_rel->relname.data);
|
|
|
|
rcname = (Name) fastgetattr(&tuple,
|
|
Anum_pg_relcheck_rcname,
|
|
rcrel->rd_att, &isnull);
|
|
if (isnull)
|
|
elog(ERROR, "RelCheckFetch: rcname IS NULL for rel %s",
|
|
relation->rd_rel->relname.data);
|
|
check[found].ccname = nameout(rcname);
|
|
val = (struct varlena *) fastgetattr(&tuple,
|
|
Anum_pg_relcheck_rcbin,
|
|
rcrel->rd_att, &isnull);
|
|
if (isnull)
|
|
elog(ERROR, "RelCheckFetch: rcbin IS NULL for rel %s",
|
|
relation->rd_rel->relname.data);
|
|
check[found].ccbin = textout(val);
|
|
val = (struct varlena *) fastgetattr(&tuple,
|
|
Anum_pg_relcheck_rcsrc,
|
|
rcrel->rd_att, &isnull);
|
|
if (isnull)
|
|
elog(ERROR, "RelCheckFetch: rcsrc IS NULL for rel %s",
|
|
relation->rd_rel->relname.data);
|
|
check[found].ccsrc = textout(val);
|
|
found++;
|
|
ReleaseBuffer(buffer);
|
|
}
|
|
|
|
if (found < ncheck)
|
|
elog(ERROR, "RelCheckFetch: %d record not found for rel %s",
|
|
ncheck - found,
|
|
relation->rd_rel->relname.data);
|
|
|
|
index_endscan(sd);
|
|
pfree(sd);
|
|
index_close(irel);
|
|
heap_close(rcrel);
|
|
|
|
}
|
|
|
|
/*
|
|
* 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 descriptor using sequential scans and write it to
|
|
* the initialization file for use by subsequent backends.
|
|
*
|
|
* This is complicated and interferes with system changes, but
|
|
* performance is so bad that we're willing to pay the tax.
|
|
*/
|
|
|
|
/* 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;
|
|
|
|
#ifndef __CYGWIN32__
|
|
if ((fd = FileNameOpenFile(INIT_FILENAME, O_RDONLY, 0600)) < 0)
|
|
#else
|
|
if ((fd = FileNameOpenFile(INIT_FILENAME, O_RDONLY | O_BINARY, 0600)) < 0)
|
|
#endif
|
|
{
|
|
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;
|
|
|
|
/* lock info is not initialized */
|
|
ird->lockInfo = (char *) NULL;
|
|
|
|
/* 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[0].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;
|
|
}
|
|
|
|
/*
|
|
* p += sizeof(IndexStrategy); ((RegProcedure **) p) = support;
|
|
*/
|
|
|
|
ird->rd_support = support;
|
|
|
|
RelationCacheInsert(ird);
|
|
RelationInitLockInfo(ird);
|
|
}
|
|
}
|
|
|
|
static void
|
|
write_irels(void)
|
|
{
|
|
int len;
|
|
int nwritten;
|
|
File fd;
|
|
Relation irel[Num_indices_bootstrap];
|
|
Relation ird;
|
|
Form_pg_am am;
|
|
Form_pg_class relform;
|
|
IndexStrategy strat;
|
|
RegProcedure *support;
|
|
ProcessingMode oldmode;
|
|
int i;
|
|
int relno;
|
|
RelationBuildDescInfo bi;
|
|
|
|
#ifndef __CYGWIN32__
|
|
fd = FileNameOpenFile(INIT_FILENAME, O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
|
#else
|
|
fd = FileNameOpenFile(INIT_FILENAME, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0600);
|
|
#endif
|
|
if (fd < 0)
|
|
elog(FATAL, "cannot create init file %s", INIT_FILENAME);
|
|
|
|
FileSeek(fd, 0L, SEEK_SET);
|
|
|
|
/*
|
|
* Build a relation descriptor for pg_attnumind 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.
|
|
*/
|
|
|
|
oldmode = GetProcessingMode();
|
|
SetProcessingMode(BootstrapProcessing);
|
|
|
|
bi.infotype = INFO_RELNAME;
|
|
bi.i.info_name = AttributeNumIndex;
|
|
irel[0] = RelationBuildDesc(bi);
|
|
irel[0]->rd_isnailed = true;
|
|
|
|
bi.i.info_name = ClassNameIndex;
|
|
irel[1] = RelationBuildDesc(bi);
|
|
irel[1]->rd_isnailed = true;
|
|
|
|
bi.i.info_name = ClassOidIndex;
|
|
irel[2] = RelationBuildDesc(bi);
|
|
irel[2]->rd_isnailed = true;
|
|
|
|
SetProcessingMode(oldmode);
|
|
|
|
/* nail the descriptor in the cache */
|
|
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);
|
|
}
|