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 "plperl_helpers.h"
8 #include "utils/fmgrprotos.h"
9 #include "utils/jsonb.h"
10 
12 
13 static SV *Jsonb_to_SV(JsonbContainer *jsonb);
14 static JsonbValue *SV_to_JsonbValue(SV *obj, JsonbParseState **ps, bool is_elem);
15 
16 
17 static SV *
19 {
20  dTHX;
21 
22  switch (jbv->type)
23  {
24  case jbvBinary:
25  return Jsonb_to_SV(jbv->val.binary.data);
26 
27  case jbvNumeric:
28  {
30  NumericGetDatum(jbv->val.numeric)));
31  SV *result = newSVnv(SvNV(cstr2sv(str)));
32 
33  pfree(str);
34  return result;
35  }
36 
37  case jbvString:
38  {
39  char *str = pnstrdup(jbv->val.string.val,
40  jbv->val.string.len);
41  SV *result = cstr2sv(str);
42 
43  pfree(str);
44  return result;
45  }
46 
47  case jbvBool:
48  return newSVnv(SvNV(jbv->val.boolean ? &PL_sv_yes : &PL_sv_no));
49 
50  case jbvNull:
51  return newSV(0);
52 
53  default:
54  elog(ERROR, "unexpected jsonb value type: %d", jbv->type);
55  return NULL;
56  }
57 }
58 
59 static SV *
61 {
62  dTHX;
63  JsonbValue v;
64  JsonbIterator *it;
66 
67  it = JsonbIteratorInit(jsonb);
68  r = JsonbIteratorNext(&it, &v, true);
69 
70  switch (r)
71  {
72  case WJB_BEGIN_ARRAY:
73  if (v.val.array.rawScalar)
74  {
75  JsonbValue tmp;
76 
77  if ((r = JsonbIteratorNext(&it, &v, true)) != WJB_ELEM ||
78  (r = JsonbIteratorNext(&it, &tmp, true)) != WJB_END_ARRAY ||
79  (r = JsonbIteratorNext(&it, &tmp, true)) != WJB_DONE)
80  elog(ERROR, "unexpected jsonb token: %d", r);
81 
82  return JsonbValue_to_SV(&v);
83  }
84  else
85  {
86  AV *av = newAV();
87 
88  while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
89  {
90  if (r == WJB_ELEM)
91  av_push(av, JsonbValue_to_SV(&v));
92  }
93 
94  return newRV((SV *) av);
95  }
96 
97  case WJB_BEGIN_OBJECT:
98  {
99  HV *hv = newHV();
100 
101  while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
102  {
103  if (r == WJB_KEY)
104  {
105  /* json key in v, json value in val */
106  JsonbValue val;
107 
108  if (JsonbIteratorNext(&it, &val, true) == WJB_VALUE)
109  {
110  SV *value = JsonbValue_to_SV(&val);
111 
112  (void) hv_store(hv,
113  v.val.string.val, v.val.string.len,
114  value, 0);
115  }
116  }
117  }
118 
119  return newRV((SV *) hv);
120  }
121 
122  default:
123  elog(ERROR, "unexpected jsonb token: %d", r);
124  return NULL;
125  }
126 }
127 
128 static JsonbValue *
129 AV_to_JsonbValue(AV *in, JsonbParseState **jsonb_state)
130 {
131  dTHX;
132  SSize_t pcount = av_len(in) + 1;
133  SSize_t i;
134 
135  pushJsonbValue(jsonb_state, WJB_BEGIN_ARRAY, NULL);
136 
137  for (i = 0; i < pcount; i++)
138  {
139  SV **value = av_fetch(in, i, FALSE);
140 
141  if (value)
142  (void) SV_to_JsonbValue(*value, jsonb_state, true);
143  }
144 
145  return pushJsonbValue(jsonb_state, WJB_END_ARRAY, NULL);
146 }
147 
148 static JsonbValue *
149 HV_to_JsonbValue(HV *obj, JsonbParseState **jsonb_state)
150 {
151  dTHX;
152  JsonbValue key;
153  SV *val;
154  char *kstr;
155  I32 klen;
156 
157  key.type = jbvString;
158 
159  pushJsonbValue(jsonb_state, WJB_BEGIN_OBJECT, NULL);
160 
161  (void) hv_iterinit(obj);
162 
163  while ((val = hv_iternextsv(obj, &kstr, &klen)))
164  {
165  key.val.string.val = pnstrdup(kstr, klen);
166  key.val.string.len = klen;
167  pushJsonbValue(jsonb_state, WJB_KEY, &key);
168  (void) SV_to_JsonbValue(val, jsonb_state, false);
169  }
170 
171  return pushJsonbValue(jsonb_state, WJB_END_OBJECT, NULL);
172 }
173 
174 static JsonbValue *
175 SV_to_JsonbValue(SV *in, JsonbParseState **jsonb_state, bool is_elem)
176 {
177  dTHX;
178  JsonbValue out; /* result */
179 
180  /* Dereference references recursively. */
181  while (SvROK(in))
182  in = SvRV(in);
183 
184  switch (SvTYPE(in))
185  {
186  case SVt_PVAV:
187  return AV_to_JsonbValue((AV *) in, jsonb_state);
188 
189  case SVt_PVHV:
190  return HV_to_JsonbValue((HV *) in, jsonb_state);
191 
192  default:
193  if (!SvOK(in))
194  {
195  out.type = jbvNull;
196  }
197  else if (SvUOK(in))
198  {
199  /*
200  * If UV is >=64 bits, we have no better way to make this
201  * happen than converting to text and back. Given the low
202  * usage of UV in Perl code, it's not clear it's worth working
203  * hard to provide alternate code paths.
204  */
205  const char *strval = SvPV_nolen(in);
206 
207  out.type = jbvNumeric;
208  out.val.numeric =
210  CStringGetDatum(strval),
212  Int32GetDatum(-1)));
213  }
214  else if (SvIOK(in))
215  {
216  IV ival = SvIV(in);
217 
218  out.type = jbvNumeric;
219  out.val.numeric =
221  Int64GetDatum((int64) ival)));
222  }
223  else if (SvNOK(in))
224  {
225  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 NaN, so we have to prevent it here
231  * explicitly. We don't really have to check for isinf()
232  * here, as numeric doesn't allow it and it would be caught
233  * later, but it makes for a nicer error message.
234  */
235  if (isinf(nval))
236  ereport(ERROR,
237  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
238  (errmsg("cannot convert infinity to jsonb"))));
239  if (isnan(nval))
240  ereport(ERROR,
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  */
261  ereport(ERROR,
262  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
263  (errmsg("cannot transform this Perl type to jsonb"))));
264  return NULL;
265  }
266  }
267 
268  /* Push result into 'jsonb_state' unless it is a raw scalar. */
269  return *jsonb_state
270  ? pushJsonbValue(jsonb_state, is_elem ? WJB_ELEM : WJB_VALUE, &out)
271  : memcpy(palloc(sizeof(JsonbValue)), &out, sizeof(JsonbValue));
272 }
273 
274 
276 
277 Datum
279 {
280  dTHX;
281  Jsonb *in = PG_GETARG_JSONB_P(0);
282  SV *sv = Jsonb_to_SV(&in->root);
283 
284  return PointerGetDatum(sv);
285 }
286 
287 
289 
290 Datum
292 {
293  dTHX;
294  JsonbParseState *jsonb_state = NULL;
295  SV *in = (SV *) PG_GETARG_POINTER(0);
296  JsonbValue *out = SV_to_JsonbValue(in, &jsonb_state, true);
297  Jsonb *result = JsonbValueToJsonb(out);
298 
299  PG_RETURN_JSONB_P(result);
300 }
static JsonbValue * HV_to_JsonbValue(HV *obj, JsonbParseState **jsonb_state)
Definition: jsonb_plperl.c:149
char * pnstrdup(const char *in, Size len)
Definition: mcxt.c:1197
Jsonb * JsonbValueToJsonb(JsonbValue *val)
Definition: jsonb_util.c:84
Definition: jsonb.h:220
#define dTHX
Definition: ppport.h:3208
#define PointerGetDatum(X)
Definition: postgres.h:556
char * val
Definition: jsonb.h:272
#define NumericGetDatum(X)
Definition: numeric.h:51
Definition: jsonb.h:239
Datum numeric_out(PG_FUNCTION_ARGS)
Definition: numeric.c:655
Definition: jsonb.h:22
static struct @145 value
int errcode(int sqlerrcode)
Definition: elog.c:608
#define PG_GETARG_POINTER(n)
Definition: fmgr.h:271
Definition: jsonb.h:236
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:615
Datum int8_numeric(PG_FUNCTION_ARGS)
Definition: numeric.c:3285
static JsonbValue * SV_to_JsonbValue(SV *obj, JsonbParseState **ps, bool is_elem)
Definition: jsonb_plperl.c:175
Datum Float8GetDatum(float8 X)
Definition: fmgr.c:1708
JsonbValue * pushJsonbValue(JsonbParseState **pstate, JsonbIteratorToken seq, JsonbValue *jbval)
Definition: jsonb_util.c:558
void pfree(void *pointer)
Definition: mcxt.c:1056
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
#define ERROR
Definition: elog.h:43
#define DatumGetCString(X)
Definition: postgres.h:566
static SV * JsonbValue_to_SV(JsonbValue *jbv)
Definition: jsonb_plperl.c:18
#define SvPV_nolen(sv)
Definition: ppport.h:5057
int isinf(double x)
#define CStringGetDatum(X)
Definition: postgres.h:578
#define PL_sv_yes
Definition: ppport.h:4130
Datum numeric_in(PG_FUNCTION_ARGS)
Definition: numeric.c:573
Definition: jsonb.h:23
Datum Int64GetDatum(int64 X)
Definition: fmgr.c:1699
#define ereport(elevel, rest)
Definition: elog.h:141
JsonbIteratorToken
Definition: jsonb.h:20
#define DirectFunctionCall3(func, arg1, arg2, arg3)
Definition: fmgr.h:619
JsonbContainer root
Definition: jsonb.h:223
struct @18::@19 av[32]
uintptr_t Datum
Definition: postgres.h:367
PG_FUNCTION_INFO_V1(jsonb_to_plperl)
static JsonbValue * AV_to_JsonbValue(AV *in, JsonbParseState **jsonb_state)
Definition: jsonb_plperl.c:129
Datum float8_numeric(PG_FUNCTION_ARGS)
Definition: numeric.c:3383
static SV * cstr2sv(const char *str)
#define InvalidOid
Definition: postgres_ext.h:36
Datum plperl_to_jsonb(PG_FUNCTION_ARGS)
Definition: jsonb_plperl.c:291
#define DatumGetNumeric(X)
Definition: numeric.h:49
JsonbIterator * JsonbIteratorInit(JsonbContainer *container)
Definition: jsonb_util.c:759
static SV * Jsonb_to_SV(JsonbContainer *jsonb)
Definition: jsonb_plperl.c:60
PG_MODULE_MAGIC
Definition: jsonb_plperl.c:11
Datum jsonb_to_plperl(PG_FUNCTION_ARGS)
Definition: jsonb_plperl.c:278
enum jbvType type
Definition: jsonb.h:263
#define Int32GetDatum(X)
Definition: postgres.h:479
void * palloc(Size size)
Definition: mcxt.c:949
int errmsg(const char *fmt,...)
Definition: elog.c:822
#define elog(elevel,...)
Definition: elog.h:228
int i
#define PG_FUNCTION_ARGS
Definition: fmgr.h:188
#define PG_RETURN_JSONB_P(x)
Definition: jsonb.h:76
#define PL_sv_no
Definition: ppport.h:4128
long val
Definition: informix.c:664
JsonbIteratorToken JsonbIteratorNext(JsonbIterator **it, JsonbValue *val, bool skipNested)
Definition: jsonb_util.c:795
Definition: jsonb.h:25
static char * sv2cstr(SV *sv)
#define PG_GETARG_JSONB_P(x)
Definition: jsonb.h:74