LCOV - code coverage report
Current view: top level - contrib/jsonb_plperl - jsonb_plperl.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 94.1 % 135 127
Test Date: 2026-09-03 01:15:55 Functions: 100.0 % 10 10
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 68.8 % 80 55

             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                 :          23 : AV_to_JsonbValue(AV *in, JsonbInState *jsonb_state)
     136                 :             : {
     137                 :          23 :     dTHX;
     138                 :          23 :     Size_t      pcount = av_count(in);
     139                 :             : 
     140                 :          23 :     pushJsonbValue(jsonb_state, WJB_BEGIN_ARRAY, NULL);
     141                 :             : 
     142         [ +  + ]:          73 :     for (Size_t i = 0; i < pcount; i++)
     143                 :             :     {
     144                 :          50 :         SV        **value = av_fetch(in, i, FALSE);
     145                 :             : 
     146         [ +  - ]:          50 :         if (value)
     147                 :          50 :             SV_to_JsonbValue(*value, jsonb_state, true);
     148                 :             :     }
     149                 :             : 
     150                 :          23 :     pushJsonbValue(jsonb_state, WJB_END_ARRAY, NULL);
     151                 :          23 : }
     152                 :             : 
     153                 :             : static void
     154                 :          29 : HV_to_JsonbValue(HV *obj, JsonbInState *jsonb_state)
     155                 :             : {
     156                 :          29 :     dTHX;
     157                 :             :     JsonbValue  key;
     158                 :             :     HE         *he;
     159                 :             : 
     160                 :          29 :     key.type = jbvString;
     161                 :             : 
     162                 :          29 :     pushJsonbValue(jsonb_state, WJB_BEGIN_OBJECT, NULL);
     163                 :             : 
     164                 :          29 :     (void) hv_iterinit(obj);
     165                 :             : 
     166         [ +  + ]:          68 :     while ((he = hv_iternext(obj)))
     167                 :             :     {
     168                 :          39 :         char       *k = hek2cstr(he);
     169                 :          39 :         SV         *val = hv_iterval(obj, he);
     170                 :             : 
     171                 :          39 :         key.val.string.val = k;
     172                 :          39 :         key.val.string.len = strlen(k);
     173                 :          39 :         pushJsonbValue(jsonb_state, WJB_KEY, &key);
     174                 :          39 :         SV_to_JsonbValue(val, jsonb_state, false);
     175                 :             :     }
     176                 :             : 
     177                 :          29 :     pushJsonbValue(jsonb_state, WJB_END_OBJECT, NULL);
     178                 :          29 : }
     179                 :             : 
     180                 :             : static void
     181                 :         154 : SV_to_JsonbValue(SV *in, JsonbInState *jsonb_state, bool is_elem)
     182                 :             : {
     183                 :         154 :     dTHX;
     184                 :             :     JsonbValue  out;            /* result */
     185                 :             : 
     186                 :             :     /* this can recurse via AV_to_JsonbValue() or HV_to_JsonbValue() */
     187                 :         154 :     check_stack_depth();
     188                 :             : 
     189                 :             :     /* Dereference references recursively. */
     190         [ +  - ]:         154 :     plperl_materialize_sv(in);
     191   [ +  -  +  + ]:         206 :     while (in && SvROK(in))
     192                 :             :     {
     193                 :             :         /*
     194                 :             :          * It's possible for circular references to make this an infinite
     195                 :             :          * loop.  Checking for such a situation seems like much more trouble
     196                 :             :          * than it's worth, but let's provide a way to break out of the loop.
     197                 :             :          */
     198         [ -  + ]:          52 :         CHECK_FOR_INTERRUPTS();
     199                 :          52 :         in = SvRV(in);
     200         [ +  - ]:          52 :         plperl_materialize_sv(in);
     201                 :             :     }
     202                 :             : 
     203         [ -  + ]:         154 :     if (!in)
     204                 :             :     {
     205                 :           0 :         out.type = jbvNull;
     206                 :             :     }
     207                 :             :     else
     208                 :             :     {
     209      [ +  +  + ]:         154 :         switch (SvTYPE(in))
     210                 :             :         {
     211                 :          23 :             case SVt_PVAV:
     212                 :          23 :                 AV_to_JsonbValue((AV *) in, jsonb_state);
     213                 :          52 :                 return;
     214                 :             : 
     215                 :          29 :             case SVt_PVHV:
     216                 :          29 :                 HV_to_JsonbValue((HV *) in, jsonb_state);
     217                 :          29 :                 return;
     218                 :             : 
     219                 :         102 :             default:
     220         [ +  + ]:         102 :                 if (!SvOK(in))
     221                 :             :                 {
     222                 :          13 :                     out.type = jbvNull;
     223                 :             :                 }
     224         [ +  + ]:          89 :                 else if (SvUOK(in))
     225                 :             :                 {
     226                 :             :                     /*
     227                 :             :                      * If UV is >=64 bits, we have no better way to make this
     228                 :             :                      * happen than converting to text and back.  Given the low
     229                 :             :                      * usage of UV in Perl code, it's not clear it's worth
     230                 :             :                      * working hard to provide alternate code paths.
     231                 :             :                      */
     232                 :           2 :                     const char *strval = SvPV_nolen(in);
     233                 :             : 
     234                 :           2 :                     out.type = jbvNumeric;
     235                 :           2 :                     out.val.numeric =
     236                 :           2 :                         DatumGetNumeric(DirectFunctionCall3(numeric_in,
     237                 :             :                                                             CStringGetDatum(strval),
     238                 :             :                                                             ObjectIdGetDatum(InvalidOid),
     239                 :             :                                                             Int32GetDatum(-1)));
     240                 :             :                 }
     241         [ +  + ]:          87 :                 else if (SvIOK(in))
     242                 :             :                 {
     243                 :          12 :                     IV          ival = SvIV(in);
     244                 :             : 
     245                 :          12 :                     out.type = jbvNumeric;
     246                 :          12 :                     out.val.numeric = int64_to_numeric(ival);
     247                 :             :                 }
     248         [ +  + ]:          75 :                 else if (SvNOK(in))
     249                 :             :                 {
     250                 :          53 :                     double      nval = SvNV(in);
     251                 :             : 
     252                 :             :                     /*
     253                 :             :                      * jsonb doesn't allow infinity or NaN (per JSON
     254                 :             :                      * specification), but the numeric type that is used for
     255                 :             :                      * the storage accepts those, so we have to reject them
     256                 :             :                      * here explicitly.
     257                 :             :                      */
     258         [ +  + ]:          53 :                     if (isinf(nval))
     259         [ +  - ]:           1 :                         ereport(ERROR,
     260                 :             :                                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     261                 :             :                                  errmsg("cannot convert infinity to jsonb")));
     262         [ -  + ]:          52 :                     if (isnan(nval))
     263         [ #  # ]:           0 :                         ereport(ERROR,
     264                 :             :                                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     265                 :             :                                  errmsg("cannot convert NaN to jsonb")));
     266                 :             : 
     267                 :          52 :                     out.type = jbvNumeric;
     268                 :          52 :                     out.val.numeric =
     269                 :          52 :                         DatumGetNumeric(DirectFunctionCall1(float8_numeric,
     270                 :             :                                                             Float8GetDatum(nval)));
     271                 :             :                 }
     272         [ +  - ]:          22 :                 else if (SvPOK(in))
     273                 :             :                 {
     274                 :          22 :                     out.type = jbvString;
     275                 :          22 :                     out.val.string.val = sv2cstr(in);
     276                 :          22 :                     out.val.string.len = strlen(out.val.string.val);
     277                 :             :                 }
     278                 :             :                 else
     279                 :             :                 {
     280                 :             :                     /*
     281                 :             :                      * XXX It might be nice if we could include the Perl type
     282                 :             :                      * in the error message.
     283                 :             :                      */
     284         [ #  # ]:           0 :                     ereport(ERROR,
     285                 :             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     286                 :             :                              errmsg("cannot transform this Perl type to jsonb")));
     287                 :             :                 }
     288                 :             :         }
     289                 :             :     }
     290                 :             : 
     291         [ +  + ]:         101 :     if (jsonb_state->parseState)
     292                 :             :     {
     293                 :             :         /* We're in an array or object, so push value as element or field. */
     294         [ +  + ]:          79 :         pushJsonbValue(jsonb_state, is_elem ? WJB_ELEM : WJB_VALUE, &out);
     295                 :             :     }
     296                 :             :     else
     297                 :             :     {
     298                 :             :         /*
     299                 :             :          * We are at top level, so it's a raw scalar.  If we just shove the
     300                 :             :          * scalar value into jsonb_state->result, JsonbValueToJsonb will take
     301                 :             :          * care of wrapping it into a dummy array.
     302                 :             :          */
     303                 :          22 :         jsonb_state->result = palloc_object(JsonbValue);
     304                 :          22 :         memcpy(jsonb_state->result, &out, sizeof(JsonbValue));
     305                 :             :     }
     306                 :             : }
     307                 :             : 
     308                 :             : 
     309                 :           4 : PG_FUNCTION_INFO_V1(jsonb_to_plperl);
     310                 :             : 
     311                 :             : Datum
     312                 :          51 : jsonb_to_plperl(PG_FUNCTION_ARGS)
     313                 :             : {
     314                 :          51 :     dTHX;
     315                 :          51 :     Jsonb      *in = PG_GETARG_JSONB_P(0);
     316                 :          51 :     SV         *sv = Jsonb_to_SV(&in->root);
     317                 :             : 
     318                 :          51 :     return PointerGetDatum(sv);
     319                 :             : }
     320                 :             : 
     321                 :             : 
     322                 :           4 : PG_FUNCTION_INFO_V1(plperl_to_jsonb);
     323                 :             : 
     324                 :             : Datum
     325                 :          65 : plperl_to_jsonb(PG_FUNCTION_ARGS)
     326                 :             : {
     327                 :          65 :     dTHX;
     328                 :          65 :     SV         *in = (SV *) PG_GETARG_POINTER(0);
     329                 :          65 :     JsonbInState jsonb_state = {0};
     330                 :             : 
     331                 :          65 :     SV_to_JsonbValue(in, &jsonb_state, true);
     332                 :          64 :     PG_RETURN_JSONB_P(JsonbValueToJsonb(jsonb_state.result));
     333                 :             : }
        

Generated by: LCOV version 2.0-1