/* * mdunlink() -- Unlink a relation. * * Note that we're passed a RelFileNode --- by the time this is called, * there won't be an SMgrRelation hashtable entry anymore. * * If isRedo is true, it's okay for the relation to be already gone. */ bool mdunlink(RelFileNode rnode, bool isRedo) { bool status = true; int save_errno = 0; char *path; /* * We have to clean out any pending fsync requests for the doomed relation, * else the next mdsync() will fail. */ ForgetRelationFsyncRequests(rnode); path = relpath(rnode); /* Delete the first segment, or only segment if not doing segmenting */ if (unlink(path) < 0) { if (!isRedo || errno != ENOENT) { status = false; save_errno = errno; } } #ifndef LET_OS_MANAGE_FILESIZE /* Delete the additional segments, if any */ if (status) { char *segpath = (char *) palloc(strlen(path) + 12); BlockNumber segno; /* * Note that because we loop until getting ENOENT, we will * correctly remove all inactive segments as well as active ones. */ for (segno = 1;; segno++) { sprintf(segpath, "%s.%u", path, segno); if (unlink(segpath) < 0) { /* ENOENT is expected after the last segment... */ if (errno != ENOENT) { status = false; save_errno = errno; } break; } } pfree(segpath); } #endif pfree(path); errno = save_errno; return status; }
/* * mdunlink() -- Unlink a relation. * * Note that we're passed a RelFileNode --- by the time this is called, * there won't be an SMgrRelation hashtable entry anymore. * * Actually, we don't unlink the first segment file of the relation, but * just truncate it to zero length, and record a request to unlink it after * the next checkpoint. Additional segments can be unlinked immediately, * however. Leaving the empty file in place prevents that relfilenode * number from being reused. The scenario this protects us from is: * 1. We delete a relation (and commit, and actually remove its file). * 2. We create a new relation, which by chance gets the same relfilenode as * the just-deleted one (OIDs must've wrapped around for that to happen). * 3. We crash before another checkpoint occurs. * During replay, we would delete the file and then recreate it, which is fine * if the contents of the file were repopulated by subsequent WAL entries. * But if we didn't WAL-log insertions, but instead relied on fsyncing the * file after populating it (as for instance CLUSTER and CREATE INDEX do), * the contents of the file would be lost forever. By leaving the empty file * until after the next checkpoint, we prevent reassignment of the relfilenode * number until it's safe, because relfilenode assignment skips over any * existing file. * * If isRedo is true, it's okay for the relation to be already gone. * Also, we should remove the file immediately instead of queuing a request * for later, since during redo there's no possibility of creating a * conflicting relation. * * Note: any failure should be reported as WARNING not ERROR, because * we are usually not in a transaction anymore when this is called. */ void mdunlink(RelFileNode rnode, ForkNumber forkNum, bool isRedo) { char *path; int ret; /* * We have to clean out any pending fsync requests for the doomed * relation, else the next mdsync() will fail. */ ForgetRelationFsyncRequests(rnode, forkNum); path = relpath(rnode, forkNum); /* * Delete or truncate the first segment. */ if (isRedo || forkNum != MAIN_FORKNUM) { ret = unlink(path); if (ret < 0) { if (!isRedo || errno != ENOENT) ereport(WARNING, (errcode_for_file_access(), errmsg("could not remove file \"%s\": %m", path))); } } else { /* truncate(2) would be easier here, but Windows hasn't got it */ int fd; fd = BasicOpenFile(path, O_RDWR | PG_BINARY, 0); if (fd >= 0) { int save_errno; ret = ftruncate(fd, 0); save_errno = errno; close(fd); errno = save_errno; } else ret = -1; if (ret < 0 && errno != ENOENT) ereport(WARNING, (errcode_for_file_access(), errmsg("could not truncate file \"%s\": %m", path))); } /* * Delete any additional segments. */ if (ret >= 0) { char *segpath = (char *) palloc(strlen(path) + 12); BlockNumber segno; /* * Note that because we loop until getting ENOENT, we will correctly * remove all inactive segments as well as active ones. */ for (segno = 1;; segno++) { sprintf(segpath, "%s.%u", path, segno); if (unlink(segpath) < 0) { /* ENOENT is expected after the last segment... */ if (errno != ENOENT) ereport(WARNING, (errcode_for_file_access(), errmsg("could not remove file \"%s\": %m", segpath))); break; } } pfree(segpath); } pfree(path); /* Register request to unlink first segment later */ if (!isRedo && forkNum == MAIN_FORKNUM) register_unlink(rnode); }