Age Owner TLA Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * toast_compression.c
4 : * Functions for toast compression.
5 : *
6 : * Copyright (c) 2021-2023, PostgreSQL Global Development Group
7 : *
8 : *
9 : * IDENTIFICATION
10 : * src/backend/access/common/toast_compression.c
11 : *
12 : *-------------------------------------------------------------------------
13 : */
14 : #include "postgres.h"
15 :
16 : #ifdef USE_LZ4
17 : #include <lz4.h>
18 : #endif
19 :
20 : #include "access/detoast.h"
21 : #include "access/toast_compression.h"
22 : #include "common/pg_lzcompress.h"
23 : #include "fmgr.h"
24 : #include "utils/builtins.h"
25 : #include "varatt.h"
26 :
27 : /* GUC */
28 : int default_toast_compression = TOAST_PGLZ_COMPRESSION;
29 :
30 : #define NO_LZ4_SUPPORT() \
31 : ereport(ERROR, \
32 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), \
33 : errmsg("compression method lz4 not supported"), \
34 : errdetail("This functionality requires the server to be built with lz4 support.")))
35 :
36 : /*
37 : * Compress a varlena using PGLZ.
38 : *
39 : * Returns the compressed varlena, or NULL if compression fails.
40 : */
41 : struct varlena *
751 rhaas 42 GIC 70161 : pglz_compress_datum(const struct varlena *value)
751 rhaas 43 ECB : {
44 : int32 valsize,
45 : len;
751 rhaas 46 GIC 70161 : struct varlena *tmp = NULL;
751 rhaas 47 ECB :
224 peter 48 GNC 70161 : valsize = VARSIZE_ANY_EXHDR(value);
751 rhaas 49 ECB :
50 : /*
51 : * No point in wasting a palloc cycle if value size is outside the allowed
52 : * range for compression.
53 : */
751 rhaas 54 GIC 70161 : if (valsize < PGLZ_strategy_default->min_input_size ||
751 rhaas 55 CBC 69161 : valsize > PGLZ_strategy_default->max_input_size)
56 1000 : return NULL;
751 rhaas 57 ECB :
58 : /*
59 : * Figure out the maximum possible size of the pglz output, add the bytes
60 : * that will be needed for varlena overhead, and allocate that amount.
61 : */
751 rhaas 62 GIC 69161 : tmp = (struct varlena *) palloc(PGLZ_MAX_OUTPUT(valsize) +
748 tgl 63 ECB : VARHDRSZ_COMPRESSED);
64 :
751 rhaas 65 GIC 69161 : len = pglz_compress(VARDATA_ANY(value),
751 rhaas 66 ECB : valsize,
67 : (char *) tmp + VARHDRSZ_COMPRESSED,
68 : NULL);
751 rhaas 69 GIC 69161 : if (len < 0)
751 rhaas 70 ECB : {
751 rhaas 71 GIC 1775 : pfree(tmp);
751 rhaas 72 CBC 1775 : return NULL;
751 rhaas 73 ECB : }
74 :
748 tgl 75 GIC 67386 : SET_VARSIZE_COMPRESSED(tmp, len + VARHDRSZ_COMPRESSED);
751 rhaas 76 ECB :
751 rhaas 77 GIC 67386 : return tmp;
751 rhaas 78 ECB : }
79 :
80 : /*
81 : * Decompress a varlena that was compressed using PGLZ.
82 : */
83 : struct varlena *
751 rhaas 84 GIC 86368 : pglz_decompress_datum(const struct varlena *value)
751 rhaas 85 ECB : {
86 : struct varlena *result;
87 : int32 rawsize;
88 :
89 : /* allocate memory for the uncompressed data */
748 tgl 90 GIC 86368 : result = (struct varlena *) palloc(VARDATA_COMPRESSED_GET_EXTSIZE(value) + VARHDRSZ);
751 rhaas 91 ECB :
92 : /* decompress the data */
748 tgl 93 GIC 86368 : rawsize = pglz_decompress((char *) value + VARHDRSZ_COMPRESSED,
748 tgl 94 CBC 86368 : VARSIZE(value) - VARHDRSZ_COMPRESSED,
751 rhaas 95 86368 : VARDATA(result),
748 tgl 96 86368 : VARDATA_COMPRESSED_GET_EXTSIZE(value), true);
751 rhaas 97 86368 : if (rawsize < 0)
751 rhaas 98 LBC 0 : ereport(ERROR,
751 rhaas 99 EUB : (errcode(ERRCODE_DATA_CORRUPTED),
100 : errmsg_internal("compressed pglz data is corrupt")));
101 :
751 rhaas 102 GIC 86368 : SET_VARSIZE(result, rawsize + VARHDRSZ);
751 rhaas 103 ECB :
751 rhaas 104 GIC 86368 : return result;
751 rhaas 105 ECB : }
106 :
107 : /*
108 : * Decompress part of a varlena that was compressed using PGLZ.
109 : */
110 : struct varlena *
751 rhaas 111 GIC 33 : pglz_decompress_datum_slice(const struct varlena *value,
697 tgl 112 ECB : int32 slicelength)
113 : {
114 : struct varlena *result;
115 : int32 rawsize;
116 :
117 : /* allocate memory for the uncompressed data */
751 rhaas 118 GIC 33 : result = (struct varlena *) palloc(slicelength + VARHDRSZ);
751 rhaas 119 ECB :
120 : /* decompress the data */
748 tgl 121 GIC 33 : rawsize = pglz_decompress((char *) value + VARHDRSZ_COMPRESSED,
748 tgl 122 CBC 33 : VARSIZE(value) - VARHDRSZ_COMPRESSED,
751 rhaas 123 33 : VARDATA(result),
751 rhaas 124 ECB : slicelength, false);
751 rhaas 125 GIC 33 : if (rawsize < 0)
751 rhaas 126 LBC 0 : ereport(ERROR,
751 rhaas 127 EUB : (errcode(ERRCODE_DATA_CORRUPTED),
128 : errmsg_internal("compressed pglz data is corrupt")));
129 :
751 rhaas 130 GIC 33 : SET_VARSIZE(result, rawsize + VARHDRSZ);
751 rhaas 131 ECB :
751 rhaas 132 GIC 33 : return result;
751 rhaas 133 ECB : }
134 :
135 : /*
136 : * Compress a varlena using LZ4.
137 : *
138 : * Returns the compressed varlena, or NULL if compression fails.
139 : */
140 : struct varlena *
751 rhaas 141 GIC 18 : lz4_compress_datum(const struct varlena *value)
751 rhaas 142 ECB : {
143 : #ifndef USE_LZ4
144 : NO_LZ4_SUPPORT();
145 : return NULL; /* keep compiler quiet */
146 : #else
147 : int32 valsize;
148 : int32 len;
149 : int32 max_size;
751 rhaas 150 GIC 18 : struct varlena *tmp = NULL;
751 rhaas 151 ECB :
751 rhaas 152 GIC 18 : valsize = VARSIZE_ANY_EXHDR(value);
751 rhaas 153 ECB :
154 : /*
155 : * Figure out the maximum possible size of the LZ4 output, add the bytes
156 : * that will be needed for varlena overhead, and allocate that amount.
157 : */
751 rhaas 158 GIC 18 : max_size = LZ4_compressBound(valsize);
748 tgl 159 CBC 18 : tmp = (struct varlena *) palloc(max_size + VARHDRSZ_COMPRESSED);
751 rhaas 160 ECB :
751 rhaas 161 GIC 18 : len = LZ4_compress_default(VARDATA_ANY(value),
748 tgl 162 ECB : (char *) tmp + VARHDRSZ_COMPRESSED,
163 : valsize, max_size);
751 rhaas 164 GIC 18 : if (len <= 0)
751 rhaas 165 LBC 0 : elog(ERROR, "lz4 compression failed");
751 rhaas 166 EUB :
167 : /* data is incompressible so just free the memory and return NULL */
751 rhaas 168 GIC 18 : if (len > valsize)
751 rhaas 169 ECB : {
751 rhaas 170 UIC 0 : pfree(tmp);
751 rhaas 171 UBC 0 : return NULL;
751 rhaas 172 EUB : }
173 :
748 tgl 174 GIC 18 : SET_VARSIZE_COMPRESSED(tmp, len + VARHDRSZ_COMPRESSED);
751 rhaas 175 ECB :
751 rhaas 176 GIC 18 : return tmp;
751 rhaas 177 ECB : #endif
178 : }
179 :
180 : /*
181 : * Decompress a varlena that was compressed using LZ4.
182 : */
183 : struct varlena *
751 rhaas 184 GIC 52 : lz4_decompress_datum(const struct varlena *value)
751 rhaas 185 ECB : {
186 : #ifndef USE_LZ4
187 : NO_LZ4_SUPPORT();
188 : return NULL; /* keep compiler quiet */
189 : #else
190 : int32 rawsize;
191 : struct varlena *result;
192 :
193 : /* allocate memory for the uncompressed data */
748 tgl 194 GIC 52 : result = (struct varlena *) palloc(VARDATA_COMPRESSED_GET_EXTSIZE(value) + VARHDRSZ);
751 rhaas 195 ECB :
196 : /* decompress the data */
748 tgl 197 GIC 52 : rawsize = LZ4_decompress_safe((char *) value + VARHDRSZ_COMPRESSED,
751 rhaas 198 CBC 52 : VARDATA(result),
748 tgl 199 52 : VARSIZE(value) - VARHDRSZ_COMPRESSED,
200 52 : VARDATA_COMPRESSED_GET_EXTSIZE(value));
751 rhaas 201 52 : if (rawsize < 0)
751 rhaas 202 LBC 0 : ereport(ERROR,
751 rhaas 203 EUB : (errcode(ERRCODE_DATA_CORRUPTED),
204 : errmsg_internal("compressed lz4 data is corrupt")));
205 :
206 :
751 rhaas 207 GIC 52 : SET_VARSIZE(result, rawsize + VARHDRSZ);
751 rhaas 208 ECB :
751 rhaas 209 GIC 52 : return result;
751 rhaas 210 ECB : #endif
211 : }
212 :
213 : /*
214 : * Decompress part of a varlena that was compressed using LZ4.
215 : */
216 : struct varlena *
751 rhaas 217 GIC 9 : lz4_decompress_datum_slice(const struct varlena *value, int32 slicelength)
751 rhaas 218 ECB : {
219 : #ifndef USE_LZ4
220 : NO_LZ4_SUPPORT();
221 : return NULL; /* keep compiler quiet */
222 : #else
223 : int32 rawsize;
224 : struct varlena *result;
225 :
226 : /* slice decompression not supported prior to 1.8.3 */
751 rhaas 227 GIC 9 : if (LZ4_versionNumber() < 10803)
751 rhaas 228 LBC 0 : return lz4_decompress_datum(value);
751 rhaas 229 EUB :
230 : /* allocate memory for the uncompressed data */
751 rhaas 231 GIC 9 : result = (struct varlena *) palloc(slicelength + VARHDRSZ);
751 rhaas 232 ECB :
233 : /* decompress the data */
748 tgl 234 GIC 9 : rawsize = LZ4_decompress_safe_partial((char *) value + VARHDRSZ_COMPRESSED,
751 rhaas 235 CBC 9 : VARDATA(result),
748 tgl 236 9 : VARSIZE(value) - VARHDRSZ_COMPRESSED,
751 rhaas 237 ECB : slicelength,
238 : slicelength);
751 rhaas 239 GIC 9 : if (rawsize < 0)
751 rhaas 240 LBC 0 : ereport(ERROR,
751 rhaas 241 EUB : (errcode(ERRCODE_DATA_CORRUPTED),
242 : errmsg_internal("compressed lz4 data is corrupt")));
243 :
751 rhaas 244 GIC 9 : SET_VARSIZE(result, rawsize + VARHDRSZ);
751 rhaas 245 ECB :
751 rhaas 246 GIC 9 : return result;
751 rhaas 247 ECB : #endif
248 : }
249 :
250 : /*
251 : * Extract compression ID from a varlena.
252 : *
253 : * Returns TOAST_INVALID_COMPRESSION_ID if the varlena is not compressed.
254 : */
255 : ToastCompressionId
751 rhaas 256 GIC 81 : toast_get_compression_id(struct varlena *attr)
751 rhaas 257 ECB : {
697 tgl 258 GIC 81 : ToastCompressionId cmid = TOAST_INVALID_COMPRESSION_ID;
751 rhaas 259 ECB :
260 : /*
261 : * If it is stored externally then fetch the compression method id from
262 : * the external toast pointer. If compressed inline, fetch it from the
263 : * toast compression header.
264 : */
751 rhaas 265 GIC 81 : if (VARATT_IS_EXTERNAL_ONDISK(attr))
751 rhaas 266 CBC 12 : {
751 rhaas 267 ECB : struct varatt_external toast_pointer;
268 :
751 rhaas 269 GIC 12 : VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
751 rhaas 270 ECB :
751 rhaas 271 GIC 12 : if (VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer))
748 tgl 272 CBC 12 : cmid = VARATT_EXTERNAL_GET_COMPRESS_METHOD(toast_pointer);
751 rhaas 273 ECB : }
751 rhaas 274 GIC 69 : else if (VARATT_IS_COMPRESSED(attr))
748 tgl 275 CBC 66 : cmid = VARDATA_COMPRESSED_GET_COMPRESS_METHOD(attr);
751 rhaas 276 ECB :
751 rhaas 277 GIC 81 : return cmid;
751 rhaas 278 ECB : }
279 :
280 : /*
281 : * CompressionNameToMethod - Get compression method from compression name
282 : *
283 : * Search in the available built-in methods. If the compression not found
284 : * in the built-in methods then return InvalidCompressionMethod.
285 : */
286 : char
746 rhaas 287 GIC 68 : CompressionNameToMethod(const char *compression)
751 rhaas 288 ECB : {
746 rhaas 289 GIC 68 : if (strcmp(compression, "pglz") == 0)
746 rhaas 290 CBC 30 : return TOAST_PGLZ_COMPRESSION;
291 38 : else if (strcmp(compression, "lz4") == 0)
751 rhaas 292 ECB : {
293 : #ifndef USE_LZ4
294 : NO_LZ4_SUPPORT();
295 : #endif
746 rhaas 296 GIC 32 : return TOAST_LZ4_COMPRESSION;
751 rhaas 297 ECB : }
298 :
746 rhaas 299 GIC 6 : return InvalidCompressionMethod;
746 rhaas 300 ECB : }
301 :
302 : /*
303 : * GetCompressionMethodName - Get compression method name
304 : */
305 : const char *
746 rhaas 306 GIC 15 : GetCompressionMethodName(char method)
746 rhaas 307 ECB : {
746 rhaas 308 GIC 15 : switch (method)
751 rhaas 309 ECB : {
746 rhaas 310 GIC 6 : case TOAST_PGLZ_COMPRESSION:
746 rhaas 311 CBC 6 : return "pglz";
312 9 : case TOAST_LZ4_COMPRESSION:
313 9 : return "lz4";
746 rhaas 314 LBC 0 : default:
746 rhaas 315 UBC 0 : elog(ERROR, "invalid compression method %c", method);
746 rhaas 316 EUB : return NULL; /* keep compiler quiet */
317 : }
318 : }
|