LCOV - differential code coverage report
Current view: top level - contrib/hstore - hstore_subs.c (source / functions) Coverage Total Hit UBC CBC
Current: Differential Code Coverage HEAD vs 15 Lines: 95.7 % 92 88 4 88
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 6 6 6
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  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-2023, 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 CBC           9 : 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               9 :     if (isSlice || list_length(indirection) != 1)
      53               2 :         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               7 :     ai = linitial_node(A_Indices, indirection);
      61               7 :     Assert(ai->uidx != NULL && ai->lidx == NULL && !ai->is_slice);
      62                 : 
      63               7 :     subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind);
      64                 :     /* If it's not text already, try to coerce */
      65               7 :     subexpr = coerce_to_target_type(pstate,
      66                 :                                     subexpr, exprType(subexpr),
      67                 :                                     TEXTOID, -1,
      68                 :                                     COERCION_ASSIGNMENT,
      69                 :                                     COERCE_IMPLICIT_CAST,
      70                 :                                     -1);
      71               7 :     if (subexpr == NULL)
      72 UBC           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 CBC           7 :     sbsref->refupperindexpr = list_make1(subexpr);
      79               7 :     sbsref->reflowerindexpr = NIL;
      80                 : 
      81                 :     /* Determine the result type of the subscripting operation; always text */
      82               7 :     sbsref->refrestype = TEXTOID;
      83               7 :     sbsref->reftypmod = -1;
      84               7 : }
      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               7 : hstore_subscript_fetch(ExprState *state,
      95                 :                        ExprEvalStep *op,
      96                 :                        ExprContext *econtext)
      97                 : {
      98               7 :     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               7 :     Assert(!(*op->resnull));
     107                 : 
     108                 :     /* Check for null subscript */
     109               7 :     if (sbsrefstate->upperindexnull[0])
     110                 :     {
     111 UBC           0 :         *op->resnull = true;
     112               0 :         return;
     113                 :     }
     114                 : 
     115                 :     /* OK, fetch/detoast the hstore and subscript */
     116 CBC           7 :     hs = DatumGetHStoreP(*op->resvalue);
     117               7 :     key = DatumGetTextPP(sbsrefstate->upperindex[0]);
     118                 : 
     119                 :     /* The rest is basically the same as hstore_fetchval() */
     120               7 :     entries = ARRPTR(hs);
     121              14 :     idx = hstoreFindKey(hs, NULL,
     122              14 :                         VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key));
     123                 : 
     124               7 :     if (idx < 0 || HSTORE_VALISNULL(entries, idx))
     125                 :     {
     126               2 :         *op->resnull = true;
     127               2 :         return;
     128                 :     }
     129                 : 
     130               5 :     out = cstring_to_text_with_len(HSTORE_VAL(entries, STRPTR(hs), idx),
     131               5 :                                    HSTORE_VALLEN(entries, idx));
     132                 : 
     133               5 :     *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               7 : hstore_subscript_assign(ExprState *state,
     144                 :                         ExprEvalStep *op,
     145                 :                         ExprContext *econtext)
     146                 : {
     147               7 :     SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
     148                 :     text       *key;
     149                 :     Pairs       p;
     150                 :     HStore     *out;
     151                 : 
     152                 :     /* Check for null subscript */
     153               7 :     if (sbsrefstate->upperindexnull[0])
     154 UBC           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 CBC           7 :     key = DatumGetTextPP(sbsrefstate->upperindex[0]);
     160                 : 
     161                 :     /* Create a Pairs entry for subscript + replacement value */
     162               7 :     p.needfree = false;
     163               7 :     p.key = VARDATA_ANY(key);
     164               7 :     p.keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key));
     165                 : 
     166               7 :     if (sbsrefstate->replacenull)
     167                 :     {
     168               1 :         p.vallen = 0;
     169               1 :         p.isnull = true;
     170                 :     }
     171                 :     else
     172                 :     {
     173               6 :         text       *val = DatumGetTextPP(sbsrefstate->replacevalue);
     174                 : 
     175               6 :         p.val = VARDATA_ANY(val);
     176               6 :         p.vallen = hstoreCheckValLen(VARSIZE_ANY_EXHDR(val));
     177               6 :         p.isnull = false;
     178                 :     }
     179                 : 
     180               7 :     if (*op->resnull)
     181                 :     {
     182                 :         /* Just build a one-element hstore (cf. hstore_from_text) */
     183               2 :         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               5 :         HStore     *hs = DatumGetHStoreP(*op->resvalue);
     192               5 :         int         s1count = HS_COUNT(hs);
     193               5 :         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               5 :         vsize = CALCDATASIZE(s1count + 1, VARSIZE(hs) + p.keylen + p.vallen);
     205               5 :         out = palloc(vsize);
     206               5 :         SET_VARSIZE(out, vsize);
     207               5 :         HS_SETCOUNT(out, s1count + 1);
     208                 : 
     209               5 :         ps1 = STRPTR(hs);
     210               5 :         bufd = pd = STRPTR(out);
     211               5 :         es1 = ARRPTR(hs);
     212               5 :         ed = ARRPTR(out);
     213                 : 
     214              37 :         for (s1idx = s2idx = 0; s1idx < s1count || s2idx < 1; ++outcount)
     215                 :         {
     216                 :             int         difference;
     217                 : 
     218              32 :             if (s1idx >= s1count)
     219               1 :                 difference = 1;
     220              31 :             else if (s2idx >= 1)
     221              10 :                 difference = -1;
     222                 :             else
     223                 :             {
     224              21 :                 int         s1keylen = HSTORE_KEYLEN(es1, s1idx);
     225              21 :                 int         s2keylen = p.keylen;
     226                 : 
     227              21 :                 if (s1keylen == s2keylen)
     228              19 :                     difference = memcmp(HSTORE_KEY(es1, ps1, s1idx),
     229              19 :                                         p.key,
     230                 :                                         s1keylen);
     231                 :                 else
     232               2 :                     difference = (s1keylen > s2keylen) ? 1 : -1;
     233                 :             }
     234                 : 
     235              32 :             if (difference >= 0)
     236                 :             {
     237               5 :                 HS_ADDITEM(ed, bufd, pd, p);
     238               5 :                 ++s2idx;
     239               5 :                 if (difference == 0)
     240               2 :                     ++s1idx;
     241                 :             }
     242                 :             else
     243                 :             {
     244              27 :                 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              27 :                 ++s1idx;
     250                 :             }
     251                 :         }
     252                 : 
     253               5 :         HS_FINALIZE(out, outcount, bufd, pd);
     254                 :     }
     255                 : 
     256               7 :     *op->resvalue = PointerGetDatum(out);
     257               7 :     *op->resnull = false;
     258               7 : }
     259                 : 
     260                 : /*
     261                 :  * Set up execution state for an hstore subscript operation.
     262                 :  */
     263                 : static void
     264               7 : hstore_exec_setup(const SubscriptingRef *sbsref,
     265                 :                   SubscriptingRefState *sbsrefstate,
     266                 :                   SubscriptExecSteps *methods)
     267                 : {
     268                 :     /* Assert we are dealing with one subscript */
     269               7 :     Assert(sbsrefstate->numlower == 0);
     270               7 :     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               7 :     methods->sbs_check_subscripts = NULL;
     275               7 :     methods->sbs_fetch = hstore_subscript_fetch;
     276               7 :     methods->sbs_assign = hstore_subscript_assign;
     277               7 :     methods->sbs_fetch_old = NULL;
     278               7 : }
     279                 : 
     280                 : /*
     281                 :  * hstore_subscript_handler
     282                 :  *      Subscripting handler for hstore.
     283                 :  */
     284               8 : PG_FUNCTION_INFO_V1(hstore_subscript_handler);
     285                 : Datum
     286              16 : 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              16 :     PG_RETURN_POINTER(&sbsroutines);
     297                 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a