mirror of
https://github.com/postgres/postgres.git
synced 2025-05-30 00:02:11 -04:00
insight that RelationFlushRelation ought to invoke smgrclose, and that the way to make that work is to ensure that mdclose doesn't fail if the relation is already closed (or unlinked, if we are looking at a DROP TABLE). While I was testing that, I was able to identify several problems that we had with multiple-segment relations. The system is now able to do initdb and pass the regression tests with a very small segment size (I had it set to 64Kb per segment for testing). I don't believe that ever worked before. File descriptor leaks seem to be gone too. I have partially addressed the concerns we had about mdtruncate(), too. On a Win32 or NFS filesystem it is not possible to unlink a file that another backend is holding open, so what md.c now does is to truncate unwanted files to zero length before trying to unlink them. The other backends will be forced to close their open files by relation cache invalidation --- but I think it would take considerable work to make that happen before vacuum truncates the relation rather than after. Leaving zero-length files lying around seems a usable compromise.
2110 lines
54 KiB
C
2110 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.68 1999/09/02 02:57:50 tgl 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 <errno.h>
|
|
#include <sys/file.h>
|
|
#include <fcntl.h>
|
|
|
|
#include "postgres.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_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 "lib/hasht.h"
|
|
#include "miscadmin.h"
|
|
#include "storage/smgr.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/catcache.h"
|
|
#include "utils/relcache.h"
|
|
|
|
|
|
static void RelationFlushRelation(Relation *relationPtr,
|
|
bool onlyFlushReferenceCountZero);
|
|
static Relation RelationNameCacheGetRelation(char *relationName);
|
|
static void init_irels(void);
|
|
static void write_irels(void);
|
|
|
|
/* ----------------
|
|
* 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);
|
|
|
|
#ifdef NOT_USED /* 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 %u)", 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 %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);
|
|
}
|
|
|
|
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);
|
|
|
|
/* make sure smgr and lower levels close the relation's files,
|
|
* if they weren't closed already
|
|
*/
|
|
smgrclose(DEFAULT_SMGR, relation);
|
|
|
|
RelationCacheDelete(relation);
|
|
|
|
FreeTupleDesc(relation->rd_att);
|
|
SystemCacheRelationFlushed(RelationGetRelid(relation));
|
|
|
|
FreeTriggerDesc(relation);
|
|
|
|
#ifdef NOT_USED
|
|
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 myxactonly then
|
|
* get rid of 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)
|
|
{
|
|
MemoryContext oldcxt;
|
|
List *curr;
|
|
List *prev = NIL;
|
|
|
|
oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt);
|
|
|
|
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))->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_myxactonly)
|
|
{
|
|
|
|
/*
|
|
* 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)
|
|
{
|
|
#ifdef NOT_USED
|
|
|
|
/*
|
|
* 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_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)
|
|
{
|
|
MemoryContext oldcxt;
|
|
|
|
if (newlyCreatedRelns == NULL)
|
|
return;
|
|
|
|
oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt);
|
|
|
|
while (newlyCreatedRelns)
|
|
{
|
|
List *l = newlyCreatedRelns;
|
|
Relation reln = lfirst(l);
|
|
|
|
Assert(reln != NULL && reln->rd_myxactonly);
|
|
|
|
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_isnoname)
|
|
{
|
|
if (!(reln->rd_nonameunlinked))
|
|
{
|
|
smgrunlink(DEFAULT_SMGR, reln);
|
|
reln->rd_nonameunlinked = TRUE;
|
|
}
|
|
}
|
|
else
|
|
smgrunlink(DEFAULT_SMGR, reln);
|
|
}
|
|
|
|
reln->rd_myxactonly = 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 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;
|
|
|
|
#ifndef __CYGWIN32__
|
|
if ((fd = FileNameOpenFile(RELCACHE_INIT_FILENAME, O_RDONLY, 0600)) < 0)
|
|
#else
|
|
if ((fd = FileNameOpenFile(RELCACHE_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)
|
|
{
|
|
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;
|
|
ProcessingMode oldmode;
|
|
int i;
|
|
int relno;
|
|
RelationBuildDescInfo bi;
|
|
|
|
#ifndef __CYGWIN32__
|
|
fd = FileNameOpenFile(RELCACHE_INIT_FILENAME, O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
|
#else
|
|
fd = FileNameOpenFile(RELCACHE_INIT_FILENAME, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0600);
|
|
#endif
|
|
if (fd < 0)
|
|
elog(FATAL, "cannot create init file %s", RELCACHE_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);
|
|
}
|