Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit e9fd041

Browse files
committed
Move heap-specific detoasting logic into a separate function.
The new function, heap_fetch_toast_slice, is shared between toast_fetch_datum_slice and toast_fetch_datum, and does all the work of scanning the TOAST table, fetching chunks, and storing them into the space allocated for the result varlena. As an incidental side effect, this allows toast_fetch_datum_slice to perform the scan with only a single scankey if all chunks are being fetched, which might have some tiny performance benefit. Discussion: http://postgr.es/m/CA+TgmobBzxwFojJ0zV0Own3dr09y43hp+OzU2VW+nos4PMXWEg@mail.gmail.com
1 parent bf7427b commit e9fd041

File tree

1 file changed

+71
-173
lines changed

1 file changed

+71
-173
lines changed

src/backend/access/common/detoast.c

+71-173
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ static struct varlena *toast_fetch_datum(struct varlena *attr);
2727
static 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);
3033
static struct varlena *toast_decompress_datum(struct varlena *attr);
3134
static struct varlena *toast_decompress_datum_slice(struct varlena *attr, int32 slicelength);
3235

@@ -325,19 +328,9 @@ static struct varlena *
325328
toast_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

Comments
 (0)