Zdenek Kotala wrote: > Alvaro Herrera napsal(a): >> Heikki Linnakangas wrote: >> >>> Hmm, you're right. I think it can be made to work by storing the >>> *end* offset of each chunk. To find the chunk containing offset X, >>> search for the first chunk with end_offset > X. >> >> FWIW I'm trying to do this. So far I've managed to make the basic thing >> work, and I'm about to have a look at the slice interface.
Okay, so this seems to work. It's missing writing the sanity checks on the returned data, and a look at the SGML docs to see if anything needs updating. I'm also going to recheck code comments that may need updates. -- Alvaro Herrera http://www.CommandPrompt.com/ PostgreSQL Replication, Consulting, Custom Development, 24x7 support
Index: src/backend/access/heap/tuptoaster.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/access/heap/tuptoaster.c,v retrieving revision 1.91 diff -c -p -r1.91 tuptoaster.c *** src/backend/access/heap/tuptoaster.c 6 Nov 2008 20:51:14 -0000 1.91 --- src/backend/access/heap/tuptoaster.c 17 Nov 2008 20:49:41 -0000 *************** toast_save_datum(Relation rel, Datum val *** 1134,1140 **** int32 align_it; /* ensure struct is aligned well enough */ } chunk_data; int32 chunk_size; ! int32 chunk_seq = 0; char *data_p; int32 data_todo; Pointer dval = DatumGetPointer(value); --- 1134,1140 ---- int32 align_it; /* ensure struct is aligned well enough */ } chunk_data; int32 chunk_size; ! int32 data_done = 0; char *data_p; int32 data_todo; Pointer dval = DatumGetPointer(value); *************** toast_save_datum(Relation rel, Datum val *** 1208,1214 **** /* * Build a tuple and store it */ ! t_values[1] = Int32GetDatum(chunk_seq++); SET_VARSIZE(&chunk_data, chunk_size + VARHDRSZ); memcpy(VARDATA(&chunk_data), data_p, chunk_size); toasttup = heap_form_tuple(toasttupDesc, t_values, t_isnull); --- 1208,1214 ---- /* * Build a tuple and store it */ ! t_values[1] = Int32GetDatum(data_done + chunk_size); SET_VARSIZE(&chunk_data, chunk_size + VARHDRSZ); memcpy(VARDATA(&chunk_data), data_p, chunk_size); toasttup = heap_form_tuple(toasttupDesc, t_values, t_isnull); *************** toast_save_datum(Relation rel, Datum val *** 1237,1242 **** --- 1237,1243 ---- */ data_todo -= chunk_size; data_p += chunk_size; + data_done += chunk_size; } /* *************** toast_fetch_datum(struct varlena * attr) *** 1336,1343 **** struct varlena *result; struct varatt_external toast_pointer; int32 ressize; ! int32 residx, ! nextidx; int32 numchunks; Pointer chunk; bool isnull; --- 1337,1344 ---- struct varlena *result; struct varatt_external toast_pointer; int32 ressize; ! int32 endoff, ! prevend; int32 numchunks; Pointer chunk; bool isnull; *************** toast_fetch_datum(struct varlena * attr) *** 1373,1394 **** ObjectIdGetDatum(toast_pointer.va_valueid)); /* ! * Read the chunks by index * ! * Note that because the index is actually on (valueid, chunkidx) we will ! * see the chunks in chunkidx order, even though we didn't explicitly ask * for it. */ ! nextidx = 0; toastscan = systable_beginscan_ordered(toastrel, toastidx, SnapshotToast, 1, &toastkey); while ((ttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL) { /* ! * Have a chunk, extract the sequence number and the data */ ! residx = DatumGetInt32(fastgetattr(ttup, 2, toasttupDesc, &isnull)); Assert(!isnull); chunk = DatumGetPointer(fastgetattr(ttup, 3, toasttupDesc, &isnull)); Assert(!isnull); --- 1374,1395 ---- ObjectIdGetDatum(toast_pointer.va_valueid)); /* ! * Read the chunks by chuck end position * ! * Note that because the index is actually on (valueid, chunk-end) we will ! * see the chunks in chunk-end order, even though we didn't explicitly ask * for it. */ ! prevend = 0; toastscan = systable_beginscan_ordered(toastrel, toastidx, SnapshotToast, 1, &toastkey); while ((ttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL) { /* ! * Have a chunk, extract its end offset and the data */ ! endoff = DatumGetInt32(fastgetattr(ttup, 2, toasttupDesc, &isnull)); Assert(!isnull); chunk = DatumGetPointer(fastgetattr(ttup, 3, toasttupDesc, &isnull)); Assert(!isnull); *************** toast_fetch_datum(struct varlena * attr) *** 1416,1472 **** /* * Some checks on the data we've found */ ! if (residx != nextidx) ! elog(ERROR, "unexpected chunk number %d (expected %d) for toast value %u in %s", ! residx, nextidx, ! toast_pointer.va_valueid, ! RelationGetRelationName(toastrel)); ! if (residx < numchunks - 1) ! { ! if (chunksize != TOAST_MAX_CHUNK_SIZE) ! elog(ERROR, "unexpected chunk size %d (expected %d) in chunk %d of %d for toast value %u in %s", ! chunksize, (int) TOAST_MAX_CHUNK_SIZE, ! residx, numchunks, ! toast_pointer.va_valueid, ! RelationGetRelationName(toastrel)); ! } ! else if (residx == numchunks - 1) ! { ! if ((residx * TOAST_MAX_CHUNK_SIZE + chunksize) != ressize) ! elog(ERROR, "unexpected chunk size %d (expected %d) in final chunk %d for toast value %u in %s", ! chunksize, ! (int) (ressize - residx * TOAST_MAX_CHUNK_SIZE), ! residx, ! toast_pointer.va_valueid, ! RelationGetRelationName(toastrel)); ! } ! else ! elog(ERROR, "unexpected chunk number %d (out of range %d..%d) for toast value %u in %s", ! residx, ! 0, numchunks - 1, toast_pointer.va_valueid, RelationGetRelationName(toastrel)); /* * Copy the data into proper place in our result */ ! memcpy(VARDATA(result) + residx * TOAST_MAX_CHUNK_SIZE, chunkdata, chunksize); ! nextidx++; } /* - * Final checks that we successfully fetched the datum - */ - if (nextidx != numchunks) - elog(ERROR, "missing chunk number %d for toast value %u in %s", - nextidx, - toast_pointer.va_valueid, - RelationGetRelationName(toastrel)); - - /* * End scan and close relations */ systable_endscan_ordered(toastscan); --- 1417,1439 ---- /* * Some checks on the data we've found */ ! if (endoff <= prevend) ! elog(ERROR, "unexpected chunk end position %d (expected > %d) for toast value %u in %s", ! endoff, prevend, toast_pointer.va_valueid, RelationGetRelationName(toastrel)); /* * Copy the data into proper place in our result */ ! memcpy(VARDATA(result) + prevend, chunkdata, chunksize); ! prevend = endoff; } /* * End scan and close relations */ systable_endscan_ordered(toastscan); *************** toast_fetch_datum_slice(struct varlena * *** 1488,1515 **** { Relation toastrel; Relation toastidx; ! ScanKeyData toastkey[3]; ! int nscankeys; SysScanDesc toastscan; HeapTuple ttup; TupleDesc toasttupDesc; struct varlena *result; struct varatt_external toast_pointer; int32 attrsize; ! int32 residx; ! int32 nextidx; ! int numchunks; ! int startchunk; ! int endchunk; ! int32 startoffset; ! int32 endoffset; ! int totalchunks; ! Pointer chunk; ! bool isnull; ! char *chunkdata; ! int32 chunksize; ! int32 chcpystrt; ! int32 chcpyend; Assert(VARATT_IS_EXTERNAL(attr)); --- 1455,1468 ---- { Relation toastrel; Relation toastidx; ! ScanKeyData toastkey[2]; SysScanDesc toastscan; HeapTuple ttup; TupleDesc toasttupDesc; struct varlena *result; struct varatt_external toast_pointer; int32 attrsize; ! int32 dstoffset; Assert(VARATT_IS_EXTERNAL(attr)); *************** toast_fetch_datum_slice(struct varlena * *** 1523,1529 **** Assert(!VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer)); attrsize = toast_pointer.va_extsize; - totalchunks = ((attrsize - 1) / TOAST_MAX_CHUNK_SIZE) + 1; if (sliceoffset >= attrsize) { --- 1476,1481 ---- *************** toast_fetch_datum_slice(struct varlena * *** 1544,1610 **** 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 its index */ toastrel = heap_open(toast_pointer.va_toastrelid, AccessShareLock); ! toasttupDesc = toastrel->rd_att; toastidx = index_open(toastrel->rd_rel->reltoastidxid, AccessShareLock); /* ! * Setup a scan key to fetch from the index. This is either two keys or ! * three depending on the number of chunks. */ ScanKeyInit(&toastkey[0], (AttrNumber) 1, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(toast_pointer.va_valueid)); /* ! * Use equality condition for one chunk, a range condition otherwise: ! */ ! if (numchunks == 1) ! { ! ScanKeyInit(&toastkey[1], ! (AttrNumber) 2, ! BTEqualStrategyNumber, F_INT4EQ, ! Int32GetDatum(startchunk)); ! nscankeys = 2; ! } ! else ! { ! ScanKeyInit(&toastkey[1], ! (AttrNumber) 2, ! BTGreaterEqualStrategyNumber, F_INT4GE, ! Int32GetDatum(startchunk)); ! ScanKeyInit(&toastkey[2], ! (AttrNumber) 2, ! BTLessEqualStrategyNumber, 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 = systable_beginscan_ordered(toastrel, toastidx, ! SnapshotToast, nscankeys, toastkey); while ((ttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL) { /* ! * Have a chunk, extract the sequence number and the data */ ! residx = DatumGetInt32(fastgetattr(ttup, 2, toasttupDesc, &isnull)); Assert(!isnull); chunk = DatumGetPointer(fastgetattr(ttup, 3, toasttupDesc, &isnull)); Assert(!isnull); --- 1496,1542 ---- if (length == 0) return result; /* Can save a lot of work at this point! */ /* * Open the toast relation and its index */ toastrel = heap_open(toast_pointer.va_toastrelid, AccessShareLock); ! toasttupDesc = RelationGetDescr(toastrel); toastidx = index_open(toastrel->rd_rel->reltoastidxid, AccessShareLock); /* ! * Setup a scan key to fetch from the index. */ ScanKeyInit(&toastkey[0], (AttrNumber) 1, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(toast_pointer.va_valueid)); + ScanKeyInit(&toastkey[1], + (AttrNumber) 2, + BTGreaterStrategyNumber, F_INT4GT, + Int32GetDatum(sliceoffset)); /* ! * Read the chunks by end offset * ! * The index is on (valueid, chunk-end) so they will come in order */ ! dstoffset = 0; toastscan = systable_beginscan_ordered(toastrel, toastidx, ! SnapshotToast, 2, toastkey); while ((ttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL) { + uint32 srcstart; + uint32 chunkend; + uint32 copylength; + Pointer chunk; + bool isnull; + char *chunkdata; + int32 chunksize; + /* ! * Have a chunk, extract the end offset and the data */ ! chunkend = DatumGetInt32(fastgetattr(ttup, 2, toasttupDesc, &isnull)); Assert(!isnull); chunk = DatumGetPointer(fastgetattr(ttup, 3, toasttupDesc, &isnull)); Assert(!isnull); *************** toast_fetch_datum_slice(struct varlena * *** 1629,1694 **** chunkdata = NULL; } /* * 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 in %s", ! residx, nextidx, ! toast_pointer.va_valueid, ! RelationGetRelationName(toastrel)); ! if (residx < totalchunks - 1) ! { ! if (chunksize != TOAST_MAX_CHUNK_SIZE) ! elog(ERROR, "unexpected chunk size %d (expected %d) in chunk %d of %d for toast value %u in %s when fetching slice", ! chunksize, (int) TOAST_MAX_CHUNK_SIZE, ! residx, totalchunks, ! toast_pointer.va_valueid, ! RelationGetRelationName(toastrel)); ! } ! else if (residx == totalchunks - 1) ! { ! if ((residx * TOAST_MAX_CHUNK_SIZE + chunksize) != attrsize) ! elog(ERROR, "unexpected chunk size %d (expected %d) in final chunk %d for toast value %u in %s when fetching slice", ! chunksize, ! (int) (attrsize - residx * TOAST_MAX_CHUNK_SIZE), ! residx, ! toast_pointer.va_valueid, ! RelationGetRelationName(toastrel)); ! } ! else ! elog(ERROR, "unexpected chunk number %d (out of range %d..%d) for toast value %u in %s", ! residx, ! 0, totalchunks - 1, ! toast_pointer.va_valueid, ! RelationGetRelationName(toastrel)); /* * 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(VARDATA(result) + ! (residx * TOAST_MAX_CHUNK_SIZE - sliceoffset) + chcpystrt, ! chunkdata + chcpystrt, ! (chcpyend - chcpystrt) + 1); ! nextidx++; } /* * Final checks that we successfully fetched the datum */ ! if (nextidx != (endchunk + 1)) ! elog(ERROR, "missing chunk number %d for toast value %u in %s", ! nextidx, ! toast_pointer.va_valueid, ! RelationGetRelationName(toastrel)); /* * End scan and close relations --- 1561,1598 ---- chunkdata = NULL; } + #if 0 /* * Some checks on the data we've found */ ! #endif /* * Copy the data into proper place in our result */ ! if (dstoffset == 0) /* first chunk; skip unneeded bytes */ ! srcstart = sliceoffset - chunkend + chunksize; ! else ! srcstart = 0; ! ! copylength = Min(chunksize - srcstart, length); ! ! memcpy(VARDATA(result) + dstoffset, ! chunkdata + srcstart, ! copylength); ! ! length -= copylength; ! dstoffset += copylength; ! if (length == 0) ! break; } + #if 0 /* * Final checks that we successfully fetched the datum */ ! #endif /* * End scan and close relations Index: src/backend/catalog/toasting.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/catalog/toasting.c,v retrieving revision 1.11 diff -c -p -r1.11 toasting.c *** src/backend/catalog/toasting.c 1 Sep 2008 20:42:43 -0000 1.11 --- src/backend/catalog/toasting.c 14 Nov 2008 23:42:02 -0000 *************** create_toast_table(Relation rel, Oid toa *** 157,163 **** OIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, ! "chunk_seq", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3, --- 157,163 ---- OIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, ! "chunk_end", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3,
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers