Skip to content

Commit ccf655c

Browse files
committed
Minor cleanup/code review for "indirect toast" stuff.
Fix some issues I noticed while fooling with an extension to allow an additional kind of toast pointer. Much of this is just comment improvement, but there are a couple of actual bugs, which might or might not be reachable today depending on what can happen during logical decoding. An example is that toast_flatten_tuple() failed to cover the possibility of an indirection pointer in its input. Back-patch to 9.4 just in case that is reachable now. In HEAD, also correct some really minor issues with recent compression reorganization, such as dangerously underparenthesized macros.
1 parent 3bc4c69 commit ccf655c

File tree

3 files changed

+59
-37
lines changed

3 files changed

+59
-37
lines changed

src/backend/access/heap/tuptoaster.c

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,9 @@ static void toast_close_indexes(Relation *toastidxs, int num_indexes,
6868
*
6969
* This will return a datum that contains all the data internally, ie, not
7070
* 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+
* ----------
7374
*/
7475
struct varlena *
7576
heap_tuple_fetch_attr(struct varlena * attr)
@@ -86,9 +87,7 @@ heap_tuple_fetch_attr(struct varlena * attr)
8687
else if (VARATT_IS_EXTERNAL_INDIRECT(attr))
8788
{
8889
/*
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
9291
*/
9392
struct varatt_indirect redirect;
9493

@@ -98,11 +97,14 @@ heap_tuple_fetch_attr(struct varlena * attr)
9897
/* nested indirect Datums aren't allowed */
9998
Assert(!VARATT_IS_EXTERNAL_INDIRECT(attr));
10099

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))
103102
return heap_tuple_fetch_attr(attr);
104103

105-
/* copy datum verbatim */
104+
/*
105+
* Copy into the caller's memory context, in case caller tries to
106+
* pfree the result.
107+
*/
106108
result = (struct varlena *) palloc(VARSIZE_ANY(attr));
107109
memcpy(result, attr, VARSIZE_ANY(attr));
108110
}
@@ -122,7 +124,10 @@ heap_tuple_fetch_attr(struct varlena * attr)
122124
* heap_tuple_untoast_attr -
123125
*
124126
* 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.
126131
* ----------
127132
*/
128133
struct varlena *
@@ -147,6 +152,9 @@ heap_tuple_untoast_attr(struct varlena * attr)
147152
}
148153
else if (VARATT_IS_EXTERNAL_INDIRECT(attr))
149154
{
155+
/*
156+
* This is an indirect pointer --- dereference it
157+
*/
150158
struct varatt_indirect redirect;
151159

152160
VARATT_EXTERNAL_GET_POINTER(redirect, attr);
@@ -155,7 +163,18 @@ heap_tuple_untoast_attr(struct varlena * attr)
155163
/* nested indirect Datums aren't allowed */
156164
Assert(!VARATT_IS_EXTERNAL_INDIRECT(attr));
157165

166+
/* recurse in case value is still extended in some other way */
158167
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+
}
159178
}
160179
else if (VARATT_IS_COMPRESSED(attr))
161180
{
@@ -231,6 +250,8 @@ heap_tuple_untoast_attr_slice(struct varlena * attr,
231250
else
232251
preslice = attr;
233252

253+
Assert(!VARATT_IS_EXTERNAL(preslice));
254+
234255
if (VARATT_IS_COMPRESSED(preslice))
235256
{
236257
PGLZ_Header *tmp = (PGLZ_Header *) preslice;
@@ -437,8 +458,6 @@ toast_delete(Relation rel, HeapTuple oldtup)
437458
continue;
438459
else if (VARATT_IS_EXTERNAL_ONDISK(PointerGetDatum(value)))
439460
toast_delete_datum(rel, value);
440-
else if (VARATT_IS_EXTERNAL_INDIRECT(PointerGetDatum(value)))
441-
elog(ERROR, "attempt to delete tuple containing indirect datums");
442461
}
443462
}
444463
}
@@ -600,7 +619,8 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
600619

601620
/*
602621
* 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).
604624
* Fetch it back (without decompression, unless we are forcing
605625
* PLAIN storage). If necessary, we'll push it out as a new
606626
* external value below.
@@ -1032,7 +1052,7 @@ toast_flatten_tuple(HeapTuple tup, TupleDesc tupleDesc)
10321052
new_value = (struct varlena *) DatumGetPointer(toast_values[i]);
10331053
if (VARATT_IS_EXTERNAL(new_value))
10341054
{
1035-
new_value = toast_fetch_datum(new_value);
1055+
new_value = heap_tuple_fetch_attr(new_value);
10361056
toast_values[i] = PointerGetDatum(new_value);
10371057
toast_free[i] = true;
10381058
}
@@ -1726,8 +1746,8 @@ toast_fetch_datum(struct varlena * attr)
17261746
int num_indexes;
17271747
int validIndex;
17281748

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");
17311751

17321752
/* Must copy to access aligned fields */
17331753
VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
@@ -1903,7 +1923,8 @@ toast_fetch_datum_slice(struct varlena * attr, int32 sliceoffset, int32 length)
19031923
int num_indexes;
19041924
int validIndex;
19051925

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");
19071928

