PostgreSQL Source Code  git master
md.c File Reference
#include "postgres.h"
#include <unistd.h>
#include <fcntl.h>
#include <sys/file.h>
#include "access/xlog.h"
#include "access/xlogutils.h"
#include "commands/tablespace.h"
#include "miscadmin.h"
#include "pg_trace.h"
#include "pgstat.h"
#include "postmaster/bgwriter.h"
#include "storage/bufmgr.h"
#include "storage/fd.h"
#include "storage/md.h"
#include "storage/relfilenode.h"
#include "storage/smgr.h"
#include "storage/sync.h"
#include "utils/hsearch.h"
#include "utils/memutils.h"
Include dependency graph for md.c:

Go to the source code of this file.

Data Structures

struct  _MdfdVec
 

Macros

#define INIT_MD_FILETAG(a, xx_rnode, xx_forknum, xx_segno)
 
#define EXTENSION_FAIL   (1 << 0)
 
#define EXTENSION_RETURN_NULL   (1 << 1)
 
#define EXTENSION_CREATE   (1 << 2)
 
#define EXTENSION_CREATE_RECOVERY   (1 << 3)
 
#define EXTENSION_DONT_CHECK_SIZE   (1 << 4)
 

Typedefs

typedef struct _MdfdVec MdfdVec
 

Functions

static void mdunlinkfork (RelFileNodeBackend rnode, ForkNumber forkNum, bool isRedo)
 
static MdfdVecmdopenfork (SMgrRelation reln, ForkNumber forknum, int behavior)
 
static void register_dirty_segment (SMgrRelation reln, ForkNumber forknum, MdfdVec *seg)
 
static void register_unlink_segment (RelFileNodeBackend rnode, ForkNumber forknum, BlockNumber segno)
 
static void register_forget_request (RelFileNodeBackend rnode, ForkNumber forknum, BlockNumber segno)
 
static void _fdvec_resize (SMgrRelation reln, ForkNumber forknum, int nseg)
 
static char * _mdfd_segpath (SMgrRelation reln, ForkNumber forknum, BlockNumber segno)
 
static MdfdVec_mdfd_openseg (SMgrRelation reln, ForkNumber forkno, BlockNumber segno, int oflags)
 
static MdfdVec_mdfd_getseg (SMgrRelation reln, ForkNumber forkno, BlockNumber blkno, bool skipFsync, int behavior)
 
static BlockNumber _mdnblocks (SMgrRelation reln, ForkNumber forknum, MdfdVec *seg)
 
void mdinit (void)
 
bool mdexists (SMgrRelation reln, ForkNumber forkNum)
 
void mdcreate (SMgrRelation reln, ForkNumber forkNum, bool isRedo)
 
void mdunlink (RelFileNodeBackend rnode, ForkNumber forkNum, bool isRedo)
 
void mdextend (SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum, char *buffer, bool skipFsync)
 
void mdopen (SMgrRelation reln)
 
void mdclose (SMgrRelation reln, ForkNumber forknum)
 
void mdprefetch (SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum)
 
void mdwriteback (SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum, BlockNumber nblocks)
 
void mdread (SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum, char *buffer)
 
void mdwrite (SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum, char *buffer, bool skipFsync)
 
BlockNumber mdnblocks (SMgrRelation reln, ForkNumber forknum)
 
void mdtruncate (SMgrRelation reln, ForkNumber forknum, BlockNumber nblocks)
 
void mdimmedsync (SMgrRelation reln, ForkNumber forknum)
 
void ForgetDatabaseSyncRequests (Oid dbid)
 
void DropRelationFiles (RelFileNode *delrels, int ndelrels, bool isRedo)
 
int mdsyncfiletag (const FileTag *ftag, char *path)
 
int mdunlinkfiletag (const FileTag *ftag, char *path)
 
bool mdfiletagmatches (const FileTag *ftag, const FileTag *candidate)
 

Variables

static MemoryContext MdCxt
 

Macro Definition Documentation

◆ EXTENSION_CREATE

#define EXTENSION_CREATE   (1 << 2)

Definition at line 108 of file md.c.

Referenced by _mdfd_getseg(), and mdextend().

◆ EXTENSION_CREATE_RECOVERY

#define EXTENSION_CREATE_RECOVERY   (1 << 3)

Definition at line 110 of file md.c.

Referenced by _mdfd_getseg(), mdread(), and mdwrite().

◆ EXTENSION_DONT_CHECK_SIZE

#define EXTENSION_DONT_CHECK_SIZE   (1 << 4)

Definition at line 118 of file md.c.

Referenced by _mdfd_getseg().

◆ EXTENSION_FAIL

#define EXTENSION_FAIL   (1 << 0)

Definition at line 104 of file md.c.

Referenced by _mdfd_getseg(), mdnblocks(), mdprefetch(), mdread(), and mdwrite().

◆ EXTENSION_RETURN_NULL

#define EXTENSION_RETURN_NULL   (1 << 1)

Definition at line 106 of file md.c.

Referenced by _mdfd_getseg(), mdexists(), mdopenfork(), and mdwriteback().

◆ INIT_MD_FILETAG

#define INIT_MD_FILETAG (   a,
  xx_rnode,
  xx_forknum,
  xx_segno 
)
Value:
( \
memset(&(a), 0, sizeof(FileTag)), \
(a).handler = SYNC_HANDLER_MD, \
(a).rnode = (xx_rnode), \
(a).forknum = (xx_forknum), \
(a).segno = (xx_segno) \
)
Definition: sync.h:45

Definition at line 92 of file md.c.

Referenced by ForgetDatabaseSyncRequests(), register_dirty_segment(), register_forget_request(), and register_unlink_segment().

Typedef Documentation

◆ MdfdVec

typedef struct _MdfdVec MdfdVec

Function Documentation

◆ _fdvec_resize()

static void _fdvec_resize ( SMgrRelation  reln,
ForkNumber  forknum,
int  nseg 
)
static

Definition at line 1025 of file md.c.

References SMgrRelationData::md_num_open_segs, SMgrRelationData::md_seg_fds, MemoryContextAlloc(), pfree(), and repalloc().

Referenced by _mdfd_openseg(), mdclose(), mdcreate(), mdopenfork(), and mdtruncate().

1028 {
1029  if (nseg == 0)
1030  {
1031  if (reln->md_num_open_segs[forknum] > 0)
1032  {
1033  pfree(reln->md_seg_fds[forknum]);
1034  reln->md_seg_fds[forknum] = NULL;
1035  }
1036  }
1037  else if (reln->md_num_open_segs[forknum] == 0)
1038  {
1039  reln->md_seg_fds[forknum] =
1040  MemoryContextAlloc(MdCxt, sizeof(MdfdVec) * nseg);
1041  }
1042  else
1043  {
1044  /*
1045  * It doesn't seem worthwhile complicating the code to amortize
1046  * repalloc() calls. Those are far faster than PathNameOpenFile() or
1047  * FileClose(), and the memory context internally will sometimes avoid
1048  * doing an actual reallocation.
1049  */
1050  reln->md_seg_fds[forknum] =
1051  repalloc(reln->md_seg_fds[forknum],
1052  sizeof(MdfdVec) * nseg);
1053  }
1054 
1055  reln->md_num_open_segs[forknum] = nseg;
1056 }
static MemoryContext MdCxt
Definition: md.c:88
void pfree(void *pointer)
Definition: mcxt.c:1056
Definition: md.c:82
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1069
int md_num_open_segs[MAX_FORKNUM+1]
Definition: smgr.h:70
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:796
struct _MdfdVec * md_seg_fds[MAX_FORKNUM+1]
Definition: smgr.h:71

◆ _mdfd_getseg()

static MdfdVec * _mdfd_getseg ( SMgrRelation  reln,
ForkNumber  forkno,
BlockNumber  blkno,
bool  skipFsync,
int  behavior 
)
static

Definition at line 1131 of file md.c.

References _mdfd_openseg(), _mdfd_segpath(), _mdnblocks(), Assert, elog, ereport, errcode_for_file_access(), errmsg(), ERROR, EXTENSION_CREATE, EXTENSION_CREATE_RECOVERY, EXTENSION_DONT_CHECK_SIZE, EXTENSION_FAIL, EXTENSION_RETURN_NULL, FATAL, FILE_POSSIBLY_DELETED, InRecovery, SMgrRelationData::md_num_open_segs, SMgrRelationData::md_seg_fds, mdextend(), _MdfdVec::mdfd_segno, mdopenfork(), palloc0(), and pfree().

Referenced by mdextend(), mdprefetch(), mdread(), mdwrite(), and mdwriteback().

