*** a/src/backend/storage/buffer/bufmgr.c --- b/src/backend/storage/buffer/bufmgr.c *************** *** 1834,1840 **** BufferGetTag(Buffer buffer, RelFileNode *rnode, ForkNumber *forknum, * written.) * * If the caller has an smgr reference for the buffer's relation, pass it ! * as the second parameter. If not, pass NULL. */ static void FlushBuffer(volatile BufferDesc *buf, SMgrRelation reln) --- 1834,1843 ---- * written.) * * If the caller has an smgr reference for the buffer's relation, pass it ! * as the second parameter. If not, pass NULL. In the latter case, the ! * relation will be marked as "transient" so that the corresponding ! * kernel-level file descriptors are closed when the current transaction ends, ! * if any. */ static void FlushBuffer(volatile BufferDesc *buf, SMgrRelation reln) *************** *** 1856,1864 **** FlushBuffer(volatile BufferDesc *buf, SMgrRelation reln) errcontext.previous = error_context_stack; error_context_stack = &errcontext; ! /* Find smgr relation for buffer */ if (reln == NULL) reln = smgropen(buf->tag.rnode, InvalidBackendId); TRACE_POSTGRESQL_BUFFER_FLUSH_START(buf->tag.forkNum, buf->tag.blockNum, --- 1859,1870 ---- errcontext.previous = error_context_stack; error_context_stack = &errcontext; ! /* Find smgr relation for buffer, and mark it as transient */ if (reln == NULL) + { reln = smgropen(buf->tag.rnode, InvalidBackendId); + smgrsettransient(reln); + } TRACE_POSTGRESQL_BUFFER_FLUSH_START(buf->tag.forkNum, buf->tag.blockNum, *** a/src/backend/storage/file/fd.c --- b/src/backend/storage/file/fd.c *************** *** 125,136 **** static int max_safe_fds = 32; /* default if not changed */ /* these are the assigned bits in fdstate below: */ #define FD_TEMPORARY (1 << 0) /* T = delete when closed */ #define FD_XACT_TEMPORARY (1 << 1) /* T = delete at eoXact */ /* ! * Flag to tell whether it's worth scanning VfdCache looking for temp files to ! * close */ static bool have_xact_temporary_files = false; typedef struct vfd { --- 125,140 ---- /* these are the assigned bits in fdstate below: */ #define FD_TEMPORARY (1 << 0) /* T = delete when closed */ #define FD_XACT_TEMPORARY (1 << 1) /* T = delete at eoXact */ + #define FD_XACT_TRANSIENT (1 << 2) /* T = close (not delete) at aoXact, + * but keep VFD */ /* ! * Global status flags */ + /* are there FD_XACT_TEMPORARY files? */ static bool have_xact_temporary_files = false; + /* are there FD_XACT_TRANSIENT files? */ + static bool have_xact_transient_files = false; typedef struct vfd { *************** *** 1027,1032 **** OpenTemporaryFileInTablespace(Oid tblspcOid, bool rejectError) --- 1031,1075 ---- } /* + * Set the transient flag on a file + * + * This flag tells CleanupTempFiles to close the kernel-level file descriptor + * (but not the VFD itself) at end of transaction. + */ + void + FileSetTransient(File file) + { + Vfd *vfdP; + + Assert(FileIsValid(file)); + + vfdP = &VfdCache[file]; + vfdP->fdstate |= FD_XACT_TRANSIENT; + + have_xact_transient_files = true; + } + + /* + * Close a file at the kernel level, but keep the VFD open + */ + static void + FileKernelClose(File file) + { + Vfd *vfdP; + + Assert(FileIsValid(file)); + + vfdP = &VfdCache[file]; + + if (!FileIsNotOpen(file)) + { + if (close(vfdP->fd)) + elog(ERROR, "could not close file \"%s\": %m", vfdP->fileName); + vfdP->fd = VFD_CLOSED; + } + } + + /* * close a file when done with it */ void *************** *** 1819,1833 **** CleanupTempFiles(bool isProcExit) * Careful here: at proc_exit we need extra cleanup, not just * xact_temporary files. */ ! if (isProcExit || have_xact_temporary_files) { Assert(FileIsNotOpen(0)); /* Make sure ring not corrupted */ for (i = 1; i < SizeVfdCache; i++) { unsigned short fdstate = VfdCache[i].fdstate; ! if ((fdstate & FD_TEMPORARY) && VfdCache[i].fileName != NULL) { /* * If we're in the process of exiting a backend process, close * all temporary files. Otherwise, only close temporary files --- 1862,1878 ---- * Careful here: at proc_exit we need extra cleanup, not just * xact_temporary files. */ ! if (isProcExit || have_xact_temporary_files || have_xact_transient_files) { Assert(FileIsNotOpen(0)); /* Make sure ring not corrupted */ for (i = 1; i < SizeVfdCache; i++) { unsigned short fdstate = VfdCache[i].fdstate; ! if (VfdCache[i].fileName != NULL) { + if (fdstate & FD_TEMPORARY) + { /* * If we're in the process of exiting a backend process, close * all temporary files. Otherwise, only close temporary files *************** *** 1844,1853 **** CleanupTempFiles(bool isProcExit) --- 1889,1902 ---- VfdCache[i].fileName); FileClose(i); } + } + else if (fdstate & FD_XACT_TRANSIENT) + FileKernelClose(i); } } have_xact_temporary_files = false; + have_xact_transient_files = false; } /* Clean up "allocated" stdio files and dirs. */ *** a/src/backend/storage/smgr/md.c --- b/src/backend/storage/smgr/md.c *************** *** 288,293 **** mdcreate(SMgrRelation reln, ForkNumber forkNum, bool isRedo) --- 288,296 ---- pfree(path); + if (reln->smgr_transient) + FileSetTransient(fd); + reln->md_fd[forkNum] = _fdvec_alloc(); reln->md_fd[forkNum]->mdfd_vfd = fd; *************** *** 542,547 **** mdopen(SMgrRelation reln, ForkNumber forknum, ExtensionBehavior behavior) --- 545,553 ---- pfree(path); + if (reln->smgr_transient) + FileSetTransient(fd); + reln->md_fd[forknum] = mdfd = _fdvec_alloc(); mdfd->mdfd_vfd = fd; *************** *** 1556,1561 **** _mdfd_openseg(SMgrRelation reln, ForkNumber forknum, BlockNumber segno, --- 1562,1570 ---- if (fd < 0) return NULL; + if (reln->smgr_transient) + FileSetTransient(fd); + /* allocate an mdfdvec entry for it */ v = _fdvec_alloc(); *** a/src/backend/storage/smgr/smgr.c --- b/src/backend/storage/smgr/smgr.c *************** *** 165,170 **** smgropen(RelFileNode rnode, BackendId backend) --- 165,171 ---- reln->smgr_targblock = InvalidBlockNumber; reln->smgr_fsm_nblocks = InvalidBlockNumber; reln->smgr_vm_nblocks = InvalidBlockNumber; + reln->smgr_transient = false; reln->smgr_which = 0; /* we only have md.c at present */ /* mark it not open */ *************** *** 176,181 **** smgropen(RelFileNode rnode, BackendId backend) --- 177,195 ---- } /* + * smgrsettransient -- mark an SMgrRelation object as transaction-bound + * + * The main effect of this is that all opened files are marked to be + * kernel-level closed (but not necessarily VFD-closed) when the current + * transaction ends. + */ + void + smgrsettransient(SMgrRelation reln) + { + reln->smgr_transient = true; + } + + /* * smgrsetowner() -- Establish a long-lived reference to an SMgrRelation object * * There can be only one owner at a time; this is sufficient since currently *** a/src/include/storage/fd.h --- b/src/include/storage/fd.h *************** *** 61,66 **** extern int max_files_per_process; --- 61,67 ---- /* Operations on virtual Files --- equivalent to Unix kernel file ops */ extern File PathNameOpenFile(FileName fileName, int fileFlags, int fileMode); extern File OpenTemporaryFile(bool interXact); + extern void FileSetTransient(File file); extern void FileClose(File file); extern int FilePrefetch(File file, off_t offset, int amount); extern int FileRead(File file, char *buffer, int amount); *** a/src/include/storage/smgr.h --- b/src/include/storage/smgr.h *************** *** 62,67 **** typedef struct SMgrRelationData --- 62,68 ---- * submodules. Do not touch them from elsewhere. */ int smgr_which; /* storage manager selector */ + bool smgr_transient; /* T if files are to be closed at EOXact */ /* for md.c; NULL for forks that are not open */ struct _MdfdVec *md_fd[MAX_FORKNUM + 1]; *************** *** 74,79 **** typedef SMgrRelationData *SMgrRelation; --- 75,81 ---- extern void smgrinit(void); extern SMgrRelation smgropen(RelFileNode rnode, BackendId backend); + extern void smgrsettransient(SMgrRelation reln); extern bool smgrexists(SMgrRelation reln, ForkNumber forknum); extern void smgrsetowner(SMgrRelation *owner, SMgrRelation reln); extern void smgrclose(SMgrRelation reln);