4 #include "plpy_typeio.h"
6 #include "utils/fmgrprotos.h"
7 #include "utils/jsonb.h"
8 #include "utils/numeric.h"
12 /* for PLyObject_AsString in plpy_typeio.c */
13 typedef char *(*PLyObject_AsString_t
) (PyObject
*plrv
);
14 static PLyObject_AsString_t PLyObject_AsString_p
;
16 typedef void (*PLy_elog_impl_t
) (int elevel
, const char *fmt
,...);
17 static PLy_elog_impl_t PLy_elog_impl_p
;
20 * decimal_constructor is a function from python library and used
21 * for transforming strings into python decimal type
23 static PyObject
*decimal_constructor
;
25 static PyObject
*PLyObject_FromJsonbContainer(JsonbContainer
*jsonb
);
26 static JsonbValue
*PLyObject_ToJsonbValue(PyObject
*obj
,
27 JsonbParseState
**jsonb_state
, bool is_elem
);
29 typedef PyObject
*(*PLyUnicode_FromStringAndSize_t
)
30 (const char *s
, Py_ssize_t size
);
31 static PLyUnicode_FromStringAndSize_t PLyUnicode_FromStringAndSize_p
;
34 * Module initialize function: fetch function pointers for cross-module calls.
39 /* Asserts verify that typedefs above match original declarations */
40 AssertVariableIsOfType(&PLyObject_AsString
, PLyObject_AsString_t
);
41 PLyObject_AsString_p
= (PLyObject_AsString_t
)
42 load_external_function("$libdir/" PLPYTHON_LIBNAME
, "PLyObject_AsString",
44 AssertVariableIsOfType(&PLyUnicode_FromStringAndSize
, PLyUnicode_FromStringAndSize_t
);
45 PLyUnicode_FromStringAndSize_p
= (PLyUnicode_FromStringAndSize_t
)
46 load_external_function("$libdir/" PLPYTHON_LIBNAME
, "PLyUnicode_FromStringAndSize",
48 AssertVariableIsOfType(&PLy_elog_impl
, PLy_elog_impl_t
);
49 PLy_elog_impl_p
= (PLy_elog_impl_t
)
50 load_external_function("$libdir/" PLPYTHON_LIBNAME
, "PLy_elog_impl",
54 /* These defines must be after the _PG_init */
55 #define PLyObject_AsString (PLyObject_AsString_p)
56 #define PLyUnicode_FromStringAndSize (PLyUnicode_FromStringAndSize_p)
58 #define PLy_elog (PLy_elog_impl_p)
61 * PLyUnicode_FromJsonbValue
63 * Transform string JsonbValue to Python string.
66 PLyUnicode_FromJsonbValue(JsonbValue
*jbv
)
68 Assert(jbv
->type
== jbvString
);
70 return PLyUnicode_FromStringAndSize(jbv
->val
.string
.val
, jbv
->val
.string
.len
);
74 * PLyUnicode_ToJsonbValue
76 * Transform Python string to JsonbValue.
79 PLyUnicode_ToJsonbValue(PyObject
*obj
, JsonbValue
*jbvElem
)
81 jbvElem
->type
= jbvString
;
82 jbvElem
->val
.string
.val
= PLyObject_AsString(obj
);
83 jbvElem
->val
.string
.len
= strlen(jbvElem
->val
.string
.val
);
87 * PLyObject_FromJsonbValue
89 * Transform JsonbValue to PyObject.
92 PLyObject_FromJsonbValue(JsonbValue
*jsonbValue
)
94 switch (jsonbValue
->type
)
100 return PLyObject_FromJsonbContainer(jsonbValue
->val
.binary
.data
);
107 num
= NumericGetDatum(jsonbValue
->val
.numeric
);
108 str
= DatumGetCString(DirectFunctionCall1(numeric_out
, num
));
110 return PyObject_CallFunction(decimal_constructor
, "s", str
);
114 return PLyUnicode_FromJsonbValue(jsonbValue
);
117 if (jsonbValue
->val
.boolean
)
123 elog(ERROR
, "unexpected jsonb value type: %d", jsonbValue
->type
);
129 * PLyObject_FromJsonbContainer
131 * Transform JsonbContainer to PyObject.
134 PLyObject_FromJsonbContainer(JsonbContainer
*jsonb
)
136 JsonbIteratorToken r
;
141 it
= JsonbIteratorInit(jsonb
);
142 r
= JsonbIteratorNext(&it
, &v
, true);
146 case WJB_BEGIN_ARRAY
:
147 if (v
.val
.array
.rawScalar
)
151 if ((r
= JsonbIteratorNext(&it
, &v
, true)) != WJB_ELEM
||
152 (r
= JsonbIteratorNext(&it
, &tmp
, true)) != WJB_END_ARRAY
||
153 (r
= JsonbIteratorNext(&it
, &tmp
, true)) != WJB_DONE
)
154 elog(ERROR
, "unexpected jsonb token: %d", r
);
156 result
= PLyObject_FromJsonbValue(&v
);
160 PyObject
*volatile elem
= NULL
;
162 result
= PyList_New(0);
168 while ((r
= JsonbIteratorNext(&it
, &v
, true)) != WJB_DONE
)
173 elem
= PLyObject_FromJsonbValue(&v
);
175 PyList_Append(result
, elem
);
190 case WJB_BEGIN_OBJECT
:
192 PyObject
*volatile result_v
= PyDict_New();
193 PyObject
*volatile key
= NULL
;
194 PyObject
*volatile val
= NULL
;
201 while ((r
= JsonbIteratorNext(&it
, &v
, true)) != WJB_DONE
)
206 key
= PLyUnicode_FromJsonbValue(&v
);
209 Py_XDECREF(result_v
);
214 if ((r
= JsonbIteratorNext(&it
, &v
, true)) != WJB_VALUE
)
215 elog(ERROR
, "unexpected jsonb token: %d", r
);
217 val
= PLyObject_FromJsonbValue(&v
);
222 Py_XDECREF(result_v
);
227 PyDict_SetItem(result_v
, key
, val
);
237 Py_XDECREF(result_v
);
249 elog(ERROR
, "unexpected jsonb token: %d", r
);
257 * PLyMapping_ToJsonbValue
259 * Transform Python dict to JsonbValue.
262 PLyMapping_ToJsonbValue(PyObject
*obj
, JsonbParseState
**jsonb_state
)
265 PyObject
*volatile items
;
266 JsonbValue
*volatile out
;
268 pcount
= PyMapping_Size(obj
);
269 items
= PyMapping_Items(obj
);
275 pushJsonbValue(jsonb_state
, WJB_BEGIN_OBJECT
, NULL
);
277 for (i
= 0; i
< pcount
; i
++)
280 PyObject
*item
= PyList_GetItem(items
, i
);
281 PyObject
*key
= PyTuple_GetItem(item
, 0);
282 PyObject
*value
= PyTuple_GetItem(item
, 1);
284 /* Python dictionary can have None as key */
287 jbvKey
.type
= jbvString
;
288 jbvKey
.val
.string
.len
= 0;
289 jbvKey
.val
.string
.val
= "";
293 /* All others types of keys we serialize to string */
294 PLyUnicode_ToJsonbValue(key
, &jbvKey
);
297 (void) pushJsonbValue(jsonb_state
, WJB_KEY
, &jbvKey
);
298 (void) PLyObject_ToJsonbValue(value
, jsonb_state
, false);
301 out
= pushJsonbValue(jsonb_state
, WJB_END_OBJECT
, NULL
);
313 * PLySequence_ToJsonbValue
315 * Transform python list to JsonbValue. Expects transformed PyObject and
316 * a state required for jsonb construction.
319 PLySequence_ToJsonbValue(PyObject
*obj
, JsonbParseState
**jsonb_state
)
323 PyObject
*volatile value
= NULL
;
325 pcount
= PySequence_Size(obj
);
327 pushJsonbValue(jsonb_state
, WJB_BEGIN_ARRAY
, NULL
);
331 for (i
= 0; i
< pcount
; i
++)
333 value
= PySequence_GetItem(obj
, i
);
336 (void) PLyObject_ToJsonbValue(value
, jsonb_state
, true);
348 return pushJsonbValue(jsonb_state
, WJB_END_ARRAY
, NULL
);
352 * PLyNumber_ToJsonbValue(PyObject *obj)
354 * Transform python number to JsonbValue.
357 PLyNumber_ToJsonbValue(PyObject
*obj
, JsonbValue
*jbvNum
)
360 char *str
= PLyObject_AsString(obj
);
366 numd
= DirectFunctionCall3(numeric_in
,
367 CStringGetDatum(str
),
368 ObjectIdGetDatum(InvalidOid
),
370 num
= DatumGetNumeric(numd
);
375 (errcode(ERRCODE_DATATYPE_MISMATCH
),
376 errmsg("could not convert value \"%s\" to jsonb", str
)));
383 * jsonb doesn't allow NaN or infinity (per JSON specification), so we
384 * have to reject those here explicitly.
386 if (numeric_is_nan(num
))
388 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE
),
389 errmsg("cannot convert NaN to jsonb")));
390 if (numeric_is_inf(num
))
392 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE
),
393 errmsg("cannot convert infinity to jsonb")));
395 jbvNum
->type
= jbvNumeric
;
396 jbvNum
->val
.numeric
= num
;
402 * PLyObject_ToJsonbValue(PyObject *obj)
404 * Transform python object to JsonbValue.
407 PLyObject_ToJsonbValue(PyObject
*obj
, JsonbParseState
**jsonb_state
, bool is_elem
)
411 if (!PyUnicode_Check(obj
))
413 if (PySequence_Check(obj
))
414 return PLySequence_ToJsonbValue(obj
, jsonb_state
);
415 else if (PyMapping_Check(obj
))
416 return PLyMapping_ToJsonbValue(obj
, jsonb_state
);
419 out
= palloc(sizeof(JsonbValue
));
423 else if (PyUnicode_Check(obj
))
424 PLyUnicode_ToJsonbValue(obj
, out
);
427 * PyNumber_Check() returns true for booleans, so boolean check should
430 else if (PyBool_Check(obj
))
433 out
->val
.boolean
= (obj
== Py_True
);
435 else if (PyNumber_Check(obj
))
436 out
= PLyNumber_ToJsonbValue(obj
, out
);
439 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
440 errmsg("Python type \"%s\" cannot be transformed to jsonb",
441 PLyObject_AsString((PyObject
*) obj
->ob_type
))));
443 /* Push result into 'jsonb_state' unless it is raw scalar value. */
444 return (*jsonb_state
?
445 pushJsonbValue(jsonb_state
, is_elem
? WJB_ELEM
: WJB_VALUE
, out
) :
452 * Transform python object to Jsonb datum
454 PG_FUNCTION_INFO_V1(plpython_to_jsonb
);
456 plpython_to_jsonb(PG_FUNCTION_ARGS
)
460 JsonbParseState
*jsonb_state
= NULL
;
462 obj
= (PyObject
*) PG_GETARG_POINTER(0);
463 out
= PLyObject_ToJsonbValue(obj
, &jsonb_state
, true);
464 PG_RETURN_POINTER(JsonbValueToJsonb(out
));
470 * Transform Jsonb datum to PyObject and return it as internal.
472 PG_FUNCTION_INFO_V1(jsonb_to_plpython
);
474 jsonb_to_plpython(PG_FUNCTION_ARGS
)
477 Jsonb
*in
= PG_GETARG_JSONB_P(0);
480 * Initialize pointer to Decimal constructor. First we try "cdecimal", C
481 * version of decimal library. In case of failure we use slower "decimal"
484 if (!decimal_constructor
)
486 PyObject
*decimal_module
= PyImport_ImportModule("cdecimal");
491 decimal_module
= PyImport_ImportModule("decimal");
493 Assert(decimal_module
);
494 decimal_constructor
= PyObject_GetAttrString(decimal_module
, "Decimal");
497 result
= PLyObject_FromJsonbContainer(&in
->root
);
499 PLy_elog(ERROR
, "transformation from jsonb to Python failed");
501 return PointerGetDatum(result
);