LCOV - code coverage report
Current view: top level - contrib/hstore - hstore_subs.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 95.5 % 88 84
Test Date: 2026-03-26 15:17:35 Functions: 100.0 % 6 6
Legend: Lines:     hit not hit

            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