@@ -27,6 +27,9 @@ static struct varlena *toast_fetch_datum(struct varlena *attr);
2727static struct varlena * toast_fetch_datum_slice (struct varlena * attr ,
2828 int32 sliceoffset ,
2929 int32 slicelength );
30+ static void heap_fetch_toast_slice (Relation toastrel , Oid valueid ,
31+ int32 attrsize , int32 sliceoffset ,
32+ int32 slicelength , struct varlena * result );
3033static struct varlena * toast_decompress_datum (struct varlena * attr );
3134static struct varlena * toast_decompress_datum_slice (struct varlena * attr , int32 slicelength );
3235
@@ -325,19 +328,9 @@ static struct varlena *
325328toast_fetch_datum (struct varlena * attr )
326329{
327330 Relation toastrel ;
328- Relation * toastidxs ;
329- ScanKeyData toastkey ;
330- SysScanDesc toastscan ;
331- HeapTuple ttup ;
332- TupleDesc toasttupDesc ;
333331 struct varlena * result ;
334332 struct varatt_external toast_pointer ;
335333 int32 attrsize ;
336- int32 expectedchunk ;
337- int32 totalchunks ;
338- int num_indexes ;
339- int validIndex ;
340- SnapshotData SnapshotToast ;
341334
342335 if (!VARATT_IS_EXTERNAL_ONDISK (attr ))
343336 elog (ERROR , "toast_fetch_datum shouldn't be called for non-ondisk datums" );
@@ -346,7 +339,6 @@ toast_fetch_datum(struct varlena *attr)
346339 VARATT_EXTERNAL_GET_POINTER (toast_pointer , attr );
347340
348341 attrsize = toast_pointer .va_extsize ;
349- totalchunks = ((attrsize - 1 ) / TOAST_MAX_CHUNK_SIZE ) + 1 ;
350342
351343 result = (struct varlena * ) palloc (attrsize + VARHDRSZ );
352344
@@ -355,130 +347,19 @@ toast_fetch_datum(struct varlena *attr)
355347 else
356348 SET_VARSIZE (result , attrsize + VARHDRSZ );
357349
350+ if (attrsize == 0 )
351+ return result ; /* Probably shouldn't happen, but just in case. */
352+
358353 /*
359354 * Open the toast relation and its indexes
360355 */
361356 toastrel = table_open (toast_pointer .va_toastrelid , AccessShareLock );
362- toasttupDesc = toastrel -> rd_att ;
363-
364- /* Look for the valid index of the toast relation */
365- validIndex = toast_open_indexes (toastrel ,
366- AccessShareLock ,
367- & toastidxs ,
368- & num_indexes );
369357
370- /*
371- * Setup a scan key to fetch from the index by va_valueid
372- */
373- ScanKeyInit (& toastkey ,
374- (AttrNumber ) 1 ,
375- BTEqualStrategyNumber , F_OIDEQ ,
376- ObjectIdGetDatum (toast_pointer .va_valueid ));
358+ /* Fetch all chunks */
359+ heap_fetch_toast_slice (toastrel , toast_pointer .va_valueid , attrsize , 0 ,
360+ attrsize , result );
377361
378- /*
379- * Read the chunks by index
380- *
381- * Note that because the index is actually on (valueid, chunkidx) we will
382- * see the chunks in chunkidx order, even though we didn't explicitly ask
383- * for it.
384- */
385- expectedchunk = 0 ;
386-
387- init_toast_snapshot (& SnapshotToast );
388- toastscan = systable_beginscan_ordered (toastrel , toastidxs [validIndex ],
389- & SnapshotToast , 1 , & toastkey );
390- while ((ttup = systable_getnext_ordered (toastscan , ForwardScanDirection )) != NULL )
391- {
392- int32 curchunk ;
393- Pointer chunk ;
394- bool isnull ;
395- char * chunkdata ;
396- int32 chunksize ;
397- int32 expected_size ;
398-
399- /*
400- * Have a chunk, extract the sequence number and the data
401- */
402- curchunk = DatumGetInt32 (fastgetattr (ttup , 2 , toasttupDesc , & isnull ));
403- Assert (!isnull );
404- chunk = DatumGetPointer (fastgetattr (ttup , 3 , toasttupDesc , & isnull ));
405- Assert (!isnull );
406- if (!VARATT_IS_EXTENDED (chunk ))
407- {
408- chunksize = VARSIZE (chunk ) - VARHDRSZ ;
409- chunkdata = VARDATA (chunk );
410- }
411- else if (VARATT_IS_SHORT (chunk ))
412- {
413- /* could happen due to heap_form_tuple doing its thing */
414- chunksize = VARSIZE_SHORT (chunk ) - VARHDRSZ_SHORT ;
415- chunkdata = VARDATA_SHORT (chunk );
416- }
417- else
418- {
419- /* should never happen */
420- elog (ERROR , "found toasted toast chunk for toast value %u in %s" ,
421- toast_pointer .va_valueid ,
422- RelationGetRelationName (toastrel ));
423- chunksize = 0 ; /* keep compiler quiet */
424- chunkdata = NULL ;
425- }
426-
427- /*
428- * Some checks on the data we've found
429- */
430- if (curchunk != expectedchunk )
431- ereport (ERROR ,
432- (errcode (ERRCODE_DATA_CORRUPTED ),
433- errmsg_internal ("unexpected chunk number %d (expected %d) for toast value %u in %s" ,
434- curchunk , expectedchunk ,
435- toast_pointer .va_valueid ,
436- RelationGetRelationName (toastrel ))));
437- if (curchunk > totalchunks - 1 )
438- ereport (ERROR ,
439- (errcode (ERRCODE_DATA_CORRUPTED ),
440- errmsg_internal ("unexpected chunk number %d (out of range %d..%d) for toast value %u in %s" ,
441- curchunk ,
442- 0 , totalchunks - 1 ,
443- toast_pointer .va_valueid ,
444- RelationGetRelationName (toastrel ))));
445- expected_size = curchunk < totalchunks - 1 ? TOAST_MAX_CHUNK_SIZE
446- : attrsize - ((totalchunks - 1 ) * TOAST_MAX_CHUNK_SIZE );
447- if (chunksize != expected_size )
448- ereport (ERROR ,
449- (errcode (ERRCODE_DATA_CORRUPTED ),
450- errmsg_internal ("unexpected chunk size %d (expected %d) in chunk %d of %d for toast value %u in %s" ,
451- chunksize , expected_size ,
452- curchunk , totalchunks ,
453- toast_pointer .va_valueid ,
454- RelationGetRelationName (toastrel ))));
455-
456- /*
457- * Copy the data into proper place in our result
458- */
459- memcpy (VARDATA (result ) + curchunk * TOAST_MAX_CHUNK_SIZE ,
460- chunkdata ,
461- chunksize );
462-
463- expectedchunk ++ ;
464- }
465-
466- /*
467- * Final checks that we successfully fetched the datum
468- */
469- if (expectedchunk != totalchunks )
470- ereport (ERROR ,
471- (errcode (ERRCODE_DATA_CORRUPTED ),
472- errmsg_internal ("missing chunk number %d for toast value %u in %s" ,
473- expectedchunk ,
474- toast_pointer .va_valueid ,
475- RelationGetRelationName (toastrel ))));
476-
477- /*
478- * End scan and close relations
479- */
480- systable_endscan_ordered (toastscan );
481- toast_close_indexes (toastidxs , num_indexes , AccessShareLock );
362+ /* Close toast table */
482363 table_close (toastrel , AccessShareLock );
483364
484365 return result ;
@@ -500,22 +381,9 @@ toast_fetch_datum_slice(struct varlena *attr, int32 sliceoffset,
500381 int32 slicelength )
501382{
502383 Relation toastrel ;
503- Relation * toastidxs ;
504- ScanKeyData toastkey [3 ];
505- int nscankeys ;
506- SysScanDesc toastscan ;
507- HeapTuple ttup ;
508- TupleDesc toasttupDesc ;
509384 struct varlena * result ;
510385 struct varatt_external toast_pointer ;
511386 int32 attrsize ;
512- int32 expectedchunk ;
513- int startchunk ;
514- int endchunk ;
515- int totalchunks ;
516- int num_indexes ;
517- int validIndex ;
518- SnapshotData SnapshotToast ;
519387
520388 if (!VARATT_IS_EXTERNAL_ONDISK (attr ))
521389 elog (ERROR , "toast_fetch_datum_slice shouldn't be called for non-ondisk datums" );
@@ -531,7 +399,6 @@ toast_fetch_datum_slice(struct varlena *attr, int32 sliceoffset,
531399 Assert (!VARATT_EXTERNAL_IS_COMPRESSED (toast_pointer ) || 0 == sliceoffset );
532400
533401 attrsize = toast_pointer .va_extsize ;
534- totalchunks = ((attrsize - 1 ) / TOAST_MAX_CHUNK_SIZE ) + 1 ;
535402
536403 if (sliceoffset >= attrsize )
537404 {
@@ -560,35 +427,74 @@ toast_fetch_datum_slice(struct varlena *attr, int32 sliceoffset,
560427 if (slicelength == 0 )
561428 return result ; /* Can save a lot of work at this point! */
562429
563- startchunk = sliceoffset / TOAST_MAX_CHUNK_SIZE ;
564- endchunk = (sliceoffset + slicelength - 1 ) / TOAST_MAX_CHUNK_SIZE ;
565- Assert (endchunk < totalchunks );
566-
567- /*
568- * Open the toast relation and its indexes
569- */
430+ /* Open the toast relation */
570431 toastrel = table_open (toast_pointer .va_toastrelid , AccessShareLock );
571- toasttupDesc = toastrel -> rd_att ;
432+
433+ /* Fetch all chunks */
434+ heap_fetch_toast_slice (toastrel , toast_pointer .va_valueid , attrsize ,
435+ sliceoffset , slicelength , result );
436+
437+ /* Close toast table */
438+ table_close (toastrel , AccessShareLock );
439+
440+ return result ;
441+ }
442+
443+ /*
444+ * Fetch a TOAST slice from a heap table.
445+ *
446+ * toastrel is the relation from which chunks are to be fetched.
447+ * valueid identifies the TOAST value from which chunks are being fetched.
448+ * attrsize is the total size of the TOAST value.
449+ * sliceoffset is the byte offset within the TOAST value from which to fetch.
450+ * slicelength is the number of bytes to be fetched from the TOAST value.
451+ * result is the varlena into which the results should be written.
452+ */
453+ static void
454+ heap_fetch_toast_slice (Relation toastrel , Oid valueid , int32 attrsize ,
455+ int32 sliceoffset , int32 slicelength ,
456+ struct varlena * result )
457+ {
458+ Relation * toastidxs ;
459+ ScanKeyData toastkey [3 ];
460+ TupleDesc toasttupDesc = toastrel -> rd_att ;
461+ int nscankeys ;
462+ SysScanDesc toastscan ;
463+ HeapTuple ttup ;
464+ int32 expectedchunk ;
465+ int32 totalchunks = ((attrsize - 1 ) / TOAST_MAX_CHUNK_SIZE ) + 1 ;
466+ int startchunk ;
467+ int endchunk ;
468+ int num_indexes ;
469+ int validIndex ;
470+ SnapshotData SnapshotToast ;
572471
573472 /* Look for the valid index of toast relation */
574473 validIndex = toast_open_indexes (toastrel ,
575474 AccessShareLock ,
576475 & toastidxs ,
577476 & num_indexes );
578477
478+ startchunk = sliceoffset / TOAST_MAX_CHUNK_SIZE ;
479+ endchunk = (sliceoffset + slicelength - 1 ) / TOAST_MAX_CHUNK_SIZE ;
480+ Assert (endchunk <= totalchunks );
481+
579482 /*
580483 * Setup a scan key to fetch from the index. This is either two keys or
581484 * three depending on the number of chunks.
582485 */
583486 ScanKeyInit (& toastkey [0 ],
584487 (AttrNumber ) 1 ,
585488 BTEqualStrategyNumber , F_OIDEQ ,
586- ObjectIdGetDatum (toast_pointer . va_valueid ));
489+ ObjectIdGetDatum (valueid ));
587490
588491 /*
589- * Use equality condition for one chunk, a range condition otherwise:
492+ * No additional condition if fetching all chunks. Otherwise, use an
493+ * equality condition for one chunk, and a range condition otherwise.
590494 */
591- if (startchunk == endchunk )
495+ if (startchunk == 0 && endchunk == totalchunks - 1 )
496+ nscankeys = 1 ;
497+ else if (startchunk == endchunk )
592498 {
593499 ScanKeyInit (& toastkey [1 ],
594500 (AttrNumber ) 2 ,
@@ -609,15 +515,17 @@ toast_fetch_datum_slice(struct varlena *attr, int32 sliceoffset,
609515 nscankeys = 3 ;
610516 }
611517
518+ /* Prepare for scan */
519+ init_toast_snapshot (& SnapshotToast );
520+ toastscan = systable_beginscan_ordered (toastrel , toastidxs [validIndex ],
521+ & SnapshotToast , nscankeys , toastkey );
522+
612523 /*
613524 * Read the chunks by index
614525 *
615526 * The index is on (valueid, chunkidx) so they will come in order
616527 */
617- init_toast_snapshot (& SnapshotToast );
618528 expectedchunk = startchunk ;
619- toastscan = systable_beginscan_ordered (toastrel , toastidxs [validIndex ],
620- & SnapshotToast , nscankeys , toastkey );
621529 while ((ttup = systable_getnext_ordered (toastscan , ForwardScanDirection )) != NULL )
622530 {
623531 int32 curchunk ;
@@ -651,8 +559,7 @@ toast_fetch_datum_slice(struct varlena *attr, int32 sliceoffset,
651559 {
652560 /* should never happen */
653561 elog (ERROR , "found toasted toast chunk for toast value %u in %s" ,
654- toast_pointer .va_valueid ,
655- RelationGetRelationName (toastrel ));
562+ valueid , RelationGetRelationName (toastrel ));
656563 chunksize = 0 ; /* keep compiler quiet */
657564 chunkdata = NULL ;
658565 }
@@ -664,16 +571,14 @@ toast_fetch_datum_slice(struct varlena *attr, int32 sliceoffset,
664571 ereport (ERROR ,
665572 (errcode (ERRCODE_DATA_CORRUPTED ),
666573 errmsg_internal ("unexpected chunk number %d (expected %d) for toast value %u in %s" ,
667- curchunk , expectedchunk ,
668- toast_pointer .va_valueid ,
574+ curchunk , expectedchunk , valueid ,
669575 RelationGetRelationName (toastrel ))));
670576 if (curchunk > endchunk )
671577 ereport (ERROR ,
672578 (errcode (ERRCODE_DATA_CORRUPTED ),
673579 errmsg_internal ("unexpected chunk number %d (out of range %d..%d) for toast value %u in %s" ,
674580 curchunk ,
675- startchunk , endchunk ,
676- toast_pointer .va_valueid ,
581+ startchunk , endchunk , valueid ,
677582 RelationGetRelationName (toastrel ))));
678583 expected_size = curchunk < totalchunks - 1 ? TOAST_MAX_CHUNK_SIZE
679584 : attrsize - ((totalchunks - 1 ) * TOAST_MAX_CHUNK_SIZE );
@@ -682,8 +587,7 @@ toast_fetch_datum_slice(struct varlena *attr, int32 sliceoffset,
682587 (errcode (ERRCODE_DATA_CORRUPTED ),
683588 errmsg_internal ("unexpected chunk size %d (expected %d) in chunk %d of %d for toast value %u in %s" ,
684589 chunksize , expected_size ,
685- curchunk , totalchunks ,
686- toast_pointer .va_valueid ,
590+ curchunk , totalchunks , valueid ,
687591 RelationGetRelationName (toastrel ))));
688592
689593 /*
@@ -711,18 +615,12 @@ toast_fetch_datum_slice(struct varlena *attr, int32 sliceoffset,
711615 ereport (ERROR ,
712616 (errcode (ERRCODE_DATA_CORRUPTED ),
713617 errmsg_internal ("missing chunk number %d for toast value %u in %s" ,
714- expectedchunk ,
715- toast_pointer .va_valueid ,
618+ expectedchunk , valueid ,
716619 RelationGetRelationName (toastrel ))));
717620
718- /*
719- * End scan and close relations
720- */
621+ /* End scan and close indexes. */
721622 systable_endscan_ordered (toastscan );
722623 toast_close_indexes (toastidxs , num_indexes , AccessShareLock );
723- table_close (toastrel , AccessShareLock );
724-
725- return result ;
726624}
727625
728626/* ----------
0 commit comments