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