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- *externalstorage (possibly still in compressed format).
87+ *externalsource (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{
10097struct 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 */
107104result = 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+ }
109128else
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)
145164pfree (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+ }
148178else if (VARATT_IS_COMPRESSED (attr ))
149179{
150180/*
@@ -191,7 +221,7 @@ heap_tuple_untoast_attr_slice(struct varlena * attr,
191221char * attrdata ;
192222int32 attrsize ;
193223
194- if (VARATT_IS_EXTERNAL (attr ))
224+ if (VARATT_IS_EXTERNAL_ONDISK (attr ))
195225{
196226struct 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) */
205235preslice = 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+ }
207248else
208249preslice = attr ;
209250
@@ -267,14 +308,24 @@ toast_raw_datum_size(Datum value)
267308struct varlena * attr = (struct varlena * )DatumGetPointer (value );
268309Size 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 */
273314struct varatt_external toast_pointer ;
274315
275316VARATT_EXTERNAL_GET_POINTER (toast_pointer ,attr );
276317result = 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+ }
278329else if (VARATT_IS_COMPRESSED (attr ))
279330{
280331/* here, va_rawsize is just the payload size */
@@ -308,7 +359,7 @@ toast_datum_size(Datum value)
308359struct varlena * attr = (struct varlena * )DatumGetPointer (value );
309360Size 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)
320371VARATT_EXTERNAL_GET_POINTER (toast_pointer ,attr );
321372result = 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+ }
323384else if (VARATT_IS_SHORT (attr ))
324385{
325386result = VARSIZE_SHORT (attr );
@@ -387,8 +448,12 @@ toast_delete(Relation rel, HeapTuple oldtup)
387448{
388449Datum 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 )))
391454toast_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,
490555new_value = (struct varlena * )DatumGetPointer (toast_values [i ]);
491556
492557/*
493- * If the old value isan external storedone , check if it has
494- *changed so we have to delete it later.
558+ * If the old value is storedon disk , check if it has changed so
559+ * we have to delete it later.
495560 */
496561if (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 )||
500565memcmp ((char * )old_value , (char * )new_value ,
501566VARSIZE_EXTERNAL (old_value ))!= 0 )
502567{
@@ -1258,6 +1323,8 @@ toast_save_datum(Relation rel, Datum value,
12581323int32 data_todo ;
12591324Pointer 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{
13421409struct 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 */
13461413VARATT_EXTERNAL_GET_POINTER (old_toast_pointer ,oldexternal );
13471414if (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 */
14581525result = (struct varlena * )palloc (TOAST_POINTER_SIZE );
1459- SET_VARSIZE_EXTERNAL (result ,TOAST_POINTER_SIZE );
1526+ SET_VARTAG_EXTERNAL (result ,VARTAG_ONDISK );
14601527memcpy (VARDATA_EXTERNAL (result ),& toast_pointer ,sizeof (toast_pointer ));
14611528
14621529return PointerGetDatum (result );
@@ -1480,7 +1547,7 @@ toast_delete_datum(Relation rel, Datum value)
14801547SysScanDesc toastscan ;
14811548HeapTuple toasttup ;
14821549
1483- if (!VARATT_IS_EXTERNAL (attr ))
1550+ if (!VARATT_IS_EXTERNAL_ONDISK (attr ))
14841551return ;
14851552
14861553/* Must copy to access aligned fields */
@@ -1608,6 +1675,9 @@ toast_fetch_datum(struct varlena * attr)
16081675char * chunkdata ;
16091676int32 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 */
16121682VARATT_EXTERNAL_GET_POINTER (toast_pointer ,attr );
16131683
@@ -1775,7 +1845,7 @@ toast_fetch_datum_slice(struct varlena * attr, int32 sliceoffset, int32 length)
17751845int32 chcpystrt ;
17761846int32 chcpyend ;
17771847
1778- Assert (VARATT_IS_EXTERNAL (attr ));
1848+ Assert (VARATT_IS_EXTERNAL_ONDISK (attr ));
17791849
17801850/* Must copy to access aligned fields */
17811851VARATT_EXTERNAL_GET_POINTER (toast_pointer ,attr );