4444
4545#undef TOAST_DEBUG
4646
47- /* Size of an EXTERNAL datum that contains a standard TOAST pointer */
48- #define TOAST_POINTER_SIZE (VARHDRSZ_EXTERNAL + sizeof(struct varatt_external))
49-
5047/*
5148 * Testing whether an externally-stored value is compressed now requires
5249 * comparing extsize (the actual length of the external data) to rawsize
@@ -87,25 +84,47 @@ static struct varlena *toast_fetch_datum_slice(struct varlena * attr,
8784 * heap_tuple_fetch_attr -
8885 *
8986 * Public entry point to get back a toasted value from
90- * external storage (possibly still in compressed format).
87+ * external source (possibly still in compressed format).
9188 *
9289 * This will return a datum that contains all the data internally, ie, not
93- * relying on external storage, but it can still be compressed or have a short
94- * header.
90+ * relying on external storage or memory , but it can still be compressed or
91+ * have a short header.
9592 ----------
9693 */
9794struct varlena *
9895heap_tuple_fetch_attr (struct varlena * attr )
9996{
10097 struct varlena * result ;
10198
102- if (VARATT_IS_EXTERNAL (attr ))
99+ if (VARATT_IS_EXTERNAL_ONDISK (attr ))
103100 {
104101 /*
105102 * This is an external stored plain value
106103 */
107104 result = toast_fetch_datum (attr );
108105 }
106+ else if (VARATT_IS_EXTERNAL_INDIRECT (attr ))
107+ {
108+ /*
109+ * copy into the caller's memory context. That's not required in all
110+ * cases but sufficient for now since this is mainly used when we need
111+ * to persist a Datum for unusually long time, like in a HOLD cursor.
112+ */
113+ struct varatt_indirect redirect ;
114+ VARATT_EXTERNAL_GET_POINTER (redirect , attr );
115+ attr = (struct varlena * )redirect .pointer ;
116+
117+ /* nested indirect Datums aren't allowed */
118+ Assert (!VARATT_IS_EXTERNAL_INDIRECT (attr ));
119+
120+ /* doesn't make much sense, but better handle it */
121+ if (VARATT_IS_EXTERNAL_ONDISK (attr ))
122+ return heap_tuple_fetch_attr (attr );
123+
124+ /* copy datum verbatim */
125+ result = (struct varlena * ) palloc (VARSIZE_ANY (attr ));
126+ memcpy (result , attr , VARSIZE_ANY (attr ));
127+ }
109128 else
110129 {
111130 /*
@@ -128,7 +147,7 @@ heap_tuple_fetch_attr(struct varlena * attr)
128147struct varlena *
129148heap_tuple_untoast_attr (struct varlena * attr )
130149{
131- if (VARATT_IS_EXTERNAL (attr ))
150+ if (VARATT_IS_EXTERNAL_ONDISK (attr ))
132151 {
133152 /*
134153 * This is an externally stored datum --- fetch it back from there
@@ -145,6 +164,17 @@ heap_tuple_untoast_attr(struct varlena * attr)
145164 pfree (tmp );
146165 }
147166 }
167+ else if (VARATT_IS_EXTERNAL_INDIRECT (attr ))
168+ {
169+ struct varatt_indirect redirect ;
170+ VARATT_EXTERNAL_GET_POINTER (redirect , attr );
171+ attr = (struct varlena * )redirect .pointer ;
172+
173+ /* nested indirect Datums aren't allowed */
174+ Assert (!VARATT_IS_EXTERNAL_INDIRECT (attr ));
175+
176+ attr = heap_tuple_untoast_attr (attr );
177+ }
148178 else if (VARATT_IS_COMPRESSED (attr ))
149179 {
150180 /*
@@ -191,7 +221,7 @@ heap_tuple_untoast_attr_slice(struct varlena * attr,
191221 char * attrdata ;
192222 int32 attrsize ;
193223
194- if (VARATT_IS_EXTERNAL (attr ))
224+ if (VARATT_IS_EXTERNAL_ONDISK (attr ))
195225 {
196226 struct varatt_external toast_pointer ;
197227
@@ -204,6 +234,17 @@ heap_tuple_untoast_attr_slice(struct varlena * attr,
204234 /* fetch it back (compressed marker will get set automatically) */
205235 preslice = toast_fetch_datum (attr );
206236 }
237+ else if (VARATT_IS_EXTERNAL_INDIRECT (attr ))
238+ {
239+ struct varatt_indirect redirect ;
240+ VARATT_EXTERNAL_GET_POINTER (redirect , attr );
241+
242+ /* nested indirect Datums aren't allowed */
243+ Assert (!VARATT_IS_EXTERNAL_INDIRECT (redirect .pointer ));
244+
245+ return heap_tuple_untoast_attr_slice (redirect .pointer ,
246+ sliceoffset , slicelength );
247+ }
207248 else
208249 preslice = attr ;
209250
@@ -267,14 +308,24 @@ toast_raw_datum_size(Datum value)
267308 struct varlena * attr = (struct varlena * ) DatumGetPointer (value );
268309 Size result ;
269310
270- if (VARATT_IS_EXTERNAL (attr ))
311+ if (VARATT_IS_EXTERNAL_ONDISK (attr ))
271312 {
272313 /* va_rawsize is the size of the original datum -- including header */
273314 struct varatt_external toast_pointer ;
274315
275316 VARATT_EXTERNAL_GET_POINTER (toast_pointer , attr );
276317 result = toast_pointer .va_rawsize ;
277318 }
319+ else if (VARATT_IS_EXTERNAL_INDIRECT (attr ))
320+ {
321+ struct varatt_indirect toast_pointer ;
322+ VARATT_EXTERNAL_GET_POINTER (toast_pointer , attr );
323+
324+ /* nested indirect Datums aren't allowed */
325+ Assert (!VARATT_IS_EXTERNAL_INDIRECT (toast_pointer .pointer ));
326+
327+ return toast_raw_datum_size (PointerGetDatum (toast_pointer .pointer ));
328+ }
278329 else if (VARATT_IS_COMPRESSED (attr ))
279330 {
280331 /* here, va_rawsize is just the payload size */
@@ -308,7 +359,7 @@ toast_datum_size(Datum value)
308359 struct varlena * attr = (struct varlena * ) DatumGetPointer (value );
309360 Size result ;
310361
311- if (VARATT_IS_EXTERNAL (attr ))
362+ if (VARATT_IS_EXTERNAL_ONDISK (attr ))
312363 {
313364 /*
314365 * Attribute is stored externally - return the extsize whether
@@ -320,6 +371,16 @@ toast_datum_size(Datum value)
320371 VARATT_EXTERNAL_GET_POINTER (toast_pointer , attr );
321372 result = toast_pointer .va_extsize ;
322373 }
374+ else if (VARATT_IS_EXTERNAL_INDIRECT (attr ))
375+ {
376+ struct varatt_indirect toast_pointer ;
377+ VARATT_EXTERNAL_GET_POINTER (toast_pointer , attr );
378+
379+ /* nested indirect Datums aren't allowed */
380+ Assert (!VARATT_IS_EXTERNAL_INDIRECT (attr ));
381+
382+ return toast_datum_size (PointerGetDatum (toast_pointer .pointer ));
383+ }
323384 else if (VARATT_IS_SHORT (attr ))
324385 {
325386 result = VARSIZE_SHORT (attr );
@@ -387,8 +448,12 @@ toast_delete(Relation rel, HeapTuple oldtup)
387448 {
388449 Datum value = toast_values [i ];
389450
390- if (!toast_isnull [i ] && VARATT_IS_EXTERNAL (PointerGetDatum (value )))
451+ if (toast_isnull [i ])
452+ continue ;
453+ else if (VARATT_IS_EXTERNAL_ONDISK (PointerGetDatum (value )))
391454 toast_delete_datum (rel , value );
455+ else if (VARATT_IS_EXTERNAL_INDIRECT (PointerGetDatum (value )))
456+ elog (ERROR , "attempt to delete tuple containing indirect datums" );
392457 }
393458 }
394459}
@@ -490,13 +555,13 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
490555 new_value = (struct varlena * ) DatumGetPointer (toast_values [i ]);
491556
492557 /*
493- * If the old value is an external stored one , check if it has
494- * changed so we have to delete it later.
558+ * If the old value is stored on disk , check if it has changed so
559+ * we have to delete it later.
495560 */
496561 if (att [i ]-> attlen == -1 && !toast_oldisnull [i ] &&
497- VARATT_IS_EXTERNAL (old_value ))
562+ VARATT_IS_EXTERNAL_ONDISK (old_value ))
498563 {
499- if (toast_isnull [i ] || !VARATT_IS_EXTERNAL (new_value ) ||
564+ if (toast_isnull [i ] || !VARATT_IS_EXTERNAL_ONDISK (new_value ) ||
500565 memcmp ((char * ) old_value , (char * ) new_value ,
501566 VARSIZE_EXTERNAL (old_value )) != 0 )
502567 {
@@ -1258,6 +1323,8 @@ toast_save_datum(Relation rel, Datum value,
12581323 int32 data_todo ;
12591324 Pointer dval = DatumGetPointer (value );
12601325
1326+ Assert (!VARATT_IS_EXTERNAL (value ));
1327+
12611328 /*
12621329 * Open the toast relation and its index. We can use the index to check
12631330 * uniqueness of the OID we assign to the toasted item, even though it has
@@ -1341,7 +1408,7 @@ toast_save_datum(Relation rel, Datum value,
13411408 {
13421409 struct varatt_external old_toast_pointer ;
13431410
1344- Assert (VARATT_IS_EXTERNAL (oldexternal ));
1411+ Assert (VARATT_IS_EXTERNAL_ONDISK (oldexternal ));
13451412 /* Must copy to access aligned fields */
13461413 VARATT_EXTERNAL_GET_POINTER (old_toast_pointer , oldexternal );
13471414 if (old_toast_pointer .va_toastrelid == rel -> rd_toastoid )
@@ -1456,7 +1523,7 @@ toast_save_datum(Relation rel, Datum value,
14561523 * Create the TOAST pointer value that we'll return
14571524 */
14581525 result = (struct varlena * ) palloc (TOAST_POINTER_SIZE );
1459- SET_VARSIZE_EXTERNAL (result , TOAST_POINTER_SIZE );
1526+ SET_VARTAG_EXTERNAL (result , VARTAG_ONDISK );
14601527 memcpy (VARDATA_EXTERNAL (result ), & toast_pointer , sizeof (toast_pointer ));
14611528
14621529 return PointerGetDatum (result );
@@ -1480,7 +1547,7 @@ toast_delete_datum(Relation rel, Datum value)
14801547 SysScanDesc toastscan ;
14811548 HeapTuple toasttup ;
14821549
1483- if (!VARATT_IS_EXTERNAL (attr ))
1550+ if (!VARATT_IS_EXTERNAL_ONDISK (attr ))
14841551 return ;
14851552
14861553 /* Must copy to access aligned fields */
@@ -1608,6 +1675,9 @@ toast_fetch_datum(struct varlena * attr)
16081675 char * chunkdata ;
16091676 int32 chunksize ;
16101677
1678+ if (VARATT_IS_EXTERNAL_INDIRECT (attr ))
1679+ elog (ERROR , "shouldn't be called for indirect tuples" );
1680+
16111681 /* Must copy to access aligned fields */
16121682 VARATT_EXTERNAL_GET_POINTER (toast_pointer , attr );
16131683
@@ -1775,7 +1845,7 @@ toast_fetch_datum_slice(struct varlena * attr, int32 sliceoffset, int32 length)
17751845 int32 chcpystrt ;
17761846 int32 chcpyend ;
17771847
1778- Assert (VARATT_IS_EXTERNAL (attr ));
1848+ Assert (VARATT_IS_EXTERNAL_ONDISK (attr ));
17791849
17801850 /* Must copy to access aligned fields */
17811851 VARATT_EXTERNAL_GET_POINTER (toast_pointer , attr );
0 commit comments