LCOV - code coverage report
Current view: top level - src/backend/access/common - toast_compression.c (source / functions) Hit Total Coverage
Test: PostgreSQL 16devel Lines: 80 90 88.9 %
Date: 2022-08-17 05:10:35 Functions: 9 9 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * toast_compression.c
       4             :  *    Functions for toast compression.
       5             :  *
       6             :  * Copyright (c) 2021-2022, PostgreSQL Global Development Group
       7             :  *
       8             :  *
       9             :  * IDENTIFICATION
      10             :  *    src/backend/access/common/toast_compression.c
      11             :  *
      12             :  *-------------------------------------------------------------------------
      13             :  */
      14             : #include "postgres.h"
      15             : 
      16             : #ifdef USE_LZ4
      17             : #include <lz4.h>
      18             : #endif
      19             : 
      20             : #include "access/detoast.h"
      21             : #include "access/toast_compression.h"
      22             : #include "common/pg_lzcompress.h"
      23             : #include "fmgr.h"
      24             : #include "utils/builtins.h"
      25             : 
      26             : /* GUC */
      27             : int         default_toast_compression = TOAST_PGLZ_COMPRESSION;
      28             : 
      29             : #define NO_LZ4_SUPPORT() \
      30             :     ereport(ERROR, \
      31             :             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), \
      32             :              errmsg("compression method lz4 not supported"), \
      33             :              errdetail("This functionality requires the server to be built with lz4 support.")))
      34             : 
      35             : /*
      36             :  * Compress a varlena using PGLZ.
      37             :  *
      38             :  * Returns the compressed varlena, or NULL if compression fails.
      39             :  */
      40             : struct varlena *
      41      127718 : pglz_compress_datum(const struct varlena *value)
      42             : {
      43             :     int32       valsize,
      44             :                 len;
      45      127718 :     struct varlena *tmp = NULL;
      46             : 
      47      127718 :     valsize = VARSIZE_ANY_EXHDR(DatumGetPointer(value));
      48             : 
      49             :     /*
      50             :      * No point in wasting a palloc cycle if value size is outside the allowed
      51             :      * range for compression.
      52             :      */
      53      127718 :     if (valsize < PGLZ_strategy_default->min_input_size ||
      54      124692 :         valsize > PGLZ_strategy_default->max_input_size)
      55        3026 :         return NULL;
      56             : 
      57             :     /*
      58             :      * Figure out the maximum possible size of the pglz output, add the bytes
      59             :      * that will be needed for varlena overhead, and allocate that amount.
      60             :      */
      61      124692 :     tmp = (struct varlena *) palloc(PGLZ_MAX_OUTPUT(valsize) +
      62             :                                     VARHDRSZ_COMPRESSED);
      63             : 
      64      124692 :     len = pglz_compress(VARDATA_ANY(value),
      65             :                         valsize,
      66             :                         (char *) tmp + VARHDRSZ_COMPRESSED,
      67             :                         NULL);
      68      124692 :     if (len < 0)
      69             :     {
      70        3900 :         pfree(tmp);
      71        3900 :         return NULL;
      72             :     }
      73             : 
      74      120792 :     SET_VARSIZE_COMPRESSED(tmp, len + VARHDRSZ_COMPRESSED);
      75             : 
      76      120792 :     return tmp;
      77             : }
      78             : 
      79             : /*
      80             :  * Decompress a varlena that was compressed using PGLZ.
      81             :  */
      82             : struct varlena *
      83      166286 : pglz_decompress_datum(const struct varlena *value)
      84             : {
      85             :     struct varlena *result;
      86             :     int32       rawsize;
      87             : 
      88             :     /* allocate memory for the uncompressed data */
      89      166286 :     result = (struct varlena *) palloc(VARDATA_COMPRESSED_GET_EXTSIZE(value) + VARHDRSZ);
      90             : 
      91             :     /* decompress the data */
      92      166286 :     rawsize = pglz_decompress((char *) value + VARHDRSZ_COMPRESSED,
      93      166286 :                               VARSIZE(value) - VARHDRSZ_COMPRESSED,
      94      166286 :                               VARDATA(result),
      95      166286 :                               VARDATA_COMPRESSED_GET_EXTSIZE(value), true);
      96      166286 :     if (rawsize < 0)
      97           0 :         ereport(ERROR,
      98             :                 (errcode(ERRCODE_DATA_CORRUPTED),
      99             :                  errmsg_internal("compressed pglz data is corrupt")));
     100             : 
     101      166286 :     SET_VARSIZE(result, rawsize + VARHDRSZ);
     102             : 
     103      166286 :     return result;
     104             : }
     105             : 
     106             : /*
     107             :  * Decompress part of a varlena that was compressed using PGLZ.
     108             :  */
     109             : struct varlena *
     110          66 : pglz_decompress_datum_slice(const struct varlena *value,
     111             :                             int32 slicelength)
     112             : {
     113             :     struct varlena *result;
     114             :     int32       rawsize;
     115             : 
     116             :     /* allocate memory for the uncompressed data */
     117          66 :     result = (struct varlena *) palloc(slicelength + VARHDRSZ);
     118             : 
     119             :     /* decompress the data */
     120          66 :     rawsize = pglz_decompress((char *) value + VARHDRSZ_COMPRESSED,
     121          66 :                               VARSIZE(value) - VARHDRSZ_COMPRESSED,
     122          66 :                               VARDATA(result),
     123             :                               slicelength, false);
     124          66 :     if (rawsize < 0)
     125           0 :         ereport(ERROR,
     126             :                 (errcode(ERRCODE_DATA_CORRUPTED),
     127             :                  errmsg_internal("compressed pglz data is corrupt")));
     128             : 
     129          66 :     SET_VARSIZE(result, rawsize + VARHDRSZ);
     130             : 
     131          66 :     return result;
     132             : }
     133             : 
     134             : /*
     135             :  * Compress a varlena using LZ4.
     136             :  *
     137             :  * Returns the compressed varlena, or NULL if compression fails.
     138             :  */
     139             : struct varlena *
     140          36 : lz4_compress_datum(const struct varlena *value)
     141             : {
     142             : #ifndef USE_LZ4
     143             :     NO_LZ4_SUPPORT();
     144             :     return NULL;                /* keep compiler quiet */
     145             : #else
     146             :     int32       valsize;
     147             :     int32       len;
     148             :     int32       max_size;
     149          36 :     struct varlena *tmp = NULL;
     150             : 
     151          36 :     valsize = VARSIZE_ANY_EXHDR(value);
     152             : 
     153             :     /*
     154             :      * Figure out the maximum possible size of the LZ4 output, add the bytes
     155             :      * that will be needed for varlena overhead, and allocate that amount.
     156             :      */
     157          36 :     max_size = LZ4_compressBound(valsize);
     158          36 :     tmp = (struct varlena *) palloc(max_size + VARHDRSZ_COMPRESSED);
     159             : 
     160          36 :     len = LZ4_compress_default(VARDATA_ANY(value),
     161             :                                (char *) tmp + VARHDRSZ_COMPRESSED,
     162             :                                valsize, max_size);
     163          36 :     if (len <= 0)
     164           0 :         elog(ERROR, "lz4 compression failed");
     165             : 
     166             :     /* data is incompressible so just free the memory and return NULL */
     167          36 :     if (len > valsize)
     168             :     {
     169           0 :         pfree(tmp);
     170           0 :         return NULL;
     171             :     }
     172             : 
     173          36 :     SET_VARSIZE_COMPRESSED(tmp, len + VARHDRSZ_COMPRESSED);
     174             : 
     175          36 :     return tmp;
     176             : #endif
     177             : }
     178             : 
     179             : /*
     180             :  * Decompress a varlena that was compressed using LZ4.
     181             :  */
     182             : struct varlena *
     183          98 : lz4_decompress_datum(const struct varlena *value)
     184             : {
     185             : #ifndef USE_LZ4
     186             :     NO_LZ4_SUPPORT();
     187             :     return NULL;                /* keep compiler quiet */
     188             : #else
     189             :     int32       rawsize;
     190             :     struct varlena *result;
     191             : 
     192             :     /* allocate memory for the uncompressed data */
     193          98 :     result = (struct varlena *) palloc(VARDATA_COMPRESSED_GET_EXTSIZE(value) + VARHDRSZ);
     194             : 
     195             :     /* decompress the data */
     196          98 :     rawsize = LZ4_decompress_safe((char *) value + VARHDRSZ_COMPRESSED,
     197          98 :                                   VARDATA(result),
     198          98 :                                   VARSIZE(value) - VARHDRSZ_COMPRESSED,
     199          98 :                                   VARDATA_COMPRESSED_GET_EXTSIZE(value));
     200          98 :     if (rawsize < 0)
     201           0 :         ereport(ERROR,
     202             :                 (errcode(ERRCODE_DATA_CORRUPTED),
     203             :                  errmsg_internal("compressed lz4 data is corrupt")));
     204             : 
     205             : 
     206          98 :     SET_VARSIZE(result, rawsize + VARHDRSZ);
     207             : 
     208          98 :     return result;
     209             : #endif
     210             : }
     211             : 
     212             : /*
     213             :  * Decompress part of a varlena that was compressed using LZ4.
     214             :  */
     215             : struct varlena *
     216          18 : lz4_decompress_datum_slice(const struct varlena *value, int32 slicelength)
     217             : {
     218             : #ifndef USE_LZ4
     219             :     NO_LZ4_SUPPORT();
     220             :     return NULL;                /* keep compiler quiet */
     221             : #else
     222             :     int32       rawsize;
     223             :     struct varlena *result;
     224             : 
     225             :     /* slice decompression not supported prior to 1.8.3 */
     226          18 :     if (LZ4_versionNumber() < 10803)
     227           0 :         return lz4_decompress_datum(value);
     228             : 
     229             :     /* allocate memory for the uncompressed data */
     230          18 :     result = (struct varlena *) palloc(slicelength + VARHDRSZ);
     231             : 
     232             :     /* decompress the data */
     233          18 :     rawsize = LZ4_decompress_safe_partial((char *) value + VARHDRSZ_COMPRESSED,
     234          18 :                                           VARDATA(result),
     235          18 :                                           VARSIZE(value) - VARHDRSZ_COMPRESSED,
     236             :                                           slicelength,
     237             :                                           slicelength);
     238          18 :     if (rawsize < 0)
     239           0 :         ereport(ERROR,
     240             :                 (errcode(ERRCODE_DATA_CORRUPTED),
     241             :                  errmsg_internal("compressed lz4 data is corrupt")));
     242             : 
     243          18 :     SET_VARSIZE(result, rawsize + VARHDRSZ);
     244             : 
     245          18 :     return result;
     246             : #endif
     247             : }
     248             : 
     249             : /*
     250             :  * Extract compression ID from a varlena.
     251             :  *
     252             :  * Returns TOAST_INVALID_COMPRESSION_ID if the varlena is not compressed.
     253             :  */
     254             : ToastCompressionId
     255         162 : toast_get_compression_id(struct varlena *attr)
     256             : {
     257         162 :     ToastCompressionId cmid = TOAST_INVALID_COMPRESSION_ID;
     258             : 
     259             :     /*
     260             :      * If it is stored externally then fetch the compression method id from
     261             :      * the external toast pointer.  If compressed inline, fetch it from the
     262             :      * toast compression header.
     263             :      */
     264         162 :     if (VARATT_IS_EXTERNAL_ONDISK(attr))
     265          24 :     {
     266             :         struct varatt_external toast_pointer;
     267             : 
     268          24 :         VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
     269             : 
     270          24 :         if (VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer))
     271          24 :             cmid = VARATT_EXTERNAL_GET_COMPRESS_METHOD(toast_pointer);
     272             :     }
     273         138 :     else if (VARATT_IS_COMPRESSED(attr))
     274         132 :         cmid = VARDATA_COMPRESSED_GET_COMPRESS_METHOD(attr);
     275             : 
     276         162 :     return cmid;
     277             : }
     278             : 
     279             : /*
     280             :  * CompressionNameToMethod - Get compression method from compression name
     281             :  *
     282             :  * Search in the available built-in methods.  If the compression not found
     283             :  * in the built-in methods then return InvalidCompressionMethod.
     284             :  */
     285             : char
     286         136 : CompressionNameToMethod(const char *compression)
     287             : {
     288         136 :     if (strcmp(compression, "pglz") == 0)
     289          60 :         return TOAST_PGLZ_COMPRESSION;
     290          76 :     else if (strcmp(compression, "lz4") == 0)
     291             :     {
     292             : #ifndef USE_LZ4
     293             :         NO_LZ4_SUPPORT();
     294             : #endif
     295          64 :         return TOAST_LZ4_COMPRESSION;
     296             :     }
     297             : 
     298          12 :     return InvalidCompressionMethod;
     299             : }
     300             : 
     301             : /*
     302             :  * GetCompressionMethodName - Get compression method name
     303             :  */
     304             : const char *
     305          30 : GetCompressionMethodName(char method)
     306             : {
     307          30 :     switch (method)
     308             :     {
     309          12 :         case TOAST_PGLZ_COMPRESSION:
     310          12 :             return "pglz";
     311          18 :         case TOAST_LZ4_COMPRESSION:
     312          18 :             return "lz4";
     313           0 :         default:
     314           0 :             elog(ERROR, "invalid compression method %c", method);
     315             :             return NULL;        /* keep compiler quiet */
     316             :     }
     317             : }

Generated by: LCOV version 1.14