1133 {
1134  MdfdVec *v;
1135  BlockNumber targetseg;
1136  BlockNumber nextsegno;
1137 
1138  /* some way to handle non-existent segments needs to be specified */
1139  Assert(behavior &
1141 
1142  targetseg = blkno / ((BlockNumber) RELSEG_SIZE);
1143 
1144  /* if an existing and opened segment, we're done */
1145  if (targetseg < reln->md_num_open_segs[forknum])
1146  {
1147  v = &reln->md_seg_fds[forknum][targetseg];
1148  return v;
1149  }
1150 
1151  /*
1152  * The target segment is not yet open. Iterate over all the segments
1153  * between the last opened and the target segment. This way missing
1154  * segments either raise an error, or get created (according to
1155  * 'behavior'). Start with either the last opened, or the first segment if
1156  * none was opened before.
1157  */
1158  if (reln->md_num_open_segs[forknum] > 0)
1159  v = &reln->md_seg_fds[forknum][reln->md_num_open_segs[forknum] - 1];
1160  else
1161  {
1162  v = mdopenfork(reln, forknum, behavior);
1163  if (!v)
1164  return NULL; /* if behavior & EXTENSION_RETURN_NULL */
1165  }
1166 
1167  for (nextsegno = reln->md_num_open_segs[forknum];
1168  nextsegno <= targetseg; nextsegno++)
1169  {
1170  BlockNumber nblocks = _mdnblocks(reln, forknum, v);
1171  int flags = 0;
1172 
1173  Assert(nextsegno == v->mdfd_segno + 1);
1174 
1175  if (nblocks > ((BlockNumber) RELSEG_SIZE))
1176  elog(FATAL, "segment too big");
1177 
1178  if ((behavior & EXTENSION_CREATE) ||
1179  (InRecovery && (behavior & EXTENSION_CREATE_RECOVERY)))
1180  {
1181  /*
1182  * Normally we will create new segments only if authorized by the
1183  * caller (i.e., we are doing mdextend()). But when doing WAL
1184  * recovery, create segments anyway; this allows cases such as
1185  * replaying WAL data that has a write into a high-numbered
1186  * segment of a relation that was later deleted. We want to go
1187  * ahead and create the segments so we can finish out the replay.
1188  *
1189  * We have to maintain the invariant that segments before the last
1190  * active segment are of size RELSEG_SIZE; therefore, if
1191  * extending, pad them out with zeroes if needed. (This only
1192  * matters if in recovery, or if the caller is extending the
1193  * relation discontiguously, but that can happen in hash indexes.)
1194  */
1195  if (nblocks < ((BlockNumber) RELSEG_SIZE))
1196  {
1197  char *zerobuf = palloc0(BLCKSZ);
1198 
1199  mdextend(reln, forknum,
1200  nextsegno * ((BlockNumber) RELSEG_SIZE) - 1,
1201  zerobuf, skipFsync);
1202  pfree(zerobuf);
1203  }
1204  flags = O_CREAT;
1205  }
1206  else if (!(behavior & EXTENSION_DONT_CHECK_SIZE) &&
1207  nblocks < ((BlockNumber) RELSEG_SIZE))
1208  {
1209  /*
1210  * When not extending (or explicitly including truncated
1211  * segments), only open the next segment if the current one is
1212  * exactly RELSEG_SIZE. If not (this branch), either return NULL
1213  * or fail.
1214  */
1215  if (behavior & EXTENSION_RETURN_NULL)
1216  {
1217  /*
1218  * Some callers discern between reasons for _mdfd_getseg()
1219  * returning NULL based on errno. As there's no failing
1220  * syscall involved in this case, explicitly set errno to
1221  * ENOENT, as that seems the closest interpretation.
1222  */
1223  errno = ENOENT;
1224  return NULL;
1225  }
1226 
1227  ereport(ERROR,
1229  errmsg("could not open file \"%s\" (target block %u): previous segment is only %u blocks",
1230  _mdfd_segpath(reln, forknum, nextsegno),
1231  blkno, nblocks)));
1232  }
1233 
1234  v = _mdfd_openseg(reln, forknum, nextsegno, flags);
1235 
1236  if (v == NULL)
1237  {
1238  if ((behavior & EXTENSION_RETURN_NULL) &&
1239  FILE_POSSIBLY_DELETED(errno))
1240  return NULL;
1241  ereport(ERROR,
1243  errmsg("could not open file \"%s\" (target block %u): %m",
1244  _mdfd_segpath(reln, forknum, nextsegno),
1245  blkno)));
1246  }
1247  }
1248 
1249  return v;
1250 }
#define EXTENSION_DONT_CHECK_SIZE
Definition: md.c:118
BlockNumber mdfd_segno
Definition: md.c:85
bool InRecovery
Definition: xlog.c:200
uint32 BlockNumber
Definition: block.h:31
#define EXTENSION_FAIL
Definition: md.c:104
void mdextend(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum, char *buffer, bool skipFsync)
Definition: md.c:388
void pfree(void *pointer)
Definition: mcxt.c:1056
static MdfdVec * mdopenfork(SMgrRelation reln, ForkNumber forknum, int behavior)
Definition: md.c:452
#define ERROR
Definition: elog.h:43
#define EXTENSION_RETURN_NULL
Definition: md.c:106
#define FATAL
Definition: elog.h:52
int errcode_for_file_access(void)
Definition: elog.c:631
#define EXTENSION_CREATE_RECOVERY
Definition: md.c:110
#define ereport(elevel, rest)
Definition: elog.h:141
void * palloc0(Size size)
Definition: mcxt.c:980
Definition: md.c:82
static MdfdVec * _mdfd_openseg(SMgrRelation reln, ForkNumber forkno, BlockNumber segno, int oflags)
Definition: md.c:1086
#define EXTENSION_CREATE
Definition: md.c:108
#define Assert(condition)
Definition: c.h:738
static BlockNumber _mdnblocks(SMgrRelation reln, ForkNumber forknum, MdfdVec *seg)
Definition: md.c:1256
int md_num_open_segs[MAX_FORKNUM+1]
Definition: smgr.h:70
int errmsg(const char *fmt,...)
Definition: elog.c:822
#define elog(elevel,...)
Definition: elog.h:228
struct _MdfdVec * md_seg_fds[MAX_FORKNUM+1]
Definition: smgr.h:71
#define FILE_POSSIBLY_DELETED(err)
Definition: fd.h:69
static char * _mdfd_segpath(SMgrRelation reln, ForkNumber forknum, BlockNumber segno)
Definition: md.c:1063

◆ _mdfd_openseg()

static MdfdVec * _mdfd_openseg ( SMgrRelation  reln,
ForkNumber  forkno,
BlockNumber  segno,
int  oflags 
)
static

Definition at line 1086 of file md.c.

References _fdvec_resize(), _mdfd_segpath(), _mdnblocks(), Assert, fd(), SMgrRelationData::md_num_open_segs, SMgrRelationData::md_seg_fds, _MdfdVec::mdfd_segno, _MdfdVec::mdfd_vfd, PathNameOpenFile(), pfree(), and PG_BINARY.

Referenced by _mdfd_getseg(), and mdnblocks().

1088 {
1089  MdfdVec *v;
1090  int fd;
1091  char *fullpath;
1092 
1093  fullpath = _mdfd_segpath(reln, forknum, segno);
1094 
1095  /* open the file */
1096  fd = PathNameOpenFile(fullpath, O_RDWR | PG_BINARY | oflags);
1097 
1098  pfree(fullpath);
1099 
1100  if (fd < 0)
1101  return NULL;
1102 
1103  /*
1104  * Segments are always opened in order from lowest to highest, so we must
1105  * be adding a new one at the end.
1106  */
1107  Assert(segno == reln->md_num_open_segs[forknum]);
1108 
1109  _fdvec_resize(reln, forknum, segno + 1);
1110 
1111  /* fill the entry */
1112  v = &reln->md_seg_fds[forknum][segno];
1113  v->mdfd_vfd = fd;
1114  v->mdfd_segno = segno;
1115 
1116  Assert(_mdnblocks(reln, forknum, v) <= ((BlockNumber) RELSEG_SIZE));
1117 
1118  /* all done */
1119  return v;
1120 }
File PathNameOpenFile(const char *fileName, int fileFlags)
Definition: fd.c:1445
BlockNumber mdfd_segno
Definition: md.c:85
uint32 BlockNumber
Definition: block.h:31
static int fd(const char *x, int i)
Definition: preproc-init.c:105
#define PG_BINARY
Definition: c.h:1230
void pfree(void *pointer)
Definition: mcxt.c:1056
static void _fdvec_resize(SMgrRelation reln, ForkNumber forknum, int nseg)
Definition: md.c:1025
Definition: md.c:82
#define Assert(condition)
Definition: c.h:738
static BlockNumber _mdnblocks(SMgrRelation reln, ForkNumber forknum, MdfdVec *seg)
Definition: md.c:1256
int md_num_open_segs[MAX_FORKNUM+1]
Definition: smgr.h:70
struct _MdfdVec * md_seg_fds[MAX_FORKNUM+1]
Definition: smgr.h:71
File mdfd_vfd
Definition: md.c:84
static char * _mdfd_segpath(SMgrRelation reln, ForkNumber forknum, BlockNumber segno)
Definition: md.c:1063

◆ _mdfd_segpath()

static char * _mdfd_segpath ( SMgrRelation  reln,
ForkNumber  forknum,
BlockNumber  segno 
)
static

Definition at line 1063 of file md.c.

References pfree(), psprintf(), relpath, and SMgrRelationData::smgr_rnode.

Referenced by _mdfd_getseg(), _mdfd_openseg(), and mdsyncfiletag().

1064 {
1065  char *path,
1066  *fullpath;
1067 
1068  path = relpath(reln->smgr_rnode, forknum);
1069 
1070  if (segno > 0)
1071  {
1072  fullpath = psprintf("%s.%u", path, segno);
1073  pfree(path);
1074  }
1075  else
1076  fullpath = path;
1077 
1078  return fullpath;
1079 }
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
void pfree(void *pointer)
Definition: mcxt.c:1056
RelFileNodeBackend smgr_rnode
Definition: smgr.h:42
#define relpath(rnode, forknum)
Definition: relpath.h:87

