mirror of
https://github.com/postgres/postgres.git
synced 2025-04-22 23:02:54 +03:00
expressions in CREATE TABLE. There is no longer an emasculated expression syntax for these things; it's full a_expr for constraints, and b_expr for defaults (unfortunately the fact that NOT NULL is a part of the column constraint syntax causes a shift/reduce conflict if you try a_expr. Oh well --- at least parenthesized boolean expressions work now). Also, stored expression for a column default is not pre-coerced to the column type; we rely on transformInsertStatement to do that when the default is actually used. This means "f1 datetime default 'now'" behaves the way people usually expect it to. BTW, all the support code is now there to implement ALTER TABLE ADD CONSTRAINT and ALTER TABLE ADD COLUMN with a default value. I didn't actually teach ALTER TABLE to call it, but it wouldn't be much work.
2197 lines
58 KiB
C
2197 lines
58 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.74 1999/10/03 23:55:33 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
|
|
* RelationRebuildRelation - rebuild 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"
|
|
#include "utils/temprel.h"
|
|
|
|
|
|
static void RelationClearRelation(Relation relation, bool rebuildIt);
|
|
static void RelationFlushRelation(Relation *relationPtr,
|
|
bool onlyFlushReferenceCountZero);
|
|
static Relation RelationNameCacheGetRelation(char *relationName);
|
|
static void RelationCacheAbortWalker(Relation *relationPtr,
|
|
int dummy);
|
|
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(Relation relation, 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,
|
|
Relation oldrelation);
|
|
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
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
|
|
|
|
/* --------------------------------
|
|
* 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, AccessShareLock);
|
|
pg_class_scan = heap_beginscan(pg_class_desc, 0, SnapshotNow, 1, &key);
|
|
pg_class_tuple = heap_getnext(pg_class_scan, 0);
|
|
|
|
/* ----------------
|
|
* get set to return tuple
|
|
* ----------------
|
|
*/
|
|
if (!HeapTupleIsValid(pg_class_tuple))
|
|
return_tuple = pg_class_tuple;
|
|
else
|
|
{
|
|
/* ------------------
|
|
* a satanic bug used to live here: pg_class_tuple used to be
|
|
* returned here without having the corresponding buffer pinned.
|
|
* so when the buffer gets replaced, all hell breaks loose.
|
|
* this bug is discovered and killed by wei on 9/27/91.
|
|
* -------------------
|
|
*/
|
|
return_tuple = heap_copytuple(pg_class_tuple);
|
|
}
|
|
|
|
/* all done */
|
|
heap_endscan(pg_class_scan);
|
|
heap_close(pg_class_desc, AccessShareLock);
|
|
|
|
return return_tuple;
|
|
}
|
|
|
|
static HeapTuple
|
|
scan_pg_rel_ind(RelationBuildDescInfo buildinfo)
|
|
{
|
|
Relation pg_class_desc;
|
|
HeapTuple return_tuple;
|
|
|
|
pg_class_desc = heap_openr(RelationRelationName, AccessShareLock);
|
|
|
|
switch (buildinfo.infotype)
|
|
{
|
|
case INFO_RELID:
|
|
return_tuple = ClassOidIndexScan(pg_class_desc, 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");
|
|
return_tuple = NULL; /* keep compiler quiet */
|
|
}
|
|
|
|
heap_close(pg_class_desc, AccessShareLock);
|
|
|
|
return return_tuple;
|
|
}
|
|
|
|
/* ----------------
|
|
* AllocateRelationDesc
|
|
*
|
|
* This is used to allocate memory for a new relation descriptor
|
|
* and initialize the rd_rel field.
|
|
*
|
|
* If 'relation' is NULL, allocate a new RelationData object.
|
|
* If not, reuse the given object (that path is taken only when
|
|
* we have to rebuild a relcache entry during RelationClearRelation).
|
|
* ----------------
|
|
*/
|
|
static Relation
|
|
AllocateRelationDesc(Relation relation, u_int natts,
|
|
Form_pg_class relp)
|
|
{
|
|
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, if needed
|
|
*/
|
|
len = sizeof(RelationData);
|
|
|
|
if (relation == NULL)
|
|
relation = (Relation) palloc(len);
|
|
|
|
/* ----------------
|
|
* clear new reldesc
|
|
* ----------------
|
|
*/
|
|
MemSet((char *) relation, 0, len);
|
|
|
|
/* make sure relation is marked as having no open file yet */
|
|
relation->rd_fd = -1;
|
|
|
|
/* 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, AccessShareLock);
|
|
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, AccessShareLock);
|
|
}
|
|
|
|
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, AccessShareLock);
|
|
|
|
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));
|
|
MemSet(attrdef, 0,
|
|
relation->rd_rel->relnatts * sizeof(AttrDefault));
|
|
}
|
|
attrdef[ndef].adnum = i;
|
|
attrdef[ndef].adbin = NULL;
|
|
ndef++;
|
|
}
|
|
}
|
|
|
|
heap_close(attrel, AccessShareLock);
|
|
|
|
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, AccessShareLock);
|
|
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, AccessShareLock);
|
|
|
|
/* ----------------
|
|
* form a RuleLock and insert into relation
|
|
* ----------------
|
|
*/
|
|
rulelock = (RuleLock *) palloc(sizeof(RuleLock));
|
|
rulelock->numLocks = numlocks;
|
|
rulelock->rules = rules;
|
|
|
|
relation->rd_rules = rulelock;
|
|
return;
|
|
}
|
|
|
|
|
|
/* --------------------------------
|
|
* RelationBuildDesc
|
|
*
|
|
* Build a relation descriptor --- either a new one, or by
|
|
* recycling the given old relation object. The latter case
|
|
* supports rebuilding a relcache entry without invalidating
|
|
* pointers to it.
|
|
*
|
|
* To build a relation descriptor, we have to allocate space,
|
|
* open the underlying unix file and initialize the following
|
|
* fields:
|
|
*
|
|
* File rd_fd; open file descriptor
|
|
* int rd_nblocks; number of blocks in rel
|
|
* it will be set in ambeginscan()
|
|
* uint16 rd_refcnt; reference count
|
|
* Form_pg_am rd_am; AM tuple
|
|
* Form_pg_class rd_rel; RELATION tuple
|
|
* Oid rd_id; relation's object id
|
|
* LockInfoData rd_lockInfo; lock manager's info
|
|
* TupleDesc rd_att; tuple descriptor
|
|
*
|
|
* Note: rd_ismem (rel is in-memory only) is currently unused
|
|
* by any part of the system. someday this will indicate that
|
|
* the relation lives only in the main-memory buffer pool
|
|
* -cim 2/4/91
|
|
* --------------------------------
|
|
*/
|
|
static Relation
|
|
RelationBuildDesc(RelationBuildDescInfo buildinfo,
|
|
Relation oldrelation)
|
|
{
|
|
File fd;
|
|
Relation relation;
|
|
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(oldrelation, 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).
|
|
* ----------------
|
|
*/
|
|
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;
|
|
|
|
/* ----------------
|
|
* initialize the relation lock manager information
|
|
* ----------------
|
|
*/
|
|
RelationInitLockInfo(relation); /* see lmgr.c */
|
|
|
|
/* ----------------
|
|
* add new reldesc to relcache
|
|
* ----------------
|
|
*/
|
|
RelationCacheInsert(relation);
|
|
|
|
/*
|
|
* Determining this requires a scan on pg_class, but to do the scan
|
|
* the rdesc for pg_class must already exist. Therefore we must do
|
|
* the check (and possible set) after cache insertion.
|
|
*/
|
|
relation->rd_rel->relhasindex =
|
|
CatalogHasIndex(relationName, RelationGetRelid(relation));
|
|
}
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
|
* Relation Descriptor Lookup Interface
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
|
|
/* --------------------------------
|
|
* RelationIdCacheGetRelation
|
|
*
|
|
* Lookup a reldesc by OID.
|
|
* Only try to get the reldesc by looking up the cache
|
|
* do not go to the disk.
|
|
*
|
|
* NB: relation ref count is incremented if successful.
|
|
* Caller should eventually decrement count. (Usually,
|
|
* that happens by calling RelationClose().)
|
|
* --------------------------------
|
|
*/
|
|
Relation
|
|
RelationIdCacheGetRelation(Oid relationId)
|
|
{
|
|
Relation rd;
|
|
|
|
RelationIdCacheLookup(relationId, rd);
|
|
|
|
if (RelationIsValid(rd))
|
|
{
|
|
if (rd->rd_fd == -1)
|
|
{
|
|
rd->rd_fd = smgropen(DEFAULT_SMGR, rd);
|
|
Assert(rd->rd_fd != -1);
|
|
}
|
|
|
|
RelationIncrementReferenceCount(rd);
|
|
|
|
}
|
|
|
|
return rd;
|
|
}
|
|
|
|
/* --------------------------------
|
|
* RelationNameCacheGetRelation
|
|
*
|
|
* As above, but lookup by name.
|
|
* --------------------------------
|
|
*/
|
|
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
|
|
*
|
|
* Lookup a reldesc by OID; make one if not already in cache.
|
|
*
|
|
* NB: relation ref count is incremented, or set to 1 if new entry.
|
|
* Caller should eventually decrement count. (Usually,
|
|
* that happens by calling RelationClose().)
|
|
* --------------------------------
|
|
*/
|
|
Relation
|
|
RelationIdGetRelation(Oid relationId)
|
|
{
|
|
Relation rd;
|
|
RelationBuildDescInfo buildinfo;
|
|
|
|
/* ----------------
|
|
* increment access statistics
|
|
* ----------------
|
|
*/
|
|
IncrHeapAccessStat(local_RelationIdGetRelation);
|
|
IncrHeapAccessStat(global_RelationIdGetRelation);
|
|
|
|
/* ----------------
|
|
* first try and get a reldesc from the cache
|
|
* ----------------
|
|
*/
|
|
rd = RelationIdCacheGetRelation(relationId);
|
|
if (RelationIsValid(rd))
|
|
return rd;
|
|
|
|
/* ----------------
|
|
* no reldesc in the cache, so have RelationBuildDesc()
|
|
* build one and add it.
|
|
* ----------------
|
|
*/
|
|
buildinfo.infotype = INFO_RELID;
|
|
buildinfo.i.info_id = relationId;
|
|
|
|
rd = RelationBuildDesc(buildinfo, NULL);
|
|
return rd;
|
|
}
|
|
|
|
/* --------------------------------
|
|
* RelationNameGetRelation
|
|
*
|
|
* As above, but lookup by name.
|
|
* --------------------------------
|
|
*/
|
|
Relation
|
|
RelationNameGetRelation(char *relationName)
|
|
{
|
|
char *temprelname;
|
|
Relation rd;
|
|
RelationBuildDescInfo buildinfo;
|
|
|
|
/* ----------------
|
|
* increment access statistics
|
|
* ----------------
|
|
*/
|
|
IncrHeapAccessStat(local_RelationNameGetRelation);
|
|
IncrHeapAccessStat(global_RelationNameGetRelation);
|
|
|
|
/* ----------------
|
|
* if caller is looking for a temp relation, substitute its real name;
|
|
* we only index temp rels by their real names.
|
|
* ----------------
|
|
*/
|
|
temprelname = get_temp_rel_by_name(relationName);
|
|
if (temprelname)
|
|
relationName = temprelname;
|
|
|
|
/* ----------------
|
|
* first try and get a reldesc from the cache
|
|
* ----------------
|
|
*/
|
|
rd = RelationNameCacheGetRelation(relationName);
|
|
if (RelationIsValid(rd))
|
|
return rd;
|
|
|
|
/* ----------------
|
|
* no reldesc in the cache, so have RelationBuildDesc()
|
|
* build one and add it.
|
|
* ----------------
|
|
*/
|
|
buildinfo.infotype = INFO_RELNAME;
|
|
buildinfo.i.info_name = relationName;
|
|
|
|
rd = RelationBuildDesc(buildinfo, NULL);
|
|
return rd;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* cache invalidation support routines
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
|
|
/* --------------------------------
|
|
* RelationClose - close an open relation
|
|
*
|
|
* Actually, we just decrement the refcount.
|
|
* --------------------------------
|
|
*/
|
|
void
|
|
RelationClose(Relation relation)
|
|
{
|
|
/* Note: no locking manipulations needed */
|
|
RelationDecrementReferenceCount(relation);
|
|
}
|
|
|
|
/* --------------------------------
|
|
* RelationClearRelation
|
|
*
|
|
* Physically blow away a relation cache entry, or reset it and rebuild
|
|
* it from scratch (that is, from catalog entries). The latter path is
|
|
* usually used when we are notified of a change to an open relation
|
|
* (one with refcount > 0). However, this routine just does whichever
|
|
* it's told to do; callers must determine which they want.
|
|
* --------------------------------
|
|
*/
|
|
static void
|
|
RelationClearRelation(Relation relation, bool rebuildIt)
|
|
{
|
|
MemoryContext oldcxt;
|
|
|
|
/*
|
|
* Make sure smgr and lower levels close the relation's files,
|
|
* if they weren't closed already. We do this unconditionally;
|
|
* if the relation is not deleted, the next smgr access should
|
|
* reopen the files automatically. This ensures that the low-level
|
|
* file access state is updated after, say, a vacuum truncation.
|
|
*
|
|
* NOTE: this call is a no-op if the relation's smgr file is already
|
|
* closed or unlinked.
|
|
*/
|
|
smgrclose(DEFAULT_SMGR, relation);
|
|
|
|
/*
|
|
* Never, never ever blow away a nailed-in system relation,
|
|
* because we'd be unable to recover.
|
|
*/
|
|
if (relation->rd_isnailed)
|
|
return;
|
|
|
|
oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt);
|
|
|
|
/* Remove relation from hash tables
|
|
*
|
|
* Note: we might be reinserting it momentarily, but we must not have it
|
|
* visible in the hash tables until it's valid again, so don't try to
|
|
* optimize this away...
|
|
*/
|
|
RelationCacheDelete(relation);
|
|
|
|
/* Clear out catcache's entries for this relation */
|
|
SystemCacheRelationFlushed(RelationGetRelid(relation));
|
|
|
|
/* Free all the subsidiary data structures of the relcache entry */
|
|
FreeTupleDesc(relation->rd_att);
|
|
FreeTriggerDesc(relation);
|
|
pfree(RelationGetForm(relation));
|
|
|
|
/*
|
|
* If we're really done with the relcache entry, blow it away.
|
|
* But if someone is still using it, reconstruct the whole deal
|
|
* without moving the physical RelationData record (so that the
|
|
* someone's pointer is still valid). Must preserve ref count
|
|
* and myxactonly flag, too.
|
|
*/
|
|
if (! rebuildIt)
|
|
{
|
|
pfree(relation);
|
|
}
|
|
else
|
|
{
|
|
uint16 old_refcnt = relation->rd_refcnt;
|
|
bool old_myxactonly = relation->rd_myxactonly;
|
|
RelationBuildDescInfo buildinfo;
|
|
|
|
buildinfo.infotype = INFO_RELID;
|
|
buildinfo.i.info_id = RelationGetRelid(relation);
|
|
|
|
if (RelationBuildDesc(buildinfo, relation) != relation)
|
|
{
|
|
/* Should only get here if relation was deleted */
|
|
pfree(relation);
|
|
elog(ERROR, "RelationClearRelation: relation %u deleted while still in use",
|
|
buildinfo.i.info_id);
|
|
}
|
|
RelationSetReferenceCount(relation, old_refcnt);
|
|
relation->rd_myxactonly = old_myxactonly;
|
|
}
|
|
|
|
MemoryContextSwitchTo(oldcxt);
|
|
}
|
|
|
|
/* --------------------------------
|
|
* RelationFlushRelation
|
|
*
|
|
* Rebuild the relation if it is open (refcount > 0), else blow it away.
|
|
* Setting onlyFlushReferenceCountZero to FALSE overrides refcount check.
|
|
* This is currently only used to process SI invalidation notifications.
|
|
* The peculiar calling convention (pointer to pointer to relation)
|
|
* is needed so that we can use this routine as a hash table walker.
|
|
* --------------------------------
|
|
*/
|
|
static void
|
|
RelationFlushRelation(Relation *relationPtr,
|
|
bool onlyFlushReferenceCountZero)
|
|
{
|
|
Relation relation = *relationPtr;
|
|
|
|
/*
|
|
* Do nothing to transaction-local relations, since they cannot be
|
|
* subjects of SI notifications from other backends.
|
|
*/
|
|
if (relation->rd_myxactonly)
|
|
return;
|
|
|
|
/*
|
|
* Zap it. Rebuild if it has nonzero ref count and we did not get
|
|
* the override flag.
|
|
*/
|
|
RelationClearRelation(relation,
|
|
(onlyFlushReferenceCountZero &&
|
|
! RelationHasReferenceCountZero(relation)));
|
|
}
|
|
|
|
/* --------------------------------
|
|
* RelationForgetRelation -
|
|
*
|
|
* RelationClearRelation + if the relation is myxactonly then
|
|
* remove the relation descriptor from the newly created
|
|
* relation list.
|
|
* --------------------------------
|
|
*/
|
|
void
|
|
RelationForgetRelation(Oid rid)
|
|
{
|
|
Relation relation;
|
|
|
|
RelationIdCacheLookup(rid, relation);
|
|
|
|
if (PointerIsValid(relation))
|
|
{
|
|
if (relation->rd_myxactonly)
|
|
{
|
|
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);
|
|
}
|
|
|
|
/* Unconditionally destroy the relcache entry */
|
|
RelationClearRelation(relation, false);
|
|
}
|
|
}
|
|
|
|
/* --------------------------------
|
|
* RelationRebuildRelation -
|
|
*
|
|
* Force a relcache entry to be rebuilt from catalog entries.
|
|
* This is needed, eg, after modifying an attribute of the rel.
|
|
* --------------------------------
|
|
*/
|
|
void
|
|
RelationRebuildRelation(Relation relation)
|
|
{
|
|
RelationClearRelation(relation, true);
|
|
}
|
|
|
|
/* --------------------------------
|
|
* 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.
|
|
*
|
|
* This is currently used only to recover from SI message buffer overflow,
|
|
* so onlyFlushReferenceCountZero is always false. We do not blow away
|
|
* transaction-local relations, since they cannot be targets of SI updates.
|
|
*/
|
|
void
|
|
RelationCacheInvalidate(bool onlyFlushReferenceCountZero)
|
|
{
|
|
HashTableWalk(RelationNameCache, (HashtFunc) RelationFlushRelation,
|
|
onlyFlushReferenceCountZero);
|
|
|
|
if (!onlyFlushReferenceCountZero)
|
|
{
|
|
/*
|
|
* Debugging check: what's left should be transaction-local relations
|
|
* plus nailed-in reldescs. There should be 6 hardwired heaps
|
|
* + 3 hardwired indices == 9 total.
|
|
*/
|
|
int numRels = length(newlyCreatedRelns) + 9;
|
|
|
|
Assert(RelationNameCache->hctl->nkeys == numRels);
|
|
Assert(RelationIdCache->hctl->nkeys == numRels);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* RelationCacheAbort
|
|
*
|
|
* Clean up the relcache at transaction abort.
|
|
*
|
|
* What we need to do here is reset relcache entry ref counts to
|
|
* their normal not-in-a-transaction state. A ref count may be
|
|
* too high because some routine was exited by elog() between
|
|
* incrementing and decrementing the count.
|
|
*
|
|
* XXX Maybe we should do this at transaction commit, too, in case
|
|
* someone forgets to decrement a refcount in a non-error path?
|
|
*/
|
|
void
|
|
RelationCacheAbort(void)
|
|
{
|
|
HashTableWalk(RelationNameCache, (HashtFunc) RelationCacheAbortWalker,
|
|
0);
|
|
}
|
|
|
|
static void
|
|
RelationCacheAbortWalker(Relation *relationPtr, int dummy)
|
|
{
|
|
Relation relation = *relationPtr;
|
|
|
|
if (relation->rd_isnailed)
|
|
RelationSetReferenceCount(relation, 1);
|
|
else
|
|
RelationSetReferenceCount(relation, 0);
|
|
}
|
|
|
|
/* --------------------------------
|
|
* RelationRegisterRelation -
|
|
* register the Relation descriptor of a newly created relation
|
|
* with the relation descriptor Cache.
|
|
* --------------------------------
|
|
*/
|
|
void
|
|
RelationRegisterRelation(Relation relation)
|
|
{
|
|
MemoryContext oldcxt;
|
|
|
|
oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt);
|
|
|
|
if (oldcxt != (MemoryContext) CacheCxt)
|
|
elog(NOIND, "RelationRegisterRelation: WARNING: Context != CacheCxt");
|
|
|
|
RelationInitLockInfo(relation);
|
|
|
|
RelationCacheInsert(relation);
|
|
|
|
/*
|
|
* we've just created the relation. It is invisible to anyone else
|
|
* before the transaction is committed. Setting rd_myxactonly allows
|
|
* us to use the local buffer manager for select/insert/etc before the
|
|
* end of transaction. (We also need to keep track of relations
|
|
* created during a transaction and does the necessary clean up at the
|
|
* end of the transaction.) - ay 3/95
|
|
*/
|
|
relation->rd_myxactonly = TRUE;
|
|
newlyCreatedRelns = lcons(relation, newlyCreatedRelns);
|
|
|
|
MemoryContextSwitchTo(oldcxt);
|
|
}
|
|
|
|
/*
|
|
* RelationPurgeLocalRelation -
|
|
* find all the Relation descriptors marked rd_myxactonly and reset them.
|
|
* This should be called at the end of a transaction (commit/abort) when
|
|
* the "local" relations will become visible to others and the multi-user
|
|
* buffer pool should be used.
|
|
*/
|
|
void
|
|
RelationPurgeLocalRelation(bool xactCommitted)
|
|
{
|
|
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);
|
|
|
|
reln->rd_myxactonly = false; /* mark it not on list anymore */
|
|
|
|
newlyCreatedRelns = lnext(newlyCreatedRelns);
|
|
pfree(l);
|
|
|
|
if (!xactCommitted)
|
|
{
|
|
|
|
/*
|
|
* remove the file if we abort. This is so that files for
|
|
* tables created inside a transaction block get removed.
|
|
*/
|
|
if (reln->rd_isnoname)
|
|
{
|
|
if (!(reln->rd_nonameunlinked))
|
|
{
|
|
smgrunlink(DEFAULT_SMGR, reln);
|
|
reln->rd_nonameunlinked = TRUE;
|
|
}
|
|
}
|
|
else
|
|
smgrunlink(DEFAULT_SMGR, reln);
|
|
}
|
|
|
|
if (!IsBootstrapProcessingMode())
|
|
RelationClearRelation(reln, false);
|
|
}
|
|
|
|
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.
|
|
*
|
|
* NB: if you change this list, fix the count in RelationCacheInvalidate!
|
|
* ----------------
|
|
*/
|
|
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, AccessShareLock);
|
|
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].adbin != 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);
|
|
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, AccessShareLock);
|
|
}
|
|
|
|
static void
|
|
RelCheckFetch(Relation relation)
|
|
{
|
|
ConstrCheck *check = relation->rd_att->constr->check;
|
|
int ncheck = relation->rd_att->constr->num_check;
|
|
Relation rcrel;
|
|
Relation irel;
|
|
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, AccessShareLock);
|
|
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);
|
|
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, AccessShareLock);
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
|
|
/* 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;
|
|
|
|
RelationInitLockInfo(ird);
|
|
|
|
RelationCacheInsert(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 relation descriptors for the critical system indexes without
|
|
* resort to the descriptor cache. In order to do this, we set
|
|
* ProcessingMode to Bootstrap. The effect of this is to disable indexed
|
|
* relation searches -- a necessary step, since we're trying to
|
|
* instantiate the index relation descriptors here. Once we have the
|
|
* descriptors, nail them into cache so we never lose them.
|
|
*
|
|
* NB: if you change this list, fix the count in RelationCacheInvalidate!
|
|
*/
|
|
|
|
oldmode = GetProcessingMode();
|
|
SetProcessingMode(BootstrapProcessing);
|
|
|
|
bi.infotype = INFO_RELNAME;
|
|
bi.i.info_name = AttributeNumIndex;
|
|
irel[0] = RelationBuildDesc(bi, NULL);
|
|
irel[0]->rd_isnailed = true;
|
|
|
|
bi.i.info_name = ClassNameIndex;
|
|
irel[1] = RelationBuildDesc(bi, NULL);
|
|
irel[1]->rd_isnailed = true;
|
|
|
|
bi.i.info_name = ClassOidIndex;
|
|
irel[2] = RelationBuildDesc(bi, NULL);
|
|
irel[2]->rd_isnailed = true;
|
|
|
|
SetProcessingMode(oldmode);
|
|
|
|
/*
|
|
* Write out the index reldescs to the special cache file.
|
|
*/
|
|
for (relno = 0; relno < Num_indices_bootstrap; relno++)
|
|
{
|
|
ird = irel[relno];
|
|
|
|
/* save the volatile fields in the relation descriptor */
|
|
am = ird->rd_am;
|
|
ird->rd_am = (Form_pg_am) NULL;
|
|
relform = ird->rd_rel;
|
|
ird->rd_rel = (Form_pg_class) NULL;
|
|
strat = ird->rd_istrat;
|
|
support = ird->rd_support;
|
|
|
|
/*
|
|
* first write the relation descriptor , excluding strategy and
|
|
* support
|
|
*/
|
|
len = sizeof(RelationData);
|
|
|
|
/* first, write the relation descriptor length */
|
|
if ((nwritten = FileWrite(fd, (char *) &len, sizeof(len)))
|
|
!= sizeof(len))
|
|
elog(FATAL, "cannot write init file -- descriptor length");
|
|
|
|
/* next, write out the Relation structure */
|
|
if ((nwritten = FileWrite(fd, (char *) ird, len)) != len)
|
|
elog(FATAL, "cannot write init file -- reldesc");
|
|
|
|
/* next, write the access method tuple form */
|
|
len = sizeof(FormData_pg_am);
|
|
if ((nwritten = FileWrite(fd, (char *) &len, sizeof(len)))
|
|
!= sizeof(len))
|
|
elog(FATAL, "cannot write init file -- am tuple form length");
|
|
|
|
if ((nwritten = FileWrite(fd, (char *) am, len)) != len)
|
|
elog(FATAL, "cannot write init file -- am tuple form");
|
|
|
|
/* next write the relation tuple form */
|
|
len = sizeof(FormData_pg_class);
|
|
if ((nwritten = FileWrite(fd, (char *) &len, sizeof(len)))
|
|
!= sizeof(len))
|
|
elog(FATAL, "cannot write init file -- relation tuple form length");
|
|
|
|
if ((nwritten = FileWrite(fd, (char *) relform, len)) != len)
|
|
elog(FATAL, "cannot write init file -- relation tuple form");
|
|
|
|
/* next, do all the attribute tuple form data entries */
|
|
len = ATTRIBUTE_TUPLE_SIZE;
|
|
for (i = 0; i < relform->relnatts; i++)
|
|
{
|
|
if ((nwritten = FileWrite(fd, (char *) &len, sizeof(len)))
|
|
!= sizeof(len))
|
|
elog(FATAL, "cannot write init file -- length of attdesc %d", i);
|
|
if ((nwritten = FileWrite(fd, (char *) ird->rd_att->attrs[i], len))
|
|
!= len)
|
|
elog(FATAL, "cannot write init file -- attdesc %d", i);
|
|
}
|
|
|
|
/* next, write the index strategy map */
|
|
len = AttributeNumberGetIndexStrategySize(relform->relnatts,
|
|
am->amstrategies);
|
|
if ((nwritten = FileWrite(fd, (char *) &len, sizeof(len)))
|
|
!= sizeof(len))
|
|
elog(FATAL, "cannot write init file -- strategy map length");
|
|
|
|
if ((nwritten = FileWrite(fd, (char *) strat, len)) != len)
|
|
elog(FATAL, "cannot write init file -- strategy map");
|
|
|
|
/* finally, write the vector of support procedures */
|
|
len = relform->relnatts * (am->amsupport * sizeof(RegProcedure));
|
|
if ((nwritten = FileWrite(fd, (char *) &len, sizeof(len)))
|
|
!= sizeof(len))
|
|
elog(FATAL, "cannot write init file -- support vector length");
|
|
|
|
if ((nwritten = FileWrite(fd, (char *) support, len)) != len)
|
|
elog(FATAL, "cannot write init file -- support vector");
|
|
|
|
/* restore volatile fields */
|
|
ird->rd_am = am;
|
|
ird->rd_rel = relform;
|
|
}
|
|
|
|
FileClose(fd);
|
|
}
|