LCOV - code coverage report
Current view: top level - src/backend/access/table - toast_helper.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 113 116 97.4 %
Date: 2024-03-28 11:11:48 Functions: 6 6 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * toast_helper.c
       4             :  *    Helper functions for table AMs implementing compressed or
       5             :  *    out-of-line storage of varlena attributes.
       6             :  *
       7             :  * Copyright (c) 2000-2024, PostgreSQL Global Development Group
       8             :  *
       9             :  * IDENTIFICATION
      10             :  *    src/backend/access/table/toast_helper.c
      11             :  *
      12             :  *-------------------------------------------------------------------------
      13             :  */
      14             : 
      15             : #include "postgres.h"
      16             : 
      17             : #include "access/detoast.h"
      18             : #include "access/toast_helper.h"
      19             : #include "access/toast_internals.h"
      20             : #include "catalog/pg_type_d.h"
      21             : #include "varatt.h"
      22             : 
      23             : 
      24             : /*
      25             :  * Prepare to TOAST a tuple.
      26             :  *
      27             :  * tupleDesc, toast_values, and toast_isnull are required parameters; they
      28             :  * provide the necessary details about the tuple to be toasted.
      29             :  *
      30             :  * toast_oldvalues and toast_oldisnull should be NULL for a newly-inserted
      31             :  * tuple; for an update, they should describe the existing tuple.
      32             :  *
      33             :  * All of these arrays should have a length equal to tupleDesc->natts.
      34             :  *
      35             :  * On return, toast_flags and toast_attr will have been initialized.
      36             :  * toast_flags is just a single uint8, but toast_attr is a caller-provided
      37             :  * array with a length equal to tupleDesc->natts.  The caller need not
      38             :  * perform any initialization of the array before calling this function.
      39             :  */
      40             : void
      41       34158 : toast_tuple_init(ToastTupleContext *ttc)
      42             : {
      43       34158 :     TupleDesc   tupleDesc = ttc->ttc_rel->rd_att;
      44       34158 :     int         numAttrs = tupleDesc->natts;
      45             :     int         i;
      46             : 
      47       34158 :     ttc->ttc_flags = 0;
      48             : 
      49      303336 :     for (i = 0; i < numAttrs; i++)
      50             :     {
      51      269178 :         Form_pg_attribute att = TupleDescAttr(tupleDesc, i);
      52             :         struct varlena *old_value;
      53             :         struct varlena *new_value;
      54             : 
      55      269178 :         ttc->ttc_attr[i].tai_colflags = 0;
      56      269178 :         ttc->ttc_attr[i].tai_oldexternal = NULL;
      57      269178 :         ttc->ttc_attr[i].tai_compression = att->attcompression;
      58             : 
      59      269178 :         if (ttc->ttc_oldvalues != NULL)
      60             :         {
      61             :             /*
      62             :              * For UPDATE get the old and new values of this attribute
      63             :              */
      64             :             old_value =
      65       48196 :                 (struct varlena *) DatumGetPointer(ttc->ttc_oldvalues[i]);
      66             :             new_value =
      67       48196 :                 (struct varlena *) DatumGetPointer(ttc->ttc_values[i]);
      68             : 
      69             :             /*
      70             :              * If the old value is stored on disk, check if it has changed so
      71             :              * we have to delete it later.
      72             :              */
      73       48196 :             if (att->attlen == -1 && !ttc->ttc_oldisnull[i] &&
      74        6064 :                 VARATT_IS_EXTERNAL_ONDISK(old_value))
      75             :             {
      76         564 :                 if (ttc->ttc_isnull[i] ||
      77         548 :                     !VARATT_IS_EXTERNAL_ONDISK(new_value) ||
      78          96 :                     memcmp((char *) old_value, (char *) new_value,
      79          96 :                            VARSIZE_EXTERNAL(old_value)) != 0)
      80             :                 {
      81             :                     /*
      82             :                      * The old external stored value isn't needed any more
      83             :                      * after the update
      84             :                      */
      85         470 :                     ttc->ttc_attr[i].tai_colflags |= TOASTCOL_NEEDS_DELETE_OLD;
      86         470 :                     ttc->ttc_flags |= TOAST_NEEDS_DELETE_OLD;
      87             :                 }
      88             :                 else
      89             :                 {
      90             :                     /*
      91             :                      * This attribute isn't changed by this update so we reuse
      92             :                      * the original reference to the old value in the new
      93             :                      * tuple.
      94             :                      */
      95          94 :                     ttc->ttc_attr[i].tai_colflags |= TOASTCOL_IGNORE;
      96          94 :                     continue;
      97             :                 }
      98             :             }
      99             :         }
     100             :         else
     101             :         {
     102             :             /*
     103             :              * For INSERT simply get the new value
     104             :              */
     105      220982 :             new_value = (struct varlena *) DatumGetPointer(ttc->ttc_values[i]);
     106             :         }
     107             : 
     108             :         /*
     109             :          * Handle NULL attributes
     110             :          */
     111      269084 :         if (ttc->ttc_isnull[i])
     112             :         {
     113       25068 :             ttc->ttc_attr[i].tai_colflags |= TOASTCOL_IGNORE;
     114       25068 :             ttc->ttc_flags |= TOAST_HAS_NULLS;
     115       25068 :             continue;
     116             :         }
     117             : 
     118             :         /*
     119             :          * Now look at varlena attributes
     120             :          */
     121      244016 :         if (att->attlen == -1)
     122             :         {
     123             :             /*
     124             :              * If the table's attribute says PLAIN always, force it so.
     125             :              */
     126       60036 :             if (att->attstorage == TYPSTORAGE_PLAIN)
     127        1350 :                 ttc->ttc_attr[i].tai_colflags |= TOASTCOL_IGNORE;
     128             : 
     129             :             /*
     130             :              * We took care of UPDATE above, so any external value we find
     131             :              * still in the tuple must be someone else's that we cannot reuse
     132             :              * (this includes the case of an out-of-line in-memory datum).
     133             :              * Fetch it back (without decompression, unless we are forcing
     134             :              * PLAIN storage).  If necessary, we'll push it out as a new
     135             :              * external value below.
     136             :              */
     137       60036 :             if (VARATT_IS_EXTERNAL(new_value))
     138             :             {
     139         938 :                 ttc->ttc_attr[i].tai_oldexternal = new_value;
     140         938 :                 if (att->attstorage == TYPSTORAGE_PLAIN)
     141           0 :                     new_value = detoast_attr(new_value);
     142             :                 else
     143         938 :                     new_value = detoast_external_attr(new_value);
     144         938 :                 ttc->ttc_values[i] = PointerGetDatum(new_value);
     145         938 :                 ttc->ttc_attr[i].tai_colflags |= TOASTCOL_NEEDS_FREE;
     146         938 :                 ttc->ttc_flags |= (TOAST_NEEDS_CHANGE | TOAST_NEEDS_FREE);
     147             :             }
     148             : 
     149             :             /*
     150             :              * Remember the size of this attribute
     151             :              */
     152       60036 :             ttc->ttc_attr[i].tai_size = VARSIZE_ANY(new_value);
     153             :         }
     154             :         else
     155             :         {
     156             :             /*
     157             :              * Not a varlena attribute, plain storage always
     158             :              */
     159      183980 :             ttc->ttc_attr[i].tai_colflags |= TOASTCOL_IGNORE;
     160             :         }
     161             :     }
     162       34158 : }
     163             : 
     164             : /*
     165             :  * Find the largest varlena attribute that satisfies certain criteria.
     166             :  *
     167             :  * The relevant column must not be marked TOASTCOL_IGNORE, and if the
     168             :  * for_compression flag is passed as true, it must also not be marked
     169             :  * TOASTCOL_INCOMPRESSIBLE.
     170             :  *
     171             :  * The column must have attstorage EXTERNAL or EXTENDED if check_main is
     172             :  * false, and must have attstorage MAIN if check_main is true.
     173             :  *
     174             :  * The column must have a minimum size of MAXALIGN(TOAST_POINTER_SIZE);
     175             :  * if not, no benefit is to be expected by compressing it.
     176             :  *
     177             :  * The return value is the index of the biggest suitable column, or
     178             :  * -1 if there is none.
     179             :  */
     180             : int
     181       39314 : toast_tuple_find_biggest_attribute(ToastTupleContext *ttc,
     182             :                                    bool for_compression, bool check_main)
     183             : {
     184       39314 :     TupleDesc   tupleDesc = ttc->ttc_rel->rd_att;
     185       39314 :     int         numAttrs = tupleDesc->natts;
     186       39314 :     int         biggest_attno = -1;
     187       39314 :     int32       biggest_size = MAXALIGN(TOAST_POINTER_SIZE);
     188       39314 :     int32       skip_colflags = TOASTCOL_IGNORE;
     189             :     int         i;
     190             : 
     191       39314 :     if (for_compression)
     192       38000 :         skip_colflags |= TOASTCOL_INCOMPRESSIBLE;
     193             : 
     194      441374 :     for (i = 0; i < numAttrs; i++)
     195             :     {
     196      402060 :         Form_pg_attribute att = TupleDescAttr(tupleDesc, i);
     197             : 
     198      402060 :         if ((ttc->ttc_attr[i].tai_colflags & skip_colflags) != 0)
     199      325228 :             continue;
     200       76832 :         if (VARATT_IS_EXTERNAL(DatumGetPointer(ttc->ttc_values[i])))
     201           0 :             continue;           /* can't happen, toast_action would be PLAIN */
     202       76832 :         if (for_compression &&
     203       73008 :             VARATT_IS_COMPRESSED(DatumGetPointer(ttc->ttc_values[i])))
     204        6122 :             continue;
     205       70710 :         if (check_main && att->attstorage != TYPSTORAGE_MAIN)
     206           0 :             continue;
     207       70710 :         if (!check_main && att->attstorage != TYPSTORAGE_EXTENDED &&
     208        5918 :             att->attstorage != TYPSTORAGE_EXTERNAL)
     209         112 :             continue;
     210             : 
     211       70598 :         if (ttc->ttc_attr[i].tai_size > biggest_size)
     212             :         {
     213       47668 :             biggest_attno = i;
     214       47668 :             biggest_size = ttc->ttc_attr[i].tai_size;
     215             :         }
     216             :     }
     217             : 
     218       39314 :     return biggest_attno;
     219             : }
     220             : 
     221             : /*
     222             :  * Try compression for an attribute.
     223             :  *
     224             :  * If we find that the attribute is not compressible, mark it so.
     225             :  */
     226             : void
     227       30898 : toast_tuple_try_compression(ToastTupleContext *ttc, int attribute)
     228             : {
     229       30898 :     Datum      *value = &ttc->ttc_values[attribute];
     230             :     Datum       new_value;
     231       30898 :     ToastAttrInfo *attr = &ttc->ttc_attr[attribute];
     232             : 
     233       30898 :     new_value = toast_compress_datum(*value, attr->tai_compression);
     234             : 
     235       30898 :     if (DatumGetPointer(new_value) != NULL)
     236             :     {
     237             :         /* successful compression */
     238       29564 :         if ((attr->tai_colflags & TOASTCOL_NEEDS_FREE) != 0)
     239         122 :             pfree(DatumGetPointer(*value));
     240       29564 :         *value = new_value;
     241       29564 :         attr->tai_colflags |= TOASTCOL_NEEDS_FREE;
     242       29564 :         attr->tai_size = VARSIZE(DatumGetPointer(*value));
     243       29564 :         ttc->ttc_flags |= (TOAST_NEEDS_CHANGE | TOAST_NEEDS_FREE);
     244             :     }
     245             :     else
     246             :     {
     247             :         /* incompressible, ignore on subsequent compression passes */
     248        1334 :         attr->tai_colflags |= TOASTCOL_INCOMPRESSIBLE;
     249             :     }
     250       30898 : }
     251             : 
     252             : /*
     253             :  * Move an attribute to external storage.
     254             :  */
     255             : void
     256       14152 : toast_tuple_externalize(ToastTupleContext *ttc, int attribute, int options)
     257             : {
     258       14152 :     Datum      *value = &ttc->ttc_values[attribute];
     259       14152 :     Datum       old_value = *value;
     260       14152 :     ToastAttrInfo *attr = &ttc->ttc_attr[attribute];
     261             : 
     262       14152 :     attr->tai_colflags |= TOASTCOL_IGNORE;
     263       14152 :     *value = toast_save_datum(ttc->ttc_rel, old_value, attr->tai_oldexternal,
     264             :                               options);
     265       14152 :     if ((attr->tai_colflags & TOASTCOL_NEEDS_FREE) != 0)
     266        8314 :         pfree(DatumGetPointer(old_value));
     267       14152 :     attr->tai_colflags |= TOASTCOL_NEEDS_FREE;
     268       14152 :     ttc->ttc_flags |= (TOAST_NEEDS_CHANGE | TOAST_NEEDS_FREE);
     269       14152 : }
     270             : 
     271             : /*
     272             :  * Perform appropriate cleanup after one tuple has been subjected to TOAST.
     273             :  */
     274             : void
     275       34158 : toast_tuple_cleanup(ToastTupleContext *ttc)
     276             : {
     277       34158 :     TupleDesc   tupleDesc = ttc->ttc_rel->rd_att;
     278       34158 :     int         numAttrs = tupleDesc->natts;
     279             : 
     280             :     /*
     281             :      * Free allocated temp values
     282             :      */
     283       34158 :     if ((ttc->ttc_flags & TOAST_NEEDS_FREE) != 0)
     284             :     {
     285             :         int         i;
     286             : 
     287      301640 :         for (i = 0; i < numAttrs; i++)
     288             :         {
     289      267624 :             ToastAttrInfo *attr = &ttc->ttc_attr[i];
     290             : 
     291      267624 :             if ((attr->tai_colflags & TOASTCOL_NEEDS_FREE) != 0)
     292       36218 :                 pfree(DatumGetPointer(ttc->ttc_values[i]));
     293             :         }
     294             :     }
     295             : 
     296             :     /*
     297             :      * Delete external values from the old tuple
     298             :      */
     299       34158 :     if ((ttc->ttc_flags & TOAST_NEEDS_DELETE_OLD) != 0)
     300             :     {
     301             :         int         i;
     302             : 
     303       10054 :         for (i = 0; i < numAttrs; i++)
     304             :         {
     305        9646 :             ToastAttrInfo *attr = &ttc->ttc_attr[i];
     306             : 
     307        9646 :             if ((attr->tai_colflags & TOASTCOL_NEEDS_DELETE_OLD) != 0)
     308         470 :                 toast_delete_datum(ttc->ttc_rel, ttc->ttc_oldvalues[i], false);
     309             :         }
     310             :     }
     311       34158 : }
     312             : 
     313             : /*
     314             :  * Check for external stored attributes and delete them from the secondary
     315             :  * relation.
     316             :  */
     317             : void
     318         472 : toast_delete_external(Relation rel, const Datum *values, const bool *isnull,
     319             :                       bool is_speculative)
     320             : {
     321         472 :     TupleDesc   tupleDesc = rel->rd_att;
     322         472 :     int         numAttrs = tupleDesc->natts;
     323             :     int         i;
     324             : 
     325        3362 :     for (i = 0; i < numAttrs; i++)
     326             :     {
     327        2890 :         if (TupleDescAttr(tupleDesc, i)->attlen == -1)
     328             :         {
     329        1042 :             Datum       value = values[i];
     330             : 
     331        1042 :             if (isnull[i])
     332         318 :                 continue;
     333         724 :             else if (VARATT_IS_EXTERNAL_ONDISK(value))
     334         492 :                 toast_delete_datum(rel, value, is_speculative);
     335             :         }
     336             :     }
     337         472 : }

Generated by: LCOV version 1.14