5050 */
5151typedef struct toast_compress_header
5252{
53- int32 vl_len_ ; /* varlena header (do not touch directly!) */
53+ int32 vl_len_ ; /* varlena header (do not touch directly!) */
5454 int32 rawsize ;
5555} toast_compress_header ;
5656
5757/*
5858 * Utilities for manipulation of header information for compressed
5959 * toast entries.
6060 */
61- #define TOAST_COMPRESS_HDRSZ ((int32) sizeof(toast_compress_header))
62- #define TOAST_COMPRESS_RAWSIZE (ptr ) (((toast_compress_header *) ptr)->rawsize)
61+ #define TOAST_COMPRESS_HDRSZ ((int32) sizeof(toast_compress_header))
62+ #define TOAST_COMPRESS_RAWSIZE (ptr ) (((toast_compress_header *) ( ptr) )->rawsize)
6363#define TOAST_COMPRESS_RAWDATA (ptr ) \
64- (((char *) ptr) + TOAST_COMPRESS_HDRSZ)
64+ (((char *) ( ptr) ) + TOAST_COMPRESS_HDRSZ)
6565#define TOAST_COMPRESS_SET_RAWSIZE (ptr , len ) \
66- (((toast_compress_header *) ptr)->rawsize = len)
66+ (((toast_compress_header *) ( ptr)) ->rawsize = ( len) )
6767
6868static void toast_delete_datum (Relation rel , Datum value );
6969static Datum toast_save_datum (Relation rel , Datum value ,
@@ -90,8 +90,9 @@ static void toast_close_indexes(Relation *toastidxs, int num_indexes,
9090 *
9191 * This will return a datum that contains all the data internally, ie, not
9292 * relying on external storage or memory, but it can still be compressed or
93- * have a short header.
94- ----------
93+ * have a short header. Note some callers assume that if the input is an
94+ * EXTERNAL datum, the result will be a pfree'able chunk.
95+ * ----------
9596 */
9697struct varlena *
9798heap_tuple_fetch_attr (struct varlena * attr )
@@ -108,9 +109,7 @@ heap_tuple_fetch_attr(struct varlena * attr)
108109 else if (VARATT_IS_EXTERNAL_INDIRECT (attr ))
109110 {
110111 /*
111- * copy into the caller's memory context. That's not required in all
112- * cases but sufficient for now since this is mainly used when we need
113- * to persist a Datum for unusually long time, like in a HOLD cursor.
112+ * This is an indirect pointer --- dereference it
114113 */
115114 struct varatt_indirect redirect ;
116115
@@ -120,11 +119,14 @@ heap_tuple_fetch_attr(struct varlena * attr)
120119 /* nested indirect Datums aren't allowed */
121120 Assert (!VARATT_IS_EXTERNAL_INDIRECT (attr ));
122121
123- /* doesn't make much sense, but better handle it */
124- if (VARATT_IS_EXTERNAL_ONDISK (attr ))
122+ /* recurse if value is still external in some other way */
123+ if (VARATT_IS_EXTERNAL (attr ))
125124 return heap_tuple_fetch_attr (attr );
126125
127- /* copy datum verbatim */
126+ /*
127+ * Copy into the caller's memory context, in case caller tries to
128+ * pfree the result.
129+ */
128130 result = (struct varlena * ) palloc (VARSIZE_ANY (attr ));
129131 memcpy (result , attr , VARSIZE_ANY (attr ));
130132 }
@@ -144,7 +146,10 @@ heap_tuple_fetch_attr(struct varlena * attr)
144146 * heap_tuple_untoast_attr -
145147 *
146148 * Public entry point to get back a toasted value from compression
147- * or external storage.
149+ * or external storage. The result is always non-extended varlena form.
150+ *
151+ * Note some callers assume that if the input is an EXTERNAL or COMPRESSED
152+ * datum, the result will be a pfree'able chunk.
148153 * ----------
149154 */
150155struct varlena *
@@ -160,12 +165,16 @@ heap_tuple_untoast_attr(struct varlena * attr)
160165 if (VARATT_IS_COMPRESSED (attr ))
161166 {
162167 struct varlena * tmp = attr ;
168+
163169 attr = toast_decompress_datum (tmp );
164170 pfree (tmp );
165171 }
166172 }
167173 else if (VARATT_IS_EXTERNAL_INDIRECT (attr ))
168174 {
175+ /*
176+ * This is an indirect pointer --- dereference it
177+ */
169178 struct varatt_indirect redirect ;
170179
171180 VARATT_EXTERNAL_GET_POINTER (redirect , attr );
@@ -174,7 +183,18 @@ heap_tuple_untoast_attr(struct varlena * attr)
174183 /* nested indirect Datums aren't allowed */
175184 Assert (!VARATT_IS_EXTERNAL_INDIRECT (attr ));
176185
186+ /* recurse in case value is still extended in some other way */
177187 attr = heap_tuple_untoast_attr (attr );
188+
189+ /* if it isn't, we'd better copy it */
190+ if (attr == (struct varlena * ) redirect .pointer )
191+ {
192+ struct varlena * result ;
193+
194+ result = (struct varlena * ) palloc (VARSIZE_ANY (attr ));
195+ memcpy (result , attr , VARSIZE_ANY (attr ));
196+ attr = result ;
197+ }
178198 }
179199 else if (VARATT_IS_COMPRESSED (attr ))
180200 {
@@ -246,9 +266,12 @@ heap_tuple_untoast_attr_slice(struct varlena * attr,
246266 else
247267 preslice = attr ;
248268
269+ Assert (!VARATT_IS_EXTERNAL (preslice ));
270+
249271 if (VARATT_IS_COMPRESSED (preslice ))
250272 {
251273 struct varlena * tmp = preslice ;
274+
252275 preslice = toast_decompress_datum (tmp );
253276
254277 if (tmp != attr )
@@ -448,8 +471,6 @@ toast_delete(Relation rel, HeapTuple oldtup)
448471 continue ;
449472 else if (VARATT_IS_EXTERNAL_ONDISK (PointerGetDatum (value )))
450473 toast_delete_datum (rel , value );
451- else if (VARATT_IS_EXTERNAL_INDIRECT (PointerGetDatum (value )))
452- elog (ERROR , "attempt to delete tuple containing indirect datums" );
453474 }
454475 }
455476}
@@ -611,7 +632,8 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
611632
612633 /*
613634 * We took care of UPDATE above, so any external value we find
614- * still in the tuple must be someone else's we cannot reuse.
635+ * still in the tuple must be someone else's that we cannot reuse
636+ * (this includes the case of an out-of-line in-memory datum).
615637 * Fetch it back (without decompression, unless we are forcing
616638 * PLAIN storage). If necessary, we'll push it out as a new
617639 * external value below.
@@ -1043,7 +1065,7 @@ toast_flatten_tuple(HeapTuple tup, TupleDesc tupleDesc)
10431065 new_value = (struct varlena * ) DatumGetPointer (toast_values [i ]);
10441066 if (VARATT_IS_EXTERNAL (new_value ))
10451067 {
1046- new_value = toast_fetch_datum (new_value );
1068+ new_value = heap_tuple_fetch_attr (new_value );
10471069 toast_values [i ] = PointerGetDatum (new_value );
10481070 toast_free [i ] = true;
10491071 }
@@ -1746,8 +1768,8 @@ toast_fetch_datum(struct varlena * attr)
17461768 int num_indexes ;
17471769 int validIndex ;
17481770
1749- if (VARATT_IS_EXTERNAL_INDIRECT (attr ))
1750- elog (ERROR , "shouldn't be called for indirect tuples " );
1771+ if (! VARATT_IS_EXTERNAL_ONDISK (attr ))
1772+ elog (ERROR , "toast_fetch_datum shouldn't be called for non-ondisk datums " );
17511773
17521774 /* Must copy to access aligned fields */
17531775 VARATT_EXTERNAL_GET_POINTER (toast_pointer , attr );
@@ -1923,7 +1945,8 @@ toast_fetch_datum_slice(struct varlena * attr, int32 sliceoffset, int32 length)
19231945 int num_indexes ;
19241946 int validIndex ;
19251947
1926- Assert (VARATT_IS_EXTERNAL_ONDISK (attr ));
1948+ if (!VARATT_IS_EXTERNAL_ONDISK (attr ))
1949+ elog (ERROR , "toast_fetch_datum_slice shouldn't be called for non-ondisk datums" );
19271950
19281951 /* Must copy to access aligned fields */
19291952 VARATT_EXTERNAL_GET_POINTER (toast_pointer , attr );
0 commit comments