Index: src/backend/access/heap/tuptoaster.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/access/heap/tuptoaster.c,v retrieving revision 1.24 diff -c -r1.24 tuptoaster.c *** src/backend/access/heap/tuptoaster.c 2001/08/10 18:57:33 1.24 --- src/backend/access/heap/tuptoaster.c 2001/10/08 13:25:38 *************** *** 47,54 **** --- 47,57 ---- 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); + /* ---------- * heap_tuple_toast_attrs - * *************** *** 165,171 **** --- 168,248 ---- return result; } + /* ---------- + * 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 = 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 - * *************** *** 1097,1100 **** --- 1174,1385 ---- } + /* ---------- + * 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; + int numchunks; + int startchunk; + int endchunk; + int32 startoffset; + int32 endoffset; + int totalchunks; + Pointer chunk; + bool isnull; + int32 chunksize; + int32 chcpystrt; + int32 chcpyend; + + char *chunks_found; + char *chunks_expected; + + 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 = 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; + + chunks_found = palloc(numchunks); + chunks_expected = palloc(numchunks); + memset(chunks_found, 0, numchunks); + memset(chunks_expected, 1, numchunks); + + /* + * Open the toast relation and its index + */ + toastrel = heap_open(attr->va_content.va_external.va_toastrelid, + AccessShareLock); + toasttupDesc = toastrel->rd_att; + toastidx = index_open(toastrel->rd_rel->reltoastidxid); + + /* + * Setup scan keys 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 + * + * Note we will not necessarily see the chunks in sequence-number order. + */ + toastscan = index_beginscan(toastidx, false, nscankeys, &toastkey[0]); + while ((indexRes = index_getnext(toastscan, ForwardScanDirection)) != NULL) + { + toasttup.t_self = indexRes->heap_iptr; + heap_fetch(toastrel, SnapshotAny, &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 < startchunk || residx > endchunk) + elog(ERROR, "unexpected chunk number %d for toast value %u", + residx, + 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); + } + if (chunks_found[residx - startchunk]++ > 0) + elog(ERROR, "chunk %d for toast value %u appears multiple times", + 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); + } + + /* + * Final checks that we successfully fetched the datum + */ + if (memcmp(chunks_found, chunks_expected, numchunks) != 0) + elog(ERROR, "not all toast chunks found for value %u", + attr->va_content.va_external.va_valueid); + pfree(chunks_expected); + pfree(chunks_found); + + /* + * End scan and close relations + */ + index_endscan(toastscan); + index_close(toastidx); + heap_close(toastrel, AccessShareLock); + + return result; + } + + #endif /* TUPLE_TOASTER_ACTIVE */ + + + Index: src/backend/utils/fmgr/fmgr.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v retrieving revision 1.55 diff -c -r1.55 fmgr.c *** src/backend/utils/fmgr/fmgr.c 2001/10/06 23:21:44 1.55 --- src/backend/utils/fmgr/fmgr.c 2001/10/08 13:25:44 *************** *** 1519,1521 **** --- 1519,1528 ---- return result; } } + + struct varlena * + pg_detoast_datum_slice(struct varlena * datum, int32 first, int32 count) + { + /* Only get the specified proportion from the toast rel */ + return (struct varlena *) heap_tuple_untoast_attr_slice((varattrib *) datum, first, count); + } Index: src/include/fmgr.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/fmgr.h,v retrieving revision 1.15 diff -c -r1.15 fmgr.h *** src/include/fmgr.h 2001/10/06 23:21:44 1.15 --- src/include/fmgr.h 2001/10/08 13:25:48 *************** *** 135,145 **** --- 135,150 ---- */ extern struct varlena *pg_detoast_datum(struct varlena * datum); extern struct varlena *pg_detoast_datum_copy(struct varlena * datum); + extern struct varlena *pg_detoast_datum_slice(struct varlena * datum, + int32 first, int32 count); #define PG_DETOAST_DATUM(datum) \ pg_detoast_datum((struct varlena *) DatumGetPointer(datum)) #define PG_DETOAST_DATUM_COPY(datum) \ pg_detoast_datum_copy((struct varlena *) DatumGetPointer(datum)) + #define PG_DETOAST_DATUM_SLICE(datum,f,c) \ + pg_detoast_datum_slice((struct varlena *) DatumGetPointer(datum), \ + (int32) f, (int32) c) /* * Support for cleaning up detoasted copies of inputs. This must only *************** *** 187,192 **** --- 192,202 ---- #define DatumGetTextPCopy(X) ((text *) PG_DETOAST_DATUM_COPY(X)) #define DatumGetBpCharPCopy(X) ((BpChar *) PG_DETOAST_DATUM_COPY(X)) #define DatumGetVarCharPCopy(X) ((VarChar *) PG_DETOAST_DATUM_COPY(X)) + /* Variants which return n bytes starting at pos. m */ + #define DatumGetByteaPSlice(X,m,n) ((bytea *) PG_DETOAST_DATUM_SLICE(X,m,n)) + #define DatumGetTextPSlice(X,m,n) ((text *) PG_DETOAST_DATUM_SLICE(X,m,n)) + #define DatumGetBpCharPSlice(X,m,n) ((BpChar *) PG_DETOAST_DATUM_SLICE(X,m,n)) + #define DatumGetVarCharPSlice(X,m,n) ((VarChar *) PG_DETOAST_DATUM_SLICE(X,m,n)) /* GETARG macros for varlena types will typically look like this: */ #define PG_GETARG_BYTEA_P(n) DatumGetByteaP(PG_GETARG_DATUM(n)) #define PG_GETARG_TEXT_P(n) DatumGetTextP(PG_GETARG_DATUM(n)) *************** *** 197,202 **** --- 207,217 ---- #define PG_GETARG_TEXT_P_COPY(n) DatumGetTextPCopy(PG_GETARG_DATUM(n)) #define PG_GETARG_BPCHAR_P_COPY(n) DatumGetBpCharPCopy(PG_GETARG_DATUM(n)) #define PG_GETARG_VARCHAR_P_COPY(n) DatumGetVarCharPCopy(PG_GETARG_DATUM(n)) + /* Variants which return a b-byte slice from a(OK to write) */ + #define PG_GETARG_BYTEA_P_SLICE(n,a,b) DatumGetByteaPSlice(PG_GETARG_DATUM(n),a,b) + #define PG_GETARG_TEXT_P_SLICE(n,a,b) DatumGetTextPSlice(PG_GETARG_DATUM(n),a,b) + #define PG_GETARG_BPCHAR_P_SLICE(n,a,b) DatumGetBpCharPSlice(PG_GETARG_DATUM(n),a,b) + #define PG_GETARG_VARCHAR_P_SLICE(n,a,b) DatumGetVarCharPSlice(PG_GETARG_DATUM(n),a,b) /* To return a NULL do this: */ #define PG_RETURN_NULL() \ Index: src/include/access/tuptoaster.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/access/tuptoaster.h,v retrieving revision 1.11 diff -c -r1.11 tuptoaster.h *** src/include/access/tuptoaster.h 2001/05/07 00:43:24 1.11 --- src/include/access/tuptoaster.h 2001/10/08 13:25:48 *************** *** 100,105 **** --- 100,116 ---- extern varattrib *heap_tuple_untoast_attr(varattrib *attr); /* ---------- + * heap_tuple_untoast_attr_slice() - + * + * Fetches only the specified portion of an attribute. + * (Handles all cases for attribute storage) + * ---------- + */ + extern varattrib *heap_tuple_untoast_attr_slice(varattrib *attr, int32 sliceoffset, + int32 slicelength); + + + /* ---------- * toast_compress_datum - * * Create a compressed version of a varlena datum, if possible