*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/storage/smgr/md.c,v 1.51 1999/07/17 20:17:48 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/storage/smgr/md.c,v 1.52 1999/09/02 02:57:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* In order to do that, we break relations up into chunks of < 2GBytes
* and store one chunk in each of several files that represent the relation.
* See the BLCKSZ and RELSEG_SIZE configuration constants in include/config.h.
+ *
+ * The file descriptor stored in the relation cache (see RelationGetFile())
+ * is actually an index into the Md_fdvec array. -1 indicates not open.
+ *
+ * When a relation is broken into multiple chunks, only the first chunk
+ * 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
#endif
} MdfdVec;
-static int Nfds = 100;
+static int Nfds = 100; /* initial/current size of Md_fdvec array */
static MdfdVec *Md_fdvec = (MdfdVec *) NULL;
-static int Md_Free = -1;
-static int CurFd = 0;
-static MemoryContext MdCxt;
+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 my allocations */
#define MDFD_DIRTY (uint16) 0x01
#define MDFD_FREE (uint16) 0x02
/* routines declared here */
+static int _mdfd_getrelnfd(Relation reln);
static MdfdVec *_mdfd_openseg(Relation reln, int segno, int oflags);
-static MdfdVec *_mdfd_getseg(Relation reln, int blkno, int oflag);
+static MdfdVec *_mdfd_getseg(Relation reln, int blkno);
static int _fdvec_alloc(void);
static void _fdvec_free(int);
static BlockNumber _mdnblocks(File file, Size blcksz);
int
mdunlink(Relation reln)
{
+ int nblocks;
int fd;
- int i;
- MdfdVec *v,
- *ov;
+ MdfdVec *v;
MemoryContext oldcxt;
- char fname[NAMEDATALEN];
- char tname[NAMEDATALEN + 10]; /* leave room for overflow
- * suffixes */
/*
- * On Windows NT you can't unlink a file if it is open so we have * to
- * do this.
+ * Force all segments of the relation to be opened, so that we
+ * won't miss deleting any of them.
*/
+ nblocks = mdnblocks(reln);
- StrNCpy(fname, RelationGetRelationName(reln)->data, NAMEDATALEN);
-
- if (FileNameUnlink(fname) < 0)
- return SM_FAIL;
-
- /* unlink all the overflow files for large relations */
- for (i = 1;; i++)
- {
- sprintf(tname, "%s.%d", fname, i);
- if (FileNameUnlink(tname) < 0)
- break;
- }
-
- /* finally, clean out the mdfd vector */
+ /*
+ * Clean out the mdfd vector, letting fd.c unlink the physical files.
+ *
+ * NOTE: We truncate the file(s) before deleting 'em, because if other
+ * backends are holding the files open, the unlink will fail on some
+ * platforms (think Microsoft). Better a zero-size file gets left around
+ * than a big file. Those other backends will be forced to close the
+ * relation by cache invalidation, but that probably hasn't happened yet.
+ */
fd = RelationGetFile(reln);
+ if (fd < 0) /* should not happen */
+ elog(ERROR, "mdunlink: mdnblocks didn't open relation");
+
Md_fdvec[fd].mdfd_flags = (uint16) 0;
oldcxt = MemoryContextSwitchTo(MdCxt);
#ifndef LET_OS_MANAGE_FILESIZE
for (v = &Md_fdvec[fd]; v != (MdfdVec *) NULL;)
{
+ MdfdVec *ov = v;
+ FileTruncate(v->mdfd_vfd, 0);
FileUnlink(v->mdfd_vfd);
- ov = v;
v = v->mdfd_chain;
if (ov != &Md_fdvec[fd])
pfree(ov);
Md_fdvec[fd].mdfd_chain = (MdfdVec *) NULL;
#else
v = &Md_fdvec[fd];
- if (v != (MdfdVec *) NULL)
- FileUnlink(v->mdfd_vfd);
+ FileTruncate(v->mdfd_vfd, 0);
+ FileUnlink(v->mdfd_vfd);
#endif
MemoryContextSwitchTo(oldcxt);
_fdvec_free(fd);
+ /* be sure to mark relation closed */
+ reln->rd_fd = -1;
+
return SM_SUCCESS;
}
MdfdVec *v;
nblocks = mdnblocks(reln);
- v = _mdfd_getseg(reln, nblocks, O_CREAT);
+ v = _mdfd_getseg(reln, nblocks);
if ((pos = FileSeek(v->mdfd_vfd, 0L, SEEK_END)) < 0)
return SM_FAIL;
}
/*
- * mdclose() -- Close the specified relation
+ * mdclose() -- Close the specified relation, if it isn't closed already.
*
* AND FREE fd vector! It may be re-used for other relation!
* reln should be flushed from cache after closing !..
mdclose(Relation reln)
{
int fd;
- MdfdVec *v,
- *ov;
+ MdfdVec *v;
MemoryContext oldcxt;
fd = RelationGetFile(reln);
+ if (fd < 0)
+ return SM_SUCCESS; /* already closed, so no work */
oldcxt = MemoryContextSwitchTo(MdCxt);
#ifndef LET_OS_MANAGE_FILESIZE
for (v = &Md_fdvec[fd]; v != (MdfdVec *) NULL;)
{
+ MdfdVec *ov = v;
+
/* if not closed already */
if (v->mdfd_vfd >= 0)
{
v->mdfd_flags &= ~MDFD_DIRTY;
}
/* Now free vector */
- ov = v;
v = v->mdfd_chain;
if (ov != &Md_fdvec[fd])
pfree(ov);
_fdvec_free(fd);
+ /* be sure to mark relation closed */
+ reln->rd_fd = -1;
+
return SM_SUCCESS;
}
int nbytes;
MdfdVec *v;
- v = _mdfd_getseg(reln, blocknum, 0);
+ v = _mdfd_getseg(reln, blocknum);
#ifndef LET_OS_MANAGE_FILESIZE
seekpos = (long) (BLCKSZ * (blocknum % RELSEG_SIZE));
long seekpos;
MdfdVec *v;
- v = _mdfd_getseg(reln, blocknum, 0);
+ v = _mdfd_getseg(reln, blocknum);
#ifndef LET_OS_MANAGE_FILESIZE
seekpos = (long) (BLCKSZ * (blocknum % RELSEG_SIZE));
long seekpos;
MdfdVec *v;
- v = _mdfd_getseg(reln, blocknum, 0);
+ v = _mdfd_getseg(reln, blocknum);
#ifndef LET_OS_MANAGE_FILESIZE
seekpos = (long) (BLCKSZ * (blocknum % RELSEG_SIZE));
/*
* mdnblocks() -- Get the number of blocks stored in a relation.
*
- * Returns # of blocks or -1 on error.
+ * Important side effect: all segments of the relation are opened
+ * and added to the mdfd_chain list. If this routine has not been
+ * called, then only segments up to the last one actually touched
+ * are present in the chain...
+ *
+ * Returns # of blocks, elog's on error.
*/
int
mdnblocks(Relation reln)
{
int fd;
MdfdVec *v;
+#ifndef LET_OS_MANAGE_FILESIZE
int nblocks;
int segno;
+#endif
- fd = RelationGetFile(reln);
+ fd = _mdfd_getrelnfd(reln);
v = &Md_fdvec[fd];
#ifndef LET_OS_MANAGE_FILESIZE
-#ifdef DIAGNOSTIC
- if (_mdnblocks(v->mdfd_vfd, BLCKSZ) > RELSEG_SIZE)
- elog(FATAL, "segment too big in getseg!");
-#endif
-
segno = 0;
for (;;)
{
int
mdtruncate(Relation reln, int nblocks)
{
+ int curnblk;
int fd;
MdfdVec *v;
-
#ifndef LET_OS_MANAGE_FILESIZE
- int curnblk,
- i,
- oldsegno,
- newsegno,
- lastsegblocks;
- MdfdVec **varray;
+ MemoryContext oldcxt;
+ int priorblocks;
+#endif
+ /* NOTE: mdnblocks makes sure we have opened all existing segments,
+ * so that truncate/delete loop will get them all!
+ */
curnblk = mdnblocks(reln);
- if (nblocks > curnblk)
- return -1;
- oldsegno = curnblk / RELSEG_SIZE;
- newsegno = nblocks / RELSEG_SIZE;
-
-#endif
+ if (nblocks < 0 || nblocks > curnblk)
+ return -1; /* bogus request */
+ if (nblocks == curnblk)
+ return nblocks; /* no work */
- fd = RelationGetFile(reln);
+ fd = _mdfd_getrelnfd(reln);
v = &Md_fdvec[fd];
#ifndef LET_OS_MANAGE_FILESIZE
- varray = (MdfdVec **)palloc((oldsegno + 1) * sizeof(MdfdVec *));
- for (i = 0; i <= oldsegno; i++)
- {
- if (!v)
- elog(ERROR,"segment isn't open in mdtruncate!");
- varray[i] = v;
- v = v->mdfd_chain;
- }
- for (i = oldsegno; i > newsegno; i--)
+ oldcxt = MemoryContextSwitchTo(MdCxt);
+ priorblocks = 0;
+ while (v != (MdfdVec *) NULL)
{
- v = varray[i];
- if (FileTruncate(v->mdfd_vfd, 0) < 0)
+ MdfdVec *ov = v;
+
+ if (priorblocks > nblocks)
{
- pfree(varray);
- return -1;
+ /* This segment is no longer wanted at all (and has already been
+ * unlinked from the mdfd_chain).
+ * We truncate the file before deleting it because if other
+ * backends are holding the file open, the unlink will fail on
+ * some platforms. Better a zero-size file gets left around than
+ * a big file...
+ */
+ FileTruncate(v->mdfd_vfd, 0);
+ FileUnlink(v->mdfd_vfd);
+ v = v->mdfd_chain;
+ Assert(ov != &Md_fdvec[fd]); /* we never drop the 1st segment */
+ pfree(ov);
}
- v->mdfd_lstbcnt = 0;
+ else if (priorblocks + RELSEG_SIZE > nblocks)
+ {
+ /* This is the last segment we want to keep.
+ * Truncate the file to the right length, and clear chain link
+ * that points to any remaining segments (which we shall zap).
+ * NOTE: if nblocks is exactly a multiple K of RELSEG_SIZE,
+ * we will truncate the K+1st segment to 0 length but keep it.
+ * This is mainly so that the right thing happens if nblocks=0.
+ */
+ int lastsegblocks = nblocks - priorblocks;
+ if (FileTruncate(v->mdfd_vfd, lastsegblocks * BLCKSZ) < 0)
+ return -1;
+ v->mdfd_lstbcnt = lastsegblocks;
+ v = v->mdfd_chain;
+ ov->mdfd_chain = (MdfdVec *) NULL;
+ }
+ else
+ {
+ /* We still need this segment and 0 or more blocks beyond it,
+ * so nothing to do here.
+ */
+ v = v->mdfd_chain;
+ }
+ priorblocks += RELSEG_SIZE;
}
- /* Calculate the # of blocks in the last segment */
- lastsegblocks = nblocks - (newsegno * RELSEG_SIZE);
- v = varray[i];
- pfree(varray);
- if (FileTruncate(v->mdfd_vfd, lastsegblocks * BLCKSZ) < 0)
- return -1;
- v->mdfd_lstbcnt = lastsegblocks;
+ MemoryContextSwitchTo(oldcxt);
#else
if (FileTruncate(v->mdfd_vfd, nblocks * BLCKSZ) < 0)
return -1;
{
#ifndef LET_OS_MANAGE_FILESIZE
for (v = &Md_fdvec[i]; v != (MdfdVec *) NULL; v = v->mdfd_chain)
+ v->mdfd_flags &= ~MDFD_DIRTY;
#else
v = &Md_fdvec[i];
- if (v != (MdfdVec *) NULL)
+ v->mdfd_flags &= ~MDFD_DIRTY;
#endif
- v->mdfd_flags &= ~MDFD_DIRTY;
}
return SM_SUCCESS;
{
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;
/* open the file */
#ifndef __CYGWIN32__
- fd = PathNameOpenFile(fullpath, O_RDWR | oflags, 0600);
+ fd = FileNameOpenFile(fullpath, O_RDWR | oflags, 0600);
#else
- fd = PathNameOpenFile(fullpath, O_RDWR | O_BINARY | oflags, 0600);
+ fd = FileNameOpenFile(fullpath, O_RDWR | O_BINARY | oflags, 0600);
#endif
if (dofree)
return v;
}
-static MdfdVec *
-_mdfd_getseg(Relation reln, int blkno, int oflag)
+/* Get the fd for the relation, opening it if it's not already open */
+
+static int
+_mdfd_getrelnfd(Relation reln)
{
- MdfdVec *v;
- int segno;
int fd;
- int i;
fd = RelationGetFile(reln);
if (fd < 0)
RelationGetRelationName(reln));
reln->rd_fd = fd;
}
+ return fd;
+}
+
+/* Find the segment of the relation holding the specified block */
+
+static MdfdVec *
+_mdfd_getseg(Relation reln, int blkno)
+{
+ MdfdVec *v;
+ int segno;
+ int fd;
+ int i;
+
+ fd = _mdfd_getrelnfd(reln);
#ifndef LET_OS_MANAGE_FILESIZE
for (v = &Md_fdvec[fd], segno = blkno / RELSEG_SIZE, i = 1;
if (v->mdfd_chain == (MdfdVec *) NULL)
{
- v->mdfd_chain = _mdfd_openseg(reln, i, oflag);
+ v->mdfd_chain = _mdfd_openseg(reln, i, O_CREAT);
if (v->mdfd_chain == (MdfdVec *) NULL)
elog(ERROR, "cannot open segment %d of relation %s",