◆ _mdnblocks()

static BlockNumber _mdnblocks ( SMgrRelation  reln,
ForkNumber  forknum,
MdfdVec seg 
)
static

Definition at line 1256 of file md.c.

References ereport, errcode_for_file_access(), errmsg(), ERROR, FilePathName(), FileSize(), and _MdfdVec::mdfd_vfd.

Referenced by _mdfd_getseg(), _mdfd_openseg(), mdextend(), mdnblocks(), and mdopenfork().

1257 {
1258  off_t len;
1259 
1260  len = FileSize(seg->mdfd_vfd);
1261  if (len < 0)
1262  ereport(ERROR,
1264  errmsg("could not seek to end of file \"%s\": %m",
1265  FilePathName(seg->mdfd_vfd))));
1266  /* note that this calculation will ignore any partial block at EOF */
1267  return (BlockNumber) (len / BLCKSZ);
1268 }
off_t FileSize(File file)
Definition: fd.c:2157
uint32 BlockNumber
Definition: block.h:31
char * FilePathName(File file)
Definition: fd.c:2209
#define ERROR
Definition: elog.h:43
int errcode_for_file_access(void)
Definition: elog.c:631
#define ereport(elevel, rest)
Definition: elog.h:141
int errmsg(const char *fmt,...)
Definition: elog.c:822
File mdfd_vfd
Definition: md.c:84

◆ DropRelationFiles()

void DropRelationFiles ( RelFileNode delrels,
int  ndelrels,
bool  isRedo 
)

Definition at line 993 of file md.c.

References i, InvalidBackendId, MAX_FORKNUM, palloc(), pfree(), smgrclose(), smgrdounlinkall(), smgropen(), and XLogDropRelation().

Referenced by FinishPreparedTransaction(), xact_redo_abort(), and xact_redo_commit().

994 {
995  SMgrRelation *srels;
996  int i;
997 
998  srels = palloc(sizeof(SMgrRelation) * ndelrels);
999  for (i = 0; i < ndelrels; i++)
1000  {
1001  SMgrRelation srel = smgropen(delrels[i], InvalidBackendId);
1002 
1003  if (isRedo)
1004  {
1005  ForkNumber fork;
1006 
1007  for (fork = 0; fork <= MAX_FORKNUM; fork++)
1008  XLogDropRelation(delrels[i], fork);
1009  }
1010  srels[i] = srel;
1011  }
1012 
1013  smgrdounlinkall(srels, ndelrels, isRedo);
1014 
1015  for (i = 0; i < ndelrels; i++)
1016  smgrclose(srels[i]);
1017  pfree(srels);
1018 }
void smgrclose(SMgrRelation reln)
Definition: smgr.c:256
void smgrdounlinkall(SMgrRelation *rels, int nrels, bool isRedo)
Definition: smgr.c:405
void pfree(void *pointer)
Definition: mcxt.c:1056
SMgrRelation smgropen(RelFileNode rnode, BackendId backend)
Definition: smgr.c:145
ForkNumber
Definition: relpath.h:40
#define InvalidBackendId
Definition: backendid.h:23
#define MAX_FORKNUM
Definition: relpath.h:55
void XLogDropRelation(RelFileNode rnode, ForkNumber forknum)
Definition: xlogutils.c:611
void * palloc(Size size)
Definition: mcxt.c:949
int i

◆ ForgetDatabaseSyncRequests()

void ForgetDatabaseSyncRequests ( Oid  dbid)

Definition at line 975 of file md.c.

References RelFileNode::dbNode, INIT_MD_FILETAG, InvalidBlockNumber, InvalidForkNumber, RegisterSyncRequest(), RelFileNode::relNode, RelFileNode::spcNode, and SYNC_FILTER_REQUEST.

Referenced by dbase_redo(), and dropdb().

976 {
977  FileTag tag;
978  RelFileNode rnode;
979 
980  rnode.dbNode = dbid;
981  rnode.spcNode = 0;
982  rnode.relNode = 0;
983 
985 
986  RegisterSyncRequest(&tag, SYNC_FILTER_REQUEST, true /* retryOnError */ );
987 }
#define INIT_MD_FILETAG(a, xx_rnode, xx_forknum, xx_segno)
Definition: md.c:92
bool RegisterSyncRequest(const FileTag *ftag, SyncRequestType type, bool retryOnError)
Definition: sync.c:530
#define InvalidBlockNumber
Definition: block.h:33
Definition: sync.h:45

◆ mdclose()

void mdclose ( SMgrRelation  reln,
ForkNumber  forknum 
)

Definition at line 506 of file md.c.

References _fdvec_resize(), FileClose(), SMgrRelationData::md_num_open_segs, SMgrRelationData::md_seg_fds, and _MdfdVec::mdfd_vfd.

Referenced by mdexists().

507 {
508  int nopensegs = reln->md_num_open_segs[forknum];
509 
510  /* No work if already closed */
511  if (nopensegs == 0)
512  return;
513 
514  /* close segments starting from the end */
515  while (nopensegs > 0)
516  {
517  MdfdVec *v = &reln->md_seg_fds[forknum][nopensegs - 1];
518 
519  FileClose(v->mdfd_vfd);
520  _fdvec_resize(reln, forknum, nopensegs - 1);
521  nopensegs--;
522  }
523 }
static void _fdvec_resize(SMgrRelation reln, ForkNumber forknum, int nseg)
Definition: md.c:1025
Definition: md.c:82
void FileClose(File file)
Definition: fd.c:1835
int md_num_open_segs[MAX_FORKNUM+1]
Definition: smgr.h:70
struct _MdfdVec * md_seg_fds[MAX_FORKNUM+1]
Definition: smgr.h:71
File mdfd_vfd
Definition: md.c:84

◆ mdcreate()

void mdcreate ( SMgrRelation  reln,
ForkNumber  forkNum,
bool  isRedo 
)

Definition at line 178 of file md.c.

References _fdvec_resize(), Assert, RelFileNode::dbNode, ereport, errcode_for_file_access(), errmsg(), ERROR, fd(), SMgrRelationData::md_num_open_segs, SMgrRelationData::md_seg_fds, _MdfdVec::mdfd_segno, _MdfdVec::mdfd_vfd, RelFileNodeBackend::node, PathNameOpenFile(), pfree(), PG_BINARY, relpath, SMgrRelationData::smgr_rnode, RelFileNode::spcNode, and TablespaceCreateDbspace().

179 {
180  MdfdVec *mdfd;
181  char *path;
182  File fd;
183 
184  if (isRedo && reln->md_num_open_segs[forkNum] > 0)
185  return; /* created and opened already... */
186 
187  Assert(reln->md_num_open_segs[forkNum] == 0);
188 
189  /*
190  * We may be using the target table space for the first time in this
191  * database, so create a per-database subdirectory if needed.
192  *
193  * XXX this is a fairly ugly violation of module layering, but this seems
194  * to be the best place to put the check. Maybe TablespaceCreateDbspace
195  * should be here and not in commands/tablespace.c? But that would imply
196  * importing a lot of stuff that smgr.c oughtn't know, either.
197  */
199  reln->smgr_rnode.node.dbNode,
200  isRedo);
201 
202  path = relpath(reln->smgr_rnode, forkNum);
203 
204  fd = PathNameOpenFile(path, O_RDWR | O_CREAT | O_EXCL | PG_BINARY);
205 
206  if (fd < 0)
207  {
208  int save_errno = errno;
209 
210  if (isRedo)
211  fd = PathNameOpenFile(path, O_RDWR | PG_BINARY);
212  if (fd < 0)
213  {
214  /* be sure to report the error reported by create, not open */
215  errno = save_errno;
216  ereport(ERROR,
218  errmsg("could not create file \"%s\": %m", path)));
219  }
220  }
221 
222  pfree(path);
223 
224  _fdvec_resize(reln, forkNum, 1);
225  mdfd = &reln->md_seg_fds[forkNum][0];
226  mdfd->mdfd_vfd = fd;
227  mdfd->mdfd_segno = 0;
228 }
File PathNameOpenFile(const char *fileName, int fileFlags)
Definition: fd.c:1445
BlockNumber mdfd_segno
Definition: md.c:85
static int fd(const char *x, int i)
Definition: preproc-init.c:105
#define PG_BINARY
Definition: c.h:1230
void pfree(void *pointer)
Definition: mcxt.c:1056
#define ERROR
Definition: elog.h:43
RelFileNodeBackend smgr_rnode
Definition: smgr.h:42
int errcode_for_file_access(void)
Definition: elog.c:631
#define ereport(elevel, rest)
Definition: elog.h:141
static void _fdvec_resize(SMgrRelation reln, ForkNumber forknum, int nseg)
Definition: md.c:1025
Definition: md.c:82
RelFileNode node
Definition: relfilenode.h:74
#define Assert(condition)
Definition: c.h:738
void TablespaceCreateDbspace(Oid spcNode, Oid dbNode, bool isRedo)
Definition: tablespace.c:115
int md_num_open_segs[MAX_FORKNUM+1]
Definition: smgr.h:70
int errmsg(const char *fmt,...)
Definition: elog.c:822
#define relpath(rnode, forknum)
Definition: relpath.h:87
struct _MdfdVec * md_seg_fds[MAX_FORKNUM+1]
Definition: smgr.h:71
File mdfd_vfd
Definition: md.c:84
int File
Definition: fd.h:49

