LCOV - code coverage report
Current view: top level - contrib/jsonb_plperl - jsonb_plperl.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 94.6 % 129 122
Test Date: 2026-07-03 19:57:34 Functions: 100.0 % 10 10
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 70.8 % 72 51

             Branch data     Line data    Source code
       1                 :             : #include "postgres.h"
       2                 :             : 
       3                 :             : #include <math.h>
       4                 :             : 
       5                 :             : #include "fmgr.h"
       6                 :             : #include "miscadmin.h"
       7                 :             : #include "plperl.h"
       8                 :             : #include "utils/fmgrprotos.h"
       9                 :             : #include "utils/jsonb.h"
      10                 :             : 
      11                 :           2 : PG_MODULE_MAGIC_EXT(
      12                 :             :                     .name = "jsonb_plperl",
      13                 :             :                     .version = PG_VERSION
      14                 :             : );
      15                 :             : 
      16                 :             : static SV  *Jsonb_to_SV(JsonbContainer *jsonb);
      17                 :             : static void SV_to_JsonbValue(SV *in, JsonbInState *jsonb_state, bool is_elem);
      18                 :             : 
      19                 :             : 
      20                 :             : static SV  *
      21                 :          81 : JsonbValue_to_SV(JsonbValue *jbv)
      22                 :             : {
      23                 :          81 :     dTHX;
      24                 :             : 
      25   [ +  +  +  +  :          81 :     switch (jbv->type)
                   +  - ]
      26                 :             :     {
      27                 :           6 :         case jbvBinary:
      28                 :           6 :             return Jsonb_to_SV(jbv->val.binary.data);
      29                 :             : 
      30                 :          49 :         case jbvNumeric:
      31                 :             :             {
      32                 :          49 :                 char       *str = DatumGetCString(DirectFunctionCall1(numeric_out,
      33                 :             :                                                                       NumericGetDatum(jbv->val.numeric)));
      34                 :          49 :                 SV         *result = newSVnv(SvNV(cstr2sv(str)));
      35                 :             : 
      36                 :          49 :                 pfree(str);
      37                 :          49 :                 return result;
      38                 :             :             }
      39                 :             : 
      40                 :          14 :         case jbvString:
      41                 :             :             {
      42                 :          14 :                 char       *str = pnstrdup(jbv->val.string.val,
      43                 :          14 :                                            jbv->val.string.len);
      44                 :          14 :                 SV         *result = cstr2sv(str);
      45                 :             : 
      46                 :          14 :                 pfree(str);
      47                 :          14 :                 return result;
      48                 :             :             }
      49                 :             : 
      50                 :           4 :         case jbvBool:
      51         [ +  + ]:           4 :             return newSVnv(SvNV(jbv->val.boolean ? &PL_sv_yes : &PL_sv_no));
      52                 :             : 
      53                 :           8 :         case jbvNull:
      54                 :           8 :             return newSV(0);
      55                 :             : 
      56                 :           0 :         default:
      57         [ #  # ]:           0 :             elog(ERROR, "unexpected jsonb value type: %d", jbv->type);
      58                 :             :             return NULL;
      59                 :             :     }
      60                 :             : }
      61                 :             : 
      62                 :             : static SV  *
      63                 :          57 : Jsonb_to_SV(JsonbContainer *jsonb)
      64                 :             : {
      65                 :          57 :     dTHX;
      66                 :             :     JsonbValue  v;
      67                 :             :     JsonbIterator *it;
      68                 :             :     JsonbIteratorToken r;
      69                 :             : 
      70                 :             :     /* this can recurse via JsonbValue_to_SV() */
      71                 :          57 :     check_stack_depth();
      72                 :             : 
      73                 :          57 :     it = JsonbIteratorInit(jsonb);
      74                 :          57 :     r = JsonbIteratorNext(&it, &v, true);
      75                 :             : 
      76      [ +  +  - ]:          57 :     switch (r)
      77                 :             :     {
      78                 :          39 :         case WJB_BEGIN_ARRAY:
      79         [ +  + ]:          39 :             if (v.val.array.rawScalar)
      80                 :             :             {
      81                 :             :                 JsonbValue  tmp;
      82                 :             : 
      83   [ +  -  +  - ]:          38 :                 if ((r = JsonbIteratorNext(&it, &v, true)) != WJB_ELEM ||
      84         [ -  + ]:          38 :                     (r = JsonbIteratorNext(&it, &tmp, true)) != WJB_END_ARRAY ||
      85                 :          19 :                     (r = JsonbIteratorNext(&it, &tmp, true)) != WJB_DONE)
      86         [ #  # ]:           0 :                     elog(ERROR, "unexpected jsonb token: %d", r);
      87                 :             : 
      88                 :          19 :                 return JsonbValue_to_SV(&v);
      89                 :             :             }
      90                 :             :             else
      91                 :             :             {
      92                 :          20 :                 AV         *av = newAV();
      93                 :             : 
      94         [ +  + ]:         104 :                 while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
      95                 :             :                 {
      96         [ +  + ]:          64 :                     if (r == WJB_ELEM)
      97                 :          44 :                         av_push(av, JsonbValue_to_SV(&v));
      98                 :             :                 }
      99                 :             : 
     100                 :          20 :                 return newRV((SV *) av);
     101                 :             :             }
     102                 :             : 
     103                 :          18 :         case WJB_BEGIN_OBJECT:
     104                 :             :             {
     105                 :          18 :                 HV         *hv = newHV();
     106                 :             : 
     107         [ +  + ]:          72 :                 while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
     108                 :             :                 {
     109         [ +  + ]:          36 :                     if (r == WJB_KEY)
     110                 :             :                     {
     111                 :             :                         /* json key in v, json value in val */
     112                 :             :                         JsonbValue  val;
     113                 :             : 
     114         [ +  - ]:          18 :                         if (JsonbIteratorNext(&it, &val, true) == WJB_VALUE)
     115                 :             :                         {
     116                 :          18 :                             SV         *value = JsonbValue_to_SV(&val);
     117                 :             : 
     118                 :          18 :                             (void) hv_store(hv,
     119                 :             :                                             v.val.string.val, v.val.string.len,
     120                 :             :                                             value, 0);
     121                 :             :                         }
     122                 :             :                     }
     123                 :             :                 }
     124                 :             : 
     125                 :          18 :                 return newRV((SV *) hv);
     126                 :             :             }
     127                 :             : 
     128                 :           0 :         default:
     129         [ #  # ]:           0 :             elog(ERROR, "unexpected jsonb token: %d", r);
     130                 :             :             return NULL;
     131                 :             :     }
     132                 :             : }
     133                 :             : 
     134                 :             : static void
     135                 :          22 : AV_to_JsonbValue(AV *in, JsonbInState *jsonb_state)
     136                 :             : {
     137                 :          22 :     dTHX;
     138                 :          22 :     SSize_t     pcount = av_len(in) + 1;
     139                 :             :     SSize_t     i;
     140                 :             : 
     141                 :          22 :     pushJsonbValue(jsonb_state, WJB_BEGIN_ARRAY, NULL);
     142                 :             : 
     143         [ +  + ]:          70 :     for (i = 0; i < pcount; i++)
     144                 :             :     {
     145                 :          48 :         SV        **value = av_fetch(in, i, FALSE);
     146                 :             : 
     147         [ +  - ]:          48 :         if (value)
     148                 :          48 :             SV_to_JsonbValue(*value, jsonb_state, true);
     149                 :             :     }
     150                 :             : 
     151                 :          22 :     pushJsonbValue(jsonb_state, WJB_END_ARRAY, NULL);
     152                 :          22 : }
     153                 :             : 
     154                 :             : static void
     155                 :          28 : HV_to_JsonbValue(HV *obj, JsonbInState *jsonb_state)
     156                 :             : {
     157                 :          28 :     dTHX;
     158                 :             :     JsonbValue  key;
     159                 :             :     SV         *val;
     160                 :             :     char       *kstr;
     161                 :             :     I32         klen;
     162                 :             : 
     163                 :          28 :     key.type = jbvString;
     164                 :             : 
     165                 :          28 :     pushJsonbValue(jsonb_state, WJB_BEGIN_OBJECT, NULL);
     166                 :             : 
     167                 :          28 :     (void) hv_iterinit(obj);
     168                 :             : 
     169         [ +  + ]:          64 :     while ((val = hv_iternextsv(obj, &kstr, &klen)))
     170                 :             :     {
     171                 :          36 :         key.val.string.val = pnstrdup(kstr, klen);
     172                 :          36 :         key.val.string.len = klen;
     173                 :          36 :         pushJsonbValue(jsonb_state, WJB_KEY, &key);
     174                 :          36 :         SV_to_JsonbValue(val, jsonb_state, false);
     175                 :             :     }
     176                 :             : 
     177                 :          28 :     pushJsonbValue(jsonb_state, WJB_END_OBJECT, NULL);
     178                 :          28 : }
     179                 :             : 
     180                 :             : static void
     181                 :         147 : SV_to_JsonbValue(SV *in, JsonbInState *jsonb_state, bool is_elem)
     182                 :             : {
     183                 :         147 :     dTHX;
     184                 :             :     JsonbValue  out;            /* result */
     185                 :             : 
     186                 :             :     /* this can recurse via AV_to_JsonbValue() or HV_to_JsonbValue() */
     187                 :         147 :     check_stack_depth();
     188                 :             : 
     189                 :             :     /* Dereference references recursively. */
     190         [ +  + ]:         197 :     while (SvROK(in))
     191                 :             :     {
     192                 :             :         /*
     193                 :             :          * It's possible for circular references to make this an infinite
     194                 :             :          * loop.  Checking for such a situation seems like much more trouble
     195                 :             :          * than it's worth, but let's provide a way to break out of the loop.
     196                 :             :          */
     197         [ -  + ]:          50 :         CHECK_FOR_INTERRUPTS();
     198                 :          50 :         in = SvRV(in);
     199                 :             :     }
     200                 :             : 
     201      [ +  +  + ]:         147 :     switch (SvTYPE(in))
     202                 :             :     {
     203                 :          22 :         case SVt_PVAV:
     204                 :          22 :             AV_to_JsonbValue((AV *) in, jsonb_state);
     205                 :          50 :             return;
     206                 :             : 
     207                 :          28 :         case SVt_PVHV:
     208                 :          28 :             HV_to_JsonbValue((HV *) in, jsonb_state);
     209                 :          28 :             return;
     210                 :             : 
     211                 :          97 :         default:
     212         [ +  + ]:          97 :             if (!SvOK(in))
     213                 :             :             {
     214                 :          12 :                 out.type = jbvNull;
     215                 :             :             }
     216         [ +  + ]:          85 :             else if (SvUOK(in))
     217                 :             :             {
     218                 :             :                 /*
     219                 :             :                  * If UV is >=64 bits, we have no better way to make this
     220                 :             :                  * happen than converting to text and back.  Given the low
     221                 :             :                  * usage of UV in Perl code, it's not clear it's worth working
     222                 :             :                  * hard to provide alternate code paths.
     223                 :             :                  */
     224                 :           2 :                 const char *strval = SvPV_nolen(in);
     225                 :             : 
     226                 :           2 :                 out.type = jbvNumeric;
     227                 :           2 :                 out.val.numeric =
     228                 :           2 :                     DatumGetNumeric(DirectFunctionCall3(numeric_in,
     229                 :             :                                                         CStringGetDatum(strval),
     230                 :             :                                                         ObjectIdGetDatum(InvalidOid),
     231                 :             :                                                         Int32GetDatum(-1)));
     232                 :             :             }
     233         [ +  + ]:          83 :             else if (SvIOK(in))
     234                 :             :             {
     235                 :          10 :                 IV          ival = SvIV(in);
     236                 :             : 
     237                 :          10 :                 out.type = jbvNumeric;
     238                 :          10 :                 out.val.numeric = int64_to_numeric(ival);
     239                 :             :             }
     240         [ +  + ]:          73 :             else if (SvNOK(in))
     241                 :             :             {
     242                 :          53 :                 double      nval = SvNV(in);
     243                 :             : 
     244                 :             :                 /*
     245                 :             :                  * jsonb doesn't allow infinity or NaN (per JSON
     246                 :             :                  * specification), but the numeric type that is used for the
     247                 :             :                  * storage accepts those, so we have to reject them here
     248                 :             :                  * explicitly.
     249                 :             :                  */
     250         [ +  + ]:          53 :                 if (isinf(nval))
     251         [ +  - ]:           1 :                     ereport(ERROR,
     252                 :             :                             (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     253                 :             :                              errmsg("cannot convert infinity to jsonb")));
     254         [ -  + ]:          52 :                 if (isnan(nval))
     255         [ #  # ]:           0 :                     ereport(ERROR,
     256                 :             :                             (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     257                 :             :                              errmsg("cannot convert NaN to jsonb")));
     258                 :             : 
     259                 :          52 :                 out.type = jbvNumeric;
     260                 :          52 :                 out.val.numeric =
     261                 :          52 :                     DatumGetNumeric(DirectFunctionCall1(float8_numeric,
     262                 :             :                                                         Float8GetDatum(nval)));
     263                 :             :             }
     264         [ +  - ]:          20 :             else if (SvPOK(in))
     265                 :             :             {
     266                 :          20 :                 out.type = jbvString;
     267                 :          20 :                 out.val.string.val = sv2cstr(in);
     268                 :          20 :                 out.val.string.len = strlen(out.val.string.val);
     269                 :             :             }
     270                 :             :             else
     271                 :             :             {
     272                 :             :                 /*
     273                 :             :                  * XXX It might be nice if we could include the Perl type in
     274                 :             :                  * the error message.
     275                 :             :                  */
     276         [ #  # ]:           0 :                 ereport(ERROR,
     277                 :             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     278                 :             :                          errmsg("cannot transform this Perl type to jsonb")));
     279                 :             :             }
     280                 :             :     }
     281                 :             : 
     282         [ +  + ]:          96 :     if (jsonb_state->parseState)
     283                 :             :     {
     284                 :             :         /* We're in an array or object, so push value as element or field. */
     285         [ +  + ]:          74 :         pushJsonbValue(jsonb_state, is_elem ? WJB_ELEM : WJB_VALUE, &out);
     286                 :             :     }
     287                 :             :     else
     288                 :             :     {
     289                 :             :         /*
     290                 :             :          * We are at top level, so it's a raw scalar.  If we just shove the
     291                 :             :          * scalar value into jsonb_state->result, JsonbValueToJsonb will take
     292                 :             :          * care of wrapping it into a dummy array.
     293                 :             :          */
     294                 :          22 :         jsonb_state->result = palloc_object(JsonbValue);
     295                 :          22 :         memcpy(jsonb_state->result, &out, sizeof(JsonbValue));
     296                 :             :     }
     297                 :             : }
     298                 :             : 
     299                 :             : 
     300                 :           4 : PG_FUNCTION_INFO_V1(jsonb_to_plperl);
     301                 :             : 
     302                 :             : Datum
     303                 :          51 : jsonb_to_plperl(PG_FUNCTION_ARGS)
     304                 :             : {
     305                 :          51 :     dTHX;
     306                 :          51 :     Jsonb      *in = PG_GETARG_JSONB_P(0);
     307                 :          51 :     SV         *sv = Jsonb_to_SV(&in->root);
     308                 :             : 
     309                 :          51 :     return PointerGetDatum(sv);
     310                 :             : }
     311                 :             : 
     312                 :             : 
     313                 :           4 : PG_FUNCTION_INFO_V1(plperl_to_jsonb);
     314                 :             : 
     315                 :             : Datum
     316                 :          63 : plperl_to_jsonb(PG_FUNCTION_ARGS)
     317                 :             : {
     318                 :          63 :     dTHX;
     319                 :          63 :     SV         *in = (SV *) PG_GETARG_POINTER(0);
     320                 :          63 :     JsonbInState jsonb_state = {0};
     321                 :             : 
     322                 :          63 :     SV_to_JsonbValue(in, &jsonb_state, true);
     323                 :          62 :     PG_RETURN_JSONB_P(JsonbValueToJsonb(jsonb_state.result));
     324                 :             : }
        

Generated by: LCOV version 2.0-1