/* * _mdfd_getseg() -- Find the segment of the relation holding the * specified block. ereport's on failure. * (Optionally, can return NULL instead of ereport for ENOENT.) */ static MdfdVec * _mdfd_getseg(SMgrRelation reln, BlockNumber blkno, bool allowNotFound) { MdfdVec *v = mdopen(reln, allowNotFound); #ifndef LET_OS_MANAGE_FILESIZE BlockNumber segstogo; BlockNumber nextsegno; if (!v) return NULL; /* only possible if allowNotFound */ for (segstogo = blkno / ((BlockNumber) RELSEG_SIZE), nextsegno = 1; segstogo > 0; nextsegno++, segstogo--) { if (v->mdfd_chain == NULL) { /* * We will create the next segment only if the target block is * within it. This prevents Sorcerer's Apprentice syndrome if * a bug at higher levels causes us to be handed a * ridiculously large blkno --- otherwise we could create many * thousands of empty segment files before reaching the * "target" block. We should never need to create more than * one new segment per call, so this restriction seems * reasonable. * * BUT: when doing WAL recovery, disable this logic and create * segments unconditionally. In this case it seems better * to assume the given blkno is good (it presumably came from * a CRC-checked WAL record); furthermore this lets us cope * in the case where we are replaying WAL data that has a write * into a high-numbered segment of a relation that was later * deleted. We want to go ahead and create the segments so * we can finish out the replay. */ v->mdfd_chain = _mdfd_openseg(reln, nextsegno, (segstogo == 1 || InRecovery) ? O_CREAT : 0); if (v->mdfd_chain == NULL) { if (allowNotFound && errno == ENOENT) return NULL; ereport(ERROR, (errcode_for_file_access(), errmsg("could not open segment %u of relation %u/%u/%u (target block %u): %m", nextsegno, reln->smgr_rnode.spcNode, reln->smgr_rnode.dbNode, reln->smgr_rnode.relNode, blkno))); } } v = v->mdfd_chain; } #endif return v; }
/* * mdnblocks() -- Get the number of blocks stored in a relation. * * Important side effect: all active segments of the relation are opened * and added to the mdfd_chain list. If this routine has not been * called, then only segments up to the last one actually touched * are present in the chain. * * Returns # of blocks, or InvalidBlockNumber on error. */ BlockNumber mdnblocks(SMgrRelation reln) { MdfdVec *v = mdopen(reln, false); #ifndef LET_OS_MANAGE_FILESIZE BlockNumber nblocks; BlockNumber segno = 0; /* * Skip through any segments that aren't the last one, to avoid redundant * seeks on them. We have previously verified that these segments are * exactly RELSEG_SIZE long, and it's useless to recheck that each time. * * NOTE: this assumption could only be wrong if another backend has * truncated the relation. We rely on higher code levels to handle that * scenario by closing and re-opening the md fd, which is handled via * relcache flush. (Since the bgwriter doesn't participate in relcache * flush, it could have segment chain entries for inactive segments; * that's OK because the bgwriter never needs to compute relation size.) */ while (v->mdfd_chain != NULL) { segno++; v = v->mdfd_chain; } for (;;) { nblocks = _mdnblocks(v->mdfd_vfd, BLCKSZ); if (nblocks > ((BlockNumber) RELSEG_SIZE)) elog(FATAL, "segment too big"); if (nblocks < ((BlockNumber) RELSEG_SIZE)) return (segno * ((BlockNumber) RELSEG_SIZE)) + nblocks; /* * If segment is exactly RELSEG_SIZE, advance to next one. */ segno++; if (v->mdfd_chain == NULL) { /* * Because we pass O_CREAT, we will create the next segment (with * zero length) immediately, if the last segment is of length * RELSEG_SIZE. While perhaps not strictly necessary, this keeps * the logic simple. */ v->mdfd_chain = _mdfd_openseg(reln, segno, O_CREAT); if (v->mdfd_chain == NULL) return InvalidBlockNumber; /* failed? */ } v = v->mdfd_chain; } #else return _mdnblocks(v->mdfd_vfd, BLCKSZ); #endif }
/* * mdnblocks() -- Get the number of blocks stored in a relation. * * Important side effect: all active segments of the relation are opened * and added to the mdfd_chain list. If this routine has not been * called, then only segments up to the last one actually touched * are present in the chain. */ BlockNumber mdnblocks(SMgrRelation reln, ForkNumber forknum) { MdfdVec *v = mdopen(reln, forknum, EXTENSION_FAIL); BlockNumber nblocks; BlockNumber segno = 0; /* * Skip through any segments that aren't the last one, to avoid redundant * seeks on them. We have previously verified that these segments are * exactly RELSEG_SIZE long, and it's useless to recheck that each time. * * NOTE: this assumption could only be wrong if another backend has * truncated the relation. We rely on higher code levels to handle that * scenario by closing and re-opening the md fd, which is handled via * relcache flush. (Since the bgwriter doesn't participate in relcache * flush, it could have segment chain entries for inactive segments; * that's OK because the bgwriter never needs to compute relation size.) */ while (v->mdfd_chain != NULL) { segno++; v = v->mdfd_chain; } for (;;) { nblocks = _mdnblocks(reln, forknum, v); if (nblocks > ((BlockNumber) RELSEG_SIZE)) elog(FATAL, "segment too big"); if (nblocks < ((BlockNumber) RELSEG_SIZE)) return (segno * ((BlockNumber) RELSEG_SIZE)) + nblocks; /* * If segment is exactly RELSEG_SIZE, advance to next one. */ segno++; if (v->mdfd_chain == NULL) { /* * Because we pass O_CREAT, we will create the next segment (with * zero length) immediately, if the last segment is of length * RELSEG_SIZE. While perhaps not strictly necessary, this keeps * the logic simple. */ v->mdfd_chain = _mdfd_openseg(reln, forknum, segno, O_CREAT); if (v->mdfd_chain == NULL) ereport(ERROR, (errcode_for_file_access(), errmsg("could not open file \"%s\": %m", _mdfd_segpath(reln, forknum, segno)))); } v = v->mdfd_chain; } }
/* * mdnblocks() -- Get the number of blocks stored in a relation. * * Important side effect: all segments of the relation are opened * and added to the mdfd_chain list. If this routine has not been * called, then only segments up to the last one actually touched * are present in the chain... * * Returns # of blocks, or InvalidBlockNumber on error. */ BlockNumber mdnblocks(SMgrRelation reln) { MdfdVec *v = mdopen(reln, false); #ifndef LET_OS_MANAGE_FILESIZE BlockNumber nblocks; BlockNumber segno = 0; /* * Skip through any segments that aren't the last one, to avoid * redundant seeks on them. We have previously verified that these * segments are exactly RELSEG_SIZE long, and it's useless to recheck * that each time. (NOTE: this assumption could only be wrong if * another backend has truncated the relation. We rely on higher code * levels to handle that scenario by closing and re-opening the md * fd.) */ while (v->mdfd_chain != NULL) { segno++; v = v->mdfd_chain; } for (;;) { nblocks = _mdnblocks(v->mdfd_vfd, BLCKSZ); if (nblocks > ((BlockNumber) RELSEG_SIZE)) elog(FATAL, "segment too big"); if (nblocks < ((BlockNumber) RELSEG_SIZE)) return (segno * ((BlockNumber) RELSEG_SIZE)) + nblocks; /* * If segment is exactly RELSEG_SIZE, advance to next one. */ segno++; if (v->mdfd_chain == NULL) { /* * Because we pass O_CREAT, we will create the next segment * (with zero length) immediately, if the last segment is of * length REL_SEGSIZE. This is unnecessary but harmless, and * testing for the case would take more cycles than it seems * worth. */ v->mdfd_chain = _mdfd_openseg(reln, segno, O_CREAT); if (v->mdfd_chain == NULL) return InvalidBlockNumber; /* failed? */ } v = v->mdfd_chain; } #else return _mdnblocks(v->mdfd_vfd, BLCKSZ); #endif }