Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit039680a

Browse files
committed
Don't assume that a tuple's header size is unchanged during toasting.
This assumption can be wrong when the toaster is passed a raw on-disktuple, because the tuple might pre-date an ALTER TABLE ADD COLUMN operationthat added columns without rewriting the table. In such a case the tuple'snatts value is smaller than what we expect from the tuple descriptor, andso its t_hoff value could be smaller too. In fact, the tuple might nothave a null bitmap at all, and yet our current opinion of it is that itcontains some trailing nulls.In such a situation, toast_insert_or_update did the wrong thing, becauseto save a few lines of code it would use the old t_hoff value as the offsetwhere heap_fill_tuple should start filling data. This did not leave enoughroom for the new nulls bitmap, with the result that the first few bytes ofdata could be overwritten with null flag bits, as in a recent report fromHubert Depesz Lubaczewski.The particular case reported requires ALTER TABLE ADD COLUMN followed byCREATE TABLE AS SELECT * FROM ... or INSERT ... SELECT * FROM ..., andfurther requires that there be some out-of-line toasted fields in one ofthe tuples to be copied; else we'll not reach the troublesome code.The problem can only manifest in this form in 8.4 and later, becausebefore commita77eaa6, CREATE TABLE AS orINSERT/SELECT wouldn't result in raw disk tuples getting passed directlyto heap_insert --- there would always have been at least a junkfilter inbetween, and that would reconstitute the tuple header with an up-to-datet_natts and hence t_hoff. But I'm backpatching the tuptoaster change allthe way anyway, because I'm not convinced there are no older code pathsthat present a similar risk.
1 parent27ef415 commit039680a

File tree

1 file changed

+46
-29
lines changed

1 file changed

+46
-29
lines changed

‎src/backend/access/heap/tuptoaster.c

Lines changed: 46 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -597,7 +597,6 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
597597
if (newtup->t_data->t_infomask&HEAP_HASOID)
598598
hoff+=sizeof(Oid);
599599
hoff=MAXALIGN(hoff);
600-
Assert(hoff==newtup->t_data->t_hoff);
601600
/* now convert to a limit on the tuple data size */
602601
maxDataLen=TOAST_TUPLE_TARGET-hoff;
603602

