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