@@ -68,8 +68,9 @@ static void toast_close_indexes(Relation *toastidxs, int num_indexes,
68
68
*
69
69
* This will return a datum that contains all the data internally, ie, not
70
70
* relying on external storage or memory, but it can still be compressed or
71
- * have a short header.
72
- ----------
71
+ * have a short header. Note some callers assume that if the input is an
72
+ * EXTERNAL datum, the result will be a pfree'able chunk.
73
+ * ----------
73
74
*/
74
75
struct varlena *
75
76
heap_tuple_fetch_attr (struct varlena * attr )
@@ -86,9 +87,7 @@ heap_tuple_fetch_attr(struct varlena * attr)
86
87
else if (VARATT_IS_EXTERNAL_INDIRECT (attr ))
87
88
{
88
89
/*
89
- * copy into the caller's memory context. That's not required in all
90
- * cases but sufficient for now since this is mainly used when we need
91
- * to persist a Datum for unusually long time, like in a HOLD cursor.
90
+ * This is an indirect pointer --- dereference it
92
91
*/
93
92
struct varatt_indirect redirect ;
94
93
@@ -98,11 +97,14 @@ heap_tuple_fetch_attr(struct varlena * attr)
98
97
/* nested indirect Datums aren't allowed */
99
98
Assert (!VARATT_IS_EXTERNAL_INDIRECT (attr ));
100
99
101
- /* doesn't make much sense, but better handle it */
102
- if (VARATT_IS_EXTERNAL_ONDISK (attr ))
100
+ /* recurse if value is still external in some other way */
101
+ if (VARATT_IS_EXTERNAL (attr ))
103
102
return heap_tuple_fetch_attr (attr );
104
103
105
- /* copy datum verbatim */
104
+ /*
105
+ * Copy into the caller's memory context, in case caller tries to
106
+ * pfree the result.
107
+ */
106
108
result = (struct varlena * ) palloc (VARSIZE_ANY (attr ));
107
109
memcpy (result , attr , VARSIZE_ANY (attr ));
108
110
}
@@ -122,7 +124,10 @@ heap_tuple_fetch_attr(struct varlena * attr)
122
124
* heap_tuple_untoast_attr -
123
125
*
124
126
* Public entry point to get back a toasted value from compression
125
- * or external storage.
127
+ * or external storage. The result is always non-extended varlena form.
128
+ *
129
+ * Note some callers assume that if the input is an EXTERNAL or COMPRESSED
130
+ * datum, the result will be a pfree'able chunk.
126
131
* ----------
127
132
*/
128
133
struct varlena *
@@ -147,6 +152,9 @@ heap_tuple_untoast_attr(struct varlena * attr)
147
152
}
148
153
else if (VARATT_IS_EXTERNAL_INDIRECT (attr ))
149
154
{
155
+ /*
156
+ * This is an indirect pointer --- dereference it
157
+ */
150
158
struct varatt_indirect redirect ;
151
159
152
160
VARATT_EXTERNAL_GET_POINTER (redirect , attr );
@@ -155,7 +163,18 @@ heap_tuple_untoast_attr(struct varlena * attr)
155
163
/* nested indirect Datums aren't allowed */
156
164
Assert (!VARATT_IS_EXTERNAL_INDIRECT (attr ));
157
165
166
+ /* recurse in case value is still extended in some other way */
158
167
attr = heap_tuple_untoast_attr (attr );
168
+
169
+ /* if it isn't, we'd better copy it */
170
+ if (attr == (struct varlena * ) redirect .pointer )
171
+ {
172
+ struct varlena * result ;
173
+
174
+ result = (struct varlena * ) palloc (VARSIZE_ANY (attr ));
175
+ memcpy (result , attr , VARSIZE_ANY (attr ));
176
+ attr = result ;
177
+ }
159
178
}
160
179
else if (VARATT_IS_COMPRESSED (attr ))
161
180
{
@@ -231,6 +250,8 @@ heap_tuple_untoast_attr_slice(struct varlena * attr,
231
250
else
232
251
preslice = attr ;
233
252
253
+ Assert (!VARATT_IS_EXTERNAL (preslice ));
254
+
234
255
if (VARATT_IS_COMPRESSED (preslice ))
235
256
{
236
257
PGLZ_Header * tmp = (PGLZ_Header * ) preslice ;
@@ -437,8 +458,6 @@ toast_delete(Relation rel, HeapTuple oldtup)
437
458
continue ;
438
459
else if (VARATT_IS_EXTERNAL_ONDISK (PointerGetDatum (value )))
439
460
toast_delete_datum (rel , value );
440
- else if (VARATT_IS_EXTERNAL_INDIRECT (PointerGetDatum (value )))
441
- elog (ERROR , "attempt to delete tuple containing indirect datums" );
442
461
}
443
462
}
444
463
}
@@ -600,7 +619,8 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
600
619
601
620
/*
602
621
* We took care of UPDATE above, so any external value we find
603
- * still in the tuple must be someone else's we cannot reuse.
622
+ * still in the tuple must be someone else's that we cannot reuse
623
+ * (this includes the case of an out-of-line in-memory datum).
604
624
* Fetch it back (without decompression, unless we are forcing
605
625
* PLAIN storage). If necessary, we'll push it out as a new
606
626
* external value below.
@@ -1032,7 +1052,7 @@ toast_flatten_tuple(HeapTuple tup, TupleDesc tupleDesc)
1032
1052
new_value = (struct varlena * ) DatumGetPointer (toast_values [i ]);
1033
1053
if (VARATT_IS_EXTERNAL (new_value ))
1034
1054
{
1035
- new_value = toast_fetch_datum (new_value );
1055
+ new_value = heap_tuple_fetch_attr (new_value );
1036
1056
toast_values [i ] = PointerGetDatum (new_value );
1037
1057
toast_free [i ] = true;
1038
1058
}
@@ -1726,8 +1746,8 @@ toast_fetch_datum(struct varlena * attr)
1726
1746
int num_indexes ;
1727
1747
int validIndex ;
1728
1748
1729
- if (VARATT_IS_EXTERNAL_INDIRECT (attr ))
1730
- elog (ERROR , "shouldn't be called for indirect tuples " );
1749
+ if (! VARATT_IS_EXTERNAL_ONDISK (attr ))
1750
+ elog (ERROR , "toast_fetch_datum shouldn't be called for non-ondisk datums " );
1731
1751
1732
1752
/* Must copy to access aligned fields */
1733
1753
VARATT_EXTERNAL_GET_POINTER (toast_pointer , attr );
@@ -1903,7 +1923,8 @@ toast_fetch_datum_slice(struct varlena * attr, int32 sliceoffset, int32 length)
1903
1923
int num_indexes ;
1904
1924
int validIndex ;
1905
1925
1906
- Assert (VARATT_IS_EXTERNAL_ONDISK (attr ));
1926
+ if (!VARATT_IS_EXTERNAL_ONDISK (attr ))
1927
+ elog (ERROR , "toast_fetch_datum_slice shouldn't be called for non-ondisk datums" );
1907
1928
1908
1929
/* Must copy to access aligned fields */
1909
1930
VARATT_EXTERNAL_GET_POINTER (toast_pointer , attr );
0 commit comments