Ejemplo n.º 1
0
/* ----------
 * toast_save_datum -
 *
 *	Save one single datum into the secondary relation and return
 *	a Datum reference for it.
 * ----------
 */
static Datum
toast_save_datum(Relation rel, Datum value, bool isFrozen)
{
	Relation	toastrel;
	Relation	toastidx;
	HeapTuple	toasttup;
	TupleDesc	toasttupDesc;
	Datum		t_values[3];
	bool		t_isnull[3];
	varattrib  *result;
	struct
	{
		struct varlena hdr;
		char		data[TOAST_MAX_CHUNK_SIZE]; /* make struct big enough */
		int32		align_it;	/* ensure struct is aligned well enough */
	}			chunk_data;
	int32		chunk_size;
	int32		chunk_seq = 0;
	char	   *data_p;
	int32		data_todo;
	int32		rawsize, extsize;

	/*
	 * Open the toast relation and its index.  We can use the index to check
	 * uniqueness of the OID we assign to the toasted item, even though it has
	 * additional columns besides OID.
	 */
	toastrel = heap_open(rel->rd_rel->reltoastrelid, RowExclusiveLock);
	toasttupDesc = toastrel->rd_att;
	toastidx = index_open(toastrel->rd_rel->reltoastidxid, RowExclusiveLock);

	/*
	 * Create the varattrib reference
	 */
	result = (varattrib *) palloc(sizeof(varattrib));

	/* rawsize is the size of the datum that will result after decompression --
	 * including the full header. so we have to adjust for short headers.
	 *
	 * extsize is the actual size of the data payload in the toast records
	 * without any headers
	 */
	if (VARATT_IS_SHORT_D(value)) 
	{
		rawsize = VARSIZE_SHORT_D(value) - VARHDRSZ_SHORT + VARHDRSZ;
		extsize = VARSIZE_SHORT_D(value) - VARHDRSZ_SHORT;
		data_p = VARDATA_SHORT_D(value);
		data_todo = VARSIZE_SHORT_D(value) - VARHDRSZ_SHORT;
	}
	else if (VARATT_IS_COMPRESSED_D(value))
	{
		/* rawsize in a compressed datum is the just the size of the payload */
		rawsize = ((varattrib *) DatumGetPointer(value))->va_compressed.va_rawsize + VARHDRSZ;
		extsize = VARSIZE_D(value) - VARHDRSZ;
		data_p = VARDATA_D(value);
		data_todo = VARSIZE_D(value) - VARHDRSZ;
		/* 	we used to set result->va_header |= VARATT_FLAG_COMPRESSED; down
		 * 	below. we don't any longer and depend on the equality holding:
		 * 	extsize = rawsize + VARHDRSZ*/
	}
	else 
	{
		rawsize = VARSIZE_D(value);
		extsize = VARSIZE_D(value) - VARHDRSZ;
		data_p = VARDATA_D(value);
		data_todo = VARSIZE_D(value) - VARHDRSZ;
	}
	
	SET_VARSIZE_EXTERNAL(result, TOAST_POINTER_SIZE);
	result->va_external.va_rawsize = rawsize;
	result->va_external.va_extsize = extsize;
	result->va_external.va_valueid = GetNewOidWithIndex(toastrel, toastidx);
	result->va_external.va_toastrelid = rel->rd_rel->reltoastrelid;

#ifdef USE_ASSERT_CHECKING
	Assert( (VARATT_IS_COMPRESSED_D(value)||0) == (VARATT_EXTERNAL_IS_COMPRESSED(result)||0) );

	if (VARATT_IS_COMPRESSED_D(value)) 
	{
		Assert(VARATT_EXTERNAL_IS_COMPRESSED(result));
		elog(DEBUG4,
			 "saved toast datum, original varsize %ud rawsize %ud new extsize %ud rawsize %uld\n", 
			 VARSIZE_D(value), ((varattrib *) DatumGetPointer(value))->va_compressed.va_rawsize,
			 result->va_external.va_extsize, result->va_external.va_rawsize);
	}
	else
	{
		Assert(!VARATT_EXTERNAL_IS_COMPRESSED(result));
		elog(DEBUG4,
			 "saved toast datum, original varsize %ud new extsize %ud rawsize %ud\n", 
			 VARSIZE_D(value),
			 result->va_external.va_extsize, result->va_external.va_rawsize);
	}
#endif

	/*
	 * Initialize constant parts of the tuple data
	 */
	t_values[0] = ObjectIdGetDatum(result->va_external.va_valueid);
	t_values[2] = PointerGetDatum(&chunk_data);
	t_isnull[0] = false;
	t_isnull[1] = false;
	t_isnull[2] = false;

	/*
	 * Split up the item into chunks
	 */
	while (data_todo > 0)
	{
		/*
		 * Calculate the size of this chunk
		 */
		chunk_size = Min(TOAST_MAX_CHUNK_SIZE, data_todo);

		/*
		 * 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);
		if (!HeapTupleIsValid(toasttup))
			elog(ERROR, "failed to build TOAST tuple");

		if(!isFrozen)
		{
			/* the normal case. regular insert */
			simple_heap_insert(toastrel, toasttup);
		}
		else
		{
			/* insert and freeze the tuple. used for errtables and their related toast data */
			frozen_heap_insert(toastrel, toasttup);
		}
			
		//heap_insert(relation, tup, GetCurrentCommandId(),
		//			   true, true, GetCurrentTransactionId());

		/*
		 * Create the index entry.	We cheat a little here by not using
		 * FormIndexDatum: this relies on the knowledge that the index columns
		 * are the same as the initial columns of the table.
		 *
		 * Note also that there had better not be any user-created index on
		 * the TOAST table, since we don't bother to update anything else.
		 */
		index_insert(toastidx, t_values, t_isnull,
					 &(toasttup->t_self),
					 toastrel, toastidx->rd_index->indisunique);

		/*
		 * Free memory
		 */
		heap_freetuple(toasttup);

		/*
		 * Move on to next chunk
		 */
		data_todo -= chunk_size;
		data_p += chunk_size;
	}

	/*
	 * Done - close toast relation
	 */
	index_close(toastidx, RowExclusiveLock);
	heap_close(toastrel, RowExclusiveLock);

	return PointerGetDatum(result);
}
Ejemplo n.º 2
0
/*
 * heap_fill_tuple
 *		Load data portion of a tuple from values/isnull arrays
 *
 * We also fill the null bitmap (if any) and set the infomask bits
 * that reflect the tuple's data contents.
 *
 * NOTE: it is now REQUIRED that the caller have pre-zeroed the data area.
 *
 *
 * @param isnull will only be used if <code>bit</code> is non-NULL
 * @param bit should be non-NULL (refer to td->t_bits) if isnull is set and contains non-null values
 */
