3232 * (by opening multiple fd.c temporary files). This is an essential feature
3333 * for sorts and hashjoins on large amounts of data.
3434 *
35- * BufFile supports temporary files that can be made read-only and shared with
36- * other backends, as infrastructure for parallel execution. Such files need
37- * to be created as a member of a SharedFileSet that all participants are
38- * attached to.
35+ * BufFile supports temporary files that can be shared with other backends, as
36+ * infrastructure for parallel execution. Such files need to be created as a
37+ * member of a SharedFileSet that all participants are attached to.
38+ *
39+ * BufFile also supports temporary files that can be used by the single backend
40+ * when the corresponding files need to be survived across the transaction and
41+ * need to be opened and closed multiple times. Such files need to be created
42+ * as a member of a SharedFileSet.
3943 *-------------------------------------------------------------------------
4044 */
4145
@@ -277,7 +281,7 @@ BufFileCreateShared(SharedFileSet *fileset, const char *name)
277281 * backends and render it read-only.
278282 */
279283BufFile *
280- BufFileOpenShared (SharedFileSet * fileset , const char * name )
284+ BufFileOpenShared (SharedFileSet * fileset , const char * name , int mode )
281285{
282286 BufFile * file ;
283287 char segment_name [MAXPGPATH ];
@@ -301,7 +305,7 @@ BufFileOpenShared(SharedFileSet *fileset, const char *name)
301305 }
302306 /* Try to load a segment. */
303307 SharedSegmentName (segment_name , name , nfiles );
304- files [nfiles ] = SharedFileSetOpen (fileset , segment_name );
308+ files [nfiles ] = SharedFileSetOpen (fileset , segment_name , mode );
305309 if (files [nfiles ] <= 0 )
306310 break ;
307311 ++ nfiles ;
@@ -321,7 +325,7 @@ BufFileOpenShared(SharedFileSet *fileset, const char *name)
321325
322326 file = makeBufFileCommon (nfiles );
323327 file -> files = files ;
324- file -> readOnly = true; /* Can't write to files opened this way */
328+ file -> readOnly = ( mode == O_RDONLY ) ? true : false;
325329 file -> fileset = fileset ;
326330 file -> name = pstrdup (name );
327331
@@ -666,11 +670,21 @@ BufFileSeek(BufFile *file, int fileno, off_t offset, int whence)
666670 newFile = file -> curFile ;
667671 newOffset = (file -> curOffset + file -> pos ) + offset ;
668672 break ;
669- #ifdef NOT_USED
670673 case SEEK_END :
671- /* could be implemented, not needed currently */
674+
675+ /*
676+ * The file size of the last file gives us the end offset of that
677+ * file.
678+ */
679+ newFile = file -> numFiles - 1 ;
680+ newOffset = FileSize (file -> files [file -> numFiles - 1 ]);
681+ if (newOffset < 0 )
682+ ereport (ERROR ,
683+ (errcode_for_file_access (),
684+ errmsg ("could not determine size of temporary file \"%s\" from BufFile \"%s\": %m" ,
685+ FilePathName (file -> files [file -> numFiles - 1 ]),
686+ file -> name )));
672687 break ;
673- #endif
674688 default :
675689 elog (ERROR , "invalid whence: %d" , whence );
676690 return EOF ;
@@ -838,3 +852,98 @@ BufFileAppend(BufFile *target, BufFile *source)
838852
839853 return startBlock ;
840854}
855+
856+ /*
857+ * Truncate a BufFile created by BufFileCreateShared up to the given fileno and
858+ * the offset.
859+ */
860+ void
861+ BufFileTruncateShared (BufFile * file , int fileno , off_t offset )
862+ {
863+ int numFiles = file -> numFiles ;
864+ int newFile = fileno ;
865+ off_t newOffset = file -> curOffset ;
866+ char segment_name [MAXPGPATH ];
867+ int i ;
868+
869+ /*
870+ * Loop over all the files up to the given fileno and remove the files
871+ * that are greater than the fileno and truncate the given file up to the
872+ * offset. Note that we also remove the given fileno if the offset is 0
873+ * provided it is not the first file in which we truncate it.
874+ */
875+ for (i = file -> numFiles - 1 ; i >= fileno ; i -- )
876+ {
877+ if ((i != fileno || offset == 0 ) && i != 0 )
878+ {
879+ SharedSegmentName (segment_name , file -> name , i );
880+ FileClose (file -> files [i ]);
881+ if (!SharedFileSetDelete (file -> fileset , segment_name , true))
882+ ereport (ERROR ,
883+ (errcode_for_file_access (),
884+ errmsg ("could not delete shared fileset \"%s\": %m" ,
885+ segment_name )));
886+ numFiles -- ;
887+ newOffset = MAX_PHYSICAL_FILESIZE ;
888+
889+ /*
890+ * This is required to indicate that we have deleted the given
891+ * fileno.
892+ */
893+ if (i == fileno )
894+ newFile -- ;
895+ }
896+ else
897+ {
898+ if (FileTruncate (file -> files [i ], offset ,
899+ WAIT_EVENT_BUFFILE_TRUNCATE ) < 0 )
900+ ereport (ERROR ,
901+ (errcode_for_file_access (),
902+ errmsg ("could not truncate file \"%s\": %m" ,
903+ FilePathName (file -> files [i ]))));
904+ newOffset = offset ;
905+ }
906+ }
907+
908+ file -> numFiles = numFiles ;
909+
910+ /*
911+ * If the truncate point is within existing buffer then we can just adjust
912+ * pos within buffer.
913+ */
914+ if (newFile == file -> curFile &&
915+ newOffset >= file -> curOffset &&
916+ newOffset <= file -> curOffset + file -> nbytes )
917+ {
918+ /* No need to reset the current pos if the new pos is greater. */
919+ if (newOffset <= file -> curOffset + file -> pos )
920+ file -> pos = (int ) (newOffset - file -> curOffset );
921+
922+ /* Adjust the nbytes for the current buffer. */
923+ file -> nbytes = (int ) (newOffset - file -> curOffset );
924+ }
925+ else if (newFile == file -> curFile &&
926+ newOffset < file -> curOffset )
927+ {
928+ /*
929+ * The truncate point is within the existing file but prior to the
930+ * current position, so we can forget the current buffer and reset the
931+ * current position.
932+ */
933+ file -> curOffset = newOffset ;
934+ file -> pos = 0 ;
935+ file -> nbytes = 0 ;
936+ }
937+ else if (newFile < file -> curFile )
938+ {
939+ /*
940+ * The truncate point is prior to the current file, so need to reset
941+ * the current position accordingly.
942+ */
943+ file -> curFile = newFile ;
944+ file -> curOffset = newOffset ;
945+ file -> pos = 0 ;
946+ file -> nbytes = 0 ;
947+ }
948+ /* Nothing to do, if the truncate point is beyond current file. */
949+ }
0 commit comments