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

Generated by: LCOV version 2.0-1