Commit 87bd9563 authored by Tom Lane's avatar Tom Lane

Restructure smgr API as per recent proposal. smgr no longer depends on

the relcache, and so the notion of 'blind write' is gone.  This should
improve efficiency in bgwriter and background checkpoint processes.
Internal restructuring in md.c to remove the not-very-useful array of
MdfdVec objects --- might as well just use pointers.
Also remove the long-dead 'persistent main memory' storage manager (mm.c),
since it seems quite unlikely to ever get resurrected.
parent f06e7952
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/nbtree/nbtree.c,v 1.111 2004/02/06 19:36:17 wieck Exp $ * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtree.c,v 1.112 2004/02/10 01:55:24 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -821,7 +821,9 @@ btvacuumcleanup(PG_FUNCTION_ARGS) ...@@ -821,7 +821,9 @@ btvacuumcleanup(PG_FUNCTION_ARGS)
/* /*
* Do the physical truncation. * Do the physical truncation.
*/ */
new_pages = smgrtruncate(DEFAULT_SMGR, rel, new_pages); if (rel->rd_smgr == NULL)
rel->rd_smgr = smgropen(rel->rd_node);
new_pages = smgrtruncate(rel->rd_smgr, new_pages);
rel->rd_nblocks = new_pages; /* update relcache rel->rd_nblocks = new_pages; /* update relcache
* immediately */ * immediately */
rel->rd_targblock = InvalidBlockNumber; rel->rd_targblock = InvalidBlockNumber;
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/backend/access/transam/slru.c,v 1.10 2004/01/28 21:02:39 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/transam/slru.c,v 1.11 2004/02/10 01:55:24 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <unistd.h> #include <unistd.h>
#include "access/slru.h" #include "access/slru.h"
#include "storage/fd.h"
#include "storage/lwlock.h" #include "storage/lwlock.h"
#include "miscadmin.h" #include "miscadmin.h"
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.161 2004/01/26 22:51:55 momjian Exp $ * $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.162 2004/02/10 01:55:24 tgl Exp $
* *
* NOTES * NOTES
* Transaction aborts can now occur two ways: * Transaction aborts can now occur two ways:
...@@ -159,6 +159,7 @@ ...@@ -159,6 +159,7 @@
#include "executor/spi.h" #include "executor/spi.h"
#include "libpq/be-fsstubs.h" #include "libpq/be-fsstubs.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "storage/fd.h"
#include "storage/proc.h" #include "storage/proc.h"
#include "storage/sinval.h" #include "storage/sinval.h"
#include "storage/smgr.h" #include "storage/smgr.h"
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.133 2004/01/26 22:35:31 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.134 2004/02/10 01:55:24 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include "catalog/catversion.h" #include "catalog/catversion.h"
#include "catalog/pg_control.h" #include "catalog/pg_control.h"
#include "storage/bufpage.h" #include "storage/bufpage.h"
#include "storage/fd.h"
#include "storage/lwlock.h" #include "storage/lwlock.h"
#include "storage/pmsignal.h" #include "storage/pmsignal.h"
#include "storage/proc.h" #include "storage/proc.h"
...@@ -3126,7 +3127,6 @@ ShutdownXLOG(int code, Datum arg) ...@@ -3126,7 +3127,6 @@ ShutdownXLOG(int code, Datum arg)
MyXactMadeTempRelUpdate = false; MyXactMadeTempRelUpdate = false;
CritSectionCount++; CritSectionCount++;
CreateDummyCaches();
CreateCheckPoint(true, true); CreateCheckPoint(true, true);
ShutdownCLOG(); ShutdownCLOG();
CritSectionCount--; CritSectionCount--;
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/backend/access/transam/xlogutils.c,v 1.28 2003/12/14 00:34:47 neilc Exp $ * $PostgreSQL: pgsql/src/backend/access/transam/xlogutils.c,v 1.29 2004/02/10 01:55:24 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -261,14 +261,12 @@ _xl_remove_hash_entry(XLogRelDesc *rdesc) ...@@ -261,14 +261,12 @@ _xl_remove_hash_entry(XLogRelDesc *rdesc)
if (hentry == NULL) if (hentry == NULL)
elog(PANIC, "_xl_remove_hash_entry: file was not found in cache"); elog(PANIC, "_xl_remove_hash_entry: file was not found in cache");
if (rdesc->reldata.rd_fd >= 0) if (rdesc->reldata.rd_smgr != NULL)
smgrclose(DEFAULT_SMGR, &(rdesc->reldata)); smgrclose(rdesc->reldata.rd_smgr);
memset(rdesc, 0, sizeof(XLogRelDesc)); memset(rdesc, 0, sizeof(XLogRelDesc));
memset(tpgc, 0, sizeof(FormData_pg_class)); memset(tpgc, 0, sizeof(FormData_pg_class));
rdesc->reldata.rd_rel = tpgc; rdesc->reldata.rd_rel = tpgc;
return;
} }
static XLogRelDesc * static XLogRelDesc *
...@@ -296,7 +294,6 @@ _xl_new_reldesc(void) ...@@ -296,7 +294,6 @@ _xl_new_reldesc(void)
void void
XLogInitRelationCache(void) XLogInitRelationCache(void)
{ {
CreateDummyCaches();
_xl_init_rel_cache(); _xl_init_rel_cache();
} }
...@@ -306,8 +303,6 @@ XLogCloseRelationCache(void) ...@@ -306,8 +303,6 @@ XLogCloseRelationCache(void)
HASH_SEQ_STATUS status; HASH_SEQ_STATUS status;
XLogRelCacheEntry *hentry; XLogRelCacheEntry *hentry;
DestroyDummyCaches();
if (!_xlrelarr) if (!_xlrelarr)
return; return;
...@@ -347,11 +342,18 @@ XLogOpenRelation(bool redo, RmgrId rmid, RelFileNode rnode) ...@@ -347,11 +342,18 @@ XLogOpenRelation(bool redo, RmgrId rmid, RelFileNode rnode)
sprintf(RelationGetRelationName(&(res->reldata)), "%u", rnode.relNode); sprintf(RelationGetRelationName(&(res->reldata)), "%u", rnode.relNode);
/* unexisting DB id */
res->reldata.rd_lockInfo.lockRelId.dbId = RecoveryDb;
res->reldata.rd_lockInfo.lockRelId.relId = rnode.relNode;
res->reldata.rd_node = rnode; res->reldata.rd_node = rnode;
/*
* We set up the lockRelId in case anything tries to lock the dummy
* relation. Note that this is fairly bogus since relNode may be
* different from the relation's OID. It shouldn't really matter
* though, since we are presumably running by ourselves and can't
* have any lock conflicts ...
*/
res->reldata.rd_lockInfo.lockRelId.dbId = rnode.tblNode;
res->reldata.rd_lockInfo.lockRelId.relId = rnode.relNode;
hentry = (XLogRelCacheEntry *) hentry = (XLogRelCacheEntry *)
hash_search(_xlrelcache, (void *) &rnode, HASH_ENTER, &found); hash_search(_xlrelcache, (void *) &rnode, HASH_ENTER, &found);
...@@ -364,9 +366,17 @@ XLogOpenRelation(bool redo, RmgrId rmid, RelFileNode rnode) ...@@ -364,9 +366,17 @@ XLogOpenRelation(bool redo, RmgrId rmid, RelFileNode rnode)
hentry->rdesc = res; hentry->rdesc = res;
res->reldata.rd_targblock = InvalidBlockNumber; res->reldata.rd_targblock = InvalidBlockNumber;
res->reldata.rd_fd = -1; res->reldata.rd_smgr = smgropen(res->reldata.rd_node);
res->reldata.rd_fd = smgropen(DEFAULT_SMGR, &(res->reldata), /*
true /* allow failure */ ); * Create the target file if it doesn't already exist. This lets
* us cope if the replay sequence contains writes to a relation
* that is later deleted. (The original coding of this routine
* would instead return NULL, causing the writes to be suppressed.
* But that seems like it risks losing valuable data if the filesystem
* loses an inode during a crash. Better to write the data until we
* are actually told to delete the file.)
*/
smgrcreate(res->reldata.rd_smgr, res->reldata.rd_istemp, true);
} }
res->moreRecently = &(_xlrelarr[0]); res->moreRecently = &(_xlrelarr[0]);
...@@ -374,8 +384,5 @@ XLogOpenRelation(bool redo, RmgrId rmid, RelFileNode rnode) ...@@ -374,8 +384,5 @@ XLogOpenRelation(bool redo, RmgrId rmid, RelFileNode rnode)
_xlrelarr[0].lessRecently = res; _xlrelarr[0].lessRecently = res;
res->lessRecently->moreRecently = res; res->lessRecently->moreRecently = res;
if (res->reldata.rd_fd < 0) /* file doesn't exist */
return (NULL);
return (&(res->reldata)); return (&(res->reldata));
} }
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.175 2004/01/07 18:56:25 neilc Exp $ * $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.176 2004/02/10 01:55:24 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -465,14 +465,12 @@ BootstrapMain(int argc, char *argv[]) ...@@ -465,14 +465,12 @@ BootstrapMain(int argc, char *argv[])
break; break;
case BS_XLOG_CHECKPOINT: case BS_XLOG_CHECKPOINT:
CreateDummyCaches();
CreateCheckPoint(false, false); CreateCheckPoint(false, false);
SetSavedRedoRecPtr(); /* pass redo ptr back to SetSavedRedoRecPtr(); /* pass redo ptr back to
* postmaster */ * postmaster */
proc_exit(0); /* done */ proc_exit(0); /* done */
case BS_XLOG_BGWRITER: case BS_XLOG_BGWRITER:
CreateDummyCaches();
BufferBackgroundWriter(); BufferBackgroundWriter();
proc_exit(0); /* done */ proc_exit(0); /* done */
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.257 2003/12/28 21:57:36 tgl Exp $ * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.258 2004/02/10 01:55:24 tgl Exp $
* *
* *
* INTERFACE ROUTINES * INTERFACE ROUTINES
...@@ -298,9 +298,9 @@ heap_create(const char *relname, ...@@ -298,9 +298,9 @@ heap_create(const char *relname,
void void
heap_storage_create(Relation rel) heap_storage_create(Relation rel)
{ {
Assert(rel->rd_fd < 0); Assert(rel->rd_smgr == NULL);
rel->rd_fd = smgrcreate(DEFAULT_SMGR, rel); rel->rd_smgr = smgropen(rel->rd_node);
Assert(rel->rd_fd >= 0); smgrcreate(rel->rd_smgr, rel->rd_istemp, false);
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
...@@ -1210,7 +1210,12 @@ heap_drop_with_catalog(Oid rid) ...@@ -1210,7 +1210,12 @@ heap_drop_with_catalog(Oid rid)
*/ */
if (rel->rd_rel->relkind != RELKIND_VIEW && if (rel->rd_rel->relkind != RELKIND_VIEW &&
rel->rd_rel->relkind != RELKIND_COMPOSITE_TYPE) rel->rd_rel->relkind != RELKIND_COMPOSITE_TYPE)
smgrunlink(DEFAULT_SMGR, rel); {
if (rel->rd_smgr == NULL)
rel->rd_smgr = smgropen(rel->rd_node);
smgrscheduleunlink(rel->rd_smgr, rel->rd_istemp);
rel->rd_smgr = NULL;
}
/* /*
* Close relcache entry, but *keep* AccessExclusiveLock on the * Close relcache entry, but *keep* AccessExclusiveLock on the
...@@ -1706,7 +1711,7 @@ SetRelationNumChecks(Relation rel, int numchecks) ...@@ -1706,7 +1711,7 @@ SetRelationNumChecks(Relation rel, int numchecks)
else else
{ {
/* Skip the disk update, but force relcache inval anyway */ /* Skip the disk update, but force relcache inval anyway */
CacheInvalidateRelcache(RelationGetRelid(rel)); CacheInvalidateRelcache(rel);
} }
heap_freetuple(reltup); heap_freetuple(reltup);
...@@ -1943,7 +1948,9 @@ RelationTruncateIndexes(Oid heapId) ...@@ -1943,7 +1948,9 @@ RelationTruncateIndexes(Oid heapId)
DropRelationBuffers(currentIndex); DropRelationBuffers(currentIndex);
/* Now truncate the actual data and set blocks to zero */ /* Now truncate the actual data and set blocks to zero */
smgrtruncate(DEFAULT_SMGR, currentIndex, 0); if (currentIndex->rd_smgr == NULL)
currentIndex->rd_smgr = smgropen(currentIndex->rd_node);
smgrtruncate(currentIndex->rd_smgr, 0);
currentIndex->rd_nblocks = 0; currentIndex->rd_nblocks = 0;
currentIndex->rd_targblock = InvalidBlockNumber; currentIndex->rd_targblock = InvalidBlockNumber;
...@@ -1990,7 +1997,9 @@ heap_truncate(Oid rid) ...@@ -1990,7 +1997,9 @@ heap_truncate(Oid rid)
DropRelationBuffers(rel); DropRelationBuffers(rel);
/* Now truncate the actual data and set blocks to zero */ /* Now truncate the actual data and set blocks to zero */
smgrtruncate(DEFAULT_SMGR, rel, 0); if (rel->rd_smgr == NULL)
rel->rd_smgr = smgropen(rel->rd_node);
smgrtruncate(rel->rd_smgr, 0);
rel->rd_nblocks = 0; rel->rd_nblocks = 0;
rel->rd_targblock = InvalidBlockNumber; rel->rd_targblock = InvalidBlockNumber;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.226 2004/01/28 21:02:39 tgl Exp $ * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.227 2004/02/10 01:55:24 tgl Exp $
* *
* *
* INTERFACE ROUTINES * INTERFACE ROUTINES
...@@ -808,7 +808,11 @@ index_drop(Oid indexId) ...@@ -808,7 +808,11 @@ index_drop(Oid indexId)
if (i < 0) if (i < 0)
elog(ERROR, "FlushRelationBuffers returned %d", i); elog(ERROR, "FlushRelationBuffers returned %d", i);
smgrunlink(DEFAULT_SMGR, userIndexRelation); if (userIndexRelation->rd_smgr == NULL)
userIndexRelation->rd_smgr = smgropen(userIndexRelation->rd_node);
smgrscheduleunlink(userIndexRelation->rd_smgr,
userIndexRelation->rd_istemp);
userIndexRelation->rd_smgr = NULL;
/* /*
* We are presently too lazy to attempt to compute the new correct * We are presently too lazy to attempt to compute the new correct
...@@ -818,7 +822,7 @@ index_drop(Oid indexId) ...@@ -818,7 +822,7 @@ index_drop(Oid indexId)
* owning relation to ensure other backends update their relcache * owning relation to ensure other backends update their relcache
* lists of indexes. * lists of indexes.
*/ */
CacheInvalidateRelcache(heapId); CacheInvalidateRelcache(userHeapRelation);
/* /*
* Close rels, but keep locks * Close rels, but keep locks
...@@ -1057,7 +1061,7 @@ setRelhasindex(Oid relid, bool hasindex, bool isprimary, Oid reltoastidxid) ...@@ -1057,7 +1061,7 @@ setRelhasindex(Oid relid, bool hasindex, bool isprimary, Oid reltoastidxid)
else else
{ {
/* no need to change tuple, but force relcache rebuild anyway */ /* no need to change tuple, but force relcache rebuild anyway */
CacheInvalidateRelcache(relid); CacheInvalidateRelcacheByTuple(tuple);
} }
if (!pg_class_scan) if (!pg_class_scan)
...@@ -1077,10 +1081,11 @@ void ...@@ -1077,10 +1081,11 @@ void
setNewRelfilenode(Relation relation) setNewRelfilenode(Relation relation)
{ {
Oid newrelfilenode; Oid newrelfilenode;
RelFileNode newrnode;
SMgrRelation srel;
Relation pg_class; Relation pg_class;
HeapTuple tuple; HeapTuple tuple;
Form_pg_class rd_rel; Form_pg_class rd_rel;
RelationData workrel;
/* Can't change relfilenode for nailed tables (indexes ok though) */ /* Can't change relfilenode for nailed tables (indexes ok though) */
Assert(!relation->rd_isnailed || Assert(!relation->rd_isnailed ||
...@@ -1107,14 +1112,18 @@ setNewRelfilenode(Relation relation) ...@@ -1107,14 +1112,18 @@ setNewRelfilenode(Relation relation)
/* create another storage file. Is it a little ugly ? */ /* create another storage file. Is it a little ugly ? */
/* NOTE: any conflict in relfilenode value will be caught here */ /* NOTE: any conflict in relfilenode value will be caught here */
memcpy((char *) &workrel, relation, sizeof(RelationData)); newrnode = relation->rd_node;
workrel.rd_fd = -1; newrnode.relNode = newrelfilenode;
workrel.rd_node.relNode = newrelfilenode;
heap_storage_create(&workrel); srel = smgropen(newrnode);
smgrclose(DEFAULT_SMGR, &workrel); smgrcreate(srel, relation->rd_istemp, false);
smgrclose(srel);
/* schedule unlinking old relfilenode */ /* schedule unlinking old relfilenode */
smgrunlink(DEFAULT_SMGR, relation); if (relation->rd_smgr == NULL)
relation->rd_smgr = smgropen(relation->rd_node);
smgrscheduleunlink(relation->rd_smgr, relation->rd_istemp);
relation->rd_smgr = NULL;
/* update the pg_class row */ /* update the pg_class row */
rd_rel->relfilenode = newrelfilenode; rd_rel->relfilenode = newrelfilenode;
...@@ -1672,7 +1681,9 @@ reindex_index(Oid indexId) ...@@ -1672,7 +1681,9 @@ reindex_index(Oid indexId)
DropRelationBuffers(iRel); DropRelationBuffers(iRel);
/* Now truncate the actual data and set blocks to zero */ /* Now truncate the actual data and set blocks to zero */
smgrtruncate(DEFAULT_SMGR, iRel, 0); if (iRel->rd_smgr == NULL)
iRel->rd_smgr = smgropen(iRel->rd_node);
smgrtruncate(iRel->rd_smgr, 0);
iRel->rd_nblocks = 0; iRel->rd_nblocks = 0;
iRel->rd_targblock = InvalidBlockNumber; iRel->rd_targblock = InvalidBlockNumber;
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.217 2004/01/28 21:02:39 tgl Exp $ * $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.218 2004/02/10 01:55:24 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#include "parser/parse_coerce.h" #include "parser/parse_coerce.h"
#include "parser/parse_relation.h" #include "parser/parse_relation.h"
#include "rewrite/rewriteHandler.h" #include "rewrite/rewriteHandler.h"
#include "storage/fd.h"
#include "tcop/pquery.h" #include "tcop/pquery.h"
#include "tcop/tcopprot.h" #include "tcop/tcopprot.h"
#include "utils/acl.h" #include "utils/acl.h"
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.130 2004/01/07 18:56:25 neilc Exp $ * $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.131 2004/02/10 01:55:25 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include "commands/comment.h" #include "commands/comment.h"
#include "commands/dbcommands.h" #include "commands/dbcommands.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "storage/fd.h"
#include "storage/freespace.h" #include "storage/freespace.h"
#include "storage/sinval.h" #include "storage/sinval.h"
#include "utils/acl.h" #include "utils/acl.h"
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.97 2004/01/28 21:02:39 tgl Exp $ * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.98 2004/02/10 01:55:25 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1010,7 +1010,7 @@ setRelhassubclassInRelation(Oid relationId, bool relhassubclass) ...@@ -1010,7 +1010,7 @@ setRelhassubclassInRelation(Oid relationId, bool relhassubclass)
else else
{ {
/* no need to change tuple, but force relcache rebuild anyway */ /* no need to change tuple, but force relcache rebuild anyway */
CacheInvalidateRelcache(relationId); CacheInvalidateRelcacheByTuple(tuple);
} }
heap_freetuple(tuple); heap_freetuple(tuple);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.163 2003/11/29 19:51:47 pgsql Exp $ * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.164 2004/02/10 01:55:25 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -691,7 +691,7 @@ renametrig(Oid relid, ...@@ -691,7 +691,7 @@ renametrig(Oid relid,
* relcache entries. (Ideally this should happen * relcache entries. (Ideally this should happen
* automatically...) * automatically...)
*/ */
CacheInvalidateRelcache(relid); CacheInvalidateRelcache(targetrel);
} }
else else
{ {
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/backend/commands/user.c,v 1.136 2004/02/02 17:21:07 momjian Exp $ * $PostgreSQL: pgsql/src/backend/commands/user.c,v 1.137 2004/02/10 01:55:25 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include "commands/user.h" #include "commands/user.h"
#include "libpq/crypt.h" #include "libpq/crypt.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "storage/fd.h"
#include "storage/pmsignal.h" #include "storage/pmsignal.h"
#include "utils/acl.h" #include "utils/acl.h"
#include "utils/array.h" #include "utils/array.h"
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.271 2004/01/07 18:56:25 neilc Exp $ * $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.272 2004/02/10 01:55:25 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -2513,7 +2513,9 @@ repair_frag(VRelStats *vacrelstats, Relation onerel, ...@@ -2513,7 +2513,9 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
/* truncate relation, if needed */ /* truncate relation, if needed */
if (blkno < nblocks) if (blkno < nblocks)
{ {
blkno = smgrtruncate(DEFAULT_SMGR, onerel, blkno); if (onerel->rd_smgr == NULL)
onerel->rd_smgr = smgropen(onerel->rd_node);
blkno = smgrtruncate(onerel->rd_smgr, blkno);
onerel->rd_nblocks = blkno; /* update relcache immediately */ onerel->rd_nblocks = blkno; /* update relcache immediately */
onerel->rd_targblock = InvalidBlockNumber; onerel->rd_targblock = InvalidBlockNumber;
vacrelstats->rel_pages = blkno; /* set new number of blocks */ vacrelstats->rel_pages = blkno; /* set new number of blocks */
...@@ -2582,7 +2584,9 @@ vacuum_heap(VRelStats *vacrelstats, Relation onerel, VacPageList vacuum_pages) ...@@ -2582,7 +2584,9 @@ vacuum_heap(VRelStats *vacrelstats, Relation onerel, VacPageList vacuum_pages)
(errmsg("\"%s\": truncated %u to %u pages", (errmsg("\"%s\": truncated %u to %u pages",
RelationGetRelationName(onerel), RelationGetRelationName(onerel),
vacrelstats->rel_pages, relblocks))); vacrelstats->rel_pages, relblocks)));
relblocks = smgrtruncate(DEFAULT_SMGR, onerel, relblocks); if (onerel->rd_smgr == NULL)
onerel->rd_smgr = smgropen(onerel->rd_node);
relblocks = smgrtruncate(onerel->rd_smgr, relblocks);
onerel->rd_nblocks = relblocks; /* update relcache immediately */ onerel->rd_nblocks = relblocks; /* update relcache immediately */
onerel->rd_targblock = InvalidBlockNumber; onerel->rd_targblock = InvalidBlockNumber;
vacrelstats->rel_pages = relblocks; /* set new number of vacrelstats->rel_pages = relblocks; /* set new number of
......
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.35 2004/02/06 19:36:17 wieck Exp $ * $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.36 2004/02/10 01:55:25 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -148,9 +148,8 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt) ...@@ -148,9 +148,8 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
vac_open_indexes(onerel, &nindexes, &Irel); vac_open_indexes(onerel, &nindexes, &Irel);
hasindex = (nindexes > 0); hasindex = (nindexes > 0);
/* Turn on vacuum cost accounting */ /* Turn vacuum cost accounting on or off */
if (VacuumCostNaptime > 0) VacuumCostActive = (VacuumCostNaptime > 0);
VacuumCostActive = true;
VacuumCostBalance = 0; VacuumCostBalance = 0;
/* Do the vacuuming */ /* Do the vacuuming */
...@@ -784,7 +783,9 @@ lazy_truncate_heap(Relation onerel, LVRelStats *vacrelstats) ...@@ -784,7 +783,9 @@ lazy_truncate_heap(Relation onerel, LVRelStats *vacrelstats)
/* /*
* Do the physical truncation. * Do the physical truncation.
*/ */
new_rel_pages = smgrtruncate(DEFAULT_SMGR, onerel, new_rel_pages); if (onerel->rd_smgr == NULL)
onerel->rd_smgr = smgropen(onerel->rd_node);
new_rel_pages = smgrtruncate(onerel->rd_smgr, new_rel_pages);
onerel->rd_nblocks = new_rel_pages; /* update relcache immediately */ onerel->rd_nblocks = new_rel_pages; /* update relcache immediately */
onerel->rd_targblock = InvalidBlockNumber; onerel->rd_targblock = InvalidBlockNumber;
vacrelstats->rel_pages = new_rel_pages; /* save new number of vacrelstats->rel_pages = new_rel_pages; /* save new number of
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/libpq/be-fsstubs.c,v 1.69 2003/11/29 19:51:49 pgsql Exp $ * $PostgreSQL: pgsql/src/backend/libpq/be-fsstubs.c,v 1.70 2004/02/10 01:55:25 tgl Exp $
* *
* NOTES * NOTES
* This should be moved to a more appropriate place. It is here * This should be moved to a more appropriate place. It is here
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
#include "libpq/be-fsstubs.h" #include "libpq/be-fsstubs.h"
#include "libpq/libpq-fs.h" #include "libpq/libpq-fs.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "storage/fd.h"
#include "storage/large_object.h" #include "storage/large_object.h"
#include "utils/memutils.h" #include "utils/memutils.h"
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/rewrite/rewriteDefine.c,v 1.92 2004/01/14 23:01:55 tgl Exp $ * $PostgreSQL: pgsql/src/backend/rewrite/rewriteDefine.c,v 1.93 2004/02/10 01:55:25 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -480,7 +480,12 @@ DefineQueryRewrite(RuleStmt *stmt) ...@@ -480,7 +480,12 @@ DefineQueryRewrite(RuleStmt *stmt)
* XXX what about getting rid of its TOAST table? For now, we don't. * XXX what about getting rid of its TOAST table? For now, we don't.
*/ */
if (RelisBecomingView) if (RelisBecomingView)
smgrunlink(DEFAULT_SMGR, event_relation); {
if (event_relation->rd_smgr == NULL)
event_relation->rd_smgr = smgropen(event_relation->rd_node);
smgrscheduleunlink(event_relation->rd_smgr, event_relation->rd_istemp);
event_relation->rd_smgr = NULL;
}
/* Close rel, but keep lock till commit... */ /* Close rel, but keep lock till commit... */
heap_close(event_relation, NoLock); heap_close(event_relation, NoLock);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/rewrite/rewriteSupport.c,v 1.57 2003/11/29 19:51:55 pgsql Exp $ * $PostgreSQL: pgsql/src/backend/rewrite/rewriteSupport.c,v 1.58 2004/02/10 01:55:25 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -84,7 +84,7 @@ SetRelationRuleStatus(Oid relationId, bool relHasRules, ...@@ -84,7 +84,7 @@ SetRelationRuleStatus(Oid relationId, bool relHasRules,
else else
{ {
/* no need to change tuple, but force relcache rebuild anyway */ /* no need to change tuple, but force relcache rebuild anyway */
CacheInvalidateRelcache(relationId); CacheInvalidateRelcacheByTuple(tuple);
} }
heap_freetuple(tuple); heap_freetuple(tuple);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/buffer/bufmgr.c,v 1.156 2004/02/06 19:36:18 wieck Exp $ * $PostgreSQL: pgsql/src/backend/storage/buffer/bufmgr.c,v 1.157 2004/02/10 01:55:25 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -85,7 +85,7 @@ static Buffer ReadBufferInternal(Relation reln, BlockNumber blockNum, ...@@ -85,7 +85,7 @@ static Buffer ReadBufferInternal(Relation reln, BlockNumber blockNum,
bool bufferLockHeld); bool bufferLockHeld);
static BufferDesc *BufferAlloc(Relation reln, BlockNumber blockNum, static BufferDesc *BufferAlloc(Relation reln, BlockNumber blockNum,
bool *foundPtr); bool *foundPtr);
static bool BufferReplace(BufferDesc *bufHdr); static void BufferReplace(BufferDesc *bufHdr);
#ifdef NOT_USED #ifdef NOT_USED
void PrintBufferDescs(void); void PrintBufferDescs(void);
...@@ -127,7 +127,6 @@ ReadBufferInternal(Relation reln, BlockNumber blockNum, ...@@ -127,7 +127,6 @@ ReadBufferInternal(Relation reln, BlockNumber blockNum,
bool bufferLockHeld) bool bufferLockHeld)
{ {
BufferDesc *bufHdr; BufferDesc *bufHdr;
int status;
bool found; bool found;
bool isExtend; bool isExtend;
bool isLocalBuf; bool isLocalBuf;
...@@ -135,6 +134,10 @@ ReadBufferInternal(Relation reln, BlockNumber blockNum, ...@@ -135,6 +134,10 @@ ReadBufferInternal(Relation reln, BlockNumber blockNum,
isExtend = (blockNum == P_NEW); isExtend = (blockNum == P_NEW);
isLocalBuf = reln->rd_istemp; isLocalBuf = reln->rd_istemp;
/* Open it at the smgr level if not already done */
if (reln->rd_smgr == NULL)
reln->rd_smgr = smgropen(reln->rd_node);
if (isLocalBuf) if (isLocalBuf)
{ {
ReadLocalBufferCount++; ReadLocalBufferCount++;
...@@ -160,7 +163,7 @@ ReadBufferInternal(Relation reln, BlockNumber blockNum, ...@@ -160,7 +163,7 @@ ReadBufferInternal(Relation reln, BlockNumber blockNum,
if (isExtend) if (isExtend)
{ {
/* must be sure we have accurate file length! */ /* must be sure we have accurate file length! */
blockNum = reln->rd_nblocks = smgrnblocks(DEFAULT_SMGR, reln); blockNum = reln->rd_nblocks = smgrnblocks(reln->rd_smgr);
reln->rd_nblocks++; reln->rd_nblocks++;
} }
...@@ -207,23 +210,19 @@ ReadBufferInternal(Relation reln, BlockNumber blockNum, ...@@ -207,23 +210,19 @@ ReadBufferInternal(Relation reln, BlockNumber blockNum,
} }
/* /*
* if we have gotten to this point, the reln pointer must be ok and * if we have gotten to this point, the relation must be open in the smgr.
* the relation file must be open.
*/ */
if (isExtend) if (isExtend)
{ {
/* new buffers are zero-filled */ /* new buffers are zero-filled */
MemSet((char *) MAKE_PTR(bufHdr->data), 0, BLCKSZ); MemSet((char *) MAKE_PTR(bufHdr->data), 0, BLCKSZ);
status = smgrextend(DEFAULT_SMGR, reln, blockNum, smgrextend(reln->rd_smgr, blockNum, (char *) MAKE_PTR(bufHdr->data));
(char *) MAKE_PTR(bufHdr->data));
} }
else else
{ {
status = smgrread(DEFAULT_SMGR, reln, blockNum, smgrread(reln->rd_smgr, blockNum, (char *) MAKE_PTR(bufHdr->data));
(char *) MAKE_PTR(bufHdr->data));
/* check for garbage data */ /* check for garbage data */
if (status == SM_SUCCESS && if (!PageHeaderIsValid((PageHeader) MAKE_PTR(bufHdr->data)))
!PageHeaderIsValid((PageHeader) MAKE_PTR(bufHdr->data)))
{ {
/* /*
* During WAL recovery, the first access to any data page should * During WAL recovery, the first access to any data page should
...@@ -250,47 +249,20 @@ ReadBufferInternal(Relation reln, BlockNumber blockNum, ...@@ -250,47 +249,20 @@ ReadBufferInternal(Relation reln, BlockNumber blockNum,
if (isLocalBuf) if (isLocalBuf)
{ {
/* No shared buffer state to update... */ /* No shared buffer state to update... */
if (status == SM_FAIL)
{
bufHdr->flags |= BM_IO_ERROR;
return InvalidBuffer;
}
return BufferDescriptorGetBuffer(bufHdr); return BufferDescriptorGetBuffer(bufHdr);
} }
/* lock buffer manager again to update IO IN PROGRESS */ /* lock buffer manager again to update IO IN PROGRESS */
LWLockAcquire(BufMgrLock, LW_EXCLUSIVE); LWLockAcquire(BufMgrLock, LW_EXCLUSIVE);
if (status == SM_FAIL) /* IO Succeeded. clear the flags, finish buffer update */
{ bufHdr->flags &= ~(BM_IO_ERROR | BM_IO_IN_PROGRESS);
/* IO Failed. cleanup the data structures and go home */
StrategyInvalidateBuffer(bufHdr);
/* remember that BufferAlloc() pinned the buffer */
UnpinBuffer(bufHdr);
/*
* Have to reset the flag so that anyone waiting for the buffer
* can tell that the contents are invalid.
*/
bufHdr->flags |= BM_IO_ERROR;
bufHdr->flags &= ~BM_IO_IN_PROGRESS;
}
else
{
/* IO Succeeded. clear the flags, finish buffer update */
bufHdr->flags &= ~(BM_IO_ERROR | BM_IO_IN_PROGRESS);
}
/* If anyone was waiting for IO to complete, wake them up now */ /* If anyone was waiting for IO to complete, wake them up now */
TerminateBufferIO(bufHdr); TerminateBufferIO(bufHdr);
LWLockRelease(BufMgrLock); LWLockRelease(BufMgrLock);
if (status == SM_FAIL)
return InvalidBuffer;
return BufferDescriptorGetBuffer(bufHdr); return BufferDescriptorGetBuffer(bufHdr);
} }
...@@ -391,8 +363,6 @@ BufferAlloc(Relation reln, ...@@ -391,8 +363,6 @@ BufferAlloc(Relation reln,
if (buf->flags & BM_DIRTY || buf->cntxDirty) if (buf->flags & BM_DIRTY || buf->cntxDirty)
{ {
bool replace_ok;
/* /*
* skip write error buffers * skip write error buffers
*/ */
...@@ -425,39 +395,21 @@ BufferAlloc(Relation reln, ...@@ -425,39 +395,21 @@ BufferAlloc(Relation reln,
* Write the buffer out, being careful to release BufMgrLock * Write the buffer out, being careful to release BufMgrLock
* before starting the I/O. * before starting the I/O.
*/ */
replace_ok = BufferReplace(buf); BufferReplace(buf);
if (replace_ok == false) /*
* BM_JUST_DIRTIED cleared by BufferReplace and shouldn't
* be set by anyone. - vadim 01/17/97
*/
if (buf->flags & BM_JUST_DIRTIED)
{ {
ereport(WARNING, elog(PANIC, "content of block %u of %u/%u changed while flushing",
(errcode(ERRCODE_IO_ERROR), buf->tag.blockNum,
errmsg("could not write block %u of %u/%u", buf->tag.rnode.tblNode, buf->tag.rnode.relNode);
buf->tag.blockNum,
buf->tag.rnode.tblNode,
buf->tag.rnode.relNode)));
inProgress = FALSE;
buf->flags |= BM_IO_ERROR;
buf->flags &= ~BM_IO_IN_PROGRESS;
TerminateBufferIO(buf);
UnpinBuffer(buf);
buf = NULL;
} }
else
{
/*
* BM_JUST_DIRTIED cleared by BufferReplace and shouldn't
* be set by anyone. - vadim 01/17/97
*/
if (buf->flags & BM_JUST_DIRTIED)
{
elog(PANIC, "content of block %u of %u/%u changed while flushing",
buf->tag.blockNum,
buf->tag.rnode.tblNode, buf->tag.rnode.relNode);
}
buf->flags &= ~BM_DIRTY; buf->flags &= ~BM_DIRTY;
buf->cntxDirty = false; buf->cntxDirty = false;
}
/* /*
* Somebody could have pinned the buffer while we were doing * Somebody could have pinned the buffer while we were doing
...@@ -721,10 +673,8 @@ BufferSync(int percent, int maxpages) ...@@ -721,10 +673,8 @@ BufferSync(int percent, int maxpages)
for (i = 0; i < num_buffer_dirty; i++) for (i = 0; i < num_buffer_dirty; i++)
{ {
Buffer buffer; Buffer buffer;
int status;
RelFileNode rnode;
XLogRecPtr recptr; XLogRecPtr recptr;
Relation reln; SMgrRelation reln;
LWLockAcquire(BufMgrLock, LW_EXCLUSIVE); LWLockAcquire(BufMgrLock, LW_EXCLUSIVE);
...@@ -775,15 +725,9 @@ BufferSync(int percent, int maxpages) ...@@ -775,15 +725,9 @@ BufferSync(int percent, int maxpages)
StartBufferIO(bufHdr, false); /* output IO start */ StartBufferIO(bufHdr, false); /* output IO start */
buffer = BufferDescriptorGetBuffer(bufHdr); buffer = BufferDescriptorGetBuffer(bufHdr);
rnode = bufHdr->tag.rnode;
LWLockRelease(BufMgrLock); LWLockRelease(BufMgrLock);
/*
* Try to find relation for buffer
*/
reln = RelationNodeCacheGetRelation(rnode);
/* /*
* Protect buffer content against concurrent update * Protect buffer content against concurrent update
*/ */
...@@ -805,27 +749,13 @@ BufferSync(int percent, int maxpages) ...@@ -805,27 +749,13 @@ BufferSync(int percent, int maxpages)
bufHdr->flags &= ~BM_JUST_DIRTIED; bufHdr->flags &= ~BM_JUST_DIRTIED;
LWLockRelease(BufMgrLock); LWLockRelease(BufMgrLock);
if (reln == NULL) /* Find smgr relation for buffer */
{ reln = smgropen(bufHdr->tag.rnode);
status = smgrblindwrt(DEFAULT_SMGR,
bufHdr->tag.rnode,
bufHdr->tag.blockNum,
(char *) MAKE_PTR(bufHdr->data));
}
else
{
status = smgrwrite(DEFAULT_SMGR, reln,
bufHdr->tag.blockNum,
(char *) MAKE_PTR(bufHdr->data));
}
if (status == SM_FAIL) /* disk failure ?! */ /* And write... */
ereport(PANIC, smgrwrite(reln,
(errcode(ERRCODE_IO_ERROR), bufHdr->tag.blockNum,
errmsg("could not write block %u of %u/%u", (char *) MAKE_PTR(bufHdr->data));
bufHdr->tag.blockNum,
bufHdr->tag.rnode.tblNode,
bufHdr->tag.rnode.relNode)));
/* /*
* Note that it's safe to change cntxDirty here because of we * Note that it's safe to change cntxDirty here because of we
...@@ -853,10 +783,6 @@ BufferSync(int percent, int maxpages) ...@@ -853,10 +783,6 @@ BufferSync(int percent, int maxpages)
bufHdr->flags &= ~BM_DIRTY; bufHdr->flags &= ~BM_DIRTY;
UnpinBuffer(bufHdr); UnpinBuffer(bufHdr);
LWLockRelease(BufMgrLock); LWLockRelease(BufMgrLock);
/* drop refcnt obtained by RelationNodeCacheGetRelation */
if (reln != NULL)
RelationDecrementReferenceCount(reln);
} }
pfree(buffer_dirty); pfree(buffer_dirty);
...@@ -1026,12 +952,22 @@ BufferBackgroundWriter(void) ...@@ -1026,12 +952,22 @@ BufferBackgroundWriter(void)
n = BufferSync(BgWriterPercent, BgWriterMaxpages); n = BufferSync(BgWriterPercent, BgWriterMaxpages);
/* /*
* Whatever signal is sent to us, let's just die galantly. If * Whatever signal is sent to us, let's just die gallantly. If
* it wasn't meant that way, the postmaster will reincarnate us. * it wasn't meant that way, the postmaster will reincarnate us.
*/ */
if (InterruptPending) if (InterruptPending)
return; return;
/*
* Whenever we have nothing to do, close all smgr files. This
* is so we won't hang onto smgr references to deleted files
* indefinitely. XXX this is a bogus, temporary solution. 'Twould
* be much better to do this once per checkpoint, but the bgwriter
* doesn't yet know anything about checkpoints.
*/
if (n == 0)
smgrcloseall();
/* /*
* Nap for the configured time or sleep for 10 seconds if * Nap for the configured time or sleep for 10 seconds if
* there was nothing to do at all. * there was nothing to do at all.
...@@ -1073,17 +1009,15 @@ BufferGetBlockNumber(Buffer buffer) ...@@ -1073,17 +1009,15 @@ BufferGetBlockNumber(Buffer buffer)
/* /*
* BufferReplace * BufferReplace
* *
* Write out the buffer corresponding to 'bufHdr'. Returns 'true' if * Write out the buffer corresponding to 'bufHdr'.
* the buffer was successfully written out, 'false' otherwise.
* *
* BufMgrLock must be held at entry, and the buffer must be pinned. * BufMgrLock must be held at entry, and the buffer must be pinned.
*/ */
static bool static void
BufferReplace(BufferDesc *bufHdr) BufferReplace(BufferDesc *bufHdr)
{ {
Relation reln; SMgrRelation reln;
XLogRecPtr recptr; XLogRecPtr recptr;
int status;
ErrorContextCallback errcontext; ErrorContextCallback errcontext;
/* To check if block content changed while flushing. - vadim 01/17/97 */ /* To check if block content changed while flushing. - vadim 01/17/97 */
...@@ -1104,36 +1038,20 @@ BufferReplace(BufferDesc *bufHdr) ...@@ -1104,36 +1038,20 @@ BufferReplace(BufferDesc *bufHdr)
recptr = BufferGetLSN(bufHdr); recptr = BufferGetLSN(bufHdr);
XLogFlush(recptr); XLogFlush(recptr);
reln = RelationNodeCacheGetRelation(bufHdr->tag.rnode); /* Find smgr relation for buffer */
reln = smgropen(bufHdr->tag.rnode);
if (reln != NULL) /* And write... */
{ smgrwrite(reln,
status = smgrwrite(DEFAULT_SMGR, reln, bufHdr->tag.blockNum,
bufHdr->tag.blockNum, (char *) MAKE_PTR(bufHdr->data));
(char *) MAKE_PTR(bufHdr->data));
}
else
{
status = smgrblindwrt(DEFAULT_SMGR, bufHdr->tag.rnode,
bufHdr->tag.blockNum,
(char *) MAKE_PTR(bufHdr->data));
}
/* drop relcache refcnt incremented by RelationNodeCacheGetRelation */
if (reln != NULL)
RelationDecrementReferenceCount(reln);
/* Pop the error context stack */ /* Pop the error context stack */
error_context_stack = errcontext.previous; error_context_stack = errcontext.previous;
LWLockAcquire(BufMgrLock, LW_EXCLUSIVE); LWLockAcquire(BufMgrLock, LW_EXCLUSIVE);
if (status == SM_FAIL)
return false;
BufferFlushCount++; BufferFlushCount++;
return true;
} }
/* /*
...@@ -1151,12 +1069,17 @@ RelationGetNumberOfBlocks(Relation relation) ...@@ -1151,12 +1069,17 @@ RelationGetNumberOfBlocks(Relation relation)
* *
* Don't call smgr on a view or a composite type, either. * Don't call smgr on a view or a composite type, either.
*/ */
if (relation->rd_rel->relkind == RELKIND_VIEW) if (relation->rd_rel->relkind == RELKIND_VIEW ||
relation->rd_nblocks = 0; relation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
else if (relation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
relation->rd_nblocks = 0; relation->rd_nblocks = 0;
else if (!relation->rd_isnew && !relation->rd_istemp) else if (!relation->rd_isnew && !relation->rd_istemp)
relation->rd_nblocks = smgrnblocks(DEFAULT_SMGR, relation); {
/* Open it at the smgr level if not already done */
if (relation->rd_smgr == NULL)
relation->rd_smgr = smgropen(relation->rd_node);
relation->rd_nblocks = smgrnblocks(relation->rd_smgr);
}
return relation->rd_nblocks; return relation->rd_nblocks;
} }
...@@ -1172,12 +1095,17 @@ RelationGetNumberOfBlocks(Relation relation) ...@@ -1172,12 +1095,17 @@ RelationGetNumberOfBlocks(Relation relation)
void void
RelationUpdateNumberOfBlocks(Relation relation) RelationUpdateNumberOfBlocks(Relation relation)
{ {
if (relation->rd_rel->relkind == RELKIND_VIEW) if (relation->rd_rel->relkind == RELKIND_VIEW ||
relation->rd_nblocks = 0; relation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
else if (relation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
relation->rd_nblocks = 0; relation->rd_nblocks = 0;
else else
relation->rd_nblocks = smgrnblocks(DEFAULT_SMGR, relation); {
/* Open it at the smgr level if not already done */
if (relation->rd_smgr == NULL)
relation->rd_smgr = smgropen(relation->rd_node);
relation->rd_nblocks = smgrnblocks(relation->rd_smgr);
}
} }
/* --------------------------------------------------------------------- /* ---------------------------------------------------------------------
...@@ -1465,7 +1393,6 @@ FlushRelationBuffers(Relation rel, BlockNumber firstDelBlock) ...@@ -1465,7 +1393,6 @@ FlushRelationBuffers(Relation rel, BlockNumber firstDelBlock)
int i; int i;
BufferDesc *bufHdr; BufferDesc *bufHdr;
XLogRecPtr recptr; XLogRecPtr recptr;
int status;
ErrorContextCallback errcontext; ErrorContextCallback errcontext;
/* Setup error traceback support for ereport() */ /* Setup error traceback support for ereport() */
...@@ -1484,17 +1411,13 @@ FlushRelationBuffers(Relation rel, BlockNumber firstDelBlock) ...@@ -1484,17 +1411,13 @@ FlushRelationBuffers(Relation rel, BlockNumber firstDelBlock)
{ {
if (bufHdr->flags & BM_DIRTY || bufHdr->cntxDirty) if (bufHdr->flags & BM_DIRTY || bufHdr->cntxDirty)
{ {
status = smgrwrite(DEFAULT_SMGR, rel, /* Open it at the smgr level if not already done */
bufHdr->tag.blockNum, if (rel->rd_smgr == NULL)
(char *) MAKE_PTR(bufHdr->data)); rel->rd_smgr = smgropen(rel->rd_node);
if (status == SM_FAIL)
{ smgrwrite(rel->rd_smgr,
error_context_stack = errcontext.previous; bufHdr->tag.blockNum,
elog(WARNING, "FlushRelationBuffers(\"%s\" (local), %u): block %u is dirty, could not flush it", (char *) MAKE_PTR(bufHdr->data));
RelationGetRelationName(rel), firstDelBlock,
bufHdr->tag.blockNum);
return (-1);
}
bufHdr->flags &= ~(BM_DIRTY | BM_JUST_DIRTIED); bufHdr->flags &= ~(BM_DIRTY | BM_JUST_DIRTIED);
bufHdr->cntxDirty = false; bufHdr->cntxDirty = false;
} }
...@@ -1553,17 +1476,13 @@ FlushRelationBuffers(Relation rel, BlockNumber firstDelBlock) ...@@ -1553,17 +1476,13 @@ FlushRelationBuffers(Relation rel, BlockNumber firstDelBlock)
LWLockRelease(BufMgrLock); LWLockRelease(BufMgrLock);
status = smgrwrite(DEFAULT_SMGR, rel, /* Open it at the smgr level if not already done */
bufHdr->tag.blockNum, if (rel->rd_smgr == NULL)
(char *) MAKE_PTR(bufHdr->data)); rel->rd_smgr = smgropen(rel->rd_node);
if (status == SM_FAIL) /* disk failure ?! */ smgrwrite(rel->rd_smgr,
ereport(PANIC, bufHdr->tag.blockNum,
(errcode(ERRCODE_IO_ERROR), (char *) MAKE_PTR(bufHdr->data));
errmsg("could not write block %u of %u/%u",
bufHdr->tag.blockNum,
bufHdr->tag.rnode.tblNode,
bufHdr->tag.rnode.relNode)));
BufferFlushCount++; BufferFlushCount++;
...@@ -2046,7 +1965,11 @@ AbortBufferIO(void) ...@@ -2046,7 +1965,11 @@ AbortBufferIO(void)
LWLockAcquire(BufMgrLock, LW_EXCLUSIVE); LWLockAcquire(BufMgrLock, LW_EXCLUSIVE);
Assert(buf->flags & BM_IO_IN_PROGRESS); Assert(buf->flags & BM_IO_IN_PROGRESS);
if (IsForInput) if (IsForInput)
{
Assert(!(buf->flags & BM_DIRTY) && !(buf->cntxDirty)); Assert(!(buf->flags & BM_DIRTY) && !(buf->cntxDirty));
/* Don't think that buffer is valid */
StrategyInvalidateBuffer(buf);
}
else else
{ {
Assert(buf->flags & BM_DIRTY || buf->cntxDirty); Assert(buf->flags & BM_DIRTY || buf->cntxDirty);
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/buffer/localbuf.c,v 1.51 2004/01/07 18:56:27 neilc Exp $ * $PostgreSQL: pgsql/src/backend/storage/buffer/localbuf.c,v 1.52 2004/02/10 01:55:25 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -90,24 +90,15 @@ LocalBufferAlloc(Relation reln, BlockNumber blockNum, bool *foundPtr) ...@@ -90,24 +90,15 @@ LocalBufferAlloc(Relation reln, BlockNumber blockNum, bool *foundPtr)
*/ */
if (bufHdr->flags & BM_DIRTY || bufHdr->cntxDirty) if (bufHdr->flags & BM_DIRTY || bufHdr->cntxDirty)
{ {
Relation bufrel = RelationNodeCacheGetRelation(bufHdr->tag.rnode); SMgrRelation reln;
/* flush this page */ /* Find smgr relation for buffer */
if (bufrel == NULL) reln = smgropen(bufHdr->tag.rnode);
{
smgrblindwrt(DEFAULT_SMGR, /* And write... */
bufHdr->tag.rnode, smgrwrite(reln,
bufHdr->tag.blockNum, bufHdr->tag.blockNum,
(char *) MAKE_PTR(bufHdr->data)); (char *) MAKE_PTR(bufHdr->data));
}
else
{
smgrwrite(DEFAULT_SMGR, bufrel,
bufHdr->tag.blockNum,
(char *) MAKE_PTR(bufHdr->data));
/* drop refcount incremented by RelationNodeCacheGetRelation */
RelationDecrementReferenceCount(bufrel);
}
LocalBufferFlushCount++; LocalBufferFlushCount++;
} }
...@@ -143,9 +134,6 @@ LocalBufferAlloc(Relation reln, BlockNumber blockNum, bool *foundPtr) ...@@ -143,9 +134,6 @@ LocalBufferAlloc(Relation reln, BlockNumber blockNum, bool *foundPtr)
/* /*
* it's all ours now. * it's all ours now.
*
* We need not in tblNode currently but will in future I think, when
* we'll give up rel->rd_fd to fmgr cache.
*/ */
bufHdr->tag.rnode = reln->rd_node; bufHdr->tag.rnode = reln->rd_node;
bufHdr->tag.blockNum = blockNum; bufHdr->tag.blockNum = blockNum;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/ipc/ipci.c,v 1.63 2004/01/26 22:59:53 momjian Exp $ * $PostgreSQL: pgsql/src/backend/storage/ipc/ipci.c,v 1.64 2004/02/10 01:55:25 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -65,9 +65,6 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, ...@@ -65,9 +65,6 @@ CreateSharedMemoryAndSemaphores(bool makePrivate,
size += FreeSpaceShmemSize(); size += FreeSpaceShmemSize();
#ifdef EXEC_BACKEND #ifdef EXEC_BACKEND
size += ShmemBackendArraySize(); size += ShmemBackendArraySize();
#endif
#ifdef STABLE_MEMORY_STORAGE
size += MMShmemSize();
#endif #endif
size += 100000; size += 100000;
/* might as well round it off to a multiple of a typical page size */ /* might as well round it off to a multiple of a typical page size */
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
# Makefile for storage/smgr # Makefile for storage/smgr
# #
# IDENTIFICATION # IDENTIFICATION
# $PostgreSQL: pgsql/src/backend/storage/smgr/Makefile,v 1.14 2003/11/29 19:51:57 pgsql Exp $ # $PostgreSQL: pgsql/src/backend/storage/smgr/Makefile,v 1.15 2004/02/10 01:55:26 tgl Exp $
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
...@@ -12,7 +12,7 @@ subdir = src/backend/storage/smgr ...@@ -12,7 +12,7 @@ subdir = src/backend/storage/smgr
top_builddir = ../../../.. top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global include $(top_builddir)/src/Makefile.global
OBJS = md.o mm.o smgr.o smgrtype.o OBJS = md.o smgr.o smgrtype.o
all: SUBSYS.o all: SUBSYS.o
......
# $PostgreSQL: pgsql/src/backend/storage/smgr/README,v 1.2 2003/11/29 19:51:57 pgsql Exp $ # $PostgreSQL: pgsql/src/backend/storage/smgr/README,v 1.3 2004/02/10 01:55:26 tgl Exp $
This directory contains the code that supports the Postgres storage manager In the original Berkeley Postgres system, there were several storage managers,
switch and all of the installed storage managers. In released systems, of which only the "magnetic disk" manager remains. (At Berkeley there were
the only supported storage manager is the magnetic disk manager. At UC also managers for the Sony WORM optical disk jukebox and persistent main
Berkeley, the Sony WORM optical disk jukebox and persistent main memory are memory, but these were never supported in any externally released Postgres,
also supported. nor in any version of PostgreSQL.) However, we retain the notion of a storage
manager switch in case anyone wants to reintroduce other kinds of storage
managers.
As of Postgres Release 3.0, every relation in the system is tagged with the In Berkeley Postgres each relation was tagged with the ID of the storage
storage manager on which it resides. The storage manager switch code turns manager to use for it. This is gone. It would be more reasonable to
what used to by filesystem operations into operations on the correct store, associate storage managers with tablespaces (a feature not present as this
for any given relation. text is being written, but one likely to emerge soon).
The files in this directory, and their contents, are The files in this directory, and their contents, are
smgrtype.c Storage manager type -- maps string names to storage manager smgrtype.c Storage manager type -- maps string names to storage manager
IDs and provides simple comparison operators. This is the IDs and provides simple comparison operators. This is the
regproc support for type 'smgr' in the system catalogs. regproc support for type 'smgr' in the system catalogs.
(This is vestigial since no columns of type smgr exist
in the catalogs anymore.)
smgr.c The storage manager switch dispatch code. The routines in smgr.c The storage manager switch dispatch code. The routines in
this file call the appropriate storage manager to do hardware this file call the appropriate storage manager to do hardware
accesses requested by the backend. accesses requested by the backend. smgr.c also manages the
file handle cache (SMgrRelation table).
md.c The magnetic disk storage manager. md.c The magnetic disk storage manager.
mm.c The persistent main memory storage manager (#undef'ed in Note that md.c in turn relies on src/backend/storage/file/fd.c.
tmp/c.h for all distributed systems).
sj.c The sony jukebox storage manager and cache management code
(#undef'ed in tmp/c.h for all distributed systems). The
routines in this file allocate extents, maintain block
maps, and guarantee the persistence and coherency of a cache
of jukebox blocks on magnetic disk.
pgjb.c The postgres jukebox interface routines. The routines here
handle exclusion on the physical device and translate requests
from the storage manager code (sj.c) into jbaccess calls.
jbaccess.c Access code for the physical Sony jukebox device. This code
was swiped from Andy McFadden's jblib.a code at UC Berkeley.
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/smgr/md.c,v 1.101 2004/01/07 18:56:27 neilc Exp $ * $PostgreSQL: pgsql/src/backend/storage/smgr/md.c,v 1.102 2004/02/10 01:55:26 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -21,108 +21,81 @@ ...@@ -21,108 +21,81 @@
#include "catalog/catalog.h" #include "catalog/catalog.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "storage/fd.h"
#include "storage/smgr.h" #include "storage/smgr.h"
#include "utils/inval.h"
#include "utils/memutils.h" #include "utils/memutils.h"
/* /*
* The magnetic disk storage manager keeps track of open file * The magnetic disk storage manager keeps track of open file
* descriptors in its own descriptor pool. This is done to make it * descriptors in its own descriptor pool. This is done to make it
* easier to support relations that are larger than the operating * easier to support relations that are larger than the operating
* system's file size limit (often 2GBytes). In order to do that, we * system's file size limit (often 2GBytes). In order to do that,
* we break relations up into chunks of < 2GBytes and store one chunk * we break relations up into chunks of < 2GBytes and store one chunk
* in each of several files that represent the relation. See the * in each of several files that represent the relation. See the
* BLCKSZ and RELSEG_SIZE configuration constants in * BLCKSZ and RELSEG_SIZE configuration constants in
* include/pg_config.h. * include/pg_config.h. All chunks except the last MUST have size exactly
* equal to RELSEG_SIZE blocks --- see mdnblocks() and mdtruncate().
* *
* The file descriptor stored in the relation cache (see RelationGetFile()) * The file descriptor pointer (md_fd field) stored in the SMgrRelation
* is actually an index into the Md_fdvec array. -1 indicates not open. * cache is, therefore, just the head of a list of MdfdVec objects.
* But note the md_fd pointer can be NULL, indicating relation not open.
* *
* When a relation is broken into multiple chunks, only the first chunk * All MdfdVec objects are palloc'd in the MdCxt memory context.
* has its own entry in the Md_fdvec array; the remaining chunks have
* palloc'd MdfdVec objects that are chained onto the first chunk via the
* mdfd_chain links. All chunks except the last MUST have size exactly
* equal to RELSEG_SIZE blocks --- see mdnblocks() and mdtruncate().
*/ */
typedef struct _MdfdVec typedef struct _MdfdVec
{ {
int mdfd_vfd; /* fd number in vfd pool */ File mdfd_vfd; /* fd number in fd.c's pool */
int mdfd_flags; /* fd status flags */
/* these are the assigned bits in mdfd_flags: */
#define MDFD_FREE (1 << 0) /* unused entry */
int mdfd_nextFree; /* link to next freelist member, if free */
#ifndef LET_OS_MANAGE_FILESIZE #ifndef LET_OS_MANAGE_FILESIZE
struct _MdfdVec *mdfd_chain; /* for large relations */ struct _MdfdVec *mdfd_chain; /* for large relations */
#endif #endif
} MdfdVec; } MdfdVec;
static int Nfds = 100; /* initial/current size of Md_fdvec array */
static MdfdVec *Md_fdvec = NULL;
static int Md_Free = -1; /* head of freelist of unused fdvec
* entries */
static int CurFd = 0; /* first never-used fdvec index */
static MemoryContext MdCxt; /* context for all md.c allocations */ static MemoryContext MdCxt; /* context for all md.c allocations */
/* routines declared here */
static void mdclose_fd(int fd);
static int _mdfd_getrelnfd(Relation reln);
static MdfdVec *_mdfd_openseg(Relation reln, BlockNumber segno, int oflags);
static MdfdVec *_mdfd_getseg(Relation reln, BlockNumber blkno);
static int _mdfd_blind_getseg(RelFileNode rnode, BlockNumber blkno);
static int _fdvec_alloc(void); /* routines declared here */
static void _fdvec_free(int); static MdfdVec *mdopen(SMgrRelation reln);
static MdfdVec *_fdvec_alloc(void);
#ifndef LET_OS_MANAGE_FILESIZE
static MdfdVec *_mdfd_openseg(SMgrRelation reln, BlockNumber segno,
int oflags);
#endif
static MdfdVec *_mdfd_getseg(SMgrRelation reln, BlockNumber blkno);
static BlockNumber _mdnblocks(File file, Size blcksz); static BlockNumber _mdnblocks(File file, Size blcksz);
/* /*
* mdinit() -- Initialize private state for magnetic disk storage manager. * mdinit() -- Initialize private state for magnetic disk storage manager.
*
* We keep a private table of all file descriptors. This routine
* allocates and initializes the table.
*
* Returns SM_SUCCESS or SM_FAIL with errno set as appropriate.
*/ */
int bool
mdinit(void) mdinit(void)
{ {
int i;
MdCxt = AllocSetContextCreate(TopMemoryContext, MdCxt = AllocSetContextCreate(TopMemoryContext,
"MdSmgr", "MdSmgr",
ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE); ALLOCSET_DEFAULT_MAXSIZE);
Md_fdvec = (MdfdVec *) MemoryContextAlloc(MdCxt, Nfds * sizeof(MdfdVec)); return true;
MemSet(Md_fdvec, 0, Nfds * sizeof(MdfdVec));
/* Set free list */
for (i = 0; i < Nfds; i++)
{
Md_fdvec[i].mdfd_nextFree = i + 1;
Md_fdvec[i].mdfd_flags = MDFD_FREE;
}
Md_Free = 0;
Md_fdvec[Nfds - 1].mdfd_nextFree = -1;
return SM_SUCCESS;
} }
int /*
mdcreate(Relation reln) * mdcreate() -- Create a new relation on magnetic disk.
*
* If isRedo is true, it's okay for the relation to exist already.
*/
bool
mdcreate(SMgrRelation reln, bool isRedo)
{ {
char *path; char *path;
int fd, File fd;
vfd;
Assert(reln->rd_fd < 0); Assert(reln->md_fd == NULL);
path = relpath(reln->rd_node); path = relpath(reln->smgr_rnode);
fd = FileNameOpenFile(path, O_RDWR | O_CREAT | O_EXCL | PG_BINARY, 0600); fd = FileNameOpenFile(path, O_RDWR | O_CREAT | O_EXCL | PG_BINARY, 0600);
...@@ -134,43 +107,45 @@ mdcreate(Relation reln) ...@@ -134,43 +107,45 @@ mdcreate(Relation reln)
* During bootstrap, there are cases where a system relation will * During bootstrap, there are cases where a system relation will
* be accessed (by internal backend processes) before the * be accessed (by internal backend processes) before the
* bootstrap script nominally creates it. Therefore, allow the * bootstrap script nominally creates it. Therefore, allow the
* file to exist already, but in bootstrap mode only. (See also * file to exist already, even if isRedo is not set. (See also
* mdopen) * mdopen)
*/ */
if (IsBootstrapProcessingMode()) if (isRedo || IsBootstrapProcessingMode())
fd = FileNameOpenFile(path, O_RDWR | PG_BINARY, 0600); fd = FileNameOpenFile(path, O_RDWR | PG_BINARY, 0600);
if (fd < 0) if (fd < 0)
{ {
pfree(path); pfree(path);
/* be sure to return the error reported by create, not open */ /* be sure to return the error reported by create, not open */
errno = save_errno; errno = save_errno;
return -1; return false;
} }
errno = 0; errno = 0;
} }
pfree(path); pfree(path);
vfd = _fdvec_alloc(); reln->md_fd = _fdvec_alloc();
if (vfd < 0)
return -1;
Md_fdvec[vfd].mdfd_vfd = fd; reln->md_fd->mdfd_vfd = fd;
Md_fdvec[vfd].mdfd_flags = (uint16) 0;
#ifndef LET_OS_MANAGE_FILESIZE #ifndef LET_OS_MANAGE_FILESIZE
Md_fdvec[vfd].mdfd_chain = NULL; reln->md_fd->mdfd_chain = NULL;
#endif #endif
return vfd; return true;
} }
/* /*
* mdunlink() -- Unlink a relation. * mdunlink() -- Unlink a relation.
*
* Note that we're passed a RelFileNode --- by the time this is called,
* there won't be an SMgrRelation hashtable entry anymore.
*
* If isRedo is true, it's okay for the relation to be already gone.
*/ */
int bool
mdunlink(RelFileNode rnode) mdunlink(RelFileNode rnode, bool isRedo)
{ {
int status = SM_SUCCESS; bool status = true;
int save_errno = 0; int save_errno = 0;
char *path; char *path;
...@@ -179,13 +154,16 @@ mdunlink(RelFileNode rnode) ...@@ -179,13 +154,16 @@ mdunlink(RelFileNode rnode)
/* Delete the first segment, or only segment if not doing segmenting */ /* Delete the first segment, or only segment if not doing segmenting */
if (unlink(path) < 0) if (unlink(path) < 0)
{ {
status = SM_FAIL; if (!isRedo || errno != ENOENT)
save_errno = errno; {
status = false;
save_errno = errno;
}
} }
#ifndef LET_OS_MANAGE_FILESIZE #ifndef LET_OS_MANAGE_FILESIZE
/* Get the additional segments, if any */ /* Get the additional segments, if any */
if (status == SM_SUCCESS) if (status)
{ {
char *segpath = (char *) palloc(strlen(path) + 12); char *segpath = (char *) palloc(strlen(path) + 12);
BlockNumber segno; BlockNumber segno;
...@@ -198,7 +176,7 @@ mdunlink(RelFileNode rnode) ...@@ -198,7 +176,7 @@ mdunlink(RelFileNode rnode)
/* ENOENT is expected after the last segment... */ /* ENOENT is expected after the last segment... */
if (errno != ENOENT) if (errno != ENOENT)
{ {
status = SM_FAIL; status = false;
save_errno = errno; save_errno = errno;
} }
break; break;
...@@ -222,16 +200,15 @@ mdunlink(RelFileNode rnode) ...@@ -222,16 +200,15 @@ mdunlink(RelFileNode rnode)
* relation (ie, blocknum is the current EOF), and so in case of * relation (ie, blocknum is the current EOF), and so in case of
* failure we clean up by truncating. * failure we clean up by truncating.
* *
* This routine returns SM_FAIL or SM_SUCCESS, with errno set as * This routine returns true or false, with errno set as appropriate.
* appropriate.
* *
* Note: this routine used to call mdnblocks() to get the block position * Note: this routine used to call mdnblocks() to get the block position
* to write at, but that's pretty silly since the caller needs to know where * to write at, but that's pretty silly since the caller needs to know where
* the block will be written, and accordingly must have done mdnblocks() * the block will be written, and accordingly must have done mdnblocks()
* already. Might as well pass in the position and save a seek. * already. Might as well pass in the position and save a seek.
*/ */
int bool
mdextend(Relation reln, BlockNumber blocknum, char *buffer) mdextend(SMgrRelation reln, BlockNumber blocknum, char *buffer)
{ {
long seekpos; long seekpos;
int nbytes; int nbytes;
...@@ -256,7 +233,7 @@ mdextend(Relation reln, BlockNumber blocknum, char *buffer) ...@@ -256,7 +233,7 @@ mdextend(Relation reln, BlockNumber blocknum, char *buffer)
* to make room for the new page's buffer. * to make room for the new page's buffer.
*/ */
if (FileSeek(v->mdfd_vfd, seekpos, SEEK_SET) != seekpos) if (FileSeek(v->mdfd_vfd, seekpos, SEEK_SET) != seekpos)
return SM_FAIL; return false;
if ((nbytes = FileWrite(v->mdfd_vfd, buffer, BLCKSZ)) != BLCKSZ) if ((nbytes = FileWrite(v->mdfd_vfd, buffer, BLCKSZ)) != BLCKSZ)
{ {
...@@ -269,29 +246,32 @@ mdextend(Relation reln, BlockNumber blocknum, char *buffer) ...@@ -269,29 +246,32 @@ mdextend(Relation reln, BlockNumber blocknum, char *buffer)
FileSeek(v->mdfd_vfd, seekpos, SEEK_SET); FileSeek(v->mdfd_vfd, seekpos, SEEK_SET);
errno = save_errno; errno = save_errno;
} }
return SM_FAIL; return false;
} }
#ifndef LET_OS_MANAGE_FILESIZE #ifndef LET_OS_MANAGE_FILESIZE
Assert(_mdnblocks(v->mdfd_vfd, BLCKSZ) <= ((BlockNumber) RELSEG_SIZE)); Assert(_mdnblocks(v->mdfd_vfd, BLCKSZ) <= ((BlockNumber) RELSEG_SIZE));
#endif #endif
return SM_SUCCESS; return true;
} }
/* /*
* mdopen() -- Open the specified relation. * mdopen() -- Open the specified relation. ereport's on failure.
*
* Note we only open the first segment, when there are multiple segments.
*/ */
int static MdfdVec *
mdopen(Relation reln) mdopen(SMgrRelation reln)
{ {
char *path; char *path;
int fd; File fd;
int vfd;
Assert(reln->rd_fd < 0); /* No work if already open */
if (reln->md_fd)
return reln->md_fd;
path = relpath(reln->rd_node); path = relpath(reln->smgr_rnode);
fd = FileNameOpenFile(path, O_RDWR | PG_BINARY, 0600); fd = FileNameOpenFile(path, O_RDWR | PG_BINARY, 0600);
...@@ -309,57 +289,45 @@ mdopen(Relation reln) ...@@ -309,57 +289,45 @@ mdopen(Relation reln)
if (fd < 0) if (fd < 0)
{ {
pfree(path); pfree(path);
return -1; ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not open relation %u/%u: %m",
reln->smgr_rnode.tblNode,
reln->smgr_rnode.relNode)));
} }
} }
pfree(path); pfree(path);
vfd = _fdvec_alloc(); reln->md_fd = _fdvec_alloc();
if (vfd < 0)
return -1;
Md_fdvec[vfd].mdfd_vfd = fd; reln->md_fd->mdfd_vfd = fd;
Md_fdvec[vfd].mdfd_flags = (uint16) 0;
#ifndef LET_OS_MANAGE_FILESIZE #ifndef LET_OS_MANAGE_FILESIZE
Md_fdvec[vfd].mdfd_chain = NULL; reln->md_fd->mdfd_chain = NULL;
Assert(_mdnblocks(fd, BLCKSZ) <= ((BlockNumber) RELSEG_SIZE)); Assert(_mdnblocks(fd, BLCKSZ) <= ((BlockNumber) RELSEG_SIZE));
#endif #endif
return vfd; return reln->md_fd;
} }
/* /*
* mdclose() -- Close the specified relation, if it isn't closed already. * mdclose() -- Close the specified relation, if it isn't closed already.
* *
* AND FREE fd vector! It may be re-used for other relations! * Returns true or false with errno set as appropriate.
* reln should be flushed from cache after closing !..
*
* Returns SM_SUCCESS or SM_FAIL with errno set as appropriate.
*/ */
int bool
mdclose(Relation reln) mdclose(SMgrRelation reln)
{ {
int fd; MdfdVec *v = reln->md_fd;
fd = RelationGetFile(reln);
if (fd < 0)
return SM_SUCCESS; /* already closed, so no work */
mdclose_fd(fd);
reln->rd_fd = -1;
return SM_SUCCESS; /* No work if already closed */
} if (v == NULL)
return true;
static void reln->md_fd = NULL; /* prevent dangling pointer after error */
mdclose_fd(int fd)
{
MdfdVec *v;
#ifndef LET_OS_MANAGE_FILESIZE #ifndef LET_OS_MANAGE_FILESIZE
for (v = &Md_fdvec[fd]; v != NULL;) while (v != NULL)
{ {
MdfdVec *ov = v; MdfdVec *ov = v;
...@@ -368,32 +336,24 @@ mdclose_fd(int fd) ...@@ -368,32 +336,24 @@ mdclose_fd(int fd)
FileClose(v->mdfd_vfd); FileClose(v->mdfd_vfd);
/* Now free vector */ /* Now free vector */
v = v->mdfd_chain; v = v->mdfd_chain;
if (ov != &Md_fdvec[fd]) pfree(ov);
pfree(ov);
} }
Md_fdvec[fd].mdfd_chain = NULL;
#else #else
v = &Md_fdvec[fd]; if (v->mdfd_vfd >= 0)
if (v != NULL) FileClose(v->mdfd_vfd);
{ pfree(v);
if (v->mdfd_vfd >= 0)
FileClose(v->mdfd_vfd);
}
#endif #endif
_fdvec_free(fd); return true;
} }
/* /*
* mdread() -- Read the specified block from a relation. * mdread() -- Read the specified block from a relation.
*
* Returns SM_SUCCESS or SM_FAIL.
*/ */
int bool
mdread(Relation reln, BlockNumber blocknum, char *buffer) mdread(SMgrRelation reln, BlockNumber blocknum, char *buffer)
{ {
int status; bool status;
long seekpos; long seekpos;
int nbytes; int nbytes;
MdfdVec *v; MdfdVec *v;
...@@ -408,9 +368,9 @@ mdread(Relation reln, BlockNumber blocknum, char *buffer) ...@@ -408,9 +368,9 @@ mdread(Relation reln, BlockNumber blocknum, char *buffer)
#endif #endif
if (FileSeek(v->mdfd_vfd, seekpos, SEEK_SET) != seekpos) if (FileSeek(v->mdfd_vfd, seekpos, SEEK_SET) != seekpos)
return SM_FAIL; return false;
status = SM_SUCCESS; status = true;
if ((nbytes = FileRead(v->mdfd_vfd, buffer, BLCKSZ)) != BLCKSZ) if ((nbytes = FileRead(v->mdfd_vfd, buffer, BLCKSZ)) != BLCKSZ)
{ {
/* /*
...@@ -425,7 +385,7 @@ mdread(Relation reln, BlockNumber blocknum, char *buffer) ...@@ -425,7 +385,7 @@ mdread(Relation reln, BlockNumber blocknum, char *buffer)
(nbytes > 0 && mdnblocks(reln) == blocknum)) (nbytes > 0 && mdnblocks(reln) == blocknum))
MemSet(buffer, 0, BLCKSZ); MemSet(buffer, 0, BLCKSZ);
else else
status = SM_FAIL; status = false;
} }
return status; return status;
...@@ -433,11 +393,9 @@ mdread(Relation reln, BlockNumber blocknum, char *buffer) ...@@ -433,11 +393,9 @@ mdread(Relation reln, BlockNumber blocknum, char *buffer)
/* /*
* mdwrite() -- Write the supplied block at the appropriate location. * mdwrite() -- Write the supplied block at the appropriate location.
*
* Returns SM_SUCCESS or SM_FAIL.
*/ */
int bool
mdwrite(Relation reln, BlockNumber blocknum, char *buffer) mdwrite(SMgrRelation reln, BlockNumber blocknum, char *buffer)
{ {
long seekpos; long seekpos;
MdfdVec *v; MdfdVec *v;
...@@ -452,69 +410,12 @@ mdwrite(Relation reln, BlockNumber blocknum, char *buffer) ...@@ -452,69 +410,12 @@ mdwrite(Relation reln, BlockNumber blocknum, char *buffer)
#endif #endif
if (FileSeek(v->mdfd_vfd, seekpos, SEEK_SET) != seekpos) if (FileSeek(v->mdfd_vfd, seekpos, SEEK_SET) != seekpos)
return SM_FAIL; return false;
if (FileWrite(v->mdfd_vfd, buffer, BLCKSZ) != BLCKSZ) if (FileWrite(v->mdfd_vfd, buffer, BLCKSZ) != BLCKSZ)
return SM_FAIL; return false;
return SM_SUCCESS; return true;
}
/*
* mdblindwrt() -- Write a block to disk blind.
*
* We have to be able to do this using only the rnode of the relation
* in which the block belongs. Otherwise this is much like mdwrite().
*/
int
mdblindwrt(RelFileNode rnode,
BlockNumber blkno,
char *buffer)
{
int status;
long seekpos;
int fd;
fd = _mdfd_blind_getseg(rnode, blkno);
if (fd < 0)
return SM_FAIL;
#ifndef LET_OS_MANAGE_FILESIZE
seekpos = (long) (BLCKSZ * (blkno % ((BlockNumber) RELSEG_SIZE)));
Assert(seekpos < BLCKSZ * RELSEG_SIZE);
#else
seekpos = (long) (BLCKSZ * (blkno));
#endif
errno = 0;
if (lseek(fd, seekpos, SEEK_SET) != seekpos)
{
elog(LOG, "lseek(%ld) failed: %m", seekpos);
close(fd);
return SM_FAIL;
}
status = SM_SUCCESS;
/* write the block */
errno = 0;
if (write(fd, buffer, BLCKSZ) != BLCKSZ)
{
/* if write didn't set errno, assume problem is no disk space */
if (errno == 0)
errno = ENOSPC;
elog(LOG, "write() failed: %m");
status = SM_FAIL;
}
if (close(fd) < 0)
{
elog(LOG, "close() failed: %m");
status = SM_FAIL;
}
return status;
} }
/* /*
...@@ -525,24 +426,16 @@ mdblindwrt(RelFileNode rnode, ...@@ -525,24 +426,16 @@ mdblindwrt(RelFileNode rnode,
* called, then only segments up to the last one actually touched * called, then only segments up to the last one actually touched
* are present in the chain... * are present in the chain...
* *
* Returns # of blocks, ereport's on error. * Returns # of blocks, or InvalidBlockNumber on error.
*/ */
BlockNumber BlockNumber
mdnblocks(Relation reln) mdnblocks(SMgrRelation reln)
{ {
int fd; MdfdVec *v = mdopen(reln);
MdfdVec *v;
#ifndef LET_OS_MANAGE_FILESIZE #ifndef LET_OS_MANAGE_FILESIZE
BlockNumber nblocks; BlockNumber nblocks;
BlockNumber segno; BlockNumber segno = 0;
#endif
fd = _mdfd_getrelnfd(reln);
v = &Md_fdvec[fd];
#ifndef LET_OS_MANAGE_FILESIZE
segno = 0;
/* /*
* Skip through any segments that aren't the last one, to avoid * Skip through any segments that aren't the last one, to avoid
...@@ -583,8 +476,7 @@ mdnblocks(Relation reln) ...@@ -583,8 +476,7 @@ mdnblocks(Relation reln)
*/ */
v->mdfd_chain = _mdfd_openseg(reln, segno, O_CREAT); v->mdfd_chain = _mdfd_openseg(reln, segno, O_CREAT);
if (v->mdfd_chain == NULL) if (v->mdfd_chain == NULL)
elog(ERROR, "could not count blocks of \"%s\": %m", return InvalidBlockNumber; /* failed? */
RelationGetRelationName(reln));
} }
v = v->mdfd_chain; v = v->mdfd_chain;
...@@ -600,9 +492,8 @@ mdnblocks(Relation reln) ...@@ -600,9 +492,8 @@ mdnblocks(Relation reln)
* Returns # of blocks or InvalidBlockNumber on error. * Returns # of blocks or InvalidBlockNumber on error.
*/ */
BlockNumber BlockNumber
mdtruncate(Relation reln, BlockNumber nblocks) mdtruncate(SMgrRelation reln, BlockNumber nblocks)
{ {
int fd;
MdfdVec *v; MdfdVec *v;
BlockNumber curnblk; BlockNumber curnblk;
...@@ -615,13 +506,14 @@ mdtruncate(Relation reln, BlockNumber nblocks) ...@@ -615,13 +506,14 @@ mdtruncate(Relation reln, BlockNumber nblocks)
* that truncate/delete loop will get them all! * that truncate/delete loop will get them all!
*/ */
curnblk = mdnblocks(reln); curnblk = mdnblocks(reln);
if (curnblk == InvalidBlockNumber)
return InvalidBlockNumber; /* mdnblocks failed */
if (nblocks > curnblk) if (nblocks > curnblk)
return InvalidBlockNumber; /* bogus request */ return InvalidBlockNumber; /* bogus request */
if (nblocks == curnblk) if (nblocks == curnblk)
return nblocks; /* no work */ return nblocks; /* no work */
fd = _mdfd_getrelnfd(reln); v = mdopen(reln);
v = &Md_fdvec[fd];
#ifndef LET_OS_MANAGE_FILESIZE #ifndef LET_OS_MANAGE_FILESIZE
priorblocks = 0; priorblocks = 0;
...@@ -641,7 +533,7 @@ mdtruncate(Relation reln, BlockNumber nblocks) ...@@ -641,7 +533,7 @@ mdtruncate(Relation reln, BlockNumber nblocks)
FileTruncate(v->mdfd_vfd, 0); FileTruncate(v->mdfd_vfd, 0);
FileUnlink(v->mdfd_vfd); FileUnlink(v->mdfd_vfd);
v = v->mdfd_chain; v = v->mdfd_chain;
Assert(ov != &Md_fdvec[fd]); /* we never drop the 1st Assert(ov != reln->md_fd); /* we never drop the 1st
* segment */ * segment */
pfree(ov); pfree(ov);
} }
...@@ -682,115 +574,65 @@ mdtruncate(Relation reln, BlockNumber nblocks) ...@@ -682,115 +574,65 @@ mdtruncate(Relation reln, BlockNumber nblocks)
/* /*
* mdcommit() -- Commit a transaction. * mdcommit() -- Commit a transaction.
*
* Returns SM_SUCCESS or SM_FAIL with errno set as appropriate.
*/ */
int bool
mdcommit(void) mdcommit(void)
{ {
/* /*
* We don't actually have to do anything here... * We don't actually have to do anything here...
*/ */
return SM_SUCCESS; return true;
} }
/* /*
* mdabort() -- Abort a transaction. * mdabort() -- Abort a transaction.
*/ */
int bool
mdabort(void) mdabort(void)
{ {
/* /*
* We don't actually have to do anything here... * We don't actually have to do anything here...
*/ */
return SM_SUCCESS; return true;
} }
/* /*
* mdsync() -- Sync previous writes to stable storage. * mdsync() -- Sync previous writes to stable storage.
*/ */
int bool
mdsync(void) mdsync(void)
{ {
sync(); sync();
if (IsUnderPostmaster) if (IsUnderPostmaster)
sleep(2); sleep(2);
sync(); sync();
return SM_SUCCESS; return true;
} }
/* /*
* _fdvec_alloc() -- Grab a free (or new) md file descriptor vector. * _fdvec_alloc() -- Make a MdfdVec object.
*/ */
static int static MdfdVec *
_fdvec_alloc(void) _fdvec_alloc(void)
{ {
MdfdVec *nvec; MdfdVec *v;
int fdvec,
i;
if (Md_Free >= 0) /* get from free list */
{
fdvec = Md_Free;
Md_Free = Md_fdvec[fdvec].mdfd_nextFree;
Assert(Md_fdvec[fdvec].mdfd_flags == MDFD_FREE);
Md_fdvec[fdvec].mdfd_flags = 0;
if (fdvec >= CurFd)
{
Assert(fdvec == CurFd);
CurFd++;
}
return fdvec;
}
/* Must allocate more room */
if (Nfds != CurFd)
elog(FATAL, "_fdvec_alloc error");
Nfds *= 2;
nvec = (MdfdVec *) MemoryContextAlloc(MdCxt, Nfds * sizeof(MdfdVec));
MemSet(nvec, 0, Nfds * sizeof(MdfdVec));
memcpy(nvec, (char *) Md_fdvec, CurFd * sizeof(MdfdVec));
pfree(Md_fdvec);
Md_fdvec = nvec; v = (MdfdVec *) MemoryContextAlloc(MdCxt, sizeof(MdfdVec));
v->mdfd_vfd = -1;
/* Set new free list */ #ifndef LET_OS_MANAGE_FILESIZE
for (i = CurFd; i < Nfds; i++) v->mdfd_chain = NULL;
{ #endif
Md_fdvec[i].mdfd_nextFree = i + 1;
Md_fdvec[i].mdfd_flags = MDFD_FREE;
}
Md_fdvec[Nfds - 1].mdfd_nextFree = -1;
Md_Free = CurFd + 1;
fdvec = CurFd;
CurFd++;
Md_fdvec[fdvec].mdfd_flags = 0;
return fdvec; return v;
} }
#ifndef LET_OS_MANAGE_FILESIZE
/* /*
* _fdvec_free() -- free md file descriptor vector. * Open the specified segment of the relation,
* * and make a MdfdVec object for it. Returns NULL on failure.
*/ */
static
void
_fdvec_free(int fdvec)
{
Assert(Md_Free < 0 || Md_fdvec[Md_Free].mdfd_flags == MDFD_FREE);
Assert(Md_fdvec[fdvec].mdfd_flags != MDFD_FREE);
Md_fdvec[fdvec].mdfd_nextFree = Md_Free;
Md_fdvec[fdvec].mdfd_flags = MDFD_FREE;
Md_Free = fdvec;
}
static MdfdVec * static MdfdVec *
_mdfd_openseg(Relation reln, BlockNumber segno, int oflags) _mdfd_openseg(SMgrRelation reln, BlockNumber segno, int oflags)
{ {
MdfdVec *v; MdfdVec *v;
int fd; int fd;
...@@ -798,7 +640,7 @@ _mdfd_openseg(Relation reln, BlockNumber segno, int oflags) ...@@ -798,7 +640,7 @@ _mdfd_openseg(Relation reln, BlockNumber segno, int oflags)
*fullpath; *fullpath;
/* be sure we have enough space for the '.segno', if any */ /* be sure we have enough space for the '.segno', if any */
path = relpath(reln->rd_node); path = relpath(reln->smgr_rnode);
if (segno > 0) if (segno > 0)
{ {
...@@ -818,61 +660,32 @@ _mdfd_openseg(Relation reln, BlockNumber segno, int oflags) ...@@ -818,61 +660,32 @@ _mdfd_openseg(Relation reln, BlockNumber segno, int oflags)
return NULL; return NULL;
/* allocate an mdfdvec entry for it */ /* allocate an mdfdvec entry for it */
v = (MdfdVec *) MemoryContextAlloc(MdCxt, sizeof(MdfdVec)); v = _fdvec_alloc();
/* fill the entry */ /* fill the entry */
v->mdfd_vfd = fd; v->mdfd_vfd = fd;
v->mdfd_flags = (uint16) 0;
#ifndef LET_OS_MANAGE_FILESIZE
v->mdfd_chain = NULL; v->mdfd_chain = NULL;
Assert(_mdnblocks(fd, BLCKSZ) <= ((BlockNumber) RELSEG_SIZE)); Assert(_mdnblocks(fd, BLCKSZ) <= ((BlockNumber) RELSEG_SIZE));
#endif
/* all done */ /* all done */
return v; return v;
} }
#endif
/*
* _mdfd_getrelnfd() -- Get the (virtual) fd for the relation,
* opening it if it's not already open
*
*/
static int
_mdfd_getrelnfd(Relation reln)
{
int fd;
fd = RelationGetFile(reln);
if (fd < 0)
{
if ((fd = mdopen(reln)) < 0)
elog(ERROR, "could not open relation \"%s\": %m",
RelationGetRelationName(reln));
reln->rd_fd = fd;
}
return fd;
}
/* /*
* _mdfd_getseg() -- Find the segment of the relation holding the * _mdfd_getseg() -- Find the segment of the relation holding the
* specified block * specified block. ereport's on failure.
*
*/ */
static MdfdVec * static MdfdVec *
_mdfd_getseg(Relation reln, BlockNumber blkno) _mdfd_getseg(SMgrRelation reln, BlockNumber blkno)
{ {
MdfdVec *v; MdfdVec *v = mdopen(reln);
int fd;
#ifndef LET_OS_MANAGE_FILESIZE #ifndef LET_OS_MANAGE_FILESIZE
BlockNumber segno; BlockNumber segno;
BlockNumber i; BlockNumber i;
#endif
fd = _mdfd_getrelnfd(reln);
#ifndef LET_OS_MANAGE_FILESIZE for (segno = blkno / ((BlockNumber) RELSEG_SIZE), i = 1;
for (v = &Md_fdvec[fd], segno = blkno / ((BlockNumber) RELSEG_SIZE), i = 1;
segno > 0; segno > 0;
i++, segno--) i++, segno--)
{ {
...@@ -892,65 +705,24 @@ _mdfd_getseg(Relation reln, BlockNumber blkno) ...@@ -892,65 +705,24 @@ _mdfd_getseg(Relation reln, BlockNumber blkno)
v->mdfd_chain = _mdfd_openseg(reln, i, (segno == 1) ? O_CREAT : 0); v->mdfd_chain = _mdfd_openseg(reln, i, (segno == 1) ? O_CREAT : 0);
if (v->mdfd_chain == NULL) if (v->mdfd_chain == NULL)
elog(ERROR, "could not open segment %u of relation \"%s\" (target block %u): %m", ereport(ERROR,
i, RelationGetRelationName(reln), blkno); (errcode_for_file_access(),
errmsg("could not open segment %u of relation %u/%u (target block %u): %m",
i,
reln->smgr_rnode.tblNode,
reln->smgr_rnode.relNode,
blkno)));
} }
v = v->mdfd_chain; v = v->mdfd_chain;
} }
#else
v = &Md_fdvec[fd];
#endif #endif
return v; return v;
} }
/* /*
* Find the segment of the relation holding the specified block. * Get number of blocks present in a single disk file
*
* This performs the same work as _mdfd_getseg() except that we must work
* "blind" with no Relation struct. We assume that we are not likely to
* touch the same relation again soon, so we do not create an FD entry for
* the relation --- we just open a kernel file descriptor which will be
* used and promptly closed. We also assume that the target block already
* exists, ie, we need not extend the relation.
*
* The return value is the kernel descriptor, or -1 on failure.
*/ */
static int
_mdfd_blind_getseg(RelFileNode rnode, BlockNumber blkno)
{
char *path;
int fd;
#ifndef LET_OS_MANAGE_FILESIZE
BlockNumber segno;
#endif
path = relpath(rnode);
#ifndef LET_OS_MANAGE_FILESIZE
/* append the '.segno', if needed */
segno = blkno / ((BlockNumber) RELSEG_SIZE);
if (segno > 0)
{
char *segpath = (char *) palloc(strlen(path) + 12);
sprintf(segpath, "%s.%u", path, segno);
pfree(path);
path = segpath;
}
#endif
/* call fd.c to allow other FDs to be closed if needed */
fd = BasicOpenFile(path, O_RDWR | PG_BINARY, 0600);
if (fd < 0)
elog(LOG, "could not open \"%s\": %m", path);
pfree(path);
return fd;
}
static BlockNumber static BlockNumber
_mdnblocks(File file, Size blcksz) _mdnblocks(File file, Size blcksz)
{ {
......
/*-------------------------------------------------------------------------
*
* mm.c
* main memory storage manager
*
* This code manages relations that reside in (presumably stable)
* main memory.
*
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/smgr/mm.c,v 1.36 2004/01/07 18:56:27 neilc Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include <math.h>
#include "storage/smgr.h"
#include "miscadmin.h"
#ifdef STABLE_MEMORY_STORAGE
/*
* MMCacheTag -- Unique triplet for blocks stored by the main memory
* storage manager.
*/
typedef struct MMCacheTag
{
Oid mmct_dbid;
Oid mmct_relid;
BlockNumber mmct_blkno;
} MMCacheTag;
/*
* Shared-memory hash table for main memory relations contains
* entries of this form.
*/
typedef struct MMHashEntry
{
MMCacheTag mmhe_tag;
int mmhe_bufno;
} MMHashEntry;
/*
* MMRelTag -- Unique identifier for each relation that is stored in the
* main-memory storage manager.
*/
typedef struct MMRelTag
{
Oid mmrt_dbid;
Oid mmrt_relid;
} MMRelTag;
/*
* Shared-memory hash table for # blocks in main memory relations contains
* entries of this form.
*/
typedef struct MMRelHashEntry
{
MMRelTag mmrhe_tag;
int mmrhe_nblocks;
} MMRelHashEntry;
#define MMNBUFFERS 10
#define MMNRELATIONS 2
static int *MMCurTop;
static int *MMCurRelno;
static MMCacheTag *MMBlockTags;
static char *MMBlockCache;
static HTAB *MMCacheHT;
static HTAB *MMRelCacheHT;
int
mminit(void)
{
char *mmcacheblk;
int mmsize = 0;
bool found;
HASHCTL info;
LWLockAcquire(MMCacheLock, LW_EXCLUSIVE);
mmsize += MAXALIGN(BLCKSZ * MMNBUFFERS);
mmsize += MAXALIGN(sizeof(*MMCurTop));
mmsize += MAXALIGN(sizeof(*MMCurRelno));
mmsize += MAXALIGN((MMNBUFFERS * sizeof(MMCacheTag)));
mmcacheblk = (char *) ShmemInitStruct("Main memory smgr", mmsize, &found);
if (mmcacheblk == NULL)
{
LWLockRelease(MMCacheLock);
return SM_FAIL;
}
info.keysize = sizeof(MMCacheTag);
info.entrysize = sizeof(MMHashEntry);
info.hash = tag_hash;
MMCacheHT = ShmemInitHash("Main memory store HT",
MMNBUFFERS, MMNBUFFERS,
&info, (HASH_ELEM | HASH_FUNCTION));
if (MMCacheHT == NULL)
{
LWLockRelease(MMCacheLock);
return SM_FAIL;
}
info.keysize = sizeof(MMRelTag);
info.entrysize = sizeof(MMRelHashEntry);
info.hash = tag_hash;
MMRelCacheHT = ShmemInitHash("Main memory rel HT",
MMNRELATIONS, MMNRELATIONS,
&info, (HASH_ELEM | HASH_FUNCTION));
if (MMRelCacheHT == NULL)
{
LWLockRelease(MMCacheLock);
return SM_FAIL;
}
if (IsUnderPostmaster) /* was IsPostmaster bjm */
{
MemSet(mmcacheblk, 0, mmsize);
LWLockRelease(MMCacheLock);
return SM_SUCCESS;
}
LWLockRelease(MMCacheLock);
MMCurTop = (int *) mmcacheblk;
mmcacheblk += sizeof(int);
MMCurRelno = (int *) mmcacheblk;
mmcacheblk += sizeof(int);
MMBlockTags = (MMCacheTag *) mmcacheblk;
mmcacheblk += (MMNBUFFERS * sizeof(MMCacheTag));
MMBlockCache = mmcacheblk;
return SM_SUCCESS;
}
int
mmshutdown(void)
{
return SM_SUCCESS;
}
int
mmcreate(Relation reln)
{
MMRelHashEntry *entry;
bool found;
MMRelTag tag;
LWLockAcquire(MMCacheLock, LW_EXCLUSIVE);
if (*MMCurRelno == MMNRELATIONS)
{
LWLockRelease(MMCacheLock);
return SM_FAIL;
}
(*MMCurRelno)++;
tag.mmrt_relid = RelationGetRelid(reln);
if (reln->rd_rel->relisshared)
tag.mmrt_dbid = (Oid) 0;
else
tag.mmrt_dbid = MyDatabaseId;
entry = (MMRelHashEntry *) hash_search(MMRelCacheHT,
(void *) &tag,
HASH_ENTER, &found);
if (entry == NULL)
{
LWLockRelease(MMCacheLock);
ereport(FATAL,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of memory")));
}
if (found)
{
/* already exists */
LWLockRelease(MMCacheLock);
return SM_FAIL;
}
entry->mmrhe_nblocks = 0;
LWLockRelease(MMCacheLock);
return SM_SUCCESS;
}
/*
* mmunlink() -- Unlink a relation.
*
* XXX currently broken: needs to accept RelFileNode, not Relation
*/
int
mmunlink(RelFileNode rnode)
{
int i;
MMHashEntry *entry;
MMRelHashEntry *rentry;
MMRelTag rtag;
LWLockAcquire(MMCacheLock, LW_EXCLUSIVE);
for (i = 0; i < MMNBUFFERS; i++)
{
if (MMBlockTags[i].mmct_dbid == rnode.tblNode
&& MMBlockTags[i].mmct_relid == rnode.relNode)
{
entry = (MMHashEntry *) hash_search(MMCacheHT,
(void *) &MMBlockTags[i],
HASH_REMOVE, NULL);
if (entry == NULL)
{
LWLockRelease(MMCacheLock);
elog(FATAL, "cache hash table corrupted");
}
MMBlockTags[i].mmct_dbid = (Oid) 0;
MMBlockTags[i].mmct_relid = (Oid) 0;
MMBlockTags[i].mmct_blkno = (BlockNumber) 0;
}
}
rtag.mmrt_dbid = rnode.tblNode;
rtag.mmrt_relid = rnode.relNode;
rentry = (MMRelHashEntry *) hash_search(MMRelCacheHT,
(void *) &rtag,
HASH_REMOVE, NULL);
if (rentry == NULL)
{
LWLockRelease(MMCacheLock);
elog(FATAL, "rel cache hash table corrupted");
}
(*MMCurRelno)--;
LWLockRelease(MMCacheLock);
return 1;
}
/*
* mmextend() -- Add a block to the specified relation.
*
* This routine returns SM_FAIL or SM_SUCCESS, with errno set as
* appropriate.
*/
int
mmextend(Relation reln, BlockNumber blocknum, char *buffer)
{
MMRelHashEntry *rentry;
MMHashEntry *entry;
int i;
Oid reldbid;
int offset;
bool found;
MMRelTag rtag;
MMCacheTag tag;
if (reln->rd_rel->relisshared)
reldbid = (Oid) 0;
else
reldbid = MyDatabaseId;
tag.mmct_dbid = rtag.mmrt_dbid = reldbid;
tag.mmct_relid = rtag.mmrt_relid = RelationGetRelid(reln);
LWLockAcquire(MMCacheLock, LW_EXCLUSIVE);
if (*MMCurTop == MMNBUFFERS)
{
for (i = 0; i < MMNBUFFERS; i++)
{
if (MMBlockTags[i].mmct_dbid == 0 &&
MMBlockTags[i].mmct_relid == 0)
break;
}
if (i == MMNBUFFERS)
{
LWLockRelease(MMCacheLock);
return SM_FAIL;
}
}
else
{
i = *MMCurTop;
(*MMCurTop)++;
}
rentry = (MMRelHashEntry *) hash_search(MMRelCacheHT,
(void *) &rtag,
HASH_FIND, NULL);
if (rentry == NULL)
{
LWLockRelease(MMCacheLock);
elog(FATAL, "rel cache hash table corrupted");
}
tag.mmct_blkno = rentry->mmrhe_nblocks;
entry = (MMHashEntry *) hash_search(MMCacheHT,
(void *) &tag,
HASH_ENTER, &found);
if (entry == NULL || found)
{
LWLockRelease(MMCacheLock);
elog(FATAL, "cache hash table corrupted");
}
entry->mmhe_bufno = i;
MMBlockTags[i].mmct_dbid = reldbid;
MMBlockTags[i].mmct_relid = RelationGetRelid(reln);
MMBlockTags[i].mmct_blkno = rentry->mmrhe_nblocks;
/* page numbers are zero-based, so we increment this at the end */
(rentry->mmrhe_nblocks)++;
/* write the extended page */
offset = (i * BLCKSZ);
memmove(&(MMBlockCache[offset]), buffer, BLCKSZ);
LWLockRelease(MMCacheLock);
return SM_SUCCESS;
}
/*
* mmopen() -- Open the specified relation.
*/
int
mmopen(Relation reln)
{
/* automatically successful */
return 0;
}
/*
* mmclose() -- Close the specified relation.
*
* Returns SM_SUCCESS or SM_FAIL with errno set as appropriate.
*/
int
mmclose(Relation reln)
{
/* automatically successful */
return SM_SUCCESS;
}
/*
* mmread() -- Read the specified block from a relation.
*
* Returns SM_SUCCESS or SM_FAIL.
*/
int
mmread(Relation reln, BlockNumber blocknum, char *buffer)
{
MMHashEntry *entry;
int offset;
MMCacheTag tag;
if (reln->rd_rel->relisshared)
tag.mmct_dbid = (Oid) 0;
else
tag.mmct_dbid = MyDatabaseId;
tag.mmct_relid = RelationGetRelid(reln);
tag.mmct_blkno = blocknum;
LWLockAcquire(MMCacheLock, LW_EXCLUSIVE);
entry = (MMHashEntry *) hash_search(MMCacheHT,
(void *) &tag,
HASH_FIND, NULL);
if (entry == NULL)
{
/* reading nonexistent pages is defined to fill them with zeroes */
LWLockRelease(MMCacheLock);
MemSet(buffer, 0, BLCKSZ);
return SM_SUCCESS;
}
offset = (entry->mmhe_bufno * BLCKSZ);
memmove(buffer, &MMBlockCache[offset], BLCKSZ);
LWLockRelease(MMCacheLock);
return SM_SUCCESS;
}
/*
* mmwrite() -- Write the supplied block at the appropriate location.
*
* Returns SM_SUCCESS or SM_FAIL.
*/
int
mmwrite(Relation reln, BlockNumber blocknum, char *buffer)
{
MMHashEntry *entry;
int offset;
MMCacheTag tag;
if (reln->rd_rel->relisshared)
tag.mmct_dbid = (Oid) 0;
else
tag.mmct_dbid = MyDatabaseId;
tag.mmct_relid = RelationGetRelid(reln);
tag.mmct_blkno = blocknum;
LWLockAcquire(MMCacheLock, LW_EXCLUSIVE);
entry = (MMHashEntry *) hash_search(MMCacheHT,
(void *) &tag,
HASH_FIND, NULL);
if (entry == NULL)
{
LWLockRelease(MMCacheLock);
elog(FATAL, "cache hash table missing requested page");
}
offset = (entry->mmhe_bufno * BLCKSZ);
memmove(&MMBlockCache[offset], buffer, BLCKSZ);
LWLockRelease(MMCacheLock);
return SM_SUCCESS;
}
/*
* mmblindwrt() -- Write a block to stable storage blind.
*
* We have to be able to do this using only the rnode of the relation
* in which the block belongs. Otherwise this is much like mmwrite().
*/
int
mmblindwrt(RelFileNode rnode,
BlockNumber blkno,
char *buffer)
{
return SM_FAIL;
}
/*
* mmnblocks() -- Get the number of blocks stored in a relation.
*
* Returns # of blocks or InvalidBlockNumber on error.
*/
BlockNumber
mmnblocks(Relation reln)
{
MMRelTag rtag;
MMRelHashEntry *rentry;
BlockNumber nblocks;
if (reln->rd_rel->relisshared)
rtag.mmrt_dbid = (Oid) 0;
else
rtag.mmrt_dbid = MyDatabaseId;
rtag.mmrt_relid = RelationGetRelid(reln);
LWLockAcquire(MMCacheLock, LW_EXCLUSIVE);
rentry = (MMRelHashEntry *) hash_search(MMRelCacheHT,
(void *) &rtag,
HASH_FIND, NULL);
if (rentry)
nblocks = rentry->mmrhe_nblocks;
else
nblocks = InvalidBlockNumber;
LWLockRelease(MMCacheLock);
return nblocks;
}
/*
* mmcommit() -- Commit a transaction.
*
* Returns SM_SUCCESS or SM_FAIL with errno set as appropriate.
*/
int
mmcommit(void)
{
return SM_SUCCESS;
}
/*
* mmabort() -- Abort a transaction.
*/
int
mmabort(void)
{
return SM_SUCCESS;
}
/*
* MMShmemSize() -- Declare amount of shared memory we require.
*
* The shared memory initialization code creates a block of shared
* memory exactly big enough to hold all the structures it needs to.
* This routine declares how much space the main memory storage
* manager will use.
*/
int
MMShmemSize(void)
{
int size = 0;
/*
* first compute space occupied by the (dbid,relid,blkno) hash table
*/
size += hash_estimate_size(MMNBUFFERS, sizeof(MMHashEntry));
/*
* now do the same for the rel hash table
*/
size += hash_estimate_size(MMNRELATIONS, sizeof(MMRelHashEntry));
/*
* finally, add in the memory block we use directly
*/
size += MAXALIGN(BLCKSZ * MMNBUFFERS);
size += MAXALIGN(sizeof(*MMCurTop));
size += MAXALIGN(sizeof(*MMCurRelno));
size += MAXALIGN(MMNBUFFERS * sizeof(MMCacheTag));
return size;
}
#endif /* STABLE_MEMORY_STORAGE */
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/smgr/smgr.c,v 1.68 2004/01/06 18:07:31 neilc Exp $ * $PostgreSQL: pgsql/src/backend/storage/smgr/smgr.c,v 1.69 2004/02/10 01:55:26 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -21,72 +21,52 @@ ...@@ -21,72 +21,52 @@
#include "storage/freespace.h" #include "storage/freespace.h"
#include "storage/ipc.h" #include "storage/ipc.h"
#include "storage/smgr.h" #include "storage/smgr.h"
#include "utils/hsearch.h"
#include "utils/memutils.h" #include "utils/memutils.h"
static void smgrshutdown(int code, Datum arg); /*
* This struct of function pointers defines the API between smgr.c and
* any individual storage manager module. Note that smgr subfunctions are
* generally expected to return TRUE on success, FALSE on error. (For
* nblocks and truncate we instead say that returning InvalidBlockNumber
* indicates an error.)
*/
typedef struct f_smgr typedef struct f_smgr
{ {
int (*smgr_init) (void); /* may be NULL */ bool (*smgr_init) (void); /* may be NULL */
int (*smgr_shutdown) (void); /* may be NULL */ bool (*smgr_shutdown) (void); /* may be NULL */
int (*smgr_create) (Relation reln); bool (*smgr_close) (SMgrRelation reln);
int (*smgr_unlink) (RelFileNode rnode); bool (*smgr_create) (SMgrRelation reln, bool isRedo);
int (*smgr_extend) (Relation reln, BlockNumber blocknum, bool (*smgr_unlink) (RelFileNode rnode, bool isRedo);
bool (*smgr_extend) (SMgrRelation reln, BlockNumber blocknum,
char *buffer); char *buffer);
int (*smgr_open) (Relation reln); bool (*smgr_read) (SMgrRelation reln, BlockNumber blocknum,
int (*smgr_close) (Relation reln);
int (*smgr_read) (Relation reln, BlockNumber blocknum,
char *buffer); char *buffer);
int (*smgr_write) (Relation reln, BlockNumber blocknum, bool (*smgr_write) (SMgrRelation reln, BlockNumber blocknum,
char *buffer); char *buffer);
int (*smgr_blindwrt) (RelFileNode rnode, BlockNumber blkno, BlockNumber (*smgr_nblocks) (SMgrRelation reln);
char *buffer); BlockNumber (*smgr_truncate) (SMgrRelation reln, BlockNumber nblocks);
BlockNumber (*smgr_nblocks) (Relation reln); bool (*smgr_commit) (void); /* may be NULL */
BlockNumber (*smgr_truncate) (Relation reln, BlockNumber nblocks); bool (*smgr_abort) (void); /* may be NULL */
int (*smgr_commit) (void); /* may be NULL */ bool (*smgr_sync) (void); /* may be NULL */
int (*smgr_abort) (void); /* may be NULL */
int (*smgr_sync) (void);
} f_smgr; } f_smgr;
/*
* The weird placement of commas in this init block is to keep the compiler
* happy, regardless of what storage managers we have (or don't have).
*/
static f_smgr smgrsw[] = {
static const f_smgr smgrsw[] = {
/* magnetic disk */ /* magnetic disk */
{mdinit, NULL, mdcreate, mdunlink, mdextend, mdopen, mdclose, {mdinit, NULL, mdclose, mdcreate, mdunlink, mdextend,
mdread, mdwrite, mdblindwrt, mdread, mdwrite, mdnblocks, mdtruncate, mdcommit, mdabort, mdsync
mdnblocks, mdtruncate, mdcommit, mdabort, mdsync }
},
#ifdef STABLE_MEMORY_STORAGE
/* main memory */
{mminit, mmshutdown, mmcreate, mmunlink, mmextend, mmopen, mmclose,
mmread, mmwrite, mmblindwrt,
mmnblocks, NULL, mmcommit, mmabort, NULL},
#endif
}; };
/* static const int NSmgr = lengthof(smgrsw);
* This array records which storage managers are write-once, and which
* support overwrite. A 'true' entry means that the storage manager is
* write-once. In the best of all possible worlds, there would be no
* write-once storage managers.
*/
#ifdef NOT_USED
static bool smgrwo[] = {
false, /* magnetic disk */
#ifdef STABLE_MEMORY_STORAGE
false, /* main memory */
#endif
};
#endif
static int NSmgr = lengthof(smgrsw); /*
* Each backend has a hashtable that stores all extant SMgrRelation objects.
*/
static HTAB *SMgrRelationHash = NULL;
/* /*
* We keep a list of all relations (represented as RelFileNode values) * We keep a list of all relations (represented as RelFileNode values)
...@@ -105,7 +85,7 @@ static int NSmgr = lengthof(smgrsw); ...@@ -105,7 +85,7 @@ static int NSmgr = lengthof(smgrsw);
typedef struct PendingRelDelete typedef struct PendingRelDelete
{ {
RelFileNode relnode; /* relation that may need to be deleted */ RelFileNode relnode; /* relation that may need to be deleted */
int16 which; /* which storage manager? */ int which; /* which storage manager? */
bool isTemp; /* is it a temporary relation? */ bool isTemp; /* is it a temporary relation? */
bool atCommit; /* T=delete at commit; F=delete at abort */ bool atCommit; /* T=delete at commit; F=delete at abort */
struct PendingRelDelete *next; /* linked-list link */ struct PendingRelDelete *next; /* linked-list link */
...@@ -114,12 +94,20 @@ typedef struct PendingRelDelete ...@@ -114,12 +94,20 @@ typedef struct PendingRelDelete
static PendingRelDelete *pendingDeletes = NULL; /* head of linked list */ static PendingRelDelete *pendingDeletes = NULL; /* head of linked list */
/* local function prototypes */
static void smgrshutdown(int code, Datum arg);
static void smgr_internal_unlink(RelFileNode rnode, int which,
bool isTemp, bool isRedo);
/* /*
* smgrinit(), smgrshutdown() -- Initialize or shut down all storage * smgrinit(), smgrshutdown() -- Initialize or shut down all storage
* managers. * managers.
* *
* Note: in the normal multiprocess scenario with a postmaster, these are
* called at postmaster start and stop, not per-backend.
*/ */
int void
smgrinit(void) smgrinit(void)
{ {
int i; int i;
...@@ -128,7 +116,7 @@ smgrinit(void) ...@@ -128,7 +116,7 @@ smgrinit(void)
{ {
if (smgrsw[i].smgr_init) if (smgrsw[i].smgr_init)
{ {
if ((*(smgrsw[i].smgr_init)) () == SM_FAIL) if (! (*(smgrsw[i].smgr_init)) ())
elog(FATAL, "smgr initialization failed on %s: %m", elog(FATAL, "smgr initialization failed on %s: %m",
DatumGetCString(DirectFunctionCall1(smgrout, DatumGetCString(DirectFunctionCall1(smgrout,
Int16GetDatum(i)))); Int16GetDatum(i))));
...@@ -137,8 +125,6 @@ smgrinit(void) ...@@ -137,8 +125,6 @@ smgrinit(void)
/* register the shutdown proc */ /* register the shutdown proc */
on_proc_exit(smgrshutdown, 0); on_proc_exit(smgrshutdown, 0);
return SM_SUCCESS;
} }
static void static void
...@@ -150,7 +136,7 @@ smgrshutdown(int code, Datum arg) ...@@ -150,7 +136,7 @@ smgrshutdown(int code, Datum arg)
{ {
if (smgrsw[i].smgr_shutdown) if (smgrsw[i].smgr_shutdown)
{ {
if ((*(smgrsw[i].smgr_shutdown)) () == SM_FAIL) if (! (*(smgrsw[i].smgr_shutdown)) ())
elog(FATAL, "smgr shutdown failed on %s: %m", elog(FATAL, "smgr shutdown failed on %s: %m",
DatumGetCString(DirectFunctionCall1(smgrout, DatumGetCString(DirectFunctionCall1(smgrout,
Int16GetDatum(i)))); Int16GetDatum(i))));
...@@ -158,58 +144,178 @@ smgrshutdown(int code, Datum arg) ...@@ -158,58 +144,178 @@ smgrshutdown(int code, Datum arg)
} }
} }
/*
* smgropen() -- Return an SMgrRelation object, creating it if need be.
*
* This does not attempt to actually open the object.
*/
SMgrRelation
smgropen(RelFileNode rnode)
{
SMgrRelation reln;
bool found;
if (SMgrRelationHash == NULL)
{
/* First time through: initialize the hash table */
HASHCTL ctl;
MemSet(&ctl, 0, sizeof(ctl));
ctl.keysize = sizeof(RelFileNode);
ctl.entrysize = sizeof(SMgrRelationData);
ctl.hash = tag_hash;
SMgrRelationHash = hash_create("smgr relation table", 400,
&ctl, HASH_ELEM | HASH_FUNCTION);
}
/* Look up or create an entry */
reln = (SMgrRelation) hash_search(SMgrRelationHash,
(void *) &rnode,
HASH_ENTER, &found);
if (reln == NULL)
ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of memory")));
/* Initialize it if not present before */
if (!found)
{
/* hash_search already filled in the lookup key */
reln->smgr_which = 0; /* we only have md.c at present */
reln->md_fd = NULL; /* mark it not open */
}
return reln;
}
/*
* smgrclose() -- Close and delete an SMgrRelation object.
*
* It is the caller's responsibility not to leave any dangling references
* to the object. (Pointers should be cleared after successful return;
* on the off chance of failure, the SMgrRelation object will still exist.)
*/
void
smgrclose(SMgrRelation reln)
{
if (! (*(smgrsw[reln->smgr_which].smgr_close)) (reln))
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not close relation %u/%u: %m",
reln->smgr_rnode.tblNode,
reln->smgr_rnode.relNode)));
if (hash_search(SMgrRelationHash,
(void *) &(reln->smgr_rnode),
HASH_REMOVE, NULL) == NULL)
elog(ERROR, "SMgrRelation hashtable corrupted");
}
/*
* smgrcloseall() -- Close all existing SMgrRelation objects.
*
* It is the caller's responsibility not to leave any dangling references.
*/
void
smgrcloseall(void)
{
HASH_SEQ_STATUS status;
SMgrRelation reln;
/* Nothing to do if hashtable not set up */
if (SMgrRelationHash == NULL)
return;
hash_seq_init(&status, SMgrRelationHash);
while ((reln = (SMgrRelation) hash_seq_search(&status)) != NULL)
{
smgrclose(reln);
}
}
/*
* smgrclosenode() -- Close SMgrRelation object for given RelFileNode,
* if one exists.
*
* This has the same effects as smgrclose(smgropen(rnode)), but it avoids
* uselessly creating a hashtable entry only to drop it again when no
* such entry exists already.
*
* It is the caller's responsibility not to leave any dangling references.
*/
void
smgrclosenode(RelFileNode rnode)
{
SMgrRelation reln;
/* Nothing to do if hashtable not set up */
if (SMgrRelationHash == NULL)
return;
reln = (SMgrRelation) hash_search(SMgrRelationHash,
(void *) &rnode,
HASH_FIND, NULL);
if (reln != NULL)
smgrclose(reln);
}
/* /*
* smgrcreate() -- Create a new relation. * smgrcreate() -- Create a new relation.
* *
* This routine takes a reldesc, creates the relation on the appropriate * Given an already-created (but presumably unused) SMgrRelation,
* device, and returns a file descriptor for it. * cause the underlying disk file or other storage to be created.
*
* If isRedo is true, it is okay for the underlying file to exist
* already because we are in a WAL replay sequence. In this case
* we should make no PendingRelDelete entry; the WAL sequence will
* tell whether to drop the file.
*/ */
int void
smgrcreate(int16 which, Relation reln) smgrcreate(SMgrRelation reln, bool isTemp, bool isRedo)
{ {
int fd;
PendingRelDelete *pending; PendingRelDelete *pending;
if ((fd = (*(smgrsw[which].smgr_create)) (reln)) < 0) if (! (*(smgrsw[reln->smgr_which].smgr_create)) (reln, isRedo))
ereport(ERROR, ereport(ERROR,
(errcode_for_file_access(), (errcode_for_file_access(),
errmsg("could not create relation \"%s\": %m", errmsg("could not create relation %u/%u: %m",
RelationGetRelationName(reln)))); reln->smgr_rnode.tblNode,
reln->smgr_rnode.relNode)));
if (isRedo)
return;
/* Add the relation to the list of stuff to delete at abort */ /* Add the relation to the list of stuff to delete at abort */
pending = (PendingRelDelete *) pending = (PendingRelDelete *)
MemoryContextAlloc(TopMemoryContext, sizeof(PendingRelDelete)); MemoryContextAlloc(TopMemoryContext, sizeof(PendingRelDelete));
pending->relnode = reln->rd_node; pending->relnode = reln->smgr_rnode;
pending->which = which; pending->which = reln->smgr_which;
pending->isTemp = reln->rd_istemp; pending->isTemp = isTemp;
pending->atCommit = false; /* delete if abort */ pending->atCommit = false; /* delete if abort */
pending->next = pendingDeletes; pending->next = pendingDeletes;
pendingDeletes = pending; pendingDeletes = pending;
return fd;
} }
/* /*
* smgrunlink() -- Unlink a relation. * smgrscheduleunlink() -- Schedule unlinking a relation at xact commit.
*
* The relation is marked to be removed from the store if we
* successfully commit the current transaction.
* *
* The relation is removed from the store. Actually, we just remember * This also implies smgrclose() on the SMgrRelation object.
* that we want to do this at transaction commit.
*/ */
int void
smgrunlink(int16 which, Relation reln) smgrscheduleunlink(SMgrRelation reln, bool isTemp)
{ {
PendingRelDelete *pending; PendingRelDelete *pending;
/* Make sure the file is closed */
if (reln->rd_fd >= 0)
smgrclose(which, reln);
/* Add the relation to the list of stuff to delete at commit */ /* Add the relation to the list of stuff to delete at commit */
pending = (PendingRelDelete *) pending = (PendingRelDelete *)
MemoryContextAlloc(TopMemoryContext, sizeof(PendingRelDelete)); MemoryContextAlloc(TopMemoryContext, sizeof(PendingRelDelete));
pending->relnode = reln->rd_node; pending->relnode = reln->smgr_rnode;
pending->which = which; pending->which = reln->smgr_which;
pending->isTemp = reln->rd_istemp; pending->isTemp = isTemp;
pending->atCommit = true; /* delete if commit */ pending->atCommit = true; /* delete if commit */
pending->next = pendingDeletes; pending->next = pendingDeletes;
pendingDeletes = pending; pendingDeletes = pending;
...@@ -224,78 +330,83 @@ smgrunlink(int16 which, Relation reln) ...@@ -224,78 +330,83 @@ smgrunlink(int16 which, Relation reln)
* immediately, but for now I'll keep the logic simple. * immediately, but for now I'll keep the logic simple.
*/ */
return SM_SUCCESS; /* Now close the file and throw away the hashtable entry */
smgrclose(reln);
} }
/* /*
* smgrextend() -- Add a new block to a file. * smgrdounlink() -- Immediately unlink a relation.
* *
* The semantics are basically the same as smgrwrite(): write at the * The relation is removed from the store. This should not be used
* specified position. However, we are expecting to extend the * during transactional operations, since it can't be undone.
* relation (ie, blocknum is the current EOF), and so in case of
* failure we clean up by truncating.
* *
* Returns SM_SUCCESS on success; aborts the current transaction on * If isRedo is true, it is okay for the underlying file to be gone
* failure. * already. (In practice isRedo will always be true.)
*
* This also implies smgrclose() on the SMgrRelation object.
*/ */
int void
smgrextend(int16 which, Relation reln, BlockNumber blocknum, char *buffer) smgrdounlink(SMgrRelation reln, bool isTemp, bool isRedo)
{ {
int status; RelFileNode rnode = reln->smgr_rnode;
int which = reln->smgr_which;
status = (*(smgrsw[which].smgr_extend)) (reln, blocknum, buffer); /* Close the file and throw away the hashtable entry */
smgrclose(reln);
if (status == SM_FAIL)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not extend relation \"%s\": %m",
RelationGetRelationName(reln)),
errhint("Check free disk space.")));
return status; smgr_internal_unlink(rnode, which, isTemp, isRedo);
} }
/* /*
* smgropen() -- Open a relation using a particular storage manager. * Shared subroutine that actually does the unlink ...
*
* Returns the fd for the open relation on success.
*
* On failure, returns -1 if failOK, else aborts the transaction.
*/ */
int static void
smgropen(int16 which, Relation reln, bool failOK) smgr_internal_unlink(RelFileNode rnode, int which, bool isTemp, bool isRedo)
{ {
int fd; /*
* Get rid of any leftover buffers for the rel (shouldn't be any in the
if (reln->rd_rel->relkind == RELKIND_VIEW) * commit case, but there can be in the abort case).
return -1; */
if (reln->rd_rel->relkind == RELKIND_COMPOSITE_TYPE) DropRelFileNodeBuffers(rnode, isTemp);
return -1;
if ((fd = (*(smgrsw[which].smgr_open)) (reln)) < 0) /*
if (!failOK) * Tell the free space map to forget this relation. It won't be accessed
ereport(ERROR, * any more anyway, but we may as well recycle the map space quickly.
(errcode_for_file_access(), */
errmsg("could not open file \"%s\": %m", FreeSpaceMapForgetRel(&rnode);
RelationGetRelationName(reln))));
/*
return fd; * And delete the physical files.
*
* Note: we treat deletion failure as a WARNING, not an error,
* because we've already decided to commit or abort the current xact.
*/
if (! (*(smgrsw[which].smgr_unlink)) (rnode, isRedo))
ereport(WARNING,
(errcode_for_file_access(),
errmsg("could not unlink relation %u/%u: %m",
rnode.tblNode,
rnode.relNode)));
} }
/* /*
* smgrclose() -- Close a relation. * smgrextend() -- Add a new block to a file.
* *
* Returns SM_SUCCESS on success, aborts on failure. * The semantics are basically the same as smgrwrite(): write at the
* specified position. However, we are expecting to extend the
* relation (ie, blocknum is the current EOF), and so in case of
* failure we clean up by truncating.
*/ */
int void
smgrclose(int16 which, Relation reln) smgrextend(SMgrRelation reln, BlockNumber blocknum, char *buffer)
{ {
if ((*(smgrsw[which].smgr_close)) (reln) == SM_FAIL) if (! (*(smgrsw[reln->smgr_which].smgr_extend)) (reln, blocknum, buffer))
ereport(ERROR, ereport(ERROR,
(errcode_for_file_access(), (errcode_for_file_access(),
errmsg("could not close relation \"%s\": %m", errmsg("could not extend relation %u/%u: %m",
RelationGetRelationName(reln)))); reln->smgr_rnode.tblNode,
reln->smgr_rnode.relNode),
return SM_SUCCESS; errhint("Check free disk space.")));
} }
/* /*
...@@ -304,24 +415,18 @@ smgrclose(int16 which, Relation reln) ...@@ -304,24 +415,18 @@ smgrclose(int16 which, Relation reln)
* *
* This routine is called from the buffer manager in order to * This routine is called from the buffer manager in order to
* instantiate pages in the shared buffer cache. All storage managers * instantiate pages in the shared buffer cache. All storage managers
* return pages in the format that POSTGRES expects. This routine * return pages in the format that POSTGRES expects.
* dispatches the read. On success, it returns SM_SUCCESS. On failure,
* the current transaction is aborted.
*/ */
int void
smgrread(int16 which, Relation reln, BlockNumber blocknum, char *buffer) smgrread(SMgrRelation reln, BlockNumber blocknum, char *buffer)
{ {
int status; if (! (*(smgrsw[reln->smgr_which].smgr_read)) (reln, blocknum, buffer))
status = (*(smgrsw[which].smgr_read)) (reln, blocknum, buffer);
if (status == SM_FAIL)
ereport(ERROR, ereport(ERROR,
(errcode_for_file_access(), (errcode_for_file_access(),
errmsg("could not read block %d of relation \"%s\": %m", errmsg("could not read block %u of relation %u/%u: %m",
blocknum, RelationGetRelationName(reln)))); blocknum,
reln->smgr_rnode.tblNode,
return status; reln->smgr_rnode.relNode)));
} }
/* /*
...@@ -329,56 +434,17 @@ smgrread(int16 which, Relation reln, BlockNumber blocknum, char *buffer) ...@@ -329,56 +434,17 @@ smgrread(int16 which, Relation reln, BlockNumber blocknum, char *buffer)
* *
* This is not a synchronous write -- the block is not necessarily * This is not a synchronous write -- the block is not necessarily
* on disk at return, only dumped out to the kernel. * on disk at return, only dumped out to the kernel.
*
* The buffer is written out via the appropriate
* storage manager. This routine returns SM_SUCCESS or aborts
* the current transaction.
*/ */
int void
smgrwrite(int16 which, Relation reln, BlockNumber blocknum, char *buffer) smgrwrite(SMgrRelation reln, BlockNumber blocknum, char *buffer)
{
int status;
status = (*(smgrsw[which].smgr_write)) (reln, blocknum, buffer);
if (status == SM_FAIL)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not write block %d of relation \"%s\": %m",
blocknum, RelationGetRelationName(reln))));
return status;
}
/*
* smgrblindwrt() -- Write a page out blind.
*
* In some cases, we may find a page in the buffer cache that we
* can't make a reldesc for. This happens, for example, when we
* want to reuse a dirty page that was written by a transaction
* that has not yet committed, which created a new relation. In
* this case, the buffer manager will call smgrblindwrt() with
* the name and OID of the database and the relation to which the
* buffer belongs. Every storage manager must be able to write
* this page out to stable storage in this circumstance.
*/
int
smgrblindwrt(int16 which,
RelFileNode rnode,
BlockNumber blkno,
char *buffer)
{ {
int status; if (! (*(smgrsw[reln->smgr_which].smgr_write)) (reln, blocknum, buffer))
status = (*(smgrsw[which].smgr_blindwrt)) (rnode, blkno, buffer);
if (status == SM_FAIL)
ereport(ERROR, ereport(ERROR,
(errcode_for_file_access(), (errcode_for_file_access(),
errmsg("could not write block %d of %u/%u blind: %m", errmsg("could not write block %u of relation %u/%u: %m",
blkno, rnode.tblNode, rnode.relNode))); blocknum,
reln->smgr_rnode.tblNode,
return status; reln->smgr_rnode.relNode)));
} }
/* /*
...@@ -389,11 +455,11 @@ smgrblindwrt(int16 which, ...@@ -389,11 +455,11 @@ smgrblindwrt(int16 which,
* transaction on failure. * transaction on failure.
*/ */
BlockNumber BlockNumber
smgrnblocks(int16 which, Relation reln) smgrnblocks(SMgrRelation reln)
{ {
BlockNumber nblocks; BlockNumber nblocks;
nblocks = (*(smgrsw[which].smgr_nblocks)) (reln); nblocks = (*(smgrsw[reln->smgr_which].smgr_nblocks)) (reln);
/* /*
* NOTE: if a relation ever did grow to 2^32-1 blocks, this code would * NOTE: if a relation ever did grow to 2^32-1 blocks, this code would
...@@ -404,8 +470,9 @@ smgrnblocks(int16 which, Relation reln) ...@@ -404,8 +470,9 @@ smgrnblocks(int16 which, Relation reln)
if (nblocks == InvalidBlockNumber) if (nblocks == InvalidBlockNumber)
ereport(ERROR, ereport(ERROR,
(errcode_for_file_access(), (errcode_for_file_access(),
errmsg("could not count blocks of relation \"%s\": %m", errmsg("could not count blocks of relation %u/%u: %m",
RelationGetRelationName(reln)))); reln->smgr_rnode.tblNode,
reln->smgr_rnode.relNode)));
return nblocks; return nblocks;
} }
...@@ -418,27 +485,25 @@ smgrnblocks(int16 which, Relation reln) ...@@ -418,27 +485,25 @@ smgrnblocks(int16 which, Relation reln)
* transaction on failure. * transaction on failure.
*/ */
BlockNumber BlockNumber
smgrtruncate(int16 which, Relation reln, BlockNumber nblocks) smgrtruncate(SMgrRelation reln, BlockNumber nblocks)
{ {
BlockNumber newblks; BlockNumber newblks;
newblks = nblocks; /*
if (smgrsw[which].smgr_truncate) * Tell the free space map to forget anything it may have stored
{ * for the about-to-be-deleted blocks. We want to be sure it
/* * won't return bogus block numbers later on.
* Tell the free space map to forget anything it may have stored */
* for the about-to-be-deleted blocks. We want to be sure it FreeSpaceMapTruncateRel(&reln->smgr_rnode, nblocks);
* won't return bogus block numbers later on.
*/ newblks = (*(smgrsw[reln->smgr_which].smgr_truncate)) (reln, nblocks);
FreeSpaceMapTruncateRel(&reln->rd_node, nblocks); if (newblks == InvalidBlockNumber)
ereport(ERROR,
newblks = (*(smgrsw[which].smgr_truncate)) (reln, nblocks); (errcode_for_file_access(),
if (newblks == InvalidBlockNumber) errmsg("could not truncate relation %u/%u to %u blocks: %m",
ereport(ERROR, reln->smgr_rnode.tblNode,
(errcode_for_file_access(), reln->smgr_rnode.relNode,
errmsg("could not truncate relation \"%s\" to %u blocks: %m", nblocks)));
RelationGetRelationName(reln), nblocks)));
}
return newblks; return newblks;
} }
...@@ -446,7 +511,7 @@ smgrtruncate(int16 which, Relation reln, BlockNumber nblocks) ...@@ -446,7 +511,7 @@ smgrtruncate(int16 which, Relation reln, BlockNumber nblocks)
/* /*
* smgrDoPendingDeletes() -- Take care of relation deletes at end of xact. * smgrDoPendingDeletes() -- Take care of relation deletes at end of xact.
*/ */
int void
smgrDoPendingDeletes(bool isCommit) smgrDoPendingDeletes(bool isCommit)
{ {
while (pendingDeletes != NULL) while (pendingDeletes != NULL)
...@@ -455,39 +520,12 @@ smgrDoPendingDeletes(bool isCommit) ...@@ -455,39 +520,12 @@ smgrDoPendingDeletes(bool isCommit)
pendingDeletes = pending->next; pendingDeletes = pending->next;
if (pending->atCommit == isCommit) if (pending->atCommit == isCommit)
{ smgr_internal_unlink(pending->relnode,
/* pending->which,
* Get rid of any leftover buffers for the rel (shouldn't be pending->isTemp,
* any in the commit case, but there can be in the abort false);
* case).
*/
DropRelFileNodeBuffers(pending->relnode, pending->isTemp);
/*
* Tell the free space map to forget this relation. It won't
* be accessed any more anyway, but we may as well recycle the
* map space quickly.
*/
FreeSpaceMapForgetRel(&pending->relnode);
/*
* And delete the physical files.
*
* Note: we treat deletion failure as a WARNING, not an error,
* because we've already decided to commit or abort the
* current xact.
*/
if ((*(smgrsw[pending->which].smgr_unlink)) (pending->relnode) == SM_FAIL)
ereport(WARNING,
(errcode_for_file_access(),
errmsg("could not unlink %u/%u: %m",
pending->relnode.tblNode,
pending->relnode.relNode)));
}
pfree(pending); pfree(pending);
} }
return SM_SUCCESS;
} }
/* /*
...@@ -496,7 +534,7 @@ smgrDoPendingDeletes(bool isCommit) ...@@ -496,7 +534,7 @@ smgrDoPendingDeletes(bool isCommit)
* *
* This is called before we actually commit. * This is called before we actually commit.
*/ */
int void
smgrcommit(void) smgrcommit(void)
{ {
int i; int i;
...@@ -505,20 +543,18 @@ smgrcommit(void) ...@@ -505,20 +543,18 @@ smgrcommit(void)
{ {
if (smgrsw[i].smgr_commit) if (smgrsw[i].smgr_commit)
{ {
if ((*(smgrsw[i].smgr_commit)) () == SM_FAIL) if (! (*(smgrsw[i].smgr_commit)) ())
elog(FATAL, "transaction commit failed on %s: %m", elog(FATAL, "transaction commit failed on %s: %m",
DatumGetCString(DirectFunctionCall1(smgrout, DatumGetCString(DirectFunctionCall1(smgrout,
Int16GetDatum(i)))); Int16GetDatum(i))));
} }
} }
return SM_SUCCESS;
} }
/* /*
* smgrabort() -- Abort changes made during the current transaction. * smgrabort() -- Abort changes made during the current transaction.
*/ */
int void
smgrabort(void) smgrabort(void)
{ {
int i; int i;
...@@ -527,20 +563,18 @@ smgrabort(void) ...@@ -527,20 +563,18 @@ smgrabort(void)
{ {
if (smgrsw[i].smgr_abort) if (smgrsw[i].smgr_abort)
{ {
if ((*(smgrsw[i].smgr_abort)) () == SM_FAIL) if (! (*(smgrsw[i].smgr_abort)) ())
elog(FATAL, "transaction abort failed on %s: %m", elog(FATAL, "transaction abort failed on %s: %m",
DatumGetCString(DirectFunctionCall1(smgrout, DatumGetCString(DirectFunctionCall1(smgrout,
Int16GetDatum(i)))); Int16GetDatum(i))));
} }
} }
return SM_SUCCESS;
} }
/* /*
* smgrsync() -- Sync files to disk at checkpoint time. * smgrsync() -- Sync files to disk at checkpoint time.
*/ */
int void
smgrsync(void) smgrsync(void)
{ {
int i; int i;
...@@ -549,26 +583,14 @@ smgrsync(void) ...@@ -549,26 +583,14 @@ smgrsync(void)
{ {
if (smgrsw[i].smgr_sync) if (smgrsw[i].smgr_sync)
{ {
if ((*(smgrsw[i].smgr_sync)) () == SM_FAIL) if (! (*(smgrsw[i].smgr_sync)) ())
elog(PANIC, "storage sync failed on %s: %m", elog(PANIC, "storage sync failed on %s: %m",
DatumGetCString(DirectFunctionCall1(smgrout, DatumGetCString(DirectFunctionCall1(smgrout,
Int16GetDatum(i)))); Int16GetDatum(i))));
} }
} }
return SM_SUCCESS;
} }
#ifdef NOT_USED
bool
smgriswo(int16 smgrno)
{
if (smgrno < 0 || smgrno >= NSmgr)
elog(ERROR, "invalid storage manager id: %d", smgrno);
return smgrwo[smgrno];
}
#endif
void void
smgr_redo(XLogRecPtr lsn, XLogRecord *record) smgr_redo(XLogRecPtr lsn, XLogRecord *record)
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/smgr/smgrtype.c,v 1.22 2003/11/29 19:51:57 pgsql Exp $ * $PostgreSQL: pgsql/src/backend/storage/smgr/smgrtype.c,v 1.23 2004/02/10 01:55:26 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -16,26 +16,21 @@ ...@@ -16,26 +16,21 @@
#include "storage/smgr.h" #include "storage/smgr.h"
typedef struct smgrid typedef struct smgrid
{ {
char *smgr_name; const char *smgr_name;
} smgrid; } smgrid;
/* /*
* StorageManager[] -- List of defined storage managers. * StorageManager[] -- List of defined storage managers.
*
* The weird comma placement is to keep compilers happy no matter
* which of these is (or is not) defined.
*/ */
static const smgrid StorageManager[] = {
static smgrid StorageManager[] = { {"magnetic disk"}
{"magnetic disk"},
#ifdef STABLE_MEMORY_STORAGE
{"main memory"}
#endif
}; };
static int NStorageManagers = lengthof(StorageManager); static const int NStorageManagers = lengthof(StorageManager);
Datum Datum
smgrin(PG_FUNCTION_ARGS) smgrin(PG_FUNCTION_ARGS)
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.209 2003/11/29 19:51:57 pgsql Exp $ * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.210 2004/02/10 01:55:26 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -48,6 +48,7 @@ ...@@ -48,6 +48,7 @@
#include "parser/parse_type.h" #include "parser/parse_type.h"
#include "rewrite/rewriteDefine.h" #include "rewrite/rewriteDefine.h"
#include "rewrite/rewriteRemove.h" #include "rewrite/rewriteRemove.h"
#include "storage/fd.h"
#include "tcop/pquery.h" #include "tcop/pquery.h"
#include "tcop/utility.h" #include "tcop/utility.h"
#include "utils/acl.h" #include "utils/acl.h"
......
...@@ -74,7 +74,7 @@ ...@@ -74,7 +74,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/cache/inval.c,v 1.59 2003/11/29 19:52:00 pgsql Exp $ * $PostgreSQL: pgsql/src/backend/utils/cache/inval.c,v 1.60 2004/02/10 01:55:26 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -83,6 +83,7 @@ ...@@ -83,6 +83,7 @@
#include "catalog/catalog.h" #include "catalog/catalog.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "storage/sinval.h" #include "storage/sinval.h"
#include "storage/smgr.h"
#include "utils/catcache.h" #include "utils/catcache.h"
#include "utils/inval.h" #include "utils/inval.h"
#include "utils/memutils.h" #include "utils/memutils.h"
...@@ -298,19 +299,22 @@ AddCatcacheInvalidationMessage(InvalidationListHeader *hdr, ...@@ -298,19 +299,22 @@ AddCatcacheInvalidationMessage(InvalidationListHeader *hdr,
*/ */
static void static void
AddRelcacheInvalidationMessage(InvalidationListHeader *hdr, AddRelcacheInvalidationMessage(InvalidationListHeader *hdr,
Oid dbId, Oid relId) Oid dbId, Oid relId, RelFileNode physId)
{ {
SharedInvalidationMessage msg; SharedInvalidationMessage msg;
/* Don't add a duplicate item */ /* Don't add a duplicate item */
/* We assume comparing relId is sufficient, needn't check dbId */ /* We assume dbId need not be checked because it will never change */
/* relfilenode fields must be checked to support reassignment */
ProcessMessageList(hdr->rclist, ProcessMessageList(hdr->rclist,
if (msg->rc.relId == relId) return); if (msg->rc.relId == relId &&
RelFileNodeEquals(msg->rc.physId, physId)) return);
/* OK, add the item */ /* OK, add the item */
msg.rc.id = SHAREDINVALRELCACHE_ID; msg.rc.id = SHAREDINVALRELCACHE_ID;
msg.rc.dbId = dbId; msg.rc.dbId = dbId;
msg.rc.relId = relId; msg.rc.relId = relId;
msg.rc.physId = physId;
AddInvalidationMessage(&hdr->rclist, &msg); AddInvalidationMessage(&hdr->rclist, &msg);
} }
...@@ -391,10 +395,10 @@ RegisterCatcacheInvalidation(int cacheId, ...@@ -391,10 +395,10 @@ RegisterCatcacheInvalidation(int cacheId,
* As above, but register a relcache invalidation event. * As above, but register a relcache invalidation event.
*/ */
static void static void
RegisterRelcacheInvalidation(Oid dbId, Oid relId) RegisterRelcacheInvalidation(Oid dbId, Oid relId, RelFileNode physId)
{ {
AddRelcacheInvalidationMessage(&CurrentCmdInvalidMsgs, AddRelcacheInvalidationMessage(&CurrentCmdInvalidMsgs,
dbId, relId); dbId, relId, physId);
/* /*
* If the relation being invalidated is one of those cached in the * If the relation being invalidated is one of those cached in the
...@@ -435,9 +439,17 @@ LocalExecuteInvalidationMessage(SharedInvalidationMessage *msg) ...@@ -435,9 +439,17 @@ LocalExecuteInvalidationMessage(SharedInvalidationMessage *msg)
} }
else if (msg->id == SHAREDINVALRELCACHE_ID) else if (msg->id == SHAREDINVALRELCACHE_ID)
{ {
if (msg->rc.dbId == MyDatabaseId || msg->rc.dbId == 0) /*
* If the message includes a valid relfilenode, we must ensure that
* smgr cache entry gets zapped. The relcache will handle this if
* called, otherwise we must do it directly.
*/
if (msg->rc.dbId == MyDatabaseId || msg->rc.dbId == InvalidOid)
{ {
RelationIdInvalidateRelationCacheByRelationId(msg->rc.relId); if (OidIsValid(msg->rc.physId.relNode))
RelationCacheInvalidateEntry(msg->rc.relId, &msg->rc.physId);
else
RelationCacheInvalidateEntry(msg->rc.relId, NULL);
for (i = 0; i < cache_callback_count; i++) for (i = 0; i < cache_callback_count; i++)
{ {
...@@ -447,6 +459,12 @@ LocalExecuteInvalidationMessage(SharedInvalidationMessage *msg) ...@@ -447,6 +459,12 @@ LocalExecuteInvalidationMessage(SharedInvalidationMessage *msg)
(*ccitem->function) (ccitem->arg, msg->rc.relId); (*ccitem->function) (ccitem->arg, msg->rc.relId);
} }
} }
else
{
/* might have smgr entry even if not in our database */
if (OidIsValid(msg->rc.physId.relNode))
smgrclosenode(msg->rc.physId);
}
} }
else else
elog(FATAL, "unrecognized SI message id: %d", msg->id); elog(FATAL, "unrecognized SI message id: %d", msg->id);
...@@ -456,7 +474,7 @@ LocalExecuteInvalidationMessage(SharedInvalidationMessage *msg) ...@@ -456,7 +474,7 @@ LocalExecuteInvalidationMessage(SharedInvalidationMessage *msg)
* InvalidateSystemCaches * InvalidateSystemCaches
* *
* This blows away all tuples in the system catalog caches and * This blows away all tuples in the system catalog caches and
* all the cached relation descriptors (and closes their files too). * all the cached relation descriptors and smgr cache entries.
* Relation descriptors that have positive refcounts are then rebuilt. * Relation descriptors that have positive refcounts are then rebuilt.
* *
* We call this when we see a shared-inval-queue overflow signal, * We call this when we see a shared-inval-queue overflow signal,
...@@ -469,7 +487,7 @@ InvalidateSystemCaches(void) ...@@ -469,7 +487,7 @@ InvalidateSystemCaches(void)
int i; int i;
ResetCatalogCaches(); ResetCatalogCaches();
RelationCacheInvalidate(); RelationCacheInvalidate(); /* gets smgr cache too */
for (i = 0; i < cache_callback_count; i++) for (i = 0; i < cache_callback_count; i++)
{ {
...@@ -488,11 +506,15 @@ static void ...@@ -488,11 +506,15 @@ static void
PrepareForTupleInvalidation(Relation relation, HeapTuple tuple, PrepareForTupleInvalidation(Relation relation, HeapTuple tuple,
void (*CacheIdRegisterFunc) (int, uint32, void (*CacheIdRegisterFunc) (int, uint32,
ItemPointer, Oid), ItemPointer, Oid),
void (*RelationIdRegisterFunc) (Oid, Oid)) void (*RelationIdRegisterFunc) (Oid, Oid,
RelFileNode))
{ {
Oid tupleRelId; Oid tupleRelId;
Oid databaseId;
Oid relationId; Oid relationId;
RelFileNode rnode;
/* Do nothing during bootstrap */
if (IsBootstrapProcessingMode()) if (IsBootstrapProcessingMode())
return; return;
...@@ -524,24 +546,49 @@ PrepareForTupleInvalidation(Relation relation, HeapTuple tuple, ...@@ -524,24 +546,49 @@ PrepareForTupleInvalidation(Relation relation, HeapTuple tuple,
tupleRelId = RelationGetRelid(relation); tupleRelId = RelationGetRelid(relation);
if (tupleRelId == RelOid_pg_class) if (tupleRelId == RelOid_pg_class)
{
Form_pg_class classtup = (Form_pg_class) GETSTRUCT(tuple);
relationId = HeapTupleGetOid(tuple); relationId = HeapTupleGetOid(tuple);
if (classtup->relisshared)
databaseId = InvalidOid;
else
databaseId = MyDatabaseId;
rnode.tblNode = databaseId; /* XXX change for tablespaces */
rnode.relNode = classtup->relfilenode;
/*
* Note: during a pg_class row update that assigns a new relfilenode
* value, we will be called on both the old and new tuples, and thus
* will broadcast invalidation messages showing both the old and new
* relfilenode values. This ensures that other backends will close
* smgr references to the old relfilenode file.
*/
}
else if (tupleRelId == RelOid_pg_attribute) else if (tupleRelId == RelOid_pg_attribute)
relationId = ((Form_pg_attribute) GETSTRUCT(tuple))->attrelid; {
Form_pg_attribute atttup = (Form_pg_attribute) GETSTRUCT(tuple);
relationId = atttup->attrelid;
/*
* KLUGE ALERT: we always send the relcache event with MyDatabaseId,
* even if the rel in question is shared (which we can't easily tell).
* This essentially means that only backends in this same database
* will react to the relcache flush request. This is in fact
* appropriate, since only those backends could see our pg_attribute
* change anyway. It looks a bit ugly though.
*/
databaseId = MyDatabaseId;
/* We assume no smgr cache flush is needed, either */
rnode.tblNode = InvalidOid;
rnode.relNode = InvalidOid;
}
else else
return; return;
/* /*
* Yes. We need to register a relcache invalidation event for the * Yes. We need to register a relcache invalidation event.
* relation identified by relationId.
*
* KLUGE ALERT: we always send the relcache event with MyDatabaseId, even
* if the rel in question is shared. This essentially means that only
* backends in this same database will react to the relcache flush
* request. This is in fact appropriate, since only those backends
* could see our pg_class or pg_attribute change anyway. It looks a
* bit ugly though.
*/ */
(*RelationIdRegisterFunc) (MyDatabaseId, relationId); (*RelationIdRegisterFunc) (databaseId, relationId, rnode);
} }
...@@ -660,7 +707,7 @@ CommandEndInvalidationMessages(bool isCommit) ...@@ -660,7 +707,7 @@ CommandEndInvalidationMessages(bool isCommit)
/* /*
* CacheInvalidateHeapTuple * CacheInvalidateHeapTuple
* Register the given tuple for invalidation at end of command * Register the given tuple for invalidation at end of command
* (ie, current command is outdating this tuple). * (ie, current command is creating or outdating this tuple).
*/ */
void void
CacheInvalidateHeapTuple(Relation relation, HeapTuple tuple) CacheInvalidateHeapTuple(Relation relation, HeapTuple tuple)
...@@ -678,12 +725,44 @@ CacheInvalidateHeapTuple(Relation relation, HeapTuple tuple) ...@@ -678,12 +725,44 @@ CacheInvalidateHeapTuple(Relation relation, HeapTuple tuple)
* This is used in places that need to force relcache rebuild but aren't * This is used in places that need to force relcache rebuild but aren't
* changing any of the tuples recognized as contributors to the relcache * changing any of the tuples recognized as contributors to the relcache
* entry by PrepareForTupleInvalidation. (An example is dropping an index.) * entry by PrepareForTupleInvalidation. (An example is dropping an index.)
* We assume in particular that relfilenode isn't changing.
*/ */
void void
CacheInvalidateRelcache(Oid relationId) CacheInvalidateRelcache(Relation relation)
{ {
/* See KLUGE ALERT in PrepareForTupleInvalidation */ Oid databaseId;
RegisterRelcacheInvalidation(MyDatabaseId, relationId); Oid relationId;
relationId = RelationGetRelid(relation);
if (relation->rd_rel->relisshared)
databaseId = InvalidOid;
else
databaseId = MyDatabaseId;
RegisterRelcacheInvalidation(databaseId, relationId, relation->rd_node);
}
/*
* CacheInvalidateRelcacheByTuple
* As above, but relation is identified by passing its pg_class tuple.
*/
void
CacheInvalidateRelcacheByTuple(HeapTuple classTuple)
{
Form_pg_class classtup = (Form_pg_class) GETSTRUCT(classTuple);
Oid databaseId;
Oid relationId;
RelFileNode rnode;
relationId = HeapTupleGetOid(classTuple);
if (classtup->relisshared)
databaseId = InvalidOid;
else
databaseId = MyDatabaseId;
rnode.tblNode = databaseId; /* XXX change for tablespaces */
rnode.relNode = classtup->relfilenode;
RegisterRelcacheInvalidation(databaseId, relationId, rnode);
} }
/* /*
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.196 2004/02/02 00:17:21 momjian Exp $ * $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.197 2004/02/10 01:55:26 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -54,6 +54,7 @@ ...@@ -54,6 +54,7 @@
#include "optimizer/clauses.h" #include "optimizer/clauses.h"
#include "optimizer/planmain.h" #include "optimizer/planmain.h"
#include "optimizer/prep.h" #include "optimizer/prep.h"
#include "storage/fd.h"
#include "storage/smgr.h" #include "storage/smgr.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/catcache.h" #include "utils/catcache.h"
...@@ -91,13 +92,6 @@ static FormData_pg_attribute Desc_pg_type[Natts_pg_type] = {Schema_pg_type}; ...@@ -91,13 +92,6 @@ static FormData_pg_attribute Desc_pg_type[Natts_pg_type] = {Schema_pg_type};
static HTAB *RelationIdCache; static HTAB *RelationIdCache;
static HTAB *RelationSysNameCache; static HTAB *RelationSysNameCache;
/*
* Bufmgr uses RelFileNode for lookup. Actually, I would like to do
* not pass Relation to bufmgr & beyond at all and keep some cache
* in smgr, but no time to do it right way now. -- vadim 10/22/2000
*/
static HTAB *RelationNodeCache;
/* /*
* This flag is false until we have prepared the critical relcache entries * This flag is false until we have prepared the critical relcache entries
* that are needed to do indexscans on the tables read by relcache building. * that are needed to do indexscans on the tables read by relcache building.
...@@ -152,18 +146,12 @@ typedef struct relnamecacheent ...@@ -152,18 +146,12 @@ typedef struct relnamecacheent
Relation reldesc; Relation reldesc;
} RelNameCacheEnt; } RelNameCacheEnt;
typedef struct relnodecacheent
{
RelFileNode relnode;
Relation reldesc;
} RelNodeCacheEnt;
/* /*
* macros to manipulate the lookup hashtables * macros to manipulate the lookup hashtables
*/ */
#define RelationCacheInsert(RELATION) \ #define RelationCacheInsert(RELATION) \
do { \ do { \
RelIdCacheEnt *idhentry; RelNodeCacheEnt *nodentry; bool found; \ RelIdCacheEnt *idhentry; bool found; \
idhentry = (RelIdCacheEnt*)hash_search(RelationIdCache, \ idhentry = (RelIdCacheEnt*)hash_search(RelationIdCache, \
(void *) &(RELATION->rd_id), \ (void *) &(RELATION->rd_id), \
HASH_ENTER, \ HASH_ENTER, \
...@@ -174,16 +162,6 @@ do { \ ...@@ -174,16 +162,6 @@ do { \
errmsg("out of memory"))); \ errmsg("out of memory"))); \
/* used to give notice if found -- now just keep quiet */ \ /* used to give notice if found -- now just keep quiet */ \
idhentry->reldesc = RELATION; \ idhentry->reldesc = RELATION; \
nodentry = (RelNodeCacheEnt*)hash_search(RelationNodeCache, \
(void *) &(RELATION->rd_node), \
HASH_ENTER, \
&found); \
if (nodentry == NULL) \
ereport(ERROR, \
(errcode(ERRCODE_OUT_OF_MEMORY), \
errmsg("out of memory"))); \
/* used to give notice if found -- now just keep quiet */ \
nodentry->reldesc = RELATION; \
if (IsSystemNamespace(RelationGetNamespace(RELATION))) \ if (IsSystemNamespace(RelationGetNamespace(RELATION))) \
{ \ { \
char *relname = RelationGetRelationName(RELATION); \ char *relname = RelationGetRelationName(RELATION); \
...@@ -223,30 +201,14 @@ do { \ ...@@ -223,30 +201,14 @@ do { \
RELATION = NULL; \ RELATION = NULL; \
} while(0) } while(0)
#define RelationNodeCacheLookup(NODE, RELATION) \
do { \
RelNodeCacheEnt *hentry; \
hentry = (RelNodeCacheEnt*)hash_search(RelationNodeCache, \
(void *)&(NODE), HASH_FIND,NULL); \
if (hentry) \
RELATION = hentry->reldesc; \
else \
RELATION = NULL; \
} while(0)
#define RelationCacheDelete(RELATION) \ #define RelationCacheDelete(RELATION) \
do { \ do { \
RelIdCacheEnt *idhentry; RelNodeCacheEnt *nodentry; \ RelIdCacheEnt *idhentry; \
idhentry = (RelIdCacheEnt*)hash_search(RelationIdCache, \ idhentry = (RelIdCacheEnt*)hash_search(RelationIdCache, \
(void *)&(RELATION->rd_id), \ (void *)&(RELATION->rd_id), \
HASH_REMOVE, NULL); \ HASH_REMOVE, NULL); \
if (idhentry == NULL) \ if (idhentry == NULL) \
elog(WARNING, "trying to delete a rd_id reldesc that does not exist"); \ elog(WARNING, "trying to delete a rd_id reldesc that does not exist"); \
nodentry = (RelNodeCacheEnt*)hash_search(RelationNodeCache, \
(void *)&(RELATION->rd_node), \
HASH_REMOVE, NULL); \
if (nodentry == NULL) \
elog(WARNING, "trying to delete a rd_node reldesc that does not exist"); \
if (IsSystemNamespace(RelationGetNamespace(RELATION))) \ if (IsSystemNamespace(RelationGetNamespace(RELATION))) \
{ \ { \
char *relname = RelationGetRelationName(RELATION); \ char *relname = RelationGetRelationName(RELATION); \
...@@ -423,7 +385,7 @@ AllocateRelationDesc(Relation relation, Form_pg_class relp) ...@@ -423,7 +385,7 @@ AllocateRelationDesc(Relation relation, Form_pg_class relp)
relation->rd_targblock = InvalidBlockNumber; relation->rd_targblock = InvalidBlockNumber;
/* make sure relation is marked as having no open file yet */ /* make sure relation is marked as having no open file yet */
relation->rd_fd = -1; relation->rd_smgr = NULL;
/* /*
* Copy the relation tuple form * Copy the relation tuple form
...@@ -914,7 +876,7 @@ RelationBuildDesc(RelationBuildDescInfo buildinfo, ...@@ -914,7 +876,7 @@ RelationBuildDesc(RelationBuildDescInfo buildinfo,
relation->rd_node.relNode = relation->rd_rel->relfilenode; relation->rd_node.relNode = relation->rd_rel->relfilenode;
/* make sure relation is marked as having no open file yet */ /* make sure relation is marked as having no open file yet */
relation->rd_fd = -1; relation->rd_smgr = NULL;
/* /*
* Insert newly created relation into relcache hash tables. * Insert newly created relation into relcache hash tables.
...@@ -1303,7 +1265,7 @@ formrdesc(const char *relationName, ...@@ -1303,7 +1265,7 @@ formrdesc(const char *relationName,
relation->rd_targblock = InvalidBlockNumber; relation->rd_targblock = InvalidBlockNumber;
/* make sure relation is marked as having no open file yet */ /* make sure relation is marked as having no open file yet */
relation->rd_fd = -1; relation->rd_smgr = NULL;
/* /*
* initialize reference count * initialize reference count
...@@ -1481,30 +1443,6 @@ RelationSysNameCacheGetRelation(const char *relationName) ...@@ -1481,30 +1443,6 @@ RelationSysNameCacheGetRelation(const char *relationName)
return rd; return rd;
} }
/*
* RelationNodeCacheGetRelation
*
* As above, but lookup by relfilenode.
*
* NOTE: this must NOT try to revalidate invalidated nailed indexes, since
* that could cause us to return an entry with a different relfilenode than
* the caller asked for. Currently this is used only by the buffer manager.
* Really the bufmgr's idea of relations should be separated out from the
* relcache ...
*/
Relation
RelationNodeCacheGetRelation(RelFileNode rnode)
{
Relation rd;
RelationNodeCacheLookup(rnode, rd);
if (RelationIsValid(rd))
RelationIncrementReferenceCount(rd);
return rd;
}
/* /*
* RelationIdGetRelation * RelationIdGetRelation
* *
...@@ -1635,14 +1573,8 @@ RelationReloadClassinfo(Relation relation) ...@@ -1635,14 +1573,8 @@ RelationReloadClassinfo(Relation relation)
elog(ERROR, "could not find tuple for system relation %u", elog(ERROR, "could not find tuple for system relation %u",
relation->rd_id); relation->rd_id);
relp = (Form_pg_class) GETSTRUCT(pg_class_tuple); relp = (Form_pg_class) GETSTRUCT(pg_class_tuple);
if (relation->rd_node.relNode != relp->relfilenode) memcpy((char *) relation->rd_rel, (char *) relp, CLASS_TUPLE_SIZE);
{ relation->rd_node.relNode = relp->relfilenode;
/* We have to re-insert the entry into the relcache indexes */
RelationCacheDelete(relation);
memcpy((char *) relation->rd_rel, (char *) relp, CLASS_TUPLE_SIZE);
relation->rd_node.relNode = relp->relfilenode;
RelationCacheInsert(relation);
}
heap_freetuple(pg_class_tuple); heap_freetuple(pg_class_tuple);
/* Must adjust number of blocks after we know the new relfilenode */ /* Must adjust number of blocks after we know the new relfilenode */
relation->rd_targblock = InvalidBlockNumber; relation->rd_targblock = InvalidBlockNumber;
...@@ -1672,10 +1604,10 @@ RelationClearRelation(Relation relation, bool rebuild) ...@@ -1672,10 +1604,10 @@ RelationClearRelation(Relation relation, bool rebuild)
* ensures that the low-level file access state is updated after, say, * ensures that the low-level file access state is updated after, say,
* a vacuum truncation. * a vacuum truncation.
*/ */
if (relation->rd_fd >= 0) if (relation->rd_smgr)
{ {
smgrclose(DEFAULT_SMGR, relation); smgrclose(relation->rd_smgr);
relation->rd_fd = -1; relation->rd_smgr = NULL;
} }
/* /*
...@@ -1866,18 +1798,31 @@ RelationForgetRelation(Oid rid) ...@@ -1866,18 +1798,31 @@ RelationForgetRelation(Oid rid)
} }
/* /*
* RelationIdInvalidateRelationCacheByRelationId * RelationCacheInvalidateEntry
* *
* This routine is invoked for SI cache flush messages. * This routine is invoked for SI cache flush messages.
* *
* We used to skip local relations, on the grounds that they could * Any relcache entry matching the relid must be flushed. (Note: caller has
* not be targets of cross-backend SI update messages; but it seems * already determined that the relid belongs to our database or is a shared
* safer to process them, so that our *own* SI update messages will * relation.) If rnode isn't NULL, we must also ensure that any smgr cache
* have the same effects during CommandCounterIncrement for both * entry matching that rnode is flushed.
* local and nonlocal relations. *
* Ordinarily, if rnode is supplied then it will match the relfilenode of
* the target relid. However, it's possible for rnode to be different if
* someone is engaged in a relfilenode change. In that case we want to
* make sure we clear the right cache entries. This has to be done here
* to keep things in sync between relcache and smgr cache --- we can't have
* someone flushing an smgr cache entry that a relcache entry still points
* to.
*
* We used to skip local relations, on the grounds that they could
* not be targets of cross-backend SI update messages; but it seems
* safer to process them, so that our *own* SI update messages will
* have the same effects during CommandCounterIncrement for both
* local and nonlocal relations.
*/ */
void void
RelationIdInvalidateRelationCacheByRelationId(Oid relationId) RelationCacheInvalidateEntry(Oid relationId, RelFileNode *rnode)
{ {
Relation relation; Relation relation;
...@@ -1886,14 +1831,27 @@ RelationIdInvalidateRelationCacheByRelationId(Oid relationId) ...@@ -1886,14 +1831,27 @@ RelationIdInvalidateRelationCacheByRelationId(Oid relationId)
if (PointerIsValid(relation)) if (PointerIsValid(relation))
{ {
relcacheInvalsReceived++; relcacheInvalsReceived++;
if (rnode)
{
/* Need to be sure smgr is flushed, but don't do it twice */
if (relation->rd_smgr == NULL ||
!RelFileNodeEquals(*rnode, relation->rd_node))
smgrclosenode(*rnode);
}
RelationFlushRelation(relation); RelationFlushRelation(relation);
} }
else
{
if (rnode)
smgrclosenode(*rnode);
}
} }
/* /*
* RelationCacheInvalidate * RelationCacheInvalidate
* Blow away cached relation descriptors that have zero reference counts, * Blow away cached relation descriptors that have zero reference counts,
* and rebuild those with positive reference counts. * and rebuild those with positive reference counts. Also reset the smgr
* relation cache.
* *
* This is currently used only to recover from SI message buffer overflow, * This is currently used only to recover from SI message buffer overflow,
* so we do not touch new-in-transaction relations; they cannot be targets * so we do not touch new-in-transaction relations; they cannot be targets
...@@ -1934,6 +1892,13 @@ RelationCacheInvalidate(void) ...@@ -1934,6 +1892,13 @@ RelationCacheInvalidate(void)
{ {
relation = idhentry->reldesc; relation = idhentry->reldesc;
/* Must close all smgr references to avoid leaving dangling ptrs */
if (relation->rd_smgr)
{
smgrclose(relation->rd_smgr);
relation->rd_smgr = NULL;
}
/* Ignore new relations, since they are never SI targets */ /* Ignore new relations, since they are never SI targets */
if (relation->rd_isnew) if (relation->rd_isnew)
continue; continue;
...@@ -1970,6 +1935,13 @@ RelationCacheInvalidate(void) ...@@ -1970,6 +1935,13 @@ RelationCacheInvalidate(void)
rebuildList = nconc(rebuildFirstList, rebuildList); rebuildList = nconc(rebuildFirstList, rebuildList);
/*
* Now zap any remaining smgr cache entries. This must happen before
* we start to rebuild entries, since that may involve catalog fetches
* which will re-open catalog files.
*/
smgrcloseall();
/* Phase 2: rebuild the items found to need rebuild in phase 1 */ /* Phase 2: rebuild the items found to need rebuild in phase 1 */
foreach(l, rebuildList) foreach(l, rebuildList)
{ {
...@@ -2107,7 +2079,7 @@ RelationBuildLocalRelation(const char *relname, ...@@ -2107,7 +2079,7 @@ RelationBuildLocalRelation(const char *relname,
rel->rd_targblock = InvalidBlockNumber; rel->rd_targblock = InvalidBlockNumber;
/* make sure relation is marked as having no open file yet */ /* make sure relation is marked as having no open file yet */
rel->rd_fd = -1; rel->rd_smgr = NULL;
RelationSetReferenceCount(rel, 1); RelationSetReferenceCount(rel, 1);
...@@ -2233,12 +2205,6 @@ RelationCacheInitialize(void) ...@@ -2233,12 +2205,6 @@ RelationCacheInitialize(void)
RelationIdCache = hash_create("Relcache by OID", INITRELCACHESIZE, RelationIdCache = hash_create("Relcache by OID", INITRELCACHESIZE,
&ctl, HASH_ELEM | HASH_FUNCTION); &ctl, HASH_ELEM | HASH_FUNCTION);
ctl.keysize = sizeof(RelFileNode);
ctl.entrysize = sizeof(RelNodeCacheEnt);
ctl.hash = tag_hash;
RelationNodeCache = hash_create("Relcache by rnode", INITRELCACHESIZE,
&ctl, HASH_ELEM | HASH_FUNCTION);
/* /*
* Try to load the relcache cache file. If successful, we're done for * Try to load the relcache cache file. If successful, we're done for
* now. Otherwise, initialize the cache with pre-made descriptors for * now. Otherwise, initialize the cache with pre-made descriptors for
...@@ -2406,65 +2372,6 @@ RelationCacheInitializePhase3(void) ...@@ -2406,65 +2372,6 @@ RelationCacheInitializePhase3(void)
} }
} }
/* used by XLogInitCache */
void CreateDummyCaches(void);
void DestroyDummyCaches(void);
void
CreateDummyCaches(void)
{
MemoryContext oldcxt;
HASHCTL ctl;
if (!CacheMemoryContext)
CreateCacheMemoryContext();
oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
MemSet(&ctl, 0, sizeof(ctl));
ctl.keysize = sizeof(NameData);
ctl.entrysize = sizeof(RelNameCacheEnt);
RelationSysNameCache = hash_create("Relcache by name", INITRELCACHESIZE,
&ctl, HASH_ELEM);
ctl.keysize = sizeof(Oid);
ctl.entrysize = sizeof(RelIdCacheEnt);
ctl.hash = tag_hash;
RelationIdCache = hash_create("Relcache by OID", INITRELCACHESIZE,
&ctl, HASH_ELEM | HASH_FUNCTION);
ctl.keysize = sizeof(RelFileNode);
ctl.entrysize = sizeof(RelNodeCacheEnt);
ctl.hash = tag_hash;
RelationNodeCache = hash_create("Relcache by rnode", INITRELCACHESIZE,
&ctl, HASH_ELEM | HASH_FUNCTION);
MemoryContextSwitchTo(oldcxt);
}
void
DestroyDummyCaches(void)
{
MemoryContext oldcxt;
if (!CacheMemoryContext)
return;
oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
if (RelationIdCache)
hash_destroy(RelationIdCache);
if (RelationSysNameCache)
hash_destroy(RelationSysNameCache);
if (RelationNodeCache)
hash_destroy(RelationNodeCache);
RelationIdCache = RelationSysNameCache = RelationNodeCache = NULL;
MemoryContextSwitchTo(oldcxt);
}
static void static void
AttrDefaultFetch(Relation relation) AttrDefaultFetch(Relation relation)
{ {
...@@ -3125,7 +3032,7 @@ load_relcache_init_file(void) ...@@ -3125,7 +3032,7 @@ load_relcache_init_file(void)
/* /*
* Reset transient-state fields in the relcache entry * Reset transient-state fields in the relcache entry
*/ */
rel->rd_fd = -1; rel->rd_smgr = NULL;
rel->rd_targblock = InvalidBlockNumber; rel->rd_targblock = InvalidBlockNumber;
if (rel->rd_isnailed) if (rel->rd_isnailed)
RelationSetReferenceCount(rel, 1); RelationSetReferenceCount(rel, 1);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/init/miscinit.c,v 1.122 2004/02/08 22:28:57 neilc Exp $ * $PostgreSQL: pgsql/src/backend/utils/init/miscinit.c,v 1.123 2004/02/10 01:55:26 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include "catalog/pg_shadow.h" #include "catalog/pg_shadow.h"
#include "libpq/libpq-be.h" #include "libpq/libpq-be.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "storage/fd.h"
#include "storage/ipc.h" #include "storage/ipc.h"
#include "storage/pg_shmem.h" #include "storage/pg_shmem.h"
#include "utils/builtins.h" #include "utils/builtins.h"
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/catalog/pg_database.h,v 1.30 2003/11/29 22:40:58 pgsql Exp $ * $PostgreSQL: pgsql/src/include/catalog/pg_database.h,v 1.31 2004/02/10 01:55:26 tgl Exp $
* *
* NOTES * NOTES
* the genbki.sh script reads this file and generates .bki * the genbki.sh script reads this file and generates .bki
...@@ -72,15 +72,6 @@ typedef FormData_pg_database *Form_pg_database; ...@@ -72,15 +72,6 @@ typedef FormData_pg_database *Form_pg_database;
DATA(insert OID = 1 ( template1 PGUID ENCODING t t 0 0 0 "" _null_ _null_ )); DATA(insert OID = 1 ( template1 PGUID ENCODING t t 0 0 0 "" _null_ _null_ ));
DESCR("Default template database"); DESCR("Default template database");
#define TemplateDbOid 1 #define TemplateDbOid 1
/* Just to mark OID as used for unused_oid script -:) */
#define DATAMARKOID(x)
DATAMARKOID(= 2)
#define RecoveryDb 2
#undef DATAMARKOID
#endif /* PG_DATABASE_H */ #endif /* PG_DATABASE_H */
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/storage/sinval.h,v 1.32 2003/11/29 22:41:13 pgsql Exp $ * $PostgreSQL: pgsql/src/include/storage/sinval.h,v 1.33 2004/02/10 01:55:26 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "storage/backendid.h" #include "storage/backendid.h"
#include "storage/itemptr.h" #include "storage/itemptr.h"
#include "storage/relfilenode.h"
/* /*
...@@ -27,6 +28,13 @@ ...@@ -27,6 +28,13 @@
* ID field). -1 means a relcache inval message. Other negative values * ID field). -1 means a relcache inval message. Other negative values
* are available to identify other inval message types. * are available to identify other inval message types.
* *
* Relcache invalidation messages usually also cause invalidation of entries
* in the smgr's relation cache. This means they must carry both logical
* and physical relation ID info (ie, both dbOID/relOID and RelFileNode).
* In some cases RelFileNode information is not available so the sender fills
* those fields with zeroes --- this is okay so long as no smgr cache flush
* is required.
*
* Shared-inval events are initially driven by detecting tuple inserts, * Shared-inval events are initially driven by detecting tuple inserts,
* updates and deletions in system catalogs (see CacheInvalidateHeapTuple). * updates and deletions in system catalogs (see CacheInvalidateHeapTuple).
* An update generates two inval events, one for the old tuple and one for * An update generates two inval events, one for the old tuple and one for
...@@ -63,6 +71,12 @@ typedef struct ...@@ -63,6 +71,12 @@ typedef struct
int16 id; /* type field --- must be first */ int16 id; /* type field --- must be first */
Oid dbId; /* database ID, or 0 if a shared relation */ Oid dbId; /* database ID, or 0 if a shared relation */
Oid relId; /* relation ID */ Oid relId; /* relation ID */
RelFileNode physId; /* physical file ID */
/*
* Note: it is likely that RelFileNode will someday be changed to
* include database ID. In that case the dbId field will be redundant
* and should be removed to save space.
*/
} SharedInvalRelcacheMsg; } SharedInvalRelcacheMsg;
typedef union typedef union
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/storage/smgr.h,v 1.39 2003/11/29 22:41:13 pgsql Exp $ * $PostgreSQL: pgsql/src/include/storage/smgr.h,v 1.40 2004/02/10 01:55:26 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -16,36 +16,54 @@ ...@@ -16,36 +16,54 @@
#include "access/xlog.h" #include "access/xlog.h"
#include "fmgr.h" #include "fmgr.h"
#include "storage/relfilenode.h"
#include "storage/block.h" #include "storage/block.h"
#include "utils/rel.h" #include "storage/relfilenode.h"
/*
* smgr.c maintains a table of SMgrRelation objects, which are essentially
* cached file handles. An SMgrRelation is created (if not already present)
* by smgropen(), and destroyed by smgrclose(). Note that neither of these
* operations imply I/O, they just create or destroy a hashtable entry.
* (But smgrclose() may release associated resources, such as OS-level file
* descriptors.)
*/
typedef struct SMgrRelationData
{
/* rnode is the hashtable lookup key, so it must be first! */
RelFileNode smgr_rnode; /* relation physical identifier */
/* additional public fields may someday exist here */
#define SM_FAIL 0 /*
#define SM_SUCCESS 1 * Fields below here are intended to be private to smgr.c and its
* submodules. Do not touch them from elsewhere.
*/
int smgr_which; /* storage manager selector */
#define DEFAULT_SMGR 0 struct _MdfdVec *md_fd; /* for md.c; NULL if not open */
} SMgrRelationData;
extern int smgrinit(void); typedef SMgrRelationData *SMgrRelation;
extern int smgrcreate(int16 which, Relation reln);
extern int smgrunlink(int16 which, Relation reln);
extern int smgrextend(int16 which, Relation reln, BlockNumber blocknum, extern void smgrinit(void);
char *buffer); extern SMgrRelation smgropen(RelFileNode rnode);
extern int smgropen(int16 which, Relation reln, bool failOK); extern void smgrclose(SMgrRelation reln);
extern int smgrclose(int16 which, Relation reln); extern void smgrcloseall(void);
extern int smgrread(int16 which, Relation reln, BlockNumber blocknum, extern void smgrclosenode(RelFileNode rnode);
char *buffer); extern void smgrcreate(SMgrRelation reln, bool isTemp, bool isRedo);
extern int smgrwrite(int16 which, Relation reln, BlockNumber blocknum, extern void smgrscheduleunlink(SMgrRelation reln, bool isTemp);
char *buffer); extern void smgrdounlink(SMgrRelation reln, bool isTemp, bool isRedo);
extern int smgrblindwrt(int16 which, RelFileNode rnode, extern void smgrextend(SMgrRelation reln, BlockNumber blocknum, char *buffer);
BlockNumber blkno, char *buffer); extern void smgrread(SMgrRelation reln, BlockNumber blocknum, char *buffer);
extern BlockNumber smgrnblocks(int16 which, Relation reln); extern void smgrwrite(SMgrRelation reln, BlockNumber blocknum, char *buffer);
extern BlockNumber smgrtruncate(int16 which, Relation reln, extern BlockNumber smgrnblocks(SMgrRelation reln);
BlockNumber nblocks); extern BlockNumber smgrtruncate(SMgrRelation reln, BlockNumber nblocks);
extern int smgrDoPendingDeletes(bool isCommit); extern void smgrDoPendingDeletes(bool isCommit);
extern int smgrcommit(void); extern void smgrcommit(void);
extern int smgrabort(void); extern void smgrabort(void);
extern int smgrsync(void); extern void smgrsync(void);
extern void smgr_redo(XLogRecPtr lsn, XLogRecord *record); extern void smgr_redo(XLogRecPtr lsn, XLogRecord *record);
extern void smgr_undo(XLogRecPtr lsn, XLogRecord *record); extern void smgr_undo(XLogRecPtr lsn, XLogRecord *record);
...@@ -55,38 +73,18 @@ extern void smgr_desc(char *buf, uint8 xl_info, char *rec); ...@@ -55,38 +73,18 @@ extern void smgr_desc(char *buf, uint8 xl_info, char *rec);
/* internals: move me elsewhere -- ay 7/94 */ /* internals: move me elsewhere -- ay 7/94 */
/* in md.c */ /* in md.c */
extern int mdinit(void); extern bool mdinit(void);
extern int mdcreate(Relation reln); extern bool mdclose(SMgrRelation reln);
extern int mdunlink(RelFileNode rnode); extern bool mdcreate(SMgrRelation reln, bool isRedo);
extern int mdextend(Relation reln, BlockNumber blocknum, char *buffer); extern bool mdunlink(RelFileNode rnode, bool isRedo);
extern int mdopen(Relation reln); extern bool mdextend(SMgrRelation reln, BlockNumber blocknum, char *buffer);
extern int mdclose(Relation reln); extern bool mdread(SMgrRelation reln, BlockNumber blocknum, char *buffer);
extern int mdread(Relation reln, BlockNumber blocknum, char *buffer); extern bool mdwrite(SMgrRelation reln, BlockNumber blocknum, char *buffer);
extern int mdwrite(Relation reln, BlockNumber blocknum, char *buffer); extern BlockNumber mdnblocks(SMgrRelation reln);
extern int mdblindwrt(RelFileNode rnode, BlockNumber blkno, char *buffer); extern BlockNumber mdtruncate(SMgrRelation reln, BlockNumber nblocks);
extern BlockNumber mdnblocks(Relation reln); extern bool mdcommit(void);
extern BlockNumber mdtruncate(Relation reln, BlockNumber nblocks); extern bool mdabort(void);
extern int mdcommit(void); extern bool mdsync(void);
extern int mdabort(void);
extern int mdsync(void);
/* mm.c */
extern int mminit(void);
extern int mmcreate(Relation reln);
extern int mmunlink(RelFileNode rnode);
extern int mmextend(Relation reln, BlockNumber blocknum, char *buffer);
extern int mmopen(Relation reln);
extern int mmclose(Relation reln);
extern int mmread(Relation reln, BlockNumber blocknum, char *buffer);
extern int mmwrite(Relation reln, BlockNumber blocknum, char *buffer);
extern int mmblindwrt(RelFileNode rnode, BlockNumber blkno, char *buffer);
extern BlockNumber mmnblocks(Relation reln);
extern BlockNumber mmtruncate(Relation reln, BlockNumber nblocks);
extern int mmcommit(void);
extern int mmabort(void);
extern int mmshutdown(void);
extern int MMShmemSize(void);
/* smgrtype.c */ /* smgrtype.c */
extern Datum smgrout(PG_FUNCTION_ARGS); extern Datum smgrout(PG_FUNCTION_ARGS);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/utils/inval.h,v 1.29 2003/11/29 22:41:15 pgsql Exp $ * $PostgreSQL: pgsql/src/include/utils/inval.h,v 1.30 2004/02/10 01:55:26 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -28,7 +28,9 @@ extern void CommandEndInvalidationMessages(bool isCommit); ...@@ -28,7 +28,9 @@ extern void CommandEndInvalidationMessages(bool isCommit);
extern void CacheInvalidateHeapTuple(Relation relation, HeapTuple tuple); extern void CacheInvalidateHeapTuple(Relation relation, HeapTuple tuple);
extern void CacheInvalidateRelcache(Oid relationId); extern void CacheInvalidateRelcache(Relation relation);
extern void CacheInvalidateRelcacheByTuple(HeapTuple classTuple);
extern void CacheRegisterSyscacheCallback(int cacheid, extern void CacheRegisterSyscacheCallback(int cacheid,
CacheCallbackFunction func, CacheCallbackFunction func,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/utils/rel.h,v 1.72 2004/01/06 18:07:32 neilc Exp $ * $PostgreSQL: pgsql/src/include/utils/rel.h,v 1.73 2004/02/10 01:55:27 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -20,7 +20,6 @@ ...@@ -20,7 +20,6 @@
#include "catalog/pg_index.h" #include "catalog/pg_index.h"
#include "rewrite/prs2lock.h" #include "rewrite/prs2lock.h"
#include "storage/block.h" #include "storage/block.h"
#include "storage/fd.h"
#include "storage/relfilenode.h" #include "storage/relfilenode.h"
...@@ -98,16 +97,16 @@ typedef struct PgStat_Info ...@@ -98,16 +97,16 @@ typedef struct PgStat_Info
bool index_scan_counted; bool index_scan_counted;
} PgStat_Info; } PgStat_Info;
/* /*
* Here are the contents of a relation cache entry. * Here are the contents of a relation cache entry.
*/ */
typedef struct RelationData typedef struct RelationData
{ {
File rd_fd; /* open file descriptor, or -1 if RelFileNode rd_node; /* relation physical identifier */
* none; this is NOT an operating /* use "struct" here to avoid needing to include smgr.h: */
* system file descriptor */ struct SMgrRelationData *rd_smgr; /* cached file handle, or NULL */
RelFileNode rd_node; /* file node (physical identifier) */
BlockNumber rd_nblocks; /* number of blocks in rel */ BlockNumber rd_nblocks; /* number of blocks in rel */
BlockNumber rd_targblock; /* current insertion target block, or BlockNumber rd_targblock; /* current insertion target block, or
* InvalidBlockNumber */ * InvalidBlockNumber */
...@@ -226,14 +225,6 @@ typedef Relation *RelationPtr; ...@@ -226,14 +225,6 @@ typedef Relation *RelationPtr;
*/ */
#define RelationGetRelid(relation) ((relation)->rd_id) #define RelationGetRelid(relation) ((relation)->rd_id)
/*
* RelationGetFile
* Returns the open file descriptor for the rel, or -1 if
* none. This is NOT an operating system file descriptor; see md.c
* for more information
*/
#define RelationGetFile(relation) ((relation)->rd_fd)
/* /*
* RelationGetNumberOfAttributes * RelationGetNumberOfAttributes
* Returns the number of attributes in a relation. * Returns the number of attributes in a relation.
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/utils/relcache.h,v 1.38 2003/11/29 22:41:16 pgsql Exp $ * $PostgreSQL: pgsql/src/include/utils/relcache.h,v 1.39 2004/02/10 01:55:27 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -24,7 +24,6 @@ extern Relation RelationSysNameGetRelation(const char *relationName); ...@@ -24,7 +24,6 @@ extern Relation RelationSysNameGetRelation(const char *relationName);
/* finds an existing cache entry, but won't make a new one */ /* finds an existing cache entry, but won't make a new one */
extern Relation RelationIdCacheGetRelation(Oid relationId); extern Relation RelationIdCacheGetRelation(Oid relationId);
extern Relation RelationNodeCacheGetRelation(RelFileNode rnode);
extern void RelationClose(Relation relation); extern void RelationClose(Relation relation);
...@@ -61,7 +60,7 @@ extern Relation RelationBuildLocalRelation(const char *relname, ...@@ -61,7 +60,7 @@ extern Relation RelationBuildLocalRelation(const char *relname,
*/ */
extern void RelationForgetRelation(Oid rid); extern void RelationForgetRelation(Oid rid);
extern void RelationIdInvalidateRelationCacheByRelationId(Oid relationId); extern void RelationCacheInvalidateEntry(Oid relationId, RelFileNode *rnode);
extern void RelationCacheInvalidate(void); extern void RelationCacheInvalidate(void);
...@@ -73,11 +72,6 @@ extern void AtEOXact_RelationCache(bool commit); ...@@ -73,11 +72,6 @@ extern void AtEOXact_RelationCache(bool commit);
extern bool RelationIdIsInInitFile(Oid relationId); extern bool RelationIdIsInInitFile(Oid relationId);
extern void RelationCacheInitFileInvalidate(bool beforeSend); extern void RelationCacheInitFileInvalidate(bool beforeSend);
/* XLOG support */
extern void CreateDummyCaches(void);
extern void DestroyDummyCaches(void);
/* should be used only by relcache.c and catcache.c */ /* should be used only by relcache.c and catcache.c */
extern bool criticalRelcachesBuilt; extern bool criticalRelcachesBuilt;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment