PostgreSQL Source Code git master
jsonb_plperl.c
Go to the documentation of this file.
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
11 .name = "jsonb_plperl",
12 .version = PG_VERSION
13);
14
15static SV *Jsonb_to_SV(JsonbContainer *jsonb);
16static void SV_to_JsonbValue(SV *obj, JsonbInState *ps, bool is_elem);
17
18
19static SV *
21{
22 dTHX;
23
24 switch (jbv->type)
25 {
26 case jbvBinary:
27 return Jsonb_to_SV(jbv->val.binary.data);
28
29 case jbvNumeric:
30 {
32 NumericGetDatum(jbv->val.numeric)));
33 SV *result = newSVnv(SvNV(cstr2sv(str)));
34
35 pfree(str);
36 return result;
37 }
38
39 case jbvString:
40 {
41 char *str = pnstrdup(jbv->val.string.val,
42 jbv->val.string.len);
43 SV *result = cstr2sv(str);
44
45 pfree(str);
46 return result;
47 }
48
49 case jbvBool:
50 return newSVnv(SvNV(jbv->val.boolean ? &PL_sv_yes : &PL_sv_no));
51
52 case jbvNull:
53 return newSV(0);
54
55 default:
56 elog(ERROR, "unexpected jsonb value type: %d", jbv->type);
57 return NULL;
58 }
59}
60
61static SV *
63{
64 dTHX;
65 JsonbValue v;
66 JsonbIterator *it;
68
69 it = JsonbIteratorInit(jsonb);
70 r = JsonbIteratorNext(&it, &v, true);
71
72 switch (r)
73 {
74 case WJB_BEGIN_ARRAY:
75 if (v.val.array.rawScalar)
76 {
77 JsonbValue tmp;
78
79 if ((r = JsonbIteratorNext(&it, &v, true)) != WJB_ELEM ||
80 (r = JsonbIteratorNext(&it, &tmp, true)) != WJB_END_ARRAY ||
81 (r = JsonbIteratorNext(&it, &tmp, true)) != WJB_DONE)
82 elog(ERROR, "unexpected jsonb token: %d", r);
83
84 return JsonbValue_to_SV(&v);
85 }
86 else
87 {
88 AV *av = newAV();
89
90 while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
91 {
92 if (r == WJB_ELEM)
93 av_push(av, JsonbValue_to_SV(&v));
94 }
95
96 return newRV((SV *) av);
97 }
98
100 {
101 HV *hv = newHV();
102
103 while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
104 {
105 if (r == WJB_KEY)
106 {
107 /* json key in v, json value in val */
109
110 if (JsonbIteratorNext(&it, &val, true) == WJB_VALUE)
111 {
112 SV *value = JsonbValue_to_SV(&val);
113
114 (void) hv_store(hv,
115 v.val.string.val, v.val.string.len,
116 value, 0);
117 }
118 }
119 }
120
121 return newRV((SV *) hv);
122 }
123
124 default:
125 elog(ERROR, "unexpected jsonb token: %d", r);
126 return NULL;
127 }
128}
129
130static void
131AV_to_JsonbValue(AV *in, JsonbInState *jsonb_state)
132{
133 dTHX;
134 SSize_t pcount = av_len(in) + 1;
135 SSize_t i;
136
137 pushJsonbValue(jsonb_state, WJB_BEGIN_ARRAY, NULL);
138
139 for (i = 0; i < pcount; i++)
140 {
141 SV **value = av_fetch(in, i, FALSE);
142
143 if (value)
144 SV_to_JsonbValue(*value, jsonb_state, true);
145 }
146
147 pushJsonbValue(jsonb_state, WJB_END_ARRAY, NULL);
148}
149
150static void
151HV_to_JsonbValue(HV *obj, JsonbInState *jsonb_state)
152{
153 dTHX;
155 SV *val;
156 char *kstr;
157 I32 klen;
158
159 key.type = jbvString;
160
161 pushJsonbValue(jsonb_state, WJB_BEGIN_OBJECT, NULL);
162
163 (void) hv_iterinit(obj);
164
165 while ((val = hv_iternextsv(obj, &kstr, &klen)))
166 {
167 key.val.string.val = pnstrdup(kstr, klen);
168 key.val.string.len = klen;
169 pushJsonbValue(jsonb_state, WJB_KEY, &key);
170 SV_to_JsonbValue(val, jsonb_state, false);
171 }
172
173 pushJsonbValue(jsonb_state, WJB_END_OBJECT, NULL);
174}
175
176static void
177SV_to_JsonbValue(SV *in, JsonbInState *jsonb_state, bool is_elem)
178{
179 dTHX;
180 JsonbValue out; /* result */
181
182 /* Dereference references recursively. */
183 while (SvROK(in))
184 in = SvRV(in);
185
186 switch (SvTYPE(in))
187 {
188 case SVt_PVAV:
189 AV_to_JsonbValue((AV *) in, jsonb_state);
190 return;
191
192 case SVt_PVHV:
193 HV_to_JsonbValue((HV *) in, jsonb_state);
194 return;
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 =
214 CStringGetDatum(strval),
216 Int32GetDatum(-1)));
217 }
218 else if (SvIOK(in))
219 {
220 IV ival = SvIV(in);
221
222 out.type = jbvNumeric;
223 out.val.numeric = int64_to_numeric(ival);
224 }
225 else if (SvNOK(in))
226 {
227 double nval = SvNV(in);
228
229 /*
230 * jsonb doesn't allow infinity or NaN (per JSON
231 * specification), but the numeric type that is used for the
232 * storage accepts those, so we have to reject them here
233 * explicitly.
234 */
235 if (isinf(nval))
237 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
238 errmsg("cannot convert infinity to jsonb")));
239 if (isnan(nval))
241 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
242 errmsg("cannot convert NaN to jsonb")));
243
244 out.type = jbvNumeric;
245 out.val.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 */
262 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
263 errmsg("cannot transform this Perl type to jsonb")));
264 }
265 }
266
267 if (jsonb_state->parseState)
268 {
269 /* We're in an array or object, so push value as element or field. */
270 pushJsonbValue(jsonb_state, is_elem ? WJB_ELEM : WJB_VALUE, &out);
271 }
272 else
273 {
274 /*
275 * We are at top level, so it's a raw scalar. If we just shove the
276 * scalar value into jsonb_state->result, JsonbValueToJsonb will take
277 * care of wrapping it into a dummy array.
278 */
279 jsonb_state->result = palloc_object(JsonbValue);
280 memcpy(jsonb_state->result, &out, sizeof(JsonbValue));
281 }
282}
283
284
286
287Datum
289{
290 dTHX;
291 Jsonb *in = PG_GETARG_JSONB_P(0);
292 SV *sv = Jsonb_to_SV(&in->root);
293
294 return PointerGetDatum(sv);
295}
296
297
299
300Datum
302{
303 dTHX;
304 SV *in = (SV *) PG_GETARG_POINTER(0);
305 JsonbInState jsonb_state = {0};
306
307 SV_to_JsonbValue(in, &jsonb_state, true);
309}
Datum float8_numeric(PG_FUNCTION_ARGS)
Definition: numeric.c:4521
Numeric int64_to_numeric(int64 val)
Definition: numeric.c:4259
Datum numeric_out(PG_FUNCTION_ARGS)
Definition: numeric.c:799
Datum numeric_in(PG_FUNCTION_ARGS)
Definition: numeric.c:626
int errcode(int sqlerrcode)
Definition: elog.c:863
int errmsg(const char *fmt,...)
Definition: elog.c:1080
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:226
#define ereport(elevel,...)
Definition: elog.h:150
#define palloc_object(type)
Definition: fe_memutils.h:74
#define PG_GETARG_POINTER(n)
Definition: fmgr.h:276
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:682
#define DirectFunctionCall3(func, arg1, arg2, arg3)
Definition: fmgr.h:686
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
const char * str
struct parser_state ps
long val
Definition: informix.c:689
static struct @171 value
int i
Definition: isn.c:77
@ jbvNumeric
Definition: jsonb.h:232
@ jbvBool
Definition: jsonb.h:233
@ jbvBinary
Definition: jsonb.h:238
@ jbvNull
Definition: jsonb.h:230
@ jbvString
Definition: jsonb.h:231
#define PG_RETURN_JSONB_P(x)
Definition: jsonb.h:420
#define PG_GETARG_JSONB_P(x)
Definition: jsonb.h:418
JsonbIteratorToken
Definition: jsonb.h:21
@ WJB_KEY
Definition: jsonb.h:23
@ WJB_DONE
Definition: jsonb.h:22
@ WJB_END_ARRAY
Definition: jsonb.h:27
@ WJB_VALUE
Definition: jsonb.h:24
@ WJB_END_OBJECT
Definition: jsonb.h:29
@ WJB_ELEM
Definition: jsonb.h:25
@ WJB_BEGIN_OBJECT
Definition: jsonb.h:28
@ WJB_BEGIN_ARRAY
Definition: jsonb.h:26
static SV * JsonbValue_to_SV(JsonbValue *jbv)
Definition: jsonb_plperl.c:20
static SV * Jsonb_to_SV(JsonbContainer *jsonb)
Definition: jsonb_plperl.c:62
PG_FUNCTION_INFO_V1(jsonb_to_plperl)
static void HV_to_JsonbValue(HV *obj, JsonbInState *jsonb_state)
Definition: jsonb_plperl.c:151
static void SV_to_JsonbValue(SV *obj, JsonbInState *ps, bool is_elem)
Definition: jsonb_plperl.c:177
Datum jsonb_to_plperl(PG_FUNCTION_ARGS)
Definition: jsonb_plperl.c:288
static void AV_to_JsonbValue(AV *in, JsonbInState *jsonb_state)
Definition: jsonb_plperl.c:131
Datum plperl_to_jsonb(PG_FUNCTION_ARGS)
Definition: jsonb_plperl.c:301
PG_MODULE_MAGIC_EXT(.name="jsonb_plperl",.version=PG_VERSION)
void pushJsonbValue(JsonbInState *pstate, JsonbIteratorToken seq, JsonbValue *jbval)
Definition: jsonb_util.c:583
JsonbIterator * JsonbIteratorInit(JsonbContainer *container)
Definition: jsonb_util.c:935
JsonbIteratorToken JsonbIteratorNext(JsonbIterator **it, JsonbValue *val, bool skipNested)
Definition: jsonb_util.c:973
Jsonb * JsonbValueToJsonb(JsonbValue *val)
Definition: jsonb_util.c:96
void pfree(void *pointer)
Definition: mcxt.c:1594
char * pnstrdup(const char *in, Size len)
Definition: mcxt.c:1770
static Numeric DatumGetNumeric(Datum X)
Definition: numeric.h:64
static Datum NumericGetDatum(Numeric X)
Definition: numeric.h:76
static char * sv2cstr(SV *sv)
Definition: plperl.h:89
static SV * cstr2sv(const char *str)
Definition: plperl.h:147
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:332
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:262
static char * DatumGetCString(Datum X)
Definition: postgres.h:345
uint64_t Datum
Definition: postgres.h:70
static Datum Float8GetDatum(float8 X)
Definition: postgres.h:492
static Datum CStringGetDatum(const char *X)
Definition: postgres.h:360
static Datum Int32GetDatum(int32 X)
Definition: postgres.h:222
#define InvalidOid
Definition: postgres_ext.h:37
#define PL_sv_yes
Definition: ppport.h:11781
#define dTHX
Definition: ppport.h:11306
#define SvPV_nolen(sv)
Definition: ppport.h:14072
#define PL_sv_no
Definition: ppport.h:11779
struct @10::@11 av[32]
JsonbValue * result
Definition: jsonb.h:333
JsonbParseState * parseState
Definition: jsonb.h:337
enum jbvType type
Definition: jsonb.h:257
char * val
Definition: jsonb.h:266
Definition: jsonb.h:215
JsonbContainer root
Definition: jsonb.h:217
const char * name