Skip to content

Commit ce242ae

Browse files
committed
tableam: New callback relation_fetch_toast_slice.
Instead of always calling heap_fetch_toast_slice during detoasting, invoke a table AM callback which, when the toast table is a heap table, will be heap_fetch_toast_slice. This makes it possible for a table AM other than heap to be used as a TOAST table. It also completes the series of commits intended to improve the interaction of tableam with TOAST that began with commit 8b94dab; detoast.c is now, hopefully, fully AM-independent. Patch by me, reviewed by Andres Freund and Peter Eisentraut. Discussion: http://postgr.es/m/CA+TgmoZv-=2iWM4jcw5ZhJeL18HF96+W1yJeYrnGMYdkFFnEpQ@mail.gmail.com
1 parent 83322e3 commit ce242ae

File tree

5 files changed

+245
-193
lines changed

5 files changed

+245
-193
lines changed

src/backend/access/common/detoast.c

Lines changed: 6 additions & 193 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,17 @@
1414
#include "postgres.h"
1515

1616
#include "access/detoast.h"
17-
#include "access/genam.h"
18-
#include "access/heaptoast.h"
1917
#include "access/table.h"
18+
#include "access/tableam.h"
2019
#include "access/toast_internals.h"
2120
#include "common/pg_lzcompress.h"
2221
#include "utils/expandeddatum.h"
23-
#include "utils/fmgroids.h"
2422
#include "utils/rel.h"
2523

2624
static struct varlena *toast_fetch_datum(struct varlena *attr);
2725
static struct varlena *toast_fetch_datum_slice(struct varlena *attr,
2826
int32 sliceoffset,
2927
int32 slicelength);
30-
static void heap_fetch_toast_slice(Relation toastrel, Oid valueid,
31-
int32 attrsize, int32 sliceoffset,
32-
int32 slicelength, struct varlena *result);
3328
static struct varlena *toast_decompress_datum(struct varlena *attr);
3429
static struct varlena *toast_decompress_datum_slice(struct varlena *attr, int32 slicelength);
3530

@@ -356,8 +351,8 @@ toast_fetch_datum(struct varlena *attr)
356351
toastrel = table_open(toast_pointer.va_toastrelid, AccessShareLock);
357352

358353
/* Fetch all chunks */
359-
heap_fetch_toast_slice(toastrel, toast_pointer.va_valueid, attrsize, 0,
360-
attrsize, result);
354+
table_relation_fetch_toast_slice(toastrel, toast_pointer.va_valueid,
355+
attrsize, 0, attrsize, result);
361356

362357
/* Close toast table */
363358
table_close(toastrel, AccessShareLock);
@@ -431,198 +426,16 @@ toast_fetch_datum_slice(struct varlena *attr, int32 sliceoffset,
431426
toastrel = table_open(toast_pointer.va_toastrelid, AccessShareLock);
432427

433428
/* Fetch all chunks */
434-
heap_fetch_toast_slice(toastrel, toast_pointer.va_valueid, attrsize,
435-
sliceoffset, slicelength, result);
429+
table_relation_fetch_toast_slice(toastrel, toast_pointer.va_valueid,
430+
attrsize, sliceoffset, slicelength,
431+
result);
436432

437433
/* Close toast table */
438434
table_close(toastrel, AccessShareLock);
439435

440436
return result;
441437
}
442438

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;
471-
472-
/* Look for the valid index of toast relation */
473-
validIndex = toast_open_indexes(toastrel,
474-
AccessShareLock,
475-
&toastidxs,
476-
&num_indexes);
477-
478-
startchunk = sliceoffset / TOAST_MAX_CHUNK_SIZE;
479-
endchunk = (sliceoffset + slicelength - 1) / TOAST_MAX_CHUNK_SIZE;
480-
Assert(endchunk <= totalchunks);
481-
482-
/*
483-
* Setup a scan key to fetch from the index. This is either two keys or
484-
* three depending on the number of chunks.
485-
*/
486-
ScanKeyInit(&toastkey[0],
487-
(AttrNumber) 1,
488-
BTEqualStrategyNumber, F_OIDEQ,
489-
ObjectIdGetDatum(valueid));
490-
491-
/*
492-
* No additional condition if fetching all chunks. Otherwise, use an
493-
* equality condition for one chunk, and a range condition otherwise.
494-
*/
495-
if (startchunk == 0 && endchunk == totalchunks - 1)
496-
nscankeys = 1;
497-
else if (startchunk == endchunk)
498-
{
499-
ScanKeyInit(&toastkey[1],
500-
(AttrNumber) 2,
501-
BTEqualStrategyNumber, F_INT4EQ,
502-
Int32GetDatum(startchunk));
503-
nscankeys = 2;
504-
}
505-
else
506-
{
507-
ScanKeyInit(&toastkey[1],
508-
(AttrNumber) 2,
509-
BTGreaterEqualStrategyNumber, F_INT4GE,
510-
Int32GetDatum(startchunk));
511-
ScanKeyInit(&toastkey[2],
512-
(AttrNumber) 2,
513-
BTLessEqualStrategyNumber, F_INT4LE,
514-
Int32GetDatum(endchunk));
515-
nscankeys = 3;
516-
}
517-
518-
/* Prepare for scan */
519-
init_toast_snapshot(&SnapshotToast);
520-
toastscan = systable_beginscan_ordered(toastrel, toastidxs[validIndex],
521-
&SnapshotToast, nscankeys, toastkey);
522-
523-
/*
524-
* Read the chunks by index
525-
*
526-
* The index is on (valueid, chunkidx) so they will come in order
527-
*/
528-
expectedchunk = startchunk;
529-
while ((ttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL)
530-
{
531-
int32 curchunk;
532-
Pointer chunk;
533-
bool isnull;
534-
char *chunkdata;
535-
int32 chunksize;
536-
int32 expected_size;
537-
int32 chcpystrt;
538-
int32 chcpyend;
539-
540-
/*
541-
* Have a chunk, extract the sequence number and the data
542-
*/
543-
curchunk = DatumGetInt32(fastgetattr(ttup, 2, toasttupDesc, &isnull));
544-
Assert(!isnull);
545-
chunk = DatumGetPointer(fastgetattr(ttup, 3, toasttupDesc, &isnull));
546-
Assert(!isnull);
547-
if (!VARATT_IS_EXTENDED(chunk))
548-
{
549-
chunksize = VARSIZE(chunk) - VARHDRSZ;
550-
chunkdata = VARDATA(chunk);
551-
}
552-
else if (VARATT_IS_SHORT(chunk))
553-
{
554-
/* could happen due to heap_form_tuple doing its thing */
555-
chunksize = VARSIZE_SHORT(chunk) - VARHDRSZ_SHORT;
556-
chunkdata = VARDATA_SHORT(chunk);
557-
}
558-
else
559-
{
560-
/* should never happen */
561-
elog(ERROR, "found toasted toast chunk for toast value %u in %s",
562-
valueid, RelationGetRelationName(toastrel));
563-
chunksize = 0; /* keep compiler quiet */
564-
chunkdata = NULL;
565-
}
566-
567-
/*
568-
* Some checks on the data we've found
569-
*/
570-
if (curchunk != expectedchunk)
571-
ereport(ERROR,
572-
(errcode(ERRCODE_DATA_CORRUPTED),
573-
errmsg_internal("unexpected chunk number %d (expected %d) for toast value %u in %s",
574-
curchunk, expectedchunk, valueid,
575-
RelationGetRelationName(toastrel))));
576-
if (curchunk > endchunk)
577-
ereport(ERROR,
578-
(errcode(ERRCODE_DATA_CORRUPTED),
579-
errmsg_internal("unexpected chunk number %d (out of range %d..%d) for toast value %u in %s",
580-
curchunk,
581-
startchunk, endchunk, valueid,
582-
RelationGetRelationName(toastrel))));
583-
expected_size = curchunk < totalchunks - 1 ? TOAST_MAX_CHUNK_SIZE
584-
: attrsize - ((totalchunks - 1) * TOAST_MAX_CHUNK_SIZE);
585-
if (chunksize != expected_size)
586-
ereport(ERROR,
587-
(errcode(ERRCODE_DATA_CORRUPTED),
588-
errmsg_internal("unexpected chunk size %d (expected %d) in chunk %d of %d for toast value %u in %s",
589-
chunksize, expected_size,
590-
curchunk, totalchunks, valueid,
591-
RelationGetRelationName(toastrel))));
592-
593-
/*
594-
* Copy the data into proper place in our result
595-
*/
596-
chcpystrt = 0;
597-
chcpyend = chunksize - 1;
598-
if (curchunk == startchunk)
599-
chcpystrt = sliceoffset % TOAST_MAX_CHUNK_SIZE;
600-
if (curchunk == endchunk)
601-
chcpyend = (sliceoffset + slicelength - 1) % TOAST_MAX_CHUNK_SIZE;
602-
603-
memcpy(VARDATA(result) +
604-
(curchunk * TOAST_MAX_CHUNK_SIZE - sliceoffset) + chcpystrt,
605-
chunkdata + chcpystrt,
606-
(chcpyend - chcpystrt) + 1);
607-
608-
expectedchunk++;
609-
}
610-
611-
/*
612-
* Final checks that we successfully fetched the datum
613-
*/
614-
if (expectedchunk != (endchunk + 1))
615-
ereport(ERROR,
616-
(errcode(ERRCODE_DATA_CORRUPTED),
617-
errmsg_internal("missing chunk number %d for toast value %u in %s",
618-
expectedchunk, valueid,
619-
RelationGetRelationName(toastrel))));
620-
621-
/* End scan and close indexes. */
622-
systable_endscan_ordered(toastscan);
623-
toast_close_indexes(toastidxs, num_indexes, AccessShareLock);
624-
}
625-
626439
/* ----------
627440
* toast_decompress_datum -
628441
*

src/backend/access/heap/heapam_handler.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2545,6 +2545,7 @@ static const TableAmRoutine heapam_methods = {
25452545
.relation_size = table_block_relation_size,
25462546
.relation_needs_toast_table = heapam_relation_needs_toast_table,
25472547
.relation_toast_am = heapam_relation_toast_am,
2548+
.relation_fetch_toast_slice = heap_fetch_toast_slice,
25482549

25492550
.relation_estimate_size = heapam_estimate_rel_size,
25502551

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy