32
32
* (by opening multiple fd.c temporary files). This is an essential feature
33
33
* for sorts and hashjoins on large amounts of data.
34
34
*
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.
39
43
*-------------------------------------------------------------------------
40
44
*/
41
45
@@ -277,7 +281,7 @@ BufFileCreateShared(SharedFileSet *fileset, const char *name)
277
281
* backends and render it read-only.
278
282
*/
279
283
BufFile *
280
- BufFileOpenShared (SharedFileSet * fileset , const char * name )
284
+ BufFileOpenShared (SharedFileSet * fileset , const char * name , int mode )
281
285
{
282
286
BufFile * file ;
283
287
char segment_name [MAXPGPATH ];
@@ -301,7 +305,7 @@ BufFileOpenShared(SharedFileSet *fileset, const char *name)
301
305
}
302
306
/* Try to load a segment. */
303
307
SharedSegmentName (segment_name , name , nfiles );
304
- files [nfiles ] = SharedFileSetOpen (fileset , segment_name );
308
+ files [nfiles ] = SharedFileSetOpen (fileset , segment_name , mode );
305
309
if (files [nfiles ] <= 0 )
306
310
break ;
307
311
++ nfiles ;
@@ -321,7 +325,7 @@ BufFileOpenShared(SharedFileSet *fileset, const char *name)
321
325
322
326
file = makeBufFileCommon (nfiles );
323
327
file -> files = files ;
324
- file -> readOnly = true; /* Can't write to files opened this way */
328
+ file -> readOnly = ( mode == O_RDONLY ) ? true : false;
325
329
file -> fileset = fileset ;
326
330
file -> name = pstrdup (name );
327
331
@@ -666,11 +670,21 @@ BufFileSeek(BufFile *file, int fileno, off_t offset, int whence)
666
670
newFile = file -> curFile ;
667
671
newOffset = (file -> curOffset + file -> pos ) + offset ;
668
672
break ;
669
- #ifdef NOT_USED
670
673
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 )));
672
687
break ;
673
- #endif
674
688
default :
675
689
elog (ERROR , "invalid whence: %d" , whence );
676
690
return EOF ;
@@ -838,3 +852,98 @@ BufFileAppend(BufFile *target, BufFile *source)
838
852
839
853
return startBlock ;
840
854
}
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