19081929
/* Must copy to access aligned fields */
19091930
VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);

src/include/access/tuptoaster.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,10 @@
9696
VARHDRSZ)
9797

9898
/* Size of an EXTERNAL datum that contains a standard TOAST pointer */
99-
#define TOAST_POINTER_SIZE (VARHDRSZ_EXTERNAL + sizeof(struct varatt_external))
99+
#define TOAST_POINTER_SIZE (VARHDRSZ_EXTERNAL + sizeof(varatt_external))
100100

101-
/* Size of an indirect datum that contains a standard TOAST pointer */
102-
#define INDIRECT_POINTER_SIZE (VARHDRSZ_EXTERNAL + sizeof(struct varatt_indirect))
101+
/* Size of an EXTERNAL datum that contains an indirection pointer */
102+
#define INDIRECT_POINTER_SIZE (VARHDRSZ_EXTERNAL + sizeof(varatt_indirect))
103103

104104
/*
105105
* Testing whether an externally-stored value is compressed now requires

src/include/postgres.h

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,11 @@
5454
*/
5555

5656
/*
57-
* struct varatt_external is a "TOAST pointer", that is, the information needed
58-
* to fetch a Datum stored in an out-of-line on-disk Datum. The data is
59-
* compressed if and only if va_extsize < va_rawsize - VARHDRSZ. This struct
60-
* must not contain any padding, because we sometimes compare pointers using
61-
* memcmp.
57+
* struct varatt_external is a traditional "TOAST pointer", that is, the
58+
* information needed to fetch a Datum stored out-of-line in a TOAST table.
59+
* The data is compressed if and only if va_extsize < va_rawsize - VARHDRSZ.
60+
* This struct must not contain any padding, because we sometimes compare
61+
* these pointers using memcmp.
6262
*
6363
* Note that this information is stored unaligned within actual tuples, so
6464
* you need to memcpy from the tuple into a local struct variable before
@@ -74,22 +74,23 @@ typedef struct varatt_external
7474
} varatt_external;
7575

7676
/*
77-
* Out-of-line Datum thats stored in memory in contrast to varatt_external
78-
* pointers which points to data in an external toast relation.
77+
* struct varatt_indirect is a "TOAST pointer" representing an out-of-line
78+
* Datum that's stored in memory, not in an external toast relation.
79+
* The creator of such a Datum is entirely responsible that the referenced
80+
* storage survives for as long as referencing pointer Datums can exist.
7981
*
80-
* Note that just as varatt_external's this is stored unaligned within the
81-
* tuple.
82+
* Note that just as for struct varatt_external, this struct is stored
83+
* unaligned within any containing tuple.
8284
*/
8385
typedef struct varatt_indirect
8486
{
8587
struct varlena *pointer; /* Pointer to in-memory varlena */
8688
} varatt_indirect;
8789

88-
8990
/*
90-
* Type of external toast datum stored. The peculiar value for VARTAG_ONDISK
91-
* comes from the requirement for on-disk compatibility with the older
92-
* definitions of varattrib_1b_e where v_tag was named va_len_1be...
91+
* Type tag for the various sorts of "TOAST pointer" datums. The peculiar
92+
* value for VARTAG_ONDISK comes from a requirement for on-disk compatibility
93+
* with a previous notion that the tag field was the pointer datum's length.
9394
*/
9495
typedef enum vartag_external
9596
{
@@ -98,9 +99,9 @@ typedef enum vartag_external
9899
} vartag_external;
99100

100101
#define VARTAG_SIZE(tag) \
101-
((tag) == VARTAG_INDIRECT ? sizeof(varatt_indirect) : \
102+
((tag) == VARTAG_INDIRECT ? sizeof(varatt_indirect) : \
102103
(tag) == VARTAG_ONDISK ? sizeof(varatt_external) : \
103-
TrapMacro(true, "unknown vartag"))
104+
TrapMacro(true, "unrecognized TOAST vartag"))
104105

105106
/*
106107
* These structs describe the header of a varlena object that may have been
@@ -132,7 +133,7 @@ typedef struct
132133
char va_data[1]; /* Data begins here */
133134
} varattrib_1b;
134135

135-
/* inline portion of a short varlena pointing to an external resource */
136+
/* TOAST pointers are a subset of varattrib_1b with an identifying tag byte */
136137
typedef struct
137138
{
138139
uint8 va_header; /* Always 0x80 or 0x01 */
@@ -162,8 +163,8 @@ typedef struct
162163
* this lets us disambiguate alignment padding bytes from the start of an
163164
* unaligned datum. (We now *require* pad bytes to be filled with zero!)
164165
*
165-
* In TOAST datums the tag field in varattrib_1b_e is used to discern whether
166-
* its an indirection pointer or more commonly an on-disk tuple.
166+
* In TOAST pointers the va_tag field (see varattrib_1b_e) is used to discern
167+
* the specific type and length of the pointer datum.
167168
*/
168169

169170
/*

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy