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

Generated by: LCOV version 1.14