LCOV - code coverage report
Current view: top level - src/backend/access/table - toast_helper.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13beta1 Lines: 114 117 97.4 %
Date: 2020-06-05 18:07:03 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-2020, 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             : 
      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 an 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       61102 : toast_tuple_init(ToastTupleContext *ttc)
      42             : {
      43       61102 :     TupleDesc   tupleDesc = ttc->ttc_rel->rd_att;
      44       61102 :     int         numAttrs = tupleDesc->natts;
      45             :     int         i;
      46             : 
      47       61102 :     ttc->ttc_flags = 0;
      48             : 
      49      682310 :     for (i = 0; i < numAttrs; i++)
      50             :     {
      51      621208 :         Form_pg_attribute att = TupleDescAttr(tupleDesc, i);
      52             :         struct varlena *old_value;
      53             :         struct varlena *new_value;
      54             : 
      55      621208 :         ttc->ttc_attr[i].tai_colflags = 0;
      56      621208 :         ttc->ttc_attr[i].tai_oldexternal = NULL;
      57             : 
      58      621208 :         if (ttc->ttc_oldvalues != NULL)
      59             :         {
      60             :             /*
      61             :              * For UPDATE get the old and new values of this attribute
      62             :              */
      63       38548 :             old_value =
      64       38548 :                 (struct varlena *) DatumGetPointer(ttc->ttc_oldvalues[i]);
      65       38548 :             new_value =
      66       38548 :                 (struct varlena *) DatumGetPointer(ttc->ttc_values[i]);
      67             : 
      68             :             /*
      69             :              * If the old value is stored on disk, check if it has changed so
      70             :              * we have to delete it later.
      71             :              */
      72       38548 :             if (att->attlen == -1 && !ttc->ttc_oldisnull[i] &&
      73        5242 :                 VARATT_IS_EXTERNAL_ONDISK(old_value))
      74             :             {
      75         554 :                 if (ttc->ttc_isnull[i] ||
      76         500 :                     !VARATT_IS_EXTERNAL_ONDISK(new_value) ||
      77          70 :                     memcmp((char *) old_value, (char *) new_value,
      78          70 :                            VARSIZE_EXTERNAL(old_value)) != 0)
      79             :                 {
      80             :                     /*
      81             :                      * The old external stored value isn't needed any more
      82             :                      * after the update
      83             :                      */
      84         486 :                     ttc->ttc_attr[i].tai_colflags |= TOASTCOL_NEEDS_DELETE_OLD;
      85         486 :                     ttc->ttc_flags |= TOAST_NEEDS_DELETE_OLD;
      86             :                 }
      87             :                 else
      88             :                 {
      89             :                     /*
      90             :                      * This attribute isn't changed by this update so we reuse
      91             :                      * the original reference to the old value in the new
      92             :                      * tuple.
      93             :                      */
      94          68 :                     ttc->ttc_attr[i].tai_colflags |= TOASTCOL_IGNORE;
      95          68 :                     continue;
      96             :                 }
      97             :             }
      98             :         }
      99             :         else
     100             :         {
     101             :             /*
     102             :              * For INSERT simply get the new value
     103             :              */
     104      582660 :             new_value = (struct varlena *) DatumGetPointer(ttc->ttc_values[i]);
     105             :         }
     106             : 
     107             :         /*
     108             :          * Handle NULL attributes
     109             :          */
     110      621140 :         if (ttc->ttc_isnull[i])
     111             :         {
     112       42852 :             ttc->ttc_attr[i].tai_colflags |= TOASTCOL_IGNORE;
     113       42852 :             ttc->ttc_flags |= TOAST_HAS_NULLS;
     114       42852 :             continue;
     115             :         }
     116             : 
     117             :         /*
     118             :          * Now look at varlena attributes
     119             :          */
     120      578288 :         if (att->attlen == -1)
     121             :         {
     122             :             /*
     123             :              * If the table's attribute says PLAIN always, force it so.
     124             :              */
     125      130482 :             if (att->attstorage == TYPSTORAGE_PLAIN)
     126          22 :                 ttc->ttc_attr[i].tai_colflags |= TOASTCOL_IGNORE;
     127             : 
     128             :             /*
     129             :              * We took care of UPDATE above, so any external value we find
     130             :              * still in the tuple must be someone else's that we cannot reuse
     131             :              * (this includes the case of an out-of-line in-memory datum).
     132             :              * Fetch it back (without decompression, unless we are forcing
     133             :              * PLAIN storage).  If necessary, we'll push it out as a new
     134             :              * external value below.
     135             :              */
     136      130482 :             if (VARATT_IS_EXTERNAL(new_value))
     137             :             {
     138         610 :                 ttc->ttc_attr[i].tai_oldexternal = new_value;
     139         610 :                 if (att->attstorage == TYPSTORAGE_PLAIN)
     140           0 :                     new_value = detoast_attr(new_value);
     141             :                 else
     142         610 :                     new_value = detoast_external_attr(new_value);
     143         610 :                 ttc->ttc_values[i] = PointerGetDatum(new_value);
     144         610 :                 ttc->ttc_attr[i].tai_colflags |= TOASTCOL_NEEDS_FREE;
     145         610 :                 ttc->ttc_flags |= (TOAST_NEEDS_CHANGE | TOAST_NEEDS_FREE);
     146             :             }
     147             : 
     148             :             /*
     149             :              * Remember the size of this attribute
     150             :              */
     151      130482 :             ttc->ttc_attr[i].tai_size = VARSIZE_ANY(new_value);
     152             :         }
     153             :         else
     154             :         {
     155             :             /*
     156             :              * Not a varlena attribute, plain storage always
     157             :              */
     158      447806 :             ttc->ttc_attr[i].tai_colflags |= TOASTCOL_IGNORE;
     159             :         }
     160             :     }
     161       61102 : }
     162             : 
     163             : /*
     164             :  * Find the largest varlena attribute that satisfies certain criteria.
     165             :  *
     166             :  * The relevant column must not be marked TOASTCOL_IGNORE, and if the
     167             :  * for_compression flag is passed as true, it must also not be marked
     168             :  * TOASTCOL_INCOMPRESSIBLE.
     169             :  *
     170             :  * The column must have attstorage EXTERNAL or EXTENDED if check_main is
     171             :  * false, and must have attstorage MAIN if check_main is true.
     172             :  *
     173             :  * The column must have a minimum size of MAXALIGN(TOAST_POINTER_SIZE);
     174             :  * if not, no benefit is to be expected by compressing it.
     175             :  *
     176             :  * The return value is the index of the biggest suitable column, or
     177             :  * -1 if there is none.
     178             :  */
     179             : int
     180       76854 : toast_tuple_find_biggest_attribute(ToastTupleContext *ttc,
     181             :                                    bool for_compression, bool check_main)
     182             : {
     183       76854 :     TupleDesc   tupleDesc = ttc->ttc_rel->rd_att;
     184       76854 :     int         numAttrs = tupleDesc->natts;
     185       76854 :     int         biggest_attno = -1;
     186       76854 :     int32       biggest_size = MAXALIGN(TOAST_POINTER_SIZE);
     187       76854 :     int32       skip_colflags = TOASTCOL_IGNORE;
     188             :     int         i;
     189             : 
     190       76854 :     if (for_compression)
     191       73100 :         skip_colflags |= TOASTCOL_INCOMPRESSIBLE;
     192             : 
     193     1098478 :     for (i = 0; i < numAttrs; i++)
     194             :     {
     195     1021624 :         Form_pg_attribute att = TupleDescAttr(tupleDesc, i);
     196             : 
     197     1021624 :         if ((ttc->ttc_attr[i].tai_colflags & skip_colflags) != 0)
     198      836466 :             continue;
     199      185158 :         if (VARATT_IS_EXTERNAL(DatumGetPointer(ttc->ttc_values[i])))
     200           0 :             continue;           /* can't happen, toast_action would be PLAIN */
     201      185158 :         if (for_compression &&
     202      174118 :             VARATT_IS_COMPRESSED(DatumGetPointer(ttc->ttc_values[i])))
     203       18034 :             continue;
     204      167124 :         if (check_main && att->attstorage != TYPSTORAGE_MAIN)
     205           0 :             continue;
     206      167124 :         if (!check_main && att->attstorage != TYPSTORAGE_EXTENDED &&
     207         572 :             att->attstorage != TYPSTORAGE_EXTERNAL)
     208          20 :             continue;
     209             : 
     210      167104 :         if (ttc->ttc_attr[i].tai_size > biggest_size)
     211             :         {
     212       96358 :             biggest_attno = i;
     213       96358 :             biggest_size = ttc->ttc_attr[i].tai_size;
     214             :         }
     215             :     }
     216             : 
     217       76854 :     return biggest_attno;
     218             : }
     219             : 
     220             : /*
     221             :  * Try compression for an attribute.
     222             :  *
     223             :  * If we find that the attribute is not compressible, mark it so.
     224             :  */
     225             : void
     226       68818 : toast_tuple_try_compression(ToastTupleContext *ttc, int attribute)
     227             : {
     228       68818 :     Datum      *value = &ttc->ttc_values[attribute];
     229       68818 :     Datum       new_value = toast_compress_datum(*value);
     230       68818 :     ToastAttrInfo *attr = &ttc->ttc_attr[attribute];
     231             : 
     232       68818 :     if (DatumGetPointer(new_value) != NULL)
     233             :     {
     234             :         /* successful compression */
     235       66190 :         if ((attr->tai_colflags & TOASTCOL_NEEDS_FREE) != 0)
     236          22 :             pfree(DatumGetPointer(*value));
     237       66190 :         *value = new_value;
     238       66190 :         attr->tai_colflags |= TOASTCOL_NEEDS_FREE;
     239       66190 :         attr->tai_size = VARSIZE(DatumGetPointer(*value));
     240       66190 :         ttc->ttc_flags |= (TOAST_NEEDS_CHANGE | TOAST_NEEDS_FREE);
     241             :     }
     242             :     else
     243             :     {
     244             :         /* incompressible, ignore on subsequent compression passes */
     245        2628 :         attr->tai_colflags |= TOASTCOL_INCOMPRESSIBLE;
     246             :     }
     247       68818 : }
     248             : 
     249             : /*
     250             :  * Move an attribute to external storage.
     251             :  */
     252             : void
     253       32256 : toast_tuple_externalize(ToastTupleContext *ttc, int attribute, int options)
     254             : {
     255       32256 :     Datum      *value = &ttc->ttc_values[attribute];
     256       32256 :     Datum       old_value = *value;
     257       32256 :     ToastAttrInfo *attr = &ttc->ttc_attr[attribute];
     258             : 
     259       32256 :     attr->tai_colflags |= TOASTCOL_IGNORE;
     260       32256 :     *value = toast_save_datum(ttc->ttc_rel, old_value, attr->tai_oldexternal,
     261             :                               options);
     262       32256 :     if ((attr->tai_colflags & TOASTCOL_NEEDS_FREE) != 0)
     263       31910 :         pfree(DatumGetPointer(old_value));
     264       32256 :     attr->tai_colflags |= TOASTCOL_NEEDS_FREE;
     265       32256 :     ttc->ttc_flags |= (TOAST_NEEDS_CHANGE | TOAST_NEEDS_FREE);
     266       32256 : }
     267             : 
     268             : /*
     269             :  * Perform appropriate cleanup after one tuple has been subjected to TOAST.
     270             :  */
     271             : void
     272       61102 : toast_tuple_cleanup(ToastTupleContext *ttc)
     273             : {
     274       61102 :     TupleDesc   tupleDesc = ttc->ttc_rel->rd_att;
     275       61102 :     int         numAttrs = tupleDesc->natts;
     276             : 
     277             :     /*
     278             :      * Free allocated temp values
     279             :      */
     280       61102 :     if ((ttc->ttc_flags & TOAST_NEEDS_FREE) != 0)
     281             :     {
     282             :         int         i;
     283             : 
     284      678078 :         for (i = 0; i < numAttrs; i++)
     285             :         {
     286      617152 :             ToastAttrInfo *attr = &ttc->ttc_attr[i];
     287             : 
     288      617152 :             if ((attr->tai_colflags & TOASTCOL_NEEDS_FREE) != 0)
     289       67124 :                 pfree(DatumGetPointer(ttc->ttc_values[i]));
     290             :         }
     291             :     }
     292             : 
     293             :     /*
     294             :      * Delete external values from the old tuple
     295             :      */
     296       61102 :     if ((ttc->ttc_flags & TOAST_NEEDS_DELETE_OLD) != 0)
     297             :     {
     298             :         int         i;
     299             : 
     300       11830 :         for (i = 0; i < numAttrs; i++)
     301             :         {
     302       11412 :             ToastAttrInfo *attr = &ttc->ttc_attr[i];
     303             : 
     304       11412 :             if ((attr->tai_colflags & TOASTCOL_NEEDS_DELETE_OLD) != 0)
     305         486 :                 toast_delete_datum(ttc->ttc_rel, ttc->ttc_oldvalues[i], false);
     306             :         }
     307             :     }
     308       61102 : }
     309             : 
     310             : /*
     311             :  * Check for external stored attributes and delete them from the secondary
     312             :  * relation.
     313             :  */
     314             : void
     315         242 : toast_delete_external(Relation rel, Datum *values, bool *isnull,
     316             :                       bool is_speculative)
     317             : {
     318         242 :     TupleDesc   tupleDesc = rel->rd_att;
     319         242 :     int         numAttrs = tupleDesc->natts;
     320             :     int         i;
     321             : 
     322        1008 :     for (i = 0; i < numAttrs; i++)
     323             :     {
     324         766 :         if (TupleDescAttr(tupleDesc, i)->attlen == -1)
     325             :         {
     326         314 :             Datum       value = values[i];
     327             : 
     328         314 :             if (isnull[i])
     329          24 :                 continue;
     330         290 :             else if (VARATT_IS_EXTERNAL_ONDISK(PointerGetDatum(value)))
     331         250 :                 toast_delete_datum(rel, value, is_speculative);
     332             :         }
     333             :     }
     334         242 : }

Generated by: LCOV version 1.13