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 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 *
JsonbValue_to_SV(JsonbValue * jbv)18 JsonbValue_to_SV(JsonbValue *jbv)
19 {
20 dTHX;
21
22 switch (jbv->type)
23 {
24 case jbvBinary:
25 return Jsonb_to_SV(jbv->val.binary.data);
26
27 case jbvNumeric:
28 {
29 char *str = DatumGetCString(DirectFunctionCall1(numeric_out,
30 NumericGetDatum(jbv->val.numeric)));
31 SV *result = newSVnv(SvNV(cstr2sv(str)));
32
33 pfree(str);
34 return result;
35 }
36
37 case jbvString:
38 {
39 char *str = pnstrdup(jbv->val.string.val,
40 jbv->val.string.len);
41 SV *result = cstr2sv(str);
42
43 pfree(str);
44 return result;
45 }
46
47 case jbvBool:
48 return newSVnv(SvNV(jbv->val.boolean ? &PL_sv_yes : &PL_sv_no));
49
50 case jbvNull:
51 return newSV(0);
52
53 default:
54 elog(ERROR, "unexpected jsonb value type: %d", jbv->type);
55 return NULL;
56 }
57 }
58
59 static SV *
Jsonb_to_SV(JsonbContainer * jsonb)60 Jsonb_to_SV(JsonbContainer *jsonb)
61 {
62 dTHX;
63 JsonbValue v;
64 JsonbIterator *it;
65 JsonbIteratorToken r;
66
67 it = JsonbIteratorInit(jsonb);
68 r = JsonbIteratorNext(&it, &v, true);
69
70 switch (r)
71 {
72 case WJB_BEGIN_ARRAY:
73 if (v.val.array.rawScalar)
74 {
75 JsonbValue tmp;
76
77 if ((r = JsonbIteratorNext(&it, &v, true)) != WJB_ELEM ||
78 (r = JsonbIteratorNext(&it, &tmp, true)) != WJB_END_ARRAY ||
79 (r = JsonbIteratorNext(&it, &tmp, true)) != WJB_DONE)
80 elog(ERROR, "unexpected jsonb token: %d", r);
81
82 return JsonbValue_to_SV(&v);
83 }
84 else
85 {
86 AV *av = newAV();
87
88 while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
89 {
90 if (r == WJB_ELEM)
91 av_push(av, JsonbValue_to_SV(&v));
92 }
93
94 return newRV((SV *) av);
95 }
96
97 case WJB_BEGIN_OBJECT:
98 {
99 HV *hv = newHV();
100
101 while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
102 {
103 if (r == WJB_KEY)
104 {
105 /* json key in v, json value in val */
106 JsonbValue val;
107
108 if (JsonbIteratorNext(&it, &val, true) == WJB_VALUE)
109 {
110 SV *value = JsonbValue_to_SV(&val);
111
112 (void) hv_store(hv,
113 v.val.string.val, v.val.string.len,
114 value, 0);
115 }
116 }
117 }
118
119 return newRV((SV *) hv);
120 }
121
122 default:
123 elog(ERROR, "unexpected jsonb token: %d", r);
124 return NULL;
125 }
126 }
127
128 static JsonbValue *
AV_to_JsonbValue(AV * in,JsonbParseState ** jsonb_state)129 AV_to_JsonbValue(AV *in, JsonbParseState **jsonb_state)
130 {
131 dTHX;
132 SSize_t pcount = av_len(in) + 1;
133 SSize_t i;
134
135 pushJsonbValue(jsonb_state, WJB_BEGIN_ARRAY, NULL);
136
137 for (i = 0; i < pcount; i++)
138 {
139 SV **value = av_fetch(in, i, FALSE);
140
141 if (value)
142 (void) SV_to_JsonbValue(*value, jsonb_state, true);
143 }
144
145 return pushJsonbValue(jsonb_state, WJB_END_ARRAY, NULL);
146 }
147
148 static JsonbValue *
HV_to_JsonbValue(HV * obj,JsonbParseState ** jsonb_state)149 HV_to_JsonbValue(HV *obj, JsonbParseState **jsonb_state)
150 {
151 dTHX;
152 JsonbValue key;
153 SV *val;
154 char *kstr;
155 I32 klen;
156
157 key.type = jbvString;
158
159 pushJsonbValue(jsonb_state, WJB_BEGIN_OBJECT, NULL);
160
161 (void) hv_iterinit(obj);
162
163 while ((val = hv_iternextsv(obj, &kstr, &klen)))
164 {
165 key.val.string.val = pnstrdup(kstr, klen);
166 key.val.string.len = klen;
167 pushJsonbValue(jsonb_state, WJB_KEY, &key);
168 (void) SV_to_JsonbValue(val, jsonb_state, false);
169 }
170
171 return pushJsonbValue(jsonb_state, WJB_END_OBJECT, NULL);
172 }
173
174 static JsonbValue *
SV_to_JsonbValue(SV * in,JsonbParseState ** jsonb_state,bool is_elem)175 SV_to_JsonbValue(SV *in, JsonbParseState **jsonb_state, bool is_elem)
176 {
177 dTHX;
178 JsonbValue out; /* result */
179
180 /* Dereference references recursively. */
181 while (SvROK(in))
182 in = SvRV(in);
183
184 switch (SvTYPE(in))
185 {
186 case SVt_PVAV:
187 return AV_to_JsonbValue((AV *) in, jsonb_state);
188
189 case SVt_PVHV:
190 return HV_to_JsonbValue((HV *) in, jsonb_state);
191
192 default:
193 if (!SvOK(in))
194 {
195 out.type = jbvNull;
196 }
197 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 const char *strval = SvPV_nolen(in);
206
207 out.type = jbvNumeric;
208 out.val.numeric =
209 DatumGetNumeric(DirectFunctionCall3(numeric_in,
210 CStringGetDatum(strval),
211 ObjectIdGetDatum(InvalidOid),
212 Int32GetDatum(-1)));
213 }
214 else if (SvIOK(in))
215 {
216 IV ival = SvIV(in);
217
218 out.type = jbvNumeric;
219 out.val.numeric =
220 DatumGetNumeric(DirectFunctionCall1(int8_numeric,
221 Int64GetDatum((int64) ival)));
222 }
223 else if (SvNOK(in))
224 {
225 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 if (isinf(nval))
236 ereport(ERROR,
237 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
238 (errmsg("cannot convert infinity to jsonb"))));
239 if (isnan(nval))
240 ereport(ERROR,
241 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
242 (errmsg("cannot convert NaN to jsonb"))));
243
244 out.type = jbvNumeric;
245 out.val.numeric =
246 DatumGetNumeric(DirectFunctionCall1(float8_numeric,
247 Float8GetDatum(nval)));
248 }
249 else if (SvPOK(in))
250 {
251 out.type = jbvString;
252 out.val.string.val = sv2cstr(in);
253 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 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 return *jsonb_state
270 ? pushJsonbValue(jsonb_state, is_elem ? WJB_ELEM : WJB_VALUE, &out)
271 : memcpy(palloc(sizeof(JsonbValue)), &out, sizeof(JsonbValue));
272 }
273
274
275 PG_FUNCTION_INFO_V1(jsonb_to_plperl);
276
277 Datum
jsonb_to_plperl(PG_FUNCTION_ARGS)278 jsonb_to_plperl(PG_FUNCTION_ARGS)
279 {
280 dTHX;
281 Jsonb *in = PG_GETARG_JSONB_P(0);
282 SV *sv = Jsonb_to_SV(&in->root);
283
284 return PointerGetDatum(sv);
285 }
286
287
288 PG_FUNCTION_INFO_V1(plperl_to_jsonb);
289
290 Datum
plperl_to_jsonb(PG_FUNCTION_ARGS)291 plperl_to_jsonb(PG_FUNCTION_ARGS)
292 {
293 dTHX;
294 JsonbParseState *jsonb_state = NULL;
295 SV *in = (SV *) PG_GETARG_POINTER(0);
296 JsonbValue *out = SV_to_JsonbValue(in, &jsonb_state, true);
297 Jsonb *result = JsonbValueToJsonb(out);
298
299 PG_RETURN_JSONB_P(result);
300 }
301