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 *
42 GIC 70161 : pglz_compress_datum(const struct varlena *value)
43 ECB : {
44 : int32 valsize,
45 : len;
46 GIC 70161 : struct varlena *tmp = NULL;
47 ECB :
48 GNC 70161 : valsize = VARSIZE_ANY_EXHDR(value);
49 ECB :
50 : /*
51 : * No point in wasting a palloc cycle if value size is outside the allowed
52 : * range for compression.
53 : */
54 GIC 70161 : if (valsize < PGLZ_strategy_default->min_input_size ||
55 CBC 69161 : valsize > PGLZ_strategy_default->max_input_size)
56 1000 : return NULL;
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 : */
62 GIC 69161 : tmp = (struct varlena *) palloc(PGLZ_MAX_OUTPUT(valsize) +
63 ECB : VARHDRSZ_COMPRESSED);
64 :
65 GIC 69161 : len = pglz_compress(VARDATA_ANY(value),
66 ECB : valsize,
67 : (char *) tmp + VARHDRSZ_COMPRESSED,
68 : NULL);
69 GIC 69161 : if (len < 0)
70 ECB : {
71 GIC 1775 : pfree(tmp);
72 CBC 1775 : return NULL;
73 ECB : }
74 :
75 GIC 67386 : SET_VARSIZE_COMPRESSED(tmp, len + VARHDRSZ_COMPRESSED);
76 ECB :
77 GIC 67386 : return tmp;
78 ECB : }
79 :
80 : /*
81 : * Decompress a varlena that was compressed using PGLZ.
82 : */
83 : struct varlena *
84 GIC 86368 : pglz_decompress_datum(const struct varlena *value)
85 ECB : {
86 : struct varlena *result;
87 : int32 rawsize;
88 :
89 : /* allocate memory for the uncompressed data */
90 GIC 86368 : result = (struct varlena *) palloc(VARDATA_COMPRESSED_GET_EXTSIZE(value) + VARHDRSZ);
91 ECB :
92 : /* decompress the data */
93 GIC 86368 : rawsize = pglz_decompress((char *) value + VARHDRSZ_COMPRESSED,
94 CBC 86368 : VARSIZE(value) - VARHDRSZ_COMPRESSED,
95 86368 : VARDATA(result),
96 86368 : VARDATA_COMPRESSED_GET_EXTSIZE(value), true);
97 86368 : if (rawsize < 0)
98 LBC 0 : ereport(ERROR,
99 EUB : (errcode(ERRCODE_DATA_CORRUPTED),
100 : errmsg_internal("compressed pglz data is corrupt")));
101 :
102 GIC 86368 : SET_VARSIZE(result, rawsize + VARHDRSZ);
103 ECB :
104 GIC 86368 : return result;
105 ECB : }
106 :
107 : /*
108 : * Decompress part of a varlena that was compressed using PGLZ.
109 : */
110 : struct varlena *
111 GIC 33 : pglz_decompress_datum_slice(const struct varlena *value,
112 ECB : int32 slicelength)
113 : {
114 : struct varlena *result;
115 : int32 rawsize;
116 :
117 : /* allocate memory for the uncompressed data */
118 GIC 33 : result = (struct varlena *) palloc(slicelength + VARHDRSZ);
119 ECB :
120 : /* decompress the data */
121 GIC 33 : rawsize = pglz_decompress((char *) value + VARHDRSZ_COMPRESSED,
122 CBC 33 : VARSIZE(value) - VARHDRSZ_COMPRESSED,
123 33 : VARDATA(result),
124 ECB : slicelength, false);
125 GIC 33 : if (rawsize < 0)
126 LBC 0 : ereport(ERROR,
127 EUB : (errcode(ERRCODE_DATA_CORRUPTED),
128 : errmsg_internal("compressed pglz data is corrupt")));
129 :
130 GIC 33 : SET_VARSIZE(result, rawsize + VARHDRSZ);
131 ECB :
132 GIC 33 : return result;
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 *
141 GIC 18 : lz4_compress_datum(const struct varlena *value)
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;
150 GIC 18 : struct varlena *tmp = NULL;
151 ECB :
152 GIC 18 : valsize = VARSIZE_ANY_EXHDR(value);
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 : */
158 GIC 18 : max_size = LZ4_compressBound(valsize);
159 CBC 18 : tmp = (struct varlena *) palloc(max_size + VARHDRSZ_COMPRESSED);
160 ECB :
161 GIC 18 : len = LZ4_compress_default(VARDATA_ANY(value),
162 ECB : (char *) tmp + VARHDRSZ_COMPRESSED,
163 : valsize, max_size);
164 GIC 18 : if (len <= 0)
165 LBC 0 : elog(ERROR, "lz4 compression failed");
166 EUB :
167 : /* data is incompressible so just free the memory and return NULL */
168 GIC 18 : if (len > valsize)
169 ECB : {
170 UIC 0 : pfree(tmp);
171 UBC 0 : return NULL;
172 EUB : }
173 :
174 GIC 18 : SET_VARSIZE_COMPRESSED(tmp, len + VARHDRSZ_COMPRESSED);
175 ECB :
176 GIC 18 : return tmp;
177 ECB : #endif
178 : }
179 :
180 : /*
181 : * Decompress a varlena that was compressed using LZ4.
182 : */
183 : struct varlena *
184 GIC 52 : lz4_decompress_datum(const struct varlena *value)
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 */
194 GIC 52 : result = (struct varlena *) palloc(VARDATA_COMPRESSED_GET_EXTSIZE(value) + VARHDRSZ);
195 ECB :
196 : /* decompress the data */
197 GIC 52 : rawsize = LZ4_decompress_safe((char *) value + VARHDRSZ_COMPRESSED,
198 CBC 52 : VARDATA(result),
199 52 : VARSIZE(value) - VARHDRSZ_COMPRESSED,
200 52 : VARDATA_COMPRESSED_GET_EXTSIZE(value));
201 52 : if (rawsize < 0)
202 LBC 0 : ereport(ERROR,
203 EUB : (errcode(ERRCODE_DATA_CORRUPTED),
204 : errmsg_internal("compressed lz4 data is corrupt")));
205 :
206 :
207 GIC 52 : SET_VARSIZE(result, rawsize + VARHDRSZ);
208 ECB :
209 GIC 52 : return result;
210 ECB : #endif
211 : }
212 :
213 : /*
214 : * Decompress part of a varlena that was compressed using LZ4.
215 : */
216 : struct varlena *
217 GIC 9 : lz4_decompress_datum_slice(const struct varlena *value, int32 slicelength)
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 */
227 GIC 9 : if (LZ4_versionNumber() < 10803)
228 LBC 0 : return lz4_decompress_datum(value);
229 EUB :
230 : /* allocate memory for the uncompressed data */
231 GIC 9 : result = (struct varlena *) palloc(slicelength + VARHDRSZ);
232 ECB :
233 : /* decompress the data */
234 GIC 9 : rawsize = LZ4_decompress_safe_partial((char *) value + VARHDRSZ_COMPRESSED,
235 CBC 9 : VARDATA(result),
236 9 : VARSIZE(value) - VARHDRSZ_COMPRESSED,
237 ECB : slicelength,
238 : slicelength);
239 GIC 9 : if (rawsize < 0)
240 LBC 0 : ereport(ERROR,
241 EUB : (errcode(ERRCODE_DATA_CORRUPTED),
242 : errmsg_internal("compressed lz4 data is corrupt")));
243 :
244 GIC 9 : SET_VARSIZE(result, rawsize + VARHDRSZ);
245 ECB :
246 GIC 9 : return result;
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
256 GIC 81 : toast_get_compression_id(struct varlena *attr)
257 ECB : {
258 GIC 81 : ToastCompressionId cmid = TOAST_INVALID_COMPRESSION_ID;
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 : */
265 GIC 81 : if (VARATT_IS_EXTERNAL_ONDISK(attr))
266 CBC 12 : {
267 ECB : struct varatt_external toast_pointer;
268 :
269 GIC 12 : VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
270 ECB :
271 GIC 12 : if (VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer))
272 CBC 12 : cmid = VARATT_EXTERNAL_GET_COMPRESS_METHOD(toast_pointer);
273 ECB : }
274 GIC 69 : else if (VARATT_IS_COMPRESSED(attr))
275 CBC 66 : cmid = VARDATA_COMPRESSED_GET_COMPRESS_METHOD(attr);
276 ECB :
277 GIC 81 : return cmid;
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
287 GIC 68 : CompressionNameToMethod(const char *compression)
288 ECB : {
289 GIC 68 : if (strcmp(compression, "pglz") == 0)
290 CBC 30 : return TOAST_PGLZ_COMPRESSION;
291 38 : else if (strcmp(compression, "lz4") == 0)
292 ECB : {
293 : #ifndef USE_LZ4
294 : NO_LZ4_SUPPORT();
295 : #endif
296 GIC 32 : return TOAST_LZ4_COMPRESSION;
297 ECB : }
298 :
299 GIC 6 : return InvalidCompressionMethod;
300 ECB : }
301 :
302 : /*
303 : * GetCompressionMethodName - Get compression method name
304 : */
305 : const char *
306 GIC 15 : GetCompressionMethodName(char method)
307 ECB : {
308 GIC 15 : switch (method)
309 ECB : {
310 GIC 6 : case TOAST_PGLZ_COMPRESSION:
311 CBC 6 : return "pglz";
312 9 : case TOAST_LZ4_COMPRESSION:
313 9 : return "lz4";
314 LBC 0 : default:
315 UBC 0 : elog(ERROR, "invalid compression method %c", method);
316 EUB : return NULL; /* keep compiler quiet */
317 : }
318 : }
|