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