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