◆ mdexists()

bool mdexists ( SMgrRelation  reln,
ForkNumber  forkNum 
)

Definition at line 161 of file md.c.

References EXTENSION_RETURN_NULL, mdclose(), and mdopenfork().

162 {
163  /*
164  * Close it first, to ensure that we notice if the fork has been unlinked
165  * since we opened it.
166  */
167  mdclose(reln, forkNum);
168 
169  return (mdopenfork(reln, forkNum, EXTENSION_RETURN_NULL) != NULL);
170 }
static MdfdVec * mdopenfork(SMgrRelation reln, ForkNumber forknum, int behavior)
Definition: md.c:452
#define EXTENSION_RETURN_NULL
Definition: md.c:106
void mdclose(SMgrRelation reln, ForkNumber forknum)
Definition: md.c:506

◆ mdextend()

void mdextend ( SMgrRelation  reln,
ForkNumber  forknum,
BlockNumber  blocknum,
char *  buffer,
bool  skipFsync 
)

Definition at line 388 of file md.c.

References _mdfd_getseg(), _mdnblocks(), Assert, ereport, errcode(), errcode_for_file_access(), errhint(), errmsg(), ERROR, EXTENSION_CREATE, FilePathName(), FileWrite(), InvalidBlockNumber, _MdfdVec::mdfd_vfd, mdnblocks(), register_dirty_segment(), relpath, SMgrRelationData::smgr_rnode, SmgrIsTemp, and WAIT_EVENT_DATA_FILE_EXTEND.

Referenced by _mdfd_getseg().

390 {
391  off_t seekpos;
392  int nbytes;
393  MdfdVec *v;
394 
395  /* This assert is too expensive to have on normally ... */
396 #ifdef CHECK_WRITE_VS_EXTEND
397  Assert(blocknum >= mdnblocks(reln, forknum));
398 #endif
399 
400  /*
401  * If a relation manages to grow to 2^32-1 blocks, refuse to extend it any
402  * more --- we mustn't create a block whose number actually is
403  * InvalidBlockNumber.
404  */
405  if (blocknum == InvalidBlockNumber)
406  ereport(ERROR,
407  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
408  errmsg("cannot extend file \"%s\" beyond %u blocks",
409  relpath(reln->smgr_rnode, forknum),
411 
412  v = _mdfd_getseg(reln, forknum, blocknum, skipFsync, EXTENSION_CREATE);
413 
414  seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) RELSEG_SIZE));
415 
416  Assert(seekpos < (off_t) BLCKSZ * RELSEG_SIZE);
417 
418  if ((nbytes = FileWrite(v->mdfd_vfd, buffer, BLCKSZ, seekpos, WAIT_EVENT_DATA_FILE_EXTEND)) != BLCKSZ)
419  {
420  if (nbytes < 0)
421  ereport(ERROR,
423  errmsg("could not extend file \"%s\": %m",
424  FilePathName(v->mdfd_vfd)),
425  errhint("Check free disk space.")));
426  /* short write: complain appropriately */
427  ereport(ERROR,
428  (errcode(ERRCODE_DISK_FULL),
429  errmsg("could not extend file \"%s\": wrote only %d of %d bytes at block %u",
431  nbytes, BLCKSZ, blocknum),
432  errhint("Check free disk space.")));
433  }
434 
435  if (!skipFsync && !SmgrIsTemp(reln))
436  register_dirty_segment(reln, forknum, v);
437 
438  Assert(_mdnblocks(reln, forknum, v) <= ((BlockNumber) RELSEG_SIZE));
439 }
static MdfdVec * _mdfd_getseg(SMgrRelation reln, ForkNumber forkno, BlockNumber blkno, bool skipFsync, int behavior)
Definition: md.c:1131
int errhint(const char *fmt,...)
Definition: elog.c:1069
BlockNumber mdnblocks(SMgrRelation reln, ForkNumber forknum)
Definition: md.c:731
int errcode(int sqlerrcode)
Definition: elog.c:608
uint32 BlockNumber
Definition: block.h:31
char * FilePathName(File file)
Definition: fd.c:2209
#define SmgrIsTemp(smgr)
Definition: smgr.h:79
#define ERROR
Definition: elog.h:43
RelFileNodeBackend smgr_rnode
Definition: smgr.h:42
int errcode_for_file_access(void)
Definition: elog.c:631
#define ereport(elevel, rest)
Definition: elog.h:141
int FileWrite(File file, char *buffer, int amount, off_t offset, uint32 wait_event_info)
Definition: fd.c:2038
Definition: md.c:82
#define EXTENSION_CREATE
Definition: md.c:108
#define Assert(condition)
Definition: c.h:738
static BlockNumber _mdnblocks(SMgrRelation reln, ForkNumber forknum, MdfdVec *seg)
Definition: md.c:1256
#define InvalidBlockNumber
Definition: block.h:33
int errmsg(const char *fmt,...)
Definition: elog.c:822
#define relpath(rnode, forknum)
Definition: relpath.h:87
File mdfd_vfd
Definition: md.c:84
static void register_dirty_segment(SMgrRelation reln, ForkNumber forknum, MdfdVec *seg)
Definition: md.c:918

◆ mdfiletagmatches()

bool mdfiletagmatches ( const FileTag ftag,
const FileTag candidate 
)

Definition at line 1343 of file md.c.

References RelFileNode::dbNode, and FileTag::rnode.

1344 {
1345  /*
1346  * For now we only use filter requests as a way to drop all scheduled
1347  * callbacks relating to a given database, when dropping the database.
1348  * We'll return true for all candidates that have the same database OID as
1349  * the ftag from the SYNC_FILTER_REQUEST request, so they're forgotten.
1350  */
1351  return ftag->rnode.dbNode == candidate->rnode.dbNode;
1352 }
RelFileNode rnode
Definition: sync.h:49

◆ mdimmedsync()

void mdimmedsync ( SMgrRelation  reln,
ForkNumber  forknum 
)

Definition at line 883 of file md.c.

References data_sync_elevel(), ereport, errcode_for_file_access(), errmsg(), ERROR, FilePathName(), FileSync(), SMgrRelationData::md_num_open_segs, SMgrRelationData::md_seg_fds, _MdfdVec::mdfd_vfd, mdnblocks(), and WAIT_EVENT_DATA_FILE_IMMEDIATE_SYNC.

884 {
885  int segno;
886 
887  /*
888  * NOTE: mdnblocks makes sure we have opened all active segments, so that
889  * fsync loop will get them all!
890  */
891  mdnblocks(reln, forknum);
892 
893  segno = reln->md_num_open_segs[forknum];
894 
895  while (segno > 0)
896  {
897  MdfdVec *v = &reln->md_seg_fds[forknum][segno - 1];
898 
902  errmsg("could not fsync file \"%s\": %m",
903  FilePathName(v->mdfd_vfd))));
904  segno--;
905  }
906 }
BlockNumber mdnblocks(SMgrRelation reln, ForkNumber forknum)
Definition: md.c:731
char * FilePathName(File file)
Definition: fd.c:2209
#define ERROR
Definition: elog.h:43
int FileSync(File file, uint32 wait_event_info)
Definition: fd.c:2136
int errcode_for_file_access(void)
Definition: elog.c:631
#define ereport(elevel, rest)
Definition: elog.h:141
int data_sync_elevel(int elevel)
Definition: fd.c:3608
Definition: md.c:82
int md_num_open_segs[MAX_FORKNUM+1]
Definition: smgr.h:70
int errmsg(const char *fmt,...)
Definition: elog.c:822
struct _MdfdVec * md_seg_fds[MAX_FORKNUM+1]
Definition: smgr.h:71
File mdfd_vfd
Definition: md.c:84

◆ mdinit()

void mdinit ( void  )

Definition at line 148 of file md.c.

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, and TopMemoryContext.

149 {
151  "MdSmgr",
153 }
#define AllocSetContextCreate
Definition: memutils.h:170
static MemoryContext MdCxt
Definition: md.c:88
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:192
MemoryContext TopMemoryContext
Definition: mcxt.c:44

◆ mdnblocks()

BlockNumber mdnblocks ( SMgrRelation  reln,
ForkNumber  forknum 
)

Definition at line 731 of file md.c.

References _mdfd_openseg(), _mdnblocks(), Assert, elog, EXTENSION_FAIL, FATAL, SMgrRelationData::md_num_open_segs, SMgrRelationData::md_seg_fds, and mdopenfork().

Referenced by mdextend(), mdimmedsync(), mdtruncate(), and mdwrite().

