Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * detoast.c
4 : : * Retrieve compressed or external variable size attributes.
5 : : *
6 : : * Copyright (c) 2000-2026, PostgreSQL Global Development Group
7 : : *
8 : : * IDENTIFICATION
9 : : * src/backend/access/common/detoast.c
10 : : *
11 : : *-------------------------------------------------------------------------
12 : : */
13 : :
14 : : #include "postgres.h"
15 : :
16 : : #include "access/detoast.h"
17 : : #include "access/table.h"
18 : : #include "access/tableam.h"
19 : : #include "access/toast_internals.h"
20 : : #include "common/int.h"
21 : : #include "common/pg_lzcompress.h"
22 : : #include "utils/expandeddatum.h"
23 : : #include "utils/rel.h"
24 : :
25 : : static varlena *toast_fetch_datum(varlena *attr);
26 : : static varlena *toast_fetch_datum_slice(varlena *attr,
27 : : int32 sliceoffset,
28 : : int32 slicelength);
29 : : static varlena *toast_decompress_datum(varlena *attr);
30 : : static varlena *toast_decompress_datum_slice(varlena *attr, int32 slicelength);
31 : :
32 : : /* ----------
33 : : * detoast_external_attr -
34 : : *
35 : : * Public entry point to get back a toasted value from
36 : : * external source (possibly still in compressed format).
37 : : *
38 : : * This will return a datum that contains all the data internally, ie, not
39 : : * relying on external storage or memory, but it can still be compressed or
40 : : * have a short header. Note some callers assume that if the input is an
41 : : * EXTERNAL datum, the result will be a pfree'able chunk.
42 : : * ----------
43 : : */
44 : : varlena *
221 michael@paquier.xyz 45 :CBC 11019 : detoast_external_attr(varlena *attr)
46 : : {
47 : : varlena *result;
48 : :
2631 rhaas@postgresql.org 49 [ + + ]: 11019 : if (VARATT_IS_EXTERNAL_ONDISK(attr))
50 : : {
51 : : /*
52 : : * This is an external stored plain value
53 : : */
54 : 4975 : result = toast_fetch_datum(attr);
55 : : }
56 [ + + ]: 6044 : else if (VARATT_IS_EXTERNAL_INDIRECT(attr))
57 : : {
58 : : /*
59 : : * This is an indirect pointer --- dereference it
60 : : */
61 : : varatt_indirect redirect;
62 : :
63 [ - + - + ]: 344 : VARATT_EXTERNAL_GET_POINTER(redirect, attr);
221 michael@paquier.xyz 64 : 344 : attr = (varlena *) redirect.pointer;
65 : :
66 : : /* nested indirect Datums aren't allowed */
2631 rhaas@postgresql.org 67 [ - + ]: 344 : Assert(!VARATT_IS_EXTERNAL_INDIRECT(attr));
68 : :
69 : : /* recurse if value is still external in some other way */
70 [ - + ]: 344 : if (VARATT_IS_EXTERNAL(attr))
2543 rhaas@postgresql.org 71 :UBC 0 : return detoast_external_attr(attr);
72 : :
73 : : /*
74 : : * Copy into the caller's memory context, in case caller tries to
75 : : * pfree the result.
76 : : */
221 michael@paquier.xyz 77 :CBC 344 : result = (varlena *) palloc(VARSIZE_ANY(attr));
2631 rhaas@postgresql.org 78 : 344 : memcpy(result, attr, VARSIZE_ANY(attr));
79 : : }
80 [ + - ]: 5700 : else if (VARATT_IS_EXTERNAL_EXPANDED(attr))
81 : : {
82 : : /*
83 : : * This is an expanded-object pointer --- get flat format
84 : : */
85 : : ExpandedObjectHeader *eoh;
86 : : Size resultsize;
87 : :
88 : 5700 : eoh = DatumGetEOHP(PointerGetDatum(attr));
89 : 5700 : resultsize = EOH_get_flat_size(eoh);
221 michael@paquier.xyz 90 : 5700 : result = (varlena *) palloc(resultsize);
661 peter@eisentraut.org 91 : 5700 : EOH_flatten_into(eoh, result, resultsize);
92 : : }
93 : : else
94 : : {
95 : : /*
96 : : * This is a plain value inside of the main tuple - why am I called?
97 : : */
2631 rhaas@postgresql.org 98 :UBC 0 : result = attr;
99 : : }
100 : :
2631 rhaas@postgresql.org 101 :CBC 11019 : return result;
102 : : }
103 : :
104 : :
105 : : /* ----------
106 : : * detoast_attr -
107 : : *
108 : : * Public entry point to get back a toasted value from compression
109 : : * or external storage. The result is always non-extended varlena form.
110 : : *
111 : : * Note some callers assume that if the input is an EXTERNAL or COMPRESSED
112 : : * datum, the result will be a pfree'able chunk.
113 : : * ----------
114 : : */
115 : : varlena *
221 michael@paquier.xyz 116 : 34078842 : detoast_attr(varlena *attr)
117 : : {
2631 rhaas@postgresql.org 118 [ + + ]: 34078842 : if (VARATT_IS_EXTERNAL_ONDISK(attr))
119 : : {
120 : : /*
121 : : * This is an externally stored datum --- fetch it back from there
122 : : */
123 : 12222 : attr = toast_fetch_datum(attr);
124 : : /* If it's compressed, decompress it */
125 [ + + ]: 12222 : if (VARATT_IS_COMPRESSED(attr))
126 : : {
221 michael@paquier.xyz 127 : 12147 : varlena *tmp = attr;
128 : :
2631 rhaas@postgresql.org 129 : 12147 : attr = toast_decompress_datum(tmp);
130 : 12147 : pfree(tmp);
131 : : }
132 : : }
133 [ + + ]: 34066620 : else if (VARATT_IS_EXTERNAL_INDIRECT(attr))
134 : : {
135 : : /*
136 : : * This is an indirect pointer --- dereference it
137 : : */
138 : : varatt_indirect redirect;
139 : :
140 [ - + - + ]: 69 : VARATT_EXTERNAL_GET_POINTER(redirect, attr);
221 michael@paquier.xyz 141 : 69 : attr = (varlena *) redirect.pointer;
142 : :
143 : : /* nested indirect Datums aren't allowed */
2631 rhaas@postgresql.org 144 [ - + ]: 69 : Assert(!VARATT_IS_EXTERNAL_INDIRECT(attr));
145 : :
146 : : /* recurse in case value is still extended in some other way */
2543 147 : 69 : attr = detoast_attr(attr);
148 : :
149 : : /* if it isn't, we'd better copy it */
221 michael@paquier.xyz 150 [ + + ]: 69 : if (attr == (varlena *) redirect.pointer)
151 : : {
152 : : varlena *result;
153 : :
154 : 17 : result = (varlena *) palloc(VARSIZE_ANY(attr));
2631 rhaas@postgresql.org 155 : 17 : memcpy(result, attr, VARSIZE_ANY(attr));
156 : 17 : attr = result;
157 : : }
158 : : }
159 [ + + ]: 34066551 : else if (VARATT_IS_EXTERNAL_EXPANDED(attr))
160 : : {
161 : : /*
162 : : * This is an expanded-object pointer --- get flat format
163 : : */
2543 164 : 5700 : attr = detoast_external_attr(attr);
165 : : /* flatteners are not allowed to produce compressed/short output */
2631 166 [ - + ]: 5700 : Assert(!VARATT_IS_EXTENDED(attr));
167 : : }
168 [ + + ]: 34060851 : else if (VARATT_IS_COMPRESSED(attr))
169 : : {
170 : : /*
171 : : * This is a compressed value inside of the main tuple
172 : : */
173 : 90310 : attr = toast_decompress_datum(attr);
174 : : }
175 [ + + ]: 33970541 : else if (VARATT_IS_SHORT(attr))
176 : : {
177 : : /*
178 : : * This is a short-header varlena --- convert to 4-byte header format
179 : : */
180 : 33970524 : Size data_size = VARSIZE_SHORT(attr) - VARHDRSZ_SHORT;
181 : 33970524 : Size new_size = data_size + VARHDRSZ;
182 : : varlena *new_attr;
183 : :
221 michael@paquier.xyz 184 : 33970524 : new_attr = (varlena *) palloc(new_size);
2631 rhaas@postgresql.org 185 : 33970524 : SET_VARSIZE(new_attr, new_size);
186 : 33970524 : memcpy(VARDATA(new_attr), VARDATA_SHORT(attr), data_size);
187 : 33970524 : attr = new_attr;
188 : : }
189 : :
190 : 34078842 : return attr;
191 : : }
192 : :
193 : :
194 : : /* ----------
195 : : * detoast_attr_slice -
196 : : *
197 : : * Public entry point to get back part of a toasted value
198 : : * from compression or external storage.
199 : : *
200 : : * sliceoffset is where to start (zero or more)
201 : : * If slicelength < 0, return everything beyond sliceoffset
202 : : * ----------
203 : : */
204 : : varlena *
221 michael@paquier.xyz 205 : 2613 : detoast_attr_slice(varlena *attr,
206 : : int32 sliceoffset, int32 slicelength)
207 : : {
208 : : varlena *preslice;
209 : : varlena *result;
210 : : char *attrdata;
211 : : int32 slicelimit;
212 : : int32 attrsize;
213 : :
2085 tgl@sss.pgh.pa.us 214 [ - + ]: 2613 : if (sliceoffset < 0)
2085 tgl@sss.pgh.pa.us 215 [ # # ]:UBC 0 : elog(ERROR, "invalid sliceoffset: %d", sliceoffset);
216 : :
217 : : /*
218 : : * Compute slicelimit = offset + length, or -1 if we must fetch all of the
219 : : * value. In case of integer overflow, we must fetch all.
220 : : */
2085 tgl@sss.pgh.pa.us 221 [ + + ]:CBC 2613 : if (slicelength < 0)
222 : 2129 : slicelimit = -1;
223 [ - + ]: 484 : else if (pg_add_s32_overflow(sliceoffset, slicelength, &slicelimit))
2085 tgl@sss.pgh.pa.us 224 :UBC 0 : slicelength = slicelimit = -1;
225 : :
2631 rhaas@postgresql.org 226 [ + + ]:CBC 2613 : if (VARATT_IS_EXTERNAL_ONDISK(attr))
227 : : {
228 : : toast_external_data toast_ext_data;
229 : : int32 extsize;
230 : : uint32 compress_method;
231 : : bool is_compressed;
232 : :
5 michael@paquier.xyz 233 :GNC 280 : toast_external_info_get(attr, &toast_ext_data);
234 : 280 : extsize = VARATT_EXTINFO_GET_EXTSIZE(toast_ext_data.extinfo);
235 : 280 : compress_method = VARATT_EXTINFO_GET_COMPRESS_METHOD(toast_ext_data.extinfo);
236 : 280 : is_compressed = VARATT_EXTINFO_IS_COMPRESSED(toast_ext_data.extinfo, toast_ext_data.rawsize);
237 : :
238 : : /* fast path for non-compressed external datums */
239 [ + + ]: 280 : if (!is_compressed)
2631 rhaas@postgresql.org 240 :CBC 172 : return toast_fetch_datum_slice(attr, sliceoffset, slicelength);
241 : :
242 : : /*
243 : : * For compressed values, we need to fetch enough slices to decompress
244 : : * at least the requested part (when a prefix is requested).
245 : : * Otherwise, just fetch all slices.
246 : : */
2085 tgl@sss.pgh.pa.us 247 [ + - ]: 108 : if (slicelimit >= 0)
248 : : {
5 michael@paquier.xyz 249 :GNC 108 : int32 max_size = extsize;
250 : :
251 : : /*
252 : : * Determine maximum amount of compressed data needed for a prefix
253 : : * of a given length (after decompression).
254 : : *
255 : : * At least for now, if it's LZ4 data, we'll have to fetch the
256 : : * whole thing, because there doesn't seem to be an API call to
257 : : * determine how much compressed data we need to be sure of being
258 : : * able to decompress the required slice.
259 : : */
260 [ + + ]: 108 : if (compress_method == TOAST_PGLZ_COMPRESSION_ID)
2011 rhaas@postgresql.org 261 :CBC 8 : max_size = pglz_maximum_compressed_size(slicelimit, max_size);
262 : :
263 : : /*
264 : : * Fetch enough compressed slices (compressed marker will get set
265 : : * automatically).
266 : : */
2546 tomas.vondra@postgre 267 : 108 : preslice = toast_fetch_datum_slice(attr, 0, max_size);
268 : : }
269 : : else
2546 tomas.vondra@postgre 270 :UBC 0 : preslice = toast_fetch_datum(attr);
271 : : }
2631 rhaas@postgresql.org 272 [ - + ]:CBC 2333 : else if (VARATT_IS_EXTERNAL_INDIRECT(attr))
273 : : {
274 : : varatt_indirect redirect;
275 : :
2631 rhaas@postgresql.org 276 [ # # # # ]:UBC 0 : VARATT_EXTERNAL_GET_POINTER(redirect, attr);
277 : :
278 : : /* nested indirect Datums aren't allowed */
279 [ # # ]: 0 : Assert(!VARATT_IS_EXTERNAL_INDIRECT(redirect.pointer));
280 : :
2543 281 : 0 : return detoast_attr_slice(redirect.pointer,
282 : : sliceoffset, slicelength);
283 : : }
2631 rhaas@postgresql.org 284 [ - + ]:CBC 2333 : else if (VARATT_IS_EXTERNAL_EXPANDED(attr))
285 : : {
286 : : /* pass it off to detoast_external_attr to flatten */
2543 rhaas@postgresql.org 287 :UBC 0 : preslice = detoast_external_attr(attr);
288 : : }
289 : : else
2631 rhaas@postgresql.org 290 :CBC 2333 : preslice = attr;
291 : :
292 [ - + ]: 2441 : Assert(!VARATT_IS_EXTERNAL(preslice));
293 : :
294 [ + + ]: 2441 : if (VARATT_IS_COMPRESSED(preslice))
295 : : {
221 michael@paquier.xyz 296 : 300 : varlena *tmp = preslice;
297 : :
298 : : /* Decompress enough to encompass the slice and the offset */
2085 tgl@sss.pgh.pa.us 299 [ + + ]: 300 : if (slicelimit >= 0)
300 : 252 : preslice = toast_decompress_datum_slice(tmp, slicelimit);
301 : : else
2631 rhaas@postgresql.org 302 : 48 : preslice = toast_decompress_datum(tmp);
303 : :
304 [ + + ]: 300 : if (tmp != attr)
305 : 108 : pfree(tmp);
306 : : }
307 : :
308 [ + + ]: 2441 : if (VARATT_IS_SHORT(preslice))
309 : : {
310 : 1540 : attrdata = VARDATA_SHORT(preslice);
311 : 1540 : attrsize = VARSIZE_SHORT(preslice) - VARHDRSZ_SHORT;
312 : : }
313 : : else
314 : : {
315 : 901 : attrdata = VARDATA(preslice);
316 : 901 : attrsize = VARSIZE(preslice) - VARHDRSZ;
317 : : }
318 : :
319 : : /* slicing of datum for compressed cases and plain value */
320 : :
321 [ + + ]: 2441 : if (sliceoffset >= attrsize)
322 : : {
323 : 15 : sliceoffset = 0;
324 : 15 : slicelength = 0;
325 : : }
2085 tgl@sss.pgh.pa.us 326 [ + + + + ]: 2426 : else if (slicelength < 0 || slicelimit > attrsize)
2631 rhaas@postgresql.org 327 : 2115 : slicelength = attrsize - sliceoffset;
328 : :
221 michael@paquier.xyz 329 : 2441 : result = (varlena *) palloc(slicelength + VARHDRSZ);
2631 rhaas@postgresql.org 330 : 2441 : SET_VARSIZE(result, slicelength + VARHDRSZ);
331 : :
332 : 2441 : memcpy(VARDATA(result), attrdata + sliceoffset, slicelength);
333 : :
334 [ + + ]: 2441 : if (preslice != attr)
335 : 300 : pfree(preslice);
336 : :
337 : 2441 : return result;
338 : : }
339 : :
340 : : /* ----------
341 : : * toast_fetch_datum -
342 : : *
343 : : * Reconstruct an in memory Datum from the chunks saved
344 : : * in the toast relation
345 : : * ----------
346 : : */
347 : : static varlena *
221 michael@paquier.xyz 348 : 17197 : toast_fetch_datum(varlena *attr)
349 : : {
350 : : Relation toastrel;
351 : : varlena *result;
352 : : toast_external_data toast_ext_data;
353 : : int32 attrsize;
354 : : Oid toastrelid;
355 : : Oid8 valueid;
356 : :
2631 rhaas@postgresql.org 357 [ - + ]: 17197 : if (!VARATT_IS_EXTERNAL_ONDISK(attr))
2631 rhaas@postgresql.org 358 [ # # ]:UBC 0 : elog(ERROR, "toast_fetch_datum shouldn't be called for non-ondisk datums");
359 : :
5 michael@paquier.xyz 360 :GNC 17197 : toast_external_info_get(attr, &toast_ext_data);
361 : 17197 : attrsize = VARATT_EXTINFO_GET_EXTSIZE(toast_ext_data.extinfo);
362 : 17197 : toastrelid = toast_ext_data.toastrelid;
363 : 17197 : valueid = toast_ext_data.valueid;
364 : :
221 michael@paquier.xyz 365 :CBC 17197 : result = (varlena *) palloc(attrsize + VARHDRSZ);
366 : :
5 michael@paquier.xyz 367 [ + + ]:GNC 17197 : if (VARATT_EXTINFO_IS_COMPRESSED(toast_ext_data.extinfo, toast_ext_data.rawsize))
2494 rhaas@postgresql.org 368 :CBC 16404 : SET_VARSIZE_COMPRESSED(result, attrsize + VARHDRSZ);
369 : : else
370 : 793 : SET_VARSIZE(result, attrsize + VARHDRSZ);
371 : :
2468 372 [ - + ]: 17197 : if (attrsize == 0)
2320 tgl@sss.pgh.pa.us 373 :UBC 0 : return result; /* Probably shouldn't happen, but just in
374 : : * case. */
375 : :
376 : : /*
377 : : * Open the toast relation and its indexes
378 : : */
5 michael@paquier.xyz 379 :GNC 17197 : toastrel = table_open(toastrelid, AccessShareLock);
380 : :
381 : : /* Fetch all chunks */
382 : 17197 : table_relation_fetch_toast_slice(toastrel, valueid,
383 : : attrsize, 0, attrsize, result);
384 : :
385 : : /* Close toast table */
2631 rhaas@postgresql.org 386 :CBC 17197 : table_close(toastrel, AccessShareLock);
387 : :
388 : 17197 : return result;
389 : : }
390 : :
391 : : /* ----------
392 : : * toast_fetch_datum_slice -
393 : : *
394 : : * Reconstruct a segment of a Datum from the chunks saved
395 : : * in the toast relation
396 : : *
397 : : * Note that this function supports non-compressed external datums
398 : : * and compressed external datums (in which case the requested slice
399 : : * has to be a prefix, i.e. sliceoffset has to be 0).
400 : : * ----------
401 : : */
402 : : static varlena *
221 michael@paquier.xyz 403 : 280 : toast_fetch_datum_slice(varlena *attr, int32 sliceoffset,
404 : : int32 slicelength)
405 : : {
406 : : Relation toastrel;
407 : : varlena *result;
408 : : toast_external_data toast_ext_data;
409 : : int32 attrsize;
410 : : Oid toastrelid;
411 : : Oid8 valueid;
412 : : bool is_compressed;
413 : :
2631 rhaas@postgresql.org 414 [ - + ]: 280 : if (!VARATT_IS_EXTERNAL_ONDISK(attr))
2631 rhaas@postgresql.org 415 [ # # ]:UBC 0 : elog(ERROR, "toast_fetch_datum_slice shouldn't be called for non-ondisk datums");
416 : :
5 michael@paquier.xyz 417 :GNC 280 : toast_external_info_get(attr, &toast_ext_data);
418 : 280 : attrsize = VARATT_EXTINFO_GET_EXTSIZE(toast_ext_data.extinfo);
419 : 280 : toastrelid = toast_ext_data.toastrelid;
420 : 280 : valueid = toast_ext_data.valueid;
421 : 280 : is_compressed = VARATT_EXTINFO_IS_COMPRESSED(toast_ext_data.extinfo, toast_ext_data.rawsize);
422 : :
423 : : /*
424 : : * It's nonsense to fetch slices of a compressed datum unless when it's a
425 : : * prefix -- this isn't lo_* we can't return a compressed datum which is
426 : : * meaningful to toast later.
427 : : */
428 [ + + - + ]: 280 : Assert(!is_compressed || 0 == sliceoffset);
429 : :
2631 rhaas@postgresql.org 430 [ - + ]:CBC 280 : if (sliceoffset >= attrsize)
431 : : {
2631 rhaas@postgresql.org 432 :UBC 0 : sliceoffset = 0;
2494 433 : 0 : slicelength = 0;
434 : : }
435 : :
436 : : /*
437 : : * When fetching a prefix of a compressed external datum, account for the
438 : : * space required by va_tcinfo, which is stored at the beginning as an
439 : : * int32 value.
440 : : */
5 michael@paquier.xyz 441 [ + + + - ]:GNC 280 : if (is_compressed && slicelength > 0)
2494 rhaas@postgresql.org 442 :CBC 108 : slicelength = slicelength + sizeof(int32);
443 : :
444 : : /*
445 : : * Adjust length request if needed. (Note: our sole caller,
446 : : * detoast_attr_slice, protects us against sliceoffset + slicelength
447 : : * overflowing.)
448 : : */
449 [ + + + + ]: 280 : if (((sliceoffset + slicelength) > attrsize) || slicelength < 0)
450 : 212 : slicelength = attrsize - sliceoffset;
451 : :
221 michael@paquier.xyz 452 : 280 : result = (varlena *) palloc(slicelength + VARHDRSZ);
453 : :
5 michael@paquier.xyz 454 [ + + ]:GNC 280 : if (is_compressed)
2494 rhaas@postgresql.org 455 :CBC 108 : SET_VARSIZE_COMPRESSED(result, slicelength + VARHDRSZ);
456 : : else
457 : 172 : SET_VARSIZE(result, slicelength + VARHDRSZ);
458 : :
459 [ - + ]: 280 : if (slicelength == 0)
2631 rhaas@postgresql.org 460 :UBC 0 : return result; /* Can save a lot of work at this point! */
461 : :
462 : : /* Open the toast relation */
5 michael@paquier.xyz 463 :GNC 280 : toastrel = table_open(toastrelid, AccessShareLock);
464 : :
465 : : /* Fetch all chunks */
466 : 280 : table_relation_fetch_toast_slice(toastrel, valueid,
467 : : attrsize, sliceoffset, slicelength,
468 : : result);
469 : :
470 : : /* Close toast table */
2468 rhaas@postgresql.org 471 :CBC 280 : table_close(toastrel, AccessShareLock);
472 : :
473 : 280 : return result;
474 : : }
475 : :
476 : : /* ----------
477 : : * toast_decompress_datum -
478 : : *
479 : : * Decompress a compressed version of a varlena datum
480 : : */
481 : : static varlena *
221 michael@paquier.xyz 482 : 102569 : toast_decompress_datum(varlena *attr)
483 : : {
484 : : ToastCompressionId cmid;
485 : :
2631 rhaas@postgresql.org 486 [ - + ]: 102569 : Assert(VARATT_IS_COMPRESSED(attr));
487 : :
488 : : /*
489 : : * Fetch the compression method id stored in the compression header and
490 : : * decompress the data using the appropriate decompression routine.
491 : : */
2011 492 : 102569 : cmid = TOAST_COMPRESS_METHOD(attr);
493 [ + + - ]: 102569 : switch (cmid)
494 : : {
495 : 375 : case TOAST_PGLZ_COMPRESSION_ID:
496 : 375 : return pglz_decompress_datum(attr);
497 : 102194 : case TOAST_LZ4_COMPRESSION_ID:
498 : 102194 : return lz4_decompress_datum(attr);
2011 rhaas@postgresql.org 499 :UBC 0 : default:
500 [ # # ]: 0 : elog(ERROR, "invalid compression method id %d", cmid);
501 : : return NULL; /* keep compiler quiet */
502 : : }
503 : : }
504 : :
505 : :
506 : : /* ----------
507 : : * toast_decompress_datum_slice -
508 : : *
509 : : * Decompress the front of a compressed version of a varlena datum.
510 : : * offset handling happens in detoast_attr_slice.
511 : : * Here we just decompress a slice from the front.
512 : : */
513 : : static varlena *
221 michael@paquier.xyz 514 :CBC 252 : toast_decompress_datum_slice(varlena *attr, int32 slicelength)
515 : : {
516 : : ToastCompressionId cmid;
517 : :
2631 rhaas@postgresql.org 518 [ - + ]: 252 : Assert(VARATT_IS_COMPRESSED(attr));
519 : :
520 : : /*
521 : : * Some callers may pass a slicelength that's more than the actual
522 : : * decompressed size. If so, just decompress normally. This avoids
523 : : * possibly allocating a larger-than-necessary result object, and may be
524 : : * faster and/or more robust as well. Notably, some versions of liblz4
525 : : * have been seen to give wrong results if passed an output size that is
526 : : * more than the data's true decompressed size.
527 : : */
2008 tgl@sss.pgh.pa.us 528 [ + + ]: 252 : if ((uint32) slicelength >= TOAST_COMPRESS_EXTSIZE(attr))
529 : 64 : return toast_decompress_datum(attr);
530 : :
531 : : /*
532 : : * Fetch the compression method id stored in the compression header and
533 : : * decompress the data slice using the appropriate decompression routine.
534 : : */
2011 rhaas@postgresql.org 535 : 188 : cmid = TOAST_COMPRESS_METHOD(attr);
536 [ + + - ]: 188 : switch (cmid)
537 : : {
538 : 24 : case TOAST_PGLZ_COMPRESSION_ID:
539 : 24 : return pglz_decompress_datum_slice(attr, slicelength);
540 : 164 : case TOAST_LZ4_COMPRESSION_ID:
541 : 164 : return lz4_decompress_datum_slice(attr, slicelength);
2011 rhaas@postgresql.org 542 :UBC 0 : default:
543 [ # # ]: 0 : elog(ERROR, "invalid compression method id %d", cmid);
544 : : return NULL; /* keep compiler quiet */
545 : : }
546 : : }
547 : :
548 : : /* ----------
549 : : * toast_raw_datum_size -
550 : : *
551 : : * Return the raw (detoasted) size of a varlena datum
552 : : * (including the VARHDRSZ header)
553 : : * ----------
554 : : */
555 : : Size
2631 rhaas@postgresql.org 556 :CBC 17916089 : toast_raw_datum_size(Datum value)
557 : : {
221 michael@paquier.xyz 558 : 17916089 : varlena *attr = (varlena *) DatumGetPointer(value);
559 : : Size result;
560 : :
2631 rhaas@postgresql.org 561 [ + + ]: 17916089 : if (VARATT_IS_EXTERNAL_ONDISK(attr))
562 : : {
563 : : /* va_rawsize is the size of the original datum -- including header */
564 : : toast_external_data toast_ext_data;
565 : :
5 michael@paquier.xyz 566 :GNC 11971 : toast_external_info_get(attr, &toast_ext_data);
567 : 11971 : result = toast_ext_data.rawsize;
568 : : }
2631 rhaas@postgresql.org 569 [ - + ]:CBC 17904118 : else if (VARATT_IS_EXTERNAL_INDIRECT(attr))
570 : : {
571 : : varatt_indirect toast_pointer;
572 : :
2631 rhaas@postgresql.org 573 [ # # # # ]:UBC 0 : VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
574 : :
575 : : /* nested indirect Datums aren't allowed */
576 [ # # ]: 0 : Assert(!VARATT_IS_EXTERNAL_INDIRECT(toast_pointer.pointer));
577 : :
578 : 0 : return toast_raw_datum_size(PointerGetDatum(toast_pointer.pointer));
579 : : }
2631 rhaas@postgresql.org 580 [ - + ]:CBC 17904118 : else if (VARATT_IS_EXTERNAL_EXPANDED(attr))
581 : : {
2631 rhaas@postgresql.org 582 :UBC 0 : result = EOH_get_flat_size(DatumGetEOHP(value));
583 : : }
2631 rhaas@postgresql.org 584 [ + + ]:CBC 17904118 : else if (VARATT_IS_COMPRESSED(attr))
585 : : {
586 : : /* here, va_rawsize is just the payload size */
2008 tgl@sss.pgh.pa.us 587 : 10638 : result = VARDATA_COMPRESSED_GET_EXTSIZE(attr) + VARHDRSZ;
588 : : }
2631 rhaas@postgresql.org 589 [ + + ]: 17893480 : else if (VARATT_IS_SHORT(attr))
590 : : {
591 : : /*
592 : : * we have to normalize the header length to VARHDRSZ or else the
593 : : * callers of this function will be confused.
594 : : */
595 : 11203970 : result = VARSIZE_SHORT(attr) - VARHDRSZ_SHORT + VARHDRSZ;
596 : : }
597 : : else
598 : : {
599 : : /* plain untoasted datum */
600 : 6689510 : result = VARSIZE(attr);
601 : : }
602 : 17916089 : return result;
603 : : }
604 : :
605 : : /* ----------
606 : : * toast_datum_size
607 : : *
608 : : * Return the physical storage size (possibly compressed) of a varlena datum
609 : : * ----------
610 : : */
611 : : Size
612 : 71 : toast_datum_size(Datum value)
613 : : {
221 michael@paquier.xyz 614 : 71 : varlena *attr = (varlena *) DatumGetPointer(value);
615 : : Size result;
616 : :
2631 rhaas@postgresql.org 617 [ + + ]: 71 : if (VARATT_IS_EXTERNAL_ONDISK(attr))
618 : : {
619 : : /*
620 : : * Attribute is stored externally - return the extsize whether
621 : : * compressed or not. We do not count the size of the toast pointer
622 : : * ... should we?
623 : : */
624 : : toast_external_data toast_ext_data;
625 : :
5 michael@paquier.xyz 626 :GNC 1 : toast_external_info_get(attr, &toast_ext_data);
627 : 1 : result = VARATT_EXTINFO_GET_EXTSIZE(toast_ext_data.extinfo);
628 : : }
2631 rhaas@postgresql.org 629 [ - + ]:CBC 70 : else if (VARATT_IS_EXTERNAL_INDIRECT(attr))
630 : : {
631 : : varatt_indirect toast_pointer;
632 : :
2631 rhaas@postgresql.org 633 [ # # # # ]:UBC 0 : VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
634 : :
635 : : /* nested indirect Datums aren't allowed */
636 [ # # ]: 0 : Assert(!VARATT_IS_EXTERNAL_INDIRECT(attr));
637 : :
638 : 0 : return toast_datum_size(PointerGetDatum(toast_pointer.pointer));
639 : : }
2631 rhaas@postgresql.org 640 [ - + ]:CBC 70 : else if (VARATT_IS_EXTERNAL_EXPANDED(attr))
641 : : {
2631 rhaas@postgresql.org 642 :UBC 0 : result = EOH_get_flat_size(DatumGetEOHP(value));
643 : : }
2631 rhaas@postgresql.org 644 [ - + ]:CBC 70 : else if (VARATT_IS_SHORT(attr))
645 : : {
2631 rhaas@postgresql.org 646 :UBC 0 : result = VARSIZE_SHORT(attr);
647 : : }
648 : : else
649 : : {
650 : : /*
651 : : * Attribute is stored inline either compressed or not, just calculate
652 : : * the size of the datum in either case.
653 : : */
2631 rhaas@postgresql.org 654 :CBC 70 : result = VARSIZE(attr);
655 : : }
656 : 71 : return result;
657 : : }
|