@@ -864,43 +863,54 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
864863
{
865864
HeapTupleHeaderolddata=newtup->t_data;
866865
HeapTupleHeadernew_data;
867-
int32new_len;
866+
int32new_header_len;
868867
int32new_data_len;
868+
int32new_tuple_len;
869869

870870
/*
871-
* Calculate the new size of the tuple. Header size should not
872-
* change, but data size might.
871+
* Calculate the new size of the tuple.
872+
*
873+
* Note: we used to assume here that the old tuple's t_hoff must equal
874+
* the new_header_len value, but that was incorrect. The old tuple
875+
* might have a smaller-than-current natts, if there's been an ALTER
876+
* TABLE ADD COLUMN since it was stored; and that would lead to a
877+
* different conclusion about the size of the null bitmap, or even
878+
* whether there needs to be one at all.
873879
*/
874-
new_len= offsetof(HeapTupleHeaderData,t_bits);
880+
new_header_len= offsetof(HeapTupleHeaderData,t_bits);
875881
if (has_nulls)
876-
new_len+=BITMAPLEN(numAttrs);
882+
new_header_len+=BITMAPLEN(numAttrs);
877883
if (olddata->t_infomask&HEAP_HASOID)
878-
new_len+=sizeof(Oid);
879-
new_len=MAXALIGN(new_len);
880-
Assert(new_len==olddata->t_hoff);
884+
new_header_len+=sizeof(Oid);
885+
new_header_len=MAXALIGN(new_header_len);
881886
new_data_len=heap_compute_data_size(tupleDesc,
882887
toast_values,toast_isnull);
883-
new_len+=new_data_len;
888+
new_tuple_len=new_header_len+new_data_len;
884889

885890
/*
886891
* Allocate and zero the space needed, and fill HeapTupleData fields.
887892
*/
888-
result_tuple= (HeapTuple)palloc0(HEAPTUPLESIZE+new_len);
889-
result_tuple->t_len=new_len;
893+
result_tuple= (HeapTuple)palloc0(HEAPTUPLESIZE+new_tuple_len);
894+
result_tuple->t_len=new_tuple_len;
890895
result_tuple->t_self=newtup->t_self;
891896
result_tuple->t_tableOid=newtup->t_tableOid;
892897
new_data= (HeapTupleHeader) ((char*)result_tuple+HEAPTUPLESIZE);
893898
result_tuple->t_data=new_data;
894899

895900
/*
896-
*Put the existing tuple header and the changed values into place
901+
*Copy the existing tuple header, but adjust natts and t_hoff.
897902
*/
898-
memcpy(new_data,olddata,olddata->t_hoff);
903+
memcpy(new_data,olddata, offsetof(HeapTupleHeaderData,t_bits));
904+
HeapTupleHeaderSetNatts(new_data,numAttrs);
905+
new_data->t_hoff=new_header_len;
906+
if (olddata->t_infomask&HEAP_HASOID)
907+
HeapTupleHeaderSetOid(new_data,HeapTupleHeaderGetOid(olddata));
899908

909+
/* Copy over the data, and fill the null bitmap if needed */
900910
heap_fill_tuple(tupleDesc,
901911
toast_values,
902912
toast_isnull,
903-
(char*)new_data+olddata->t_hoff,
913+
(char*)new_data+new_header_len,
904914
new_data_len,
905915
&(new_data->t_infomask),
906916
has_nulls ?new_data->t_bits :NULL);
@@ -1028,8 +1038,9 @@ toast_flatten_tuple_attribute(Datum value,
10281038
TupleDesctupleDesc;
10291039
HeapTupleHeaderolddata;
10301040
HeapTupleHeadernew_data;
1031-
int32new_len;
1041+
int32new_header_len;
10321042
int32new_data_len;
1043+
int32new_tuple_len;
10331044
HeapTupleDatatmptup;
10341045
Form_pg_attribute*att;
10351046
intnumAttrs;
@@ -1100,33 +1111,39 @@ toast_flatten_tuple_attribute(Datum value,
11001111
}
11011112

11021113
/*
1103-
* Calculate the new size of the tuple. Header size should not change,
1104-
* but data size might.
1114+
* Calculate the new size of the tuple.
1115+
*
1116+
* This should match the reconstruction code in toast_insert_or_update.
11051117
*/
1106-
new_len= offsetof(HeapTupleHeaderData,t_bits);
1118+
new_header_len= offsetof(HeapTupleHeaderData,t_bits);
11071119
if (has_nulls)
1108-
new_len+=BITMAPLEN(numAttrs);
1120+
new_header_len+=BITMAPLEN(numAttrs);
11091121
if (olddata->t_infomask&HEAP_HASOID)
1110-
new_len+=sizeof(Oid);
1111-
new_len=MAXALIGN(new_len);
1112-
Assert(new_len==olddata->t_hoff);
1122+
new_header_len+=sizeof(Oid);
1123+
new_header_len=MAXALIGN(new_header_len);
11131124
new_data_len=heap_compute_data_size(tupleDesc,
11141125
toast_values,toast_isnull);
1115-
new_len+=new_data_len;
1126+
new_tuple_len=new_header_len+new_data_len;
11161127

1117-
new_data= (HeapTupleHeader)palloc0(new_len);
1128+
new_data= (HeapTupleHeader)palloc0(new_tuple_len);
11181129

11191130
/*
1120-
*Put the tuple header and the changed values into place
1131+
*Copy theexistingtuple header, but adjust natts and t_hoff.
11211132
*/
1122-
memcpy(new_data,olddata,olddata->t_hoff);
1133+
memcpy(new_data,olddata, offsetof(HeapTupleHeaderData,t_bits));
1134+
HeapTupleHeaderSetNatts(new_data,numAttrs);
1135+
new_data->t_hoff=new_header_len;
1136+
if (olddata->t_infomask&HEAP_HASOID)
1137+
HeapTupleHeaderSetOid(new_data,HeapTupleHeaderGetOid(olddata));
11231138

1124-
HeapTupleHeaderSetDatumLength(new_data,new_len);
1139+
/* Reset the datum length field, too */
1140+
HeapTupleHeaderSetDatumLength(new_data,new_tuple_len);
11251141

1142+
/* Copy over the data, and fill the null bitmap if needed */
11261143
heap_fill_tuple(tupleDesc,
11271144
toast_values,
11281145
toast_isnull,
1129-
(char*)new_data+olddata->t_hoff,
1146+
(char*)new_data+new_header_len,
11301147
new_data_len,
11311148
&(new_data->t_infomask),
11321149
has_nulls ?new_data->t_bits :NULL);

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp