if (newtup->t_data->t_infomask & HEAP_HASOID)
hoff += sizeof(Oid);
hoff = MAXALIGN(hoff);
- Assert(hoff == newtup->t_data->t_hoff);
/* now convert to a limit on the tuple data size */
maxDataLen = TOAST_TUPLE_TARGET - hoff;
{
HeapTupleHeader olddata = newtup->t_data;
HeapTupleHeader new_data;
- int32 new_len;
+ int32 new_header_len;
int32 new_data_len;
+ int32 new_tuple_len;
/*
- * Calculate the new size of the tuple. Header size should not
- * change, but data size might.
+ * Calculate the new size of the tuple.
+ *
+ * Note: we used to assume here that the old tuple's t_hoff must equal
+ * the new_header_len value, but that was incorrect. The old tuple
+ * might have a smaller-than-current natts, if there's been an ALTER
+ * TABLE ADD COLUMN since it was stored; and that would lead to a
+ * different conclusion about the size of the null bitmap, or even
+ * whether there needs to be one at all.
*/
- new_len = offsetof(HeapTupleHeaderData, t_bits);
+ new_header_len = offsetof(HeapTupleHeaderData, t_bits);
if (has_nulls)
- new_len += BITMAPLEN(numAttrs);
+ new_header_len += BITMAPLEN(numAttrs);
if (olddata->t_infomask & HEAP_HASOID)
- new_len += sizeof(Oid);
- new_len = MAXALIGN(new_len);
- Assert(new_len == olddata->t_hoff);
+ new_header_len += sizeof(Oid);
+ new_header_len = MAXALIGN(new_header_len);
new_data_len = heap_compute_data_size(tupleDesc,
toast_values, toast_isnull);
- new_len += new_data_len;
+ new_tuple_len = new_header_len + new_data_len;
/*
* Allocate and zero the space needed, and fill HeapTupleData fields.
*/
- result_tuple = (HeapTuple) palloc0(HEAPTUPLESIZE + new_len);
- result_tuple->t_len = new_len;
+ result_tuple = (HeapTuple) palloc0(HEAPTUPLESIZE + new_tuple_len);
+ result_tuple->t_len = new_tuple_len;
result_tuple->t_self = newtup->t_self;
result_tuple->t_tableOid = newtup->t_tableOid;
new_data = (HeapTupleHeader) ((char *) result_tuple + HEAPTUPLESIZE);
result_tuple->t_data = new_data;
/*
- * Put the existing tuple header and the changed values into place
+ * Copy the existing tuple header, but adjust natts and t_hoff.
*/
- memcpy(new_data, olddata, olddata->t_hoff);
+ memcpy(new_data, olddata, offsetof(HeapTupleHeaderData, t_bits));
+ HeapTupleHeaderSetNatts(new_data, numAttrs);
+ new_data->t_hoff = new_header_len;
+ if (olddata->t_infomask & HEAP_HASOID)
+ HeapTupleHeaderSetOid(new_data, HeapTupleHeaderGetOid(olddata));
+ /* Copy over the data, and fill the null bitmap if needed */
heap_fill_tuple(tupleDesc,
toast_values,
toast_isnull,
- (char *) new_data + olddata->t_hoff,
+ (char *) new_data + new_header_len,
new_data_len,
&(new_data->t_infomask),
has_nulls ? new_data->t_bits : NULL);
TupleDesc tupleDesc;
HeapTupleHeader olddata;
HeapTupleHeader new_data;
- int32 new_len;
+ int32 new_header_len;
int32 new_data_len;
+ int32 new_tuple_len;
HeapTupleData tmptup;
Form_pg_attribute *att;
int numAttrs;
}
/*
- * Calculate the new size of the tuple. Header size should not change,
- * but data size might.
+ * Calculate the new size of the tuple.
+ *
+ * This should match the reconstruction code in toast_insert_or_update.
*/
- new_len = offsetof(HeapTupleHeaderData, t_bits);
+ new_header_len = offsetof(HeapTupleHeaderData, t_bits);
if (has_nulls)
- new_len += BITMAPLEN(numAttrs);
+ new_header_len += BITMAPLEN(numAttrs);
if (olddata->t_infomask & HEAP_HASOID)
- new_len += sizeof(Oid);
- new_len = MAXALIGN(new_len);
- Assert(new_len == olddata->t_hoff);
+ new_header_len += sizeof(Oid);
+ new_header_len = MAXALIGN(new_header_len);
new_data_len = heap_compute_data_size(tupleDesc,
toast_values, toast_isnull);
- new_len += new_data_len;
+ new_tuple_len = new_header_len + new_data_len;
- new_data = (HeapTupleHeader) palloc0(new_len);
+ new_data = (HeapTupleHeader) palloc0(new_tuple_len);
/*
- * Put the tuple header and the changed values into place
+ * Copy the existing tuple header, but adjust natts and t_hoff.
*/
- memcpy(new_data, olddata, olddata->t_hoff);
+ memcpy(new_data, olddata, offsetof(HeapTupleHeaderData, t_bits));
+ HeapTupleHeaderSetNatts(new_data, numAttrs);
+ new_data->t_hoff = new_header_len;
+ if (olddata->t_infomask & HEAP_HASOID)
+ HeapTupleHeaderSetOid(new_data, HeapTupleHeaderGetOid(olddata));
- HeapTupleHeaderSetDatumLength(new_data, new_len);
+ /* Reset the datum length field, too */
+ HeapTupleHeaderSetDatumLength(new_data, new_tuple_len);
+ /* Copy over the data, and fill the null bitmap if needed */
heap_fill_tuple(tupleDesc,
toast_values,
toast_isnull,
- (char *) new_data + olddata->t_hoff,
+ (char *) new_data + new_header_len,
new_data_len,
&(new_data->t_infomask),
has_nulls ? new_data->t_bits : NULL);