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

Generated by: LCOV version 1.13