aboutsummaryrefslogtreecommitdiff
path: root/src/backend/access/common/detoast.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/access/common/detoast.c')
-rw-r--r--src/backend/access/common/detoast.c33
1 files changed, 26 insertions, 7 deletions
diff --git a/src/backend/access/common/detoast.c b/src/backend/access/common/detoast.c
index 44c37edcbb4..3d02d2b586c 100644
--- a/src/backend/access/common/detoast.c
+++ b/src/backend/access/common/detoast.c
@@ -17,6 +17,7 @@
#include "access/table.h"
#include "access/tableam.h"
#include "access/toast_internals.h"
+#include "common/int.h"
#include "common/pg_lzcompress.h"
#include "utils/expandeddatum.h"
#include "utils/rel.h"
@@ -196,7 +197,8 @@ detoast_attr(struct varlena *attr)
* Public entry point to get back part of a toasted value
* from compression or external storage.
*
- * Note: When slicelength is negative, return suffix of the value.
+ * sliceoffset is where to start (zero or more)
+ * If slicelength < 0, return everything beyond sliceoffset
* ----------
*/
struct varlena *
@@ -206,8 +208,21 @@ detoast_attr_slice(struct varlena *attr,
struct varlena *preslice;
struct varlena *result;
char *attrdata;
+ int32 slicelimit;
int32 attrsize;
+ if (sliceoffset < 0)
+ elog(ERROR, "invalid sliceoffset: %d", sliceoffset);
+
+ /*
+ * Compute slicelimit = offset + length, or -1 if we must fetch all of the
+ * value. In case of integer overflow, we must fetch all.
+ */
+ if (slicelength < 0)
+ slicelimit = -1;
+ else if (pg_add_s32_overflow(sliceoffset, slicelength, &slicelimit))
+ slicelength = slicelimit = -1;
+
if (VARATT_IS_EXTERNAL_ONDISK(attr))
{
struct varatt_external toast_pointer;
@@ -223,7 +238,7 @@ detoast_attr_slice(struct varlena *attr,
* at least the requested part (when a prefix is requested).
* Otherwise, just fetch all slices.
*/
- if (slicelength > 0 && sliceoffset >= 0)
+ if (slicelimit >= 0)
{
int32 max_size;
@@ -231,7 +246,7 @@ detoast_attr_slice(struct varlena *attr,
* Determine maximum amount of compressed data needed for a prefix
* of a given length (after decompression).
*/
- max_size = pglz_maximum_compressed_size(sliceoffset + slicelength,
+ max_size = pglz_maximum_compressed_size(slicelimit,
toast_pointer.va_extsize);
/*
@@ -270,8 +285,8 @@ detoast_attr_slice(struct varlena *attr,
struct varlena *tmp = preslice;
/* Decompress enough to encompass the slice and the offset */
- if (slicelength > 0 && sliceoffset >= 0)
- preslice = toast_decompress_datum_slice(tmp, slicelength + sliceoffset);
+ if (slicelimit >= 0)
+ preslice = toast_decompress_datum_slice(tmp, slicelimit);
else
preslice = toast_decompress_datum(tmp);
@@ -297,8 +312,7 @@ detoast_attr_slice(struct varlena *attr,
sliceoffset = 0;
slicelength = 0;
}
-
- if (((sliceoffset + slicelength) > attrsize) || slicelength < 0)
+ else if (slicelength < 0 || slicelimit > attrsize)
slicelength = attrsize - sliceoffset;
result = (struct varlena *) palloc(slicelength + VARHDRSZ);
@@ -410,6 +424,11 @@ toast_fetch_datum_slice(struct varlena *attr, int32 sliceoffset,
if (VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer) && slicelength > 0)
slicelength = slicelength + sizeof(int32);
+ /*
+ * Adjust length request if needed. (Note: our sole caller,
+ * detoast_attr_slice, protects us against sliceoffset + slicelength
+ * overflowing.)
+ */
if (((sliceoffset + slicelength) > attrsize) || slicelength < 0)
slicelength = attrsize - sliceoffset;