Size
heap_fill_tuple(TupleDesc tupleDesc,
				Datum *values, bool *isnull,
				char *data, uint16 *infomask, bits8 *bit)
{
	char	   *start = data;
	bits8	   *bitP;
	int			bitmask;
	int			i;
	int			numberOfAttributes = tupleDesc->natts;
	Form_pg_attribute *att = tupleDesc->attrs;

	if (bit != NULL)
	{
		bitP = &bit[-1];
		bitmask = HIGHBIT;
	}
	else
	{
		/* just to keep compiler quiet */
		bitP = NULL;
		bitmask = 0;
	}

	*infomask &= ~(HEAP_HASNULL | HEAP_HASVARWIDTH | HEAP_HASEXTENDED);

	for (i = 0; i < numberOfAttributes; i++)
	{
		Size		data_length;

		if (bit != NULL)
		{
			if (bitmask != HIGHBIT)
				bitmask <<= 1;
			else
			{
				bitP += 1;
				*bitP = 0x0;
				bitmask = 1;
			}

			if (isnull[i])
			{
				*infomask |= HEAP_HASNULL;
				continue;
			}

			*bitP |= bitmask;
		}

		/*
		 * XXX we use the att_align macros on the pointer value itself, not on
		 * an offset.  This is a bit of a hack.
		 */

		if (att[i]->attbyval)
		{
			/* pass-by-value */
			data = (char *) att_align_zero(data, att[i]->attalign);
			store_att_byval(data, values[i], att[i]->attlen);
			data_length = att[i]->attlen;
		}
		else if (att[i]->attlen == -1)
		{
			/* varlena */
			*infomask |= HEAP_HASVARWIDTH;
			if (VARATT_IS_COMPRESSED_D(values[i]))
				*infomask |= HEAP_HASCOMPRESSED;
			if (VARATT_IS_EXTERNAL_D(values[i])) 
			{
				*infomask |= HEAP_HASEXTERNAL;
				data = (char *) att_align_zero(data, att[i]->attalign);
				data_length = VARSIZE_EXTERNAL(DatumGetPointer(values[i]));
				memcpy(data, DatumGetPointer(values[i]), data_length);
			}
			else if (VARATT_IS_SHORT_D(values[i])) 
			{
				/* no alignment for short varlenas */
				data_length = VARSIZE_SHORT(DatumGetPointer(values[i]));
				memcpy(data, DatumGetPointer(values[i]), data_length);
			}
			else if (VARATT_COULD_SHORT_D(values[i]) &&
					 att[i]->atttypid != INT2VECTOROID &&
					 att[i]->atttypid != OIDVECTOROID &&
					 att[i]->atttypid < FirstNormalObjectId)
			{
				/* convert to short varlena -- no alignment */
				data_length = VARSIZE_D(values[i]) - VARHDRSZ + VARHDRSZ_SHORT;
				*data = VARSIZE_TO_SHORT_D(values[i]);
				memcpy(data+1,
					   VARDATA_D(values[i]),
					   data_length-1);
			}
			else
			{
				/* must store full 4-byte header varlena */
				data = (char *) att_align_zero(data, att[i]->attalign);
				data_length = VARSIZE(DatumGetPointer(values[i]));
				memcpy(data, DatumGetPointer(values[i]), data_length);
			}
		}
		else if (att[i]->attlen == -2)
		{
			/* cstring */
			data = (char *) att_align_zero(data, att[i]->attalign);
			*infomask |= HEAP_HASVARWIDTH;
			data_length = strlen(DatumGetCString(values[i])) + 1;
			memcpy(data, DatumGetPointer(values[i]), data_length);
		}
		else
		{
			/* fixed-length pass-by-reference */
			data = (char *) att_align_zero(data, att[i]->attalign);
			Assert(att[i]->attlen > 0);
			data_length = att[i]->attlen;
			memcpy(data, DatumGetPointer(values[i]), data_length);
		}

		data += data_length;
	}
	return data - start;
}