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