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

Generated by: LCOV version 1.14