732 {
733  MdfdVec *v = mdopenfork(reln, forknum, EXTENSION_FAIL);
734  BlockNumber nblocks;
735  BlockNumber segno = 0;
736 
737  /* mdopen has opened the first segment */
738  Assert(reln->md_num_open_segs[forknum] > 0);
739 
740  /*
741  * Start from the last open segments, to avoid redundant seeks. We have
742  * previously verified that these segments are exactly RELSEG_SIZE long,
743  * and it's useless to recheck that each time.
744  *
745  * NOTE: this assumption could only be wrong if another backend has
746  * truncated the relation. We rely on higher code levels to handle that
747  * scenario by closing and re-opening the md fd, which is handled via
748  * relcache flush. (Since the checkpointer doesn't participate in
749  * relcache flush, it could have segment entries for inactive segments;
750  * that's OK because the checkpointer never needs to compute relation
751  * size.)
752  */
753  segno = reln->md_num_open_segs[forknum] - 1;
754  v = &reln->md_seg_fds[forknum][segno];
755 
756  for (;;)
757  {
758  nblocks = _mdnblocks(reln, forknum, v);
759  if (nblocks > ((BlockNumber) RELSEG_SIZE))
760  elog(FATAL, "segment too big");
761  if (nblocks < ((BlockNumber) RELSEG_SIZE))
762  return (segno * ((BlockNumber) RELSEG_SIZE)) + nblocks;
763 
764  /*
765  * If segment is exactly RELSEG_SIZE, advance to next one.
766  */
767  segno++;
768 
769  /*
770  * We used to pass O_CREAT here, but that has the disadvantage that it
771  * might create a segment which has vanished through some operating
772  * system misadventure. In such a case, creating the segment here
773  * undermines _mdfd_getseg's attempts to notice and report an error
774  * upon access to a missing segment.
775  */
776  v = _mdfd_openseg(reln, forknum, segno, 0);
777  if (v == NULL)
778  return segno * ((BlockNumber) RELSEG_SIZE);
779  }
780 }
uint32 BlockNumber
Definition: block.h:31
#define EXTENSION_FAIL
Definition: md.c:104
static MdfdVec * mdopenfork(SMgrRelation reln, ForkNumber forknum, int behavior)
Definition: md.c:452
#define FATAL
Definition: elog.h:52
Definition: md.c:82
static MdfdVec * _mdfd_openseg(SMgrRelation reln, ForkNumber forkno, BlockNumber segno, int oflags)
Definition: md.c:1086
#define Assert(condition)
Definition: c.h:738
static BlockNumber _mdnblocks(SMgrRelation reln, ForkNumber forknum, MdfdVec *seg)
Definition: md.c:1256
int md_num_open_segs[MAX_FORKNUM+1]
Definition: smgr.h:70
#define elog(elevel,...)
Definition: elog.h:228
struct _MdfdVec * md_seg_fds[MAX_FORKNUM+1]
Definition: smgr.h:71

◆ mdopen()

void mdopen ( SMgrRelation  reln)

Definition at line 495 of file md.c.

References MAX_FORKNUM, and SMgrRelationData::md_num_open_segs.

496 {
497  /* mark it not open */
498  for (int forknum = 0; forknum <= MAX_FORKNUM; forknum++)
499  reln->md_num_open_segs[forknum] = 0;
500 }
#define MAX_FORKNUM
Definition: relpath.h:55
int md_num_open_segs[MAX_FORKNUM+1]
Definition: smgr.h:70

◆ mdopenfork()

static MdfdVec * mdopenfork ( SMgrRelation  reln,
ForkNumber  forknum,
int  behavior 
)
static

Definition at line 452 of file md.c.

References _fdvec_resize(), _mdnblocks(), Assert, ereport, errcode_for_file_access(), errmsg(), ERROR, EXTENSION_RETURN_NULL, fd(), FILE_POSSIBLY_DELETED, SMgrRelationData::md_num_open_segs, SMgrRelationData::md_seg_fds, _MdfdVec::mdfd_segno, _MdfdVec::mdfd_vfd, PathNameOpenFile(), pfree(), PG_BINARY, relpath, and SMgrRelationData::smgr_rnode.

Referenced by _mdfd_getseg(), mdexists(), and mdnblocks().

453 {
454  MdfdVec *mdfd;
455  char *path;
456  File fd;
457 
458  /* No work if already open */
459  if (reln->md_num_open_segs[forknum] > 0)
460  return &reln->md_seg_fds[forknum][0];
461 
462  path = relpath(reln->smgr_rnode, forknum);
463 
464  fd = PathNameOpenFile(path, O_RDWR | PG_BINARY);
465 
466  if (fd < 0)
467  {
468  if ((behavior & EXTENSION_RETURN_NULL) &&
469  FILE_POSSIBLY_DELETED(errno))
470  {
471  pfree(path);
472  return NULL;
473  }
474  ereport(ERROR,
476  errmsg("could not open file \"%s\": %m", path)));
477  }
478 
479  pfree(path);
480 
481  _fdvec_resize(reln, forknum, 1);
482  mdfd = &reln->md_seg_fds[forknum][0];
483  mdfd->mdfd_vfd = fd;
484  mdfd->mdfd_segno = 0;
485 
486  Assert(_mdnblocks(reln, forknum, mdfd) <= ((BlockNumber) RELSEG_SIZE));
487 
488  return mdfd;
489 }
File PathNameOpenFile(const char *fileName, int fileFlags)
Definition: fd.c:1445
BlockNumber mdfd_segno
Definition: md.c:85
uint32 BlockNumber
Definition: block.h:31
static int fd(const char *x, int i)
Definition: preproc-init.c:105
#define PG_BINARY
Definition: c.h:1230
void pfree(void *pointer)
Definition: mcxt.c:1056
#define ERROR
Definition: elog.h:43
#define EXTENSION_RETURN_NULL
Definition: md.c:106
RelFileNodeBackend smgr_rnode
Definition: smgr.h:42
int errcode_for_file_access(void)
Definition: elog.c:631
#define ereport(elevel, rest)
Definition: elog.h:141
static void _fdvec_resize(SMgrRelation reln, ForkNumber forknum, int nseg)
Definition: md.c:1025
Definition: md.c:82
#define Assert(condition)
Definition: c.h:738
static BlockNumber _mdnblocks(SMgrRelation reln, ForkNumber forknum, MdfdVec *seg)
Definition: md.c:1256
int md_num_open_segs[MAX_FORKNUM+1]
Definition: smgr.h:70
int errmsg(const char *fmt,...)
Definition: elog.c:822
#define relpath(rnode, forknum)
Definition: relpath.h:87
struct _MdfdVec * md_seg_fds[MAX_FORKNUM+1]
Definition: smgr.h:71
File mdfd_vfd
Definition: md.c:84
int File
Definition: fd.h:49
#define FILE_POSSIBLY_DELETED(err)
Definition: fd.h:69

◆ mdprefetch()

void mdprefetch ( SMgrRelation  reln,
ForkNumber  forknum,
BlockNumber  blocknum 
)

Definition at line 529 of file md.c.

References _mdfd_getseg(), Assert, EXTENSION_FAIL, FilePrefetch(), _MdfdVec::mdfd_vfd, and WAIT_EVENT_DATA_FILE_PREFETCH.

530 {
531 #ifdef USE_PREFETCH
532  off_t seekpos;
533  MdfdVec *v;
534 
535  v = _mdfd_getseg(reln, forknum, blocknum, false, EXTENSION_FAIL);
536 
537  seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) RELSEG_SIZE));
538 
539  Assert(seekpos < (off_t) BLCKSZ * RELSEG_SIZE);
540 
541  (void) FilePrefetch(v->mdfd_vfd, seekpos, BLCKSZ, WAIT_EVENT_DATA_FILE_PREFETCH);
542 #endif /* USE_PREFETCH */
543 }
static MdfdVec * _mdfd_getseg(SMgrRelation reln, ForkNumber forkno, BlockNumber blkno, bool skipFsync, int behavior)
Definition: md.c:1131
uint32 BlockNumber
Definition: block.h:31
#define EXTENSION_FAIL
Definition: md.c:104
Definition: md.c:82
int FilePrefetch(File file, off_t offset, int amount, uint32 wait_event_info)
Definition: fd.c:1931
#define Assert(condition)
Definition: c.h:738
File mdfd_vfd
Definition: md.c:84

◆ mdread()

void mdread ( SMgrRelation  reln,
ForkNumber  forknum,
BlockNumber  blocknum,
char *  buffer 
)

Definition at line 601 of file md.c.

References _mdfd_getseg(), Assert, RelFileNodeBackend::backend, RelFileNode::dbNode, ereport, errcode(), ERRCODE_DATA_CORRUPTED, errcode_for_file_access(), errmsg(), ERROR, EXTENSION_CREATE_RECOVERY, EXTENSION_FAIL, FilePathName(), FileRead(), InRecovery, _MdfdVec::mdfd_vfd, MemSet, RelFileNodeBackend::node, RelFileNode::relNode, SMgrRelationData::smgr_rnode, RelFileNode::spcNode, WAIT_EVENT_DATA_FILE_READ, and zero_damaged_pages.

