Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * tuptoaster.c
4 : * Support routines for external and compressed storage of
5 : * variable size attributes.
6 : *
7 : * Copyright (c) 2000-2017, PostgreSQL Global Development Group
8 : *
9 : *
10 : * IDENTIFICATION
11 : * src/backend/access/heap/tuptoaster.c
12 : *
13 : *
14 : * INTERFACE ROUTINES
15 : * toast_insert_or_update -
16 : * Try to make a given tuple fit into one page by compressing
17 : * or moving off attributes
18 : *
19 : * toast_delete -
20 : * Reclaim toast storage when a tuple is deleted
21 : *
22 : * heap_tuple_untoast_attr -
23 : * Fetch back a given value from the "secondary" relation
24 : *
25 : *-------------------------------------------------------------------------
26 : */
27 :
28 : #include "postgres.h"
29 :
30 : #include <unistd.h>
31 : #include <fcntl.h>
32 :
33 : #include "access/genam.h"
34 : #include "access/heapam.h"
35 : #include "access/tuptoaster.h"
36 : #include "access/xact.h"
37 : #include "catalog/catalog.h"
38 : #include "common/pg_lzcompress.h"
39 : #include "miscadmin.h"
40 : #include "utils/expandeddatum.h"
41 : #include "utils/fmgroids.h"
42 : #include "utils/rel.h"
43 : #include "utils/snapmgr.h"
44 : #include "utils/typcache.h"
45 : #include "utils/tqual.h"
46 :
47 :
48 : #undef TOAST_DEBUG
49 :
50 : /*
51 : * The information at the start of the compressed toast data.
52 : */
53 : typedef struct toast_compress_header
54 : {
55 : int32 vl_len_; /* varlena header (do not touch directly!) */
56 : int32 rawsize;
57 : } toast_compress_header;
58 :
59 : /*
60 : * Utilities for manipulation of header information for compressed
61 : * toast entries.
62 : */
63 : #define TOAST_COMPRESS_HDRSZ ((int32) sizeof(toast_compress_header))
64 : #define TOAST_COMPRESS_RAWSIZE(ptr) (((toast_compress_header *) (ptr))->rawsize)
65 : #define TOAST_COMPRESS_RAWDATA(ptr) \
66 : (((char *) (ptr)) + TOAST_COMPRESS_HDRSZ)
67 : #define TOAST_COMPRESS_SET_RAWSIZE(ptr, len) \
68 : (((toast_compress_header *) (ptr))->rawsize = (len))
69 :
70 : static void toast_delete_datum(Relation rel, Datum value, bool is_speculative);
71 : static Datum toast_save_datum(Relation rel, Datum value,
72 : struct varlena *oldexternal, int options);
73 : static bool toastrel_valueid_exists(Relation toastrel, Oid valueid);
74 : static bool toastid_valueid_exists(Oid toastrelid, Oid valueid);
75 : static struct varlena *toast_fetch_datum(struct varlena *attr);
76 : static struct varlena *toast_fetch_datum_slice(struct varlena *attr,
77 : int32 sliceoffset, int32 length);
78 : static struct varlena *toast_decompress_datum(struct varlena *attr);
79 : static int toast_open_indexes(Relation toastrel,
80 : LOCKMODE lock,
81 : Relation **toastidxs,
82 : int *num_indexes);
83 : static void toast_close_indexes(Relation *toastidxs, int num_indexes,
84 : LOCKMODE lock);
85 : static void init_toast_snapshot(Snapshot toast_snapshot);
86 :
87 :
88 : /* ----------
89 : * heap_tuple_fetch_attr -
90 : *
91 : * Public entry point to get back a toasted value from
92 : * external source (possibly still in compressed format).
93 : *
94 : * This will return a datum that contains all the data internally, ie, not
95 : * relying on external storage or memory, but it can still be compressed or
96 : * have a short header. Note some callers assume that if the input is an
97 : * EXTERNAL datum, the result will be a pfree'able chunk.
98 : * ----------
99 : */
100 : struct varlena *
101 610 : heap_tuple_fetch_attr(struct varlena *attr)
102 : {
103 : struct varlena *result;
104 :
105 610 : if (VARATT_IS_EXTERNAL_ONDISK(attr))
106 : {
107 : /*
108 : * This is an external stored plain value
109 : */
110 125 : result = toast_fetch_datum(attr);
111 : }
112 485 : else if (VARATT_IS_EXTERNAL_INDIRECT(attr))
113 82 : {
114 : /*
115 : * This is an indirect pointer --- dereference it
116 : */
117 : struct varatt_indirect redirect;
118 :
119 82 : VARATT_EXTERNAL_GET_POINTER(redirect, attr);
120 82 : attr = (struct varlena *) redirect.pointer;
121 :
122 : /* nested indirect Datums aren't allowed */
123 82 : Assert(!VARATT_IS_EXTERNAL_INDIRECT(attr));
124 :
125 : /* recurse if value is still external in some other way */
126 82 : if (VARATT_IS_EXTERNAL(attr))
127 0 : return heap_tuple_fetch_attr(attr);
128 :
129 : /*
130 : * Copy into the caller's memory context, in case caller tries to
131 : * pfree the result.
132 : */
133 82 : result = (struct varlena *) palloc(VARSIZE_ANY(attr));
134 82 : memcpy(result, attr, VARSIZE_ANY(attr));
135 : }
136 403 : else if (VARATT_IS_EXTERNAL_EXPANDED(attr))
137 403 : {
138 : /*
139 : * This is an expanded-object pointer --- get flat format
140 : */
141 : ExpandedObjectHeader *eoh;
142 : Size resultsize;
143 :
144 403 : eoh = DatumGetEOHP(PointerGetDatum(attr));
145 403 : resultsize = EOH_get_flat_size(eoh);
146 403 : result = (struct varlena *) palloc(resultsize);
147 403 : EOH_flatten_into(eoh, (void *) result, resultsize);
148 : }
149 : else
150 : {
151 : /*
152 : * This is a plain value inside of the main tuple - why am I called?
153 : */
154 0 : result = attr;
155 : }
156 :
157 610 : return result;
158 : }
159 :
160 :
161 : /* ----------
162 : * heap_tuple_untoast_attr -
163 : *
164 : * Public entry point to get back a toasted value from compression
165 : * or external storage. The result is always non-extended varlena form.
166 : *
167 : * Note some callers assume that if the input is an EXTERNAL or COMPRESSED
168 : * datum, the result will be a pfree'able chunk.
169 : * ----------
170 : */
171 : struct varlena *
172 1814087 : heap_tuple_untoast_attr(struct varlena *attr)
173 : {
174 1814087 : if (VARATT_IS_EXTERNAL_ONDISK(attr))
175 : {
176 : /*
177 : * This is an externally stored datum --- fetch it back from there
178 : */
179 214 : attr = toast_fetch_datum(attr);
180 : /* If it's compressed, decompress it */
181 428 : if (VARATT_IS_COMPRESSED(attr))
182 : {
183 214 : struct varlena *tmp = attr;
184 :
185 214 : attr = toast_decompress_datum(tmp);
186 214 : pfree(tmp);
187 : }
188 : }
189 1813873 : else if (VARATT_IS_EXTERNAL_INDIRECT(attr))
190 10 : {
191 : /*
192 : * This is an indirect pointer --- dereference it
193 : */
194 : struct varatt_indirect redirect;
195 :
196 10 : VARATT_EXTERNAL_GET_POINTER(redirect, attr);
197 10 : attr = (struct varlena *) redirect.pointer;
198 :
199 : /* nested indirect Datums aren't allowed */
200 10 : Assert(!VARATT_IS_EXTERNAL_INDIRECT(attr));
201 :
202 : /* recurse in case value is still extended in some other way */
203 10 : attr = heap_tuple_untoast_attr(attr);
204 :
205 : /* if it isn't, we'd better copy it */
206 10 : if (attr == (struct varlena *) redirect.pointer)
207 : {
208 : struct varlena *result;
209 :
210 0 : result = (struct varlena *) palloc(VARSIZE_ANY(attr));
211 0 : memcpy(result, attr, VARSIZE_ANY(attr));
212 0 : attr = result;
213 : }
214 : }
215 1813863 : else if (VARATT_IS_EXTERNAL_EXPANDED(attr))
216 : {
217 : /*
218 : * This is an expanded-object pointer --- get flat format
219 : */
220 403 : attr = heap_tuple_fetch_attr(attr);
221 : /* flatteners are not allowed to produce compressed/short output */
222 403 : Assert(!VARATT_IS_EXTENDED(attr));
223 : }
224 1813460 : else if (VARATT_IS_COMPRESSED(attr))
225 : {
226 : /*
227 : * This is a compressed value inside of the main tuple
228 : */
229 5198 : attr = toast_decompress_datum(attr);
230 : }
231 1808262 : else if (VARATT_IS_SHORT(attr))
232 : {
233 : /*
234 : * This is a short-header varlena --- convert to 4-byte header format
235 : */
236 1808262 : Size data_size = VARSIZE_SHORT(attr) - VARHDRSZ_SHORT;
237 1808262 : Size new_size = data_size + VARHDRSZ;
238 : struct varlena *new_attr;
239 :
240 1808262 : new_attr = (struct varlena *) palloc(new_size);
241 1808262 : SET_VARSIZE(new_attr, new_size);
242 1808262 : memcpy(VARDATA(new_attr), VARDATA_SHORT(attr), data_size);
243 1808262 : attr = new_attr;
244 : }
245 :
246 1814087 : return attr;
247 : }
248 :
249 :
250 : /* ----------
251 : * heap_tuple_untoast_attr_slice -
252 : *
253 : * Public entry point to get back part of a toasted value
254 : * from compression or external storage.
255 : * ----------
256 : */
257 : struct varlena *
258 35 : heap_tuple_untoast_attr_slice(struct varlena *attr,
259 : int32 sliceoffset, int32 slicelength)
260 : {
261 : struct varlena *preslice;
262 : struct varlena *result;
263 : char *attrdata;
264 : int32 attrsize;
265 :
266 35 : if (VARATT_IS_EXTERNAL_ONDISK(attr))
267 5 : {
268 : struct varatt_external toast_pointer;
269 :
270 17 : VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
271 :
272 : /* fast path for non-compressed external datums */
273 17 : if (!VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer))
274 12 : return toast_fetch_datum_slice(attr, sliceoffset, slicelength);
275 :
276 : /* fetch it back (compressed marker will get set automatically) */
277 5 : preslice = toast_fetch_datum(attr);
278 : }
279 18 : else if (VARATT_IS_EXTERNAL_INDIRECT(attr))
280 : {
281 : struct varatt_indirect redirect;
282 :
283 0 : VARATT_EXTERNAL_GET_POINTER(redirect, attr);
284 :
285 : /* nested indirect Datums aren't allowed */
286 0 : Assert(!VARATT_IS_EXTERNAL_INDIRECT(redirect.pointer));
287 :
288 0 : return heap_tuple_untoast_attr_slice(redirect.pointer,
289 : sliceoffset, slicelength);
290 : }
291 18 : else if (VARATT_IS_EXTERNAL_EXPANDED(attr))
292 : {
293 : /* pass it off to heap_tuple_fetch_attr to flatten */
294 0 : preslice = heap_tuple_fetch_attr(attr);
295 : }
296 : else
297 18 : preslice = attr;
298 :
299 23 : Assert(!VARATT_IS_EXTERNAL(preslice));
300 :
301 23 : if (VARATT_IS_COMPRESSED(preslice))
302 : {
303 17 : struct varlena *tmp = preslice;
304 :
305 17 : preslice = toast_decompress_datum(tmp);
306 :
307 17 : if (tmp != attr)
308 5 : pfree(tmp);
309 : }
310 :
311 23 : if (VARATT_IS_SHORT(preslice))
312 : {
313 0 : attrdata = VARDATA_SHORT(preslice);
314 0 : attrsize = VARSIZE_SHORT(preslice) - VARHDRSZ_SHORT;
315 : }
316 : else
317 : {
318 23 : attrdata = VARDATA(preslice);
319 23 : attrsize = VARSIZE(preslice) - VARHDRSZ;
320 : }
321 :
322 : /* slicing of datum for compressed cases and plain value */
323 :
324 23 : if (sliceoffset >= attrsize)
325 : {
326 3 : sliceoffset = 0;
327 3 : slicelength = 0;
328 : }
329 :
330 23 : if (((sliceoffset + slicelength) > attrsize) || slicelength < 0)
331 8 : slicelength = attrsize - sliceoffset;
332 :
333 23 : result = (struct varlena *) palloc(slicelength + VARHDRSZ);
334 23 : SET_VARSIZE(result, slicelength + VARHDRSZ);
335 :
336 23 : memcpy(VARDATA(result), attrdata + sliceoffset, slicelength);
337 :
338 23 : if (preslice != attr)
339 17 : pfree(preslice);
340 :
341 23 : return result;
342 : }
343 :
344 :
345 : /* ----------
346 : * toast_raw_datum_size -
347 : *
348 : * Return the raw (detoasted) size of a varlena datum
349 : * (including the VARHDRSZ header)
350 : * ----------
351 : */
352 : Size
353 395786 : toast_raw_datum_size(Datum value)
354 : {
355 395786 : struct varlena *attr = (struct varlena *) DatumGetPointer(value);
356 : Size result;
357 :
358 395786 : if (VARATT_IS_EXTERNAL_ONDISK(attr))
359 136 : {
360 : /* va_rawsize is the size of the original datum -- including header */
361 : struct varatt_external toast_pointer;
362 :
363 136 : VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
364 136 : result = toast_pointer.va_rawsize;
365 : }
366 395650 : else if (VARATT_IS_EXTERNAL_INDIRECT(attr))
367 : {
368 : struct varatt_indirect toast_pointer;
369 :
370 0 : VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
371 :
372 : /* nested indirect Datums aren't allowed */
373 0 : Assert(!VARATT_IS_EXTERNAL_INDIRECT(toast_pointer.pointer));
374 :
375 0 : return toast_raw_datum_size(PointerGetDatum(toast_pointer.pointer));
376 : }
377 395650 : else if (VARATT_IS_EXTERNAL_EXPANDED(attr))
378 : {
379 0 : result = EOH_get_flat_size(DatumGetEOHP(value));
380 : }
381 395650 : else if (VARATT_IS_COMPRESSED(attr))
382 : {
383 : /* here, va_rawsize is just the payload size */
384 772 : result = VARRAWSIZE_4B_C(attr) + VARHDRSZ;
385 : }
386 394878 : else if (VARATT_IS_SHORT(attr))
387 : {
388 : /*
389 : * we have to normalize the header length to VARHDRSZ or else the
390 : * callers of this function will be confused.
391 : */
392 266960 : result = VARSIZE_SHORT(attr) - VARHDRSZ_SHORT + VARHDRSZ;
393 : }
394 : else
395 : {
396 : /* plain untoasted datum */
397 127918 : result = VARSIZE(attr);
398 : }
399 395786 : return result;
400 : }
401 :
402 : /* ----------
403 : * toast_datum_size
404 : *
405 : * Return the physical storage size (possibly compressed) of a varlena datum
406 : * ----------
407 : */
408 : Size
409 10 : toast_datum_size(Datum value)
410 : {
411 10 : struct varlena *attr = (struct varlena *) DatumGetPointer(value);
412 : Size result;
413 :
414 10 : if (VARATT_IS_EXTERNAL_ONDISK(attr))
415 0 : {
416 : /*
417 : * Attribute is stored externally - return the extsize whether
418 : * compressed or not. We do not count the size of the toast pointer
419 : * ... should we?
420 : */
421 : struct varatt_external toast_pointer;
422 :
423 0 : VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
424 0 : result = toast_pointer.va_extsize;
425 : }
426 10 : else if (VARATT_IS_EXTERNAL_INDIRECT(attr))
427 : {
428 : struct varatt_indirect toast_pointer;
429 :
430 0 : VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
431 :
432 : /* nested indirect Datums aren't allowed */
433 0 : Assert(!VARATT_IS_EXTERNAL_INDIRECT(attr));
434 :
435 0 : return toast_datum_size(PointerGetDatum(toast_pointer.pointer));
436 : }
437 10 : else if (VARATT_IS_EXTERNAL_EXPANDED(attr))
438 : {
439 0 : result = EOH_get_flat_size(DatumGetEOHP(value));
440 : }
441 10 : else if (VARATT_IS_SHORT(attr))
442 : {
443 0 : result = VARSIZE_SHORT(attr);
444 : }
445 : else
446 : {
447 : /*
448 : * Attribute is stored inline either compressed or not, just calculate
449 : * the size of the datum in either case.
450 : */
451 10 : result = VARSIZE(attr);
452 : }
453 10 : return result;
454 : }
455 :
456 :
457 : /* ----------
458 : * toast_delete -
459 : *
460 : * Cascaded delete toast-entries on DELETE
461 : * ----------
462 : */
463 : void
464 7 : toast_delete(Relation rel, HeapTuple oldtup, bool is_speculative)
465 : {
466 : TupleDesc tupleDesc;
467 : int numAttrs;
468 : int i;
469 : Datum toast_values[MaxHeapAttributeNumber];
470 : bool toast_isnull[MaxHeapAttributeNumber];
471 :
472 : /*
473 : * We should only ever be called for tuples of plain relations or
474 : * materialized views --- recursing on a toast rel is bad news.
475 : */
476 7 : Assert(rel->rd_rel->relkind == RELKIND_RELATION ||
477 : rel->rd_rel->relkind == RELKIND_MATVIEW);
478 :
479 : /*
480 : * Get the tuple descriptor and break down the tuple into fields.
481 : *
482 : * NOTE: it's debatable whether to use heap_deform_tuple() here or just
483 : * heap_getattr() only the varlena columns. The latter could win if there
484 : * are few varlena columns and many non-varlena ones. However,
485 : * heap_deform_tuple costs only O(N) while the heap_getattr way would cost
486 : * O(N^2) if there are many varlena columns, so it seems better to err on
487 : * the side of linear cost. (We won't even be here unless there's at
488 : * least one varlena column, by the way.)
489 : */
490 7 : tupleDesc = rel->rd_att;
491 7 : numAttrs = tupleDesc->natts;
492 :
493 7 : Assert(numAttrs <= MaxHeapAttributeNumber);
494 7 : heap_deform_tuple(oldtup, tupleDesc, toast_values, toast_isnull);
495 :
496 : /*
497 : * Check for external stored attributes and delete them from the secondary
498 : * relation.
499 : */
500 50 : for (i = 0; i < numAttrs; i++)
501 : {
502 43 : if (TupleDescAttr(tupleDesc, i)->attlen == -1)
503 : {
504 13 : Datum value = toast_values[i];
505 :
506 13 : if (toast_isnull[i])
507 0 : continue;
508 13 : else if (VARATT_IS_EXTERNAL_ONDISK(PointerGetDatum(value)))
509 7 : toast_delete_datum(rel, value, is_speculative);
510 : }
511 : }
512 7 : }
513 :
514 :
515 : /* ----------
516 : * toast_insert_or_update -
517 : *
518 : * Delete no-longer-used toast-entries and create new ones to
519 : * make the new tuple fit on INSERT or UPDATE
520 : *
521 : * Inputs:
522 : * newtup: the candidate new tuple to be inserted
523 : * oldtup: the old row version for UPDATE, or NULL for INSERT
524 : * options: options to be passed to heap_insert() for toast rows
525 : * Result:
526 : * either newtup if no toasting is needed, or a palloc'd modified tuple
527 : * that is what should actually get stored
528 : *
529 : * NOTE: neither newtup nor oldtup will be modified. This is a change
530 : * from the pre-8.1 API of this routine.
531 : * ----------
532 : */
533 : HeapTuple
534 1889 : toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
535 : int options)
536 : {
537 : HeapTuple result_tuple;
538 : TupleDesc tupleDesc;
539 : int numAttrs;
540 : int i;
541 :
542 1889 : bool need_change = false;
543 1889 : bool need_free = false;
544 1889 : bool need_delold = false;
545 1889 : bool has_nulls = false;
546 :
547 : Size maxDataLen;
548 : Size hoff;
549 :
550 : char toast_action[MaxHeapAttributeNumber];
551 : bool toast_isnull[MaxHeapAttributeNumber];
552 : bool toast_oldisnull[MaxHeapAttributeNumber];
553 : Datum toast_values[MaxHeapAttributeNumber];
554 : Datum toast_oldvalues[MaxHeapAttributeNumber];
555 : struct varlena *toast_oldexternal[MaxHeapAttributeNumber];
556 : int32 toast_sizes[MaxHeapAttributeNumber];
557 : bool toast_free[MaxHeapAttributeNumber];
558 : bool toast_delold[MaxHeapAttributeNumber];
559 :
560 : /*
561 : * Ignore the INSERT_SPECULATIVE option. Speculative insertions/super
562 : * deletions just normally insert/delete the toast values. It seems
563 : * easiest to deal with that here, instead on, potentially, multiple
564 : * callers.
565 : */
566 1889 : options &= ~HEAP_INSERT_SPECULATIVE;
567 :
568 : /*
569 : * We should only ever be called for tuples of plain relations or
570 : * materialized views --- recursing on a toast rel is bad news.
571 : */
572 1889 : Assert(rel->rd_rel->relkind == RELKIND_RELATION ||
573 : rel->rd_rel->relkind == RELKIND_MATVIEW);
574 :
575 : /*
576 : * Get the tuple descriptor and break down the tuple(s) into fields.
577 : */
578 1889 : tupleDesc = rel->rd_att;
579 1889 : numAttrs = tupleDesc->natts;
580 :
581 1889 : Assert(numAttrs <= MaxHeapAttributeNumber);
582 1889 : heap_deform_tuple(newtup, tupleDesc, toast_values, toast_isnull);
583 1889 : if (oldtup != NULL)
584 69 : heap_deform_tuple(oldtup, tupleDesc, toast_oldvalues, toast_oldisnull);
585 :
586 : /* ----------
587 : * Then collect information about the values given
588 : *
589 : * NOTE: toast_action[i] can have these values:
590 : * ' ' default handling
591 : * 'p' already processed --- don't touch it
592 : * 'x' incompressible, but OK to move off
593 : *
594 : * NOTE: toast_sizes[i] is only made valid for varlena attributes with
595 : * toast_action[i] different from 'p'.
596 : * ----------
597 : */
598 1889 : memset(toast_action, ' ', numAttrs * sizeof(char));
599 1889 : memset(toast_oldexternal, 0, numAttrs * sizeof(struct varlena *));
600 1889 : memset(toast_free, 0, numAttrs * sizeof(bool));
601 1889 : memset(toast_delold, 0, numAttrs * sizeof(bool));
602 :
603 10879 : for (i = 0; i < numAttrs; i++)
604 : {
605 8990 : Form_pg_attribute att = TupleDescAttr(tupleDesc, i);
606 : struct varlena *old_value;
607 : struct varlena *new_value;
608 :
609 8990 : if (oldtup != NULL)
610 : {
611 : /*
612 : * For UPDATE get the old and new values of this attribute
613 : */
614 670 : old_value = (struct varlena *) DatumGetPointer(toast_oldvalues[i]);
615 670 : new_value = (struct varlena *) DatumGetPointer(toast_values[i]);
616 :
617 : /*
618 : * If the old value is stored on disk, check if it has changed so
619 : * we have to delete it later.
620 : */
621 833 : if (att->attlen == -1 && !toast_oldisnull[i] &&
622 193 : VARATT_IS_EXTERNAL_ONDISK(old_value))
623 : {
624 40 : if (toast_isnull[i] || !VARATT_IS_EXTERNAL_ONDISK(new_value) ||
625 20 : memcmp((char *) old_value, (char *) new_value,
626 20 : VARSIZE_EXTERNAL(old_value)) != 0)
627 : {
628 : /*
629 : * The old external stored value isn't needed any more
630 : * after the update
631 : */
632 20 : toast_delold[i] = true;
633 20 : need_delold = true;
634 : }
635 : else
636 : {
637 : /*
638 : * This attribute isn't changed by this update so we reuse
639 : * the original reference to the old value in the new
640 : * tuple.
641 : */
642 10 : toast_action[i] = 'p';
643 10 : continue;
644 : }
645 : }
646 : }
647 : else
648 : {
649 : /*
650 : * For INSERT simply get the new value
651 : */
652 8320 : new_value = (struct varlena *) DatumGetPointer(toast_values[i]);
653 : }
654 :
655 : /*
656 : * Handle NULL attributes
657 : */
658 8980 : if (toast_isnull[i])
659 : {
660 435 : toast_action[i] = 'p';
661 435 : has_nulls = true;
662 435 : continue;
663 : }
664 :
665 : /*
666 : * Now look at varlena attributes
667 : */
668 8545 : if (att->attlen == -1)
669 : {
670 : /*
671 : * If the table's attribute says PLAIN always, force it so.
672 : */
673 2546 : if (att->attstorage == 'p')
674 2 : toast_action[i] = 'p';
675 :
676 : /*
677 : * We took care of UPDATE above, so any external value we find
678 : * still in the tuple must be someone else's that we cannot reuse
679 : * (this includes the case of an out-of-line in-memory datum).
680 : * Fetch it back (without decompression, unless we are forcing
681 : * PLAIN storage). If necessary, we'll push it out as a new
682 : * external value below.
683 : */
684 2546 : if (VARATT_IS_EXTERNAL(new_value))
685 : {
686 43 : toast_oldexternal[i] = new_value;
687 43 : if (att->attstorage == 'p')
688 0 : new_value = heap_tuple_untoast_attr(new_value);
689 : else
690 43 : new_value = heap_tuple_fetch_attr(new_value);
691 43 : toast_values[i] = PointerGetDatum(new_value);
692 43 : toast_free[i] = true;
693 43 : need_change = true;
694 43 : need_free = true;
695 : }
696 :
697 : /*
698 : * Remember the size of this attribute
699 : */
700 2546 : toast_sizes[i] = VARSIZE_ANY(new_value);
701 : }
702 : else
703 : {
704 : /*
705 : * Not a varlena attribute, plain storage always
706 : */
707 5999 : toast_action[i] = 'p';
708 : }
709 : }
710 :
711 : /* ----------
712 : * Compress and/or save external until data fits into target length
713 : *
714 : * 1: Inline compress attributes with attstorage 'x', and store very
715 : * large attributes with attstorage 'x' or 'e' external immediately
716 : * 2: Store attributes with attstorage 'x' or 'e' external
717 : * 3: Inline compress attributes with attstorage 'm'
718 : * 4: Store attributes with attstorage 'm' external
719 : * ----------
720 : */
721 :
722 : /* compute header overhead --- this should match heap_form_tuple() */
723 1889 : hoff = SizeofHeapTupleHeader;
724 1889 : if (has_nulls)
725 82 : hoff += BITMAPLEN(numAttrs);
726 1889 : if (newtup->t_data->t_infomask & HEAP_HASOID)
727 467 : hoff += sizeof(Oid);
728 1889 : hoff = MAXALIGN(hoff);
729 : /* now convert to a limit on the tuple data size */
730 1889 : maxDataLen = TOAST_TUPLE_TARGET - hoff;
731 :
732 : /*
733 : * Look for attributes with attstorage 'x' to compress. Also find large
734 : * attributes with attstorage 'x' or 'e', and store them external.
735 : */
736 5719 : while (heap_compute_data_size(tupleDesc,
737 : toast_values, toast_isnull) > maxDataLen)
738 : {
739 1957 : int biggest_attno = -1;
740 1957 : int32 biggest_size = MAXALIGN(TOAST_POINTER_SIZE);
741 : Datum old_value;
742 : Datum new_value;
743 :
744 : /*
745 : * Search for the biggest yet unprocessed internal attribute
746 : */
747 12733 : for (i = 0; i < numAttrs; i++)
748 : {
749 10776 : Form_pg_attribute att = TupleDescAttr(tupleDesc, i);
750 :
751 10776 : if (toast_action[i] != ' ')
752 7958 : continue;
753 2818 : if (VARATT_IS_EXTERNAL(DatumGetPointer(toast_values[i])))
754 0 : continue; /* can't happen, toast_action would be 'p' */
755 2818 : if (VARATT_IS_COMPRESSED(DatumGetPointer(toast_values[i])))
756 135 : continue;
757 2683 : if (att->attstorage != 'x' && att->attstorage != 'e')
758 0 : continue;
759 2683 : if (toast_sizes[i] > biggest_size)
760 : {
761 2114 : biggest_attno = i;
762 2114 : biggest_size = toast_sizes[i];
763 : }
764 : }
765 :
766 1957 : if (biggest_attno < 0)
767 16 : break;
768 :
769 : /*
770 : * Attempt to compress it inline, if it has attstorage 'x'
771 : */
772 1941 : i = biggest_attno;
773 1941 : if (TupleDescAttr(tupleDesc, i)->attstorage == 'x')
774 : {
775 1937 : old_value = toast_values[i];
776 1937 : new_value = toast_compress_datum(old_value);
777 :
778 1937 : if (DatumGetPointer(new_value) != NULL)
779 : {
780 : /* successful compression */
781 1924 : if (toast_free[i])
782 15 : pfree(DatumGetPointer(old_value));
783 1924 : toast_values[i] = new_value;
784 1924 : toast_free[i] = true;
785 1924 : toast_sizes[i] = VARSIZE(DatumGetPointer(toast_values[i]));
786 1924 : need_change = true;
787 1924 : need_free = true;
788 : }
789 : else
790 : {
791 : /* incompressible, ignore on subsequent compression passes */
792 13 : toast_action[i] = 'x';
793 : }
794 : }
795 : else
796 : {
797 : /* has attstorage 'e', ignore on subsequent compression passes */
798 4 : toast_action[i] = 'x';
799 : }
800 :
801 : /*
802 : * If this value is by itself more than maxDataLen (after compression
803 : * if any), push it out to the toast table immediately, if possible.
804 : * This avoids uselessly compressing other fields in the common case
805 : * where we have one long field and several short ones.
806 : *
807 : * XXX maybe the threshold should be less than maxDataLen?
808 : */
809 2051 : if (toast_sizes[i] > maxDataLen &&
810 110 : rel->rd_rel->reltoastrelid != InvalidOid)
811 : {
812 110 : old_value = toast_values[i];
813 110 : toast_action[i] = 'p';
814 110 : toast_values[i] = toast_save_datum(rel, toast_values[i],
815 : toast_oldexternal[i], options);
816 110 : if (toast_free[i])
817 106 : pfree(DatumGetPointer(old_value));
818 110 : toast_free[i] = true;
819 110 : need_change = true;
820 110 : need_free = true;
821 : }
822 : }
823 :
824 : /*
825 : * Second we look for attributes of attstorage 'x' or 'e' that are still
826 : * inline. But skip this if there's no toast table to push them to.
827 : */
828 3794 : while (heap_compute_data_size(tupleDesc,
829 16 : toast_values, toast_isnull) > maxDataLen &&
830 16 : rel->rd_rel->reltoastrelid != InvalidOid)
831 : {
832 16 : int biggest_attno = -1;
833 16 : int32 biggest_size = MAXALIGN(TOAST_POINTER_SIZE);
834 : Datum old_value;
835 :
836 : /*------
837 : * Search for the biggest yet inlined attribute with
838 : * attstorage equals 'x' or 'e'
839 : *------
840 : */
841 334 : for (i = 0; i < numAttrs; i++)
842 : {
843 318 : Form_pg_attribute att = TupleDescAttr(tupleDesc, i);
844 :
845 318 : if (toast_action[i] == 'p')
846 262 : continue;
847 56 : if (VARATT_IS_EXTERNAL(DatumGetPointer(toast_values[i])))
848 0 : continue; /* can't happen, toast_action would be 'p' */
849 56 : if (att->attstorage != 'x' && att->attstorage != 'e')
850 0 : continue;
851 56 : if (toast_sizes[i] > biggest_size)
852 : {
853 39 : biggest_attno = i;
854 39 : biggest_size = toast_sizes[i];
855 : }
856 : }
857 :
858 16 : if (biggest_attno < 0)
859 0 : break;
860 :
861 : /*
862 : * Store this external
863 : */
864 16 : i = biggest_attno;
865 16 : old_value = toast_values[i];
866 16 : toast_action[i] = 'p';
867 16 : toast_values[i] = toast_save_datum(rel, toast_values[i],
868 : toast_oldexternal[i], options);
869 16 : if (toast_free[i])
870 16 : pfree(DatumGetPointer(old_value));
871 16 : toast_free[i] = true;
872 :
873 16 : need_change = true;
874 16 : need_free = true;
875 : }
876 :
877 : /*
878 : * Round 3 - this time we take attributes with storage 'm' into
879 : * compression
880 : */
881 3778 : while (heap_compute_data_size(tupleDesc,
882 : toast_values, toast_isnull) > maxDataLen)
883 : {
884 0 : int biggest_attno = -1;
885 0 : int32 biggest_size = MAXALIGN(TOAST_POINTER_SIZE);
886 : Datum old_value;
887 : Datum new_value;
888 :
889 : /*
890 : * Search for the biggest yet uncompressed internal attribute
891 : */
892 0 : for (i = 0; i < numAttrs; i++)
893 : {
894 0 : if (toast_action[i] != ' ')
895 0 : continue;
896 0 : if (VARATT_IS_EXTERNAL(DatumGetPointer(toast_values[i])))
897 0 : continue; /* can't happen, toast_action would be 'p' */
898 0 : if (VARATT_IS_COMPRESSED(DatumGetPointer(toast_values[i])))
899 0 : continue;
900 0 : if (TupleDescAttr(tupleDesc, i)->attstorage != 'm')
901 0 : continue;
902 0 : if (toast_sizes[i] > biggest_size)
903 : {
904 0 : biggest_attno = i;
905 0 : biggest_size = toast_sizes[i];
906 : }
907 : }
908 :
909 0 : if (biggest_attno < 0)
910 0 : break;
911 :
912 : /*
913 : * Attempt to compress it inline
914 : */
915 0 : i = biggest_attno;
916 0 : old_value = toast_values[i];
917 0 : new_value = toast_compress_datum(old_value);
918 :
919 0 : if (DatumGetPointer(new_value) != NULL)
920 : {
921 : /* successful compression */
922 0 : if (toast_free[i])
923 0 : pfree(DatumGetPointer(old_value));
924 0 : toast_values[i] = new_value;
925 0 : toast_free[i] = true;
926 0 : toast_sizes[i] = VARSIZE(DatumGetPointer(toast_values[i]));
927 0 : need_change = true;
928 0 : need_free = true;
929 : }
930 : else
931 : {
932 : /* incompressible, ignore on subsequent compression passes */
933 0 : toast_action[i] = 'x';
934 : }
935 : }
936 :
937 : /*
938 : * Finally we store attributes of type 'm' externally. At this point we
939 : * increase the target tuple size, so that 'm' attributes aren't stored
940 : * externally unless really necessary.
941 : */
942 1889 : maxDataLen = TOAST_TUPLE_TARGET_MAIN - hoff;
943 :
944 3778 : while (heap_compute_data_size(tupleDesc,
945 0 : toast_values, toast_isnull) > maxDataLen &&
946 0 : rel->rd_rel->reltoastrelid != InvalidOid)
947 : {
948 0 : int biggest_attno = -1;
949 0 : int32 biggest_size = MAXALIGN(TOAST_POINTER_SIZE);
950 : Datum old_value;
951 :
952 : /*--------
953 : * Search for the biggest yet inlined attribute with
954 : * attstorage = 'm'
955 : *--------
956 : */
957 0 : for (i = 0; i < numAttrs; i++)
958 : {
959 0 : if (toast_action[i] == 'p')
960 0 : continue;
961 0 : if (VARATT_IS_EXTERNAL(DatumGetPointer(toast_values[i])))
962 0 : continue; /* can't happen, toast_action would be 'p' */
963 0 : if (TupleDescAttr(tupleDesc, i)->attstorage != 'm')
964 0 : continue;
965 0 : if (toast_sizes[i] > biggest_size)
966 : {
967 0 : biggest_attno = i;
968 0 : biggest_size = toast_sizes[i];
969 : }
970 : }
971 :
972 0 : if (biggest_attno < 0)
973 0 : break;
974 :
975 : /*
976 : * Store this external
977 : */
978 0 : i = biggest_attno;
979 0 : old_value = toast_values[i];
980 0 : toast_action[i] = 'p';
981 0 : toast_values[i] = toast_save_datum(rel, toast_values[i],
982 : toast_oldexternal[i], options);
983 0 : if (toast_free[i])
984 0 : pfree(DatumGetPointer(old_value));
985 0 : toast_free[i] = true;
986 :
987 0 : need_change = true;
988 0 : need_free = true;
989 : }
990 :
991 : /*
992 : * In the case we toasted any values, we need to build a new heap tuple
993 : * with the changed values.
994 : */
995 1889 : if (need_change)
996 : {
997 1883 : HeapTupleHeader olddata = newtup->t_data;
998 : HeapTupleHeader new_data;
999 : int32 new_header_len;
1000 : int32 new_data_len;
1001 : int32 new_tuple_len;
1002 :
1003 : /*
1004 : * Calculate the new size of the tuple.
1005 : *
1006 : * Note: we used to assume here that the old tuple's t_hoff must equal
1007 : * the new_header_len value, but that was incorrect. The old tuple
1008 : * might have a smaller-than-current natts, if there's been an ALTER
1009 : * TABLE ADD COLUMN since it was stored; and that would lead to a
1010 : * different conclusion about the size of the null bitmap, or even
1011 : * whether there needs to be one at all.
1012 : */
1013 1883 : new_header_len = SizeofHeapTupleHeader;
1014 1883 : if (has_nulls)
1015 78 : new_header_len += BITMAPLEN(numAttrs);
1016 1883 : if (olddata->t_infomask & HEAP_HASOID)
1017 467 : new_header_len += sizeof(Oid);
1018 1883 : new_header_len = MAXALIGN(new_header_len);
1019 1883 : new_data_len = heap_compute_data_size(tupleDesc,
1020 : toast_values, toast_isnull);
1021 1883 : new_tuple_len = new_header_len + new_data_len;
1022 :
1023 : /*
1024 : * Allocate and zero the space needed, and fill HeapTupleData fields.
1025 : */
1026 1883 : result_tuple = (HeapTuple) palloc0(HEAPTUPLESIZE + new_tuple_len);
1027 1883 : result_tuple->t_len = new_tuple_len;
1028 1883 : result_tuple->t_self = newtup->t_self;
1029 1883 : result_tuple->t_tableOid = newtup->t_tableOid;
1030 1883 : new_data = (HeapTupleHeader) ((char *) result_tuple + HEAPTUPLESIZE);
1031 1883 : result_tuple->t_data = new_data;
1032 :
1033 : /*
1034 : * Copy the existing tuple header, but adjust natts and t_hoff.
1035 : */
1036 1883 : memcpy(new_data, olddata, SizeofHeapTupleHeader);
1037 1883 : HeapTupleHeaderSetNatts(new_data, numAttrs);
1038 1883 : new_data->t_hoff = new_header_len;
1039 1883 : if (olddata->t_infomask & HEAP_HASOID)
1040 467 : HeapTupleHeaderSetOid(new_data, HeapTupleHeaderGetOid(olddata));
1041 :
1042 : /* Copy over the data, and fill the null bitmap if needed */
1043 1883 : heap_fill_tuple(tupleDesc,
1044 : toast_values,
1045 : toast_isnull,
1046 : (char *) new_data + new_header_len,
1047 : new_data_len,
1048 : &(new_data->t_infomask),
1049 : has_nulls ? new_data->t_bits : NULL);
1050 : }
1051 : else
1052 6 : result_tuple = newtup;
1053 :
1054 : /*
1055 : * Free allocated temp values
1056 : */
1057 1889 : if (need_free)
1058 10849 : for (i = 0; i < numAttrs; i++)
1059 8966 : if (toast_free[i])
1060 1956 : pfree(DatumGetPointer(toast_values[i]));
1061 :
1062 : /*
1063 : * Delete external values from the old tuple
1064 : */
1065 1889 : if (need_delold)
1066 166 : for (i = 0; i < numAttrs; i++)
1067 151 : if (toast_delold[i])
1068 20 : toast_delete_datum(rel, toast_oldvalues[i], false);
1069 :
1070 1889 : return result_tuple;
1071 : }
1072 :
1073 :
1074 : /* ----------
1075 : * toast_flatten_tuple -
1076 : *
1077 : * "Flatten" a tuple to contain no out-of-line toasted fields.
1078 : * (This does not eliminate compressed or short-header datums.)
1079 : *
1080 : * Note: we expect the caller already checked HeapTupleHasExternal(tup),
1081 : * so there is no need for a short-circuit path.
1082 : * ----------
1083 : */
1084 : HeapTuple
1085 92 : toast_flatten_tuple(HeapTuple tup, TupleDesc tupleDesc)
1086 : {
1087 : HeapTuple new_tuple;
1088 92 : int numAttrs = tupleDesc->natts;
1089 : int i;
1090 : Datum toast_values[MaxTupleAttributeNumber];
1091 : bool toast_isnull[MaxTupleAttributeNumber];
1092 : bool toast_free[MaxTupleAttributeNumber];
1093 :
1094 : /*
1095 : * Break down the tuple into fields.
1096 : */
1097 92 : Assert(numAttrs <= MaxTupleAttributeNumber);
1098 92 : heap_deform_tuple(tup, tupleDesc, toast_values, toast_isnull);
1099 :
1100 92 : memset(toast_free, 0, numAttrs * sizeof(bool));
1101 :
1102 2465 : for (i = 0; i < numAttrs; i++)
1103 : {
1104 : /*
1105 : * Look at non-null varlena attributes
1106 : */
1107 2373 : if (!toast_isnull[i] && TupleDescAttr(tupleDesc, i)->attlen == -1)
1108 : {
1109 : struct varlena *new_value;
1110 :
1111 385 : new_value = (struct varlena *) DatumGetPointer(toast_values[i]);
1112 385 : if (VARATT_IS_EXTERNAL(new_value))
1113 : {
1114 97 : new_value = heap_tuple_fetch_attr(new_value);
1115 97 : toast_values[i] = PointerGetDatum(new_value);
1116 97 : toast_free[i] = true;
1117 : }
1118 : }
1119 : }
1120 :
1121 : /*
1122 : * Form the reconfigured tuple.
1123 : */
1124 92 : new_tuple = heap_form_tuple(tupleDesc, toast_values, toast_isnull);
1125 :
1126 : /*
1127 : * Be sure to copy the tuple's OID and identity fields. We also make a
1128 : * point of copying visibility info, just in case anybody looks at those
1129 : * fields in a syscache entry.
1130 : */
1131 92 : if (tupleDesc->tdhasoid)
1132 1 : HeapTupleSetOid(new_tuple, HeapTupleGetOid(tup));
1133 :
1134 92 : new_tuple->t_self = tup->t_self;
1135 92 : new_tuple->t_tableOid = tup->t_tableOid;
1136 :
1137 92 : new_tuple->t_data->t_choice = tup->t_data->t_choice;
1138 92 : new_tuple->t_data->t_ctid = tup->t_data->t_ctid;
1139 92 : new_tuple->t_data->t_infomask &= ~HEAP_XACT_MASK;
1140 184 : new_tuple->t_data->t_infomask |=
1141 92 : tup->t_data->t_infomask & HEAP_XACT_MASK;
1142 92 : new_tuple->t_data->t_infomask2 &= ~HEAP2_XACT_MASK;
1143 184 : new_tuple->t_data->t_infomask2 |=
1144 92 : tup->t_data->t_infomask2 & HEAP2_XACT_MASK;
1145 :
1146 : /*
1147 : * Free allocated temp values
1148 : */
1149 2465 : for (i = 0; i < numAttrs; i++)
1150 2373 : if (toast_free[i])
1151 97 : pfree(DatumGetPointer(toast_values[i]));
1152 :
1153 92 : return new_tuple;
1154 : }
1155 :
1156 :
1157 : /* ----------
1158 : * toast_flatten_tuple_to_datum -
1159 : *
1160 : * "Flatten" a tuple containing out-of-line toasted fields into a Datum.
1161 : * The result is always palloc'd in the current memory context.
1162 : *
1163 : * We have a general rule that Datums of container types (rows, arrays,
1164 : * ranges, etc) must not contain any external TOAST pointers. Without
1165 : * this rule, we'd have to look inside each Datum when preparing a tuple
1166 : * for storage, which would be expensive and would fail to extend cleanly
1167 : * to new sorts of container types.
1168 : *
1169 : * However, we don't want to say that tuples represented as HeapTuples
1170 : * can't contain toasted fields, so instead this routine should be called
1171 : * when such a HeapTuple is being converted into a Datum.
1172 : *
1173 : * While we're at it, we decompress any compressed fields too. This is not
1174 : * necessary for correctness, but reflects an expectation that compression
1175 : * will be more effective if applied to the whole tuple not individual
1176 : * fields. We are not so concerned about that that we want to deconstruct
1177 : * and reconstruct tuples just to get rid of compressed fields, however.
1178 : * So callers typically won't call this unless they see that the tuple has
1179 : * at least one external field.
1180 : *
1181 : * On the other hand, in-line short-header varlena fields are left alone.
1182 : * If we "untoasted" them here, they'd just get changed back to short-header
1183 : * format anyway within heap_fill_tuple.
1184 : * ----------
1185 : */
1186 : Datum
1187 10 : toast_flatten_tuple_to_datum(HeapTupleHeader tup,
1188 : uint32 tup_len,
1189 : TupleDesc tupleDesc)
1190 : {
1191 : HeapTupleHeader new_data;
1192 : int32 new_header_len;
1193 : int32 new_data_len;
1194 : int32 new_tuple_len;
1195 : HeapTupleData tmptup;
1196 10 : int numAttrs = tupleDesc->natts;
1197 : int i;
1198 10 : bool has_nulls = false;
1199 : Datum toast_values[MaxTupleAttributeNumber];
1200 : bool toast_isnull[MaxTupleAttributeNumber];
1201 : bool toast_free[MaxTupleAttributeNumber];
1202 :
1203 : /* Build a temporary HeapTuple control structure */
1204 10 : tmptup.t_len = tup_len;
1205 10 : ItemPointerSetInvalid(&(tmptup.t_self));
1206 10 : tmptup.t_tableOid = InvalidOid;
1207 10 : tmptup.t_data = tup;
1208 :
1209 : /*
1210 : * Break down the tuple into fields.
1211 : */
1212 10 : Assert(numAttrs <= MaxTupleAttributeNumber);
1213 10 : heap_deform_tuple(&tmptup, tupleDesc, toast_values, toast_isnull);
1214 :
1215 10 : memset(toast_free, 0, numAttrs * sizeof(bool));
1216 :
1217 47 : for (i = 0; i < numAttrs; i++)
1218 : {
1219 : /*
1220 : * Look at non-null varlena attributes
1221 : */
1222 37 : if (toast_isnull[i])
1223 5 : has_nulls = true;
1224 32 : else if (TupleDescAttr(tupleDesc, i)->attlen == -1)
1225 : {
1226 : struct varlena *new_value;
1227 :
1228 24 : new_value = (struct varlena *) DatumGetPointer(toast_values[i]);
1229 35 : if (VARATT_IS_EXTERNAL(new_value) ||
1230 11 : VARATT_IS_COMPRESSED(new_value))
1231 : {
1232 13 : new_value = heap_tuple_untoast_attr(new_value);
1233 13 : toast_values[i] = PointerGetDatum(new_value);
1234 13 : toast_free[i] = true;
1235 : }
1236 : }
1237 : }
1238 :
1239 : /*
1240 : * Calculate the new size of the tuple.
1241 : *
1242 : * This should match the reconstruction code in toast_insert_or_update.
1243 : */
1244 10 : new_header_len = SizeofHeapTupleHeader;
1245 10 : if (has_nulls)
1246 5 : new_header_len += BITMAPLEN(numAttrs);
1247 10 : if (tup->t_infomask & HEAP_HASOID)
1248 0 : new_header_len += sizeof(Oid);
1249 10 : new_header_len = MAXALIGN(new_header_len);
1250 10 : new_data_len = heap_compute_data_size(tupleDesc,
1251 : toast_values, toast_isnull);
1252 10 : new_tuple_len = new_header_len + new_data_len;
1253 :
1254 10 : new_data = (HeapTupleHeader) palloc0(new_tuple_len);
1255 :
1256 : /*
1257 : * Copy the existing tuple header, but adjust natts and t_hoff.
1258 : */
1259 10 : memcpy(new_data, tup, SizeofHeapTupleHeader);
1260 10 : HeapTupleHeaderSetNatts(new_data, numAttrs);
1261 10 : new_data->t_hoff = new_header_len;
1262 10 : if (tup->t_infomask & HEAP_HASOID)
1263 0 : HeapTupleHeaderSetOid(new_data, HeapTupleHeaderGetOid(tup));
1264 :
1265 : /* Set the composite-Datum header fields correctly */
1266 10 : HeapTupleHeaderSetDatumLength(new_data, new_tuple_len);
1267 10 : HeapTupleHeaderSetTypeId(new_data, tupleDesc->tdtypeid);
1268 10 : HeapTupleHeaderSetTypMod(new_data, tupleDesc->tdtypmod);
1269 :
1270 : /* Copy over the data, and fill the null bitmap if needed */
1271 10 : heap_fill_tuple(tupleDesc,
1272 : toast_values,
1273 : toast_isnull,
1274 : (char *) new_data + new_header_len,
1275 : new_data_len,
1276 : &(new_data->t_infomask),
1277 : has_nulls ? new_data->t_bits : NULL);
1278 :
1279 : /*
1280 : * Free allocated temp values
1281 : */
1282 47 : for (i = 0; i < numAttrs; i++)
1283 37 : if (toast_free[i])
1284 13 : pfree(DatumGetPointer(toast_values[i]));
1285 :
1286 10 : return PointerGetDatum(new_data);
1287 : }
1288 :
1289 :
1290 : /* ----------
1291 : * toast_build_flattened_tuple -
1292 : *
1293 : * Build a tuple containing no out-of-line toasted fields.
1294 : * (This does not eliminate compressed or short-header datums.)
1295 : *
1296 : * This is essentially just like heap_form_tuple, except that it will
1297 : * expand any external-data pointers beforehand.
1298 : *
1299 : * It's not very clear whether it would be preferable to decompress
1300 : * in-line compressed datums while at it. For now, we don't.
1301 : * ----------
1302 : */
1303 : HeapTuple
1304 1041 : toast_build_flattened_tuple(TupleDesc tupleDesc,
1305 : Datum *values,
1306 : bool *isnull)
1307 : {
1308 : HeapTuple new_tuple;
1309 1041 : int numAttrs = tupleDesc->natts;
1310 : int num_to_free;
1311 : int i;
1312 : Datum new_values[MaxTupleAttributeNumber];
1313 : Pointer freeable_values[MaxTupleAttributeNumber];
1314 :
1315 : /*
1316 : * We can pass the caller's isnull array directly to heap_form_tuple, but
1317 : * we potentially need to modify the values array.
1318 : */
1319 1041 : Assert(numAttrs <= MaxTupleAttributeNumber);
1320 1041 : memcpy(new_values, values, numAttrs * sizeof(Datum));
1321 :
1322 1041 : num_to_free = 0;
1323 3731 : for (i = 0; i < numAttrs; i++)
1324 : {
1325 : /*
1326 : * Look at non-null varlena attributes
1327 : */
1328 2690 : if (!isnull[i] && TupleDescAttr(tupleDesc, i)->attlen == -1)
1329 : {
1330 : struct varlena *new_value;
1331 :
1332 908 : new_value = (struct varlena *) DatumGetPointer(new_values[i]);
1333 908 : if (VARATT_IS_EXTERNAL(new_value))
1334 : {
1335 67 : new_value = heap_tuple_fetch_attr(new_value);
1336 67 : new_values[i] = PointerGetDatum(new_value);
1337 67 : freeable_values[num_to_free++] = (Pointer) new_value;
1338 : }
1339 : }
1340 : }
1341 :
1342 : /*
1343 : * Form the reconfigured tuple.
1344 : */
1345 1041 : new_tuple = heap_form_tuple(tupleDesc, new_values, isnull);
1346 :
1347 : /*
1348 : * Free allocated temp values
1349 : */
1350 1108 : for (i = 0; i < num_to_free; i++)
1351 67 : pfree(freeable_values[i]);
1352 :
1353 1041 : return new_tuple;
1354 : }
1355 :
1356 :
1357 : /* ----------
1358 : * toast_compress_datum -
1359 : *
1360 : * Create a compressed version of a varlena datum
1361 : *
1362 : * If we fail (ie, compressed result is actually bigger than original)
1363 : * then return NULL. We must not use compressed data if it'd expand
1364 : * the tuple!
1365 : *
1366 : * We use VAR{SIZE,DATA}_ANY so we can handle short varlenas here without
1367 : * copying them. But we can't handle external or compressed datums.
1368 : * ----------
1369 : */
1370 : Datum
1371 2037 : toast_compress_datum(Datum value)
1372 : {
1373 : struct varlena *tmp;
1374 2037 : int32 valsize = VARSIZE_ANY_EXHDR(DatumGetPointer(value));
1375 : int32 len;
1376 :
1377 2037 : Assert(!VARATT_IS_EXTERNAL(DatumGetPointer(value)));
1378 2037 : Assert(!VARATT_IS_COMPRESSED(DatumGetPointer(value)));
1379 :
1380 : /*
1381 : * No point in wasting a palloc cycle if value size is out of the allowed
1382 : * range for compression
1383 : */
1384 4063 : if (valsize < PGLZ_strategy_default->min_input_size ||
1385 2026 : valsize > PGLZ_strategy_default->max_input_size)
1386 11 : return PointerGetDatum(NULL);
1387 :
1388 2026 : tmp = (struct varlena *) palloc(PGLZ_MAX_OUTPUT(valsize) +
1389 : TOAST_COMPRESS_HDRSZ);
1390 :
1391 : /*
1392 : * We recheck the actual size even if pglz_compress() reports success,
1393 : * because it might be satisfied with having saved as little as one byte
1394 : * in the compressed data --- which could turn into a net loss once you
1395 : * consider header and alignment padding. Worst case, the compressed
1396 : * format might require three padding bytes (plus header, which is
1397 : * included in VARSIZE(tmp)), whereas the uncompressed format would take
1398 : * only one header byte and no padding if the value is short enough. So
1399 : * we insist on a savings of more than 2 bytes to ensure we have a gain.
1400 : */
1401 2026 : len = pglz_compress(VARDATA_ANY(DatumGetPointer(value)),
1402 : valsize,
1403 : TOAST_COMPRESS_RAWDATA(tmp),
1404 : PGLZ_strategy_default);
1405 3950 : if (len >= 0 &&
1406 1924 : len + TOAST_COMPRESS_HDRSZ < valsize - 2)
1407 : {
1408 1924 : TOAST_COMPRESS_SET_RAWSIZE(tmp, valsize);
1409 1924 : SET_VARSIZE_COMPRESSED(tmp, len + TOAST_COMPRESS_HDRSZ);
1410 : /* successful compression */
1411 1924 : return PointerGetDatum(tmp);
1412 : }
1413 : else
1414 : {
1415 : /* incompressible data */
1416 102 : pfree(tmp);
1417 102 : return PointerGetDatum(NULL);
1418 : }
1419 : }
1420 :
1421 :
1422 : /* ----------
1423 : * toast_get_valid_index
1424 : *
1425 : * Get OID of valid index associated to given toast relation. A toast
1426 : * relation can have only one valid index at the same time.
1427 : */
1428 : Oid
1429 50 : toast_get_valid_index(Oid toastoid, LOCKMODE lock)
1430 : {
1431 : int num_indexes;
1432 : int validIndex;
1433 : Oid validIndexOid;
1434 : Relation *toastidxs;
1435 : Relation toastrel;
1436 :
1437 : /* Open the toast relation */
1438 50 : toastrel = heap_open(toastoid, lock);
1439 :
1440 : /* Look for the valid index of the toast relation */
1441 50 : validIndex = toast_open_indexes(toastrel,
1442 : lock,
1443 : &toastidxs,
1444 : &num_indexes);
1445 50 : validIndexOid = RelationGetRelid(toastidxs[validIndex]);
1446 :
1447 : /* Close the toast relation and all its indexes */
1448 50 : toast_close_indexes(toastidxs, num_indexes, lock);
1449 50 : heap_close(toastrel, lock);
1450 :
1451 50 : return validIndexOid;
1452 : }
1453 :
1454 :
1455 : /* ----------
1456 : * toast_save_datum -
1457 : *
1458 : * Save one single datum into the secondary relation and return
1459 : * a Datum reference for it.
1460 : *
1461 : * rel: the main relation we're working with (not the toast rel!)
1462 : * value: datum to be pushed to toast storage
1463 : * oldexternal: if not NULL, toast pointer previously representing the datum
1464 : * options: options to be passed to heap_insert() for toast rows
1465 : * ----------
1466 : */
1467 : static Datum
1468 126 : toast_save_datum(Relation rel, Datum value,
1469 : struct varlena *oldexternal, int options)
1470 : {
1471 : Relation toastrel;
1472 : Relation *toastidxs;
1473 : HeapTuple toasttup;
1474 : TupleDesc toasttupDesc;
1475 : Datum t_values[3];
1476 : bool t_isnull[3];
1477 126 : CommandId mycid = GetCurrentCommandId(true);
1478 : struct varlena *result;
1479 : struct varatt_external toast_pointer;
1480 : union
1481 : {
1482 : struct varlena hdr;
1483 : /* this is to make the union big enough for a chunk: */
1484 : char data[TOAST_MAX_CHUNK_SIZE + VARHDRSZ];
1485 : /* ensure union is aligned well enough: */
1486 : int32 align_it;
1487 : } chunk_data;
1488 : int32 chunk_size;
1489 126 : int32 chunk_seq = 0;
1490 : char *data_p;
1491 : int32 data_todo;
1492 126 : Pointer dval = DatumGetPointer(value);
1493 : int num_indexes;
1494 : int validIndex;
1495 :
1496 126 : Assert(!VARATT_IS_EXTERNAL(value));
1497 :
1498 : /*
1499 : * Open the toast relation and its indexes. We can use the index to check
1500 : * uniqueness of the OID we assign to the toasted item, even though it has
1501 : * additional columns besides OID.
1502 : */
1503 126 : toastrel = heap_open(rel->rd_rel->reltoastrelid, RowExclusiveLock);
1504 126 : toasttupDesc = toastrel->rd_att;
1505 :
1506 : /* Open all the toast indexes and look for the valid one */
1507 126 : validIndex = toast_open_indexes(toastrel,
1508 : RowExclusiveLock,
1509 : &toastidxs,
1510 : &num_indexes);
1511 :
1512 : /*
1513 : * Get the data pointer and length, and compute va_rawsize and va_extsize.
1514 : *
1515 : * va_rawsize is the size of the equivalent fully uncompressed datum, so
1516 : * we have to adjust for short headers.
1517 : *
1518 : * va_extsize is the actual size of the data payload in the toast records.
1519 : */
1520 126 : if (VARATT_IS_SHORT(dval))
1521 : {
1522 0 : data_p = VARDATA_SHORT(dval);
1523 0 : data_todo = VARSIZE_SHORT(dval) - VARHDRSZ_SHORT;
1524 0 : toast_pointer.va_rawsize = data_todo + VARHDRSZ; /* as if not short */
1525 0 : toast_pointer.va_extsize = data_todo;
1526 : }
1527 126 : else if (VARATT_IS_COMPRESSED(dval))
1528 : {
1529 122 : data_p = VARDATA(dval);
1530 122 : data_todo = VARSIZE(dval) - VARHDRSZ;
1531 : /* rawsize in a compressed datum is just the size of the payload */
1532 122 : toast_pointer.va_rawsize = VARRAWSIZE_4B_C(dval) + VARHDRSZ;
1533 122 : toast_pointer.va_extsize = data_todo;
1534 : /* Assert that the numbers look like it's compressed */
1535 122 : Assert(VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer));
1536 : }
1537 : else
1538 : {
1539 4 : data_p = VARDATA(dval);
1540 4 : data_todo = VARSIZE(dval) - VARHDRSZ;
1541 4 : toast_pointer.va_rawsize = VARSIZE(dval);
1542 4 : toast_pointer.va_extsize = data_todo;
1543 : }
1544 :
1545 : /*
1546 : * Insert the correct table OID into the result TOAST pointer.
1547 : *
1548 : * Normally this is the actual OID of the target toast table, but during
1549 : * table-rewriting operations such as CLUSTER, we have to insert the OID
1550 : * of the table's real permanent toast table instead. rd_toastoid is set
1551 : * if we have to substitute such an OID.
1552 : */
1553 126 : if (OidIsValid(rel->rd_toastoid))
1554 1 : toast_pointer.va_toastrelid = rel->rd_toastoid;
1555 : else
1556 125 : toast_pointer.va_toastrelid = RelationGetRelid(toastrel);
1557 :
1558 : /*
1559 : * Choose an OID to use as the value ID for this toast value.
1560 : *
1561 : * Normally we just choose an unused OID within the toast table. But
1562 : * during table-rewriting operations where we are preserving an existing
1563 : * toast table OID, we want to preserve toast value OIDs too. So, if
1564 : * rd_toastoid is set and we had a prior external value from that same
1565 : * toast table, re-use its value ID. If we didn't have a prior external
1566 : * value (which is a corner case, but possible if the table's attstorage
1567 : * options have been changed), we have to pick a value ID that doesn't
1568 : * conflict with either new or existing toast value OIDs.
1569 : */
1570 126 : if (!OidIsValid(rel->rd_toastoid))
1571 : {
1572 : /* normal case: just choose an unused OID */
1573 125 : toast_pointer.va_valueid =
1574 125 : GetNewOidWithIndex(toastrel,
1575 125 : RelationGetRelid(toastidxs[validIndex]),
1576 : (AttrNumber) 1);
1577 : }
1578 : else
1579 : {
1580 : /* rewrite case: check to see if value was in old toast table */
1581 1 : toast_pointer.va_valueid = InvalidOid;
1582 1 : if (oldexternal != NULL)
1583 : {
1584 : struct varatt_external old_toast_pointer;
1585 :
1586 1 : Assert(VARATT_IS_EXTERNAL_ONDISK(oldexternal));
1587 : /* Must copy to access aligned fields */
1588 1 : VARATT_EXTERNAL_GET_POINTER(old_toast_pointer, oldexternal);
1589 1 : if (old_toast_pointer.va_toastrelid == rel->rd_toastoid)
1590 : {
1591 : /* This value came from the old toast table; reuse its OID */
1592 1 : toast_pointer.va_valueid = old_toast_pointer.va_valueid;
1593 :
1594 : /*
1595 : * There is a corner case here: the table rewrite might have
1596 : * to copy both live and recently-dead versions of a row, and
1597 : * those versions could easily reference the same toast value.
1598 : * When we copy the second or later version of such a row,
1599 : * reusing the OID will mean we select an OID that's already
1600 : * in the new toast table. Check for that, and if so, just
1601 : * fall through without writing the data again.
1602 : *
1603 : * While annoying and ugly-looking, this is a good thing
1604 : * because it ensures that we wind up with only one copy of
1605 : * the toast value when there is only one copy in the old
1606 : * toast table. Before we detected this case, we'd have made
1607 : * multiple copies, wasting space; and what's worse, the
1608 : * copies belonging to already-deleted heap tuples would not
1609 : * be reclaimed by VACUUM.
1610 : */
1611 1 : if (toastrel_valueid_exists(toastrel,
1612 : toast_pointer.va_valueid))
1613 : {
1614 : /* Match, so short-circuit the data storage loop below */
1615 0 : data_todo = 0;
1616 : }
1617 : }
1618 : }
1619 1 : if (toast_pointer.va_valueid == InvalidOid)
1620 : {
1621 : /*
1622 : * new value; must choose an OID that doesn't conflict in either
1623 : * old or new toast table
1624 : */
1625 : do
1626 : {
1627 0 : toast_pointer.va_valueid =
1628 0 : GetNewOidWithIndex(toastrel,
1629 0 : RelationGetRelid(toastidxs[validIndex]),
1630 : (AttrNumber) 1);
1631 0 : } while (toastid_valueid_exists(rel->rd_toastoid,
1632 0 : toast_pointer.va_valueid));
1633 : }
1634 : }
1635 :
1636 : /*
1637 : * Initialize constant parts of the tuple data
1638 : */
1639 126 : t_values[0] = ObjectIdGetDatum(toast_pointer.va_valueid);
1640 126 : t_values[2] = PointerGetDatum(&chunk_data);
1641 126 : t_isnull[0] = false;
1642 126 : t_isnull[1] = false;
1643 126 : t_isnull[2] = false;
1644 :
1645 : /*
1646 : * Split up the item into chunks
1647 : */
1648 972 : while (data_todo > 0)
1649 : {
1650 : int i;
1651 :
1652 720 : CHECK_FOR_INTERRUPTS();
1653 :
1654 : /*
1655 : * Calculate the size of this chunk
1656 : */
1657 720 : chunk_size = Min(TOAST_MAX_CHUNK_SIZE, data_todo);
1658 :
1659 : /*
1660 : * Build a tuple and store it
1661 : */
1662 720 : t_values[1] = Int32GetDatum(chunk_seq++);
1663 720 : SET_VARSIZE(&chunk_data, chunk_size + VARHDRSZ);
1664 720 : memcpy(VARDATA(&chunk_data), data_p, chunk_size);
1665 720 : toasttup = heap_form_tuple(toasttupDesc, t_values, t_isnull);
1666 :
1667 720 : heap_insert(toastrel, toasttup, mycid, options, NULL);
1668 :
1669 : /*
1670 : * Create the index entry. We cheat a little here by not using
1671 : * FormIndexDatum: this relies on the knowledge that the index columns
1672 : * are the same as the initial columns of the table for all the
1673 : * indexes. We also cheat by not providing an IndexInfo: this is okay
1674 : * for now because btree doesn't need one, but we might have to be
1675 : * more honest someday.
1676 : *
1677 : * Note also that there had better not be any user-created index on
1678 : * the TOAST table, since we don't bother to update anything else.
1679 : */
1680 1440 : for (i = 0; i < num_indexes; i++)
1681 : {
1682 : /* Only index relations marked as ready can be updated */
1683 720 : if (IndexIsReady(toastidxs[i]->rd_index))
1684 720 : index_insert(toastidxs[i], t_values, t_isnull,
1685 : &(toasttup->t_self),
1686 : toastrel,
1687 720 : toastidxs[i]->rd_index->indisunique ?
1688 : UNIQUE_CHECK_YES : UNIQUE_CHECK_NO,
1689 : NULL);
1690 : }
1691 :
1692 : /*
1693 : * Free memory
1694 : */
1695 720 : heap_freetuple(toasttup);
1696 :
1697 : /*
1698 : * Move on to next chunk
1699 : */
1700 720 : data_todo -= chunk_size;
1701 720 : data_p += chunk_size;
1702 : }
1703 :
1704 : /*
1705 : * Done - close toast relation and its indexes
1706 : */
1707 126 : toast_close_indexes(toastidxs, num_indexes, RowExclusiveLock);
1708 126 : heap_close(toastrel, RowExclusiveLock);
1709 :
1710 : /*
1711 : * Create the TOAST pointer value that we'll return
1712 : */
1713 126 : result = (struct varlena *) palloc(TOAST_POINTER_SIZE);
1714 126 : SET_VARTAG_EXTERNAL(result, VARTAG_ONDISK);
1715 126 : memcpy(VARDATA_EXTERNAL(result), &toast_pointer, sizeof(toast_pointer));
1716 :
1717 126 : return PointerGetDatum(result);
1718 : }
1719 :
1720 :
1721 : /* ----------
1722 : * toast_delete_datum -
1723 : *
1724 : * Delete a single external stored value.
1725 : * ----------
1726 : */
1727 : static void
1728 27 : toast_delete_datum(Relation rel, Datum value, bool is_speculative)
1729 : {
1730 27 : struct varlena *attr = (struct varlena *) DatumGetPointer(value);
1731 : struct varatt_external toast_pointer;
1732 : Relation toastrel;
1733 : Relation *toastidxs;
1734 : ScanKeyData toastkey;
1735 : SysScanDesc toastscan;
1736 : HeapTuple toasttup;
1737 : int num_indexes;
1738 : int validIndex;
1739 : SnapshotData SnapshotToast;
1740 :
1741 27 : if (!VARATT_IS_EXTERNAL_ONDISK(attr))
1742 27 : return;
1743 :
1744 : /* Must copy to access aligned fields */
1745 27 : VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
1746 :
1747 : /*
1748 : * Open the toast relation and its indexes
1749 : */
1750 27 : toastrel = heap_open(toast_pointer.va_toastrelid, RowExclusiveLock);
1751 :
1752 : /* Fetch valid relation used for process */
1753 27 : validIndex = toast_open_indexes(toastrel,
1754 : RowExclusiveLock,
1755 : &toastidxs,
1756 : &num_indexes);
1757 :
1758 : /*
1759 : * Setup a scan key to find chunks with matching va_valueid
1760 : */
1761 27 : ScanKeyInit(&toastkey,
1762 : (AttrNumber) 1,
1763 : BTEqualStrategyNumber, F_OIDEQ,
1764 : ObjectIdGetDatum(toast_pointer.va_valueid));
1765 :
1766 : /*
1767 : * Find all the chunks. (We don't actually care whether we see them in
1768 : * sequence or not, but since we've already locked the index we might as
1769 : * well use systable_beginscan_ordered.)
1770 : */
1771 27 : init_toast_snapshot(&SnapshotToast);
1772 27 : toastscan = systable_beginscan_ordered(toastrel, toastidxs[validIndex],
1773 : &SnapshotToast, 1, &toastkey);
1774 171 : while ((toasttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL)
1775 : {
1776 : /*
1777 : * Have a chunk, delete it
1778 : */
1779 117 : if (is_speculative)
1780 0 : heap_abort_speculative(toastrel, toasttup);
1781 : else
1782 117 : simple_heap_delete(toastrel, &toasttup->t_self);
1783 : }
1784 :
1785 : /*
1786 : * End scan and close relations
1787 : */
1788 27 : systable_endscan_ordered(toastscan);
1789 27 : toast_close_indexes(toastidxs, num_indexes, RowExclusiveLock);
1790 27 : heap_close(toastrel, RowExclusiveLock);
1791 : }
1792 :
1793 :
1794 : /* ----------
1795 : * toastrel_valueid_exists -
1796 : *
1797 : * Test whether a toast value with the given ID exists in the toast relation
1798 : * ----------
1799 : */
1800 : static bool
1801 1 : toastrel_valueid_exists(Relation toastrel, Oid valueid)
1802 : {
1803 1 : bool result = false;
1804 : ScanKeyData toastkey;
1805 : SysScanDesc toastscan;
1806 : int num_indexes;
1807 : int validIndex;
1808 : Relation *toastidxs;
1809 : SnapshotData SnapshotToast;
1810 :
1811 : /* Fetch a valid index relation */
1812 1 : validIndex = toast_open_indexes(toastrel,
1813 : RowExclusiveLock,
1814 : &toastidxs,
1815 : &num_indexes);
1816 :
1817 : /*
1818 : * Setup a scan key to find chunks with matching va_valueid
1819 : */
1820 1 : ScanKeyInit(&toastkey,
1821 : (AttrNumber) 1,
1822 : BTEqualStrategyNumber, F_OIDEQ,
1823 : ObjectIdGetDatum(valueid));
1824 :
1825 : /*
1826 : * Is there any such chunk?
1827 : */
1828 1 : init_toast_snapshot(&SnapshotToast);
1829 1 : toastscan = systable_beginscan(toastrel,
1830 1 : RelationGetRelid(toastidxs[validIndex]),
1831 : true, &SnapshotToast, 1, &toastkey);
1832 :
1833 1 : if (systable_getnext(toastscan) != NULL)
1834 0 : result = true;
1835 :
1836 1 : systable_endscan(toastscan);
1837 :
1838 : /* Clean up */
1839 1 : toast_close_indexes(toastidxs, num_indexes, RowExclusiveLock);
1840 :
1841 1 : return result;
1842 : }
1843 :
1844 : /* ----------
1845 : * toastid_valueid_exists -
1846 : *
1847 : * As above, but work from toast rel's OID not an open relation
1848 : * ----------
1849 : */
1850 : static bool
1851 0 : toastid_valueid_exists(Oid toastrelid, Oid valueid)
1852 : {
1853 : bool result;
1854 : Relation toastrel;
1855 :
1856 0 : toastrel = heap_open(toastrelid, AccessShareLock);
1857 :
1858 0 : result = toastrel_valueid_exists(toastrel, valueid);
1859 :
1860 0 : heap_close(toastrel, AccessShareLock);
1861 :
1862 0 : return result;
1863 : }
1864 :
1865 :
1866 : /* ----------
1867 : * toast_fetch_datum -
1868 : *
1869 : * Reconstruct an in memory Datum from the chunks saved
1870 : * in the toast relation
1871 : * ----------
1872 : */
1873 : static struct varlena *
1874 344 : toast_fetch_datum(struct varlena *attr)
1875 : {
1876 : Relation toastrel;
1877 : Relation *toastidxs;
1878 : ScanKeyData toastkey;
1879 : SysScanDesc toastscan;
1880 : HeapTuple ttup;
1881 : TupleDesc toasttupDesc;
1882 : struct varlena *result;
1883 : struct varatt_external toast_pointer;
1884 : int32 ressize;
1885 : int32 residx,
1886 : nextidx;
1887 : int32 numchunks;
1888 : Pointer chunk;
1889 : bool isnull;
1890 : char *chunkdata;
1891 : int32 chunksize;
1892 : int num_indexes;
1893 : int validIndex;
1894 : SnapshotData SnapshotToast;
1895 :
1896 344 : if (!VARATT_IS_EXTERNAL_ONDISK(attr))
1897 0 : elog(ERROR, "toast_fetch_datum shouldn't be called for non-ondisk datums");
1898 :
1899 : /* Must copy to access aligned fields */
1900 344 : VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
1901 :
1902 344 : ressize = toast_pointer.va_extsize;
1903 344 : numchunks = ((ressize - 1) / TOAST_MAX_CHUNK_SIZE) + 1;
1904 :
1905 344 : result = (struct varlena *) palloc(ressize + VARHDRSZ);
1906 :
1907 344 : if (VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer))
1908 344 : SET_VARSIZE_COMPRESSED(result, ressize + VARHDRSZ);
1909 : else
1910 0 : SET_VARSIZE(result, ressize + VARHDRSZ);
1911 :
1912 : /*
1913 : * Open the toast relation and its indexes
1914 : */
1915 344 : toastrel = heap_open(toast_pointer.va_toastrelid, AccessShareLock);
1916 344 : toasttupDesc = toastrel->rd_att;
1917 :
1918 : /* Look for the valid index of the toast relation */
1919 344 : validIndex = toast_open_indexes(toastrel,
1920 : AccessShareLock,
1921 : &toastidxs,
1922 : &num_indexes);
1923 :
1924 : /*
1925 : * Setup a scan key to fetch from the index by va_valueid
1926 : */
1927 344 : ScanKeyInit(&toastkey,
1928 : (AttrNumber) 1,
1929 : BTEqualStrategyNumber, F_OIDEQ,
1930 : ObjectIdGetDatum(toast_pointer.va_valueid));
1931 :
1932 : /*
1933 : * Read the chunks by index
1934 : *
1935 : * Note that because the index is actually on (valueid, chunkidx) we will
1936 : * see the chunks in chunkidx order, even though we didn't explicitly ask
1937 : * for it.
1938 : */
1939 344 : nextidx = 0;
1940 :
1941 344 : init_toast_snapshot(&SnapshotToast);
1942 344 : toastscan = systable_beginscan_ordered(toastrel, toastidxs[validIndex],
1943 : &SnapshotToast, 1, &toastkey);
1944 1995 : while ((ttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL)
1945 : {
1946 : /*
1947 : * Have a chunk, extract the sequence number and the data
1948 : */
1949 1307 : residx = DatumGetInt32(fastgetattr(ttup, 2, toasttupDesc, &isnull));
1950 1307 : Assert(!isnull);
1951 1307 : chunk = DatumGetPointer(fastgetattr(ttup, 3, toasttupDesc, &isnull));
1952 1307 : Assert(!isnull);
1953 1307 : if (!VARATT_IS_EXTENDED(chunk))
1954 : {
1955 1307 : chunksize = VARSIZE(chunk) - VARHDRSZ;
1956 1307 : chunkdata = VARDATA(chunk);
1957 : }
1958 0 : else if (VARATT_IS_SHORT(chunk))
1959 : {
1960 : /* could happen due to heap_form_tuple doing its thing */
1961 0 : chunksize = VARSIZE_SHORT(chunk) - VARHDRSZ_SHORT;
1962 0 : chunkdata = VARDATA_SHORT(chunk);
1963 : }
1964 : else
1965 : {
1966 : /* should never happen */
1967 0 : elog(ERROR, "found toasted toast chunk for toast value %u in %s",
1968 : toast_pointer.va_valueid,
1969 : RelationGetRelationName(toastrel));
1970 : chunksize = 0; /* keep compiler quiet */
1971 : chunkdata = NULL;
1972 : }
1973 :
1974 : /*
1975 : * Some checks on the data we've found
1976 : */
1977 1307 : if (residx != nextidx)
1978 0 : elog(ERROR, "unexpected chunk number %d (expected %d) for toast value %u in %s",
1979 : residx, nextidx,
1980 : toast_pointer.va_valueid,
1981 : RelationGetRelationName(toastrel));
1982 1307 : if (residx < numchunks - 1)
1983 : {
1984 963 : if (chunksize != TOAST_MAX_CHUNK_SIZE)
1985 0 : elog(ERROR, "unexpected chunk size %d (expected %d) in chunk %d of %d for toast value %u in %s",
1986 : chunksize, (int) TOAST_MAX_CHUNK_SIZE,
1987 : residx, numchunks,
1988 : toast_pointer.va_valueid,
1989 : RelationGetRelationName(toastrel));
1990 : }
1991 344 : else if (residx == numchunks - 1)
1992 : {
1993 344 : if ((residx * TOAST_MAX_CHUNK_SIZE + chunksize) != ressize)
1994 0 : elog(ERROR, "unexpected chunk size %d (expected %d) in final chunk %d for toast value %u in %s",
1995 : chunksize,
1996 : (int) (ressize - residx * TOAST_MAX_CHUNK_SIZE),
1997 : residx,
1998 : toast_pointer.va_valueid,
1999 : RelationGetRelationName(toastrel));
2000 : }
2001 : else
2002 0 : elog(ERROR, "unexpected chunk number %d (out of range %d..%d) for toast value %u in %s",
2003 : residx,
2004 : 0, numchunks - 1,
2005 : toast_pointer.va_valueid,
2006 : RelationGetRelationName(toastrel));
2007 :
2008 : /*
2009 : * Copy the data into proper place in our result
2010 : */
2011 1307 : memcpy(VARDATA(result) + residx * TOAST_MAX_CHUNK_SIZE,
2012 : chunkdata,
2013 : chunksize);
2014 :
2015 1307 : nextidx++;
2016 : }
2017 :
2018 : /*
2019 : * Final checks that we successfully fetched the datum
2020 : */
2021 344 : if (nextidx != numchunks)
2022 0 : elog(ERROR, "missing chunk number %d for toast value %u in %s",
2023 : nextidx,
2024 : toast_pointer.va_valueid,
2025 : RelationGetRelationName(toastrel));
2026 :
2027 : /*
2028 : * End scan and close relations
2029 : */
2030 344 : systable_endscan_ordered(toastscan);
2031 344 : toast_close_indexes(toastidxs, num_indexes, AccessShareLock);
2032 344 : heap_close(toastrel, AccessShareLock);
2033 :
2034 344 : return result;
2035 : }
2036 :
2037 : /* ----------
2038 : * toast_fetch_datum_slice -
2039 : *
2040 : * Reconstruct a segment of a Datum from the chunks saved
2041 : * in the toast relation
2042 : * ----------
2043 : */
2044 : static struct varlena *
2045 12 : toast_fetch_datum_slice(struct varlena *attr, int32 sliceoffset, int32 length)
2046 : {
2047 : Relation toastrel;
2048 : Relation *toastidxs;
2049 : ScanKeyData toastkey[3];
2050 : int nscankeys;
2051 : SysScanDesc toastscan;
2052 : HeapTuple ttup;
2053 : TupleDesc toasttupDesc;
2054 : struct varlena *result;
2055 : struct varatt_external toast_pointer;
2056 : int32 attrsize;
2057 : int32 residx;
2058 : int32 nextidx;
2059 : int numchunks;
2060 : int startchunk;
2061 : int endchunk;
2062 : int32 startoffset;
2063 : int32 endoffset;
2064 : int totalchunks;
2065 : Pointer chunk;
2066 : bool isnull;
2067 : char *chunkdata;
2068 : int32 chunksize;
2069 : int32 chcpystrt;
2070 : int32 chcpyend;
2071 : int num_indexes;
2072 : int validIndex;
2073 : SnapshotData SnapshotToast;
2074 :
2075 12 : if (!VARATT_IS_EXTERNAL_ONDISK(attr))
2076 0 : elog(ERROR, "toast_fetch_datum_slice shouldn't be called for non-ondisk datums");
2077 :
2078 : /* Must copy to access aligned fields */
2079 12 : VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
2080 :
2081 : /*
2082 : * It's nonsense to fetch slices of a compressed datum -- this isn't lo_*
2083 : * we can't return a compressed datum which is meaningful to toast later
2084 : */
2085 12 : Assert(!VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer));
2086 :
2087 12 : attrsize = toast_pointer.va_extsize;
2088 12 : totalchunks = ((attrsize - 1) / TOAST_MAX_CHUNK_SIZE) + 1;
2089 :
2090 12 : if (sliceoffset >= attrsize)
2091 : {
2092 0 : sliceoffset = 0;
2093 0 : length = 0;
2094 : }
2095 :
2096 12 : if (((sliceoffset + length) > attrsize) || length < 0)
2097 8 : length = attrsize - sliceoffset;
2098 :
2099 12 : result = (struct varlena *) palloc(length + VARHDRSZ);
2100 :
2101 12 : if (VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer))
2102 0 : SET_VARSIZE_COMPRESSED(result, length + VARHDRSZ);
2103 : else
2104 12 : SET_VARSIZE(result, length + VARHDRSZ);
2105 :
2106 12 : if (length == 0)
2107 0 : return result; /* Can save a lot of work at this point! */
2108 :
2109 12 : startchunk = sliceoffset / TOAST_MAX_CHUNK_SIZE;
2110 12 : endchunk = (sliceoffset + length - 1) / TOAST_MAX_CHUNK_SIZE;
2111 12 : numchunks = (endchunk - startchunk) + 1;
2112 :
2113 12 : startoffset = sliceoffset % TOAST_MAX_CHUNK_SIZE;
2114 12 : endoffset = (sliceoffset + length - 1) % TOAST_MAX_CHUNK_SIZE;
2115 :
2116 : /*
2117 : * Open the toast relation and its indexes
2118 : */
2119 12 : toastrel = heap_open(toast_pointer.va_toastrelid, AccessShareLock);
2120 12 : toasttupDesc = toastrel->rd_att;
2121 :
2122 : /* Look for the valid index of toast relation */
2123 12 : validIndex = toast_open_indexes(toastrel,
2124 : AccessShareLock,
2125 : &toastidxs,
2126 : &num_indexes);
2127 :
2128 : /*
2129 : * Setup a scan key to fetch from the index. This is either two keys or
2130 : * three depending on the number of chunks.
2131 : */
2132 12 : ScanKeyInit(&toastkey[0],
2133 : (AttrNumber) 1,
2134 : BTEqualStrategyNumber, F_OIDEQ,
2135 : ObjectIdGetDatum(toast_pointer.va_valueid));
2136 :
2137 : /*
2138 : * Use equality condition for one chunk, a range condition otherwise:
2139 : */
2140 12 : if (numchunks == 1)
2141 : {
2142 8 : ScanKeyInit(&toastkey[1],
2143 : (AttrNumber) 2,
2144 : BTEqualStrategyNumber, F_INT4EQ,
2145 : Int32GetDatum(startchunk));
2146 8 : nscankeys = 2;
2147 : }
2148 : else
2149 : {
2150 4 : ScanKeyInit(&toastkey[1],
2151 : (AttrNumber) 2,
2152 : BTGreaterEqualStrategyNumber, F_INT4GE,
2153 : Int32GetDatum(startchunk));
2154 4 : ScanKeyInit(&toastkey[2],
2155 : (AttrNumber) 2,
2156 : BTLessEqualStrategyNumber, F_INT4LE,
2157 : Int32GetDatum(endchunk));
2158 4 : nscankeys = 3;
2159 : }
2160 :
2161 : /*
2162 : * Read the chunks by index
2163 : *
2164 : * The index is on (valueid, chunkidx) so they will come in order
2165 : */
2166 12 : init_toast_snapshot(&SnapshotToast);
2167 12 : nextidx = startchunk;
2168 12 : toastscan = systable_beginscan_ordered(toastrel, toastidxs[validIndex],
2169 : &SnapshotToast, nscankeys, toastkey);
2170 232 : while ((ttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL)
2171 : {
2172 : /*
2173 : * Have a chunk, extract the sequence number and the data
2174 : */
2175 208 : residx = DatumGetInt32(fastgetattr(ttup, 2, toasttupDesc, &isnull));
2176 208 : Assert(!isnull);
2177 208 : chunk = DatumGetPointer(fastgetattr(ttup, 3, toasttupDesc, &isnull));
2178 208 : Assert(!isnull);
2179 208 : if (!VARATT_IS_EXTENDED(chunk))
2180 : {
2181 208 : chunksize = VARSIZE(chunk) - VARHDRSZ;
2182 208 : chunkdata = VARDATA(chunk);
2183 : }
2184 0 : else if (VARATT_IS_SHORT(chunk))
2185 : {
2186 : /* could happen due to heap_form_tuple doing its thing */
2187 0 : chunksize = VARSIZE_SHORT(chunk) - VARHDRSZ_SHORT;
2188 0 : chunkdata = VARDATA_SHORT(chunk);
2189 : }
2190 : else
2191 : {
2192 : /* should never happen */
2193 0 : elog(ERROR, "found toasted toast chunk for toast value %u in %s",
2194 : toast_pointer.va_valueid,
2195 : RelationGetRelationName(toastrel));
2196 : chunksize = 0; /* keep compiler quiet */
2197 : chunkdata = NULL;
2198 : }
2199 :
2200 : /*
2201 : * Some checks on the data we've found
2202 : */
2203 208 : if ((residx != nextidx) || (residx > endchunk) || (residx < startchunk))
2204 0 : elog(ERROR, "unexpected chunk number %d (expected %d) for toast value %u in %s",
2205 : residx, nextidx,
2206 : toast_pointer.va_valueid,
2207 : RelationGetRelationName(toastrel));
2208 208 : if (residx < totalchunks - 1)
2209 : {
2210 200 : if (chunksize != TOAST_MAX_CHUNK_SIZE)
2211 0 : elog(ERROR, "unexpected chunk size %d (expected %d) in chunk %d of %d for toast value %u in %s when fetching slice",
2212 : chunksize, (int) TOAST_MAX_CHUNK_SIZE,
2213 : residx, totalchunks,
2214 : toast_pointer.va_valueid,
2215 : RelationGetRelationName(toastrel));
2216 : }
2217 8 : else if (residx == totalchunks - 1)
2218 : {
2219 8 : if ((residx * TOAST_MAX_CHUNK_SIZE + chunksize) != attrsize)
2220 0 : elog(ERROR, "unexpected chunk size %d (expected %d) in final chunk %d for toast value %u in %s when fetching slice",
2221 : chunksize,
2222 : (int) (attrsize - residx * TOAST_MAX_CHUNK_SIZE),
2223 : residx,
2224 : toast_pointer.va_valueid,
2225 : RelationGetRelationName(toastrel));
2226 : }
2227 : else
2228 0 : elog(ERROR, "unexpected chunk number %d (out of range %d..%d) for toast value %u in %s",
2229 : residx,
2230 : 0, totalchunks - 1,
2231 : toast_pointer.va_valueid,
2232 : RelationGetRelationName(toastrel));
2233 :
2234 : /*
2235 : * Copy the data into proper place in our result
2236 : */
2237 208 : chcpystrt = 0;
2238 208 : chcpyend = chunksize - 1;
2239 208 : if (residx == startchunk)
2240 12 : chcpystrt = startoffset;
2241 208 : if (residx == endchunk)
2242 12 : chcpyend = endoffset;
2243 :
2244 624 : memcpy(VARDATA(result) +
2245 416 : (residx * TOAST_MAX_CHUNK_SIZE - sliceoffset) + chcpystrt,
2246 208 : chunkdata + chcpystrt,
2247 208 : (chcpyend - chcpystrt) + 1);
2248 :
2249 208 : nextidx++;
2250 : }
2251 :
2252 : /*
2253 : * Final checks that we successfully fetched the datum
2254 : */
2255 12 : if (nextidx != (endchunk + 1))
2256 0 : elog(ERROR, "missing chunk number %d for toast value %u in %s",
2257 : nextidx,
2258 : toast_pointer.va_valueid,
2259 : RelationGetRelationName(toastrel));
2260 :
2261 : /*
2262 : * End scan and close relations
2263 : */
2264 12 : systable_endscan_ordered(toastscan);
2265 12 : toast_close_indexes(toastidxs, num_indexes, AccessShareLock);
2266 12 : heap_close(toastrel, AccessShareLock);
2267 :
2268 12 : return result;
2269 : }
2270 :
2271 : /* ----------
2272 : * toast_decompress_datum -
2273 : *
2274 : * Decompress a compressed version of a varlena datum
2275 : */
2276 : static struct varlena *
2277 5429 : toast_decompress_datum(struct varlena *attr)
2278 : {
2279 : struct varlena *result;
2280 :
2281 5429 : Assert(VARATT_IS_COMPRESSED(attr));
2282 :
2283 5429 : result = (struct varlena *)
2284 5429 : palloc(TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
2285 5429 : SET_VARSIZE(result, TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
2286 :
2287 10858 : if (pglz_decompress(TOAST_COMPRESS_RAWDATA(attr),
2288 5429 : VARSIZE(attr) - TOAST_COMPRESS_HDRSZ,
2289 5429 : VARDATA(result),
2290 : TOAST_COMPRESS_RAWSIZE(attr)) < 0)
2291 0 : elog(ERROR, "compressed data is corrupted");
2292 :
2293 5429 : return result;
2294 : }
2295 :
2296 :
2297 : /* ----------
2298 : * toast_open_indexes
2299 : *
2300 : * Get an array of the indexes associated to the given toast relation
2301 : * and return as well the position of the valid index used by the toast
2302 : * relation in this array. It is the responsibility of the caller of this
2303 : * function to close the indexes as well as free them.
2304 : */
2305 : static int
2306 560 : toast_open_indexes(Relation toastrel,
2307 : LOCKMODE lock,
2308 : Relation **toastidxs,
2309 : int *num_indexes)
2310 : {
2311 560 : int i = 0;
2312 560 : int res = 0;
2313 560 : bool found = false;
2314 : List *indexlist;
2315 : ListCell *lc;
2316 :
2317 : /* Get index list of the toast relation */
2318 560 : indexlist = RelationGetIndexList(toastrel);
2319 560 : Assert(indexlist != NIL);
2320 :
2321 560 : *num_indexes = list_length(indexlist);
2322 :
2323 : /* Open all the index relations */
2324 560 : *toastidxs = (Relation *) palloc(*num_indexes * sizeof(Relation));
2325 1120 : foreach(lc, indexlist)
2326 560 : (*toastidxs)[i++] = index_open(lfirst_oid(lc), lock);
2327 :
2328 : /* Fetch the first valid index in list */
2329 560 : for (i = 0; i < *num_indexes; i++)
2330 : {
2331 560 : Relation toastidx = (*toastidxs)[i];
2332 :
2333 560 : if (toastidx->rd_index->indisvalid)
2334 : {
2335 560 : res = i;
2336 560 : found = true;
2337 560 : break;
2338 : }
2339 : }
2340 :
2341 : /*
2342 : * Free index list, not necessary anymore as relations are opened and a
2343 : * valid index has been found.
2344 : */
2345 560 : list_free(indexlist);
2346 :
2347 : /*
2348 : * The toast relation should have one valid index, so something is going
2349 : * wrong if there is nothing.
2350 : */
2351 560 : if (!found)
2352 0 : elog(ERROR, "no valid index found for toast relation with Oid %u",
2353 : RelationGetRelid(toastrel));
2354 :
2355 560 : return res;
2356 : }
2357 :
2358 : /* ----------
2359 : * toast_close_indexes
2360 : *
2361 : * Close an array of indexes for a toast relation and free it. This should
2362 : * be called for a set of indexes opened previously with toast_open_indexes.
2363 : */
2364 : static void
2365 560 : toast_close_indexes(Relation *toastidxs, int num_indexes, LOCKMODE lock)
2366 : {
2367 : int i;
2368 :
2369 : /* Close relations and clean up things */
2370 1120 : for (i = 0; i < num_indexes; i++)
2371 560 : index_close(toastidxs[i], lock);
2372 560 : pfree(toastidxs);
2373 560 : }
2374 :
2375 : /* ----------
2376 : * init_toast_snapshot
2377 : *
2378 : * Initialize an appropriate TOAST snapshot. We must use an MVCC snapshot
2379 : * to initialize the TOAST snapshot; since we don't know which one to use,
2380 : * just use the oldest one. This is safe: at worst, we will get a "snapshot
2381 : * too old" error that might have been avoided otherwise.
2382 : */
2383 : static void
2384 384 : init_toast_snapshot(Snapshot toast_snapshot)
2385 : {
2386 384 : Snapshot snapshot = GetOldestSnapshot();
2387 :
2388 384 : if (snapshot == NULL)
2389 0 : elog(ERROR, "no known snapshots");
2390 :
2391 384 : InitToastSnapshot(*toast_snapshot, snapshot->lsn, snapshot->whenTaken);
2392 384 : }
|