compressed-datum-slicing-20190101a.patch
application/octet-stream
Filename: compressed-datum-slicing-20190101a.patch
Type: application/octet-stream
Part: 0
Message:
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 | 9 | 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 cd42c50b09..828a75a531 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,10 @@ heap_tuple_untoast_attr_slice(struct varlena *attr,
{
struct varlena *tmp = preslice;
- preslice = toast_decompress_datum(tmp);
+ if (sliceoffset == 0 && slicelength > 0)
+ preslice = toast_decompress_datum_slice(tmp, slicelength);
+ else
+ preslice = toast_decompress_datum(tmp);
if (tmp != attr)
pfree(tmp);
@@ -2287,13 +2291,44 @@ 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
+ * We do not try to slice with an offset, as that means decompressing
+ * and then throwing away the data at 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(TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
+ SET_VARSIZE(result, TOAST_COMPRESS_RAWSIZE(attr) + 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;
+}
+
+
/* ----------
* toast_open_indexes
*
diff --git a/src/backend/access/transam/xlogreader.c b/src/backend/access/transam/xlogreader.c
index 0768ca7822..9f91ae240f 100644
--- a/src/backend/access/transam/xlogreader.c
+++ b/src/backend/access/transam/xlogreader.c
@@ -1426,7 +1426,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..0e8b01ba5d 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++;
}
@@ -768,11 +758,11 @@ pglz_decompress(const char *source, int32 slen, char *dest,
/*
* Check we decompressed the right amount.
*/
- 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_ */