@@ -27,6 +27,9 @@ static struct varlena *toast_fetch_datum(struct varlena *attr);
27
27
static struct varlena * toast_fetch_datum_slice (struct varlena * attr ,
28
28
int32 sliceoffset ,
29
29
int32 slicelength );
30
+ static void heap_fetch_toast_slice (Relation toastrel , Oid valueid ,
31
+ int32 attrsize , int32 sliceoffset ,
32
+ int32 slicelength , struct varlena * result );
30
33
static struct varlena * toast_decompress_datum (struct varlena * attr );
31
34
static struct varlena * toast_decompress_datum_slice (struct varlena * attr , int32 slicelength );
32
35
@@ -325,19 +328,9 @@ static struct varlena *
325
328
toast_fetch_datum (struct varlena * attr )
326
329
{
327
330
Relation toastrel ;
328
- Relation * toastidxs ;
329
- ScanKeyData toastkey ;
330
- SysScanDesc toastscan ;
331
- HeapTuple ttup ;
332
- TupleDesc toasttupDesc ;
333
331
struct varlena * result ;
334
332
struct varatt_external toast_pointer ;
335
333
int32 attrsize ;
336
- int32 expectedchunk ;
337
- int32 totalchunks ;
338
- int num_indexes ;
339
- int validIndex ;
340
- SnapshotData SnapshotToast ;
341
334
342
335
if (!VARATT_IS_EXTERNAL_ONDISK (attr ))
343
336
elog (ERROR , "toast_fetch_datum shouldn't be called for non-ondisk datums" );
@@ -346,7 +339,6 @@ toast_fetch_datum(struct varlena *attr)
346
339
VARATT_EXTERNAL_GET_POINTER (toast_pointer , attr );
347
340
348
341
attrsize = toast_pointer .va_extsize ;
349
- totalchunks = ((attrsize - 1 ) / TOAST_MAX_CHUNK_SIZE ) + 1 ;
350
342
351
343
result = (struct varlena * ) palloc (attrsize + VARHDRSZ );
352
344
@@ -355,130 +347,19 @@ toast_fetch_datum(struct varlena *attr)
355
347
else
356
348
SET_VARSIZE (result , attrsize + VARHDRSZ );
357
349
350
+ if (attrsize == 0 )
351
+ return result ; /* Probably shouldn't happen, but just in case. */
352
+
358
353
/*
359
354
* Open the toast relation and its indexes
360
355
*/
361
356
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 );
369
357
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 );
377
361
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 */
482
363
table_close (toastrel , AccessShareLock );
483
364
484
365
return result ;
@@ -500,22 +381,9 @@ toast_fetch_datum_slice(struct varlena *attr, int32 sliceoffset,
500
381
int32 slicelength )
501
382
{
502
383
Relation toastrel ;
503
- Relation * toastidxs ;
504
- ScanKeyData toastkey [3 ];
505
- int nscankeys ;
506
- SysScanDesc toastscan ;
507
- HeapTuple ttup ;
508
- TupleDesc toasttupDesc ;
509
384
struct varlena * result ;
510
385
struct varatt_external toast_pointer ;
511
386
int32 attrsize ;
512
- int32 expectedchunk ;
513
- int startchunk ;
514
- int endchunk ;
515
- int totalchunks ;
516
- int num_indexes ;
517
- int validIndex ;
518
- SnapshotData SnapshotToast ;
519
387
520
388
if (!VARATT_IS_EXTERNAL_ONDISK (attr ))
521
389
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,
531
399
Assert (!VARATT_EXTERNAL_IS_COMPRESSED (toast_pointer ) || 0 == sliceoffset );
532
400
533
401
attrsize = toast_pointer .va_extsize ;
534
- totalchunks = ((attrsize - 1 ) / TOAST_MAX_CHUNK_SIZE ) + 1 ;
535
402
536
403
if (sliceoffset >= attrsize )
537
404
{
@@ -560,35 +427,74 @@ toast_fetch_datum_slice(struct varlena *attr, int32 sliceoffset,
560
427
if (slicelength == 0 )
561
428
return result ; /* Can save a lot of work at this point! */
562
429
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 */
570
431
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 ;
572
471
573
472
/* Look for the valid index of toast relation */
574
473
validIndex = toast_open_indexes (toastrel ,
575
474
AccessShareLock ,
576
475
& toastidxs ,
577
476
& num_indexes );
578
477
478
+ startchunk = sliceoffset / TOAST_MAX_CHUNK_SIZE ;
479
+ endchunk = (sliceoffset + slicelength - 1 ) / TOAST_MAX_CHUNK_SIZE ;
480
+ Assert (endchunk <= totalchunks );
481
+
579
482
/*
580
483
* Setup a scan key to fetch from the index. This is either two keys or
581
484
* three depending on the number of chunks.
582
485
*/
583
486
ScanKeyInit (& toastkey [0 ],
584
487
(AttrNumber ) 1 ,
585
488
BTEqualStrategyNumber , F_OIDEQ ,
586
- ObjectIdGetDatum (toast_pointer . va_valueid ));
489
+ ObjectIdGetDatum (valueid ));
587
490
588
491
/*
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.
590
494
*/
591
- if (startchunk == endchunk )
495
+ if (startchunk == 0 && endchunk == totalchunks - 1 )
496
+ nscankeys = 1 ;
497
+ else if (startchunk == endchunk )
592
498
{
593
499
ScanKeyInit (& toastkey [1 ],
594
500
(AttrNumber ) 2 ,
@@ -609,15 +515,17 @@ toast_fetch_datum_slice(struct varlena *attr, int32 sliceoffset,
609
515
nscankeys = 3 ;
610
516
}
611
517
518
+ /* Prepare for scan */
519
+ init_toast_snapshot (& SnapshotToast );
520
+ toastscan = systable_beginscan_ordered (toastrel , toastidxs [validIndex ],
521
+ & SnapshotToast , nscankeys , toastkey );
522
+
612
523
/*
613
524
* Read the chunks by index
614
525
*
615
526
* The index is on (valueid, chunkidx) so they will come in order
616
527
*/
617
- init_toast_snapshot (& SnapshotToast );
618
528
expectedchunk = startchunk ;
619
- toastscan = systable_beginscan_ordered (toastrel , toastidxs [validIndex ],
620
- & SnapshotToast , nscankeys , toastkey );
621
529
while ((ttup = systable_getnext_ordered (toastscan , ForwardScanDirection )) != NULL )
622
530
{
623
531
int32 curchunk ;
@@ -651,8 +559,7 @@ toast_fetch_datum_slice(struct varlena *attr, int32 sliceoffset,
651
559
{
652
560
/* should never happen */
653
561
elog (ERROR , "found toasted toast chunk for toast value %u in %s" ,
654
- toast_pointer .va_valueid ,
655
- RelationGetRelationName (toastrel ));
562
+ valueid , RelationGetRelationName (toastrel ));
656
563
chunksize = 0 ; /* keep compiler quiet */
657
564
chunkdata = NULL ;
658
565
}
@@ -664,16 +571,14 @@ toast_fetch_datum_slice(struct varlena *attr, int32 sliceoffset,
664
571
ereport (ERROR ,
665
572
(errcode (ERRCODE_DATA_CORRUPTED ),
666
573
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 ,
669
575
RelationGetRelationName (toastrel ))));
670
576
if (curchunk > endchunk )
671
577
ereport (ERROR ,
672
578
(errcode (ERRCODE_DATA_CORRUPTED ),
673
579
errmsg_internal ("unexpected chunk number %d (out of range %d..%d) for toast value %u in %s" ,
674
580
curchunk ,
675
- startchunk , endchunk ,
676
- toast_pointer .va_valueid ,
581
+ startchunk , endchunk , valueid ,
677
582
RelationGetRelationName (toastrel ))));
678
583
expected_size = curchunk < totalchunks - 1 ? TOAST_MAX_CHUNK_SIZE
679
584
: attrsize - ((totalchunks - 1 ) * TOAST_MAX_CHUNK_SIZE );
@@ -682,8 +587,7 @@ toast_fetch_datum_slice(struct varlena *attr, int32 sliceoffset,
682
587
(errcode (ERRCODE_DATA_CORRUPTED ),
683
588
errmsg_internal ("unexpected chunk size %d (expected %d) in chunk %d of %d for toast value %u in %s" ,
684
589
chunksize , expected_size ,
685
- curchunk , totalchunks ,
686
- toast_pointer .va_valueid ,
590
+ curchunk , totalchunks , valueid ,
687
591
RelationGetRelationName (toastrel ))));
688
592
689
593
/*
@@ -711,18 +615,12 @@ toast_fetch_datum_slice(struct varlena *attr, int32 sliceoffset,
711
615
ereport (ERROR ,
712
616
(errcode (ERRCODE_DATA_CORRUPTED ),
713
617
errmsg_internal ("missing chunk number %d for toast value %u in %s" ,
714
- expectedchunk ,
715
- toast_pointer .va_valueid ,
618
+ expectedchunk , valueid ,
716
619
RelationGetRelationName (toastrel ))));
717
620
718
- /*
719
- * End scan and close relations
720
- */
621
+ /* End scan and close indexes. */
721
622
systable_endscan_ordered (toastscan );
722
623
toast_close_indexes (toastidxs , num_indexes , AccessShareLock );
723
- table_close (toastrel , AccessShareLock );
724
-
725
- return result ;
726
624
}
727
625
728
626
/* ----------
0 commit comments