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