@@ -152,7 +152,17 @@ typedef struct LogicalTape
152152struct LogicalTapeSet
153153{
154154 BufFile * pfile ; /* underlying file for whole tape set */
155- long nFileBlocks ; /* # of blocks used in underlying file */
155+
156+ /*
157+ * File size tracking. nBlocksWritten is the size of the underlying file,
158+ * in BLCKSZ blocks. nBlocksAllocated is the number of blocks allocated
159+ * by ltsGetFreeBlock(), and it is always greater than or equal to
160+ * nBlocksWritten. Blocks between nBlocksAllocated and nBlocksWritten are
161+ * blocks that have been allocated for a tape, but have not been written
162+ * to the underlying file yet.
163+ */
164+ long nBlocksAllocated ; /* # of blocks allocated */
165+ long nBlocksWritten ; /* # of blocks used in underlying file */
156166
157167 /*
158168 * We store the numbers of recycled-and-available blocks in freeBlocks[].
@@ -187,21 +197,43 @@ static void ltsReleaseBlock(LogicalTapeSet *lts, long blocknum);
187197/*
188198 * Write a block-sized buffer to the specified block of the underlying file.
189199 *
190- * NB: should not attempt to write beyond current end of file (ie, create
191- * "holes" in file), since BufFile doesn't allow that. The first write pass
192- * must write blocks sequentially.
193- *
194200 * No need for an error return convention; we ereport() on any error.
195201 */
196202static void
197203ltsWriteBlock (LogicalTapeSet * lts , long blocknum , void * buffer )
198204{
205+ /*
206+ * BufFile does not support "holes", so if we're about to write a block
207+ * that's past the current end of file, fill the space between the current
208+ * end of file and the target block with zeros.
209+ *
210+ * This should happen rarely, otherwise you are not writing very
211+ * sequentially. In current use, this only happens when the sort ends
212+ * writing a run, and switches to another tape. The last block of the
213+ * previous tape isn't flushed to disk until the end of the sort, so you
214+ * get one-block hole, where the last block of the previous tape will
215+ * later go.
216+ */
217+ while (blocknum > lts -> nBlocksWritten )
218+ {
219+ char zerobuf [BLCKSZ ];
220+
221+ MemSet (zerobuf , 0 , sizeof (zerobuf ));
222+
223+ ltsWriteBlock (lts , lts -> nBlocksWritten , zerobuf );
224+ }
225+
226+ /* Write the requested block */
199227 if (BufFileSeekBlock (lts -> pfile , blocknum ) != 0 ||
200228 BufFileWrite (lts -> pfile , buffer , BLCKSZ ) != BLCKSZ )
201229 ereport (ERROR ,
202230 (errcode_for_file_access (),
203231 errmsg ("could not write block %ld of temporary file: %m" ,
204232 blocknum )));
233+
234+ /* Update nBlocksWritten, if we extended the file */
235+ if (blocknum == lts -> nBlocksWritten )
236+ lts -> nBlocksWritten ++ ;
205237}
206238
207239/*
@@ -281,9 +313,6 @@ freeBlocks_cmp(const void *a, const void *b)
281313
282314/*
283315 * Select a currently unused block for writing to.
284- *
285- * NB: should only be called when writer is ready to write immediately,
286- * to ensure that first write pass is sequential.
287316 */
288317static long
289318ltsGetFreeBlock (LogicalTapeSet * lts )
@@ -304,7 +333,7 @@ ltsGetFreeBlock(LogicalTapeSet *lts)
304333 return lts -> freeBlocks [-- lts -> nFreeBlocks ];
305334 }
306335 else
307- return lts -> nFileBlocks ++ ;
336+ return lts -> nBlocksAllocated ++ ;
308337}
309338
310339/*
@@ -360,7 +389,8 @@ LogicalTapeSetCreate(int ntapes)
360389 lts = (LogicalTapeSet * ) palloc (offsetof(LogicalTapeSet , tapes ) +
361390 ntapes * sizeof (LogicalTape ));
362391 lts -> pfile = BufFileCreateTemp (false);
363- lts -> nFileBlocks = 0L ;
392+ lts -> nBlocksAllocated = 0L ;
393+ lts -> nBlocksWritten = 0L ;
364394 lts -> forgetFreeSpace = false;
365395 lts -> blocksSorted = true; /* a zero-length array is sorted ... */
366396 lts -> freeBlocksLen = 32 ; /* reasonable initial guess */
@@ -858,5 +888,5 @@ LogicalTapeTell(LogicalTapeSet *lts, int tapenum,
858888long
859889LogicalTapeSetBlocks (LogicalTapeSet * lts )
860890{
861- return lts -> nFileBlocks ;
891+ return lts -> nBlocksAllocated ;
862892}
0 commit comments