603 {
604  off_t seekpos;
605  int nbytes;
606  MdfdVec *v;
607 
608  TRACE_POSTGRESQL_SMGR_MD_READ_START(forknum, blocknum,
609  reln->smgr_rnode.node.spcNode,
610  reln->smgr_rnode.node.dbNode,
611  reln->smgr_rnode.node.relNode,
612  reln->smgr_rnode.backend);
613 
614  v = _mdfd_getseg(reln, forknum, blocknum, false,
616 
617  seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) RELSEG_SIZE));
618 
619  Assert(seekpos < (off_t) BLCKSZ * RELSEG_SIZE);
620 
621  nbytes = FileRead(v->mdfd_vfd, buffer, BLCKSZ, seekpos, WAIT_EVENT_DATA_FILE_READ);
622 
623  TRACE_POSTGRESQL_SMGR_MD_READ_DONE(forknum, blocknum,
624  reln->smgr_rnode.node.spcNode,
625  reln->smgr_rnode.node.dbNode,
626  reln->smgr_rnode.node.relNode,
627  reln->smgr_rnode.backend,
628  nbytes,
629  BLCKSZ);
630 
631  if (nbytes != BLCKSZ)
632  {
633  if (nbytes < 0)
634  ereport(ERROR,
636  errmsg("could not read block %u in file \"%s\": %m",
637  blocknum, FilePathName(v->mdfd_vfd))));
638 
639  /*
640  * Short read: we are at or past EOF, or we read a partial block at
641  * EOF. Normally this is an error; upper levels should never try to
642  * read a nonexistent block. However, if zero_damaged_pages is ON or
643  * we are InRecovery, we should instead return zeroes without
644  * complaining. This allows, for example, the case of trying to
645  * update a block that was later truncated away.
646  */
648  MemSet(buffer, 0, BLCKSZ);
649  else
650  ereport(ERROR,
652  errmsg("could not read block %u in file \"%s\": read only %d of %d bytes",
653  blocknum, FilePathName(v->mdfd_vfd),
654  nbytes, BLCKSZ)));
655  }
656 }
static MdfdVec * _mdfd_getseg(SMgrRelation reln, ForkNumber forkno, BlockNumber blkno, bool skipFsync, int behavior)
Definition: md.c:1131
bool InRecovery
Definition: xlog.c:200
int errcode(int sqlerrcode)
Definition: elog.c:608
#define MemSet(start, val, len)
Definition: c.h:971
uint32 BlockNumber
Definition: block.h:31
char * FilePathName(File file)
Definition: fd.c:2209
#define EXTENSION_FAIL
Definition: md.c:104
#define ERROR
Definition: elog.h:43
RelFileNodeBackend smgr_rnode
Definition: smgr.h:42
int errcode_for_file_access(void)
Definition: elog.c:631
#define EXTENSION_CREATE_RECOVERY
Definition: md.c:110
#define ereport(elevel, rest)
Definition: elog.h:141
#define ERRCODE_DATA_CORRUPTED
Definition: pg_basebackup.c:45
Definition: md.c:82
RelFileNode node
Definition: relfilenode.h:74
#define Assert(condition)
Definition: c.h:738
BackendId backend
Definition: relfilenode.h:75
int errmsg(const char *fmt,...)
Definition: elog.c:822
int FileRead(File file, char *buffer, int amount, off_t offset, uint32 wait_event_info)
Definition: fd.c:1982
File mdfd_vfd
Definition: md.c:84
bool zero_damaged_pages
Definition: bufmgr.c:109

◆ mdsyncfiletag()

int mdsyncfiletag ( const FileTag ftag,
char *  path 
)

Definition at line 1277 of file md.c.

References _mdfd_segpath(), FileClose(), FilePathName(), FileSync(), FileTag::forknum, InvalidBackendId, MAXPGPATH, SMgrRelationData::md_num_open_segs, SMgrRelationData::md_seg_fds, _MdfdVec::mdfd_vfd, PathNameOpenFile(), pfree(), PG_BINARY, FileTag::rnode, FileTag::segno, smgropen(), strlcpy(), and WAIT_EVENT_DATA_FILE_SYNC.

1278 {
1280  File file;
1281  bool need_to_close;
1282  int result,
1283  save_errno;
1284 
1285  /* See if we already have the file open, or need to open it. */
1286  if (ftag->segno < reln->md_num_open_segs[ftag->forknum])
1287  {
1288  file = reln->md_seg_fds[ftag->forknum][ftag->segno].mdfd_vfd;
1289  strlcpy(path, FilePathName(file), MAXPGPATH);
1290  need_to_close = false;
1291  }
1292  else
1293  {
1294  char *p;
1295 
1296  p = _mdfd_segpath(reln, ftag->forknum, ftag->segno);
1297  strlcpy(path, p, MAXPGPATH);
1298  pfree(p);
1299 
1300  file = PathNameOpenFile(path, O_RDWR | PG_BINARY);
1301  if (file < 0)
1302  return -1;
1303  need_to_close = true;
1304  }
1305 
1306  /* Sync the file. */
1307  result = FileSync(file, WAIT_EVENT_DATA_FILE_SYNC);
1308  save_errno = errno;
1309 
1310  if (need_to_close)
1311  FileClose(file);
1312 
1313  errno = save_errno;
1314  return result;
1315 }
uint32 segno
Definition: sync.h:50
File PathNameOpenFile(const char *fileName, int fileFlags)
Definition: fd.c:1445
int16 forknum
Definition: sync.h:48
RelFileNode rnode
Definition: sync.h:49
char * FilePathName(File file)
Definition: fd.c:2209
#define PG_BINARY
Definition: c.h:1230
void pfree(void *pointer)
Definition: mcxt.c:1056
#define MAXPGPATH
int FileSync(File file, uint32 wait_event_info)
Definition: fd.c:2136
SMgrRelation smgropen(RelFileNode rnode, BackendId backend)
Definition: smgr.c:145
#define InvalidBackendId
Definition: backendid.h:23
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45
void FileClose(File file)
Definition: fd.c:1835
int md_num_open_segs[MAX_FORKNUM+1]
Definition: smgr.h:70
struct _MdfdVec * md_seg_fds[MAX_FORKNUM+1]
Definition: smgr.h:71
File mdfd_vfd
Definition: md.c:84
int File
Definition: fd.h:49
static char * _mdfd_segpath(SMgrRelation reln, ForkNumber forknum, BlockNumber segno)
Definition: md.c:1063

◆ mdtruncate()

void mdtruncate ( SMgrRelation  reln,
ForkNumber  forknum,
BlockNumber  nblocks 
)

Definition at line 786 of file md.c.

References _fdvec_resize(), Assert, ereport, errcode_for_file_access(), errmsg(), ERROR, FileClose(), FilePathName(), FileTruncate(), InRecovery, SMgrRelationData::md_num_open_segs, SMgrRelationData::md_seg_fds, _MdfdVec::mdfd_vfd, mdnblocks(), register_dirty_segment(), relpath, SMgrRelationData::smgr_rnode, SmgrIsTemp, and WAIT_EVENT_DATA_FILE_TRUNCATE.

787 {
788  BlockNumber curnblk;
789  BlockNumber priorblocks;
790  int curopensegs;
791 
792  /*
793  * NOTE: mdnblocks makes sure we have opened all active segments, so that
794  * truncation loop will get them all!
795  */
796  curnblk = mdnblocks(reln, forknum);
797  if (nblocks > curnblk)
798  {
799  /* Bogus request ... but no complaint if InRecovery */
800  if (InRecovery)
801  return;
802  ereport(ERROR,
803  (errmsg("could not truncate file \"%s\" to %u blocks: it's only %u blocks now",
804  relpath(reln->smgr_rnode, forknum),
805  nblocks, curnblk)));
806  }
807  if (nblocks == curnblk)
808  return; /* no work */
809 
810  /*
811  * Truncate segments, starting at the last one. Starting at the end makes
812  * managing the memory for the fd array easier, should there be errors.
813  */
814  curopensegs = reln->md_num_open_segs[forknum];
815  while (curopensegs > 0)
816  {
817  MdfdVec *v;
818 
819  priorblocks = (curopensegs - 1) * RELSEG_SIZE;
820 
821  v = &reln->md_seg_fds[forknum][curopensegs - 1];
822 
823  if (priorblocks > nblocks)
824  {
825  /*
826  * This segment is no longer active. We truncate the file, but do
827  * not delete it, for reasons explained in the header comments.
828  */
830  ereport(ERROR,
832  errmsg("could not truncate file \"%s\": %m",
833  FilePathName(v->mdfd_vfd))));
834 
835  if (!SmgrIsTemp(reln))
836  register_dirty_segment(reln, forknum, v);
837 
838  /* we never drop the 1st segment */
839  Assert(v != &reln->md_seg_fds[forknum][0]);
840 
841  FileClose(v->mdfd_vfd);
842  _fdvec_resize(reln, forknum, curopensegs - 1);
843  }
844  else if (priorblocks + ((BlockNumber) RELSEG_SIZE) > nblocks)
845  {
846  /*
847  * This is the last segment we want to keep. Truncate the file to
848  * the right length. NOTE: if nblocks is exactly a multiple K of
849  * RELSEG_SIZE, we will truncate the K+1st segment to 0 length but
850  * keep it. This adheres to the invariant given in the header
851  * comments.
852  */
853  BlockNumber lastsegblocks = nblocks - priorblocks;
854 
855  if (FileTruncate(v->mdfd_vfd, (off_t) lastsegblocks * BLCKSZ, WAIT_EVENT_DATA_FILE_TRUNCATE) < 0)
856  ereport(ERROR,
858  errmsg("could not truncate file \"%s\" to %u blocks: %m",
860  nblocks)));
861  if (!SmgrIsTemp(reln))
862  register_dirty_segment(reln, forknum, v);
863  }
864  else
865  {
866  /*
867  * We still need this segment, so nothing to do for this and any
868  * earlier segment.
869  */
870  break;
871  }
872  curopensegs--;
873  }
874 }
BlockNumber mdnblocks(SMgrRelation reln, ForkNumber forknum)
Definition: md.c:731
bool InRecovery
Definition: xlog.c:200
uint32 BlockNumber
Definition: block.h:31
char * FilePathName(File file)
Definition: fd.c:2209
#define SmgrIsTemp(smgr)
Definition: smgr.h:79
#define ERROR
Definition: elog.h:43
RelFileNodeBackend smgr_rnode
Definition: smgr.h:42
int errcode_for_file_access(void)
Definition: elog.c:631
#define ereport(elevel, rest)
Definition: elog.h:141
static void _fdvec_resize(SMgrRelation reln, ForkNumber forknum, int nseg)
Definition: md.c:1025
Definition: md.c:82
void FileClose(File file)
Definition: fd.c:1835
#define Assert(condition)
Definition: c.h:738
int md_num_open_segs[MAX_FORKNUM+1]
Definition: smgr.h:70
int errmsg(const char *fmt,...)
Definition: elog.c:822
#define relpath(rnode, forknum)
Definition: relpath.h:87
struct _MdfdVec * md_seg_fds[MAX_FORKNUM+1]
Definition: smgr.h:71
int FileTruncate(File file, off_t offset, uint32 wait_event_info)
Definition: fd.c:2174
File mdfd_vfd
Definition: md.c:84
static void register_dirty_segment(SMgrRelation reln, ForkNumber forknum, MdfdVec *seg)
Definition: md.c:918

