compressed-datum-slicing-20190313b.patch
application/octet-stream
Filename: compressed-datum-slicing-20190313b.patch
Type: application/octet-stream
Part: 0
Message:
Re: Compressed TOAST Slicing
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
| File | + | − |
|---|---|---|
| src/backend/access/heap/tuptoaster.c | 36 | 1 |
| src/backend/utils/adt/varlena.c | 14 | 9 |
| src/common/pg_lzcompress.c | 24 | 19 |
| src/include/common/pg_lzcompress.h | 2 | 0 |
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index cd921a4600..46b7d7a2a6 100644
--- a/src/backend/access/heap/tuptoaster.c
+++ b/src/backend/access/heap/tuptoaster.c
@@ -75,6 +75,7 @@ static struct varlena *toast_fetch_datum(struct varlena *attr);
static struct varlena *toast_fetch_datum_slice(struct varlena *attr,
int32 sliceoffset, int32 length);
static struct varlena *toast_decompress_datum(struct varlena *attr);
+static struct varlena *toast_decompress_datum_slice(struct varlena *attr, int32 slicelength);
static int toast_open_indexes(Relation toastrel,
LOCKMODE lock,
Relation **toastidxs,
@@ -301,7 +302,11 @@ heap_tuple_untoast_attr_slice(struct varlena *attr,
{
struct varlena *tmp = preslice;
- preslice = toast_decompress_datum(tmp);
+ /* Decompress enough to encompass the slice and the offset */
+ if (slicelength > 0 && sliceoffset >= 0)
+ preslice = toast_decompress_datum_slice(tmp, slicelength + sliceoffset);
+ else
+ preslice = toast_decompress_datum(tmp);
if (tmp != attr)
pfree(tmp);
@@ -2279,6 +2284,36 @@ toast_decompress_datum(struct varlena *attr)
}
+/* ----------
+ * toast_decompress_datum_slice -
+ *
+ * Decompress the front of a compressed version of a varlena datum.
+ * offset handling happens in heap_tuple_untoast_attr_slice.
+ * Here we just decompress a slice from the front.
+ */
+static struct varlena *
+toast_decompress_datum_slice(struct varlena *attr, int32 slicelength)
+{
+ struct varlena *result;
+ int32 rawsize;
+
+ Assert(VARATT_IS_COMPRESSED(attr));
+
+ result = (struct varlena *) palloc(slicelength + VARHDRSZ);
+ SET_VARSIZE(result, slicelength + VARHDRSZ);
+
+ rawsize = pglz_decompress_checked(TOAST_COMPRESS_RAWDATA(attr),
+ VARSIZE(attr) - TOAST_COMPRESS_HDRSZ,
+ VARDATA(result),
+ slicelength, false);
+ if (rawsize < 0)
+ elog(ERROR, "compressed data is corrupted");
+
+ SET_VARSIZE(result, rawsize + VARHDRSZ);
+ return result;
+}
+
+
/* ----------
* toast_open_indexes
*
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index 39c394331b..d89c0b7201 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -1835,7 +1835,7 @@ text_starts_with(PG_FUNCTION_ARGS)
result = false;
else
{
- text *targ1 = DatumGetTextPP(arg1);
+ text *targ1 = text_substring(arg1, 1, len2, false);
text *targ2 = DatumGetTextPP(arg2);
result = (memcmp(VARDATA_ANY(targ1), VARDATA_ANY(targ2),
@@ -5237,17 +5237,22 @@ text_concat_ws(PG_FUNCTION_ARGS)
Datum
text_left(PG_FUNCTION_ARGS)
{
- text *str = PG_GETARG_TEXT_PP(0);
- const char *p = VARDATA_ANY(str);
- int len = VARSIZE_ANY_EXHDR(str);
- int n = PG_GETARG_INT32(1);
- int rlen;
+ int n = PG_GETARG_INT32(1);
if (n < 0)
+ {
+ text *str = PG_GETARG_TEXT_PP(0);
+ const char *p = VARDATA_ANY(str);
+ int len = VARSIZE_ANY_EXHDR(str);
+ int rlen;
n = pg_mbstrlen_with_len(p, len) + n;
- rlen = pg_mbcharcliplen(p, len, n);
-
- PG_RETURN_TEXT_P(cstring_to_text_with_len(p, rlen));
+ rlen = pg_mbcharcliplen(p, len, n);
+ PG_RETURN_TEXT_P(cstring_to_text_with_len(p, rlen));
+ }
+ else
+ {
+ PG_RETURN_TEXT_P(text_substring(PG_GETARG_DATUM(0), 1, n, false));
+ }
}
/*
diff --git a/src/common/pg_lzcompress.c b/src/common/pg_lzcompress.c
index f15725a57c..05b2b3d5d1 100644
--- a/src/common/pg_lzcompress.c
+++ b/src/common/pg_lzcompress.c
@@ -681,6 +681,22 @@ pglz_compress(const char *source, int32 slen, char *dest,
int32
pglz_decompress(const char *source, int32 slen, char *dest,
int32 rawsize)
+{
+ return pglz_decompress_checked(source, slen, dest, rawsize, true);
+}
+
+/* ----------
+ * pglz_decompress_checked -
+ *
+ * Decompresses source into dest. Returns the number of bytes
+ * decompressed in the destination buffer, and *optionally*
+ * checks that both the source and dest buffers have been
+ * fully read and written to, respectively.
+ * ----------
+ */
+int32
+pglz_decompress_checked(const char *source, int32 slen, char *dest,
+ int32 rawsize, bool check_buffers)
{
const unsigned char *sp;
const unsigned char *srcend;
@@ -701,8 +717,9 @@ pglz_decompress(const char *source, int32 slen, char *dest,
unsigned char ctrl = *sp++;
int ctrlc;
- for (ctrlc = 0; ctrlc < 8 && sp < srcend; ctrlc++)
+ for (ctrlc = 0; ctrlc < 8 && sp < srcend && dp < destend; ctrlc++)
{
+
if (ctrl & 1)
{
/*
@@ -721,25 +738,13 @@ pglz_decompress(const char *source, int32 slen, char *dest,
if (len == 18)
len += *sp++;
- /*
- * Check for output buffer overrun, to ensure we don't clobber
- * memory in case of corrupt input. Note: we must advance dp
- * here to ensure the error is detected below the loop. We
- * don't simply put the elog inside the loop since that will
- * probably interfere with optimization.
- */
- if (dp + len > destend)
- {
- dp += len;
- break;
- }
-
/*
* Now we copy the bytes specified by the tag from OUTPUT to
* OUTPUT. It is dangerous and platform dependent to use
* memcpy() here, because the copied areas could overlap
* extremely!
*/
+ len = Min(len, destend - dp);
while (len--)
{
*dp = dp[-off];
@@ -752,9 +757,6 @@ pglz_decompress(const char *source, int32 slen, char *dest,
* An unset control bit means LITERAL BYTE. So we just copy
* one from INPUT to OUTPUT.
*/
- if (dp >= destend) /* check for buffer overrun */
- break; /* do not clobber memory */
-
*dp++ = *sp++;
}
@@ -767,12 +769,15 @@ pglz_decompress(const char *source, int32 slen, char *dest,
/*
* Check we decompressed the right amount.
+ * If we are slicing, then we won't necessarily
+ * be at the end of the source or dest buffers
+ * when we hit a stop, so we don't test them.
*/
- if (dp != destend || sp != srcend)
+ if (check_buffers && (dp != destend || sp != srcend))
return -1;
/*
* That's it.
*/
- return rawsize;
+ return (char*)dp - dest;
}
diff --git a/src/include/common/pg_lzcompress.h b/src/include/common/pg_lzcompress.h
index d4b2e8a53c..63fab7b642 100644
--- a/src/include/common/pg_lzcompress.h
+++ b/src/include/common/pg_lzcompress.h
@@ -87,5 +87,7 @@ extern int32 pglz_compress(const char *source, int32 slen, char *dest,
const PGLZ_Strategy *strategy);
extern int32 pglz_decompress(const char *source, int32 slen, char *dest,
int32 rawsize);
+extern int32 pglz_decompress_checked(const char *source, int32 slen, char *dest,
+ int32 rawsize, bool check_buffers);
#endif /* _PG_LZCOMPRESS_H_ */