compressed-datum-slicing-20190103a.patch
application/octet-stream
Filename: compressed-datum-slicing-20190103a.patch
Type: application/octet-stream
Part: 1
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 | 37 | 2 |
| src/backend/access/transam/xlogreader.c | 1 | 1 |
| src/common/pg_lzcompress.c | 12 | 19 |
| src/include/common/pg_lzcompress.h | 1 | 1 |
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index fdbaf38126..ff47c07f02 100644
--- a/src/backend/access/heap/tuptoaster.c
+++ b/src/backend/access/heap/tuptoaster.c
@@ -76,6 +76,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,
@@ -302,7 +303,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);
@@ -2274,9 +2279,39 @@ toast_decompress_datum(struct varlena *attr)
if (pglz_decompress(TOAST_COMPRESS_RAWDATA(attr),
VARSIZE(attr) - TOAST_COMPRESS_HDRSZ,
VARDATA(result),
- TOAST_COMPRESS_RAWSIZE(attr)) < 0)
+ TOAST_COMPRESS_RAWSIZE(attr), false) < 0)
+ elog(ERROR, "compressed data is corrupted");
+
+ return result;
+}
+
+
+/* ----------
+ * 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(TOAST_COMPRESS_RAWDATA(attr),
+ VARSIZE(attr) - TOAST_COMPRESS_HDRSZ,
+ VARDATA(result),
+ slicelength, true);
+ if (rawsize < 0)
elog(ERROR, "compressed data is corrupted");
+ SET_VARSIZE(result, rawsize + VARHDRSZ);
return result;
}
diff --git a/src/backend/access/transam/xlogreader.c b/src/backend/access/transam/xlogreader.c
index c5e019bf77..859b3ec4fd 100644
--- a/src/backend/access/transam/xlogreader.c
+++ b/src/backend/access/transam/xlogreader.c
@@ -1425,7 +1425,7 @@ RestoreBlockImage(XLogReaderState *record, uint8 block_id, char *page)
{
/* If a backup block image is compressed, decompress it */
if (pglz_decompress(ptr, bkpb->bimg_len, tmp.data,
- BLCKSZ - bkpb->hole_length) < 0)
+ BLCKSZ - bkpb->hole_length, false) < 0)
{
report_invalid_record(record, "invalid compressed image at %X/%X, block %d",
(uint32) (record->ReadRecPtr >> 32),
diff --git a/src/common/pg_lzcompress.c b/src/common/pg_lzcompress.c
index 2d25da3a23..4a46b9376b 100644
--- a/src/common/pg_lzcompress.c
+++ b/src/common/pg_lzcompress.c
@@ -680,7 +680,7 @@ pglz_compress(const char *source, int32 slen, char *dest,
*/
int32
pglz_decompress(const char *source, int32 slen, char *dest,
- int32 rawsize)
+ int32 rawsize, bool is_slice)
{
const unsigned char *sp;
const unsigned char *srcend;
@@ -703,6 +703,10 @@ pglz_decompress(const char *source, int32 slen, char *dest,
for (ctrlc = 0; ctrlc < 8 && sp < srcend; ctrlc++)
{
+
+ if (dp >= destend) /* check for buffer overrun */
+ break; /* do not clobber memory */
+
if (ctrl & 1)
{
/*
@@ -721,19 +725,6 @@ 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
@@ -744,6 +735,8 @@ pglz_decompress(const char *source, int32 slen, char *dest,
{
*dp = dp[-off];
dp++;
+ if (dp >= destend) /* check for buffer overrun */
+ break; /* do not clobber memory */
}
}
else
@@ -752,9 +745,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 +757,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 then.
*/
- if (dp != destend || sp != srcend)
+ if (!is_slice && (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..d8364cd34a 100644
--- a/src/include/common/pg_lzcompress.h
+++ b/src/include/common/pg_lzcompress.h
@@ -86,6 +86,6 @@ extern const PGLZ_Strategy *const PGLZ_strategy_always;
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);
+ int32 rawsize, bool is_slice);
#endif /* _PG_LZCOMPRESS_H_ */