diff options
Diffstat (limited to 'src/backend/access')
-rw-r--r-- | src/backend/access/heap/tuptoaster.c | 274 |
1 files changed, 272 insertions, 2 deletions
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c index 48a15cf5d34..2a0b5c27629 100644 --- a/src/backend/access/heap/tuptoaster.c +++ b/src/backend/access/heap/tuptoaster.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/heap/tuptoaster.c,v 1.27 2002/01/16 20:29:01 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/heap/tuptoaster.c,v 1.28 2002/03/05 05:33:06 momjian Exp $ * * * INTERFACE ROUTINES @@ -47,6 +47,8 @@ static void toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup); static Datum toast_save_datum(Relation rel, Datum value); static varattrib *toast_fetch_datum(varattrib *attr); +static varattrib *toast_fetch_datum_slice(varattrib *attr, + int32 sliceoffset, int32 length); /* ---------- @@ -163,6 +165,80 @@ heap_tuple_untoast_attr(varattrib *attr) /* ---------- + * heap_tuple_untoast_attr_slice - + * + * Public entry point to get back part of a toasted value + * from compression or external storage. + * ---------- + */ +varattrib * +heap_tuple_untoast_attr_slice(varattrib *attr, int32 sliceoffset, int32 slicelength) +{ + varattrib *preslice; + varattrib *result; + int32 attrsize; + + if (VARATT_IS_COMPRESSED(attr)) + { + varattrib *tmp; + + if (VARATT_IS_EXTERNAL(attr)) + { + tmp = toast_fetch_datum(attr); + } + else + { + tmp = attr; /* compressed in main tuple */ + } + + preslice = (varattrib *) palloc(attr->va_content.va_external.va_rawsize + + VARHDRSZ); + VARATT_SIZEP(preslice) = attr->va_content.va_external.va_rawsize + VARHDRSZ; + pglz_decompress((PGLZ_Header *) tmp, VARATT_DATA(preslice)); + + if (tmp != attr) + pfree(tmp); + } + else + { + /* Plain value */ + if (VARATT_IS_EXTERNAL(attr)) + { + /* fast path */ + return (toast_fetch_datum_slice(attr, sliceoffset, slicelength)); + } + else + { + preslice = attr; + } + } + + /* slicing of datum for compressed cases and plain value */ + + attrsize = VARSIZE(preslice) - VARHDRSZ; + if (sliceoffset >= attrsize) + { + sliceoffset = 0; + slicelength = 0; + } + + if (((sliceoffset + slicelength) > attrsize) || slicelength < 0) + { + slicelength = attrsize - sliceoffset; + } + + result = (varattrib *) palloc(slicelength + VARHDRSZ); + VARATT_SIZEP(result) = slicelength + VARHDRSZ; + + memcpy(VARDATA(result), VARDATA(preslice) + sliceoffset, slicelength); + + if (preslice != attr) pfree(preslice); + + return result; +} + + +/* ---------- * toast_raw_datum_size - * * Return the raw (detoasted) size of a varlena datum @@ -981,7 +1057,7 @@ toast_fetch_datum(varattrib *attr) VARATT_SIZEP(result) |= VARATT_FLAG_COMPRESSED; /* - * Open the toast relation and it's index + * Open the toast relation and its index */ toastrel = heap_open(attr->va_content.va_external.va_toastrelid, AccessShareLock); @@ -1081,4 +1157,198 @@ toast_fetch_datum(varattrib *attr) return result; } +/* ---------- + * toast_fetch_datum_slice - + * + * Reconstruct a segment of a varattrib from the chunks saved + * in the toast relation + * ---------- + */ +static varattrib * +toast_fetch_datum_slice(varattrib *attr, int32 sliceoffset, int32 length) +{ + Relation toastrel; + Relation toastidx; + ScanKeyData toastkey[3]; + IndexScanDesc toastscan; + HeapTupleData toasttup; + HeapTuple ttup; + TupleDesc toasttupDesc; + RetrieveIndexResult indexRes; + Buffer buffer; + + varattrib *result; + int32 attrsize; + int32 nscankeys; + int32 residx; + int32 nextidx; + int numchunks; + int startchunk; + int endchunk; + int32 startoffset; + int32 endoffset; + int totalchunks; + Pointer chunk; + bool isnull; + int32 chunksize; + int32 chcpystrt; + int32 chcpyend; + + attrsize = attr->va_content.va_external.va_extsize; + totalchunks = ((attrsize - 1) / TOAST_MAX_CHUNK_SIZE) + 1; + + if (sliceoffset >= attrsize) + { + sliceoffset = 0; + length = 0; + } + + if (((sliceoffset + length) > attrsize) || length < 0) + { + length = attrsize - sliceoffset; + } + + result = (varattrib *) palloc(length + VARHDRSZ); + VARATT_SIZEP(result) = length + VARHDRSZ; + + if (VARATT_IS_COMPRESSED(attr)) + VARATT_SIZEP(result) |= VARATT_FLAG_COMPRESSED; + + if (length == 0) return (result); /* Can save a lot of work at this point! */ + + startchunk = sliceoffset / TOAST_MAX_CHUNK_SIZE; + endchunk = (sliceoffset + length - 1) / TOAST_MAX_CHUNK_SIZE; + numchunks = (endchunk - startchunk ) + 1; + + startoffset = sliceoffset % TOAST_MAX_CHUNK_SIZE; + endoffset = (sliceoffset + length - 1) % TOAST_MAX_CHUNK_SIZE; + + /* + * Open the toast relation and it's index + */ + toastrel = heap_open(attr->va_content.va_external.va_toastrelid, + AccessShareLock); + toasttupDesc = toastrel->rd_att; + toastidx = index_open(toastrel->rd_rel->reltoastidxid); + + /* + * Setup a scan key to fetch from the index. This is either two keys + * or three depending on the number of chunks. + */ + ScanKeyEntryInitialize(&toastkey[0], + (bits16) 0, + (AttrNumber) 1, + (RegProcedure) F_OIDEQ, + ObjectIdGetDatum(attr->va_content.va_external.va_valueid)); + /* + * Now dependent on number of chunks: + */ + + if (numchunks == 1) + { + ScanKeyEntryInitialize(&toastkey[1], + (bits16) 0, + (AttrNumber) 2, + (RegProcedure) F_INT4EQ, + Int32GetDatum(startchunk)); + nscankeys = 2; + } + else + { + ScanKeyEntryInitialize(&toastkey[1], + (bits16) 0, + (AttrNumber) 2, + (RegProcedure) F_INT4GE, + Int32GetDatum(startchunk)); + ScanKeyEntryInitialize(&toastkey[2], + (bits16) 0, + (AttrNumber) 2, + (RegProcedure) F_INT4LE, + Int32GetDatum(endchunk)); + nscankeys = 3; + } + + /* + * Read the chunks by index + * + * The index is on (valueid, chunkidx) so they will come in order + */ + nextidx = startchunk; + toastscan = index_beginscan(toastidx, false, nscankeys, &toastkey[0]); + while ((indexRes = index_getnext(toastscan, ForwardScanDirection)) != NULL) + { + toasttup.t_self = indexRes->heap_iptr; + heap_fetch(toastrel, SnapshotToast, &toasttup, &buffer, toastscan); + pfree(indexRes); + + if (toasttup.t_data == NULL) + continue; + ttup = &toasttup; + + /* + * Have a chunk, extract the sequence number and the data + */ + residx = DatumGetInt32(heap_getattr(ttup, 2, toasttupDesc, &isnull)); + Assert(!isnull); + chunk = DatumGetPointer(heap_getattr(ttup, 3, toasttupDesc, &isnull)); + Assert(!isnull); + chunksize = VARATT_SIZE(chunk) - VARHDRSZ; + + /* + * Some checks on the data we've found + */ + if ((residx != nextidx) || (residx > endchunk) || (residx < startchunk)) + elog(ERROR, "unexpected chunk number %d (expected %d) for toast value %u", + residx, nextidx, + attr->va_content.va_external.va_valueid); + if (residx < totalchunks - 1) + { + if (chunksize != TOAST_MAX_CHUNK_SIZE) + elog(ERROR, "unexpected chunk size %d in chunk %d for toast value %u", + chunksize, residx, + attr->va_content.va_external.va_valueid); + } + else + { + if ((residx * TOAST_MAX_CHUNK_SIZE + chunksize) != attrsize) + elog(ERROR, "unexpected chunk size %d in chunk %d for toast value %u", + chunksize, residx, + attr->va_content.va_external.va_valueid); + } + + /* + * Copy the data into proper place in our result + */ + chcpystrt = 0; + chcpyend = chunksize - 1; + if (residx == startchunk) chcpystrt = startoffset; + if (residx == endchunk) chcpyend = endoffset; + + memcpy(((char *) VARATT_DATA(result)) + + (residx * TOAST_MAX_CHUNK_SIZE - sliceoffset) +chcpystrt, + VARATT_DATA(chunk) + chcpystrt, + (chcpyend - chcpystrt) + 1); + + ReleaseBuffer(buffer); + nextidx++; + } + + /* + * Final checks that we successfully fetched the datum + */ + if ( nextidx != (endchunk + 1)) + elog(ERROR, "missing chunk number %d for toast value %u", + nextidx, + attr->va_content.va_external.va_valueid); + + /* + * End scan and close relations + */ + index_endscan(toastscan); + index_close(toastidx); + heap_close(toastrel, AccessShareLock); + + return result; +} + #endif /* TUPLE_TOASTER_ACTIVE */ |