Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
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)