◆ mdunlink()

void mdunlink ( RelFileNodeBackend  rnode,
ForkNumber  forkNum,
bool  isRedo 
)

Definition at line 278 of file md.c.

References InvalidForkNumber, MAX_FORKNUM, and mdunlinkfork().

279 {
280  /* Now do the per-fork work */
281  if (forkNum == InvalidForkNumber)
282  {
283  for (forkNum = 0; forkNum <= MAX_FORKNUM; forkNum++)
284  mdunlinkfork(rnode, forkNum, isRedo);
285  }
286  else
287  mdunlinkfork(rnode, forkNum, isRedo);
288 }
static void mdunlinkfork(RelFileNodeBackend rnode, ForkNumber forkNum, bool isRedo)
Definition: md.c:291
#define MAX_FORKNUM
Definition: relpath.h:55

◆ mdunlinkfiletag()

int mdunlinkfiletag ( const FileTag ftag,
char *  path 
)

Definition at line 1324 of file md.c.

References MAIN_FORKNUM, MAXPGPATH, pfree(), relpathperm, FileTag::rnode, and strlcpy().

1325 {
1326  char *p;
1327 
1328  /* Compute the path. */
1329  p = relpathperm(ftag->rnode, MAIN_FORKNUM);
1330  strlcpy(path, p, MAXPGPATH);
1331  pfree(p);
1332 
1333  /* Try to unlink the file. */
1334  return unlink(path);
1335 }
#define relpathperm(rnode, forknum)
Definition: relpath.h:83
RelFileNode rnode
Definition: sync.h:49
void pfree(void *pointer)
Definition: mcxt.c:1056
#define MAXPGPATH
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45

◆ mdunlinkfork()

static void mdunlinkfork ( RelFileNodeBackend  rnode,
ForkNumber  forkNum,
bool  isRedo 
)
static

Definition at line 291 of file md.c.

References CloseTransientFile(), ereport, errcode_for_file_access(), errmsg(), fd(), ftruncate, MAIN_FORKNUM, OpenTransientFile(), palloc(), pfree(), PG_BINARY, register_forget_request(), register_unlink_segment(), RelFileNodeBackendIsTemp, relpath, sprintf, and WARNING.

Referenced by mdunlink().

292 {
293  char *path;
294  int ret;
295 
296  path = relpath(rnode, forkNum);
297 
298  /*
299  * Delete or truncate the first segment.
300  */
301  if (isRedo || forkNum != MAIN_FORKNUM || RelFileNodeBackendIsTemp(rnode))
302  {
303  /* First, forget any pending sync requests for the first segment */
304  if (!RelFileNodeBackendIsTemp(rnode))
305  register_forget_request(rnode, forkNum, 0 /* first seg */ );
306 
307  /* Next unlink the file */
308  ret = unlink(path);
309  if (ret < 0 && errno != ENOENT)
312  errmsg("could not remove file \"%s\": %m", path)));
313  }
314  else
315  {
316  /* truncate(2) would be easier here, but Windows hasn't got it */
317  int fd;
318 
319  fd = OpenTransientFile(path, O_RDWR | PG_BINARY);
320  if (fd >= 0)
321  {
322  int save_errno;
323 
324  ret = ftruncate(fd, 0);
325  save_errno = errno;
326  CloseTransientFile(fd);
327  errno = save_errno;
328  }
329  else
330  ret = -1;
331  if (ret < 0 && errno != ENOENT)
334  errmsg("could not truncate file \"%s\": %m", path)));
335 
336  /* Register request to unlink first segment later */
337  register_unlink_segment(rnode, forkNum, 0 /* first seg */ );
338  }
339 
340  /*
341  * Delete any additional segments.
342  */
343  if (ret >= 0)
344  {
345  char *segpath = (char *) palloc(strlen(path) + 12);
346  BlockNumber segno;
347 
348  /*
349  * Note that because we loop until getting ENOENT, we will correctly
350  * remove all inactive segments as well as active ones.
351  */
352  for (segno = 1;; segno++)
353  {
354  /*
355  * Forget any pending sync requests for this segment before we try
356  * to unlink.
357  */
358  if (!RelFileNodeBackendIsTemp(rnode))
359  register_forget_request(rnode, forkNum, segno);
360 
361  sprintf(segpath, "%s.%u", path, segno);
362  if (unlink(segpath) < 0)
363  {
364  /* ENOENT is expected after the last segment... */
365  if (errno != ENOENT)
368  errmsg("could not remove file \"%s\": %m", segpath)));
369  break;
370  }
371  }
372  pfree(segpath);
373  }
374 
375  pfree(path);
376 }
#define RelFileNodeBackendIsTemp(rnode)
Definition: relfilenode.h:78
uint32 BlockNumber
Definition: block.h:31
static int fd(const char *x, int i)
Definition: preproc-init.c:105
#define PG_BINARY
Definition: c.h:1230
static void register_forget_request(RelFileNodeBackend rnode, ForkNumber forknum, BlockNumber segno)
Definition: md.c:961
#define sprintf
Definition: port.h:194
void pfree(void *pointer)
Definition: mcxt.c:1056
int OpenTransientFile(const char *fileName, int fileFlags)
Definition: fd.c:2381
static void register_unlink_segment(RelFileNodeBackend rnode, ForkNumber forknum, BlockNumber segno)
Definition: md.c:944
int errcode_for_file_access(void)
Definition: elog.c:631
#define ereport(elevel, rest)
Definition: elog.h:141
int CloseTransientFile(int fd)
Definition: fd.c:2558
#define WARNING
Definition: elog.h:40
void * palloc(Size size)
Definition: mcxt.c:949
int errmsg(const char *fmt,...)
Definition: elog.c:822
#define relpath(rnode, forknum)
Definition: relpath.h:87
#define ftruncate(a, b)
Definition: win32_port.h:59

◆ mdwrite()

void mdwrite ( SMgrRelation  reln,
ForkNumber  forknum,
BlockNumber  blocknum,
char *  buffer,
bool  skipFsync 
)

Definition at line 666 of file md.c.

References _mdfd_getseg(), Assert, RelFileNodeBackend::backend, RelFileNode::dbNode, ereport, errcode(), errcode_for_file_access(), errhint(), errmsg(), ERROR, EXTENSION_CREATE_RECOVERY, EXTENSION_FAIL, FilePathName(), FileWrite(), _MdfdVec::mdfd_vfd, mdnblocks(), RelFileNodeBackend::node, register_dirty_segment(), RelFileNode::relNode, SMgrRelationData::smgr_rnode, SmgrIsTemp, RelFileNode::spcNode, and WAIT_EVENT_DATA_FILE_WRITE.

668 {
669  off_t seekpos;
670  int nbytes;
671  MdfdVec *v;
672 
673  /* This assert is too expensive to have on normally ... */
674 #ifdef CHECK_WRITE_VS_EXTEND
675  Assert(blocknum < mdnblocks(reln, forknum));
676 #endif
677 
678  TRACE_POSTGRESQL_SMGR_MD_WRITE_START(forknum, blocknum,
679  reln->smgr_rnode.node.spcNode,
680  reln->smgr_rnode.node.dbNode,
681  reln->smgr_rnode.node.relNode,
682  reln->smgr_rnode.backend);
683 
684  v = _mdfd_getseg(reln, forknum, blocknum, skipFsync,
686 
687  seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) RELSEG_SIZE));
688 
689  Assert(seekpos < (off_t) BLCKSZ * RELSEG_SIZE);
690 
691  nbytes = FileWrite(v->mdfd_vfd, buffer, BLCKSZ, seekpos, WAIT_EVENT_DATA_FILE_WRITE);
692 
693  TRACE_POSTGRESQL_SMGR_MD_WRITE_DONE(forknum, blocknum,
694  reln->smgr_rnode.node.spcNode,
695  reln->smgr_rnode.node.dbNode,
696  reln->smgr_rnode.node.relNode,
697  reln->smgr_rnode.backend,
698  nbytes,
699  BLCKSZ);
700 
701  if (nbytes != BLCKSZ)
702  {
703  if (nbytes < 0)
704  ereport(ERROR,
706  errmsg("could not write block %u in file \"%s\": %m",
707  blocknum, FilePathName(v->mdfd_vfd))));
708  /* short write: complain appropriately */
709  ereport(ERROR,
710  (errcode(ERRCODE_DISK_FULL),
711  errmsg("could not write block %u in file \"%s\": wrote only %d of %d bytes",
712  blocknum,
714  nbytes, BLCKSZ),
715  errhint("Check free disk space.")));
716  }
717 
718  if (!skipFsync && !SmgrIsTemp(reln))
719  register_dirty_segment(reln, forknum, v);
720 }
static MdfdVec * _mdfd_getseg(SMgrRelation reln, ForkNumber forkno, BlockNumber blkno, bool skipFsync, int behavior)
Definition: md.c:1131
int errhint(const char *fmt,...)
Definition: elog.c:1069
BlockNumber mdnblocks(SMgrRelation reln, ForkNumber forknum)
Definition: md.c:731
int errcode(int sqlerrcode)
Definition: elog.c:608
uint32 BlockNumber
Definition: block.h:31
char * FilePathName(File file)
Definition: fd.c:2209
#define EXTENSION_FAIL
Definition: md.c:104
#define SmgrIsTemp(smgr)
Definition: smgr.h:79
#define ERROR
Definition: elog.h:43
RelFileNodeBackend smgr_rnode
Definition: smgr.h:42
int errcode_for_file_access(void)
Definition: elog.c:631
#define EXTENSION_CREATE_RECOVERY
Definition: md.c:110
#define ereport(elevel, rest)
Definition: elog.h:141
int FileWrite(File file, char *buffer, int amount, off_t offset, uint32 wait_event_info)
Definition: fd.c:2038
Definition: md.c:82
RelFileNode node
Definition: relfilenode.h:74
#define Assert(condition)
Definition: c.h:738
BackendId backend
Definition: relfilenode.h:75
int errmsg(const char *fmt,...)
Definition: elog.c:822
File mdfd_vfd
Definition: md.c:84
static void register_dirty_segment(SMgrRelation reln, ForkNumber forknum, MdfdVec *seg)
Definition: md.c:918

◆ mdwriteback()

void mdwriteback ( SMgrRelation  reln,
ForkNumber  forknum,
BlockNumber  blocknum,
BlockNumber  nblocks 
)

Definition at line 552 of file md.c.

References _mdfd_getseg(), Assert, EXTENSION_RETURN_NULL, FileWriteback(), _MdfdVec::mdfd_vfd, and WAIT_EVENT_DATA_FILE_FLUSH.

554 {
555  /*
556  * Issue flush requests in as few requests as possible; have to split at
557  * segment boundaries though, since those are actually separate files.
558  */
559  while (nblocks > 0)
560  {
561  BlockNumber nflush = nblocks;
562  off_t seekpos;
563  MdfdVec *v;
564  int segnum_start,
565  segnum_end;
566 
567  v = _mdfd_getseg(reln, forknum, blocknum, true /* not used */ ,
569 
570  /*
571  * We might be flushing buffers of already removed relations, that's
572  * ok, just ignore that case.
573  */
574  if (!v)
575  return;
576 
577  /* compute offset inside the current segment */
578  segnum_start = blocknum / RELSEG_SIZE;
579 
580  /* compute number of desired writes within the current segment */
581  segnum_end = (blocknum + nblocks - 1) / RELSEG_SIZE;
582  if (segnum_start != segnum_end)
583  nflush = RELSEG_SIZE - (blocknum % ((BlockNumber) RELSEG_SIZE));
584 
585  Assert(nflush >= 1);
586  Assert(nflush <= nblocks);
587 
588  seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) RELSEG_SIZE));
589 
590  FileWriteback(v->mdfd_vfd, seekpos, (off_t) BLCKSZ * nflush, WAIT_EVENT_DATA_FILE_FLUSH);
591 
592  nblocks -= nflush;
593  blocknum += nflush;
594  }
595 }
static MdfdVec * _mdfd_getseg(SMgrRelation reln, ForkNumber forkno, BlockNumber blkno, bool skipFsync, int behavior)
Definition: md.c:1131
uint32 BlockNumber
Definition: block.h:31
#define EXTENSION_RETURN_NULL
Definition: md.c:106
Definition: md.c:82
#define Assert(condition)
Definition: c.h:738
void FileWriteback(File file, off_t offset, off_t nbytes, uint32 wait_event_info)
Definition: fd.c:1959
File mdfd_vfd
Definition: md.c:84

◆ register_dirty_segment()

static void register_dirty_segment ( SMgrRelation  reln,
ForkNumber  forknum,
MdfdVec seg 
)
static

Definition at line 918 of file md.c.

References Assert, data_sync_elevel(), DEBUG1, ereport, errcode_for_file_access(), errmsg(), ERROR, FilePathName(), FileSync(), INIT_MD_FILETAG, _MdfdVec::mdfd_segno, _MdfdVec::mdfd_vfd, RelFileNodeBackend::node, RegisterSyncRequest(), SMgrRelationData::smgr_rnode, SmgrIsTemp, SYNC_REQUEST, and WAIT_EVENT_DATA_FILE_SYNC.

Referenced by mdextend(), mdtruncate(), and mdwrite().

919 {
920  FileTag tag;
921 
922  INIT_MD_FILETAG(tag, reln->smgr_rnode.node, forknum, seg->mdfd_segno);
923 
924  /* Temp relations should never be fsync'd */
925  Assert(!SmgrIsTemp(reln));
926 
927  if (!RegisterSyncRequest(&tag, SYNC_REQUEST, false /* retryOnError */ ))
928  {
929  ereport(DEBUG1,
930  (errmsg("could not forward fsync request because request queue is full")));
931 
935  errmsg("could not fsync file \"%s\": %m",
936  FilePathName(seg->mdfd_vfd))));
937  }
938 }
#define DEBUG1
Definition: elog.h:25
BlockNumber mdfd_segno
Definition: md.c:85
#define INIT_MD_FILETAG(a, xx_rnode, xx_forknum, xx_segno)
Definition: md.c:92
char * FilePathName(File file)
Definition: fd.c:2209
#define SmgrIsTemp(smgr)
Definition: smgr.h:79
#define ERROR
Definition: elog.h:43
RelFileNodeBackend smgr_rnode
Definition: smgr.h:42
int FileSync(File file, uint32 wait_event_info)
Definition: fd.c:2136
int errcode_for_file_access(void)
Definition: elog.c:631
#define ereport(elevel, rest)
Definition: elog.h:141
int data_sync_elevel(int elevel)
Definition: fd.c:3608
bool RegisterSyncRequest(const FileTag *ftag, SyncRequestType type, bool retryOnError)
Definition: sync.c:530
RelFileNode node
Definition: relfilenode.h:74
#define Assert(condition)
Definition: c.h:738
int errmsg(const char *fmt,...)
Definition: elog.c:822
File mdfd_vfd
Definition: md.c:84
Definition: sync.h:45

◆ register_forget_request()

static void register_forget_request ( RelFileNodeBackend  rnode,
ForkNumber  forknum,
BlockNumber  segno 
)
static

Definition at line 961 of file md.c.

References INIT_MD_FILETAG, RelFileNodeBackend::node, RegisterSyncRequest(), and SYNC_FORGET_REQUEST.

Referenced by mdunlinkfork().

963 {
964  FileTag tag;
965 
966  INIT_MD_FILETAG(tag, rnode.node, forknum, segno);
967 
968  RegisterSyncRequest(&tag, SYNC_FORGET_REQUEST, true /* retryOnError */ );
969 }
#define INIT_MD_FILETAG(a, xx_rnode, xx_forknum, xx_segno)
Definition: md.c:92
bool RegisterSyncRequest(const FileTag *ftag, SyncRequestType type, bool retryOnError)
Definition: sync.c:530
RelFileNode node
Definition: relfilenode.h:74
Definition: sync.h:45

◆ register_unlink_segment()

static void register_unlink_segment ( RelFileNodeBackend  rnode,
ForkNumber  forknum,
BlockNumber  segno 
)
static

Definition at line 944 of file md.c.

References Assert, INIT_MD_FILETAG, RelFileNodeBackend::node, RegisterSyncRequest(), RelFileNodeBackendIsTemp, and SYNC_UNLINK_REQUEST.

Referenced by mdunlinkfork().

946 {
947  FileTag tag;
948 
949  INIT_MD_FILETAG(tag, rnode.node, forknum, segno);
950 
951  /* Should never be used with temp relations */
953 
954  RegisterSyncRequest(&tag, SYNC_UNLINK_REQUEST, true /* retryOnError */ );
955 }
#define RelFileNodeBackendIsTemp(rnode)
Definition: relfilenode.h:78
#define INIT_MD_FILETAG(a, xx_rnode, xx_forknum, xx_segno)
Definition: md.c:92
bool RegisterSyncRequest(const FileTag *ftag, SyncRequestType type, bool retryOnError)
Definition: sync.c:530
RelFileNode node
Definition: relfilenode.h:74
#define Assert(condition)
Definition: c.h:738
Definition: sync.h:45

Variable Documentation

◆ MdCxt

MemoryContext MdCxt
static

Definition at line 88 of file md.c.