PostgreSQL Source Code  git master
tuptoaster.c
Go to the documentation of this file.
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!) */
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);
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 *
102 {
103  struct varlena *result;
104 
105  if (VARATT_IS_EXTERNAL_ONDISK(attr))
106  {
107  /*
108  * This is an external stored plain value
109  */
110  result = toast_fetch_datum(attr);
111  }
112  else if (VARATT_IS_EXTERNAL_INDIRECT(attr))
113  {
114  /*
115  * This is an indirect pointer --- dereference it
116  */
117  struct varatt_indirect redirect;
118 
119  VARATT_EXTERNAL_GET_POINTER(redirect, attr);
120  attr = (struct varlena *) redirect.pointer;
121 
122  /* nested indirect Datums aren't allowed */
124 
125  /* recurse if value is still external in some other way */
126  if (VARATT_IS_EXTERNAL(attr))
127  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  result = (struct varlena *) palloc(VARSIZE_ANY(attr));
134  memcpy(result, attr, VARSIZE_ANY(attr));
135  }
136  else if (VARATT_IS_EXTERNAL_EXPANDED(attr))
137  {
138  /*
139  * This is an expanded-object pointer --- get flat format
140  */
142  Size resultsize;
143 
144  eoh = DatumGetEOHP(PointerGetDatum(attr));
145  resultsize = EOH_get_flat_size(eoh);
146  result = (struct varlena *) palloc(resultsize);
147  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  result = attr;
155  }
156 
157  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 *
173 {
174  if (VARATT_IS_EXTERNAL_ONDISK(attr))
175  {
176  /*
177  * This is an externally stored datum --- fetch it back from there
178  */
179  attr = toast_fetch_datum(attr);
180  /* If it's compressed, decompress it */
181  if (VARATT_IS_COMPRESSED(attr))
182  {
183  struct varlena *tmp = attr;
184 
185  attr = toast_decompress_datum(tmp);
186  pfree(tmp);
187  }
188  }
189  else if (VARATT_IS_EXTERNAL_INDIRECT(attr))
190  {
191  /*
192  * This is an indirect pointer --- dereference it
193  */
194  struct varatt_indirect redirect;
195 
196  VARATT_EXTERNAL_GET_POINTER(redirect, attr);
197  attr = (struct varlena *) redirect.pointer;
198 
199  /* nested indirect Datums aren't allowed */
201 
202  /* recurse in case value is still extended in some other way */
203  attr = heap_tuple_untoast_attr(attr);
204 
205  /* if it isn't, we'd better copy it */
206  if (attr == (struct varlena *) redirect.pointer)
207  {
208  struct varlena *result;
209 
210  result = (struct varlena *) palloc(VARSIZE_ANY(attr));
211  memcpy(result, attr, VARSIZE_ANY(attr));
212  attr = result;
213  }
214  }
215  else if (VARATT_IS_EXTERNAL_EXPANDED(attr))
216  {
217  /*
218  * This is an expanded-object pointer --- get flat format
219  */
220  attr = heap_tuple_fetch_attr(attr);
221  /* flatteners are not allowed to produce compressed/short output */
222  Assert(!VARATT_IS_EXTENDED(attr));
223  }
224  else if (VARATT_IS_COMPRESSED(attr))
225  {
226  /*
227  * This is a compressed value inside of the main tuple
228  */
229  attr = toast_decompress_datum(attr);
230  }
231  else if (VARATT_IS_SHORT(attr))
232  {
233  /*
234  * This is a short-header varlena --- convert to 4-byte header format
235  */
236  Size data_size = VARSIZE_SHORT(attr) - VARHDRSZ_SHORT;
237  Size new_size = data_size + VARHDRSZ;
238  struct varlena *new_attr;
239 
240  new_attr = (struct varlena *) palloc(new_size);
241  SET_VARSIZE(new_attr, new_size);
242  memcpy(VARDATA(new_attr), VARDATA_SHORT(attr), data_size);
243  attr = new_attr;
244  }
245 
246  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 *
259  int32 sliceoffset, int32 slicelength)
260 {
261  struct varlena *preslice;
262  struct varlena *result;
263  char *attrdata;
264  int32 attrsize;
265 
266  if (VARATT_IS_EXTERNAL_ONDISK(attr))
267  {
268  struct varatt_external toast_pointer;
269 
270  VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
271 
272  /* fast path for non-compressed external datums */
273  if (!VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer))
274  return toast_fetch_datum_slice(attr, sliceoffset, slicelength);
275 
276  /* fetch it back (compressed marker will get set automatically) */
277  preslice = toast_fetch_datum(attr);
278  }
279  else if (VARATT_IS_EXTERNAL_INDIRECT(attr))
280  {
281  struct varatt_indirect redirect;
282 
283  VARATT_EXTERNAL_GET_POINTER(redirect, attr);
284 
285  /* nested indirect Datums aren't allowed */
287 
288  return heap_tuple_untoast_attr_slice(redirect.pointer,
289  sliceoffset, slicelength);
290  }
291  else if (VARATT_IS_EXTERNAL_EXPANDED(attr))
292  {
293  /* pass it off to heap_tuple_fetch_attr to flatten */
294  preslice = heap_tuple_fetch_attr(attr);
295  }
296  else
297  preslice = attr;
298 
299  Assert(!VARATT_IS_EXTERNAL(preslice));
300 
301  if (VARATT_IS_COMPRESSED(preslice))
302  {
303  struct varlena *tmp = preslice;
304 
305  preslice = toast_decompress_datum(tmp);
306 
307  if (tmp != attr)
308  pfree(tmp);
309  }
310 
311  if (VARATT_IS_SHORT(preslice))
312  {
313  attrdata = VARDATA_SHORT(preslice);
314  attrsize = VARSIZE_SHORT(preslice) - VARHDRSZ_SHORT;
315  }
316  else
317  {
318  attrdata = VARDATA(preslice);
319  attrsize = VARSIZE(preslice) - VARHDRSZ;
320  }
321 
322  /* slicing of datum for compressed cases and plain value */
323 
324  if (sliceoffset >= attrsize)
325  {
326  sliceoffset = 0;
327  slicelength = 0;
328  }
329 
330  if (((sliceoffset + slicelength) > attrsize) || slicelength < 0)
331  slicelength = attrsize - sliceoffset;
332 
333  result = (struct varlena *) palloc(slicelength + VARHDRSZ);
334  SET_VARSIZE(result, slicelength + VARHDRSZ);
335 
336  memcpy(VARDATA(result), attrdata + sliceoffset, slicelength);
337 
338  if (preslice != attr)
339  pfree(preslice);
340 
341  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
354 {
355  struct varlena *attr = (struct varlena *) DatumGetPointer(value);
356  Size result;
357 
358  if (VARATT_IS_EXTERNAL_ONDISK(attr))
359  {
360  /* va_rawsize is the size of the original datum -- including header */
361  struct varatt_external toast_pointer;
362 
363  VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
364  result = toast_pointer.va_rawsize;
365  }
366  else if (VARATT_IS_EXTERNAL_INDIRECT(attr))
367  {
368  struct varatt_indirect toast_pointer;
369 
370  VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
371 
372  /* nested indirect Datums aren't allowed */
373  Assert(!VARATT_IS_EXTERNAL_INDIRECT(toast_pointer.pointer));
374 
375  return toast_raw_datum_size(PointerGetDatum(toast_pointer.pointer));
376  }
377  else if (VARATT_IS_EXTERNAL_EXPANDED(attr))
378  {
379  result = EOH_get_flat_size(DatumGetEOHP(value));
380  }
381  else if (VARATT_IS_COMPRESSED(attr))
382  {
383  /* here, va_rawsize is just the payload size */
384  result = VARRAWSIZE_4B_C(attr) + VARHDRSZ;
385  }
386  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  result = VARSIZE_SHORT(attr) - VARHDRSZ_SHORT + VARHDRSZ;
393  }
394  else
395  {
396  /* plain untoasted datum */
397  result = VARSIZE(attr);
398  }
399  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
410 {
411  struct varlena *attr = (struct varlena *) DatumGetPointer(value);
412  Size result;
413 
414  if (VARATT_IS_EXTERNAL_ONDISK(attr))
415  {
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  VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
424  result = toast_pointer.va_extsize;
425  }
426  else if (VARATT_IS_EXTERNAL_INDIRECT(attr))
427  {
428  struct varatt_indirect toast_pointer;
429 
430  VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
431 
432  /* nested indirect Datums aren't allowed */
434 
435  return toast_datum_size(PointerGetDatum(toast_pointer.pointer));
436  }
437  else if (VARATT_IS_EXTERNAL_EXPANDED(attr))
438  {
439  result = EOH_get_flat_size(DatumGetEOHP(value));
440  }
441  else if (VARATT_IS_SHORT(attr))
442  {
443  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  result = VARSIZE(attr);
452  }
453  return result;
454 }
455 
456 
457 /* ----------
458  * toast_delete -
459  *
460  * Cascaded delete toast-entries on DELETE
461  * ----------
462  */
463 void
464 toast_delete(Relation rel, HeapTuple oldtup, bool is_speculative)
465 {
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  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  tupleDesc = rel->rd_att;
491  numAttrs = tupleDesc->natts;
492 
493  Assert(numAttrs <= MaxHeapAttributeNumber);
494  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  for (i = 0; i < numAttrs; i++)
501  {
502  if (TupleDescAttr(tupleDesc, i)->attlen == -1)
503  {
504  Datum value = toast_values[i];
505 
506  if (toast_isnull[i])
507  continue;
509  toast_delete_datum(rel, value, is_speculative);
510  }
511  }
512 }
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
535  int options)
536 {
537  HeapTuple result_tuple;
539  int numAttrs;
540  int i;
541 
542  bool need_change = false;
543  bool need_free = false;
544  bool need_delold = false;
545  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  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  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  tupleDesc = rel->rd_att;
579  numAttrs = tupleDesc->natts;
580 
581  Assert(numAttrs <= MaxHeapAttributeNumber);
582  heap_deform_tuple(newtup, tupleDesc, toast_values, toast_isnull);
583  if (oldtup != NULL)
584  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  memset(toast_action, ' ', numAttrs * sizeof(char));
599  memset(toast_oldexternal, 0, numAttrs * sizeof(struct varlena *));
600  memset(toast_free, 0, numAttrs * sizeof(bool));
601  memset(toast_delold, 0, numAttrs * sizeof(bool));
602 
603  for (i = 0; i < numAttrs; i++)
604  {
605  Form_pg_attribute att = TupleDescAttr(tupleDesc, i);
606  struct varlena *old_value;
607  struct varlena *new_value;
608 
609  if (oldtup != NULL)
610  {
611  /*
612  * For UPDATE get the old and new values of this attribute
613  */
614  old_value = (struct varlena *) DatumGetPointer(toast_oldvalues[i]);
615  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  if (att->attlen == -1 && !toast_oldisnull[i] &&
622  VARATT_IS_EXTERNAL_ONDISK(old_value))
623  {
624  if (toast_isnull[i] || !VARATT_IS_EXTERNAL_ONDISK(new_value) ||
625  memcmp((char *) old_value, (char *) new_value,
626  VARSIZE_EXTERNAL(old_value)) != 0)
627  {
628  /*
629  * The old external stored value isn't needed any more
630  * after the update
631  */
632  toast_delold[i] = true;
633  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  toast_action[i] = 'p';
643  continue;
644  }
645  }
646  }
647  else
648  {
649  /*
650  * For INSERT simply get the new value
651  */
652  new_value = (struct varlena *) DatumGetPointer(toast_values[i]);
653  }
654 
655  /*
656  * Handle NULL attributes
657  */
658  if (toast_isnull[i])
659  {
660  toast_action[i] = 'p';
661  has_nulls = true;
662  continue;
663  }
664 
665  /*
666  * Now look at varlena attributes
667  */
668  if (att->attlen == -1)
669  {
670  /*
671  * If the table's attribute says PLAIN always, force it so.
672  */
673  if (att->attstorage == 'p')
674  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  if (VARATT_IS_EXTERNAL(new_value))
685  {
686  toast_oldexternal[i] = new_value;
687  if (att->attstorage == 'p')
688  new_value = heap_tuple_untoast_attr(new_value);
689  else
690  new_value = heap_tuple_fetch_attr(new_value);
691  toast_values[i] = PointerGetDatum(new_value);
692  toast_free[i] = true;
693  need_change = true;
694  need_free = true;
695  }
696 
697  /*
698  * Remember the size of this attribute
699  */
700  toast_sizes[i] = VARSIZE_ANY(new_value);
701  }
702  else
703  {
704  /*
705  * Not a varlena attribute, plain storage always
706  */
707  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  hoff = SizeofHeapTupleHeader;
724  if (has_nulls)
725  hoff += BITMAPLEN(numAttrs);
726  if (newtup->t_data->t_infomask & HEAP_HASOID)
727  hoff += sizeof(Oid);
728  hoff = MAXALIGN(hoff);
729  /* now convert to a limit on the tuple data size */
730  maxDataLen = RelationGetToastTupleTarget(rel, 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  while (heap_compute_data_size(tupleDesc,
737  toast_values, toast_isnull) > maxDataLen)
738  {
739  int biggest_attno = -1;
740  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  for (i = 0; i < numAttrs; i++)
748  {
749  Form_pg_attribute att = TupleDescAttr(tupleDesc, i);
750 
751  if (toast_action[i] != ' ')
752  continue;
753  if (VARATT_IS_EXTERNAL(DatumGetPointer(toast_values[i])))
754  continue; /* can't happen, toast_action would be 'p' */
755  if (VARATT_IS_COMPRESSED(DatumGetPointer(toast_values[i])))
756  continue;
757  if (att->attstorage != 'x' && att->attstorage != 'e')
758  continue;
759  if (toast_sizes[i] > biggest_size)
760  {
761  biggest_attno = i;
762  biggest_size = toast_sizes[i];
763  }
764  }
765 
766  if (biggest_attno < 0)
767  break;
768 
769  /*
770  * Attempt to compress it inline, if it has attstorage 'x'
771  */
772  i = biggest_attno;
773  if (TupleDescAttr(tupleDesc, i)->attstorage == 'x')
774  {
775  old_value = toast_values[i];
776  new_value = toast_compress_datum(old_value);
777 
778  if (DatumGetPointer(new_value) != NULL)
779  {
780  /* successful compression */
781  if (toast_free[i])
782  pfree(DatumGetPointer(old_value));
783  toast_values[i] = new_value;
784  toast_free[i] = true;
785  toast_sizes[i] = VARSIZE(DatumGetPointer(toast_values[i]));
786  need_change = true;
787  need_free = true;
788  }
789  else
790  {
791  /* incompressible, ignore on subsequent compression passes */
792  toast_action[i] = 'x';
793  }
794  }
795  else
796  {
797  /* has attstorage 'e', ignore on subsequent compression passes */
798  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  if (toast_sizes[i] > maxDataLen &&
810  rel->rd_rel->reltoastrelid != InvalidOid)
811  {
812  old_value = toast_values[i];
813  toast_action[i] = 'p';
814  toast_values[i] = toast_save_datum(rel, toast_values[i],
815  toast_oldexternal[i], options);
816  if (toast_free[i])
817  pfree(DatumGetPointer(old_value));
818  toast_free[i] = true;
819  need_change = true;
820  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  while (heap_compute_data_size(tupleDesc,
829  toast_values, toast_isnull) > maxDataLen &&
830  rel->rd_rel->reltoastrelid != InvalidOid)
831  {
832  int biggest_attno = -1;
833  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  for (i = 0; i < numAttrs; i++)
842  {
843  Form_pg_attribute att = TupleDescAttr(tupleDesc, i);
844 
845  if (toast_action[i] == 'p')
846  continue;
847  if (VARATT_IS_EXTERNAL(DatumGetPointer(toast_values[i])))
848  continue; /* can't happen, toast_action would be 'p' */
849  if (att->attstorage != 'x' && att->attstorage != 'e')
850  continue;
851  if (toast_sizes[i] > biggest_size)
852  {
853  biggest_attno = i;
854  biggest_size = toast_sizes[i];
855  }
856  }
857 
858  if (biggest_attno < 0)
859  break;
860 
861  /*
862  * Store this external
863  */
864  i = biggest_attno;
865  old_value = toast_values[i];
866  toast_action[i] = 'p';
867  toast_values[i] = toast_save_datum(rel, toast_values[i],
868  toast_oldexternal[i], options);
869  if (toast_free[i])
870  pfree(DatumGetPointer(old_value));
871  toast_free[i] = true;
872 
873  need_change = true;
874  need_free = true;
875  }
876 
877  /*
878  * Round 3 - this time we take attributes with storage 'm' into
879  * compression
880  */
881  while (heap_compute_data_size(tupleDesc,
882  toast_values, toast_isnull) > maxDataLen)
883  {
884  int biggest_attno = -1;
885  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  for (i = 0; i < numAttrs; i++)
893  {
894  if (toast_action[i] != ' ')
895  continue;
896  if (VARATT_IS_EXTERNAL(DatumGetPointer(toast_values[i])))
897  continue; /* can't happen, toast_action would be 'p' */
898  if (VARATT_IS_COMPRESSED(DatumGetPointer(toast_values[i])))
899  continue;
900  if (TupleDescAttr(tupleDesc, i)->attstorage != 'm')
901  continue;
902  if (toast_sizes[i] > biggest_size)
903  {
904  biggest_attno = i;
905  biggest_size = toast_sizes[i];
906  }
907  }
908 
909  if (biggest_attno < 0)
910  break;
911 
912  /*
913  * Attempt to compress it inline
914  */
915  i = biggest_attno;
916  old_value = toast_values[i];
917  new_value = toast_compress_datum(old_value);
918 
919  if (DatumGetPointer(new_value) != NULL)
920  {
921  /* successful compression */
922  if (toast_free[i])
923  pfree(DatumGetPointer(old_value));
924  toast_values[i] = new_value;
925  toast_free[i] = true;
926  toast_sizes[i] = VARSIZE(DatumGetPointer(toast_values[i]));
927  need_change = true;
928  need_free = true;
929  }
930  else
931  {
932  /* incompressible, ignore on subsequent compression passes */
933  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  maxDataLen = TOAST_TUPLE_TARGET_MAIN - hoff;
943 
944  while (heap_compute_data_size(tupleDesc,
945  toast_values, toast_isnull) > maxDataLen &&
946  rel->rd_rel->reltoastrelid != InvalidOid)
947  {
948  int biggest_attno = -1;
949  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  for (i = 0; i < numAttrs; i++)
958  {
959  if (toast_action[i] == 'p')
960  continue;
961  if (VARATT_IS_EXTERNAL(DatumGetPointer(toast_values[i])))
962  continue; /* can't happen, toast_action would be 'p' */
963  if (TupleDescAttr(tupleDesc, i)->attstorage != 'm')
964  continue;
965  if (toast_sizes[i] > biggest_size)
966  {
967  biggest_attno = i;
968  biggest_size = toast_sizes[i];
969  }
970  }
971 
972  if (biggest_attno < 0)
973  break;
974 
975  /*
976  * Store this external
977  */
978  i = biggest_attno;
979  old_value = toast_values[i];
980  toast_action[i] = 'p';
981  toast_values[i] = toast_save_datum(rel, toast_values[i],
982  toast_oldexternal[i], options);
983  if (toast_free[i])
984  pfree(DatumGetPointer(old_value));
985  toast_free[i] = true;
986 
987  need_change = true;
988  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  if (need_change)
996  {
997  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  new_header_len = SizeofHeapTupleHeader;
1014  if (has_nulls)
1015  new_header_len += BITMAPLEN(numAttrs);
1016  if (olddata->t_infomask & HEAP_HASOID)
1017  new_header_len += sizeof(Oid);
1018  new_header_len = MAXALIGN(new_header_len);
1019  new_data_len = heap_compute_data_size(tupleDesc,
1020  toast_values, toast_isnull);
1021  new_tuple_len = new_header_len + new_data_len;
1022 
1023  /*
1024  * Allocate and zero the space needed, and fill HeapTupleData fields.
1025  */
1026  result_tuple = (HeapTuple) palloc0(HEAPTUPLESIZE + new_tuple_len);
1027  result_tuple->t_len = new_tuple_len;
1028  result_tuple->t_self = newtup->t_self;
1029  result_tuple->t_tableOid = newtup->t_tableOid;
1030  new_data = (HeapTupleHeader) ((char *) result_tuple + HEAPTUPLESIZE);
1031  result_tuple->t_data = new_data;
1032 
1033  /*
1034  * Copy the existing tuple header, but adjust natts and t_hoff.
1035  */
1036  memcpy(new_data, olddata, SizeofHeapTupleHeader);
1037  HeapTupleHeaderSetNatts(new_data, numAttrs);
1038  new_data->t_hoff = new_header_len;
1039  if (olddata->t_infomask & HEAP_HASOID)
1040  HeapTupleHeaderSetOid(new_data, HeapTupleHeaderGetOid(olddata));
1041 
1042  /* Copy over the data, and fill the null bitmap if needed */
1043  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  result_tuple = newtup;
1053 
1054  /*
1055  * Free allocated temp values
1056  */
1057  if (need_free)
1058  for (i = 0; i < numAttrs; i++)
1059  if (toast_free[i])
1060  pfree(DatumGetPointer(toast_values[i]));
1061 
1062  /*
1063  * Delete external values from the old tuple
1064  */
1065  if (need_delold)
1066  for (i = 0; i < numAttrs; i++)
1067  if (toast_delold[i])
1068  toast_delete_datum(rel, toast_oldvalues[i], false);
1069 
1070  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
1086 {
1087  HeapTuple new_tuple;
1088  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  Assert(numAttrs <= MaxTupleAttributeNumber);
1098  heap_deform_tuple(tup, tupleDesc, toast_values, toast_isnull);
1099 
1100  memset(toast_free, 0, numAttrs * sizeof(bool));
1101 
1102  for (i = 0; i < numAttrs; i++)
1103  {
1104  /*
1105  * Look at non-null varlena attributes
1106  */
1107  if (!toast_isnull[i] && TupleDescAttr(tupleDesc, i)->attlen == -1)
1108  {
1109  struct varlena *new_value;
1110 
1111  new_value = (struct varlena *) DatumGetPointer(toast_values[i]);
1112  if (VARATT_IS_EXTERNAL(new_value))
1113  {
1114  new_value = heap_tuple_fetch_attr(new_value);
1115  toast_values[i] = PointerGetDatum(new_value);
1116  toast_free[i] = true;
1117  }
1118  }
1119  }
1120 
1121  /*
1122  * Form the reconfigured tuple.
1123  */
1124  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  if (tupleDesc->tdhasoid)
1132  HeapTupleSetOid(new_tuple, HeapTupleGetOid(tup));
1133 
1134  new_tuple->t_self = tup->t_self;
1135  new_tuple->t_tableOid = tup->t_tableOid;
1136 
1137  new_tuple->t_data->t_choice = tup->t_data->t_choice;
1138  new_tuple->t_data->t_ctid = tup->t_data->t_ctid;
1139  new_tuple->t_data->t_infomask &= ~HEAP_XACT_MASK;
1140  new_tuple->t_data->t_infomask |=
1142  new_tuple->t_data->t_infomask2 &= ~HEAP2_XACT_MASK;
1143  new_tuple->t_data->t_infomask2 |=
1145 
1146  /*
1147  * Free allocated temp values
1148  */
1149  for (i = 0; i < numAttrs; i++)
1150  if (toast_free[i])
1151  pfree(DatumGetPointer(toast_values[i]));
1152 
1153  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
1188  uint32 tup_len,
1190 {
1191  HeapTupleHeader new_data;
1192  int32 new_header_len;
1193  int32 new_data_len;
1194  int32 new_tuple_len;
1195  HeapTupleData tmptup;
1196  int numAttrs = tupleDesc->natts;
1197  int i;
1198  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  tmptup.t_len = tup_len;
1205  ItemPointerSetInvalid(&(tmptup.t_self));
1206  tmptup.t_tableOid = InvalidOid;
1207  tmptup.t_data = tup;
1208 
1209  /*
1210  * Break down the tuple into fields.
1211  */
1212  Assert(numAttrs <= MaxTupleAttributeNumber);
1213  heap_deform_tuple(&tmptup, tupleDesc, toast_values, toast_isnull);
1214 
1215  memset(toast_free, 0, numAttrs * sizeof(bool));
1216 
1217  for (i = 0; i < numAttrs; i++)
1218  {
1219  /*
1220  * Look at non-null varlena attributes
1221  */
1222  if (toast_isnull[i])
1223  has_nulls = true;
1224  else if (TupleDescAttr(tupleDesc, i)->attlen == -1)
1225  {
1226  struct varlena *new_value;
1227 
1228  new_value = (struct varlena *) DatumGetPointer(toast_values[i]);
1229  if (VARATT_IS_EXTERNAL(new_value) ||
1230  VARATT_IS_COMPRESSED(new_value))
1231  {
1232  new_value = heap_tuple_untoast_attr(new_value);
1233  toast_values[i] = PointerGetDatum(new_value);
1234  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  new_header_len = SizeofHeapTupleHeader;
1245  if (has_nulls)
1246  new_header_len += BITMAPLEN(numAttrs);
1247  if (tup->t_infomask & HEAP_HASOID)
1248  new_header_len += sizeof(Oid);
1249  new_header_len = MAXALIGN(new_header_len);
1250  new_data_len = heap_compute_data_size(tupleDesc,
1251  toast_values, toast_isnull);
1252  new_tuple_len = new_header_len + new_data_len;
1253 
1254  new_data = (HeapTupleHeader) palloc0(new_tuple_len);
1255 
1256  /*
1257  * Copy the existing tuple header, but adjust natts and t_hoff.
1258  */
1259  memcpy(new_data, tup, SizeofHeapTupleHeader);
1260  HeapTupleHeaderSetNatts(new_data, numAttrs);
1261  new_data->t_hoff = new_header_len;
1262  if (tup->t_infomask & HEAP_HASOID)
1264 
1265  /* Set the composite-Datum header fields correctly */
1266  HeapTupleHeaderSetDatumLength(new_data, new_tuple_len);
1267  HeapTupleHeaderSetTypeId(new_data, tupleDesc->tdtypeid);
1268  HeapTupleHeaderSetTypMod(new_data, tupleDesc->tdtypmod);
1269 
1270  /* Copy over the data, and fill the null bitmap if needed */
1271  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  for (i = 0; i < numAttrs; i++)
1283  if (toast_free[i])
1284  pfree(DatumGetPointer(toast_values[i]));
1285 
1286  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
1305  Datum *values,
1306  bool *isnull)
1307 {
1308  HeapTuple new_tuple;
1309  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  Assert(numAttrs <= MaxTupleAttributeNumber);
1320  memcpy(new_values, values, numAttrs * sizeof(Datum));
1321 
1322  num_to_free = 0;
1323  for (i = 0; i < numAttrs; i++)
1324  {
1325  /*
1326  * Look at non-null varlena attributes
1327  */
1328  if (!isnull[i] && TupleDescAttr(tupleDesc, i)->attlen == -1)
1329  {
1330  struct varlena *new_value;
1331 
1332  new_value = (struct varlena *) DatumGetPointer(new_values[i]);
1333  if (VARATT_IS_EXTERNAL(new_value))
1334  {
1335  new_value = heap_tuple_fetch_attr(new_value);
1336  new_values[i] = PointerGetDatum(new_value);
1337  freeable_values[num_to_free++] = (Pointer) new_value;
1338  }
1339  }
1340  }
1341 
1342  /*
1343  * Form the reconfigured tuple.
1344  */
1345  new_tuple = heap_form_tuple(tupleDesc, new_values, isnull);
1346 
1347  /*
1348  * Free allocated temp values
1349  */
1350  for (i = 0; i < num_to_free; i++)
1351  pfree(freeable_values[i]);
1352 
1353  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
1372 {
1373  struct varlena *tmp;
1374  int32 valsize = VARSIZE_ANY_EXHDR(DatumGetPointer(value));
1375  int32 len;
1376 
1379 
1380  /*
1381  * No point in wasting a palloc cycle if value size is out of the allowed
1382  * range for compression
1383  */
1384  if (valsize < PGLZ_strategy_default->min_input_size ||
1386  return PointerGetDatum(NULL);
1387 
1388  tmp = (struct varlena *) palloc(PGLZ_MAX_OUTPUT(valsize) +
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  */
1402  valsize,
1405  if (len >= 0 &&
1406  len + TOAST_COMPRESS_HDRSZ < valsize - 2)
1407  {
1408  TOAST_COMPRESS_SET_RAWSIZE(tmp, valsize);
1410  /* successful compression */
1411  return PointerGetDatum(tmp);
1412  }
1413  else
1414  {
1415  /* incompressible data */
1416  pfree(tmp);
1417  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
1430 {
1431  int num_indexes;
1432  int validIndex;
1433  Oid validIndexOid;
1434  Relation *toastidxs;
1435  Relation toastrel;
1436 
1437  /* Open the toast relation */
1438  toastrel = heap_open(toastoid, lock);
1439 
1440  /* Look for the valid index of the toast relation */
1441  validIndex = toast_open_indexes(toastrel,
1442  lock,
1443  &toastidxs,
1444  &num_indexes);
1445  validIndexOid = RelationGetRelid(toastidxs[validIndex]);
1446 
1447  /* Close the toast relation and all its indexes */
1448  toast_close_indexes(toastidxs, num_indexes, lock);
1449  heap_close(toastrel, lock);
1450 
1451  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
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  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  int32 chunk_seq = 0;
1490  char *data_p;
1491  int32 data_todo;
1492  Pointer dval = DatumGetPointer(value);
1493  int num_indexes;
1494  int validIndex;
1495 
1496  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  toastrel = heap_open(rel->rd_rel->reltoastrelid, RowExclusiveLock);
1504  toasttupDesc = toastrel->rd_att;
1505 
1506  /* Open all the toast indexes and look for the valid one */
1507  validIndex = toast_open_indexes(toastrel,
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  if (VARATT_IS_SHORT(dval))
1521  {
1522  data_p = VARDATA_SHORT(dval);
1523  data_todo = VARSIZE_SHORT(dval) - VARHDRSZ_SHORT;
1524  toast_pointer.va_rawsize = data_todo + VARHDRSZ; /* as if not short */
1525  toast_pointer.va_extsize = data_todo;
1526  }
1527  else if (VARATT_IS_COMPRESSED(dval))
1528  {
1529  data_p = VARDATA(dval);
1530  data_todo = VARSIZE(dval) - VARHDRSZ;
1531  /* rawsize in a compressed datum is just the size of the payload */
1532  toast_pointer.va_rawsize = VARRAWSIZE_4B_C(dval) + VARHDRSZ;
1533  toast_pointer.va_extsize = data_todo;
1534  /* Assert that the numbers look like it's compressed */
1535  Assert(VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer));
1536  }
1537  else
1538  {
1539  data_p = VARDATA(dval);
1540  data_todo = VARSIZE(dval) - VARHDRSZ;
1541  toast_pointer.va_rawsize = VARSIZE(dval);
1542  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  if (OidIsValid(rel->rd_toastoid))
1554  toast_pointer.va_toastrelid = rel->rd_toastoid;
1555  else
1556  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  if (!OidIsValid(rel->rd_toastoid))
1571  {
1572  /* normal case: just choose an unused OID */
1573  toast_pointer.va_valueid =
1574  GetNewOidWithIndex(toastrel,
1575  RelationGetRelid(toastidxs[validIndex]),
1576  (AttrNumber) 1);
1577  }
1578  else
1579  {
1580  /* rewrite case: check to see if value was in old toast table */
1581  toast_pointer.va_valueid = InvalidOid;
1582  if (oldexternal != NULL)
1583  {
1584  struct varatt_external old_toast_pointer;
1585 
1586  Assert(VARATT_IS_EXTERNAL_ONDISK(oldexternal));
1587  /* Must copy to access aligned fields */
1588  VARATT_EXTERNAL_GET_POINTER(old_toast_pointer, oldexternal);
1589  if (old_toast_pointer.va_toastrelid == rel->rd_toastoid)
1590  {
1591  /* This value came from the old toast table; reuse its OID */
1592  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  if (toastrel_valueid_exists(toastrel,
1612  toast_pointer.va_valueid))
1613  {
1614  /* Match, so short-circuit the data storage loop below */
1615  data_todo = 0;
1616  }
1617  }
1618  }
1619  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  toast_pointer.va_valueid =
1628  GetNewOidWithIndex(toastrel,
1629  RelationGetRelid(toastidxs[validIndex]),
1630  (AttrNumber) 1);
1631  } while (toastid_valueid_exists(rel->rd_toastoid,
1632  toast_pointer.va_valueid));
1633  }
1634  }
1635 
1636  /*
1637  * Initialize constant parts of the tuple data
1638  */
1639  t_values[0] = ObjectIdGetDatum(toast_pointer.va_valueid);
1640  t_values[2] = PointerGetDatum(&chunk_data);
1641  t_isnull[0] = false;
1642  t_isnull[1] = false;
1643  t_isnull[2] = false;
1644 
1645  /*
1646  * Split up the item into chunks
1647  */
1648  while (data_todo > 0)
1649  {
1650  int i;
1651 
1653 
1654  /*
1655  * Calculate the size of this chunk
1656  */
1657  chunk_size = Min(TOAST_MAX_CHUNK_SIZE, data_todo);
1658 
1659  /*
1660  * Build a tuple and store it
1661  */
1662  t_values[1] = Int32GetDatum(chunk_seq++);
1663  SET_VARSIZE(&chunk_data, chunk_size + VARHDRSZ);
1664  memcpy(VARDATA(&chunk_data), data_p, chunk_size);
1665  toasttup = heap_form_tuple(toasttupDesc, t_values, t_isnull);
1666 
1667  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  for (i = 0; i < num_indexes; i++)
1681  {
1682  /* Only index relations marked as ready can be updated */
1683  if (IndexIsReady(toastidxs[i]->rd_index))
1684  index_insert(toastidxs[i], t_values, t_isnull,
1685  &(toasttup->t_self),
1686  toastrel,
1687  toastidxs[i]->rd_index->indisunique ?
1689  NULL);
1690  }
1691 
1692  /*
1693  * Free memory
1694  */
1695  heap_freetuple(toasttup);
1696 
1697  /*
1698  * Move on to next chunk
1699  */
1700  data_todo -= chunk_size;
1701  data_p += chunk_size;
1702  }
1703 
1704  /*
1705  * Done - close toast relation and its indexes
1706  */
1707  toast_close_indexes(toastidxs, num_indexes, RowExclusiveLock);
1708  heap_close(toastrel, RowExclusiveLock);
1709 
1710  /*
1711  * Create the TOAST pointer value that we'll return
1712  */
1713  result = (struct varlena *) palloc(TOAST_POINTER_SIZE);
1715  memcpy(VARDATA_EXTERNAL(result), &toast_pointer, sizeof(toast_pointer));
1716 
1717  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 toast_delete_datum(Relation rel, Datum value, bool is_speculative)
1729 {
1730  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  if (!VARATT_IS_EXTERNAL_ONDISK(attr))
1742  return;
1743 
1744  /* Must copy to access aligned fields */
1745  VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
1746 
1747  /*
1748  * Open the toast relation and its indexes
1749  */
1750  toastrel = heap_open(toast_pointer.va_toastrelid, RowExclusiveLock);
1751 
1752  /* Fetch valid relation used for process */
1753  validIndex = toast_open_indexes(toastrel,
1755  &toastidxs,
1756  &num_indexes);
1757 
1758  /*
1759  * Setup a scan key to find chunks with matching va_valueid
1760  */
1761  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  init_toast_snapshot(&SnapshotToast);
1772  toastscan = systable_beginscan_ordered(toastrel, toastidxs[validIndex],
1773  &SnapshotToast, 1, &toastkey);
1774  while ((toasttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL)
1775  {
1776  /*
1777  * Have a chunk, delete it
1778  */
1779  if (is_speculative)
1780  heap_abort_speculative(toastrel, toasttup);
1781  else
1782  simple_heap_delete(toastrel, &toasttup->t_self);
1783  }
1784 
1785  /*
1786  * End scan and close relations
1787  */
1788  systable_endscan_ordered(toastscan);
1789  toast_close_indexes(toastidxs, num_indexes, RowExclusiveLock);
1790  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
1802 {
1803  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  validIndex = toast_open_indexes(toastrel,
1814  &toastidxs,
1815  &num_indexes);
1816 
1817  /*
1818  * Setup a scan key to find chunks with matching va_valueid
1819  */
1820  ScanKeyInit(&toastkey,
1821  (AttrNumber) 1,
1822  BTEqualStrategyNumber, F_OIDEQ,
1823  ObjectIdGetDatum(valueid));
1824 
1825  /*
1826  * Is there any such chunk?
1827  */
1828  init_toast_snapshot(&SnapshotToast);
1829  toastscan = systable_beginscan(toastrel,
1830  RelationGetRelid(toastidxs[validIndex]),
1831  true, &SnapshotToast, 1, &toastkey);
1832 
1833  if (systable_getnext(toastscan) != NULL)
1834  result = true;
1835 
1836  systable_endscan(toastscan);
1837 
1838  /* Clean up */
1839  toast_close_indexes(toastidxs, num_indexes, RowExclusiveLock);
1840 
1841  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 toastid_valueid_exists(Oid toastrelid, Oid valueid)
1852 {
1853  bool result;
1854  Relation toastrel;
1855 
1856  toastrel = heap_open(toastrelid, AccessShareLock);
1857 
1858  result = toastrel_valueid_exists(toastrel, valueid);
1859 
1860  heap_close(toastrel, AccessShareLock);
1861 
1862  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 *
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  if (!VARATT_IS_EXTERNAL_ONDISK(attr))
1897  elog(ERROR, "toast_fetch_datum shouldn't be called for non-ondisk datums");
1898 
1899  /* Must copy to access aligned fields */
1900  VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
1901 
1902  ressize = toast_pointer.va_extsize;
1903  numchunks = ((ressize - 1) / TOAST_MAX_CHUNK_SIZE) + 1;
1904 
1905  result = (struct varlena *) palloc(ressize + VARHDRSZ);
1906 
1907  if (VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer))
1908  SET_VARSIZE_COMPRESSED(result, ressize + VARHDRSZ);
1909  else
1910  SET_VARSIZE(result, ressize + VARHDRSZ);
1911 
1912  /*
1913  * Open the toast relation and its indexes
1914  */
1915  toastrel = heap_open(toast_pointer.va_toastrelid, AccessShareLock);
1916  toasttupDesc = toastrel->rd_att;
1917 
1918  /* Look for the valid index of the toast relation */
1919  validIndex = toast_open_indexes(toastrel,
1921  &toastidxs,
1922  &num_indexes);
1923 
1924  /*
1925  * Setup a scan key to fetch from the index by va_valueid
1926  */
1927  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  nextidx = 0;
1940 
1941  init_toast_snapshot(&SnapshotToast);
1942  toastscan = systable_beginscan_ordered(toastrel, toastidxs[validIndex],
1943  &SnapshotToast, 1, &toastkey);
1944  while ((ttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL)
1945  {
1946  /*
1947  * Have a chunk, extract the sequence number and the data
1948  */
1949  residx = DatumGetInt32(fastgetattr(ttup, 2, toasttupDesc, &isnull));
1950  Assert(!isnull);
1951  chunk = DatumGetPointer(fastgetattr(ttup, 3, toasttupDesc, &isnull));
1952  Assert(!isnull);
1953  if (!VARATT_IS_EXTENDED(chunk))
1954  {
1955  chunksize = VARSIZE(chunk) - VARHDRSZ;
1956  chunkdata = VARDATA(chunk);
1957  }
1958  else if (VARATT_IS_SHORT(chunk))
1959  {
1960  /* could happen due to heap_form_tuple doing its thing */
1961  chunksize = VARSIZE_SHORT(chunk) - VARHDRSZ_SHORT;
1962  chunkdata = VARDATA_SHORT(chunk);
1963  }
1964  else
1965  {
1966  /* should never happen */
1967  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  if (residx != nextidx)
1978  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  if (residx < numchunks - 1)
1983  {
1984  if (chunksize != TOAST_MAX_CHUNK_SIZE)
1985  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  else if (residx == numchunks - 1)
1992  {
1993  if ((residx * TOAST_MAX_CHUNK_SIZE + chunksize) != ressize)
1994  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  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  memcpy(VARDATA(result) + residx * TOAST_MAX_CHUNK_SIZE,
2012  chunkdata,
2013  chunksize);
2014 
2015  nextidx++;
2016  }
2017 
2018  /*
2019  * Final checks that we successfully fetched the datum
2020  */
2021  if (nextidx != numchunks)
2022  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  systable_endscan_ordered(toastscan);
2031  toast_close_indexes(toastidxs, num_indexes, AccessShareLock);
2032  heap_close(toastrel, AccessShareLock);
2033 
2034  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 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  if (!VARATT_IS_EXTERNAL_ONDISK(attr))
2076  elog(ERROR, "toast_fetch_datum_slice shouldn't be called for non-ondisk datums");
2077 
2078  /* Must copy to access aligned fields */
2079  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  Assert(!VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer));
2086 
2087  attrsize = toast_pointer.va_extsize;
2088  totalchunks = ((attrsize - 1) / TOAST_MAX_CHUNK_SIZE) + 1;
2089 
2090  if (sliceoffset >= attrsize)
2091  {
2092  sliceoffset = 0;
2093  length = 0;
2094  }
2095 
2096  if (((sliceoffset + length) > attrsize) || length < 0)
2097  length = attrsize - sliceoffset;
2098 
2099  result = (struct varlena *) palloc(length + VARHDRSZ);
2100 
2101  if (VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer))
2102  SET_VARSIZE_COMPRESSED(result, length + VARHDRSZ);
2103  else
2104  SET_VARSIZE(result, length + VARHDRSZ);
2105 
2106  if (length == 0)
2107  return result; /* Can save a lot of work at this point! */
2108 
2109  startchunk = sliceoffset / TOAST_MAX_CHUNK_SIZE;
2110  endchunk = (sliceoffset + length - 1) / TOAST_MAX_CHUNK_SIZE;
2111  numchunks = (endchunk - startchunk) + 1;
2112 
2113  startoffset = sliceoffset % TOAST_MAX_CHUNK_SIZE;
2114  endoffset = (sliceoffset + length - 1) % TOAST_MAX_CHUNK_SIZE;
2115 
2116  /*
2117  * Open the toast relation and its indexes
2118  */
2119  toastrel = heap_open(toast_pointer.va_toastrelid, AccessShareLock);
2120  toasttupDesc = toastrel->rd_att;
2121 
2122  /* Look for the valid index of toast relation */
2123  validIndex = toast_open_indexes(toastrel,
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  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  if (numchunks == 1)
2141  {
2142  ScanKeyInit(&toastkey[1],
2143  (AttrNumber) 2,
2144  BTEqualStrategyNumber, F_INT4EQ,
2145  Int32GetDatum(startchunk));
2146  nscankeys = 2;
2147  }
2148  else
2149  {
2150  ScanKeyInit(&toastkey[1],
2151  (AttrNumber) 2,
2152  BTGreaterEqualStrategyNumber, F_INT4GE,
2153  Int32GetDatum(startchunk));
2154  ScanKeyInit(&toastkey[2],
2155  (AttrNumber) 2,
2156  BTLessEqualStrategyNumber, F_INT4LE,
2157  Int32GetDatum(endchunk));
2158  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  init_toast_snapshot(&SnapshotToast);
2167  nextidx = startchunk;
2168  toastscan = systable_beginscan_ordered(toastrel, toastidxs[validIndex],
2169  &SnapshotToast, nscankeys, toastkey);
2170  while ((ttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL)
2171  {
2172  /*
2173  * Have a chunk, extract the sequence number and the data
2174  */
2175  residx = DatumGetInt32(fastgetattr(ttup, 2, toasttupDesc, &isnull));
2176  Assert(!isnull);
2177  chunk = DatumGetPointer(fastgetattr(ttup, 3, toasttupDesc, &isnull));
2178  Assert(!isnull);
2179  if (!VARATT_IS_EXTENDED(chunk))
2180  {
2181  chunksize = VARSIZE(chunk) - VARHDRSZ;
2182  chunkdata = VARDATA(chunk);
2183  }
2184  else if (VARATT_IS_SHORT(chunk))
2185  {
2186  /* could happen due to heap_form_tuple doing its thing */
2187  chunksize = VARSIZE_SHORT(chunk) - VARHDRSZ_SHORT;
2188  chunkdata = VARDATA_SHORT(chunk);
2189  }
2190  else
2191  {
2192  /* should never happen */
2193  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  if ((residx != nextidx) || (residx > endchunk) || (residx < startchunk))
2204  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  if (residx < totalchunks - 1)
2209  {
2210  if (chunksize != TOAST_MAX_CHUNK_SIZE)
2211  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  else if (residx == totalchunks - 1)
2218  {
2219  if ((residx * TOAST_MAX_CHUNK_SIZE + chunksize) != attrsize)
2220  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  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  chcpystrt = 0;
2238  chcpyend = chunksize - 1;
2239  if (residx == startchunk)
2240  chcpystrt = startoffset;
2241  if (residx == endchunk)
2242  chcpyend = endoffset;
2243 
2244  memcpy(VARDATA(result) +
2245  (residx * TOAST_MAX_CHUNK_SIZE - sliceoffset) + chcpystrt,
2246  chunkdata + chcpystrt,
2247  (chcpyend - chcpystrt) + 1);
2248 
2249  nextidx++;
2250  }
2251 
2252  /*
2253  * Final checks that we successfully fetched the datum
2254  */
2255  if (nextidx != (endchunk + 1))
2256  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  systable_endscan_ordered(toastscan);
2265  toast_close_indexes(toastidxs, num_indexes, AccessShareLock);
2266  heap_close(toastrel, AccessShareLock);
2267 
2268  return result;
2269 }
2270 
2271 /* ----------
2272  * toast_decompress_datum -
2273  *
2274  * Decompress a compressed version of a varlena datum
2275  */
2276 static struct varlena *
2278 {
2279  struct varlena *result;
2280 
2282 
2283  result = (struct varlena *)
2285  SET_VARSIZE(result, TOAST_COMPRESS_RAWSIZE(attr) + VARHDRSZ);
2286 
2288  VARSIZE(attr) - TOAST_COMPRESS_HDRSZ,
2289  VARDATA(result),
2290  TOAST_COMPRESS_RAWSIZE(attr)) < 0)
2291  elog(ERROR, "compressed data is corrupted");
2292 
2293  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
2307  LOCKMODE lock,
2308  Relation **toastidxs,
2309  int *num_indexes)
2310 {
2311  int i = 0;
2312  int res = 0;
2313  bool found = false;
2314  List *indexlist;
2315  ListCell *lc;
2316 
2317  /* Get index list of the toast relation */
2318  indexlist = RelationGetIndexList(toastrel);
2319  Assert(indexlist != NIL);
2320 
2321  *num_indexes = list_length(indexlist);
2322 
2323  /* Open all the index relations */
2324  *toastidxs = (Relation *) palloc(*num_indexes * sizeof(Relation));
2325  foreach(lc, indexlist)
2326  (*toastidxs)[i++] = index_open(lfirst_oid(lc), lock);
2327 
2328  /* Fetch the first valid index in list */
2329  for (i = 0; i < *num_indexes; i++)
2330  {
2331  Relation toastidx = (*toastidxs)[i];
2332 
2333  if (toastidx->rd_index->indisvalid)
2334  {
2335  res = i;
2336  found = true;
2337  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  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  if (!found)
2352  elog(ERROR, "no valid index found for toast relation with Oid %u",
2353  RelationGetRelid(toastrel));
2354 
2355  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 toast_close_indexes(Relation *toastidxs, int num_indexes, LOCKMODE lock)
2366 {
2367  int i;
2368 
2369  /* Close relations and clean up things */
2370  for (i = 0; i < num_indexes; i++)
2371  index_close(toastidxs[i], lock);
2372  pfree(toastidxs);
2373 }
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
2385 {
2386  Snapshot snapshot = GetOldestSnapshot();
2387 
2388  if (snapshot == NULL)
2389  elog(ERROR, "no known snapshots");
2390 
2391  InitToastSnapshot(*toast_snapshot, snapshot->lsn, snapshot->whenTaken);
2392 }
#define NIL
Definition: pg_list.h:69
uint32 CommandId
Definition: c.h:459
Oid GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
Definition: catalog.c:336
int length(const List *list)
Definition: list.c:1309
#define VARATT_IS_EXTERNAL_ONDISK(PTR)
Definition: postgres.h:315
static struct varlena * toast_fetch_datum_slice(struct varlena *attr, int32 sliceoffset, int32 length)
Definition: tuptoaster.c:2045
#define VARATT_IS_COMPRESSED(PTR)
Definition: postgres.h:313
void heap_fill_tuple(TupleDesc tupleDesc, Datum *values, bool *isnull, char *data, Size data_size, uint16 *infomask, bits8 *bit)
Definition: heaptuple.c:145
#define SizeofHeapTupleHeader
Definition: htup_details.h:175
HeapTuple toast_flatten_tuple(HeapTuple tup, TupleDesc tupleDesc)
Definition: tuptoaster.c:1085
union HeapTupleHeaderData::@45 t_choice
HeapTupleData * HeapTuple
Definition: htup.h:70
Oid tdtypeid
Definition: tupdesc.h:80
#define HeapTupleHeaderSetTypeId(tup, typeid)
Definition: htup_details.h:455
struct toast_compress_header toast_compress_header
#define VARDATA_ANY(PTR)
Definition: postgres.h:347
#define VARDATA(PTR)
Definition: postgres.h:303
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:499
void heap_abort_speculative(Relation relation, HeapTuple tuple)
Definition: heapam.c:6107
#define fastgetattr(tup, attnum, tupleDesc, isnull)
Definition: htup_details.h:724
bool tdhasoid
Definition: tupdesc.h:82
static int toast_open_indexes(Relation toastrel, LOCKMODE lock, Relation **toastidxs, int *num_indexes)
Definition: tuptoaster.c:2306
#define VARATT_IS_EXTERNAL_EXPANDED(PTR)
Definition: postgres.h:323
#define TOAST_COMPRESS_RAWSIZE(ptr)
Definition: tuptoaster.c:64
bits8 t_bits[FLEXIBLE_ARRAY_MEMBER]
Definition: htup_details.h:168
#define TOAST_COMPRESS_HDRSZ
Definition: tuptoaster.c:63
#define MaxTupleAttributeNumber
Definition: htup_details.h:33
#define DatumGetInt32(X)
Definition: postgres.h:478
int LOCKMODE
Definition: lockdefs.h:26
#define VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer)
Definition: tuptoaster.h:111
HeapTuple toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup, int options)
Definition: tuptoaster.c:534
#define VARHDRSZ_SHORT
Definition: postgres.h:269
#define VARSIZE(PTR)
Definition: postgres.h:304
#define PointerGetDatum(X)
Definition: postgres.h:562
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
struct varlena * heap_tuple_fetch_attr(struct varlena *attr)
Definition: tuptoaster.c:101
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:90
#define VARHDRSZ
Definition: c.h:493
#define IndexIsReady(indexForm)
Definition: pg_index.h:108
static void init_toast_snapshot(Snapshot toast_snapshot)
Definition: tuptoaster.c:2384
static bool toastrel_valueid_exists(Relation toastrel, Oid valueid)
Definition: tuptoaster.c:1801
#define Min(x, y)
Definition: c.h:802
#define InitToastSnapshot(snapshotdata, l, w)
Definition: tqual.h:118
XLogRecPtr lsn
Definition: snapshot.h:114
static void toast_close_indexes(Relation *toastidxs, int num_indexes, LOCKMODE lock)
Definition: tuptoaster.c:2365
#define TOAST_TUPLE_TARGET_MAIN
Definition: tuptoaster.h:68
#define RELKIND_MATVIEW
Definition: pg_class.h:165
Oid toast_get_valid_index(Oid toastoid, LOCKMODE lock)
Definition: tuptoaster.c:1429
#define AccessShareLock
Definition: lockdefs.h:36
Oid va_toastrelid
Definition: postgres.h:73
int32 va_rawsize
Definition: postgres.h:70
#define HEAP2_XACT_MASK
Definition: htup_details.h:274
#define BITMAPLEN(NATTS)
Definition: htup_details.h:553
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:695
static Datum toast_save_datum(Relation rel, Datum value, struct varlena *oldexternal, int options)
Definition: tuptoaster.c:1468
struct varlena * heap_tuple_untoast_attr(struct varlena *attr)
Definition: tuptoaster.c:172
#define heap_close(r, l)
Definition: heapam.h:97
HeapTuple systable_getnext_ordered(SysScanDesc sysscan, ScanDirection direction)
Definition: genam.c:597
Form_pg_class rd_rel
Definition: rel.h:114
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1373
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:576
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:328
#define VARDATA_EXTERNAL(PTR)
Definition: postgres.h:311
int natts
Definition: tupdesc.h:79
#define HEAP_INSERT_SPECULATIVE
Definition: heapam.h:31
int32 tdtypmod
Definition: tupdesc.h:81
signed int int32
Definition: c.h:284
#define HeapTupleHeaderSetDatumLength(tup, len)
Definition: htup_details.h:447
#define BTLessEqualStrategyNumber
Definition: stratnum.h:30
#define TOAST_COMPRESS_SET_RAWSIZE(ptr, len)
Definition: tuptoaster.c:67
HeapTupleHeader t_data
Definition: htup.h:67
#define HeapTupleSetOid(tuple, oid)
Definition: htup_details.h:703
#define VARATT_IS_EXTERNAL(PTR)
Definition: postgres.h:314
HeapTuple toast_build_flattened_tuple(TupleDesc tupleDesc, Datum *values, bool *isnull)
Definition: tuptoaster.c:1304
Form_pg_index rd_index
Definition: rel.h:159
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:416
void pfree(void *pointer)
Definition: mcxt.c:949
Size toast_raw_datum_size(Datum value)
Definition: tuptoaster.c:353
char * Pointer
Definition: c.h:273
#define VARATT_IS_EXTERNAL_INDIRECT(PTR)
Definition: postgres.h:317
#define ObjectIdGetDatum(X)
Definition: postgres.h:513
#define ERROR
Definition: elog.h:43
#define HeapTupleHeaderSetOid(tup, oid)
Definition: htup_details.h:478
Datum toast_compress_datum(Datum value)
Definition: tuptoaster.c:1371
#define VARATT_IS_SHORT(PTR)
Definition: postgres.h:325
Size toast_datum_size(Datum value)
Definition: tuptoaster.c:409
ItemPointerData t_ctid
Definition: htup_details.h:155
static struct @121 value
ItemPointerData t_self
Definition: htup.h:65
uint32 t_len
Definition: htup.h:64
Datum toast_flatten_tuple_to_datum(HeapTupleHeader tup, uint32 tup_len, TupleDesc tupleDesc)
Definition: tuptoaster.c:1187
#define SET_VARTAG_EXTERNAL(PTR, tag)
Definition: postgres.h:332
Oid rd_toastoid
Definition: rel.h:215
#define RowExclusiveLock
Definition: lockdefs.h:38
#define RelationGetRelationName(relation)
Definition: rel.h:445
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:187
unsigned int uint32
Definition: c.h:296
Oid t_tableOid
Definition: htup.h:66
Size EOH_get_flat_size(ExpandedObjectHeader *eohptr)
Definition: expandeddatum.c:75
Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid, int options, BulkInsertState bistate)
Definition: heapam.c:2413
#define VARSIZE_SHORT(PTR)
Definition: postgres.h:306
int32 pglz_compress(const char *source, int32 slen, char *dest, const PGLZ_Strategy *strategy)
ExpandedObjectHeader * DatumGetEOHP(Datum d)
Definition: expandeddatum.c:29
#define VARRAWSIZE_4B_C(PTR)
Definition: postgres.h:284
#define TOAST_MAX_CHUNK_SIZE
Definition: tuptoaster.h:91
struct varlena * heap_tuple_untoast_attr_slice(struct varlena *attr, int32 sliceoffset, int32 slicelength)
Definition: tuptoaster.c:258
void * palloc0(Size size)
Definition: mcxt.c:877
#define TOAST_POINTER_SIZE
Definition: tuptoaster.h:99
uintptr_t Datum
Definition: postgres.h:372
int32 pglz_decompress(const char *source, int32 slen, char *dest, int32 rawsize)
#define HeapTupleHeaderSetTypMod(tup, typmod)
Definition: htup_details.h:465
void EOH_flatten_into(ExpandedObjectHeader *eohptr, void *result, Size allocated_size)
Definition: expandeddatum.c:81
Snapshot GetOldestSnapshot(void)
Definition: snapmgr.c:411
Relation heap_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1290
void systable_endscan_ordered(SysScanDesc sysscan)
Definition: genam.c:614
TupleDesc rd_att
Definition: rel.h:115
#define VARSIZE_ANY(PTR)
Definition: postgres.h:334
#define InvalidOid
Definition: postgres_ext.h:36
#define PGLZ_MAX_OUTPUT(_dlen)
Definition: pg_lzcompress.h:21
#define MaxHeapAttributeNumber
Definition: htup_details.h:47
#define Assert(condition)
Definition: c.h:670
#define HeapTupleHeaderSetNatts(tup, natts)
Definition: htup_details.h:540
#define TOAST_TUPLE_TARGET
Definition: tuptoaster.h:57
#define VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr)
Definition: tuptoaster.h:121
int32 va_extsize
Definition: postgres.h:71
int32 max_input_size
Definition: pg_lzcompress.h:60
static struct varlena * toast_fetch_datum(struct varlena *attr)
Definition: tuptoaster.c:1874
size_t Size
Definition: c.h:404
#define RelationGetToastTupleTarget(relation, defaulttarg)
Definition: rel.h:293
static int list_length(const List *l)
Definition: pg_list.h:89
void simple_heap_delete(Relation relation, ItemPointer tid)
Definition: heapam.c:3417
static struct varlena * toast_decompress_datum(struct varlena *attr)
Definition: tuptoaster.c:2277
#define MAXALIGN(LEN)
Definition: c.h:623
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4360
Size heap_compute_data_size(TupleDesc tupleDesc, Datum *values, bool *isnull)
Definition: heaptuple.c:85
struct varlena * pointer
Definition: postgres.h:87
static bool toastid_valueid_exists(Oid toastrelid, Oid valueid)
Definition: tuptoaster.c:1851
#define VARATT_IS_EXTENDED(PTR)
Definition: postgres.h:326
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:176
#define DatumGetPointer(X)
Definition: postgres.h:555
void heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *values, bool *isnull)
Definition: heaptuple.c:936
static Datum values[MAXATTR]
Definition: bootstrap.c:164
#define VARDATA_SHORT(PTR)
Definition: postgres.h:307
#define HeapTupleHeaderGetOid(tup)
Definition: htup_details.h:470
static void toast_delete_datum(Relation rel, Datum value, bool is_speculative)
Definition: tuptoaster.c:1728
#define Int32GetDatum(X)
Definition: postgres.h:485
#define ItemPointerSetInvalid(pointer)
Definition: itemptr.h:150
#define VARSIZE_ANY_EXHDR(PTR)
Definition: postgres.h:340
void * palloc(Size size)
Definition: mcxt.c:848
SysScanDesc systable_beginscan_ordered(Relation heapRelation, Relation indexRelation, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:533
#define HEAPTUPLESIZE
Definition: htup.h:72
void list_free(List *list)
Definition: list.c:1133
#define TOAST_COMPRESS_RAWDATA(ptr)
Definition: tuptoaster.c:65
int i
#define HEAP_HASOID
Definition: htup_details.h:183
#define SET_VARSIZE_COMPRESSED(PTR, len)
Definition: postgres.h:330
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
void toast_delete(Relation rel, HeapTuple oldtup, bool is_speculative)
Definition: tuptoaster.c:464
Definition: c.h:487
#define HEAP_XACT_MASK
Definition: htup_details.h:209
TimestampTz whenTaken
Definition: snapshot.h:113
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:98
#define SET_VARSIZE(PTR, len)
Definition: postgres.h:328
const PGLZ_Strategy *const PGLZ_strategy_default
CommandId GetCurrentCommandId(bool used)
Definition: xact.c:680
#define elog
Definition: elog.h:219
#define HeapTupleGetOid(tuple)
Definition: htup_details.h:700
#define RELKIND_RELATION
Definition: pg_class.h:160
Definition: pg_list.h:45
int16 AttrNumber
Definition: attnum.h:21
#define RelationGetRelid(relation)
Definition: rel.h:425
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:151
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define BTGreaterEqualStrategyNumber
Definition: stratnum.h:32
#define lfirst_oid(lc)
Definition: pg_list.h:108
bool index_insert(Relation indexRelation, Datum *values, bool *isnull, ItemPointer heap_t_ctid, Relation heapRelation, IndexUniqueCheck checkUnique, IndexInfo *indexInfo)
Definition: indexam.c:194
#define VARSIZE_EXTERNAL(PTR)
Definition: postgres.h:310