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