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 "miscadmin.h"
4#include "plpy_elog.h"
5#include "plpy_typeio.h"
6#include "plpy_util.h"
7#include "utils/fmgrprotos.h"
8#include "utils/jsonb.h"
9#include "utils/numeric.h"
10
12 .name = "jsonb_plpython",
13 .version = PG_VERSION
14);
15
16/* for PLyObject_AsString in plpy_typeio.c */
17typedef char *(*PLyObject_AsString_t) (PyObject *plrv);
19
20typedef void (*PLy_elog_impl_t) (int elevel, const char *fmt, ...);
22
23/*
24 * decimal_constructor is a function from python library and used
25 * for transforming strings into python decimal type
26 */
28
30static void PLyObject_ToJsonbValue(PyObject *obj,
32
33typedef PyObject *(*PLyUnicode_FromStringAndSize_t)
34 (const char *s, Py_ssize_t size);
36
37/* Static asserts verify that typedefs above match original declarations */
41
42
43/*
44 * Module initialize function: fetch function pointers for cross-module calls.
45 */
46void
48{
50 load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLyObject_AsString",
51 true, NULL);
53 load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLyUnicode_FromStringAndSize",
54 true, NULL);
56 load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLy_elog_impl",
57 true, NULL);
58}
59
60/* These defines must be after the _PG_init */
61#define PLyObject_AsString (PLyObject_AsString_p)
62#define PLyUnicode_FromStringAndSize (PLyUnicode_FromStringAndSize_p)
63#undef PLy_elog
64#define PLy_elog (PLy_elog_impl_p)
65
66/*
67 * PLyUnicode_FromJsonbValue
68 *
69 * Transform string JsonbValue to Python string.
70 */
71static PyObject *
73{
74 Assert(jbv->type == jbvString);
75
76 return PLyUnicode_FromStringAndSize(jbv->val.string.val, jbv->val.string.len);
77}
78
79/*
80 * PLyUnicode_ToJsonbValue
81 *
82 * Transform Python string to JsonbValue.
83 */
84static void
86{
87 jbvElem->type = jbvString;
88 jbvElem->val.string.val = PLyObject_AsString(obj);
89 jbvElem->val.string.len = strlen(jbvElem->val.string.val);
90}
91
92/*
93 * PLyObject_FromJsonbValue
94 *
95 * Transform JsonbValue to PyObject.
96 */
97static PyObject *
99{
100 switch (jsonbValue->type)
101 {
102 case jbvNull:
104
105 case jbvBinary:
106 return PLyObject_FromJsonbContainer(jsonbValue->val.binary.data);
107
108 case jbvNumeric:
109 {
110 Datum num;
111 char *str;
112
113 num = NumericGetDatum(jsonbValue->val.numeric);
115
117 }
118
119 case jbvString:
121
122 case jbvBool:
123 if (jsonbValue->val.boolean)
125 else
127
128 default:
129 elog(ERROR, "unexpected jsonb value type: %d", jsonbValue->type);
130 return NULL;
131 }
132}
133
134/*
135 * PLyObject_FromJsonbContainer
136 *
137 * Transform JsonbContainer to PyObject.
138 */
139static PyObject *
141{
143 JsonbValue v;
146
147 /* this can recurse via PLyObject_FromJsonbValue() */
149
150 it = JsonbIteratorInit(jsonb);
151 r = JsonbIteratorNext(&it, &v, true);
152
153 switch (r)
154 {
155 case WJB_BEGIN_ARRAY:
156 if (v.val.array.rawScalar)
157 {
158 JsonbValue tmp;
159
160 if ((r = JsonbIteratorNext(&it, &v, true)) != WJB_ELEM ||
161 (r = JsonbIteratorNext(&it, &tmp, true)) != WJB_END_ARRAY ||
162 (r = JsonbIteratorNext(&it, &tmp, true)) != WJB_DONE)
163 elog(ERROR, "unexpected jsonb token: %d", r);
164
166 }
167 else
168 {
169 PyObject *volatile elem = NULL;
170
171 result = PyList_New(0);
172 if (!result)
173 return NULL;
174
175 PG_TRY();
176 {
177 while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
178 {
179 if (r != WJB_ELEM)
180 continue;
181
182 elem = PLyObject_FromJsonbValue(&v);
183
184 PyList_Append(result, elem);
185 Py_XDECREF(elem);
186 elem = NULL;
187 }
188 }
189 PG_CATCH();
190 {
191 Py_XDECREF(elem);
193 PG_RE_THROW();
194 }
195 PG_END_TRY();
196 }
197 break;
198
199 case WJB_BEGIN_OBJECT:
200 {
201 PyObject *volatile result_v = PyDict_New();
202 PyObject *volatile key = NULL;
203 PyObject *volatile val = NULL;
204
205 if (!result_v)
206 return NULL;
207
208 PG_TRY();
209 {
210 while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
211 {
212 if (r != WJB_KEY)
213 continue;
214
216 if (!key)
217 {
219 result_v = NULL;
220 break;
221 }
222
223 if ((r = JsonbIteratorNext(&it, &v, true)) != WJB_VALUE)
224 elog(ERROR, "unexpected jsonb token: %d", r);
225
227 if (!val)
228 {
229 Py_XDECREF(key);
230 key = NULL;
232 result_v = NULL;
233 break;
234 }
235
237
238 Py_XDECREF(key);
239 key = NULL;
241 val = NULL;
242 }
243 }
244 PG_CATCH();
245 {
247 Py_XDECREF(key);
249 PG_RE_THROW();
250 }
251 PG_END_TRY();
252
254 }
255 break;
256
257 default:
258 elog(ERROR, "unexpected jsonb token: %d", r);
259 return NULL;
260 }
261
262 return result;
263}
264
265/*
266 * PLyMapping_ToJsonbValue
267 *
268 * Transform Python dict to JsonbValue.
269 */
270static void
272{
274 PyObject *volatile items;
275
276 pcount = PyMapping_Size(obj);
277 if (pcount < 0)
278 PLy_elog(ERROR, "could not get size of Python mapping");
279
280 items = PyMapping_Items(obj);
281 if (items == NULL)
282 PLy_elog(ERROR, "could not get items from Python mapping");
283
284 PG_TRY();
285 {
287
289
290 for (i = 0; i < pcount; i++)
291 {
293 PyObject *item = PyList_GetItem(items, i);
294 PyObject *key;
296
297 /* The mapping's items() must yield key/value pairs */
298 if (item == NULL || !PyTuple_Check(item) || PyTuple_Size(item) < 2)
299 PLy_elog(ERROR, "items() of a Python mapping must return key/value pairs");
300
301 key = PyTuple_GetItem(item, 0);
302 value = PyTuple_GetItem(item, 1);
303
304 /* Python dictionary can have None as key */
305 if (key == Py_None)
306 {
307 jbvKey.type = jbvString;
308 jbvKey.val.string.len = 0;
309 jbvKey.val.string.val = "";
310 }
311 else
312 {
313 /* All others types of keys we serialize to string */
315 }
316
319 }
320
322 }
323 PG_FINALLY();
324 {
326 }
327 PG_END_TRY();
328}
329
330/*
331 * PLySequence_ToJsonbValue
332 *
333 * Transform python list to JsonbValue. Expects transformed PyObject and
334 * a state required for jsonb construction.
335 */
336static void
338{
341 PyObject *volatile value = NULL;
342
343 pcount = PySequence_Size(obj);
344
346
347 PG_TRY();
348 {
349 for (i = 0; i < pcount; i++)
350 {
351 value = PySequence_GetItem(obj, i);
352
353 /* PySequence_GetItem() can return NULL, with an exception set */
354 if (value == NULL)
355 PLy_elog(ERROR, "could not get element %d from sequence", (int) i);
356
359 value = NULL;
360 }
361 }
362 PG_CATCH();
363 {
365 PG_RE_THROW();
366 }
367 PG_END_TRY();
368
370}
371
372/*
373 * PLyNumber_ToJsonbValue(PyObject *obj)
374 *
375 * Transform python number to JsonbValue.
376 */
377static JsonbValue *
379{
380 Numeric num;
381 char *str = PLyObject_AsString(obj);
382
383 PG_TRY();
384 {
385 Datum numd;
386
390 Int32GetDatum(-1));
391 num = DatumGetNumeric(numd);
392 }
393 PG_CATCH();
394 {
397 errmsg("could not convert value \"%s\" to jsonb", str)));
398 }
399 PG_END_TRY();
400
401 pfree(str);
402
403 /*
404 * jsonb doesn't allow NaN or infinity (per JSON specification), so we
405 * have to reject those here explicitly.
406 */
407 if (numeric_is_nan(num))
410 errmsg("cannot convert NaN to jsonb")));
411 if (numeric_is_inf(num))
414 errmsg("cannot convert infinity to jsonb")));
415
416 jbvNum->type = jbvNumeric;
417 jbvNum->val.numeric = num;
418
419 return jbvNum;
420}
421
422/*
423 * PLyObject_ToJsonbValue(PyObject *obj)
424 *
425 * Transform python object to JsonbValue.
426 */
427static void
429{
430 JsonbValue *out;
431
432 /* this can recurse via PLyMapping_ToJsonbValue() */
434
435 if (!PyUnicode_Check(obj))
436 {
437 if (PySequence_Check(obj))
438 {
440 return;
441 }
442 else if (PyMapping_Check(obj))
443 {
445 return;
446 }
447 }
448
450
451 if (obj == Py_None)
452 out->type = jbvNull;
453 else if (PyUnicode_Check(obj))
454 PLyUnicode_ToJsonbValue(obj, out);
455
456 /*
457 * PyNumber_Check() returns true for booleans, so boolean check should
458 * come first.
459 */
460 else if (PyBool_Check(obj))
461 {
462 out->type = jbvBool;
463 out->val.boolean = (obj == Py_True);
464 }
465 else if (PyNumber_Check(obj))
466 out = PLyNumber_ToJsonbValue(obj, out);
467 else
470 errmsg("Python type \"%s\" cannot be transformed to jsonb",
471 PLyObject_AsString((PyObject *) obj->ob_type))));
472
473 if (jsonb_state->parseState)
474 {
475 /* We're in an array or object, so push value as element or field. */
477 }
478 else
479 {
480 /*
481 * We are at top level, so it's a raw scalar. If we just shove the
482 * scalar value into jsonb_state->result, JsonbValueToJsonb will take
483 * care of wrapping it into a dummy array.
484 */
485 jsonb_state->result = out;
486 }
487}
488
489/*
490 * plpython_to_jsonb
491 *
492 * Transform python object to Jsonb datum
493 */
495Datum
504
505/*
506 * jsonb_to_plpython
507 *
508 * Transform Jsonb datum to PyObject and return it as internal.
509 */
511Datum
513{
515 Jsonb *in = PG_GETARG_JSONB_P(0);
516
517 /*
518 * Initialize pointer to Decimal constructor. First we try "cdecimal", C
519 * version of decimal library. In case of failure we use slower "decimal"
520 * module.
521 */
523 {
525
526 if (!decimal_module)
527 {
528 PyErr_Clear();
530 }
533 }
534
536 if (!result)
537 PLy_elog(ERROR, "transformation from jsonb to Python failed");
538
539 return PointerGetDatum(result);
540}
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:943
#define StaticAssertVariableIsOfType(varname, typename)
Definition c.h:1068
uint32 result
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:875
#define PG_RE_THROW()
Definition elog.h:407
#define PG_TRY(...)
Definition elog.h:374
#define PG_END_TRY(...)
Definition elog.h:399
#define ERROR
Definition elog.h:40
#define PG_CATCH(...)
Definition elog.h:384
#define elog(elevel,...)
Definition elog.h:228
#define PG_FINALLY(...)
Definition elog.h:391
#define ereport(elevel,...)
Definition elog.h:152
#define palloc_object(type)
Definition fe_memutils.h:89
#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:688
#define PG_FUNCTION_INFO_V1(funcname)
Definition fmgr.h:417
#define DirectFunctionCall3(func, arg1, arg2, arg3)
Definition fmgr.h:692
#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 @175 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:1619
static Numeric DatumGetNumeric(Datum X)
Definition numeric.h:64
static Datum NumericGetDatum(Numeric X)
Definition numeric.h:76
static char * errmsg
static Datum ObjectIdGetDatum(Oid X)
Definition postgres.h:252
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:383
static Datum Int32GetDatum(int32 X)
Definition postgres.h:212
#define PointerGetDatum(X)
Definition postgres.h:354
#define InvalidOid
static int fb(int x)
void check_stack_depth(void)
Definition stack_depth.c:96
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