LCOV - code coverage report
Current view: top level - contrib/hstore - hstore_subs.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 95.5 % 88 84
Test Date: 2026-07-03 19:57:34 Functions: 100.0 % 6 6
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 74.2 % 66 49

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * hstore_subs.c
       4                 :             :  *    Subscripting support functions for hstore.
       5                 :             :  *
       6                 :             :  * This is a great deal simpler than array_subs.c, because the result of
       7                 :             :  * subscripting an hstore is just a text string (the value for the key).
       8                 :             :  * We do not need to support array slicing notation, nor multiple subscripts.
       9                 :             :  * Less obviously, because the subscript result is never a SQL container
      10                 :             :  * type, there will never be any nested-assignment scenarios, so we do not
      11                 :             :  * need a fetch_old function.  In turn, that means we can drop the
      12                 :             :  * check_subscripts function and just let the fetch and assign functions
      13                 :             :  * do everything.
      14                 :             :  *
      15                 :             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
      16                 :             :  * Portions Copyright (c) 1994, Regents of the University of California
      17                 :             :  *
      18                 :             :  *
      19                 :             :  * IDENTIFICATION
      20                 :             :  *    contrib/hstore/hstore_subs.c
      21                 :             :  *
      22                 :             :  *-------------------------------------------------------------------------
      23                 :             :  */
      24                 :             : #include "postgres.h"
      25                 :             : 
      26                 :             : #include "catalog/pg_type_d.h"
      27                 :             : #include "executor/execExpr.h"
      28                 :             : #include "hstore.h"
      29                 :             : #include "nodes/nodeFuncs.h"
      30                 :             : #include "nodes/subscripting.h"
      31                 :             : #include "parser/parse_coerce.h"
      32                 :             : #include "parser/parse_expr.h"
      33                 :             : #include "utils/builtins.h"
      34                 :             : 
      35                 :             : 
      36                 :             : /*
      37                 :             :  * Finish parse analysis of a SubscriptingRef expression for hstore.
      38                 :             :  *
      39                 :             :  * Verify there's just one subscript, coerce it to text,
      40                 :             :  * and set the result type of the SubscriptingRef node.
      41                 :             :  */
      42                 :             : static void
      43                 :           9 : hstore_subscript_transform(SubscriptingRef *sbsref,
      44                 :             :                            List *indirection,
      45                 :             :                            ParseState *pstate,
      46                 :             :                            bool isSlice,
      47                 :             :                            bool isAssignment)
      48                 :             : {
      49                 :             :     A_Indices  *ai;
      50                 :             :     Node       *subexpr;
      51                 :             : 
      52                 :             :     /* We support only single-subscript, non-slice cases */
      53   [ +  +  +  + ]:           9 :     if (isSlice || list_length(indirection) != 1)
      54         [ +  - ]:           2 :         ereport(ERROR,
      55                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
      56                 :             :                  errmsg("hstore allows only one subscript"),
      57                 :             :                  parser_errposition(pstate,
      58                 :             :                                     exprLocation((Node *) indirection))));
      59                 :             : 
      60                 :             :     /* Transform the subscript expression to type text */
      61                 :           7 :     ai = linitial_node(A_Indices, indirection);
      62                 :             :     Assert(ai->uidx != NULL && ai->lidx == NULL && !ai->is_slice);
      63                 :             : 
      64                 :           7 :     subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind);
      65                 :             :     /* If it's not text already, try to coerce */
      66                 :           7 :     subexpr = coerce_to_target_type(pstate,
      67                 :             :                                     subexpr, exprType(subexpr),
      68                 :             :                                     TEXTOID, -1,
      69                 :             :                                     COERCION_ASSIGNMENT,
      70                 :             :                                     COERCE_IMPLICIT_CAST,
      71                 :             :                                     -1);
      72         [ -  + ]:           7 :     if (subexpr == NULL)
      73         [ #  # ]:           0 :         ereport(ERROR,
      74                 :             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
      75                 :             :                  errmsg("hstore subscript must have type text"),
      76                 :             :                  parser_errposition(pstate, exprLocation(ai->uidx))));
      77                 :             : 
      78                 :             :     /* ... and store the transformed subscript into the SubscriptingRef node */
      79                 :           7 :     sbsref->refupperindexpr = list_make1(subexpr);
      80                 :           7 :     sbsref->reflowerindexpr = NIL;
      81                 :             : 
      82                 :             :     /* Determine the result type of the subscripting operation; always text */
      83                 :           7 :     sbsref->refrestype = TEXTOID;
      84                 :           7 :     sbsref->reftypmod = -1;
      85                 :           7 : }
      86                 :             : 
      87                 :             : /*
      88                 :             :  * Evaluate SubscriptingRef fetch for hstore.
      89                 :             :  *
      90                 :             :  * Source container is in step's result variable (it's known not NULL, since
      91                 :             :  * we set fetch_strict to true), and the subscript expression is in the
      92                 :             :  * upperindex[] array.
      93                 :             :  */
      94                 :             : static void
      95                 :           7 : hstore_subscript_fetch(ExprState *state,
      96                 :             :                        ExprEvalStep *op,
      97                 :             :                        ExprContext *econtext)
      98                 :             : {
      99                 :           7 :     SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
     100                 :             :     HStore     *hs;
     101                 :             :     text       *key;
     102                 :             :     HEntry     *entries;
     103                 :             :     int         idx;
     104                 :             :     text       *out;
     105                 :             : 
     106                 :             :     /* Should not get here if source hstore is null */
     107                 :             :     Assert(!(*op->resnull));
     108                 :             : 
     109                 :             :     /* Check for null subscript */
     110         [ -  + ]:           7 :     if (sbsrefstate->upperindexnull[0])
     111                 :             :     {
     112                 :           0 :         *op->resnull = true;
     113                 :           0 :         return;
     114                 :             :     }
     115                 :             : 
     116                 :             :     /* OK, fetch/detoast the hstore and subscript */
     117                 :           7 :     hs = DatumGetHStoreP(*op->resvalue);
     118                 :           7 :     key = DatumGetTextPP(sbsrefstate->upperindex[0]);
     119                 :             : 
     120                 :             :     /* The rest is basically the same as hstore_fetchval() */
     121                 :           7 :     entries = ARRPTR(hs);
     122                 :           7 :     idx = hstoreFindKey(hs, NULL,
     123                 :           7 :                         VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key));
     124                 :             : 
     125   [ +  +  -  + ]:           7 :     if (idx < 0 || HSTORE_VALISNULL(entries, idx))
     126                 :             :     {
     127                 :           2 :         *op->resnull = true;
     128                 :           2 :         return;
     129                 :             :     }
     130                 :             : 
     131         [ +  - ]:           5 :     out = cstring_to_text_with_len(HSTORE_VAL(entries, STRPTR(hs), idx),
     132         [ -  + ]:           5 :                                    HSTORE_VALLEN(entries, idx));
     133                 :             : 
     134                 :           5 :     *op->resvalue = PointerGetDatum(out);
     135                 :             : }
     136                 :             : 
     137                 :             : /*
     138                 :             :  * Evaluate SubscriptingRef assignment for hstore.
     139                 :             :  *
     140                 :             :  * Input container (possibly null) is in result area, replacement value is in
     141                 :             :  * SubscriptingRefState's replacevalue/replacenull.
     142                 :             :  */
     143                 :             : static void
     144                 :           7 : hstore_subscript_assign(ExprState *state,
     145                 :             :                         ExprEvalStep *op,
     146                 :             :                         ExprContext *econtext)
     147                 :             : {
     148                 :           7 :     SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
     149                 :             :     text       *key;
     150                 :             :     Pairs       p;
     151                 :             :     HStore     *out;
     152                 :             : 
     153                 :             :     /* Check for null subscript */
     154         [ -  + ]:           7 :     if (sbsrefstate->upperindexnull[0])
     155         [ #  # ]:           0 :         ereport(ERROR,
     156                 :             :                 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
     157                 :             :                  errmsg("hstore subscript in assignment must not be null")));
     158                 :             : 
     159                 :             :     /* OK, fetch/detoast the subscript */
     160                 :           7 :     key = DatumGetTextPP(sbsrefstate->upperindex[0]);
     161                 :             : 
     162                 :             :     /* Create a Pairs entry for subscript + replacement value */
     163                 :           7 :     p.needfree = false;
     164                 :           7 :     p.key = VARDATA_ANY(key);
     165                 :           7 :     p.keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key));
     166                 :             : 
     167         [ +  + ]:           7 :     if (sbsrefstate->replacenull)
     168                 :             :     {
     169                 :           1 :         p.vallen = 0;
     170                 :           1 :         p.isnull = true;
     171                 :             :     }
     172                 :             :     else
     173                 :             :     {
     174                 :           6 :         text       *val = DatumGetTextPP(sbsrefstate->replacevalue);
     175                 :             : 
     176                 :           6 :         p.val = VARDATA_ANY(val);
     177                 :           6 :         p.vallen = hstoreCheckValLen(VARSIZE_ANY_EXHDR(val));
     178                 :           6 :         p.isnull = false;
     179                 :             :     }
     180                 :             : 
     181         [ +  + ]:           7 :     if (*op->resnull)
     182                 :             :     {
     183                 :             :         /* Just build a one-element hstore (cf. hstore_from_text) */
     184                 :           2 :         out = hstorePairs(&p, 1, p.keylen + p.vallen);
     185                 :             :     }
     186                 :             :     else
     187                 :             :     {
     188                 :             :         /*
     189                 :             :          * Otherwise, merge the new key into the hstore.  Based on
     190                 :             :          * hstore_concat.
     191                 :             :          */
     192                 :           5 :         HStore     *hs = DatumGetHStoreP(*op->resvalue);
     193                 :           5 :         int         s1count = HS_COUNT(hs);
     194                 :           5 :         int         outcount = 0;
     195                 :             :         int         vsize;
     196                 :             :         char       *ps1,
     197                 :             :                    *bufd,
     198                 :             :                    *pd;
     199                 :             :         HEntry     *es1,
     200                 :             :                    *ed;
     201                 :             :         int         s1idx;
     202                 :             :         int         s2idx;
     203                 :             : 
     204                 :             :         /* Allocate result without considering possibility of duplicate */
     205                 :           5 :         vsize = CALCDATASIZE(s1count + 1, VARSIZE(hs) + p.keylen + p.vallen);
     206                 :           5 :         out = palloc(vsize);
     207                 :           5 :         SET_VARSIZE(out, vsize);
     208                 :           5 :         HS_SETCOUNT(out, s1count + 1);
     209                 :             : 
     210                 :           5 :         ps1 = STRPTR(hs);
     211                 :           5 :         bufd = pd = STRPTR(out);
     212                 :           5 :         es1 = ARRPTR(hs);
     213                 :           5 :         ed = ARRPTR(out);
     214                 :             : 
     215   [ +  +  +  + ]:          37 :         for (s1idx = s2idx = 0; s1idx < s1count || s2idx < 1; ++outcount)
     216                 :             :         {
     217                 :             :             int         difference;
     218                 :             : 
     219         [ +  + ]:          32 :             if (s1idx >= s1count)
     220                 :           1 :                 difference = 1;
     221         [ +  + ]:          31 :             else if (s2idx >= 1)
     222                 :          10 :                 difference = -1;
     223                 :             :             else
     224                 :             :             {
     225         [ +  + ]:          21 :                 int         s1keylen = HSTORE_KEYLEN(es1, s1idx);
     226                 :          21 :                 int         s2keylen = p.keylen;
     227                 :             : 
     228         [ +  + ]:          21 :                 if (s1keylen == s2keylen)
     229                 :          19 :                     difference = memcmp(HSTORE_KEY(es1, ps1, s1idx),
     230         [ +  + ]:          19 :                                         p.key,
     231                 :             :                                         s1keylen);
     232                 :             :                 else
     233         [ +  - ]:           2 :                     difference = (s1keylen > s2keylen) ? 1 : -1;
     234                 :             :             }
     235                 :             : 
     236         [ +  + ]:          32 :             if (difference >= 0)
     237                 :             :             {
     238         [ -  + ]:           5 :                 HS_ADDITEM(ed, bufd, pd, p);
     239                 :           5 :                 ++s2idx;
     240         [ +  + ]:           5 :                 if (difference == 0)
     241                 :           2 :                     ++s1idx;
     242                 :             :             }
     243                 :             :             else
     244                 :             :             {
     245   [ +  +  -  +  :          27 :                 HS_COPYITEM(ed, bufd, pd,
          +  +  +  +  -  
                +  -  + ]
     246                 :             :                             HSTORE_KEY(es1, ps1, s1idx),
     247                 :             :                             HSTORE_KEYLEN(es1, s1idx),
     248                 :             :                             HSTORE_VALLEN(es1, s1idx),
     249                 :             :                             HSTORE_VALISNULL(es1, s1idx));
     250                 :          27 :                 ++s1idx;
     251                 :             :             }
     252                 :             :         }
     253                 :             : 
     254   [ +  -  +  + ]:           5 :         HS_FINALIZE(out, outcount, bufd, pd);
     255                 :             :     }
     256                 :             : 
     257                 :           7 :     *op->resvalue = PointerGetDatum(out);
     258                 :           7 :     *op->resnull = false;
     259                 :           7 : }
     260                 :             : 
     261                 :             : /*
     262                 :             :  * Set up execution state for an hstore subscript operation.
     263                 :             :  */
     264                 :             : static void
     265                 :           7 : hstore_exec_setup(const SubscriptingRef *sbsref,
     266                 :             :                   SubscriptingRefState *sbsrefstate,
     267                 :             :                   SubscriptExecSteps *methods)
     268                 :             : {
     269                 :             :     /* Assert we are dealing with one subscript */
     270                 :             :     Assert(sbsrefstate->numlower == 0);
     271                 :             :     Assert(sbsrefstate->numupper == 1);
     272                 :             :     /* We can't check upperprovided[0] here, but it must be true */
     273                 :             : 
     274                 :             :     /* Pass back pointers to appropriate step execution functions */
     275                 :           7 :     methods->sbs_check_subscripts = NULL;
     276                 :           7 :     methods->sbs_fetch = hstore_subscript_fetch;
     277                 :           7 :     methods->sbs_assign = hstore_subscript_assign;
     278                 :           7 :     methods->sbs_fetch_old = NULL;
     279                 :           7 : }
     280                 :             : 
     281                 :             : /*
     282                 :             :  * hstore_subscript_handler
     283                 :             :  *      Subscripting handler for hstore.
     284                 :             :  */
     285                 :           8 : PG_FUNCTION_INFO_V1(hstore_subscript_handler);
     286                 :             : Datum
     287                 :          16 : hstore_subscript_handler(PG_FUNCTION_ARGS)
     288                 :             : {
     289                 :             :     static const SubscriptRoutines sbsroutines = {
     290                 :             :         .transform = hstore_subscript_transform,
     291                 :             :         .exec_setup = hstore_exec_setup,
     292                 :             :         .fetch_strict = true,   /* fetch returns NULL for NULL inputs */
     293                 :             :         .fetch_leakproof = true,    /* fetch returns NULL for bad subscript */
     294                 :             :         .store_leakproof = false    /* ... but assignment throws error */
     295                 :             :     };
     296                 :             : 
     297                 :          16 :     PG_RETURN_POINTER(&sbsroutines);
     298                 :             : }
        

Generated by: LCOV version 2.0-1