LCOV - code coverage report
Current view: top level - src/backend/utils/adt - datum.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 92.2 % 154 142
Test Date: 2026-02-17 17:20:33 Functions: 100.0 % 10 10
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * datum.c
       4              :  *    POSTGRES Datum (abstract data type) manipulation routines.
       5              :  *
       6              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7              :  * Portions Copyright (c) 1994, Regents of the University of California
       8              :  *
       9              :  *
      10              :  * IDENTIFICATION
      11              :  *    src/backend/utils/adt/datum.c
      12              :  *
      13              :  *-------------------------------------------------------------------------
      14              :  */
      15              : 
      16              : /*
      17              :  * In the implementation of these routines we assume the following:
      18              :  *
      19              :  * A) if a type is "byVal" then all the information is stored in the
      20              :  * Datum itself (i.e. no pointers involved!). In this case the
      21              :  * length of the type is always greater than zero and not more than
      22              :  * "sizeof(Datum)"
      23              :  *
      24              :  * B) if a type is not "byVal" and it has a fixed length (typlen > 0),
      25              :  * then the "Datum" always contains a pointer to a stream of bytes.
      26              :  * The number of significant bytes are always equal to the typlen.
      27              :  *
      28              :  * C) if a type is not "byVal" and has typlen == -1,
      29              :  * then the "Datum" always points to a "varlena".
      30              :  * This varlena structure has information about the actual length of this
      31              :  * particular instance of the type and about its value.
      32              :  *
      33              :  * D) if a type is not "byVal" and has typlen == -2,
      34              :  * then the "Datum" always points to a null-terminated C string.
      35              :  *
      36              :  * Note that we do not treat "toasted" datums specially; therefore what
      37              :  * will be copied or compared is the compressed data or toast reference.
      38              :  * An exception is made for datumCopy() of an expanded object, however,
      39              :  * because most callers expect to get a simple contiguous (and pfree'able)
      40              :  * result from datumCopy().  See also datumTransfer().
      41              :  */
      42              : 
      43              : #include "postgres.h"
      44              : 
      45              : #include "access/detoast.h"
      46              : #include "common/hashfn.h"
      47              : #include "fmgr.h"
      48              : #include "utils/datum.h"
      49              : #include "utils/expandeddatum.h"
      50              : #include "utils/fmgrprotos.h"
      51              : 
      52              : 
      53              : /*-------------------------------------------------------------------------
      54              :  * datumGetSize
      55              :  *
      56              :  * Find the "real" size of a datum, given the datum value,
      57              :  * whether it is a "by value", and the declared type length.
      58              :  * (For TOAST pointer datums, this is the size of the pointer datum.)
      59              :  *
      60              :  * This is essentially an out-of-line version of the att_addlength_datum()
      61              :  * macro in access/tupmacs.h.  We do a tad more error checking though.
      62              :  *-------------------------------------------------------------------------
      63              :  */
      64              : Size
      65      5703037 : datumGetSize(Datum value, bool typByVal, int typLen)
      66              : {
      67              :     Size        size;
      68              : 
      69      5703037 :     if (typByVal)
      70              :     {
      71              :         /* Pass-by-value types are always fixed-length */
      72              :         Assert(typLen > 0 && typLen <= sizeof(Datum));
      73       837393 :         size = (Size) typLen;
      74              :     }
      75              :     else
      76              :     {
      77      4865644 :         if (typLen > 0)
      78              :         {
      79              :             /* Fixed-length pass-by-ref type */
      80      3387096 :             size = (Size) typLen;
      81              :         }
      82      1478548 :         else if (typLen == -1)
      83              :         {
      84              :             /* It is a varlena datatype */
      85      1268140 :             varlena    *s = (varlena *) DatumGetPointer(value);
      86              : 
      87      1268140 :             if (!s)
      88            0 :                 ereport(ERROR,
      89              :                         (errcode(ERRCODE_DATA_EXCEPTION),
      90              :                          errmsg("invalid Datum pointer")));
      91              : 
      92      1268140 :             size = (Size) VARSIZE_ANY(s);
      93              :         }
      94       210408 :         else if (typLen == -2)
      95              :         {
      96              :             /* It is a cstring datatype */
      97       210408 :             char       *s = (char *) DatumGetPointer(value);
      98              : 
      99       210408 :             if (!s)
     100            0 :                 ereport(ERROR,
     101              :                         (errcode(ERRCODE_DATA_EXCEPTION),
     102              :                          errmsg("invalid Datum pointer")));
     103              : 
     104       210408 :             size = (Size) (strlen(s) + 1);
     105              :         }
     106              :         else
     107              :         {
     108            0 :             elog(ERROR, "invalid typLen: %d", typLen);
     109              :             size = 0;           /* keep compiler quiet */
     110              :         }
     111              :     }
     112              : 
     113      5703037 :     return size;
     114              : }
     115              : 
     116              : /*-------------------------------------------------------------------------
     117              :  * datumCopy
     118              :  *
     119              :  * Make a copy of a non-NULL datum.
     120              :  *
     121              :  * If the datatype is pass-by-reference, memory is obtained with palloc().
     122              :  *
     123              :  * If the value is a reference to an expanded object, we flatten into memory
     124              :  * obtained with palloc().  We need to copy because one of the main uses of
     125              :  * this function is to copy a datum out of a transient memory context that's
     126              :  * about to be destroyed, and the expanded object is probably in a child
     127              :  * context that will also go away.  Moreover, many callers assume that the
     128              :  * result is a single pfree-able chunk.
     129              :  *-------------------------------------------------------------------------
     130              :  */
     131              : Datum
     132     12923904 : datumCopy(Datum value, bool typByVal, int typLen)
     133              : {
     134              :     Datum       res;
     135              : 
     136     12923904 :     if (typByVal)
     137      6588633 :         res = value;
     138      6335271 :     else if (typLen == -1)
     139              :     {
     140              :         /* It is a varlena datatype */
     141      3491346 :         varlena    *vl = (varlena *) DatumGetPointer(value);
     142              : 
     143      3491346 :         if (VARATT_IS_EXTERNAL_EXPANDED(vl))
     144              :         {
     145              :             /* Flatten into the caller's memory context */
     146          472 :             ExpandedObjectHeader *eoh = DatumGetEOHP(value);
     147              :             Size        resultsize;
     148              :             char       *resultptr;
     149              : 
     150          472 :             resultsize = EOH_get_flat_size(eoh);
     151          472 :             resultptr = (char *) palloc(resultsize);
     152          472 :             EOH_flatten_into(eoh, resultptr, resultsize);
     153          472 :             res = PointerGetDatum(resultptr);
     154              :         }
     155              :         else
     156              :         {
     157              :             /* Otherwise, just copy the varlena datum verbatim */
     158              :             Size        realSize;
     159              :             char       *resultptr;
     160              : 
     161      3490874 :             realSize = (Size) VARSIZE_ANY(vl);
     162      3490874 :             resultptr = (char *) palloc(realSize);
     163      3490874 :             memcpy(resultptr, vl, realSize);
     164      3490874 :             res = PointerGetDatum(resultptr);
     165              :         }
     166              :     }
     167              :     else
     168              :     {
     169              :         /* Pass by reference, but not varlena, so not toasted */
     170              :         Size        realSize;
     171              :         char       *resultptr;
     172              : 
     173      2843925 :         realSize = datumGetSize(value, typByVal, typLen);
     174              : 
     175      2843925 :         resultptr = (char *) palloc(realSize);
     176      2843925 :         memcpy(resultptr, DatumGetPointer(value), realSize);
     177      2843925 :         res = PointerGetDatum(resultptr);
     178              :     }
     179     12923904 :     return res;
     180              : }
     181              : 
     182              : /*-------------------------------------------------------------------------
     183              :  * datumTransfer
     184              :  *
     185              :  * Transfer a non-NULL datum into the current memory context.
     186              :  *
     187              :  * This is equivalent to datumCopy() except when the datum is a read-write
     188              :  * pointer to an expanded object.  In that case we merely reparent the object
     189              :  * into the current context, and return its standard R/W pointer (in case the
     190              :  * given one is a transient pointer of shorter lifespan).
     191              :  *-------------------------------------------------------------------------
     192              :  */
     193              : Datum
     194        59528 : datumTransfer(Datum value, bool typByVal, int typLen)
     195              : {
     196       117273 :     if (!typByVal && typLen == -1 &&
     197        57745 :         VARATT_IS_EXTERNAL_EXPANDED_RW(DatumGetPointer(value)))
     198         1996 :         value = TransferExpandedObject(value, CurrentMemoryContext);
     199              :     else
     200        57532 :         value = datumCopy(value, typByVal, typLen);
     201        59528 :     return value;
     202              : }
     203              : 
     204              : /*-------------------------------------------------------------------------
     205              :  * datumIsEqual
     206              :  *
     207              :  * Return true if two datums are equal, false otherwise
     208              :  *
     209              :  * NOTE: XXX!
     210              :  * We just compare the bytes of the two values, one by one.
     211              :  * This routine will return false if there are 2 different
     212              :  * representations of the same value (something along the lines
     213              :  * of say the representation of zero in one's complement arithmetic).
     214              :  * Also, it will probably not give the answer you want if either
     215              :  * datum has been "toasted".
     216              :  *
     217              :  * Do not try to make this any smarter than it currently is with respect
     218              :  * to "toasted" datums, because some of the callers could be working in the
     219              :  * context of an aborted transaction.
     220              :  *-------------------------------------------------------------------------
     221              :  */
     222              : bool
     223      1998648 : datumIsEqual(Datum value1, Datum value2, bool typByVal, int typLen)
     224              : {
     225              :     bool        res;
     226              : 
     227      1998648 :     if (typByVal)
     228              :     {
     229              :         /*
     230              :          * just compare the two datums. NOTE: just comparing "len" bytes will
     231              :          * not do the work, because we do not know how these bytes are aligned
     232              :          * inside the "Datum".  We assume instead that any given datatype is
     233              :          * consistent about how it fills extraneous bits in the Datum.
     234              :          */
     235      1411298 :         res = (value1 == value2);
     236              :     }
     237              :     else
     238              :     {
     239              :         Size        size1,
     240              :                     size2;
     241              :         char       *s1,
     242              :                    *s2;
     243              : 
     244              :         /*
     245              :          * Compare the bytes pointed by the pointers stored in the datums.
     246              :          */
     247       587350 :         size1 = datumGetSize(value1, typByVal, typLen);
     248       587350 :         size2 = datumGetSize(value2, typByVal, typLen);
     249       587350 :         if (size1 != size2)
     250         7398 :             return false;
     251       579952 :         s1 = (char *) DatumGetPointer(value1);
     252       579952 :         s2 = (char *) DatumGetPointer(value2);
     253       579952 :         res = (memcmp(s1, s2, size1) == 0);
     254              :     }
     255      1991250 :     return res;
     256              : }
     257              : 
     258              : /*-------------------------------------------------------------------------
     259              :  * datum_image_eq
     260              :  *
     261              :  * Compares two datums for identical contents, based on byte images.  Return
     262              :  * true if the two datums are equal, false otherwise.
     263              :  *-------------------------------------------------------------------------
     264              :  */
     265              : bool
     266     10482279 : datum_image_eq(Datum value1, Datum value2, bool typByVal, int typLen)
     267              : {
     268              :     Size        len1,
     269              :                 len2;
     270     10482279 :     bool        result = true;
     271              : 
     272     10482279 :     if (typByVal)
     273              :     {
     274      8862165 :         result = (value1 == value2);
     275              :     }
     276      1620114 :     else if (typLen > 0)
     277              :     {
     278      1200540 :         result = (memcmp(DatumGetPointer(value1),
     279      1200540 :                          DatumGetPointer(value2),
     280              :                          typLen) == 0);
     281              :     }
     282       419574 :     else if (typLen == -1)
     283              :     {
     284       164590 :         len1 = toast_raw_datum_size(value1);
     285       164590 :         len2 = toast_raw_datum_size(value2);
     286              :         /* No need to de-toast if lengths don't match. */
     287       164590 :         if (len1 != len2)
     288        11426 :             result = false;
     289              :         else
     290              :         {
     291              :             varlena    *arg1val;
     292              :             varlena    *arg2val;
     293              : 
     294       153164 :             arg1val = PG_DETOAST_DATUM_PACKED(value1);
     295       153164 :             arg2val = PG_DETOAST_DATUM_PACKED(value2);
     296              : 
     297       153164 :             result = (memcmp(VARDATA_ANY(arg1val),
     298       153164 :                              VARDATA_ANY(arg2val),
     299              :                              len1 - VARHDRSZ) == 0);
     300              : 
     301              :             /* Only free memory if it's a copy made here. */
     302       153164 :             if (arg1val != DatumGetPointer(value1))
     303            3 :                 pfree(arg1val);
     304       153164 :             if (arg2val != DatumGetPointer(value2))
     305            3 :                 pfree(arg2val);
     306              :         }
     307              :     }
     308       254984 :     else if (typLen == -2)
     309              :     {
     310              :         char       *s1,
     311              :                    *s2;
     312              : 
     313              :         /* Compare cstring datums */
     314       254984 :         s1 = DatumGetCString(value1);
     315       254984 :         s2 = DatumGetCString(value2);
     316       254984 :         len1 = strlen(s1) + 1;
     317       254984 :         len2 = strlen(s2) + 1;
     318       254984 :         if (len1 != len2)
     319       140293 :             return false;
     320       114691 :         result = (memcmp(s1, s2, len1) == 0);
     321              :     }
     322              :     else
     323            0 :         elog(ERROR, "unexpected typLen: %d", typLen);
     324              : 
     325     10341986 :     return result;
     326              : }
     327              : 
     328              : /*-------------------------------------------------------------------------
     329              :  * datum_image_hash
     330              :  *
     331              :  * Generate a hash value based on the binary representation of 'value'.  Most
     332              :  * use cases will want to use the hash function specific to the Datum's type,
     333              :  * however, some corner cases require generating a hash value based on the
     334              :  * actual bits rather than the logical value.
     335              :  *-------------------------------------------------------------------------
     336              :  */
     337              : uint32
     338        60394 : datum_image_hash(Datum value, bool typByVal, int typLen)
     339              : {
     340              :     Size        len;
     341              :     uint32      result;
     342              : 
     343        60394 :     if (typByVal)
     344        60292 :         result = hash_bytes((unsigned char *) &value, sizeof(Datum));
     345          102 :     else if (typLen > 0)
     346           18 :         result = hash_bytes((unsigned char *) DatumGetPointer(value), typLen);
     347           84 :     else if (typLen == -1)
     348              :     {
     349              :         varlena    *val;
     350              : 
     351           84 :         len = toast_raw_datum_size(value);
     352              : 
     353           84 :         val = PG_DETOAST_DATUM_PACKED(value);
     354              : 
     355           84 :         result = hash_bytes((unsigned char *) VARDATA_ANY(val), len - VARHDRSZ);
     356              : 
     357              :         /* Only free memory if it's a copy made here. */
     358           84 :         if (val != DatumGetPointer(value))
     359            6 :             pfree(val);
     360              :     }
     361            0 :     else if (typLen == -2)
     362              :     {
     363              :         char       *s;
     364              : 
     365            0 :         s = DatumGetCString(value);
     366            0 :         len = strlen(s) + 1;
     367              : 
     368            0 :         result = hash_bytes((unsigned char *) s, len);
     369              :     }
     370              :     else
     371              :     {
     372            0 :         elog(ERROR, "unexpected typLen: %d", typLen);
     373              :         result = 0;             /* keep compiler quiet */
     374              :     }
     375              : 
     376        60394 :     return result;
     377              : }
     378              : 
     379              : /*-------------------------------------------------------------------------
     380              :  * btequalimage
     381              :  *
     382              :  * Generic "equalimage" support function.
     383              :  *
     384              :  * B-Tree operator classes whose equality function could safely be replaced by
     385              :  * datum_image_eq() in all cases can use this as their "equalimage" support
     386              :  * function.
     387              :  *
     388              :  * Currently, we unconditionally assume that any B-Tree operator class that
     389              :  * registers btequalimage as its support function 4 must be able to safely use
     390              :  * optimizations like deduplication (i.e. we return true unconditionally).  If
     391              :  * it ever proved necessary to rescind support for an operator class, we could
     392              :  * do that in a targeted fashion by doing something with the opcintype
     393              :  * argument.
     394              :  *-------------------------------------------------------------------------
     395              :  */
     396              : Datum
     397        44917 : btequalimage(PG_FUNCTION_ARGS)
     398              : {
     399              : #ifdef NOT_USED
     400              :     Oid         opcintype = PG_GETARG_OID(0);
     401              : #endif
     402              : 
     403        44917 :     PG_RETURN_BOOL(true);
     404              : }
     405              : 
     406              : /*-------------------------------------------------------------------------
     407              :  * datumEstimateSpace
     408              :  *
     409              :  * Compute the amount of space that datumSerialize will require for a
     410              :  * particular Datum.
     411              :  *-------------------------------------------------------------------------
     412              :  */
     413              : Size
     414           75 : datumEstimateSpace(Datum value, bool isnull, bool typByVal, int typLen)
     415              : {
     416           75 :     Size        sz = sizeof(int);
     417              : 
     418           75 :     if (!isnull)
     419              :     {
     420              :         /* no need to use add_size, can't overflow */
     421           75 :         if (typByVal)
     422           66 :             sz += sizeof(Datum);
     423           18 :         else if (typLen == -1 &&
     424            9 :                  VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(value)))
     425              :         {
     426              :             /* Expanded objects need to be flattened, see comment below */
     427            3 :             sz += EOH_get_flat_size(DatumGetEOHP(value));
     428              :         }
     429              :         else
     430            6 :             sz += datumGetSize(value, typByVal, typLen);
     431              :     }
     432              : 
     433           75 :     return sz;
     434              : }
     435              : 
     436              : /*-------------------------------------------------------------------------
     437              :  * datumSerialize
     438              :  *
     439              :  * Serialize a possibly-NULL datum into caller-provided storage.
     440              :  *
     441              :  * Note: "expanded" objects are flattened so as to produce a self-contained
     442              :  * representation, but other sorts of toast pointers are transferred as-is.
     443              :  * This is because the intended use of this function is to pass the value
     444              :  * to another process within the same database server.  The other process
     445              :  * could not access an "expanded" object within this process's memory, but
     446              :  * we assume it can dereference the same TOAST pointers this one can.
     447              :  *
     448              :  * The format is as follows: first, we write a 4-byte header word, which
     449              :  * is either the length of a pass-by-reference datum, -1 for a
     450              :  * pass-by-value datum, or -2 for a NULL.  If the value is NULL, nothing
     451              :  * further is written.  If it is pass-by-value, sizeof(Datum) bytes
     452              :  * follow.  Otherwise, the number of bytes indicated by the header word
     453              :  * follow.  The caller is responsible for ensuring that there is enough
     454              :  * storage to store the number of bytes that will be written; use
     455              :  * datumEstimateSpace() to find out how many will be needed.
     456              :  * *start_address is updated to point to the byte immediately following
     457              :  * those written.
     458              :  *-------------------------------------------------------------------------
     459              :  */
     460              : void
     461           57 : datumSerialize(Datum value, bool isnull, bool typByVal, int typLen,
     462              :                char **start_address)
     463              : {
     464           57 :     ExpandedObjectHeader *eoh = NULL;
     465              :     int         header;
     466              : 
     467              :     /* Write header word. */
     468           57 :     if (isnull)
     469            0 :         header = -2;
     470           57 :     else if (typByVal)
     471           48 :         header = -1;
     472           18 :     else if (typLen == -1 &&
     473            9 :              VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(value)))
     474              :     {
     475            3 :         eoh = DatumGetEOHP(value);
     476            3 :         header = EOH_get_flat_size(eoh);
     477              :     }
     478              :     else
     479            6 :         header = datumGetSize(value, typByVal, typLen);
     480           57 :     memcpy(*start_address, &header, sizeof(int));
     481           57 :     *start_address += sizeof(int);
     482              : 
     483              :     /* If not null, write payload bytes. */
     484           57 :     if (!isnull)
     485              :     {
     486           57 :         if (typByVal)
     487              :         {
     488           48 :             memcpy(*start_address, &value, sizeof(Datum));
     489           48 :             *start_address += sizeof(Datum);
     490              :         }
     491            9 :         else if (eoh)
     492              :         {
     493              :             char       *tmp;
     494              : 
     495              :             /*
     496              :              * EOH_flatten_into expects the target address to be maxaligned,
     497              :              * so we can't store directly to *start_address.
     498              :              */
     499            3 :             tmp = (char *) palloc(header);
     500            3 :             EOH_flatten_into(eoh, tmp, header);
     501            3 :             memcpy(*start_address, tmp, header);
     502            3 :             *start_address += header;
     503              : 
     504              :             /* be tidy. */
     505            3 :             pfree(tmp);
     506              :         }
     507              :         else
     508              :         {
     509            6 :             memcpy(*start_address, DatumGetPointer(value), header);
     510            6 :             *start_address += header;
     511              :         }
     512              :     }
     513           57 : }
     514              : 
     515              : /*-------------------------------------------------------------------------
     516              :  * datumRestore
     517              :  *
     518              :  * Restore a possibly-NULL datum previously serialized by datumSerialize.
     519              :  * *start_address is updated according to the number of bytes consumed.
     520              :  *-------------------------------------------------------------------------
     521              :  */
     522              : Datum
     523          138 : datumRestore(char **start_address, bool *isnull)
     524              : {
     525              :     int         header;
     526              :     void       *d;
     527              : 
     528              :     /* Read header word. */
     529          138 :     memcpy(&header, *start_address, sizeof(int));
     530          138 :     *start_address += sizeof(int);
     531              : 
     532              :     /* If this datum is NULL, we can stop here. */
     533          138 :     if (header == -2)
     534              :     {
     535            0 :         *isnull = true;
     536            0 :         return (Datum) 0;
     537              :     }
     538              : 
     539              :     /* OK, datum is not null. */
     540          138 :     *isnull = false;
     541              : 
     542              :     /* If this datum is pass-by-value, sizeof(Datum) bytes follow. */
     543          138 :     if (header == -1)
     544              :     {
     545              :         Datum       val;
     546              : 
     547          108 :         memcpy(&val, *start_address, sizeof(Datum));
     548          108 :         *start_address += sizeof(Datum);
     549          108 :         return val;
     550              :     }
     551              : 
     552              :     /* Pass-by-reference case; copy indicated number of bytes. */
     553              :     Assert(header > 0);
     554           30 :     d = palloc(header);
     555           30 :     memcpy(d, *start_address, header);
     556           30 :     *start_address += header;
     557           30 :     return PointerGetDatum(d);
     558              : }
        

Generated by: LCOV version 2.0-1