]> granicus.if.org Git - postgresql/blob - src/backend/utils/sort/tuplestore.c
Improve trace_sort code to also show the total memory or disk space used.
[postgresql] / src / backend / utils / sort / tuplestore.c
1 /*-------------------------------------------------------------------------
2  *
3  * tuplestore.c
4  *        Generalized routines for temporary tuple storage.
5  *
6  * This module handles temporary storage of tuples for purposes such
7  * as Materialize nodes, hashjoin batch files, etc.  It is essentially
8  * a dumbed-down version of tuplesort.c; it does no sorting of tuples
9  * but can only store and regurgitate a sequence of tuples.  However,
10  * because no sort is required, it is allowed to start reading the sequence
11  * before it has all been written.      This is particularly useful for cursors,
12  * because it allows random access within the already-scanned portion of
13  * a query without having to process the underlying scan to completion.
14  * A temporary file is used to handle the data if it exceeds the
15  * space limit specified by the caller.
16  *
17  * The (approximate) amount of memory allowed to the tuplestore is specified
18  * in kilobytes by the caller.  We absorb tuples and simply store them in an
19  * in-memory array as long as we haven't exceeded maxKBytes.  If we do exceed
20  * maxKBytes, we dump all the tuples into a temp file and then read from that
21  * when needed.
22  *
23  * When the caller requests random access to the data, we write the temp file
24  * in a format that allows either forward or backward scan.  Otherwise, only
25  * forward scan is allowed.  But rewind and markpos/restorepos are allowed
26  * in any case.
27  *
28  * Because we allow reading before writing is complete, there are two
29  * interesting positions in the temp file: the current read position and
30  * the current write position.  At any given instant, the temp file's seek
31  * position corresponds to one of these, and the other one is remembered in
32  * the Tuplestore's state.
33  *
34  *
35  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
36  * Portions Copyright (c) 1994, Regents of the University of California
37  *
38  * IDENTIFICATION
39  *        $PostgreSQL: pgsql/src/backend/utils/sort/tuplestore.c,v 1.23 2005/10/15 02:49:37 momjian Exp $
40  *
41  *-------------------------------------------------------------------------
42  */
43
44 #include "postgres.h"
45
46 #include "access/heapam.h"
47 #include "storage/buffile.h"
48 #include "utils/memutils.h"
49 #include "utils/tuplestore.h"
50
51
52 /*
53  * Possible states of a Tuplestore object.      These denote the states that
54  * persist between calls of Tuplestore routines.
55  */
56 typedef enum
57 {
58         TSS_INMEM,                                      /* Tuples still fit in memory */
59         TSS_WRITEFILE,                          /* Writing to temp file */
60         TSS_READFILE                            /* Reading from temp file */
61 } TupStoreStatus;
62
63 /*
64  * Private state of a Tuplestore operation.
65  */
66 struct Tuplestorestate
67 {
68         TupStoreStatus status;          /* enumerated value as shown above */
69         bool            randomAccess;   /* did caller request random access? */
70         bool            interXact;              /* keep open through transactions? */
71         long            availMem;               /* remaining memory available, in bytes */
72         BufFile    *myfile;                     /* underlying file, or NULL if none */
73
74         /*
75          * These function pointers decouple the routines that must know what kind
76          * of tuple we are handling from the routines that don't need to know it.
77          * They are set up by the tuplestore_begin_xxx routines.
78          *
79          * (Although tuplestore.c currently only supports heap tuples, I've copied
80          * this part of tuplesort.c so that extension to other kinds of objects
81          * will be easy if it's ever needed.)
82          *
83          * Function to copy a supplied input tuple into palloc'd space. (NB: we
84          * assume that a single pfree() is enough to release the tuple later, so
85          * the representation must be "flat" in one palloc chunk.) state->availMem
86          * must be decreased by the amount of space used.
87          */
88         void       *(*copytup) (Tuplestorestate *state, void *tup);
89
90         /*
91          * Function to write a stored tuple onto tape.  The representation of the
92          * tuple on tape need not be the same as it is in memory; requirements on
93          * the tape representation are given below.  After writing the tuple,
94          * pfree() it, and increase state->availMem by the amount of memory space
95          * thereby released.
96          */
97         void            (*writetup) (Tuplestorestate *state, void *tup);
98
99         /*
100          * Function to read a stored tuple from tape back into memory. 'len' is
101          * the already-read length of the stored tuple.  Create and return a
102          * palloc'd copy, and decrease state->availMem by the amount of memory
103          * space consumed.
104          */
105         void       *(*readtup) (Tuplestorestate *state, unsigned int len);
106
107         /*
108          * This array holds pointers to tuples in memory if we are in state INMEM.
109          * In states WRITEFILE and READFILE it's not used.
110          */
111         void      **memtuples;          /* array of pointers to palloc'd tuples */
112         int                     memtupcount;    /* number of tuples currently present */
113         int                     memtupsize;             /* allocated length of memtuples array */
114
115         /*
116          * These variables are used to keep track of the current position.
117          *
118          * In state WRITEFILE, the current file seek position is the write point, and
119          * the read position is remembered in readpos_xxx; in state READFILE, the
120          * current file seek position is the read point, and the write position is
121          * remembered in writepos_xxx.  (The write position is the same as EOF,
122          * but since BufFileSeek doesn't currently implement SEEK_END, we have to
123          * remember it explicitly.)
124          *
125          * Special case: if we are in WRITEFILE state and eof_reached is true, then
126          * the read position is implicitly equal to the write position (and hence
127          * to the file seek position); this way we need not update the readpos_xxx
128          * variables on each write.
129          */
130         bool            eof_reached;    /* read reached EOF (always valid) */
131         int                     current;                /* next array index (valid if INMEM) */
132         int                     readpos_file;   /* file# (valid if WRITEFILE and not eof) */
133         long            readpos_offset; /* offset (valid if WRITEFILE and not eof) */
134         int                     writepos_file;  /* file# (valid if READFILE) */
135         long            writepos_offset;        /* offset (valid if READFILE) */
136
137         /* markpos_xxx holds marked position for mark and restore */
138         int                     markpos_current;        /* saved "current" */
139         int                     markpos_file;   /* saved "readpos_file" */
140         long            markpos_offset; /* saved "readpos_offset" */
141 };
142
143 #define COPYTUP(state,tup)      ((*(state)->copytup) (state, tup))
144 #define WRITETUP(state,tup) ((*(state)->writetup) (state, tup))
145 #define READTUP(state,len)      ((*(state)->readtup) (state, len))
146 #define LACKMEM(state)          ((state)->availMem < 0)
147 #define USEMEM(state,amt)       ((state)->availMem -= (amt))
148 #define FREEMEM(state,amt)      ((state)->availMem += (amt))
149
150 /*--------------------
151  *
152  * NOTES about on-tape representation of tuples:
153  *
154  * We require the first "unsigned int" of a stored tuple to be the total size
155  * on-tape of the tuple, including itself (so it is never zero).
156  * The remainder of the stored tuple
157  * may or may not match the in-memory representation of the tuple ---
158  * any conversion needed is the job of the writetup and readtup routines.
159  *
160  * If state->randomAccess is true, then the stored representation of the
161  * tuple must be followed by another "unsigned int" that is a copy of the
162  * length --- so the total tape space used is actually sizeof(unsigned int)
163  * more than the stored length value.  This allows read-backwards.      When
164  * randomAccess is not true, the write/read routines may omit the extra
165  * length word.
166  *
167  * writetup is expected to write both length words as well as the tuple
168  * data.  When readtup is called, the tape is positioned just after the
169  * front length word; readtup must read the tuple data and advance past
170  * the back length word (if present).
171  *
172  * The write/read routines can make use of the tuple description data
173  * stored in the Tuplestorestate record, if needed. They are also expected
174  * to adjust state->availMem by the amount of memory space (not tape space!)
175  * released or consumed.  There is no error return from either writetup
176  * or readtup; they should ereport() on failure.
177  *
178  *
179  * NOTES about memory consumption calculations:
180  *
181  * We count space allocated for tuples against the maxKBytes limit,
182  * plus the space used by the variable-size array memtuples.
183  * Fixed-size space (primarily the BufFile I/O buffer) is not counted.
184  *
185  * Note that we count actual space used (as shown by GetMemoryChunkSpace)
186  * rather than the originally-requested size.  This is important since
187  * palloc can add substantial overhead.  It's not a complete answer since
188  * we won't count any wasted space in palloc allocation blocks, but it's
189  * a lot better than what we were doing before 7.3.
190  *
191  *--------------------
192  */
193
194
195 static Tuplestorestate *tuplestore_begin_common(bool randomAccess,
196                                                 bool interXact,
197                                                 int maxKBytes);
198 static void dumptuples(Tuplestorestate *state);
199 static unsigned int getlen(Tuplestorestate *state, bool eofOK);
200 static void *copytup_heap(Tuplestorestate *state, void *tup);
201 static void writetup_heap(Tuplestorestate *state, void *tup);
202 static void *readtup_heap(Tuplestorestate *state, unsigned int len);
203
204
205 /*
206  *              tuplestore_begin_xxx
207  *
208  * Initialize for a tuple store operation.
209  */
210 static Tuplestorestate *
211 tuplestore_begin_common(bool randomAccess, bool interXact, int maxKBytes)
212 {
213         Tuplestorestate *state;
214
215         state = (Tuplestorestate *) palloc0(sizeof(Tuplestorestate));
216
217         state->status = TSS_INMEM;
218         state->randomAccess = randomAccess;
219         state->interXact = interXact;
220         state->availMem = maxKBytes * 1024L;
221         state->myfile = NULL;
222
223         state->memtupcount = 0;
224         state->memtupsize = 1024;       /* initial guess */
225         state->memtuples = (void **) palloc(state->memtupsize * sizeof(void *));
226
227         USEMEM(state, GetMemoryChunkSpace(state->memtuples));
228
229         state->eof_reached = false;
230         state->current = 0;
231
232         return state;
233 }
234
235 /*
236  * tuplestore_begin_heap
237  *
238  * Create a new tuplestore; other types of tuple stores (other than
239  * "heap" tuple stores, for heap tuples) are possible, but not presently
240  * implemented.
241  *
242  * randomAccess: if true, both forward and backward accesses to the
243  * tuple store are allowed.
244  *
245  * interXact: if true, the files used for on-disk storage persist beyond the
246  * end of the current transaction.      NOTE: It's the caller's responsibility to
247  * create such a tuplestore in a memory context that will also survive
248  * transaction boundaries, and to ensure the tuplestore is closed when it's
249  * no longer wanted.
250  *
251  * maxKBytes: how much data to store in memory (any data beyond this
252  * amount is paged to disk).  When in doubt, use work_mem.
253  */
254 Tuplestorestate *
255 tuplestore_begin_heap(bool randomAccess, bool interXact, int maxKBytes)
256 {
257         Tuplestorestate *state = tuplestore_begin_common(randomAccess,
258                                                                                                          interXact,
259                                                                                                          maxKBytes);
260
261         state->copytup = copytup_heap;
262         state->writetup = writetup_heap;
263         state->readtup = readtup_heap;
264
265         return state;
266 }
267
268 /*
269  * tuplestore_end
270  *
271  *      Release resources and clean up.
272  */
273 void
274 tuplestore_end(Tuplestorestate *state)
275 {
276         int                     i;
277
278         if (state->myfile)
279                 BufFileClose(state->myfile);
280         if (state->memtuples)
281         {
282                 for (i = 0; i < state->memtupcount; i++)
283                         pfree(state->memtuples[i]);
284                 pfree(state->memtuples);
285         }
286 }
287
288 /*
289  * tuplestore_ateof
290  *
291  * Returns the current eof_reached state.
292  */
293 bool
294 tuplestore_ateof(Tuplestorestate *state)
295 {
296         return state->eof_reached;
297 }
298
299 /*
300  * Accept one tuple and append it to the tuplestore.
301  *
302  * Note that the input tuple is always copied; the caller need not save it.
303  *
304  * If the read status is currently "AT EOF" then it remains so (the read
305  * pointer advances along with the write pointer); otherwise the read
306  * pointer is unchanged.  This is for the convenience of nodeMaterial.c.
307  */
308 void
309 tuplestore_puttuple(Tuplestorestate *state, void *tuple)
310 {
311         /*
312          * Copy the tuple.      (Must do this even in WRITEFILE case.)
313          */
314         tuple = COPYTUP(state, tuple);
315
316         switch (state->status)
317         {
318                 case TSS_INMEM:
319                         /* Grow the array as needed */
320                         if (state->memtupcount >= state->memtupsize)
321                         {
322                                 FREEMEM(state, GetMemoryChunkSpace(state->memtuples));
323                                 state->memtupsize *= 2;
324                                 state->memtuples = (void **)
325                                         repalloc(state->memtuples,
326                                                          state->memtupsize * sizeof(void *));
327                                 USEMEM(state, GetMemoryChunkSpace(state->memtuples));
328                         }
329
330                         /* Stash the tuple in the in-memory array */
331                         state->memtuples[state->memtupcount++] = tuple;
332
333                         /* If eof_reached, keep read position in sync */
334                         if (state->eof_reached)
335                                 state->current = state->memtupcount;
336
337                         /*
338                          * Done if we still fit in available memory.
339                          */
340                         if (!LACKMEM(state))
341                                 return;
342
343                         /*
344                          * Nope; time to switch to tape-based operation.
345                          */
346                         state->myfile = BufFileCreateTemp(state->interXact);
347                         state->status = TSS_WRITEFILE;
348                         dumptuples(state);
349                         break;
350                 case TSS_WRITEFILE:
351                         WRITETUP(state, tuple);
352                         break;
353                 case TSS_READFILE:
354
355                         /*
356                          * Switch from reading to writing.
357                          */
358                         if (!state->eof_reached)
359                                 BufFileTell(state->myfile,
360                                                         &state->readpos_file, &state->readpos_offset);
361                         if (BufFileSeek(state->myfile,
362                                                         state->writepos_file, state->writepos_offset,
363                                                         SEEK_SET) != 0)
364                                 elog(ERROR, "seek to EOF failed");
365                         state->status = TSS_WRITEFILE;
366                         WRITETUP(state, tuple);
367                         break;
368                 default:
369                         elog(ERROR, "invalid tuplestore state");
370                         break;
371         }
372 }
373
374 /*
375  * Fetch the next tuple in either forward or back direction.
376  * Returns NULL if no more tuples.      If should_free is set, the
377  * caller must pfree the returned tuple when done with it.
378  */
379 void *
380 tuplestore_gettuple(Tuplestorestate *state, bool forward,
381                                         bool *should_free)
382 {
383         unsigned int tuplen;
384         void       *tup;
385
386         Assert(forward || state->randomAccess);
387
388         switch (state->status)
389         {
390                 case TSS_INMEM:
391                         *should_free = false;
392                         if (forward)
393                         {
394                                 if (state->current < state->memtupcount)
395                                         return state->memtuples[state->current++];
396                                 state->eof_reached = true;
397                                 return NULL;
398                         }
399                         else
400                         {
401                                 if (state->current <= 0)
402                                         return NULL;
403
404                                 /*
405                                  * if all tuples are fetched already then we return last
406                                  * tuple, else - tuple before last returned.
407                                  */
408                                 if (state->eof_reached)
409                                         state->eof_reached = false;
410                                 else
411                                 {
412                                         state->current--;       /* last returned tuple */
413                                         if (state->current <= 0)
414                                                 return NULL;
415                                 }
416                                 return state->memtuples[state->current - 1];
417                         }
418                         break;
419
420                 case TSS_WRITEFILE:
421                         /* Skip state change if we'll just return NULL */
422                         if (state->eof_reached && forward)
423                                 return NULL;
424
425                         /*
426                          * Switch from writing to reading.
427                          */
428                         BufFileTell(state->myfile,
429                                                 &state->writepos_file, &state->writepos_offset);
430                         if (!state->eof_reached)
431                                 if (BufFileSeek(state->myfile,
432                                                                 state->readpos_file, state->readpos_offset,
433                                                                 SEEK_SET) != 0)
434                                         elog(ERROR, "seek failed");
435                         state->status = TSS_READFILE;
436                         /* FALL THRU into READFILE case */
437
438                 case TSS_READFILE:
439                         *should_free = true;
440                         if (forward)
441                         {
442                                 if ((tuplen = getlen(state, true)) != 0)
443                                 {
444                                         tup = READTUP(state, tuplen);
445                                         return tup;
446                                 }
447                                 else
448                                 {
449                                         state->eof_reached = true;
450                                         return NULL;
451                                 }
452                         }
453
454                         /*
455                          * Backward.
456                          *
457                          * if all tuples are fetched already then we return last tuple, else
458                          * - tuple before last returned.
459                          *
460                          * Back up to fetch previously-returned tuple's ending length word.
461                          * If seek fails, assume we are at start of file.
462                          */
463                         if (BufFileSeek(state->myfile, 0, -(long) sizeof(unsigned int),
464                                                         SEEK_CUR) != 0)
465                                 return NULL;
466                         tuplen = getlen(state, false);
467
468                         if (state->eof_reached)
469                         {
470                                 state->eof_reached = false;
471                                 /* We will return the tuple returned before returning NULL */
472                         }
473                         else
474                         {
475                                 /*
476                                  * Back up to get ending length word of tuple before it.
477                                  */
478                                 if (BufFileSeek(state->myfile, 0,
479                                                                 -(long) (tuplen + 2 * sizeof(unsigned int)),
480                                                                 SEEK_CUR) != 0)
481                                 {
482                                         /*
483                                          * If that fails, presumably the prev tuple is the first
484                                          * in the file.  Back up so that it becomes next to read
485                                          * in forward direction (not obviously right, but that is
486                                          * what in-memory case does).
487                                          */
488                                         if (BufFileSeek(state->myfile, 0,
489                                                                         -(long) (tuplen + sizeof(unsigned int)),
490                                                                         SEEK_CUR) != 0)
491                                                 elog(ERROR, "bogus tuple length in backward scan");
492                                         return NULL;
493                                 }
494                                 tuplen = getlen(state, false);
495                         }
496
497                         /*
498                          * Now we have the length of the prior tuple, back up and read it.
499                          * Note: READTUP expects we are positioned after the initial
500                          * length word of the tuple, so back up to that point.
501                          */
502                         if (BufFileSeek(state->myfile, 0,
503                                                         -(long) tuplen,
504                                                         SEEK_CUR) != 0)
505                                 elog(ERROR, "bogus tuple length in backward scan");
506                         tup = READTUP(state, tuplen);
507                         return tup;
508
509                 default:
510                         elog(ERROR, "invalid tuplestore state");
511                         return NULL;            /* keep compiler quiet */
512         }
513 }
514
515 /*
516  * dumptuples - remove tuples from memory and write to tape
517  *
518  * As a side effect, we must set readpos and markpos to the value
519  * corresponding to "current"; otherwise, a dump would lose the current read
520  * position.
521  */
522 static void
523 dumptuples(Tuplestorestate *state)
524 {
525         int                     i;
526
527         for (i = 0;; i++)
528         {
529                 if (i == state->current)
530                         BufFileTell(state->myfile,
531                                                 &state->readpos_file, &state->readpos_offset);
532                 if (i == state->markpos_current)
533                         BufFileTell(state->myfile,
534                                                 &state->markpos_file, &state->markpos_offset);
535                 if (i >= state->memtupcount)
536                         break;
537                 WRITETUP(state, state->memtuples[i]);
538         }
539         state->memtupcount = 0;
540 }
541
542 /*
543  * tuplestore_rescan            - rewind and replay the scan
544  */
545 void
546 tuplestore_rescan(Tuplestorestate *state)
547 {
548         switch (state->status)
549         {
550                 case TSS_INMEM:
551                         state->eof_reached = false;
552                         state->current = 0;
553                         break;
554                 case TSS_WRITEFILE:
555                         state->eof_reached = false;
556                         state->readpos_file = 0;
557                         state->readpos_offset = 0L;
558                         break;
559                 case TSS_READFILE:
560                         state->eof_reached = false;
561                         if (BufFileSeek(state->myfile, 0, 0L, SEEK_SET) != 0)
562                                 elog(ERROR, "seek to start failed");
563                         break;
564                 default:
565                         elog(ERROR, "invalid tuplestore state");
566                         break;
567         }
568 }
569
570 /*
571  * tuplestore_markpos   - saves current position in the tuple sequence
572  */
573 void
574 tuplestore_markpos(Tuplestorestate *state)
575 {
576         switch (state->status)
577         {
578                 case TSS_INMEM:
579                         state->markpos_current = state->current;
580                         break;
581                 case TSS_WRITEFILE:
582                         if (state->eof_reached)
583                         {
584                                 /* Need to record the implicit read position */
585                                 BufFileTell(state->myfile,
586                                                         &state->markpos_file,
587                                                         &state->markpos_offset);
588                         }
589                         else
590                         {
591                                 state->markpos_file = state->readpos_file;
592                                 state->markpos_offset = state->readpos_offset;
593                         }
594                         break;
595                 case TSS_READFILE:
596                         BufFileTell(state->myfile,
597                                                 &state->markpos_file,
598                                                 &state->markpos_offset);
599                         break;
600                 default:
601                         elog(ERROR, "invalid tuplestore state");
602                         break;
603         }
604 }
605
606 /*
607  * tuplestore_restorepos - restores current position in tuple sequence to
608  *                                                last saved position
609  */
610 void
611 tuplestore_restorepos(Tuplestorestate *state)
612 {
613         switch (state->status)
614         {
615                 case TSS_INMEM:
616                         state->eof_reached = false;
617                         state->current = state->markpos_current;
618                         break;
619                 case TSS_WRITEFILE:
620                         state->eof_reached = false;
621                         state->readpos_file = state->markpos_file;
622                         state->readpos_offset = state->markpos_offset;
623                         break;
624                 case TSS_READFILE:
625                         state->eof_reached = false;
626                         if (BufFileSeek(state->myfile,
627                                                         state->markpos_file,
628                                                         state->markpos_offset,
629                                                         SEEK_SET) != 0)
630                                 elog(ERROR, "tuplestore_restorepos failed");
631                         break;
632                 default:
633                         elog(ERROR, "invalid tuplestore state");
634                         break;
635         }
636 }
637
638
639 /*
640  * Tape interface routines
641  */
642
643 static unsigned int
644 getlen(Tuplestorestate *state, bool eofOK)
645 {
646         unsigned int len;
647         size_t          nbytes;
648
649         nbytes = BufFileRead(state->myfile, (void *) &len, sizeof(len));
650         if (nbytes == sizeof(len))
651                 return len;
652         if (nbytes != 0)
653                 elog(ERROR, "unexpected end of tape");
654         if (!eofOK)
655                 elog(ERROR, "unexpected end of data");
656         return 0;
657 }
658
659
660 /*
661  * Routines specialized for HeapTuple case
662  */
663
664 static void *
665 copytup_heap(Tuplestorestate *state, void *tup)
666 {
667         HeapTuple       tuple = (HeapTuple) tup;
668
669         tuple = heap_copytuple(tuple);
670         USEMEM(state, GetMemoryChunkSpace(tuple));
671         return (void *) tuple;
672 }
673
674 /*
675  * We don't bother to write the HeapTupleData part of the tuple.
676  */
677
678 static void
679 writetup_heap(Tuplestorestate *state, void *tup)
680 {
681         HeapTuple       tuple = (HeapTuple) tup;
682         unsigned int tuplen;
683
684         tuplen = tuple->t_len + sizeof(tuplen);
685         if (BufFileWrite(state->myfile, (void *) &tuplen,
686                                          sizeof(tuplen)) != sizeof(tuplen))
687                 elog(ERROR, "write failed");
688         if (BufFileWrite(state->myfile, (void *) tuple->t_data,
689                                          tuple->t_len) != (size_t) tuple->t_len)
690                 elog(ERROR, "write failed");
691         if (state->randomAccess)        /* need trailing length word? */
692                 if (BufFileWrite(state->myfile, (void *) &tuplen,
693                                                  sizeof(tuplen)) != sizeof(tuplen))
694                         elog(ERROR, "write failed");
695
696         FREEMEM(state, GetMemoryChunkSpace(tuple));
697         heap_freetuple(tuple);
698 }
699
700 static void *
701 readtup_heap(Tuplestorestate *state, unsigned int len)
702 {
703         unsigned int tuplen = len - sizeof(unsigned int) + HEAPTUPLESIZE;
704         HeapTuple       tuple = (HeapTuple) palloc(tuplen);
705
706         USEMEM(state, GetMemoryChunkSpace(tuple));
707         /* reconstruct the HeapTupleData portion */
708         tuple->t_len = len - sizeof(unsigned int);
709         ItemPointerSetInvalid(&(tuple->t_self));
710         tuple->t_datamcxt = CurrentMemoryContext;
711         tuple->t_data = (HeapTupleHeader) (((char *) tuple) + HEAPTUPLESIZE);
712         /* read in the tuple proper */
713         if (BufFileRead(state->myfile, (void *) tuple->t_data,
714                                         tuple->t_len) != (size_t) tuple->t_len)
715                 elog(ERROR, "unexpected end of data");
716         if (state->randomAccess)        /* need trailing length word? */
717                 if (BufFileRead(state->myfile, (void *) &tuplen,
718                                                 sizeof(tuplen)) != sizeof(tuplen))
719                         elog(ERROR, "unexpected end of data");
720         return (void *) tuple;
721 }