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-06-30 20:16:43 Functions: 100.0 % 10 10
Legend: Lines:     hit not hit

            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