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_ */