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