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

Reply via email to