PostgreSQL Source Code git master
Loading...
Searching...
No Matches
jsonb_plpython.c
Go to the documentation of this file.
1#include "postgres.h"
2
3#include "plpy_elog.h"
4#include "plpy_typeio.h"
5#include "plpy_util.h"
6#include "utils/fmgrprotos.h"
7#include "utils/jsonb.h"
8#include "utils/numeric.h"
9
11 .name = "jsonb_plpython",
12 .version = PG_VERSION
13);
14
15/* for PLyObject_AsString in plpy_typeio.c */
16typedef char *(*PLyObject_AsString_t) (PyObject *plrv);
18
19typedef void (*PLy_elog_impl_t) (int elevel, const char *fmt,...);
21
22/*
23 * decimal_constructor is a function from python library and used
24 * for transforming strings into python decimal type
25 */
27
29static void PLyObject_ToJsonbValue(PyObject *obj,
31
32typedef PyObject *(*PLyUnicode_FromStringAndSize_t)
33 (const char *s, Py_ssize_t size);
35
36/* Static asserts verify that typedefs above match original declarations */
40
41
42/*
43 * Module initialize function: fetch function pointers for cross-module calls.
44 */
45void
47{
49 load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLyObject_AsString",
50 true, NULL);
52 load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLyUnicode_FromStringAndSize",
53 true, NULL);
55 load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLy_elog_impl",
56 true, NULL);
57}
58
59/* These defines must be after the _PG_init */
60#define PLyObject_AsString (PLyObject_AsString_p)
61#define PLyUnicode_FromStringAndSize (PLyUnicode_FromStringAndSize_p)
62#undef PLy_elog
63#define PLy_elog (PLy_elog_impl_p)
64
65/*
66 * PLyUnicode_FromJsonbValue
67 *
68 * Transform string JsonbValue to Python string.
69 */
70static PyObject *
72{
73 Assert(jbv->type == jbvString);
74
75 return PLyUnicode_FromStringAndSize(jbv->val.string.val, jbv->val.string.len);
76}
77
78/*
79 * PLyUnicode_ToJsonbValue
80 *
81 * Transform Python string to JsonbValue.
82 */
83static void
85{
86 jbvElem->type = jbvString;
87 jbvElem->val.string.val = PLyObject_AsString(obj);
88 jbvElem->val.string.len = strlen(jbvElem->val.string.val);
89}
90
91/*
92 * PLyObject_FromJsonbValue
93 *
94 * Transform JsonbValue to PyObject.
95 */
96static PyObject *
98{
99 switch (jsonbValue->type)
100 {
101 case jbvNull:
103
104 case jbvBinary:
105 return PLyObject_FromJsonbContainer(jsonbValue->val.binary.data);
106
107 case jbvNumeric:
108 {
109 Datum num;
110 char *str;
111
112 num = NumericGetDatum(jsonbValue->val.numeric);
114
116 }
117
118 case jbvString:
120
121 case jbvBool:
122 if (jsonbValue->val.boolean)
124 else
126
127 default:
128 elog(ERROR, "unexpected jsonb value type: %d", jsonbValue->type);
129 return NULL;
130 }
131}
132
133/*
134 * PLyObject_FromJsonbContainer
135 *
136 * Transform JsonbContainer to PyObject.
137 */
138static PyObject *
140{
142 JsonbValue v;
144 PyObject *result;
145
146 it = JsonbIteratorInit(jsonb);
147 r = JsonbIteratorNext(&it, &v, true);
148
149 switch (r)
150 {
151 case WJB_BEGIN_ARRAY:
152 if (v.val.array.rawScalar)
153 {
154 JsonbValue tmp;
155
156 if ((r = JsonbIteratorNext(&it, &v, true)) != WJB_ELEM ||
157 (r = JsonbIteratorNext(&it, &tmp, true)) != WJB_END_ARRAY ||
158 (r = JsonbIteratorNext(&it, &tmp, true)) != WJB_DONE)
159 elog(ERROR, "unexpected jsonb token: %d", r);
160
161 result = PLyObject_FromJsonbValue(&v);
162 }
163 else
164 {
165 PyObject *volatile elem = NULL;
166
167 result = PyList_New(0);
168 if (!result)
169 return NULL;
170
171 PG_TRY();
172 {
173 while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
174 {
175 if (r != WJB_ELEM)
176 continue;
177
178 elem = PLyObject_FromJsonbValue(&v);
179
180 PyList_Append(result, elem);
181 Py_XDECREF(elem);
182 elem = NULL;
183 }
184 }
185 PG_CATCH();
186 {
187 Py_XDECREF(elem);
188 Py_XDECREF(result);
189 PG_RE_THROW();
190 }
191 PG_END_TRY();
192 }
193 break;
194
195 case WJB_BEGIN_OBJECT:
196 {
197 PyObject *volatile result_v = PyDict_New();
198 PyObject *volatile key = NULL;
199 PyObject *volatile val = NULL;
200
201 if (!result_v)
202 return NULL;
203
204 PG_TRY();
205 {
206 while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
207 {
208 if (r != WJB_KEY)
209 continue;
210
212 if (!key)
213 {
215 result_v = NULL;
216 break;
217 }
218
219 if ((r = JsonbIteratorNext(&it, &v, true)) != WJB_VALUE)
220 elog(ERROR, "unexpected jsonb token: %d", r);
221
223 if (!val)
224 {
225 Py_XDECREF(key);
226 key = NULL;
228 result_v = NULL;
229 break;
230 }
231
233
234 Py_XDECREF(key);
235 key = NULL;
237 val = NULL;
238 }
239 }
240 PG_CATCH();
241 {
243 Py_XDECREF(key);
245 PG_RE_THROW();
246 }
247 PG_END_TRY();
248
249 result = result_v;
250 }
251 break;
252
253 default:
254 elog(ERROR, "unexpected jsonb token: %d", r);
255 return NULL;
256 }
257
258 return result;
259}
260
261/*
262 * PLyMapping_ToJsonbValue
263 *
264 * Transform Python dict to JsonbValue.
265 */
266static void
268{
270 PyObject *volatile items;
271
272 pcount = PyMapping_Size(obj);
273 items = PyMapping_Items(obj);
274
275 PG_TRY();
276 {
278
280
281 for (i = 0; i < pcount; i++)
282 {
284 PyObject *item = PyList_GetItem(items, i);
285 PyObject *key = PyTuple_GetItem(item, 0);
286 PyObject *value = PyTuple_GetItem(item, 1);
287
288 /* Python dictionary can have None as key */
289 if (key == Py_None)
290 {
291 jbvKey.type = jbvString;
292 jbvKey.val.string.len = 0;
293 jbvKey.val.string.val = "";
294 }
295 else
296 {
297 /* All others types of keys we serialize to string */
299 }
300
303 }
304
306 }
307 PG_FINALLY();
308 {
310 }
311 PG_END_TRY();
312}
313
314/*
315 * PLySequence_ToJsonbValue
316 *
317 * Transform python list to JsonbValue. Expects transformed PyObject and
318 * a state required for jsonb construction.
319 */
320static void
322{
325 PyObject *volatile value = NULL;
326
327 pcount = PySequence_Size(obj);
328
330
331 PG_TRY();
332 {
333 for (i = 0; i < pcount; i++)
334 {
335 value = PySequence_GetItem(obj, i);
336 Assert(value);
337
340 value = NULL;
341 }
342 }
343 PG_CATCH();
344 {
346 PG_RE_THROW();
347 }
348 PG_END_TRY();
349
351}
352
353/*
354 * PLyNumber_ToJsonbValue(PyObject *obj)
355 *
356 * Transform python number to JsonbValue.
357 */
358static JsonbValue *
360{
361 Numeric num;
362 char *str = PLyObject_AsString(obj);
363
364 PG_TRY();
365 {
366 Datum numd;
367
371 Int32GetDatum(-1));
372 num = DatumGetNumeric(numd);
373 }
374 PG_CATCH();
375 {
378 errmsg("could not convert value \"%s\" to jsonb", str)));
379 }
380 PG_END_TRY();
381
382 pfree(str);
383
384 /*
385 * jsonb doesn't allow NaN or infinity (per JSON specification), so we
386 * have to reject those here explicitly.
387 */
388 if (numeric_is_nan(num))
391 errmsg("cannot convert NaN to jsonb")));
392 if (numeric_is_inf(num))
395 errmsg("cannot convert infinity to jsonb")));
396
397 jbvNum->type = jbvNumeric;
398 jbvNum->val.numeric = num;
399
400 return jbvNum;
401}
402
403/*
404 * PLyObject_ToJsonbValue(PyObject *obj)
405 *
406 * Transform python object to JsonbValue.
407 */
408static void
410{
411 JsonbValue *out;
412
413 if (!PyUnicode_Check(obj))
414 {
415 if (PySequence_Check(obj))
416 {
418 return;
419 }
420 else if (PyMapping_Check(obj))
421 {
423 return;
424 }
425 }
426
428
429 if (obj == Py_None)
430 out->type = jbvNull;
431 else if (PyUnicode_Check(obj))
432 PLyUnicode_ToJsonbValue(obj, out);
433
434 /*
435 * PyNumber_Check() returns true for booleans, so boolean check should
436 * come first.
437 */
438 else if (PyBool_Check(obj))
439 {
440 out->type = jbvBool;
441 out->val.boolean = (obj == Py_True);
442 }
443 else if (PyNumber_Check(obj))
444 out = PLyNumber_ToJsonbValue(obj, out);
445 else
448 errmsg("Python type \"%s\" cannot be transformed to jsonb",
449 PLyObject_AsString((PyObject *) obj->ob_type))));
450
451 if (jsonb_state->parseState)
452 {
453 /* We're in an array or object, so push value as element or field. */
455 }
456 else
457 {
458 /*
459 * We are at top level, so it's a raw scalar. If we just shove the
460 * scalar value into jsonb_state->result, JsonbValueToJsonb will take
461 * care of wrapping it into a dummy array.
462 */
463 jsonb_state->result = out;
464 }
465}
466
467/*
468 * plpython_to_jsonb
469 *
470 * Transform python object to Jsonb datum
471 */
473Datum
482
483/*
484 * jsonb_to_plpython
485 *
486 * Transform Jsonb datum to PyObject and return it as internal.
487 */
489Datum
491{
492 PyObject *result;
493 Jsonb *in = PG_GETARG_JSONB_P(0);
494
495 /*
496 * Initialize pointer to Decimal constructor. First we try "cdecimal", C
497 * version of decimal library. In case of failure we use slower "decimal"
498 * module.
499 */
501 {
503
504 if (!decimal_module)
505 {
506 PyErr_Clear();
508 }
511 }
512
513 result = PLyObject_FromJsonbContainer(&in->root);
514 if (!result)
515 PLy_elog(ERROR, "transformation from jsonb to Python failed");
516
517 return PointerGetDatum(result);
518}
Datum numeric_out(PG_FUNCTION_ARGS)
Definition numeric.c:799
Datum numeric_in(PG_FUNCTION_ARGS)
Definition numeric.c:626
bool numeric_is_nan(Numeric num)
Definition numeric.c:834
bool numeric_is_inf(Numeric num)
Definition numeric.c:845
#define Assert(condition)
Definition c.h:873
#define StaticAssertVariableIsOfType(varname, typename)
Definition c.h:974
void * load_external_function(const char *filename, const char *funcname, bool signalNotFound, void **filehandle)
Definition dfmgr.c:95
int errcode(int sqlerrcode)
Definition elog.c:863
int errmsg(const char *fmt,...)
Definition elog.c:1080
#define PG_RE_THROW()
Definition elog.h:405
#define PG_TRY(...)
Definition elog.h:372
#define PG_END_TRY(...)
Definition elog.h:397
#define ERROR
Definition elog.h:39
#define PG_CATCH(...)
Definition elog.h:382
#define elog(elevel,...)
Definition elog.h:226
#define PG_FINALLY(...)
Definition elog.h:389
#define ereport(elevel,...)
Definition elog.h:150
#define palloc_object(type)
Definition fe_memutils.h:74
#define PG_GETARG_POINTER(n)
Definition fmgr.h:277
#define PG_MODULE_MAGIC_EXT(...)
Definition fmgr.h:540
#define DirectFunctionCall1(func, arg1)
Definition fmgr.h:684
#define PG_FUNCTION_INFO_V1(funcname)
Definition fmgr.h:417
#define DirectFunctionCall3(func, arg1, arg2, arg3)
Definition fmgr.h:688
#define PG_RETURN_POINTER(x)
Definition fmgr.h:363
#define PG_FUNCTION_ARGS
Definition fmgr.h:193
const char * str
long val
Definition informix.c:689
static struct @172 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_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 void PLyMapping_ToJsonbValue(PyObject *obj, JsonbInState *jsonb_state)
#define PLy_elog
#define PLyUnicode_FromStringAndSize
static void PLyObject_ToJsonbValue(PyObject *obj, JsonbInState *jsonb_state, bool is_elem)
void _PG_init(void)
static PLy_elog_impl_t PLy_elog_impl_p
static JsonbValue * PLyNumber_ToJsonbValue(PyObject *obj, JsonbValue *jbvNum)
PyObject *(* PLyUnicode_FromStringAndSize_t)(const char *s, Py_ssize_t size)
Datum plpython_to_jsonb(PG_FUNCTION_ARGS)
void(* PLy_elog_impl_t)(int elevel, const char *fmt,...)
static PyObject * PLyObject_FromJsonbValue(JsonbValue *jsonbValue)
static PLyObject_AsString_t PLyObject_AsString_p
StaticAssertVariableIsOfType & PLy_elog_impl
static void PLySequence_ToJsonbValue(PyObject *obj, JsonbInState *jsonb_state)
char *(* PLyObject_AsString_t)(PyObject *plrv)
static PyObject * decimal_constructor
Datum jsonb_to_plpython(PG_FUNCTION_ARGS)
static PLyUnicode_FromStringAndSize_t PLyUnicode_FromStringAndSize_p
static void PLyUnicode_ToJsonbValue(PyObject *obj, JsonbValue *jbvElem)
static PyObject * PLyObject_FromJsonbContainer(JsonbContainer *jsonb)
#define PLyObject_AsString
static PyObject * PLyUnicode_FromJsonbValue(JsonbValue *jbv)
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:1616
static Numeric DatumGetNumeric(Datum X)
Definition numeric.h:64
static Datum NumericGetDatum(Numeric X)
Definition numeric.h:76
static Datum PointerGetDatum(const void *X)
Definition postgres.h:352
static Datum ObjectIdGetDatum(Oid X)
Definition postgres.h:262
static char * DatumGetCString(Datum X)
Definition postgres.h:365
uint64_t Datum
Definition postgres.h:70
static Datum CStringGetDatum(const char *X)
Definition postgres.h:380
static Datum Int32GetDatum(int32 X)
Definition postgres.h:222
#define InvalidOid
static int fb(int x)
enum jbvType type
Definition jsonb.h:257
char * val
Definition jsonb.h:266
Definition jsonb.h:215
JsonbContainer root
Definition jsonb.h:217
static ItemArray items
const char * name