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

Generated by: LCOV version 2.3.2-1