aboutsummaryrefslogtreecommitdiff
path: root/src/backend/access/heap/tuptoaster.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/access/heap/tuptoaster.c')
-rw-r--r--src/backend/access/heap/tuptoaster.c31
1 files changed, 26 insertions, 5 deletions
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index 3a9d2ff38ed..f9d1fb36bc8 100644
--- a/src/backend/access/heap/tuptoaster.c
+++ b/src/backend/access/heap/tuptoaster.c
@@ -35,6 +35,7 @@
#include "access/tuptoaster.h"
#include "access/xact.h"
#include "catalog/catalog.h"
+#include "common/int.h"
#include "common/pg_lzcompress.h"
#include "miscadmin.h"
#include "utils/expandeddatum.h"
@@ -252,6 +253,9 @@ heap_tuple_untoast_attr(struct varlena *attr)
*
* Public entry point to get back part of a toasted value
* from compression or external storage.
+ *
+ * sliceoffset is where to start (zero or more)
+ * If slicelength < 0, return everything beyond sliceoffset
* ----------
*/
struct varlena *
@@ -261,8 +265,21 @@ heap_tuple_untoast_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;
@@ -303,8 +320,8 @@ heap_tuple_untoast_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);
@@ -330,8 +347,7 @@ heap_tuple_untoast_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);
@@ -2086,7 +2102,12 @@ toast_fetch_datum_slice(struct varlena *attr, int32 sliceoffset, int32 length)
length = 0;
}
- if (((sliceoffset + length) > attrsize) || length < 0)
+ /*
+ * Adjust length request if needed. (Note: our sole caller,
+ * heap_tuple_untoast_attr_slice, protects us against sliceoffset + length
+ * overflowing.)
+ */
+ else if (((sliceoffset + length) > attrsize) || length < 0)
length = attrsize - sliceoffset;
result = (struct varlena *) palloc(length + VARHDRSZ);