PostgreSQL Source Code  git master
logtape.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * logtape.c
4  * Management of "logical tapes" within temporary files.
5  *
6  * This module exists to support sorting via multiple merge passes (see
7  * tuplesort.c). Merging is an ideal algorithm for tape devices, but if
8  * we implement it on disk by creating a separate file for each "tape",
9  * there is an annoying problem: the peak space usage is at least twice
10  * the volume of actual data to be sorted. (This must be so because each
11  * datum will appear in both the input and output tapes of the final
12  * merge pass. For seven-tape polyphase merge, which is otherwise a
13  * pretty good algorithm, peak usage is more like 4x actual data volume.)
14  *
15  * We can work around this problem by recognizing that any one tape
16  * dataset (with the possible exception of the final output) is written
17  * and read exactly once in a perfectly sequential manner. Therefore,
18  * a datum once read will not be required again, and we can recycle its
19  * space for use by the new tape dataset(s) being generated. In this way,
20  * the total space usage is essentially just the actual data volume, plus
21  * insignificant bookkeeping and start/stop overhead.
22  *
23  * Few OSes allow arbitrary parts of a file to be released back to the OS,
24  * so we have to implement this space-recycling ourselves within a single
25  * logical file. logtape.c exists to perform this bookkeeping and provide
26  * the illusion of N independent tape devices to tuplesort.c. Note that
27  * logtape.c itself depends on buffile.c to provide a "logical file" of
28  * larger size than the underlying OS may support.
29  *
30  * For simplicity, we allocate and release space in the underlying file
31  * in BLCKSZ-size blocks. Space allocation boils down to keeping track
32  * of which blocks in the underlying file belong to which logical tape,
33  * plus any blocks that are free (recycled and not yet reused).
34  * The blocks in each logical tape form a chain, with a prev- and next-
35  * pointer in each block.
36  *
37  * The initial write pass is guaranteed to fill the underlying file
38  * perfectly sequentially, no matter how data is divided into logical tapes.
39  * Once we begin merge passes, the access pattern becomes considerably
40  * less predictable --- but the seeking involved should be comparable to
41  * what would happen if we kept each logical tape in a separate file,
42  * so there's no serious performance penalty paid to obtain the space
43  * savings of recycling. We try to localize the write accesses by always
44  * writing to the lowest-numbered free block when we have a choice; it's
45  * not clear this helps much, but it can't hurt. (XXX perhaps a LIFO
46  * policy for free blocks would be better?)
47  *
48  * To further make the I/Os more sequential, we can use a larger buffer
49  * when reading, and read multiple blocks from the same tape in one go,
50  * whenever the buffer becomes empty.
51  *
52  * To support the above policy of writing to the lowest free block, the
53  * freelist is a min heap.
54  *
55  * Since all the bookkeeping and buffer memory is allocated with palloc(),
56  * and the underlying file(s) are made with OpenTemporaryFile, all resources
57  * for a logical tape set are certain to be cleaned up even if processing
58  * is aborted by ereport(ERROR). To avoid confusion, the caller should take
59  * care that all calls for a single LogicalTapeSet are made in the same
60  * palloc context.
61  *
62  * To support parallel sort operations involving coordinated callers to
63  * tuplesort.c routines across multiple workers, it is necessary to
64  * concatenate each worker BufFile/tapeset into one single logical tapeset
65  * managed by the leader. Workers should have produced one final
66  * materialized tape (their entire output) when this happens in leader.
67  * There will always be the same number of runs as input tapes, and the same
68  * number of input tapes as participants (worker Tuplesortstates).
69  *
70  * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
71  * Portions Copyright (c) 1994, Regents of the University of California
72  *
73  * IDENTIFICATION
74  * src/backend/utils/sort/logtape.c
75  *
76  *-------------------------------------------------------------------------
77  */
78 
79 #include "postgres.h"
80 
81 #include <fcntl.h>
82 
83 #include "storage/buffile.h"
84 #include "utils/builtins.h"
85 #include "utils/logtape.h"
86 #include "utils/memdebug.h"
87 #include "utils/memutils.h"
88 
89 /*
90  * A TapeBlockTrailer is stored at the end of each BLCKSZ block.
91  *
92  * The first block of a tape has prev == -1. The last block of a tape
93  * stores the number of valid bytes on the block, inverted, in 'next'
94  * Therefore next < 0 indicates the last block.
95  */
96 typedef struct TapeBlockTrailer
97 {
98  long prev; /* previous block on this tape, or -1 on first
99  * block */
100  long next; /* next block on this tape, or # of valid
101  * bytes on last block (if < 0) */
103 
104 #define TapeBlockPayloadSize (BLCKSZ - sizeof(TapeBlockTrailer))
105 #define TapeBlockGetTrailer(buf) \
106  ((TapeBlockTrailer *) ((char *) buf + TapeBlockPayloadSize))
107 
108 #define TapeBlockIsLast(buf) (TapeBlockGetTrailer(buf)->next < 0)
109 #define TapeBlockGetNBytes(buf) \
110  (TapeBlockIsLast(buf) ? \
111  (- TapeBlockGetTrailer(buf)->next) : TapeBlockPayloadSize)
112 #define TapeBlockSetNBytes(buf, nbytes) \
113  (TapeBlockGetTrailer(buf)->next = -(nbytes))
114 
115 /*
116  * When multiple tapes are being written to concurrently (as in HashAgg),
117  * avoid excessive fragmentation by preallocating block numbers to individual
118  * tapes. Each preallocation doubles in size starting at
119  * TAPE_WRITE_PREALLOC_MIN blocks up to TAPE_WRITE_PREALLOC_MAX blocks.
120  *
121  * No filesystem operations are performed for preallocation; only the block
122  * numbers are reserved. This may lead to sparse writes, which will cause
123  * ltsWriteBlock() to fill in holes with zeros.
124  */
125 #define TAPE_WRITE_PREALLOC_MIN 8
126 #define TAPE_WRITE_PREALLOC_MAX 128
127 
128 /*
129  * This data structure represents a single "logical tape" within the set
130  * of logical tapes stored in the same file.
131  *
132  * While writing, we hold the current partially-written data block in the
133  * buffer. While reading, we can hold multiple blocks in the buffer. Note
134  * that we don't retain the trailers of a block when it's read into the
135  * buffer. The buffer therefore contains one large contiguous chunk of data
136  * from the tape.
137  */
138 typedef struct LogicalTape
139 {
140  bool writing; /* T while in write phase */
141  bool frozen; /* T if blocks should not be freed when read */
142  bool dirty; /* does buffer need to be written? */
143 
144  /*
145  * Block numbers of the first, current, and next block of the tape.
146  *
147  * The "current" block number is only valid when writing, or reading from
148  * a frozen tape. (When reading from an unfrozen tape, we use a larger
149  * read buffer that holds multiple blocks, so the "current" block is
150  * ambiguous.)
151  *
152  * When concatenation of worker tape BufFiles is performed, an offset to
153  * the first block in the unified BufFile space is applied during reads.
154  */
159 
160  /*
161  * Buffer for current data block(s).
162  */
163  char *buffer; /* physical buffer (separately palloc'd) */
164  int buffer_size; /* allocated size of the buffer */
165  int max_size; /* highest useful, safe buffer_size */
166  int pos; /* next read/write position in buffer */
167  int nbytes; /* total # of valid bytes in buffer */
168 
169  /*
170  * Preallocated block numbers are held in an array sorted in descending
171  * order; blocks are consumed from the end of the array (lowest block
172  * numbers first).
173  */
174  long *prealloc;
175  int nprealloc; /* number of elements in list */
176  int prealloc_size; /* number of elements list can hold */
177 } LogicalTape;
178 
179 /*
180  * This data structure represents a set of related "logical tapes" sharing
181  * space in a single underlying file. (But that "file" may be multiple files
182  * if needed to escape OS limits on file size; buffile.c handles that for us.)
183  * The number of tapes is fixed at creation.
184  */
186 {
187  BufFile *pfile; /* underlying file for whole tape set */
188 
189  /*
190  * File size tracking. nBlocksWritten is the size of the underlying file,
191  * in BLCKSZ blocks. nBlocksAllocated is the number of blocks allocated
192  * by ltsReleaseBlock(), and it is always greater than or equal to
193  * nBlocksWritten. Blocks between nBlocksAllocated and nBlocksWritten are
194  * blocks that have been allocated for a tape, but have not been written
195  * to the underlying file yet. nHoleBlocks tracks the total number of
196  * blocks that are in unused holes between worker spaces following BufFile
197  * concatenation.
198  */
199  long nBlocksAllocated; /* # of blocks allocated */
200  long nBlocksWritten; /* # of blocks used in underlying file */
201  long nHoleBlocks; /* # of "hole" blocks left */
202 
203  /*
204  * We store the numbers of recycled-and-available blocks in freeBlocks[].
205  * When there are no such blocks, we extend the underlying file.
206  *
207  * If forgetFreeSpace is true then any freed blocks are simply forgotten
208  * rather than being remembered in freeBlocks[]. See notes for
209  * LogicalTapeSetForgetFreeSpace().
210  */
211  bool forgetFreeSpace; /* are we remembering free blocks? */
212  long *freeBlocks; /* resizable array holding minheap */
213  long nFreeBlocks; /* # of currently free blocks */
214  Size freeBlocksLen; /* current allocated length of freeBlocks[] */
215  bool enable_prealloc; /* preallocate write blocks? */
216 
217  /* The array of logical tapes. */
218  int nTapes; /* # of logical tapes in set */
219  LogicalTape *tapes; /* has nTapes nentries */
220 };
221 
222 static void ltsWriteBlock(LogicalTapeSet *lts, long blocknum, void *buffer);
223 static void ltsReadBlock(LogicalTapeSet *lts, long blocknum, void *buffer);
224 static long ltsGetBlock(LogicalTapeSet *lts, LogicalTape *lt);
225 static long ltsGetFreeBlock(LogicalTapeSet *lts);
226 static long ltsGetPreallocBlock(LogicalTapeSet *lts, LogicalTape *lt);
227 static void ltsReleaseBlock(LogicalTapeSet *lts, long blocknum);
228 static void ltsConcatWorkerTapes(LogicalTapeSet *lts, TapeShare *shared,
229  SharedFileSet *fileset);
230 static void ltsInitTape(LogicalTape *lt);
231 static void ltsInitReadBuffer(LogicalTapeSet *lts, LogicalTape *lt);
232 
233 
234 /*
235  * Write a block-sized buffer to the specified block of the underlying file.
236  *
237  * No need for an error return convention; we ereport() on any error.
238  */
239 static void
240 ltsWriteBlock(LogicalTapeSet *lts, long blocknum, void *buffer)
241 {
242  /*
243  * BufFile does not support "holes", so if we're about to write a block
244  * that's past the current end of file, fill the space between the current
245  * end of file and the target block with zeros.
246  *
247  * This can happen either when tapes preallocate blocks; or for the last
248  * block of a tape which might not have been flushed.
249  *
250  * Note that BufFile concatenation can leave "holes" in BufFile between
251  * worker-owned block ranges. These are tracked for reporting purposes
252  * only. We never read from nor write to these hole blocks, and so they
253  * are not considered here.
254  */
255  while (blocknum > lts->nBlocksWritten)
256  {
257  PGAlignedBlock zerobuf;
258 
259  MemSet(zerobuf.data, 0, sizeof(zerobuf));
260 
261  ltsWriteBlock(lts, lts->nBlocksWritten, zerobuf.data);
262  }
263 
264  /* Write the requested block */
265  if (BufFileSeekBlock(lts->pfile, blocknum) != 0)
266  ereport(ERROR,
268  errmsg("could not seek to block %ld of temporary file",
269  blocknum)));
270  BufFileWrite(lts->pfile, buffer, BLCKSZ);
271 
272  /* Update nBlocksWritten, if we extended the file */
273  if (blocknum == lts->nBlocksWritten)
274  lts->nBlocksWritten++;
275 }
276 
277 /*
278  * Read a block-sized buffer from the specified block of the underlying file.
279  *
280  * No need for an error return convention; we ereport() on any error. This
281  * module should never attempt to read a block it doesn't know is there.
282  */
283 static void
284 ltsReadBlock(LogicalTapeSet *lts, long blocknum, void *buffer)
285 {
286  size_t nread;
287 
288  if (BufFileSeekBlock(lts->pfile, blocknum) != 0)
289  ereport(ERROR,
291  errmsg("could not seek to block %ld of temporary file",
292  blocknum)));
293  nread = BufFileRead(lts->pfile, buffer, BLCKSZ);
294  if (nread != BLCKSZ)
295  ereport(ERROR,
297  errmsg("could not read block %ld of temporary file: read only %zu of %zu bytes",
298  blocknum, nread, (size_t) BLCKSZ)));
299 }
300 
301 /*
302  * Read as many blocks as we can into the per-tape buffer.
303  *
304  * Returns true if anything was read, 'false' on EOF.
305  */
306 static bool
308 {
309  lt->pos = 0;
310  lt->nbytes = 0;
311 
312  do
313  {
314  char *thisbuf = lt->buffer + lt->nbytes;
315  long datablocknum = lt->nextBlockNumber;
316 
317  /* Fetch next block number */
318  if (datablocknum == -1L)
319  break; /* EOF */
320  /* Apply worker offset, needed for leader tapesets */
321  datablocknum += lt->offsetBlockNumber;
322 
323  /* Read the block */
324  ltsReadBlock(lts, datablocknum, (void *) thisbuf);
325  if (!lt->frozen)
326  ltsReleaseBlock(lts, datablocknum);
328 
329  lt->nbytes += TapeBlockGetNBytes(thisbuf);
330  if (TapeBlockIsLast(thisbuf))
331  {
332  lt->nextBlockNumber = -1L;
333  /* EOF */
334  break;
335  }
336  else
337  lt->nextBlockNumber = TapeBlockGetTrailer(thisbuf)->next;
338 
339  /* Advance to next block, if we have buffer space left */
340  } while (lt->buffer_size - lt->nbytes > BLCKSZ);
341 
342  return (lt->nbytes > 0);
343 }
344 
345 static inline void
346 swap_nodes(long *heap, unsigned long a, unsigned long b)
347 {
348  unsigned long swap;
349 
350  swap = heap[a];
351  heap[a] = heap[b];
352  heap[b] = swap;
353 }
354 
355 static inline unsigned long
356 left_offset(unsigned long i)
357 {
358  return 2 * i + 1;
359 }
360 
361 static inline unsigned long
362 right_offset(unsigned i)
363 {
364  return 2 * i + 2;
365 }
366 
367 static inline unsigned long
368 parent_offset(unsigned long i)
369 {
370  return (i - 1) / 2;
371 }
372 
373 /*
374  * Get the next block for writing.
375  */
376 static long
378 {
379  if (lts->enable_prealloc)
380  return ltsGetPreallocBlock(lts, lt);
381  else
382  return ltsGetFreeBlock(lts);
383 }
384 
385 /*
386  * Select the lowest currently unused block from the tape set's global free
387  * list min heap.
388  */
389 static long
391 {
392  long *heap = lts->freeBlocks;
393  long blocknum;
394  int heapsize;
395  unsigned long pos;
396 
397  /* freelist empty; allocate a new block */
398  if (lts->nFreeBlocks == 0)
399  return lts->nBlocksAllocated++;
400 
401  if (lts->nFreeBlocks == 1)
402  {
403  lts->nFreeBlocks--;
404  return lts->freeBlocks[0];
405  }
406 
407  /* take top of minheap */
408  blocknum = heap[0];
409 
410  /* replace with end of minheap array */
411  heap[0] = heap[--lts->nFreeBlocks];
412 
413  /* sift down */
414  pos = 0;
415  heapsize = lts->nFreeBlocks;
416  while (true)
417  {
418  unsigned long left = left_offset(pos);
419  unsigned long right = right_offset(pos);
420  unsigned long min_child;
421 
422  if (left < heapsize && right < heapsize)
423  min_child = (heap[left] < heap[right]) ? left : right;
424  else if (left < heapsize)
425  min_child = left;
426  else if (right < heapsize)
427  min_child = right;
428  else
429  break;
430 
431  if (heap[min_child] >= heap[pos])
432  break;
433 
434  swap_nodes(heap, min_child, pos);
435  pos = min_child;
436  }
437 
438  return blocknum;
439 }
440 
441 /*
442  * Return the lowest free block number from the tape's preallocation list.
443  * Refill the preallocation list with blocks from the tape set's free list if
444  * necessary.
445  */
446 static long
448 {
449  /* sorted in descending order, so return the last element */
450  if (lt->nprealloc > 0)
451  return lt->prealloc[--lt->nprealloc];
452 
453  if (lt->prealloc == NULL)
454  {
456  lt->prealloc = (long *) palloc(sizeof(long) * lt->prealloc_size);
457  }
458  else if (lt->prealloc_size < TAPE_WRITE_PREALLOC_MAX)
459  {
460  /* when the preallocation list runs out, double the size */
461  lt->prealloc_size *= 2;
464  lt->prealloc = (long *) repalloc(lt->prealloc,
465  sizeof(long) * lt->prealloc_size);
466  }
467 
468  /* refill preallocation list */
469  lt->nprealloc = lt->prealloc_size;
470  for (int i = lt->nprealloc; i > 0; i--)
471  {
472  lt->prealloc[i - 1] = ltsGetFreeBlock(lts);
473 
474  /* verify descending order */
475  Assert(i == lt->nprealloc || lt->prealloc[i - 1] > lt->prealloc[i]);
476  }
477 
478  return lt->prealloc[--lt->nprealloc];
479 }
480 
481 /*
482  * Return a block# to the freelist.
483  */
484 static void
485 ltsReleaseBlock(LogicalTapeSet *lts, long blocknum)
486 {
487  long *heap;
488  unsigned long pos;
489 
490  /*
491  * Do nothing if we're no longer interested in remembering free space.
492  */
493  if (lts->forgetFreeSpace)
494  return;
495 
496  /*
497  * Enlarge freeBlocks array if full.
498  */
499  if (lts->nFreeBlocks >= lts->freeBlocksLen)
500  {
501  /*
502  * If the freelist becomes very large, just return and leak this free
503  * block.
504  */
505  if (lts->freeBlocksLen * 2 * sizeof(long) > MaxAllocSize)
506  return;
507 
508  lts->freeBlocksLen *= 2;
509  lts->freeBlocks = (long *) repalloc(lts->freeBlocks,
510  lts->freeBlocksLen * sizeof(long));
511  }
512 
513  heap = lts->freeBlocks;
514  pos = lts->nFreeBlocks;
515 
516  /* place entry at end of minheap array */
517  heap[pos] = blocknum;
518  lts->nFreeBlocks++;
519 
520  /* sift up */
521  while (pos != 0)
522  {
523  unsigned long parent = parent_offset(pos);
524 
525  if (heap[parent] < heap[pos])
526  break;
527 
528  swap_nodes(heap, parent, pos);
529  pos = parent;
530  }
531 }
532 
533 /*
534  * Claim ownership of a set of logical tapes from existing shared BufFiles.
535  *
536  * Caller should be leader process. Though tapes are marked as frozen in
537  * workers, they are not frozen when opened within leader, since unfrozen tapes
538  * use a larger read buffer. (Frozen tapes have smaller read buffer, optimized
539  * for random access.)
540  */
541 static void
543  SharedFileSet *fileset)
544 {
545  LogicalTape *lt = NULL;
546  long tapeblocks = 0L;
547  long nphysicalblocks = 0L;
548  int i;
549 
550  /* Should have at least one worker tape, plus leader's tape */
551  Assert(lts->nTapes >= 2);
552 
553  /*
554  * Build concatenated view of all BufFiles, remembering the block number
555  * where each source file begins. No changes are needed for leader/last
556  * tape.
557  */
558  for (i = 0; i < lts->nTapes - 1; i++)
559  {
560  char filename[MAXPGPATH];
561  BufFile *file;
562  int64 filesize;
563 
564  lt = &lts->tapes[i];
565 
566  pg_itoa(i, filename);
567  file = BufFileOpenShared(fileset, filename, O_RDONLY);
568  filesize = BufFileSize(file);
569 
570  /*
571  * Stash first BufFile, and concatenate subsequent BufFiles to that.
572  * Store block offset into each tape as we go.
573  */
574  lt->firstBlockNumber = shared[i].firstblocknumber;
575  if (i == 0)
576  {
577  lts->pfile = file;
578  lt->offsetBlockNumber = 0L;
579  }
580  else
581  {
582  lt->offsetBlockNumber = BufFileAppend(lts->pfile, file);
583  }
584  /* Don't allocate more for read buffer than could possibly help */
585  lt->max_size = Min(MaxAllocSize, filesize);
586  tapeblocks = filesize / BLCKSZ;
587  nphysicalblocks += tapeblocks;
588  }
589 
590  /*
591  * Set # of allocated blocks, as well as # blocks written. Use extent of
592  * new BufFile space (from 0 to end of last worker's tape space) for this.
593  * Allocated/written blocks should include space used by holes left
594  * between concatenated BufFiles.
595  */
596  lts->nBlocksAllocated = lt->offsetBlockNumber + tapeblocks;
597  lts->nBlocksWritten = lts->nBlocksAllocated;
598 
599  /*
600  * Compute number of hole blocks so that we can later work backwards, and
601  * instrument number of physical blocks. We don't simply use physical
602  * blocks directly for instrumentation because this would break if we ever
603  * subsequently wrote to the leader tape.
604  *
605  * Working backwards like this keeps our options open. If shared BufFiles
606  * ever support being written to post-export, logtape.c can automatically
607  * take advantage of that. We'd then support writing to the leader tape
608  * while recycling space from worker tapes, because the leader tape has a
609  * zero offset (write routines won't need to have extra logic to apply an
610  * offset).
611  *
612  * The only thing that currently prevents writing to the leader tape from
613  * working is the fact that BufFiles opened using BufFileOpenShared() are
614  * read-only by definition, but that could be changed if it seemed
615  * worthwhile. For now, writing to the leader tape will raise a "Bad file
616  * descriptor" error, so tuplesort must avoid writing to the leader tape
617  * altogether.
618  */
619  lts->nHoleBlocks = lts->nBlocksAllocated - nphysicalblocks;
620 }
621 
622 /*
623  * Initialize per-tape struct. Note we allocate the I/O buffer lazily.
624  */
625 static void
627 {
628  lt->writing = true;
629  lt->frozen = false;
630  lt->dirty = false;
631  lt->firstBlockNumber = -1L;
632  lt->curBlockNumber = -1L;
633  lt->nextBlockNumber = -1L;
634  lt->offsetBlockNumber = 0L;
635  lt->buffer = NULL;
636  lt->buffer_size = 0;
637  /* palloc() larger than MaxAllocSize would fail */
638  lt->max_size = MaxAllocSize;
639  lt->pos = 0;
640  lt->nbytes = 0;
641  lt->prealloc = NULL;
642  lt->nprealloc = 0;
643  lt->prealloc_size = 0;
644 }
645 
646 /*
647  * Lazily allocate and initialize the read buffer. This avoids waste when many
648  * tapes are open at once, but not all are active between rewinding and
649  * reading.
650  */
651 static void
653 {
654  Assert(lt->buffer_size > 0);
655  lt->buffer = palloc(lt->buffer_size);
656 
657  /* Read the first block, or reset if tape is empty */
659  lt->pos = 0;
660  lt->nbytes = 0;
661  ltsReadFillBuffer(lts, lt);
662 }
663 
664 /*
665  * Create a set of logical tapes in a temporary underlying file.
666  *
667  * Each tape is initialized in write state. Serial callers pass ntapes,
668  * NULL argument for shared, and -1 for worker. Parallel worker callers
669  * pass ntapes, a shared file handle, NULL shared argument, and their own
670  * worker number. Leader callers, which claim shared worker tapes here,
671  * must supply non-sentinel values for all arguments except worker number,
672  * which should be -1.
673  *
674  * Leader caller is passing back an array of metadata each worker captured
675  * when LogicalTapeFreeze() was called for their final result tapes. Passed
676  * tapes array is actually sized ntapes - 1, because it includes only
677  * worker tapes, whereas leader requires its own leader tape. Note that we
678  * rely on the assumption that reclaimed worker tapes will only be read
679  * from once by leader, and never written to again (tapes are initialized
680  * for writing, but that's only to be consistent). Leader may not write to
681  * its own tape purely due to a restriction in the shared buffile
682  * infrastructure that may be lifted in the future.
683  */
685 LogicalTapeSetCreate(int ntapes, bool preallocate, TapeShare *shared,
686  SharedFileSet *fileset, int worker)
687 {
688  LogicalTapeSet *lts;
689  int i;
690 
691  /*
692  * Create top-level struct including per-tape LogicalTape structs.
693  */
694  Assert(ntapes > 0);
695  lts = (LogicalTapeSet *) palloc(sizeof(LogicalTapeSet));
696  lts->nBlocksAllocated = 0L;
697  lts->nBlocksWritten = 0L;
698  lts->nHoleBlocks = 0L;
699  lts->forgetFreeSpace = false;
700  lts->freeBlocksLen = 32; /* reasonable initial guess */
701  lts->freeBlocks = (long *) palloc(lts->freeBlocksLen * sizeof(long));
702  lts->nFreeBlocks = 0;
703  lts->enable_prealloc = preallocate;
704  lts->nTapes = ntapes;
705  lts->tapes = (LogicalTape *) palloc(ntapes * sizeof(LogicalTape));
706 
707  for (i = 0; i < ntapes; i++)
708  ltsInitTape(&lts->tapes[i]);
709 
710  /*
711  * Create temp BufFile storage as required.
712  *
713  * Leader concatenates worker tapes, which requires special adjustment to
714  * final tapeset data. Things are simpler for the worker case and the
715  * serial case, though. They are generally very similar -- workers use a
716  * shared fileset, whereas serial sorts use a conventional serial BufFile.
717  */
718  if (shared)
719  ltsConcatWorkerTapes(lts, shared, fileset);
720  else if (fileset)
721  {
722  char filename[MAXPGPATH];
723 
724  pg_itoa(worker, filename);
725  lts->pfile = BufFileCreateShared(fileset, filename);
726  }
727  else
728  lts->pfile = BufFileCreateTemp(false);
729 
730  return lts;
731 }
732 
733 /*
734  * Close a logical tape set and release all resources.
735  */
736 void
738 {
739  LogicalTape *lt;
740  int i;
741 
742  BufFileClose(lts->pfile);
743  for (i = 0; i < lts->nTapes; i++)
744  {
745  lt = &lts->tapes[i];
746  if (lt->buffer)
747  pfree(lt->buffer);
748  }
749  pfree(lts->tapes);
750  pfree(lts->freeBlocks);
751  pfree(lts);
752 }
753 
754 /*
755  * Mark a logical tape set as not needing management of free space anymore.
756  *
757  * This should be called if the caller does not intend to write any more data
758  * into the tape set, but is reading from un-frozen tapes. Since no more
759  * writes are planned, remembering free blocks is no longer useful. Setting
760  * this flag lets us avoid wasting time and space in ltsReleaseBlock(), which
761  * is not designed to handle large numbers of free blocks.
762  */
763 void
765 {
766  lts->forgetFreeSpace = true;
767 }
768 
769 /*
770  * Write to a logical tape.
771  *
772  * There are no error returns; we ereport() on failure.
773  */
774 void
776  void *ptr, size_t size)
777 {
778  LogicalTape *lt;
779  size_t nthistime;
780 
781  Assert(tapenum >= 0 && tapenum < lts->nTapes);
782  lt = &lts->tapes[tapenum];
783  Assert(lt->writing);
784  Assert(lt->offsetBlockNumber == 0L);
785 
786  /* Allocate data buffer and first block on first write */
787  if (lt->buffer == NULL)
788  {
789  lt->buffer = (char *) palloc(BLCKSZ);
790  lt->buffer_size = BLCKSZ;
791  }
792  if (lt->curBlockNumber == -1)
793  {
794  Assert(lt->firstBlockNumber == -1);
795  Assert(lt->pos == 0);
796 
797  lt->curBlockNumber = ltsGetBlock(lts, lt);
799 
800  TapeBlockGetTrailer(lt->buffer)->prev = -1L;
801  }
802 
803  Assert(lt->buffer_size == BLCKSZ);
804  while (size > 0)
805  {
806  if (lt->pos >= (int) TapeBlockPayloadSize)
807  {
808  /* Buffer full, dump it out */
809  long nextBlockNumber;
810 
811  if (!lt->dirty)
812  {
813  /* Hmm, went directly from reading to writing? */
814  elog(ERROR, "invalid logtape state: should be dirty");
815  }
816 
817  /*
818  * First allocate the next block, so that we can store it in the
819  * 'next' pointer of this block.
820  */
821  nextBlockNumber = ltsGetBlock(lts, lt);
822 
823  /* set the next-pointer and dump the current block. */
824  TapeBlockGetTrailer(lt->buffer)->next = nextBlockNumber;
825  ltsWriteBlock(lts, lt->curBlockNumber, (void *) lt->buffer);
826 
827  /* initialize the prev-pointer of the next block */
828  TapeBlockGetTrailer(lt->buffer)->prev = lt->curBlockNumber;
829  lt->curBlockNumber = nextBlockNumber;
830  lt->pos = 0;
831  lt->nbytes = 0;
832  }
833 
834  nthistime = TapeBlockPayloadSize - lt->pos;
835  if (nthistime > size)
836  nthistime = size;
837  Assert(nthistime > 0);
838 
839  memcpy(lt->buffer + lt->pos, ptr, nthistime);
840 
841  lt->dirty = true;
842  lt->pos += nthistime;
843  if (lt->nbytes < lt->pos)
844  lt->nbytes = lt->pos;
845  ptr = (void *) ((char *) ptr + nthistime);
846  size -= nthistime;
847  }
848 }
849 
850 /*
851  * Rewind logical tape and switch from writing to reading.
852  *
853  * The tape must currently be in writing state, or "frozen" in read state.
854  *
855  * 'buffer_size' specifies how much memory to use for the read buffer.
856  * Regardless of the argument, the actual amount of memory used is between
857  * BLCKSZ and MaxAllocSize, and is a multiple of BLCKSZ. The given value is
858  * rounded down and truncated to fit those constraints, if necessary. If the
859  * tape is frozen, the 'buffer_size' argument is ignored, and a small BLCKSZ
860  * byte buffer is used.
861  */
862 void
863 LogicalTapeRewindForRead(LogicalTapeSet *lts, int tapenum, size_t buffer_size)
864 {
865  LogicalTape *lt;
866 
867  Assert(tapenum >= 0 && tapenum < lts->nTapes);
868  lt = &lts->tapes[tapenum];
869 
870  /*
871  * Round and cap buffer_size if needed.
872  */
873  if (lt->frozen)
874  buffer_size = BLCKSZ;
875  else
876  {
877  /* need at least one block */
878  if (buffer_size < BLCKSZ)
879  buffer_size = BLCKSZ;
880 
881  /* palloc() larger than max_size is unlikely to be helpful */
882  if (buffer_size > lt->max_size)
883  buffer_size = lt->max_size;
884 
885  /* round down to BLCKSZ boundary */
886  buffer_size -= buffer_size % BLCKSZ;
887  }
888 
889  if (lt->writing)
890  {
891  /*
892  * Completion of a write phase. Flush last partial data block, and
893  * rewind for normal (destructive) read.
894  */
895  if (lt->dirty)
896  {
897  /*
898  * As long as we've filled the buffer at least once, its contents
899  * are entirely defined from valgrind's point of view, even though
900  * contents beyond the current end point may be stale. But it's
901  * possible - at least in the case of a parallel sort - to sort
902  * such small amount of data that we do not fill the buffer even
903  * once. Tell valgrind that its contents are defined, so it
904  * doesn't bleat.
905  */
907  lt->buffer_size - lt->nbytes);
908 
909  TapeBlockSetNBytes(lt->buffer, lt->nbytes);
910  ltsWriteBlock(lts, lt->curBlockNumber, (void *) lt->buffer);
911  }
912  lt->writing = false;
913  }
914  else
915  {
916  /*
917  * This is only OK if tape is frozen; we rewind for (another) read
918  * pass.
919  */
920  Assert(lt->frozen);
921  }
922 
923  if (lt->buffer)
924  pfree(lt->buffer);
925 
926  /* the buffer is lazily allocated, but set the size here */
927  lt->buffer = NULL;
928  lt->buffer_size = buffer_size;
929 
930  /* free the preallocation list, and return unused block numbers */
931  if (lt->prealloc != NULL)
932  {
933  for (int i = lt->nprealloc; i > 0; i--)
934  ltsReleaseBlock(lts, lt->prealloc[i - 1]);
935  pfree(lt->prealloc);
936  lt->prealloc = NULL;
937  lt->nprealloc = 0;
938  lt->prealloc_size = 0;
939  }
940 }
941 
942 /*
943  * Rewind logical tape and switch from reading to writing.
944  *
945  * NOTE: we assume the caller has read the tape to the end; otherwise
946  * untouched data will not have been freed. We could add more code to free
947  * any unread blocks, but in current usage of this module it'd be useless
948  * code.
949  */
950 void
952 {
953  LogicalTape *lt;
954 
955  Assert(tapenum >= 0 && tapenum < lts->nTapes);
956  lt = &lts->tapes[tapenum];
957 
958  Assert(!lt->writing && !lt->frozen);
959  lt->writing = true;
960  lt->dirty = false;
961  lt->firstBlockNumber = -1L;
962  lt->curBlockNumber = -1L;
963  lt->pos = 0;
964  lt->nbytes = 0;
965  if (lt->buffer)
966  pfree(lt->buffer);
967  lt->buffer = NULL;
968  lt->buffer_size = 0;
969 }
970 
971 /*
972  * Read from a logical tape.
973  *
974  * Early EOF is indicated by return value less than #bytes requested.
975  */
976 size_t
977 LogicalTapeRead(LogicalTapeSet *lts, int tapenum,
978  void *ptr, size_t size)
979 {
980  LogicalTape *lt;
981  size_t nread = 0;
982  size_t nthistime;
983 
984  Assert(tapenum >= 0 && tapenum < lts->nTapes);
985  lt = &lts->tapes[tapenum];
986  Assert(!lt->writing);
987 
988  if (lt->buffer == NULL)
989  ltsInitReadBuffer(lts, lt);
990 
991  while (size > 0)
992  {
993  if (lt->pos >= lt->nbytes)
994  {
995  /* Try to load more data into buffer. */
996  if (!ltsReadFillBuffer(lts, lt))
997  break; /* EOF */
998  }
999 
1000  nthistime = lt->nbytes - lt->pos;
1001  if (nthistime > size)
1002  nthistime = size;
1003  Assert(nthistime > 0);
1004 
1005  memcpy(ptr, lt->buffer + lt->pos, nthistime);
1006 
1007  lt->pos += nthistime;
1008  ptr = (void *) ((char *) ptr + nthistime);
1009  size -= nthistime;
1010  nread += nthistime;
1011  }
1012 
1013  return nread;
1014 }
1015 
1016 /*
1017  * "Freeze" the contents of a tape so that it can be read multiple times
1018  * and/or read backwards. Once a tape is frozen, its contents will not
1019  * be released until the LogicalTapeSet is destroyed. This is expected
1020  * to be used only for the final output pass of a merge.
1021  *
1022  * This *must* be called just at the end of a write pass, before the
1023  * tape is rewound (after rewind is too late!). It performs a rewind
1024  * and switch to read mode "for free". An immediately following rewind-
1025  * for-read call is OK but not necessary.
1026  *
1027  * share output argument is set with details of storage used for tape after
1028  * freezing, which may be passed to LogicalTapeSetCreate within leader
1029  * process later. This metadata is only of interest to worker callers
1030  * freezing their final output for leader (single materialized tape).
1031  * Serial sorts should set share to NULL.
1032  */
1033 void
1034 LogicalTapeFreeze(LogicalTapeSet *lts, int tapenum, TapeShare *share)
1035 {
1036  LogicalTape *lt;
1037 
1038  Assert(tapenum >= 0 && tapenum < lts->nTapes);
1039  lt = &lts->tapes[tapenum];
1040  Assert(lt->writing);
1041  Assert(lt->offsetBlockNumber == 0L);
1042 
1043  /*
1044  * Completion of a write phase. Flush last partial data block, and rewind
1045  * for nondestructive read.
1046  */
1047  if (lt->dirty)
1048  {
1049  /*
1050  * As long as we've filled the buffer at least once, its contents are
1051  * entirely defined from valgrind's point of view, even though
1052  * contents beyond the current end point may be stale. But it's
1053  * possible - at least in the case of a parallel sort - to sort such
1054  * small amount of data that we do not fill the buffer even once. Tell
1055  * valgrind that its contents are defined, so it doesn't bleat.
1056  */
1058  lt->buffer_size - lt->nbytes);
1059 
1060  TapeBlockSetNBytes(lt->buffer, lt->nbytes);
1061  ltsWriteBlock(lts, lt->curBlockNumber, (void *) lt->buffer);
1062  lt->writing = false;
1063  }
1064  lt->writing = false;
1065  lt->frozen = true;
1066 
1067  /*
1068  * The seek and backspace functions assume a single block read buffer.
1069  * That's OK with current usage. A larger buffer is helpful to make the
1070  * read pattern of the backing file look more sequential to the OS, when
1071  * we're reading from multiple tapes. But at the end of a sort, when a
1072  * tape is frozen, we only read from a single tape anyway.
1073  */
1074  if (!lt->buffer || lt->buffer_size != BLCKSZ)
1075  {
1076  if (lt->buffer)
1077  pfree(lt->buffer);
1078  lt->buffer = palloc(BLCKSZ);
1079  lt->buffer_size = BLCKSZ;
1080  }
1081 
1082  /* Read the first block, or reset if tape is empty */
1083  lt->curBlockNumber = lt->firstBlockNumber;
1084  lt->pos = 0;
1085  lt->nbytes = 0;
1086 
1087  if (lt->firstBlockNumber == -1L)
1088  lt->nextBlockNumber = -1L;
1089  ltsReadBlock(lts, lt->curBlockNumber, (void *) lt->buffer);
1090  if (TapeBlockIsLast(lt->buffer))
1091  lt->nextBlockNumber = -1L;
1092  else
1093  lt->nextBlockNumber = TapeBlockGetTrailer(lt->buffer)->next;
1094  lt->nbytes = TapeBlockGetNBytes(lt->buffer);
1095 
1096  /* Handle extra steps when caller is to share its tapeset */
1097  if (share)
1098  {
1099  BufFileExportShared(lts->pfile);
1100  share->firstblocknumber = lt->firstBlockNumber;
1101  }
1102 }
1103 
1104 /*
1105  * Add additional tapes to this tape set. Not intended to be used when any
1106  * tapes are frozen.
1107  */
1108 void
1109 LogicalTapeSetExtend(LogicalTapeSet *lts, int nAdditional)
1110 {
1111  int i;
1112  int nTapesOrig = lts->nTapes;
1113 
1114  lts->nTapes += nAdditional;
1115 
1116  lts->tapes = (LogicalTape *) repalloc(lts->tapes,
1117  lts->nTapes * sizeof(LogicalTape));
1118 
1119  for (i = nTapesOrig; i < lts->nTapes; i++)
1120  ltsInitTape(&lts->tapes[i]);
1121 }
1122 
1123 /*
1124  * Backspace the tape a given number of bytes. (We also support a more
1125  * general seek interface, see below.)
1126  *
1127  * *Only* a frozen-for-read tape can be backed up; we don't support
1128  * random access during write, and an unfrozen read tape may have
1129  * already discarded the desired data!
1130  *
1131  * Returns the number of bytes backed up. It can be less than the
1132  * requested amount, if there isn't that much data before the current
1133  * position. The tape is positioned to the beginning of the tape in
1134  * that case.
1135  */
1136 size_t
1137 LogicalTapeBackspace(LogicalTapeSet *lts, int tapenum, size_t size)
1138 {
1139  LogicalTape *lt;
1140  size_t seekpos = 0;
1141 
1142  Assert(tapenum >= 0 && tapenum < lts->nTapes);
1143  lt = &lts->tapes[tapenum];
1144  Assert(lt->frozen);
1145  Assert(lt->buffer_size == BLCKSZ);
1146 
1147  if (lt->buffer == NULL)
1148  ltsInitReadBuffer(lts, lt);
1149 
1150  /*
1151  * Easy case for seek within current block.
1152  */
1153  if (size <= (size_t) lt->pos)
1154  {
1155  lt->pos -= (int) size;
1156  return size;
1157  }
1158 
1159  /*
1160  * Not-so-easy case, have to walk back the chain of blocks. This
1161  * implementation would be pretty inefficient for long seeks, but we
1162  * really aren't doing that (a seek over one tuple is typical).
1163  */
1164  seekpos = (size_t) lt->pos; /* part within this block */
1165  while (size > seekpos)
1166  {
1167  long prev = TapeBlockGetTrailer(lt->buffer)->prev;
1168 
1169  if (prev == -1L)
1170  {
1171  /* Tried to back up beyond the beginning of tape. */
1172  if (lt->curBlockNumber != lt->firstBlockNumber)
1173  elog(ERROR, "unexpected end of tape");
1174  lt->pos = 0;
1175  return seekpos;
1176  }
1177 
1178  ltsReadBlock(lts, prev, (void *) lt->buffer);
1179 
1180  if (TapeBlockGetTrailer(lt->buffer)->next != lt->curBlockNumber)
1181  elog(ERROR, "broken tape, next of block %ld is %ld, expected %ld",
1182  prev,
1183  TapeBlockGetTrailer(lt->buffer)->next,
1184  lt->curBlockNumber);
1185 
1187  lt->curBlockNumber = prev;
1188  lt->nextBlockNumber = TapeBlockGetTrailer(lt->buffer)->next;
1189 
1190  seekpos += TapeBlockPayloadSize;
1191  }
1192 
1193  /*
1194  * 'seekpos' can now be greater than 'size', because it points to the
1195  * beginning the target block. The difference is the position within the
1196  * page.
1197  */
1198  lt->pos = seekpos - size;
1199  return size;
1200 }
1201 
1202 /*
1203  * Seek to an arbitrary position in a logical tape.
1204  *
1205  * *Only* a frozen-for-read tape can be seeked.
1206  *
1207  * Must be called with a block/offset previously returned by
1208  * LogicalTapeTell().
1209  */
1210 void
1212  long blocknum, int offset)
1213 {
1214  LogicalTape *lt;
1215 
1216  Assert(tapenum >= 0 && tapenum < lts->nTapes);
1217  lt = &lts->tapes[tapenum];
1218  Assert(lt->frozen);
1219  Assert(offset >= 0 && offset <= TapeBlockPayloadSize);
1220  Assert(lt->buffer_size == BLCKSZ);
1221 
1222  if (lt->buffer == NULL)
1223  ltsInitReadBuffer(lts, lt);
1224 
1225  if (blocknum != lt->curBlockNumber)
1226  {
1227  ltsReadBlock(lts, blocknum, (void *) lt->buffer);
1228  lt->curBlockNumber = blocknum;
1230  lt->nextBlockNumber = TapeBlockGetTrailer(lt->buffer)->next;
1231  }
1232 
1233  if (offset > lt->nbytes)
1234  elog(ERROR, "invalid tape seek position");
1235  lt->pos = offset;
1236 }
1237 
1238 /*
1239  * Obtain current position in a form suitable for a later LogicalTapeSeek.
1240  *
1241  * NOTE: it'd be OK to do this during write phase with intention of using
1242  * the position for a seek after freezing. Not clear if anyone needs that.
1243  */
1244 void
1246  long *blocknum, int *offset)
1247 {
1248  LogicalTape *lt;
1249 
1250  Assert(tapenum >= 0 && tapenum < lts->nTapes);
1251  lt = &lts->tapes[tapenum];
1252 
1253  if (lt->buffer == NULL)
1254  ltsInitReadBuffer(lts, lt);
1255 
1256  Assert(lt->offsetBlockNumber == 0L);
1257 
1258  /* With a larger buffer, 'pos' wouldn't be the same as offset within page */
1259  Assert(lt->buffer_size == BLCKSZ);
1260 
1261  *blocknum = lt->curBlockNumber;
1262  *offset = lt->pos;
1263 }
1264 
1265 /*
1266  * Obtain total disk space currently used by a LogicalTapeSet, in blocks.
1267  *
1268  * This should not be called while there are open write buffers; otherwise it
1269  * may not account for buffered data.
1270  */
1271 long
1273 {
1274 #ifdef USE_ASSERT_CHECKING
1275  for (int i = 0; i < lts->nTapes; i++)
1276  {
1277  LogicalTape *lt = &lts->tapes[i];
1278 
1279  Assert(!lt->writing || lt->buffer == NULL);
1280  }
1281 #endif
1282  return lts->nBlocksWritten - lts->nHoleBlocks;
1283 }
LogicalTapeSet * LogicalTapeSetCreate(int ntapes, bool preallocate, TapeShare *shared, SharedFileSet *fileset, int worker)
Definition: logtape.c:685
int max_size
Definition: logtape.c:165
size_t LogicalTapeRead(LogicalTapeSet *lts, int tapenum, void *ptr, size_t size)
Definition: logtape.c:977
#define VALGRIND_MAKE_MEM_DEFINED(addr, size)
Definition: memdebug.h:26
#define TapeBlockIsLast(buf)
Definition: logtape.c:108
#define TapeBlockPayloadSize
Definition: logtape.c:104
long offsetBlockNumber
Definition: logtape.c:158
long nBlocksWritten
Definition: logtape.c:200
int64 BufFileSize(BufFile *file)
Definition: buffile.c:794
static bool ltsReadFillBuffer(LogicalTapeSet *lts, LogicalTape *lt)
Definition: logtape.c:307
long * prealloc
Definition: logtape.c:174
#define Min(x, y)
Definition: c.h:986
static long ltsGetFreeBlock(LogicalTapeSet *lts)
Definition: logtape.c:390
BufFile * pfile
Definition: logtape.c:187
#define MemSet(start, val, len)
Definition: c.h:1008
int prealloc_size
Definition: logtape.c:176
static void ltsReleaseBlock(LogicalTapeSet *lts, long blocknum)
Definition: logtape.c:485
long firstblocknumber
Definition: logtape.h:50
bool frozen
Definition: logtape.c:141
long nextBlockNumber
Definition: logtape.c:157
void BufFileClose(BufFile *file)
Definition: buffile.c:395
void LogicalTapeRewindForWrite(LogicalTapeSet *lts, int tapenum)
Definition: logtape.c:951
static void swap_nodes(long *heap, unsigned long a, unsigned long b)
Definition: logtape.c:346
bool writing
Definition: logtape.c:140
long * freeBlocks
Definition: logtape.c:212
bool dirty
Definition: logtape.c:142
char data[BLCKSZ]
Definition: c.h:1141
void pfree(void *pointer)
Definition: mcxt.c:1169
void LogicalTapeWrite(LogicalTapeSet *lts, int tapenum, void *ptr, size_t size)
Definition: logtape.c:775
#define ERROR
Definition: elog.h:46
BufFile * BufFileCreateTemp(bool interXact)
Definition: buffile.c:188
#define MAXPGPATH
BufFile * BufFileCreateShared(SharedFileSet *fileset, const char *name)
Definition: buffile.c:262
size_t LogicalTapeBackspace(LogicalTapeSet *lts, int tapenum, size_t size)
Definition: logtape.c:1137
int errcode_for_file_access(void)
Definition: elog.c:721
long nHoleBlocks
Definition: logtape.c:201
int nbytes
Definition: logtape.c:167
static unsigned long right_offset(unsigned i)
Definition: logtape.c:362
void LogicalTapeTell(LogicalTapeSet *lts, int tapenum, long *blocknum, int *offset)
Definition: logtape.c:1245
int nprealloc
Definition: logtape.c:175
BufFile * BufFileOpenShared(SharedFileSet *fileset, const char *name, int mode)
Definition: buffile.c:284
static unsigned long left_offset(unsigned long i)
Definition: logtape.c:356
#define MaxAllocSize
Definition: memutils.h:40
static void ltsInitTape(LogicalTape *lt)
Definition: logtape.c:626
long firstBlockNumber
Definition: logtape.c:155
long nFreeBlocks
Definition: logtape.c:213
#define TAPE_WRITE_PREALLOC_MAX
Definition: logtape.c:126
int BufFileSeekBlock(BufFile *file, long blknum)
Definition: buffile.c:761
#define TAPE_WRITE_PREALLOC_MIN
Definition: logtape.c:125
#define TapeBlockSetNBytes(buf, nbytes)
Definition: logtape.c:112
static void ltsInitReadBuffer(LogicalTapeSet *lts, LogicalTape *lt)
Definition: logtape.c:652
static void ltsConcatWorkerTapes(LogicalTapeSet *lts, TapeShare *shared, SharedFileSet *fileset)
Definition: logtape.c:542
#define ereport(elevel,...)
Definition: elog.h:157
static long ltsGetPreallocBlock(LogicalTapeSet *lts, LogicalTape *lt)
Definition: logtape.c:447
long curBlockNumber
Definition: logtape.c:156
#define Assert(condition)
Definition: c.h:804
bool enable_prealloc
Definition: logtape.c:215
struct TapeBlockTrailer TapeBlockTrailer
static long ltsGetBlock(LogicalTapeSet *lts, LogicalTape *lt)
Definition: logtape.c:377
size_t Size
Definition: c.h:540
void BufFileExportShared(BufFile *file)
Definition: buffile.c:377
LogicalTape * tapes
Definition: logtape.c:219
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1182
void LogicalTapeRewindForRead(LogicalTapeSet *lts, int tapenum, size_t buffer_size)
Definition: logtape.c:863
#define TapeBlockGetNBytes(buf)
Definition: logtape.c:109
void LogicalTapeFreeze(LogicalTapeSet *lts, int tapenum, TapeShare *share)
Definition: logtape.c:1034
char * buffer
Definition: logtape.c:163
static char * filename
Definition: pg_dumpall.c:91
void * palloc(Size size)
Definition: mcxt.c:1062
int errmsg(const char *fmt,...)
Definition: elog.c:909
#define elog(elevel,...)
Definition: elog.h:232
bool forgetFreeSpace
Definition: logtape.c:211
int i
size_t BufFileRead(BufFile *file, void *ptr, size_t size)
Definition: buffile.c:543
void LogicalTapeSetClose(LogicalTapeSet *lts)
Definition: logtape.c:737
void LogicalTapeSeek(LogicalTapeSet *lts, int tapenum, long blocknum, int offset)
Definition: logtape.c:1211
void LogicalTapeSetForgetFreeSpace(LogicalTapeSet *lts)
Definition: logtape.c:764
Size freeBlocksLen
Definition: logtape.c:214
long LogicalTapeSetBlocks(LogicalTapeSet *lts)
Definition: logtape.c:1272
void BufFileWrite(BufFile *file, void *ptr, size_t size)
Definition: buffile.c:586
struct LogicalTape LogicalTape
static unsigned long parent_offset(unsigned long i)
Definition: logtape.c:368
long BufFileAppend(BufFile *target, BufFile *source)
Definition: buffile.c:833
long nBlocksAllocated
Definition: logtape.c:199
void LogicalTapeSetExtend(LogicalTapeSet *lts, int nAdditional)
Definition: logtape.c:1109
int buffer_size
Definition: logtape.c:164
static void ltsWriteBlock(LogicalTapeSet *lts, long blocknum, void *buffer)
Definition: logtape.c:240
#define TapeBlockGetTrailer(buf)
Definition: logtape.c:105
static void ltsReadBlock(LogicalTapeSet *lts, long blocknum, void *buffer)
Definition: logtape.c:284
int pg_itoa(int16 i, char *a)
Definition: numutils.c:338