Commit ef072219 authored by Tom Lane's avatar Tom Lane

Clean up smgr.c/md.c APIs as per discussion a couple months ago. Instead of

having md.c return a success/failure boolean to smgr.c, which was just going
to elog anyway, let md.c issue the elog messages itself.  This allows better
error reporting, particularly in cases such as "short read" or "short write"
which Peter was complaining of.  Also, remove the kluge of allowing mdread()
to return zeroes from a read-beyond-EOF: this is now an error condition
except when InRecovery or zero_damaged_pages = true.  (Hash indexes used to
require that behavior, but no more.)  Also, enforce that mdwrite() is to be
used for rewriting existing blocks while mdextend() is to be used for
extending the relation EOF.  This restriction lets us get rid of the old
ad-hoc defense against creating huge files by an accidental reference to
a bogus block number: we'll only create new segments in mdextend() not
mdwrite() or mdread().  (Again, when InRecovery we allow it anyway, since
we need to allow updates of blocks that were later truncated away.)
Also, clean up the original makeshift patch for bug #2737: move the
responsibility for padding relation segments to full length into md.c.
parent 990fea84
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/hash/hashpage.c,v 1.61 2006/11/19 21:33:23 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/hash/hashpage.c,v 1.62 2007/01/03 18:11:01 tgl Exp $
* *
* NOTES * NOTES
* Postgres hash pages look like ordinary relation pages. The opaque * Postgres hash pages look like ordinary relation pages. The opaque
...@@ -533,10 +533,8 @@ fail: ...@@ -533,10 +533,8 @@ fail:
* *
* This does not need to initialize the new bucket pages; we'll do that as * This does not need to initialize the new bucket pages; we'll do that as
* each one is used by _hash_expandtable(). But we have to extend the logical * each one is used by _hash_expandtable(). But we have to extend the logical
* EOF to the end of the splitpoint; otherwise the first overflow page * EOF to the end of the splitpoint; this keeps smgr's idea of the EOF in
* allocated beyond the splitpoint will represent a noncontiguous access, * sync with ours, so that overflow-page allocation works correctly.
* which can confuse md.c (and will probably be forbidden by future changes
* to md.c).
* *
* We do this by writing a page of zeroes at the end of the splitpoint range. * We do this by writing a page of zeroes at the end of the splitpoint range.
* We expect that the filesystem will ensure that the intervening pages read * We expect that the filesystem will ensure that the intervening pages read
...@@ -559,7 +557,6 @@ _hash_alloc_buckets(Relation rel, uint32 nblocks) ...@@ -559,7 +557,6 @@ _hash_alloc_buckets(Relation rel, uint32 nblocks)
{ {
BlockNumber firstblock; BlockNumber firstblock;
BlockNumber lastblock; BlockNumber lastblock;
BlockNumber endblock;
char zerobuf[BLCKSZ]; char zerobuf[BLCKSZ];
/* /*
...@@ -577,24 +574,9 @@ _hash_alloc_buckets(Relation rel, uint32 nblocks) ...@@ -577,24 +574,9 @@ _hash_alloc_buckets(Relation rel, uint32 nblocks)
if (lastblock < firstblock || lastblock == InvalidBlockNumber) if (lastblock < firstblock || lastblock == InvalidBlockNumber)
return InvalidBlockNumber; return InvalidBlockNumber;
/* Note: we assume RelationGetNumberOfBlocks did RelationOpenSmgr for us */
MemSet(zerobuf, 0, sizeof(zerobuf)); MemSet(zerobuf, 0, sizeof(zerobuf));
/* /* Note: we assume RelationGetNumberOfBlocks did RelationOpenSmgr for us */
* XXX If the extension results in creation of new segment files,
* we have to make sure that each non-last file is correctly filled out to
* RELSEG_SIZE blocks. This ought to be done inside mdextend, but
* changing the smgr API seems best left for development cycle not late
* beta. Temporary fix for bug #2737.
*/
#ifndef LET_OS_MANAGE_FILESIZE
for (endblock = firstblock | (RELSEG_SIZE - 1);
endblock < lastblock;
endblock += RELSEG_SIZE)
smgrextend(rel->rd_smgr, endblock, zerobuf, rel->rd_istemp);
#endif
smgrextend(rel->rd_smgr, lastblock, zerobuf, rel->rd_istemp); smgrextend(rel->rd_smgr, lastblock, zerobuf, rel->rd_istemp);
return firstblock; return firstblock;
......
...@@ -36,9 +36,9 @@ ...@@ -36,9 +36,9 @@
* that is of no value (since other backends have no interest in them yet) * that is of no value (since other backends have no interest in them yet)
* and it created locking problems for CHECKPOINT, because the upper-level * and it created locking problems for CHECKPOINT, because the upper-level
* pages were held exclusive-locked for long periods. Now we just build * pages were held exclusive-locked for long periods. Now we just build
* the pages in local memory and smgrwrite() them as we finish them. They * the pages in local memory and smgrwrite or smgrextend them as we finish
* will need to be re-read into shared buffers on first use after the build * them. They will need to be re-read into shared buffers on first use after
* finishes. * the build finishes.
* *
* Since the index will never be used unless it is completely built, * Since the index will never be used unless it is completely built,
* from a crash-recovery point of view there is no need to WAL-log the * from a crash-recovery point of view there is no need to WAL-log the
...@@ -57,7 +57,7 @@ ...@@ -57,7 +57,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/nbtsort.c,v 1.107 2006/10/04 00:29:49 momjian Exp $ * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtsort.c,v 1.108 2007/01/03 18:11:01 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -309,7 +309,7 @@ _bt_blwritepage(BTWriteState *wstate, Page page, BlockNumber blkno) ...@@ -309,7 +309,7 @@ _bt_blwritepage(BTWriteState *wstate, Page page, BlockNumber blkno)
{ {
if (!wstate->btws_zeropage) if (!wstate->btws_zeropage)
wstate->btws_zeropage = (Page) palloc0(BLCKSZ); wstate->btws_zeropage = (Page) palloc0(BLCKSZ);
smgrwrite(wstate->index->rd_smgr, wstate->btws_pages_written++, smgrextend(wstate->index->rd_smgr, wstate->btws_pages_written++,
(char *) wstate->btws_zeropage, (char *) wstate->btws_zeropage,
true); true);
} }
...@@ -319,10 +319,17 @@ _bt_blwritepage(BTWriteState *wstate, Page page, BlockNumber blkno) ...@@ -319,10 +319,17 @@ _bt_blwritepage(BTWriteState *wstate, Page page, BlockNumber blkno)
* index, because there's no need for smgr to schedule an fsync for this * index, because there's no need for smgr to schedule an fsync for this
* write; we'll do it ourselves before ending the build. * write; we'll do it ourselves before ending the build.
*/ */
smgrwrite(wstate->index->rd_smgr, blkno, (char *) page, true);
if (blkno == wstate->btws_pages_written) if (blkno == wstate->btws_pages_written)
{
/* extending the file... */
smgrextend(wstate->index->rd_smgr, blkno, (char *) page, true);
wstate->btws_pages_written++; wstate->btws_pages_written++;
}
else
{
/* overwriting a block we zero-filled before */
smgrwrite(wstate->index->rd_smgr, blkno, (char *) page, true);
}
pfree(page); pfree(page);
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.208 2006/12/30 21:21:53 tgl Exp $ * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.209 2007/01/03 18:11:01 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -6083,7 +6083,7 @@ copy_relation_data(Relation rel, SMgrRelation dst) ...@@ -6083,7 +6083,7 @@ copy_relation_data(Relation rel, SMgrRelation dst)
* rel, because there's no need for smgr to schedule an fsync for this * rel, because there's no need for smgr to schedule an fsync for this
* write; we'll do it ourselves below. * write; we'll do it ourselves below.
*/ */
smgrwrite(dst, blkno, buf, true); smgrextend(dst, blkno, buf, true);
} }
/* /*
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/smgr/md.c,v 1.123 2006/11/20 01:07:56 tgl Exp $ * $PostgreSQL: pgsql/src/backend/storage/smgr/md.c,v 1.124 2007/01/03 18:11:01 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "miscadmin.h" #include "miscadmin.h"
#include "postmaster/bgwriter.h" #include "postmaster/bgwriter.h"
#include "storage/fd.h" #include "storage/fd.h"
#include "storage/bufmgr.h"
#include "storage/smgr.h" #include "storage/smgr.h"
#include "utils/hsearch.h" #include "utils/hsearch.h"
#include "utils/memutils.h" #include "utils/memutils.h"
...@@ -108,9 +109,16 @@ typedef struct ...@@ -108,9 +109,16 @@ typedef struct
static HTAB *pendingOpsTable = NULL; static HTAB *pendingOpsTable = NULL;
typedef enum /* behavior for mdopen & _mdfd_getseg */
{
EXTENSION_FAIL, /* ereport if segment not present */
EXTENSION_RETURN_NULL, /* return NULL if not present */
EXTENSION_CREATE /* create new segments as needed */
} ExtensionBehavior;
/* local routines */ /* local routines */
static MdfdVec *mdopen(SMgrRelation reln, bool allowNotFound); static MdfdVec *mdopen(SMgrRelation reln, ExtensionBehavior behavior);
static bool register_dirty_segment(SMgrRelation reln, MdfdVec *seg); static void register_dirty_segment(SMgrRelation reln, MdfdVec *seg);
static MdfdVec *_fdvec_alloc(void); static MdfdVec *_fdvec_alloc(void);
#ifndef LET_OS_MANAGE_FILESIZE #ifndef LET_OS_MANAGE_FILESIZE
...@@ -118,14 +126,14 @@ static MdfdVec *_mdfd_openseg(SMgrRelation reln, BlockNumber segno, ...@@ -118,14 +126,14 @@ static MdfdVec *_mdfd_openseg(SMgrRelation reln, BlockNumber segno,
int oflags); int oflags);
#endif #endif
static MdfdVec *_mdfd_getseg(SMgrRelation reln, BlockNumber blkno, static MdfdVec *_mdfd_getseg(SMgrRelation reln, BlockNumber blkno,
bool allowNotFound); bool isTemp, ExtensionBehavior behavior);
static BlockNumber _mdnblocks(File file, Size blcksz); static BlockNumber _mdnblocks(SMgrRelation reln, MdfdVec *seg);
/* /*
* mdinit() -- Initialize private state for magnetic disk storage manager. * mdinit() -- Initialize private state for magnetic disk storage manager.
*/ */
bool void
mdinit(void) mdinit(void)
{ {
MdCxt = AllocSetContextCreate(TopMemoryContext, MdCxt = AllocSetContextCreate(TopMemoryContext,
...@@ -154,8 +162,6 @@ mdinit(void) ...@@ -154,8 +162,6 @@ mdinit(void)
&hash_ctl, &hash_ctl,
HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT); HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
} }
return true;
} }
/* /*
...@@ -163,14 +169,14 @@ mdinit(void) ...@@ -163,14 +169,14 @@ mdinit(void)
* *
* If isRedo is true, it's okay for the relation to exist already. * If isRedo is true, it's okay for the relation to exist already.
*/ */
bool void
mdcreate(SMgrRelation reln, bool isRedo) mdcreate(SMgrRelation reln, bool isRedo)
{ {
char *path; char *path;
File fd; File fd;
if (isRedo && reln->md_fd != NULL) if (isRedo && reln->md_fd != NULL)
return true; /* created and opened already... */ return; /* created and opened already... */
Assert(reln->md_fd == NULL); Assert(reln->md_fd == NULL);
...@@ -193,11 +199,15 @@ mdcreate(SMgrRelation reln, bool isRedo) ...@@ -193,11 +199,15 @@ mdcreate(SMgrRelation reln, bool isRedo)
if (fd < 0) if (fd < 0)
{ {
pfree(path); pfree(path);
/* be sure to return the error reported by create, not open */ /* be sure to report the error reported by create, not open */
errno = save_errno; errno = save_errno;
return false; ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not create relation %u/%u/%u: %m",
reln->smgr_rnode.spcNode,
reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode)));
} }
errno = 0;
} }
pfree(path); pfree(path);
...@@ -209,8 +219,6 @@ mdcreate(SMgrRelation reln, bool isRedo) ...@@ -209,8 +219,6 @@ mdcreate(SMgrRelation reln, bool isRedo)
#ifndef LET_OS_MANAGE_FILESIZE #ifndef LET_OS_MANAGE_FILESIZE
reln->md_fd->mdfd_chain = NULL; reln->md_fd->mdfd_chain = NULL;
#endif #endif
return true;
} }
/* /*
...@@ -220,12 +228,12 @@ mdcreate(SMgrRelation reln, bool isRedo) ...@@ -220,12 +228,12 @@ mdcreate(SMgrRelation reln, bool isRedo)
* there won't be an SMgrRelation hashtable entry anymore. * there won't be an SMgrRelation hashtable entry anymore.
* *
* If isRedo is true, it's okay for the relation to be already gone. * If isRedo is true, it's okay for the relation to be already gone.
* Also, any failure should be reported as WARNING not ERROR, because
* we are usually not in a transaction anymore when this is called.
*/ */
bool void
mdunlink(RelFileNode rnode, bool isRedo) mdunlink(RelFileNode rnode, bool isRedo)
{ {
bool status = true;
int save_errno = 0;
char *path; char *path;
path = relpath(rnode); path = relpath(rnode);
...@@ -234,15 +242,17 @@ mdunlink(RelFileNode rnode, bool isRedo) ...@@ -234,15 +242,17 @@ mdunlink(RelFileNode rnode, bool isRedo)
if (unlink(path) < 0) if (unlink(path) < 0)
{ {
if (!isRedo || errno != ENOENT) if (!isRedo || errno != ENOENT)
{ ereport(WARNING,
status = false; (errcode_for_file_access(),
save_errno = errno; errmsg("could not remove relation %u/%u/%u: %m",
} rnode.spcNode,
rnode.dbNode,
rnode.relNode)));
} }
#ifndef LET_OS_MANAGE_FILESIZE #ifndef LET_OS_MANAGE_FILESIZE
/* Delete the additional segments, if any */ /* Delete the additional segments, if any */
if (status) else
{ {
char *segpath = (char *) palloc(strlen(path) + 12); char *segpath = (char *) palloc(strlen(path) + 12);
BlockNumber segno; BlockNumber segno;
...@@ -258,10 +268,13 @@ mdunlink(RelFileNode rnode, bool isRedo) ...@@ -258,10 +268,13 @@ mdunlink(RelFileNode rnode, bool isRedo)
{ {
/* ENOENT is expected after the last segment... */ /* ENOENT is expected after the last segment... */
if (errno != ENOENT) if (errno != ENOENT)
{ ereport(WARNING,
status = false; (errcode_for_file_access(),
save_errno = errno; errmsg("could not remove segment %u of relation %u/%u/%u: %m",
} segno,
rnode.spcNode,
rnode.dbNode,
rnode.relNode)));
break; break;
} }
} }
...@@ -270,29 +283,44 @@ mdunlink(RelFileNode rnode, bool isRedo) ...@@ -270,29 +283,44 @@ mdunlink(RelFileNode rnode, bool isRedo)
#endif #endif
pfree(path); pfree(path);
errno = save_errno;
return status;
} }
/* /*
* mdextend() -- Add a block to the specified relation. * mdextend() -- Add a block to the specified relation.
* *
* The semantics are basically the same as mdwrite(): write at the * The semantics are nearly the same as mdwrite(): write at the
* specified position. However, we are expecting to extend the * specified position. However, this is to be used for the case of
* relation (ie, blocknum is >= the current EOF), and so in case of * extending a relation (i.e., blocknum is at or beyond the current
* failure we clean up by truncating. * EOF). Note that we assume writing a block beyond current EOF
* * causes intervening file space to become filled with zeroes.
* This routine returns true or false, with errno set as appropriate.
*/ */
bool void
mdextend(SMgrRelation reln, BlockNumber blocknum, char *buffer, bool isTemp) mdextend(SMgrRelation reln, BlockNumber blocknum, char *buffer, bool isTemp)
{ {
long seekpos; long seekpos;
int nbytes; int nbytes;
MdfdVec *v; MdfdVec *v;
v = _mdfd_getseg(reln, blocknum, false); /* This assert is too expensive to have on normally ... */
#ifdef CHECK_WRITE_VS_EXTEND
Assert(blocknum >= mdnblocks(reln));
#endif
/*
* If a relation manages to grow to 2^32-1 blocks, refuse to extend it
* any more --- we mustn't create a block whose number
* actually is InvalidBlockNumber.
*/
if (blocknum == InvalidBlockNumber)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("cannot extend relation %u/%u/%u beyond %u blocks",
reln->smgr_rnode.spcNode,
reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode,
InvalidBlockNumber)));
v = _mdfd_getseg(reln, blocknum, isTemp, EXTENSION_CREATE);
#ifndef LET_OS_MANAGE_FILESIZE #ifndef LET_OS_MANAGE_FILESIZE
seekpos = (long) (BLCKSZ * (blocknum % ((BlockNumber) RELSEG_SIZE))); seekpos = (long) (BLCKSZ * (blocknum % ((BlockNumber) RELSEG_SIZE)));
...@@ -302,52 +330,64 @@ mdextend(SMgrRelation reln, BlockNumber blocknum, char *buffer, bool isTemp) ...@@ -302,52 +330,64 @@ mdextend(SMgrRelation reln, BlockNumber blocknum, char *buffer, bool isTemp)
#endif #endif
/* /*
* Note: because caller obtained blocknum by calling _mdnblocks, which did * Note: because caller usually obtained blocknum by calling mdnblocks,
* a seek(SEEK_END), this seek is often redundant and will be optimized * which did a seek(SEEK_END), this seek is often redundant and will be
* away by fd.c. It's not redundant, however, if there is a partial page * optimized away by fd.c. It's not redundant, however, if there is a
* at the end of the file. In that case we want to try to overwrite the * partial page at the end of the file. In that case we want to try to
* partial page with a full page. It's also not redundant if bufmgr.c had * overwrite the partial page with a full page. It's also not redundant
* to dump another buffer of the same file to make room for the new page's * if bufmgr.c had to dump another buffer of the same file to make room
* buffer. * 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 false; ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not seek to block %u of relation %u/%u/%u: %m",
blocknum,
reln->smgr_rnode.spcNode,
reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode)));
if ((nbytes = FileWrite(v->mdfd_vfd, buffer, BLCKSZ)) != BLCKSZ) if ((nbytes = FileWrite(v->mdfd_vfd, buffer, BLCKSZ)) != BLCKSZ)
{ {
if (nbytes > 0) if (nbytes < 0)
{ ereport(ERROR,
int save_errno = errno; (errcode_for_file_access(),
errmsg("could not extend relation %u/%u/%u: %m",
/* Remove the partially-written page */ reln->smgr_rnode.spcNode,
FileTruncate(v->mdfd_vfd, seekpos); reln->smgr_rnode.dbNode,
FileSeek(v->mdfd_vfd, seekpos, SEEK_SET); reln->smgr_rnode.relNode),
errno = save_errno; errhint("Check free disk space.")));
} /* short write: complain appropriately */
return false; ereport(ERROR,
(errcode(ERRCODE_DISK_FULL),
errmsg("could not extend relation %u/%u/%u: wrote only %d of %d bytes at block %u",
reln->smgr_rnode.spcNode,
reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode,
nbytes, BLCKSZ, blocknum),
errhint("Check free disk space.")));
} }
if (!isTemp) if (!isTemp)
{ register_dirty_segment(reln, v);
if (!register_dirty_segment(reln, v))
return false;
}
#ifndef LET_OS_MANAGE_FILESIZE #ifndef LET_OS_MANAGE_FILESIZE
Assert(_mdnblocks(v->mdfd_vfd, BLCKSZ) <= ((BlockNumber) RELSEG_SIZE)); Assert(_mdnblocks(reln, v) <= ((BlockNumber) RELSEG_SIZE));
#endif #endif
return true;
} }
/* /*
* mdopen() -- Open the specified relation. ereport's on failure. * mdopen() -- Open the specified relation.
* (Optionally, can return NULL instead of ereport for ENOENT.)
* *
* Note we only open the first segment, when there are multiple segments. * Note we only open the first segment, when there are multiple segments.
*
* If first segment is not present, either ereport or return NULL according
* to "behavior". We treat EXTENSION_CREATE the same as EXTENSION_FAIL;
* EXTENSION_CREATE means it's OK to extend an existing relation, not to
* invent one out of whole cloth.
*/ */
static MdfdVec * static MdfdVec *
mdopen(SMgrRelation reln, bool allowNotFound) mdopen(SMgrRelation reln, ExtensionBehavior behavior)
{ {
MdfdVec *mdfd; MdfdVec *mdfd;
char *path; char *path;
...@@ -374,7 +414,7 @@ mdopen(SMgrRelation reln, bool allowNotFound) ...@@ -374,7 +414,7 @@ mdopen(SMgrRelation reln, bool allowNotFound)
if (fd < 0) if (fd < 0)
{ {
pfree(path); pfree(path);
if (allowNotFound && errno == ENOENT) if (behavior == EXTENSION_RETURN_NULL && errno == ENOENT)
return NULL; return NULL;
ereport(ERROR, ereport(ERROR,
(errcode_for_file_access(), (errcode_for_file_access(),
...@@ -393,7 +433,7 @@ mdopen(SMgrRelation reln, bool allowNotFound) ...@@ -393,7 +433,7 @@ mdopen(SMgrRelation reln, bool allowNotFound)
mdfd->mdfd_segno = 0; mdfd->mdfd_segno = 0;
#ifndef LET_OS_MANAGE_FILESIZE #ifndef LET_OS_MANAGE_FILESIZE
mdfd->mdfd_chain = NULL; mdfd->mdfd_chain = NULL;
Assert(_mdnblocks(fd, BLCKSZ) <= ((BlockNumber) RELSEG_SIZE)); Assert(_mdnblocks(reln, mdfd) <= ((BlockNumber) RELSEG_SIZE));
#endif #endif
return mdfd; return mdfd;
...@@ -401,17 +441,15 @@ mdopen(SMgrRelation reln, bool allowNotFound) ...@@ -401,17 +441,15 @@ mdopen(SMgrRelation reln, bool allowNotFound)
/* /*
* mdclose() -- Close the specified relation, if it isn't closed already. * mdclose() -- Close the specified relation, if it isn't closed already.
*
* Returns true or false with errno set as appropriate.
*/ */
bool void
mdclose(SMgrRelation reln) mdclose(SMgrRelation reln)
{ {
MdfdVec *v = reln->md_fd; MdfdVec *v = reln->md_fd;
/* No work if already closed */ /* No work if already closed */
if (v == NULL) if (v == NULL)
return true; return;
reln->md_fd = NULL; /* prevent dangling pointer after error */ reln->md_fd = NULL; /* prevent dangling pointer after error */
...@@ -432,22 +470,19 @@ mdclose(SMgrRelation reln) ...@@ -432,22 +470,19 @@ mdclose(SMgrRelation reln)
FileClose(v->mdfd_vfd); FileClose(v->mdfd_vfd);
pfree(v); pfree(v);
#endif #endif
return true;
} }
/* /*
* mdread() -- Read the specified block from a relation. * mdread() -- Read the specified block from a relation.
*/ */
bool void
mdread(SMgrRelation reln, BlockNumber blocknum, char *buffer) mdread(SMgrRelation reln, BlockNumber blocknum, char *buffer)
{ {
bool status;
long seekpos; long seekpos;
int nbytes; int nbytes;
MdfdVec *v; MdfdVec *v;
v = _mdfd_getseg(reln, blocknum, false); v = _mdfd_getseg(reln, blocknum, false, EXTENSION_FAIL);
#ifndef LET_OS_MANAGE_FILESIZE #ifndef LET_OS_MANAGE_FILESIZE
seekpos = (long) (BLCKSZ * (blocknum % ((BlockNumber) RELSEG_SIZE))); seekpos = (long) (BLCKSZ * (blocknum % ((BlockNumber) RELSEG_SIZE)));
...@@ -457,39 +492,66 @@ mdread(SMgrRelation reln, BlockNumber blocknum, char *buffer) ...@@ -457,39 +492,66 @@ mdread(SMgrRelation 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 false; ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not seek to block %u of relation %u/%u/%u: %m",
blocknum,
reln->smgr_rnode.spcNode,
reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode)));
status = true;
if ((nbytes = FileRead(v->mdfd_vfd, buffer, BLCKSZ)) != BLCKSZ) if ((nbytes = FileRead(v->mdfd_vfd, buffer, BLCKSZ)) != BLCKSZ)
{ {
if (nbytes < 0)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not read block %u of relation %u/%u/%u: %m",
blocknum,
reln->smgr_rnode.spcNode,
reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode)));
/* /*
* If we are at or past EOF, return zeroes without complaining. Also * Short read: we are at or past EOF, or we read a partial block at
* substitute zeroes if we found a partial block at EOF. * EOF. Normally this is an error; upper levels should never try to
* * read a nonexistent block. However, if zero_damaged_pages is ON
* XXX this is really ugly, bad design. However the current * or we are InRecovery, we should instead return zeroes without
* implementation of hash indexes requires it, because hash index * complaining. This allows, for example, the case of trying to
* pages are initialized out-of-order. * update a block that was later truncated away.
*/ */
if (nbytes == 0 || if (zero_damaged_pages || InRecovery)
(nbytes > 0 && mdnblocks(reln) == blocknum))
MemSet(buffer, 0, BLCKSZ); MemSet(buffer, 0, BLCKSZ);
else else
status = false; ereport(ERROR,
(errcode(ERRCODE_DATA_CORRUPTED),
errmsg("could not read block %u of relation %u/%u/%u: read only %d of %d bytes",
blocknum,
reln->smgr_rnode.spcNode,
reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode,
nbytes, BLCKSZ)));
} }
return status;
} }
/* /*
* mdwrite() -- Write the supplied block at the appropriate location. * mdwrite() -- Write the supplied block at the appropriate location.
*
* This is to be used only for updating already-existing blocks of a
* relation (ie, those before the current EOF). To extend a relation,
* use mdextend().
*/ */
bool void
mdwrite(SMgrRelation reln, BlockNumber blocknum, char *buffer, bool isTemp) mdwrite(SMgrRelation reln, BlockNumber blocknum, char *buffer, bool isTemp)
{ {
long seekpos; long seekpos;
int nbytes;
MdfdVec *v; MdfdVec *v;
v = _mdfd_getseg(reln, blocknum, false); /* This assert is too expensive to have on normally ... */
#ifdef CHECK_WRITE_VS_EXTEND
Assert(blocknum < mdnblocks(reln));
#endif
v = _mdfd_getseg(reln, blocknum, isTemp, EXTENSION_FAIL);
#ifndef LET_OS_MANAGE_FILESIZE #ifndef LET_OS_MANAGE_FILESIZE
seekpos = (long) (BLCKSZ * (blocknum % ((BlockNumber) RELSEG_SIZE))); seekpos = (long) (BLCKSZ * (blocknum % ((BlockNumber) RELSEG_SIZE)));
...@@ -499,18 +561,38 @@ mdwrite(SMgrRelation reln, BlockNumber blocknum, char *buffer, bool isTemp) ...@@ -499,18 +561,38 @@ mdwrite(SMgrRelation reln, BlockNumber blocknum, char *buffer, bool isTemp)
#endif #endif
if (FileSeek(v->mdfd_vfd, seekpos, SEEK_SET) != seekpos) if (FileSeek(v->mdfd_vfd, seekpos, SEEK_SET) != seekpos)
return false; ereport(ERROR,
(errcode_for_file_access(),
if (FileWrite(v->mdfd_vfd, buffer, BLCKSZ) != BLCKSZ) errmsg("could not seek to block %u of relation %u/%u/%u: %m",
return false; blocknum,
reln->smgr_rnode.spcNode,
reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode)));
if (!isTemp) if ((nbytes = FileWrite(v->mdfd_vfd, buffer, BLCKSZ)) != BLCKSZ)
{ {
if (!register_dirty_segment(reln, v)) if (nbytes < 0)
return false; ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not write block %u of relation %u/%u/%u: %m",
blocknum,
reln->smgr_rnode.spcNode,
reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode)));
/* short write: complain appropriately */
ereport(ERROR,
(errcode(ERRCODE_DISK_FULL),
errmsg("could not write block %u of relation %u/%u/%u: wrote only %d of %d bytes",
blocknum,
reln->smgr_rnode.spcNode,
reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode,
nbytes, BLCKSZ),
errhint("Check free disk space.")));
} }
return true; if (!isTemp)
register_dirty_segment(reln, v);
} }
/* /*
...@@ -520,13 +602,11 @@ mdwrite(SMgrRelation reln, BlockNumber blocknum, char *buffer, bool isTemp) ...@@ -520,13 +602,11 @@ mdwrite(SMgrRelation reln, BlockNumber blocknum, char *buffer, bool isTemp)
* and added to the mdfd_chain list. If this routine has not been * and added to the mdfd_chain list. If this routine has not been
* 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, or InvalidBlockNumber on error.
*/ */
BlockNumber BlockNumber
mdnblocks(SMgrRelation reln) mdnblocks(SMgrRelation reln)
{ {
MdfdVec *v = mdopen(reln, false); MdfdVec *v = mdopen(reln, EXTENSION_FAIL);
#ifndef LET_OS_MANAGE_FILESIZE #ifndef LET_OS_MANAGE_FILESIZE
BlockNumber nblocks; BlockNumber nblocks;
...@@ -552,7 +632,7 @@ mdnblocks(SMgrRelation reln) ...@@ -552,7 +632,7 @@ mdnblocks(SMgrRelation reln)
for (;;) for (;;)
{ {
nblocks = _mdnblocks(v->mdfd_vfd, BLCKSZ); nblocks = _mdnblocks(reln, v);
if (nblocks > ((BlockNumber) RELSEG_SIZE)) if (nblocks > ((BlockNumber) RELSEG_SIZE))
elog(FATAL, "segment too big"); elog(FATAL, "segment too big");
if (nblocks < ((BlockNumber) RELSEG_SIZE)) if (nblocks < ((BlockNumber) RELSEG_SIZE))
...@@ -573,22 +653,26 @@ mdnblocks(SMgrRelation reln) ...@@ -573,22 +653,26 @@ mdnblocks(SMgrRelation 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)
return InvalidBlockNumber; /* failed? */ ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not open segment %u of relation %u/%u/%u: %m",
segno,
reln->smgr_rnode.spcNode,
reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode)));
} }
v = v->mdfd_chain; v = v->mdfd_chain;
} }
#else #else
return _mdnblocks(v->mdfd_vfd, BLCKSZ); return _mdnblocks(reln, v);
#endif #endif
} }
/* /*
* mdtruncate() -- Truncate relation to specified number of blocks. * mdtruncate() -- Truncate relation to specified number of blocks.
*
* Returns # of blocks or InvalidBlockNumber on error.
*/ */
BlockNumber void
mdtruncate(SMgrRelation reln, BlockNumber nblocks, bool isTemp) mdtruncate(SMgrRelation reln, BlockNumber nblocks, bool isTemp)
{ {
MdfdVec *v; MdfdVec *v;
...@@ -603,14 +687,22 @@ mdtruncate(SMgrRelation reln, BlockNumber nblocks, bool isTemp) ...@@ -603,14 +687,22 @@ mdtruncate(SMgrRelation reln, BlockNumber nblocks, bool isTemp)
* that truncation loop will get them all! * that truncation 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 */ {
/* Bogus request ... but no complaint if InRecovery */
if (InRecovery)
return;
ereport(ERROR,
(errmsg("could not truncate relation %u/%u/%u to %u blocks: it's only %u blocks now",
reln->smgr_rnode.spcNode,
reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode,
nblocks, curnblk)));
}
if (nblocks == curnblk) if (nblocks == curnblk)
return nblocks; /* no work */ return; /* no work */
v = mdopen(reln, false); v = mdopen(reln, EXTENSION_FAIL);
#ifndef LET_OS_MANAGE_FILESIZE #ifndef LET_OS_MANAGE_FILESIZE
priorblocks = 0; priorblocks = 0;
...@@ -626,12 +718,15 @@ mdtruncate(SMgrRelation reln, BlockNumber nblocks, bool isTemp) ...@@ -626,12 +718,15 @@ mdtruncate(SMgrRelation reln, BlockNumber nblocks, bool isTemp)
* not delete it, for reasons explained in the header comments. * not delete it, for reasons explained in the header comments.
*/ */
if (FileTruncate(v->mdfd_vfd, 0) < 0) if (FileTruncate(v->mdfd_vfd, 0) < 0)
return InvalidBlockNumber; ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not truncate relation %u/%u/%u to %u blocks: %m",
reln->smgr_rnode.spcNode,
reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode,
nblocks)));
if (!isTemp) if (!isTemp)
{ register_dirty_segment(reln, v);
if (!register_dirty_segment(reln, v))
return InvalidBlockNumber;
}
v = v->mdfd_chain; v = v->mdfd_chain;
Assert(ov != reln->md_fd); /* we never drop the 1st segment */ Assert(ov != reln->md_fd); /* we never drop the 1st segment */
pfree(ov); pfree(ov);
...@@ -649,12 +744,15 @@ mdtruncate(SMgrRelation reln, BlockNumber nblocks, bool isTemp) ...@@ -649,12 +744,15 @@ mdtruncate(SMgrRelation reln, BlockNumber nblocks, bool isTemp)
BlockNumber lastsegblocks = nblocks - priorblocks; BlockNumber lastsegblocks = nblocks - priorblocks;
if (FileTruncate(v->mdfd_vfd, lastsegblocks * BLCKSZ) < 0) if (FileTruncate(v->mdfd_vfd, lastsegblocks * BLCKSZ) < 0)
return InvalidBlockNumber; ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not truncate relation %u/%u/%u to %u blocks: %m",
reln->smgr_rnode.spcNode,
reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode,
nblocks)));
if (!isTemp) if (!isTemp)
{ register_dirty_segment(reln, v);
if (!register_dirty_segment(reln, v))
return InvalidBlockNumber;
}
v = v->mdfd_chain; v = v->mdfd_chain;
ov->mdfd_chain = NULL; ov->mdfd_chain = NULL;
} }
...@@ -670,15 +768,16 @@ mdtruncate(SMgrRelation reln, BlockNumber nblocks, bool isTemp) ...@@ -670,15 +768,16 @@ mdtruncate(SMgrRelation reln, BlockNumber nblocks, bool isTemp)
} }
#else #else
if (FileTruncate(v->mdfd_vfd, nblocks * BLCKSZ) < 0) if (FileTruncate(v->mdfd_vfd, nblocks * BLCKSZ) < 0)
return InvalidBlockNumber; ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not truncate relation %u/%u/%u to %u blocks: %m",
reln->smgr_rnode.spcNode,
reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode,
nblocks)));
if (!isTemp) if (!isTemp)
{ register_dirty_segment(reln, v);
if (!register_dirty_segment(reln, v))
return InvalidBlockNumber;
}
#endif #endif
return nblocks;
} }
/* /*
...@@ -687,7 +786,7 @@ mdtruncate(SMgrRelation reln, BlockNumber nblocks, bool isTemp) ...@@ -687,7 +786,7 @@ mdtruncate(SMgrRelation reln, BlockNumber nblocks, bool isTemp)
* Note that only writes already issued are synced; this routine knows * Note that only writes already issued are synced; this routine knows
* nothing of dirty buffers that may exist inside the buffer manager. * nothing of dirty buffers that may exist inside the buffer manager.
*/ */
bool void
mdimmedsync(SMgrRelation reln) mdimmedsync(SMgrRelation reln)
{ {
MdfdVec *v; MdfdVec *v;
...@@ -698,24 +797,32 @@ mdimmedsync(SMgrRelation reln) ...@@ -698,24 +797,32 @@ mdimmedsync(SMgrRelation reln)
* that fsync loop will get them all! * that fsync loop will get them all!
*/ */
curnblk = mdnblocks(reln); curnblk = mdnblocks(reln);
if (curnblk == InvalidBlockNumber)
return false; /* mdnblocks failed */
v = mdopen(reln, false); v = mdopen(reln, EXTENSION_FAIL);
#ifndef LET_OS_MANAGE_FILESIZE #ifndef LET_OS_MANAGE_FILESIZE
while (v != NULL) while (v != NULL)
{ {
if (FileSync(v->mdfd_vfd) < 0) if (FileSync(v->mdfd_vfd) < 0)
return false; ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not fsync segment %u of relation %u/%u/%u: %m",
v->mdfd_segno,
reln->smgr_rnode.spcNode,
reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode)));
v = v->mdfd_chain; v = v->mdfd_chain;
} }
#else #else
if (FileSync(v->mdfd_vfd) < 0) if (FileSync(v->mdfd_vfd) < 0)
return false; ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not fsync segment %u of relation %u/%u/%u: %m",
v->mdfd_segno,
reln->smgr_rnode.spcNode,
reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode)));
#endif #endif
return true;
} }
/* /*
...@@ -724,7 +831,7 @@ mdimmedsync(SMgrRelation reln) ...@@ -724,7 +831,7 @@ mdimmedsync(SMgrRelation reln)
* This is only called during checkpoints, and checkpoints should only * This is only called during checkpoints, and checkpoints should only
* occur in processes that have created a pendingOpsTable. * occur in processes that have created a pendingOpsTable.
*/ */
bool void
mdsync(void) mdsync(void)
{ {
HASH_SEQ_STATUS hstat; HASH_SEQ_STATUS hstat;
...@@ -732,7 +839,7 @@ mdsync(void) ...@@ -732,7 +839,7 @@ mdsync(void)
int absorb_counter; int absorb_counter;
if (!pendingOpsTable) if (!pendingOpsTable)
return false; elog(ERROR, "cannot sync without a pendingOpsTable");
/* /*
* If we are in the bgwriter, the sync had better include all fsync * If we are in the bgwriter, the sync had better include all fsync
...@@ -795,21 +902,18 @@ mdsync(void) ...@@ -795,21 +902,18 @@ mdsync(void)
*/ */
seg = _mdfd_getseg(reln, seg = _mdfd_getseg(reln,
entry->segno * ((BlockNumber) RELSEG_SIZE), entry->segno * ((BlockNumber) RELSEG_SIZE),
true); false, EXTENSION_RETURN_NULL);
if (seg) if (seg)
{ {
if (FileSync(seg->mdfd_vfd) < 0 && if (FileSync(seg->mdfd_vfd) < 0 &&
errno != ENOENT) errno != ENOENT)
{ ereport(ERROR,
ereport(LOG,
(errcode_for_file_access(), (errcode_for_file_access(),
errmsg("could not fsync segment %u of relation %u/%u/%u: %m", errmsg("could not fsync segment %u of relation %u/%u/%u: %m",
entry->segno, entry->segno,
entry->rnode.spcNode, entry->rnode.spcNode,
entry->rnode.dbNode, entry->rnode.dbNode,
entry->rnode.relNode))); entry->rnode.relNode)));
return false;
}
} }
} }
...@@ -818,8 +922,6 @@ mdsync(void) ...@@ -818,8 +922,6 @@ mdsync(void)
HASH_REMOVE, NULL) == NULL) HASH_REMOVE, NULL) == NULL)
elog(ERROR, "pendingOpsTable corrupted"); elog(ERROR, "pendingOpsTable corrupted");
} }
return true;
} }
/* /*
...@@ -830,11 +932,8 @@ mdsync(void) ...@@ -830,11 +932,8 @@ mdsync(void)
* to the background writer process. If that fails, just do the fsync * to the background writer process. If that fails, just do the fsync
* locally before returning (we expect this will not happen often enough * locally before returning (we expect this will not happen often enough
* to be a performance problem). * to be a performance problem).
*
* A false result implies I/O failure during local fsync. errno will be
* valid for error reporting.
*/ */
static bool static void
register_dirty_segment(SMgrRelation reln, MdfdVec *seg) register_dirty_segment(SMgrRelation reln, MdfdVec *seg)
{ {
if (pendingOpsTable) if (pendingOpsTable)
...@@ -847,17 +946,21 @@ register_dirty_segment(SMgrRelation reln, MdfdVec *seg) ...@@ -847,17 +946,21 @@ register_dirty_segment(SMgrRelation reln, MdfdVec *seg)
entry.segno = seg->mdfd_segno; entry.segno = seg->mdfd_segno;
(void) hash_search(pendingOpsTable, &entry, HASH_ENTER, NULL); (void) hash_search(pendingOpsTable, &entry, HASH_ENTER, NULL);
return true;
} }
else else
{ {
if (ForwardFsyncRequest(reln->smgr_rnode, seg->mdfd_segno)) if (ForwardFsyncRequest(reln->smgr_rnode, seg->mdfd_segno))
return true; return; /* passed it off successfully */
}
if (FileSync(seg->mdfd_vfd) < 0) if (FileSync(seg->mdfd_vfd) < 0)
return false; ereport(ERROR,
return true; (errcode_for_file_access(),
errmsg("could not fsync segment %u of relation %u/%u/%u: %m",
seg->mdfd_segno,
reln->smgr_rnode.spcNode,
reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode)));
}
} }
/* /*
...@@ -931,7 +1034,7 @@ _mdfd_openseg(SMgrRelation reln, BlockNumber segno, int oflags) ...@@ -931,7 +1034,7 @@ _mdfd_openseg(SMgrRelation reln, BlockNumber segno, int oflags)
v->mdfd_vfd = fd; v->mdfd_vfd = fd;
v->mdfd_segno = segno; v->mdfd_segno = segno;
v->mdfd_chain = NULL; v->mdfd_chain = NULL;
Assert(_mdnblocks(fd, BLCKSZ) <= ((BlockNumber) RELSEG_SIZE)); Assert(_mdnblocks(reln, v) <= ((BlockNumber) RELSEG_SIZE));
/* all done */ /* all done */
return v; return v;
...@@ -940,51 +1043,66 @@ _mdfd_openseg(SMgrRelation reln, BlockNumber segno, int oflags) ...@@ -940,51 +1043,66 @@ _mdfd_openseg(SMgrRelation reln, BlockNumber segno, int oflags)
/* /*
* _mdfd_getseg() -- Find the segment of the relation holding the * _mdfd_getseg() -- Find the segment of the relation holding the
* specified block. ereport's on failure. * specified block.
* (Optionally, can return NULL instead of ereport for ENOENT.) *
* If the segment doesn't exist, we ereport, return NULL, or create the
* segment, according to "behavior". Note: isTemp need only be correct
* in the EXTENSION_CREATE case.
*/ */
static MdfdVec * static MdfdVec *
_mdfd_getseg(SMgrRelation reln, BlockNumber blkno, bool allowNotFound) _mdfd_getseg(SMgrRelation reln, BlockNumber blkno, bool isTemp,
ExtensionBehavior behavior)
{ {
MdfdVec *v = mdopen(reln, allowNotFound); MdfdVec *v = mdopen(reln, behavior);
#ifndef LET_OS_MANAGE_FILESIZE #ifndef LET_OS_MANAGE_FILESIZE
BlockNumber segstogo; BlockNumber targetseg;
BlockNumber nextsegno; BlockNumber nextsegno;
if (!v) if (!v)
return NULL; /* only possible if allowNotFound */ return NULL; /* only possible if EXTENSION_RETURN_NULL */
for (segstogo = blkno / ((BlockNumber) RELSEG_SIZE), nextsegno = 1; targetseg = blkno / ((BlockNumber) RELSEG_SIZE);
segstogo > 0; for (nextsegno = 1; nextsegno <= targetseg; nextsegno++)
nextsegno++, segstogo--)
{ {
Assert(nextsegno == v->mdfd_segno + 1);
if (v->mdfd_chain == NULL) if (v->mdfd_chain == NULL)
{ {
/* /*
* We will create the next segment only if the target block is * Normally we will create new segments only if authorized by
* within it. This prevents Sorcerer's Apprentice syndrome if a * the caller (i.e., we are doing mdextend()). But when doing
* bug at higher levels causes us to be handed a ridiculously * WAL recovery, create segments anyway; this allows cases such as
* large blkno --- otherwise we could create many thousands of * replaying WAL data that has a write into a high-numbered
* empty segment files before reaching the "target" block. We * segment of a relation that was later deleted. We want to go
* should never need to create more than one new segment per call, * ahead and create the segments so we can finish out the replay.
* so this restriction seems reasonable.
* *
* BUT: when doing WAL recovery, disable this logic and create * We have to maintain the invariant that segments before the
* segments unconditionally. In this case it seems better to * last active segment are of size RELSEG_SIZE; therefore, pad
* assume the given blkno is good (it presumably came from a * them out with zeroes if needed. (This only matters if caller
* CRC-checked WAL record); furthermore this lets us cope in the * is extending the relation discontiguously, but that can happen
* case where we are replaying WAL data that has a write into a * in hash indexes.)
* high-numbered segment of a relation that was later deleted. We */
* want to go ahead and create the segments so we can finish out if (behavior == EXTENSION_CREATE || InRecovery)
* the replay. {
*/ if (_mdnblocks(reln, v) < RELSEG_SIZE)
v->mdfd_chain = _mdfd_openseg(reln, {
nextsegno, char *zerobuf = palloc0(BLCKSZ);
(segstogo == 1 || InRecovery) ? O_CREAT : 0);
mdextend(reln, nextsegno * ((BlockNumber) RELSEG_SIZE) - 1,
zerobuf, isTemp);
pfree(zerobuf);
}
v->mdfd_chain = _mdfd_openseg(reln, nextsegno, O_CREAT);
}
else
{
/* We won't create segment if not existent */
v->mdfd_chain = _mdfd_openseg(reln, nextsegno, 0);
}
if (v->mdfd_chain == NULL) if (v->mdfd_chain == NULL)
{ {
if (allowNotFound && errno == ENOENT) if (behavior == EXTENSION_RETURN_NULL && errno == ENOENT)
return NULL; return NULL;
ereport(ERROR, ereport(ERROR,
(errcode_for_file_access(), (errcode_for_file_access(),
...@@ -1007,12 +1125,19 @@ _mdfd_getseg(SMgrRelation reln, BlockNumber blkno, bool allowNotFound) ...@@ -1007,12 +1125,19 @@ _mdfd_getseg(SMgrRelation reln, BlockNumber blkno, bool allowNotFound)
* Get number of blocks present in a single disk file * Get number of blocks present in a single disk file
*/ */
static BlockNumber static BlockNumber
_mdnblocks(File file, Size blcksz) _mdnblocks(SMgrRelation reln, MdfdVec *seg)
{ {
long len; long len;
len = FileSeek(file, 0L, SEEK_END); len = FileSeek(seg->mdfd_vfd, 0L, SEEK_END);
if (len < 0) if (len < 0)
return 0; /* on failure, assume file is empty */ ereport(ERROR,
return (BlockNumber) (len / blcksz); (errcode_for_file_access(),
errmsg("could not seek to end of segment %u of relation %u/%u/%u: %m",
seg->mdfd_segno,
reln->smgr_rnode.spcNode,
reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode)));
/* note that this calculation will ignore any partial block at EOF */
return (BlockNumber) (len / BLCKSZ);
} }
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/smgr/smgr.c,v 1.101 2006/10/04 00:29:58 momjian Exp $ * $PostgreSQL: pgsql/src/backend/storage/smgr/smgr.c,v 1.102 2007/01/03 18:11:01 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -31,30 +31,33 @@ ...@@ -31,30 +31,33 @@
/* /*
* This struct of function pointers defines the API between smgr.c and * This struct of function pointers defines the API between smgr.c and
* any individual storage manager module. Note that smgr subfunctions are * any individual storage manager module. Note that smgr subfunctions are
* generally expected to return TRUE on success, FALSE on error. (For * generally expected to report problems via elog(ERROR). An exception is
* nblocks and truncate we instead say that returning InvalidBlockNumber * that smgr_unlink should use elog(WARNING), rather than erroring out,
* indicates an error.) * because we normally unlink relations during post-commit/abort cleanup,
* and so it's too late to raise an error. Also, various conditions that
* would normally be errors should be allowed during bootstrap and/or WAL
* recovery --- see comments in md.c for details.
*/ */
typedef struct f_smgr typedef struct f_smgr
{ {
bool (*smgr_init) (void); /* may be NULL */ void (*smgr_init) (void); /* may be NULL */
bool (*smgr_shutdown) (void); /* may be NULL */ void (*smgr_shutdown) (void); /* may be NULL */
bool (*smgr_close) (SMgrRelation reln); void (*smgr_close) (SMgrRelation reln);
bool (*smgr_create) (SMgrRelation reln, bool isRedo); void (*smgr_create) (SMgrRelation reln, bool isRedo);
bool (*smgr_unlink) (RelFileNode rnode, bool isRedo); void (*smgr_unlink) (RelFileNode rnode, bool isRedo);
bool (*smgr_extend) (SMgrRelation reln, BlockNumber blocknum, void (*smgr_extend) (SMgrRelation reln, BlockNumber blocknum,
char *buffer, bool isTemp); char *buffer, bool isTemp);
bool (*smgr_read) (SMgrRelation reln, BlockNumber blocknum, void (*smgr_read) (SMgrRelation reln, BlockNumber blocknum,
char *buffer); char *buffer);
bool (*smgr_write) (SMgrRelation reln, BlockNumber blocknum, void (*smgr_write) (SMgrRelation reln, BlockNumber blocknum,
char *buffer, bool isTemp); char *buffer, bool isTemp);
BlockNumber (*smgr_nblocks) (SMgrRelation reln); BlockNumber (*smgr_nblocks) (SMgrRelation reln);
BlockNumber (*smgr_truncate) (SMgrRelation reln, BlockNumber nblocks, void (*smgr_truncate) (SMgrRelation reln, BlockNumber nblocks,
bool isTemp); bool isTemp);
bool (*smgr_immedsync) (SMgrRelation reln); void (*smgr_immedsync) (SMgrRelation reln);
bool (*smgr_commit) (void); /* may be NULL */ void (*smgr_commit) (void); /* may be NULL */
bool (*smgr_abort) (void); /* may be NULL */ void (*smgr_abort) (void); /* may be NULL */
bool (*smgr_sync) (void); /* may be NULL */ void (*smgr_sync) (void); /* may be NULL */
} f_smgr; } f_smgr;
...@@ -152,12 +155,7 @@ smgrinit(void) ...@@ -152,12 +155,7 @@ smgrinit(void)
for (i = 0; i < NSmgr; i++) for (i = 0; i < NSmgr; i++)
{ {
if (smgrsw[i].smgr_init) if (smgrsw[i].smgr_init)
{ (*(smgrsw[i].smgr_init)) ();
if (!(*(smgrsw[i].smgr_init)) ())
elog(FATAL, "smgr initialization failed on %s: %m",
DatumGetCString(DirectFunctionCall1(smgrout,
Int16GetDatum(i))));
}
} }
/* register the shutdown proc */ /* register the shutdown proc */
...@@ -175,12 +173,7 @@ smgrshutdown(int code, Datum arg) ...@@ -175,12 +173,7 @@ smgrshutdown(int code, Datum arg)
for (i = 0; i < NSmgr; i++) for (i = 0; i < NSmgr; i++)
{ {
if (smgrsw[i].smgr_shutdown) if (smgrsw[i].smgr_shutdown)
{ (*(smgrsw[i].smgr_shutdown)) ();
if (!(*(smgrsw[i].smgr_shutdown)) ())
elog(FATAL, "smgr shutdown failed on %s: %m",
DatumGetCString(DirectFunctionCall1(smgrout,
Int16GetDatum(i))));
}
} }
} }
...@@ -256,13 +249,7 @@ smgrclose(SMgrRelation reln) ...@@ -256,13 +249,7 @@ smgrclose(SMgrRelation reln)
{ {
SMgrRelation *owner; SMgrRelation *owner;
if (!(*(smgrsw[reln->smgr_which].smgr_close)) (reln)) (*(smgrsw[reln->smgr_which].smgr_close)) (reln);
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not close relation %u/%u/%u: %m",
reln->smgr_rnode.spcNode,
reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode)));
owner = reln->smgr_owner; owner = reln->smgr_owner;
...@@ -354,13 +341,7 @@ smgrcreate(SMgrRelation reln, bool isTemp, bool isRedo) ...@@ -354,13 +341,7 @@ smgrcreate(SMgrRelation reln, bool isTemp, bool isRedo)
reln->smgr_rnode.dbNode, reln->smgr_rnode.dbNode,
isRedo); isRedo);
if (!(*(smgrsw[reln->smgr_which].smgr_create)) (reln, isRedo)) (*(smgrsw[reln->smgr_which].smgr_create)) (reln, isRedo);
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not create relation %u/%u/%u: %m",
reln->smgr_rnode.spcNode,
reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode)));
if (isRedo) if (isRedo)
return; return;
...@@ -482,38 +463,26 @@ smgr_internal_unlink(RelFileNode rnode, int which, bool isTemp, bool isRedo) ...@@ -482,38 +463,26 @@ smgr_internal_unlink(RelFileNode rnode, int which, bool isTemp, bool isRedo)
/* /*
* And delete the physical files. * And delete the physical files.
* *
* Note: we treat deletion failure as a WARNING, not an error, because * Note: smgr_unlink must treat deletion failure as a WARNING, not an
* we've already decided to commit or abort the current xact. * ERROR, because we've already decided to commit or abort the current
* xact.
*/ */
if (!(*(smgrsw[which].smgr_unlink)) (rnode, isRedo)) (*(smgrsw[which].smgr_unlink)) (rnode, isRedo);
ereport(WARNING,
(errcode_for_file_access(),
errmsg("could not remove relation %u/%u/%u: %m",
rnode.spcNode,
rnode.dbNode,
rnode.relNode)));
} }
/* /*
* smgrextend() -- Add a new block to a file. * smgrextend() -- Add a new block to a file.
* *
* The semantics are basically the same as smgrwrite(): write at the * The semantics are nearly the same as smgrwrite(): write at the
* specified position. However, we are expecting to extend the * specified position. However, this is to be used for the case of
* relation (ie, blocknum is the current EOF), and so in case of * extending a relation (i.e., blocknum is at or beyond the current
* failure we clean up by truncating. * EOF). Note that we assume writing a block beyond current EOF
* causes intervening file space to become filled with zeroes.
*/ */
void void
smgrextend(SMgrRelation reln, BlockNumber blocknum, char *buffer, bool isTemp) smgrextend(SMgrRelation reln, BlockNumber blocknum, char *buffer, bool isTemp)
{ {
if (!(*(smgrsw[reln->smgr_which].smgr_extend)) (reln, blocknum, buffer, (*(smgrsw[reln->smgr_which].smgr_extend)) (reln, blocknum, buffer, isTemp);
isTemp))
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not extend relation %u/%u/%u: %m",
reln->smgr_rnode.spcNode,
reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode),
errhint("Check free disk space.")));
} }
/* /*
...@@ -527,19 +496,16 @@ smgrextend(SMgrRelation reln, BlockNumber blocknum, char *buffer, bool isTemp) ...@@ -527,19 +496,16 @@ smgrextend(SMgrRelation reln, BlockNumber blocknum, char *buffer, bool isTemp)
void void
smgrread(SMgrRelation reln, BlockNumber blocknum, char *buffer) smgrread(SMgrRelation reln, BlockNumber blocknum, char *buffer)
{ {
if (!(*(smgrsw[reln->smgr_which].smgr_read)) (reln, blocknum, buffer)) (*(smgrsw[reln->smgr_which].smgr_read)) (reln, blocknum, buffer);
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not read block %u of relation %u/%u/%u: %m",
blocknum,
reln->smgr_rnode.spcNode,
reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode)));
} }
/* /*
* smgrwrite() -- Write the supplied buffer out. * smgrwrite() -- Write the supplied buffer out.
* *
* This is to be used only for updating already-existing blocks of a
* relation (ie, those before the current EOF). To extend a relation,
* use smgrextend().
*
* 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. However, * on disk at return, only dumped out to the kernel. However,
* provisions will be made to fsync the write before the next checkpoint. * provisions will be made to fsync the write before the next checkpoint.
...@@ -551,60 +517,26 @@ smgrread(SMgrRelation reln, BlockNumber blocknum, char *buffer) ...@@ -551,60 +517,26 @@ smgrread(SMgrRelation reln, BlockNumber blocknum, char *buffer)
void void
smgrwrite(SMgrRelation reln, BlockNumber blocknum, char *buffer, bool isTemp) smgrwrite(SMgrRelation reln, BlockNumber blocknum, char *buffer, bool isTemp)
{ {
if (!(*(smgrsw[reln->smgr_which].smgr_write)) (reln, blocknum, buffer, (*(smgrsw[reln->smgr_which].smgr_write)) (reln, blocknum, buffer, isTemp);
isTemp))
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not write block %u of relation %u/%u/%u: %m",
blocknum,
reln->smgr_rnode.spcNode,
reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode)));
} }
/* /*
* smgrnblocks() -- Calculate the number of blocks in the * smgrnblocks() -- Calculate the number of blocks in the
* supplied relation. * supplied relation.
*
* Returns the number of blocks on success, aborts the current
* transaction on failure.
*/ */
BlockNumber BlockNumber
smgrnblocks(SMgrRelation reln) smgrnblocks(SMgrRelation reln)
{ {
BlockNumber nblocks; return (*(smgrsw[reln->smgr_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
* fail --- but that's a good thing, because it would stop us from
* extending the rel another block and having a block whose number
* actually is InvalidBlockNumber.
*/
if (nblocks == InvalidBlockNumber)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not count blocks of relation %u/%u/%u: %m",
reln->smgr_rnode.spcNode,
reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode)));
return nblocks;
} }
/* /*
* smgrtruncate() -- Truncate supplied relation to the specified number * smgrtruncate() -- Truncate supplied relation to the specified number
* of blocks * of blocks
*
* Returns the number of blocks on success, aborts the current
* transaction on failure.
*/ */
BlockNumber void
smgrtruncate(SMgrRelation reln, BlockNumber nblocks, bool isTemp) smgrtruncate(SMgrRelation reln, BlockNumber nblocks, bool isTemp)
{ {
BlockNumber newblks;
/* /*
* Get rid of any buffers for the about-to-be-deleted blocks. bufmgr will * Get rid of any buffers for the about-to-be-deleted blocks. bufmgr will
* just drop them without bothering to write the contents. * just drop them without bothering to write the contents.
...@@ -619,16 +551,7 @@ smgrtruncate(SMgrRelation reln, BlockNumber nblocks, bool isTemp) ...@@ -619,16 +551,7 @@ smgrtruncate(SMgrRelation reln, BlockNumber nblocks, bool isTemp)
FreeSpaceMapTruncateRel(&reln->smgr_rnode, nblocks); FreeSpaceMapTruncateRel(&reln->smgr_rnode, nblocks);
/* Do the truncation */ /* Do the truncation */
newblks = (*(smgrsw[reln->smgr_which].smgr_truncate)) (reln, nblocks, (*(smgrsw[reln->smgr_which].smgr_truncate)) (reln, nblocks, isTemp);
isTemp);
if (newblks == InvalidBlockNumber)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not truncate relation %u/%u/%u to %u blocks: %m",
reln->smgr_rnode.spcNode,
reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode,
nblocks)));
if (!isTemp) if (!isTemp)
{ {
...@@ -642,7 +565,7 @@ smgrtruncate(SMgrRelation reln, BlockNumber nblocks, bool isTemp) ...@@ -642,7 +565,7 @@ smgrtruncate(SMgrRelation reln, BlockNumber nblocks, bool isTemp)
XLogRecData rdata; XLogRecData rdata;
xl_smgr_truncate xlrec; xl_smgr_truncate xlrec;
xlrec.blkno = newblks; xlrec.blkno = nblocks;
xlrec.rnode = reln->smgr_rnode; xlrec.rnode = reln->smgr_rnode;
rdata.data = (char *) &xlrec; rdata.data = (char *) &xlrec;
...@@ -653,8 +576,6 @@ smgrtruncate(SMgrRelation reln, BlockNumber nblocks, bool isTemp) ...@@ -653,8 +576,6 @@ smgrtruncate(SMgrRelation reln, BlockNumber nblocks, bool isTemp)
lsn = XLogInsert(RM_SMGR_ID, XLOG_SMGR_TRUNCATE | XLOG_NO_TRAN, lsn = XLogInsert(RM_SMGR_ID, XLOG_SMGR_TRUNCATE | XLOG_NO_TRAN,
&rdata); &rdata);
} }
return newblks;
} }
/* /*
...@@ -683,13 +604,7 @@ smgrtruncate(SMgrRelation reln, BlockNumber nblocks, bool isTemp) ...@@ -683,13 +604,7 @@ smgrtruncate(SMgrRelation reln, BlockNumber nblocks, bool isTemp)
void void
smgrimmedsync(SMgrRelation reln) smgrimmedsync(SMgrRelation reln)
{ {
if (!(*(smgrsw[reln->smgr_which].smgr_immedsync)) (reln)) (*(smgrsw[reln->smgr_which].smgr_immedsync)) (reln);
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not sync relation %u/%u/%u: %m",
reln->smgr_rnode.spcNode,
reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode)));
} }
...@@ -843,12 +758,7 @@ smgrcommit(void) ...@@ -843,12 +758,7 @@ smgrcommit(void)
for (i = 0; i < NSmgr; i++) for (i = 0; i < NSmgr; i++)
{ {
if (smgrsw[i].smgr_commit) if (smgrsw[i].smgr_commit)
{ (*(smgrsw[i].smgr_commit)) ();
if (!(*(smgrsw[i].smgr_commit)) ())
elog(ERROR, "transaction commit failed on %s: %m",
DatumGetCString(DirectFunctionCall1(smgrout,
Int16GetDatum(i))));
}
} }
} }
...@@ -863,12 +773,7 @@ smgrabort(void) ...@@ -863,12 +773,7 @@ smgrabort(void)
for (i = 0; i < NSmgr; i++) for (i = 0; i < NSmgr; i++)
{ {
if (smgrsw[i].smgr_abort) if (smgrsw[i].smgr_abort)
{ (*(smgrsw[i].smgr_abort)) ();
if (!(*(smgrsw[i].smgr_abort)) ())
elog(ERROR, "transaction abort failed on %s: %m",
DatumGetCString(DirectFunctionCall1(smgrout,
Int16GetDatum(i))));
}
} }
} }
...@@ -883,12 +788,7 @@ smgrsync(void) ...@@ -883,12 +788,7 @@ smgrsync(void)
for (i = 0; i < NSmgr; i++) for (i = 0; i < NSmgr; i++)
{ {
if (smgrsw[i].smgr_sync) if (smgrsw[i].smgr_sync)
{ (*(smgrsw[i].smgr_sync)) ();
if (!(*(smgrsw[i].smgr_sync)) ())
elog(ERROR, "storage sync failed on %s: %m",
DatumGetCString(DirectFunctionCall1(smgrout,
Int16GetDatum(i))));
}
} }
} }
...@@ -910,7 +810,6 @@ smgr_redo(XLogRecPtr lsn, XLogRecord *record) ...@@ -910,7 +810,6 @@ smgr_redo(XLogRecPtr lsn, XLogRecord *record)
{ {
xl_smgr_truncate *xlrec = (xl_smgr_truncate *) XLogRecGetData(record); xl_smgr_truncate *xlrec = (xl_smgr_truncate *) XLogRecGetData(record);
SMgrRelation reln; SMgrRelation reln;
BlockNumber newblks;
reln = smgropen(xlrec->rnode); reln = smgropen(xlrec->rnode);
...@@ -931,17 +830,9 @@ smgr_redo(XLogRecPtr lsn, XLogRecord *record) ...@@ -931,17 +830,9 @@ smgr_redo(XLogRecPtr lsn, XLogRecord *record)
FreeSpaceMapTruncateRel(&reln->smgr_rnode, xlrec->blkno); FreeSpaceMapTruncateRel(&reln->smgr_rnode, xlrec->blkno);
/* Do the truncation */ /* Do the truncation */
newblks = (*(smgrsw[reln->smgr_which].smgr_truncate)) (reln, (*(smgrsw[reln->smgr_which].smgr_truncate)) (reln,
xlrec->blkno, xlrec->blkno,
false); false);
if (newblks == InvalidBlockNumber)
ereport(WARNING,
(errcode_for_file_access(),
errmsg("could not truncate relation %u/%u/%u to %u blocks: %m",
reln->smgr_rnode.spcNode,
reln->smgr_rnode.dbNode,
reln->smgr_rnode.relNode,
xlrec->blkno)));
/* Also tell xlogutils.c about it */ /* Also tell xlogutils.c about it */
XLogTruncateRelation(xlrec->rnode, xlrec->blkno); XLogTruncateRelation(xlrec->rnode, xlrec->blkno);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2006, 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.55 2006/03/24 04:32:13 tgl Exp $ * $PostgreSQL: pgsql/src/include/storage/smgr.h,v 1.56 2007/01/03 18:11:01 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -72,7 +72,7 @@ extern void smgrread(SMgrRelation reln, BlockNumber blocknum, char *buffer); ...@@ -72,7 +72,7 @@ extern void smgrread(SMgrRelation reln, BlockNumber blocknum, char *buffer);
extern void smgrwrite(SMgrRelation reln, BlockNumber blocknum, char *buffer, extern void smgrwrite(SMgrRelation reln, BlockNumber blocknum, char *buffer,
bool isTemp); bool isTemp);
extern BlockNumber smgrnblocks(SMgrRelation reln); extern BlockNumber smgrnblocks(SMgrRelation reln);
extern BlockNumber smgrtruncate(SMgrRelation reln, BlockNumber nblocks, extern void smgrtruncate(SMgrRelation reln, BlockNumber nblocks,
bool isTemp); bool isTemp);
extern void smgrimmedsync(SMgrRelation reln); extern void smgrimmedsync(SMgrRelation reln);
extern void smgrDoPendingDeletes(bool isCommit); extern void smgrDoPendingDeletes(bool isCommit);
...@@ -91,20 +91,19 @@ extern void smgr_desc(StringInfo buf, uint8 xl_info, char *rec); ...@@ -91,20 +91,19 @@ extern void smgr_desc(StringInfo 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 bool mdinit(void); extern void mdinit(void);
extern bool mdclose(SMgrRelation reln); extern void mdclose(SMgrRelation reln);
extern bool mdcreate(SMgrRelation reln, bool isRedo); extern void mdcreate(SMgrRelation reln, bool isRedo);
extern bool mdunlink(RelFileNode rnode, bool isRedo); extern void mdunlink(RelFileNode rnode, bool isRedo);
extern bool mdextend(SMgrRelation reln, BlockNumber blocknum, char *buffer, extern void mdextend(SMgrRelation reln, BlockNumber blocknum, char *buffer,
bool isTemp); bool isTemp);
extern bool mdread(SMgrRelation reln, BlockNumber blocknum, char *buffer); extern void mdread(SMgrRelation reln, BlockNumber blocknum, char *buffer);
extern bool mdwrite(SMgrRelation reln, BlockNumber blocknum, char *buffer, extern void mdwrite(SMgrRelation reln, BlockNumber blocknum, char *buffer,
bool isTemp); bool isTemp);
extern BlockNumber mdnblocks(SMgrRelation reln); extern BlockNumber mdnblocks(SMgrRelation reln);
extern BlockNumber mdtruncate(SMgrRelation reln, BlockNumber nblocks, extern void mdtruncate(SMgrRelation reln, BlockNumber nblocks, bool isTemp);
bool isTemp); extern void mdimmedsync(SMgrRelation reln);
extern bool mdimmedsync(SMgrRelation reln); extern void mdsync(void);
extern bool mdsync(void);
extern void RememberFsyncRequest(RelFileNode rnode, BlockNumber segno); extern void RememberFsyncRequest(RelFileNode rnode, BlockNumber segno);
......
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