Age Owner TLA Line data Source code
1 : #include "postgres.h"
2 :
3 : #include "plpy_elog.h"
4 : #include "plpy_typeio.h"
5 : #include "plpython.h"
6 : #include "utils/fmgrprotos.h"
7 : #include "utils/jsonb.h"
8 : #include "utils/numeric.h"
9 :
1838 peter_e 10 CBC 1 : PG_MODULE_MAGIC;
11 :
12 : /* for PLyObject_AsString in plpy_typeio.c */
13 : typedef char *(*PLyObject_AsString_t) (PyObject *plrv);
14 : static PLyObject_AsString_t PLyObject_AsString_p;
15 :
16 : typedef void (*PLy_elog_impl_t) (int elevel, const char *fmt,...);
17 : static PLy_elog_impl_t PLy_elog_impl_p;
18 :
19 : /*
20 : * decimal_constructor is a function from python library and used
21 : * for transforming strings into python decimal type
22 : */
23 : static PyObject *decimal_constructor;
24 :
25 : static PyObject *PLyObject_FromJsonbContainer(JsonbContainer *jsonb);
26 : static JsonbValue *PLyObject_ToJsonbValue(PyObject *obj,
27 : JsonbParseState **jsonb_state, bool is_elem);
28 :
29 : typedef PyObject *(*PLyUnicode_FromStringAndSize_t)
30 : (const char *s, Py_ssize_t size);
31 : static PLyUnicode_FromStringAndSize_t PLyUnicode_FromStringAndSize_p;
32 :
33 : /*
34 : * Module initialize function: fetch function pointers for cross-module calls.
1838 peter_e 35 ECB : */
36 : void
1838 peter_e 37 GIC 1 : _PG_init(void)
38 : {
1838 peter_e 39 ECB : /* Asserts verify that typedefs above match original declarations */
40 : AssertVariableIsOfType(&PLyObject_AsString, PLyObject_AsString_t);
1838 peter_e 41 GIC 1 : PLyObject_AsString_p = (PLyObject_AsString_t)
42 1 : load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLyObject_AsString",
1838 peter_e 43 ECB : true, NULL);
44 : AssertVariableIsOfType(&PLyUnicode_FromStringAndSize, PLyUnicode_FromStringAndSize_t);
1838 peter_e 45 GIC 1 : PLyUnicode_FromStringAndSize_p = (PLyUnicode_FromStringAndSize_t)
46 1 : load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLyUnicode_FromStringAndSize",
1838 peter_e 47 ECB : true, NULL);
48 : AssertVariableIsOfType(&PLy_elog_impl, PLy_elog_impl_t);
1838 peter_e 49 GIC 1 : PLy_elog_impl_p = (PLy_elog_impl_t)
1838 peter_e 50 CBC 1 : load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLy_elog_impl",
51 : true, NULL);
1838 peter_e 52 GIC 1 : }
53 :
54 : /* These defines must be after the _PG_init */
55 : #define PLyObject_AsString (PLyObject_AsString_p)
56 : #define PLyUnicode_FromStringAndSize (PLyUnicode_FromStringAndSize_p)
57 : #undef PLy_elog
58 : #define PLy_elog (PLy_elog_impl_p)
59 :
60 : /*
61 : * PLyUnicode_FromJsonbValue
62 : *
63 : * Transform string JsonbValue to Python string.
1838 peter_e 64 ECB : */
65 : static PyObject *
398 andres 66 CBC 21 : PLyUnicode_FromJsonbValue(JsonbValue *jbv)
67 : {
1838 peter_e 68 21 : Assert(jbv->type == jbvString);
69 :
398 andres 70 GIC 21 : return PLyUnicode_FromStringAndSize(jbv->val.string.val, jbv->val.string.len);
71 : }
72 :
73 : /*
74 : * PLyUnicode_ToJsonbValue
75 : *
76 : * Transform Python string to JsonbValue.
1838 peter_e 77 ECB : */
78 : static void
398 andres 79 CBC 13 : PLyUnicode_ToJsonbValue(PyObject *obj, JsonbValue *jbvElem)
1838 peter_e 80 ECB : {
1838 peter_e 81 CBC 13 : jbvElem->type = jbvString;
82 13 : jbvElem->val.string.val = PLyObject_AsString(obj);
1838 peter_e 83 GIC 13 : jbvElem->val.string.len = strlen(jbvElem->val.string.val);
84 13 : }
85 :
86 : /*
87 : * PLyObject_FromJsonbValue
88 : *
89 : * Transform JsonbValue to PyObject.
1838 peter_e 90 ECB : */
91 : static PyObject *
1838 peter_e 92 CBC 40 : PLyObject_FromJsonbValue(JsonbValue *jsonbValue)
93 : {
94 40 : switch (jsonbValue->type)
1838 peter_e 95 ECB : {
1838 peter_e 96 GIC 5 : case jbvNull:
1838 peter_e 97 CBC 5 : Py_RETURN_NONE;
1838 peter_e 98 ECB :
1838 peter_e 99 GIC 4 : case jbvBinary:
1838 peter_e 100 CBC 4 : return PLyObject_FromJsonbContainer(jsonbValue->val.binary.data);
101 :
1838 peter_e 102 GIC 18 : case jbvNumeric:
103 : {
104 : Datum num;
1838 peter_e 105 ECB : char *str;
106 :
1838 peter_e 107 GIC 18 : num = NumericGetDatum(jsonbValue->val.numeric);
1838 peter_e 108 CBC 18 : str = DatumGetCString(DirectFunctionCall1(numeric_out, num));
109 :
1838 peter_e 110 GIC 18 : return PyObject_CallFunction(decimal_constructor, "s", str);
1838 peter_e 111 ECB : }
112 :
1838 peter_e 113 GIC 8 : case jbvString:
398 andres 114 CBC 8 : return PLyUnicode_FromJsonbValue(jsonbValue);
1838 peter_e 115 ECB :
1838 peter_e 116 CBC 5 : case jbvBool:
1838 peter_e 117 GIC 5 : if (jsonbValue->val.boolean)
1838 peter_e 118 GBC 5 : Py_RETURN_TRUE;
119 : else
1838 peter_e 120 UBC 0 : Py_RETURN_FALSE;
1838 peter_e 121 EUB :
1838 peter_e 122 UIC 0 : default:
123 0 : elog(ERROR, "unexpected jsonb value type: %d", jsonbValue->type);
124 : return NULL;
125 : }
126 : }
127 :
128 : /*
129 : * PLyObject_FromJsonbContainer
130 : *
131 : * Transform JsonbContainer to PyObject.
1838 peter_e 132 ECB : */
133 : static PyObject *
1838 peter_e 134 GIC 30 : PLyObject_FromJsonbContainer(JsonbContainer *jsonb)
135 : {
136 : JsonbIteratorToken r;
137 : JsonbValue v;
138 : JsonbIterator *it;
1838 peter_e 139 ECB : PyObject *result;
140 :
1838 peter_e 141 GIC 30 : it = JsonbIteratorInit(jsonb);
1838 peter_e 142 CBC 30 : r = JsonbIteratorNext(&it, &v, true);
143 :
144 30 : switch (r)
1838 peter_e 145 ECB : {
1838 peter_e 146 GIC 20 : case WJB_BEGIN_ARRAY:
147 20 : if (v.val.array.rawScalar)
148 : {
1838 peter_e 149 ECB : JsonbValue tmp;
150 :
1838 peter_e 151 CBC 9 : if ((r = JsonbIteratorNext(&it, &v, true)) != WJB_ELEM ||
1838 peter_e 152 GBC 9 : (r = JsonbIteratorNext(&it, &tmp, true)) != WJB_END_ARRAY ||
1838 peter_e 153 GIC 9 : (r = JsonbIteratorNext(&it, &tmp, true)) != WJB_DONE)
1838 peter_e 154 LBC 0 : elog(ERROR, "unexpected jsonb token: %d", r);
155 :
1838 peter_e 156 GIC 9 : result = PLyObject_FromJsonbValue(&v);
157 : }
1838 peter_e 158 ECB : else
159 : {
1464 tgl 160 CBC 11 : PyObject *volatile elem = NULL;
1464 tgl 161 ECB :
1838 peter_e 162 GBC 11 : result = PyList_New(0);
1838 peter_e 163 GIC 11 : if (!result)
1838 peter_e 164 LBC 0 : return NULL;
165 :
1464 tgl 166 CBC 11 : PG_TRY();
167 : {
168 40 : while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
1838 peter_e 169 ECB : {
1464 tgl 170 GIC 29 : if (r != WJB_ELEM)
1464 tgl 171 CBC 11 : continue;
172 :
173 18 : elem = PLyObject_FromJsonbValue(&v);
1838 peter_e 174 ECB :
1838 peter_e 175 CBC 18 : PyList_Append(result, elem);
1838 peter_e 176 GIC 18 : Py_XDECREF(elem);
1464 tgl 177 18 : elem = NULL;
1838 peter_e 178 EUB : }
179 : }
1464 tgl 180 UBC 0 : PG_CATCH();
1464 tgl 181 EUB : {
1464 tgl 182 UBC 0 : Py_XDECREF(elem);
1464 tgl 183 UIC 0 : Py_XDECREF(result);
1464 tgl 184 LBC 0 : PG_RE_THROW();
185 : }
1464 tgl 186 CBC 11 : PG_END_TRY();
187 : }
1838 peter_e 188 20 : break;
189 :
190 10 : case WJB_BEGIN_OBJECT:
1838 peter_e 191 ECB : {
1464 tgl 192 CBC 10 : PyObject *volatile result_v = PyDict_New();
1464 tgl 193 GIC 10 : PyObject *volatile key = NULL;
1464 tgl 194 CBC 10 : PyObject *volatile val = NULL;
1838 peter_e 195 EUB :
1464 tgl 196 GIC 10 : if (!result_v)
1464 tgl 197 LBC 0 : return NULL;
198 :
1464 tgl 199 CBC 10 : PG_TRY();
200 : {
201 33 : while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
1838 peter_e 202 ECB : {
1464 tgl 203 GIC 23 : if (r != WJB_KEY)
1464 tgl 204 CBC 10 : continue;
1838 peter_e 205 ECB :
398 andres 206 GIC 13 : key = PLyUnicode_FromJsonbValue(&v);
1464 tgl 207 GBC 13 : if (!key)
1464 tgl 208 EUB : {
1464 tgl 209 UBC 0 : Py_XDECREF(result_v);
1464 tgl 210 UIC 0 : result_v = NULL;
211 0 : break;
1464 tgl 212 ECB : }
1464 tgl 213 EUB :
1464 tgl 214 GIC 13 : if ((r = JsonbIteratorNext(&it, &v, true)) != WJB_VALUE)
1464 tgl 215 LBC 0 : elog(ERROR, "unexpected jsonb token: %d", r);
1464 tgl 216 ECB :
1464 tgl 217 GIC 13 : val = PLyObject_FromJsonbValue(&v);
1464 tgl 218 GBC 13 : if (!val)
1838 peter_e 219 EUB : {
1838 peter_e 220 UBC 0 : Py_XDECREF(key);
1464 tgl 221 0 : key = NULL;
222 0 : Py_XDECREF(result_v);
1464 tgl 223 UIC 0 : result_v = NULL;
224 0 : break;
1838 peter_e 225 ECB : }
226 :
1464 tgl 227 CBC 13 : PyDict_SetItem(result_v, key, val);
1838 peter_e 228 ECB :
1464 tgl 229 CBC 13 : Py_XDECREF(key);
230 13 : key = NULL;
1464 tgl 231 GIC 13 : Py_XDECREF(val);
232 13 : val = NULL;
1464 tgl 233 EUB : }
234 : }
1464 tgl 235 UBC 0 : PG_CATCH();
1464 tgl 236 EUB : {
1464 tgl 237 UBC 0 : Py_XDECREF(result_v);
1838 peter_e 238 0 : Py_XDECREF(key);
1464 tgl 239 UIC 0 : Py_XDECREF(val);
1464 tgl 240 LBC 0 : PG_RE_THROW();
241 : }
1464 tgl 242 CBC 10 : PG_END_TRY();
243 :
244 10 : result = result_v;
245 : }
1838 peter_e 246 GBC 10 : break;
1838 peter_e 247 EUB :
1838 peter_e 248 UIC 0 : default:
249 0 : elog(ERROR, "unexpected jsonb token: %d", r);
250 : return NULL;
1838 peter_e 251 ECB : }
252 :
1838 peter_e 253 GIC 30 : return result;
254 : }
255 :
256 : /*
257 : * PLyMapping_ToJsonbValue
258 : *
259 : * Transform Python dict to JsonbValue.
1838 peter_e 260 ECB : */
261 : static JsonbValue *
1838 peter_e 262 GIC 5 : PLyMapping_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state)
263 : {
264 : Py_ssize_t pcount;
265 : PyObject *volatile items;
1464 tgl 266 ECB : JsonbValue *volatile out;
1838 peter_e 267 :
1838 peter_e 268 GIC 5 : pcount = PyMapping_Size(obj);
1487 peter 269 CBC 5 : items = PyMapping_Items(obj);
270 :
1838 peter_e 271 GIC 5 : PG_TRY();
272 : {
1838 peter_e 273 ECB : Py_ssize_t i;
274 :
1838 peter_e 275 CBC 5 : pushJsonbValue(jsonb_state, WJB_BEGIN_OBJECT, NULL);
276 :
1838 peter_e 277 GIC 12 : for (i = 0; i < pcount; i++)
1838 peter_e 278 ECB : {
279 : JsonbValue jbvKey;
1838 peter_e 280 CBC 7 : PyObject *item = PyList_GetItem(items, i);
1838 peter_e 281 GIC 7 : PyObject *key = PyTuple_GetItem(item, 0);
282 7 : PyObject *value = PyTuple_GetItem(item, 1);
1838 peter_e 283 ECB :
284 : /* Python dictionary can have None as key */
1838 peter_e 285 CBC 7 : if (key == Py_None)
1838 peter_e 286 ECB : {
1838 peter_e 287 CBC 1 : jbvKey.type = jbvString;
1838 peter_e 288 GIC 1 : jbvKey.val.string.len = 0;
289 1 : jbvKey.val.string.val = "";
290 : }
291 : else
1838 peter_e 292 ECB : {
293 : /* All others types of keys we serialize to string */
398 andres 294 GIC 6 : PLyUnicode_ToJsonbValue(key, &jbvKey);
1838 peter_e 295 ECB : }
296 :
1838 peter_e 297 GIC 7 : (void) pushJsonbValue(jsonb_state, WJB_KEY, &jbvKey);
298 7 : (void) PLyObject_ToJsonbValue(value, jsonb_state, false);
1838 peter_e 299 ECB : }
300 :
1838 peter_e 301 GBC 5 : out = pushJsonbValue(jsonb_state, WJB_END_OBJECT, NULL);
302 : }
1255 peter 303 LBC 0 : PG_FINALLY();
304 : {
1487 peter 305 CBC 5 : Py_DECREF(items);
306 : }
1838 peter_e 307 5 : PG_END_TRY();
308 :
1838 peter_e 309 GIC 5 : return out;
310 : }
311 :
312 : /*
313 : * PLySequence_ToJsonbValue
314 : *
315 : * Transform python list to JsonbValue. Expects transformed PyObject and
316 : * a state required for jsonb construction.
1838 peter_e 317 ECB : */
318 : static JsonbValue *
1838 peter_e 319 GIC 10 : PLySequence_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state)
320 : {
1838 peter_e 321 ECB : Py_ssize_t i;
322 : Py_ssize_t pcount;
1464 tgl 323 CBC 10 : PyObject *volatile value = NULL;
324 :
1838 peter_e 325 10 : pcount = PySequence_Size(obj);
326 :
327 10 : pushJsonbValue(jsonb_state, WJB_BEGIN_ARRAY, NULL);
328 :
1464 tgl 329 10 : PG_TRY();
330 : {
331 28 : for (i = 0; i < pcount; i++)
1464 tgl 332 ECB : {
1464 tgl 333 GIC 18 : value = PySequence_GetItem(obj, i);
1464 tgl 334 CBC 18 : Assert(value);
1759 akorotkov 335 ECB :
1464 tgl 336 CBC 18 : (void) PLyObject_ToJsonbValue(value, jsonb_state, true);
1464 tgl 337 GIC 18 : Py_XDECREF(value);
338 18 : value = NULL;
1464 tgl 339 EUB : }
340 : }
1464 tgl 341 UBC 0 : PG_CATCH();
1464 tgl 342 EUB : {
1759 akorotkov 343 UIC 0 : Py_XDECREF(value);
1464 tgl 344 LBC 0 : PG_RE_THROW();
345 : }
1464 tgl 346 CBC 10 : PG_END_TRY();
347 :
1838 peter_e 348 GIC 10 : return pushJsonbValue(jsonb_state, WJB_END_ARRAY, NULL);
349 : }
350 :
351 : /*
352 : * PLyNumber_ToJsonbValue(PyObject *obj)
353 : *
354 : * Transform python number to JsonbValue.
1838 peter_e 355 ECB : */
356 : static JsonbValue *
1838 peter_e 357 GIC 16 : PLyNumber_ToJsonbValue(PyObject *obj, JsonbValue *jbvNum)
1838 peter_e 358 ECB : {
359 : Numeric num;
1838 peter_e 360 CBC 16 : char *str = PLyObject_AsString(obj);
361 :
1838 peter_e 362 GIC 16 : PG_TRY();
363 : {
1803 tgl 364 ECB : Datum numd;
365 :
1803 tgl 366 GIC 16 : numd = DirectFunctionCall3(numeric_in,
367 : CStringGetDatum(str),
1803 tgl 368 ECB : ObjectIdGetDatum(InvalidOid),
369 : Int32GetDatum(-1));
1803 tgl 370 CBC 15 : num = DatumGetNumeric(numd);
371 : }
1838 peter_e 372 1 : PG_CATCH();
373 : {
1838 peter_e 374 GIC 1 : ereport(ERROR,
375 : (errcode(ERRCODE_DATATYPE_MISMATCH),
1165 alvherre 376 ECB : errmsg("could not convert value \"%s\" to jsonb", str)));
377 : }
1838 peter_e 378 CBC 15 : PG_END_TRY();
379 :
1838 peter_e 380 GIC 15 : pfree(str);
381 :
382 : /*
383 : * jsonb doesn't allow NaN or infinity (per JSON specification), so we
991 tgl 384 ECB : * have to reject those here explicitly.
1803 peter_e 385 EUB : */
1803 peter_e 386 GIC 15 : if (numeric_is_nan(num))
1803 peter_e 387 UIC 0 : ereport(ERROR,
1803 peter_e 388 ECB : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
1165 alvherre 389 EUB : errmsg("cannot convert NaN to jsonb")));
991 tgl 390 GIC 15 : if (numeric_is_inf(num))
991 tgl 391 UIC 0 : ereport(ERROR,
392 : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
991 tgl 393 ECB : errmsg("cannot convert infinity to jsonb")));
1803 peter_e 394 :
1838 peter_e 395 GIC 15 : jbvNum->type = jbvNumeric;
1838 peter_e 396 CBC 15 : jbvNum->val.numeric = num;
397 :
1838 peter_e 398 GIC 15 : return jbvNum;
399 : }
400 :
401 : /*
402 : * PLyObject_ToJsonbValue(PyObject *obj)
403 : *
404 : * Transform python object to JsonbValue.
1838 peter_e 405 ECB : */
406 : static JsonbValue *
1838 peter_e 407 GIC 47 : PLyObject_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state, bool is_elem)
408 : {
1838 peter_e 409 ECB : JsonbValue *out;
410 :
398 andres 411 CBC 47 : if (!PyUnicode_Check(obj))
1838 peter_e 412 ECB : {
1838 peter_e 413 CBC 40 : if (PySequence_Check(obj))
414 10 : return PLySequence_ToJsonbValue(obj, jsonb_state);
1838 peter_e 415 GIC 30 : else if (PyMapping_Check(obj))
416 5 : return PLyMapping_ToJsonbValue(obj, jsonb_state);
1838 peter_e 417 ECB : }
418 :
1165 tgl 419 CBC 32 : out = palloc(sizeof(JsonbValue));
1838 peter_e 420 ECB :
1838 peter_e 421 CBC 32 : if (obj == Py_None)
422 4 : out->type = jbvNull;
398 andres 423 GIC 28 : else if (PyUnicode_Check(obj))
424 7 : PLyUnicode_ToJsonbValue(obj, out);
425 :
426 : /*
427 : * PyNumber_Check() returns true for booleans, so boolean check should
1809 tgl 428 ECB : * come first.
429 : */
1838 peter_e 430 CBC 21 : else if (PyBool_Check(obj))
1838 peter_e 431 ECB : {
1838 peter_e 432 GIC 5 : out->type = jbvBool;
1838 peter_e 433 CBC 5 : out->val.boolean = (obj == Py_True);
1838 peter_e 434 ECB : }
1838 peter_e 435 GIC 16 : else if (PyNumber_Check(obj))
1838 peter_e 436 GBC 16 : out = PLyNumber_ToJsonbValue(obj, out);
437 : else
1838 peter_e 438 UIC 0 : ereport(ERROR,
439 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
440 : errmsg("Python type \"%s\" cannot be transformed to jsonb",
441 : PLyObject_AsString((PyObject *) obj->ob_type))));
1838 peter_e 442 ECB :
443 : /* Push result into 'jsonb_state' unless it is raw scalar value. */
1838 peter_e 444 GIC 31 : return (*jsonb_state ?
445 31 : pushJsonbValue(jsonb_state, is_elem ? WJB_ELEM : WJB_VALUE, out) :
446 : out);
447 : }
448 :
449 : /*
450 : * plpython_to_jsonb
451 : *
1838 peter_e 452 ECB : * Transform python object to Jsonb datum
453 : */
1838 peter_e 454 CBC 2 : PG_FUNCTION_INFO_V1(plpython_to_jsonb);
455 : Datum
1838 peter_e 456 GIC 22 : plpython_to_jsonb(PG_FUNCTION_ARGS)
457 : {
1838 peter_e 458 ECB : PyObject *obj;
459 : JsonbValue *out;
1838 peter_e 460 CBC 22 : JsonbParseState *jsonb_state = NULL;
1838 peter_e 461 ECB :
1838 peter_e 462 CBC 22 : obj = (PyObject *) PG_GETARG_POINTER(0);
1838 peter_e 463 GIC 22 : out = PLyObject_ToJsonbValue(obj, &jsonb_state, true);
464 21 : PG_RETURN_POINTER(JsonbValueToJsonb(out));
465 : }
466 :
467 : /*
468 : * jsonb_to_plpython
469 : *
1838 peter_e 470 ECB : * Transform Jsonb datum to PyObject and return it as internal.
471 : */
1838 peter_e 472 CBC 2 : PG_FUNCTION_INFO_V1(jsonb_to_plpython);
473 : Datum
1838 peter_e 474 GIC 26 : jsonb_to_plpython(PG_FUNCTION_ARGS)
1838 peter_e 475 ECB : {
476 : PyObject *result;
1838 peter_e 477 GIC 26 : Jsonb *in = PG_GETARG_JSONB_P(0);
478 :
479 : /*
480 : * Initialize pointer to Decimal constructor. First we try "cdecimal", C
481 : * version of decimal library. In case of failure we use slower "decimal"
1838 peter_e 482 ECB : * module.
483 : */
1838 peter_e 484 CBC 26 : if (!decimal_constructor)
485 : {
486 1 : PyObject *decimal_module = PyImport_ImportModule("cdecimal");
487 :
488 1 : if (!decimal_module)
1838 peter_e 489 ECB : {
1838 peter_e 490 GIC 1 : PyErr_Clear();
1838 peter_e 491 CBC 1 : decimal_module = PyImport_ImportModule("decimal");
1838 peter_e 492 ECB : }
1838 peter_e 493 GIC 1 : Assert(decimal_module);
494 1 : decimal_constructor = PyObject_GetAttrString(decimal_module, "Decimal");
1838 peter_e 495 ECB : }
496 :
1838 peter_e 497 GBC 26 : result = PLyObject_FromJsonbContainer(&in->root);
1838 peter_e 498 GIC 26 : if (!result)
1838 peter_e 499 LBC 0 : PLy_elog(ERROR, "transformation from jsonb to Python failed");
500 :
1838 peter_e 501 GIC 26 : return PointerGetDatum(result);
502 : }
|