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 :
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.
35 ECB : */
36 : void
37 GIC 1 : _PG_init(void)
38 : {
39 ECB : /* Asserts verify that typedefs above match original declarations */
40 : AssertVariableIsOfType(&PLyObject_AsString, PLyObject_AsString_t);
41 GIC 1 : PLyObject_AsString_p = (PLyObject_AsString_t)
42 1 : load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLyObject_AsString",
43 ECB : true, NULL);
44 : AssertVariableIsOfType(&PLyUnicode_FromStringAndSize, PLyUnicode_FromStringAndSize_t);
45 GIC 1 : PLyUnicode_FromStringAndSize_p = (PLyUnicode_FromStringAndSize_t)
46 1 : load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLyUnicode_FromStringAndSize",
47 ECB : true, NULL);
48 : AssertVariableIsOfType(&PLy_elog_impl, PLy_elog_impl_t);
49 GIC 1 : PLy_elog_impl_p = (PLy_elog_impl_t)
50 CBC 1 : load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLy_elog_impl",
51 : true, NULL);
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.
64 ECB : */
65 : static PyObject *
66 CBC 21 : PLyUnicode_FromJsonbValue(JsonbValue *jbv)
67 : {
68 21 : Assert(jbv->type == jbvString);
69 :
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.
77 ECB : */
78 : static void
79 CBC 13 : PLyUnicode_ToJsonbValue(PyObject *obj, JsonbValue *jbvElem)
80 ECB : {
81 CBC 13 : jbvElem->type = jbvString;
82 13 : jbvElem->val.string.val = PLyObject_AsString(obj);
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.
90 ECB : */
91 : static PyObject *
92 CBC 40 : PLyObject_FromJsonbValue(JsonbValue *jsonbValue)
93 : {
94 40 : switch (jsonbValue->type)
95 ECB : {
96 GIC 5 : case jbvNull:
97 CBC 5 : Py_RETURN_NONE;
98 ECB :
99 GIC 4 : case jbvBinary:
100 CBC 4 : return PLyObject_FromJsonbContainer(jsonbValue->val.binary.data);
101 :
102 GIC 18 : case jbvNumeric:
103 : {
104 : Datum num;
105 ECB : char *str;
106 :
107 GIC 18 : num = NumericGetDatum(jsonbValue->val.numeric);
108 CBC 18 : str = DatumGetCString(DirectFunctionCall1(numeric_out, num));
109 :
110 GIC 18 : return PyObject_CallFunction(decimal_constructor, "s", str);
111 ECB : }
112 :
113 GIC 8 : case jbvString:
114 CBC 8 : return PLyUnicode_FromJsonbValue(jsonbValue);
115 ECB :
116 CBC 5 : case jbvBool:
117 GIC 5 : if (jsonbValue->val.boolean)
118 GBC 5 : Py_RETURN_TRUE;
119 : else
120 UBC 0 : Py_RETURN_FALSE;
121 EUB :
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.
132 ECB : */
133 : static PyObject *
134 GIC 30 : PLyObject_FromJsonbContainer(JsonbContainer *jsonb)
135 : {
136 : JsonbIteratorToken r;
137 : JsonbValue v;
138 : JsonbIterator *it;
139 ECB : PyObject *result;
140 :
141 GIC 30 : it = JsonbIteratorInit(jsonb);
142 CBC 30 : r = JsonbIteratorNext(&it, &v, true);
143 :
144 30 : switch (r)
145 ECB : {
146 GIC 20 : case WJB_BEGIN_ARRAY:
147 20 : if (v.val.array.rawScalar)
148 : {
149 ECB : JsonbValue tmp;
150 :
151 CBC 9 : if ((r = JsonbIteratorNext(&it, &v, true)) != WJB_ELEM ||
152 GBC 9 : (r = JsonbIteratorNext(&it, &tmp, true)) != WJB_END_ARRAY ||
153 GIC 9 : (r = JsonbIteratorNext(&it, &tmp, true)) != WJB_DONE)
154 LBC 0 : elog(ERROR, "unexpected jsonb token: %d", r);
155 :
156 GIC 9 : result = PLyObject_FromJsonbValue(&v);
157 : }
158 ECB : else
159 : {
160 CBC 11 : PyObject *volatile elem = NULL;
161 ECB :
162 GBC 11 : result = PyList_New(0);
163 GIC 11 : if (!result)
164 LBC 0 : return NULL;
165 :
166 CBC 11 : PG_TRY();
167 : {
168 40 : while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
169 ECB : {
170 GIC 29 : if (r != WJB_ELEM)
171 CBC 11 : continue;
172 :
173 18 : elem = PLyObject_FromJsonbValue(&v);
174 ECB :
175 CBC 18 : PyList_Append(result, elem);
176 GIC 18 : Py_XDECREF(elem);
177 18 : elem = NULL;
178 EUB : }
179 : }
180 UBC 0 : PG_CATCH();
181 EUB : {
182 UBC 0 : Py_XDECREF(elem);
183 UIC 0 : Py_XDECREF(result);
184 LBC 0 : PG_RE_THROW();
185 : }
186 CBC 11 : PG_END_TRY();
187 : }
188 20 : break;
189 :
190 10 : case WJB_BEGIN_OBJECT:
191 ECB : {
192 CBC 10 : PyObject *volatile result_v = PyDict_New();
193 GIC 10 : PyObject *volatile key = NULL;
194 CBC 10 : PyObject *volatile val = NULL;
195 EUB :
196 GIC 10 : if (!result_v)
197 LBC 0 : return NULL;
198 :
199 CBC 10 : PG_TRY();
200 : {
201 33 : while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
202 ECB : {
203 GIC 23 : if (r != WJB_KEY)
204 CBC 10 : continue;
205 ECB :
206 GIC 13 : key = PLyUnicode_FromJsonbValue(&v);
207 GBC 13 : if (!key)
208 EUB : {
209 UBC 0 : Py_XDECREF(result_v);
210 UIC 0 : result_v = NULL;
211 0 : break;
212 ECB : }
213 EUB :
214 GIC 13 : if ((r = JsonbIteratorNext(&it, &v, true)) != WJB_VALUE)
215 LBC 0 : elog(ERROR, "unexpected jsonb token: %d", r);
216 ECB :
217 GIC 13 : val = PLyObject_FromJsonbValue(&v);
218 GBC 13 : if (!val)
219 EUB : {
220 UBC 0 : Py_XDECREF(key);
221 0 : key = NULL;
222 0 : Py_XDECREF(result_v);
223 UIC 0 : result_v = NULL;
224 0 : break;
225 ECB : }
226 :
227 CBC 13 : PyDict_SetItem(result_v, key, val);
228 ECB :
229 CBC 13 : Py_XDECREF(key);
230 13 : key = NULL;
231 GIC 13 : Py_XDECREF(val);
232 13 : val = NULL;
233 EUB : }
234 : }
235 UBC 0 : PG_CATCH();
236 EUB : {
237 UBC 0 : Py_XDECREF(result_v);
238 0 : Py_XDECREF(key);
239 UIC 0 : Py_XDECREF(val);
240 LBC 0 : PG_RE_THROW();
241 : }
242 CBC 10 : PG_END_TRY();
243 :
244 10 : result = result_v;
245 : }
246 GBC 10 : break;
247 EUB :
248 UIC 0 : default:
249 0 : elog(ERROR, "unexpected jsonb token: %d", r);
250 : return NULL;
251 ECB : }
252 :
253 GIC 30 : return result;
254 : }
255 :
256 : /*
257 : * PLyMapping_ToJsonbValue
258 : *
259 : * Transform Python dict to JsonbValue.
260 ECB : */
261 : static JsonbValue *
262 GIC 5 : PLyMapping_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state)
263 : {
264 : Py_ssize_t pcount;
265 : PyObject *volatile items;
266 ECB : JsonbValue *volatile out;
267 :
268 GIC 5 : pcount = PyMapping_Size(obj);
269 CBC 5 : items = PyMapping_Items(obj);
270 :
271 GIC 5 : PG_TRY();
272 : {
273 ECB : Py_ssize_t i;
274 :
275 CBC 5 : pushJsonbValue(jsonb_state, WJB_BEGIN_OBJECT, NULL);
276 :
277 GIC 12 : for (i = 0; i < pcount; i++)
278 ECB : {
279 : JsonbValue jbvKey;
280 CBC 7 : PyObject *item = PyList_GetItem(items, i);
281 GIC 7 : PyObject *key = PyTuple_GetItem(item, 0);
282 7 : PyObject *value = PyTuple_GetItem(item, 1);
283 ECB :
284 : /* Python dictionary can have None as key */
285 CBC 7 : if (key == Py_None)
286 ECB : {
287 CBC 1 : jbvKey.type = jbvString;
288 GIC 1 : jbvKey.val.string.len = 0;
289 1 : jbvKey.val.string.val = "";
290 : }
291 : else
292 ECB : {
293 : /* All others types of keys we serialize to string */
294 GIC 6 : PLyUnicode_ToJsonbValue(key, &jbvKey);
295 ECB : }
296 :
297 GIC 7 : (void) pushJsonbValue(jsonb_state, WJB_KEY, &jbvKey);
298 7 : (void) PLyObject_ToJsonbValue(value, jsonb_state, false);
299 ECB : }
300 :
301 GBC 5 : out = pushJsonbValue(jsonb_state, WJB_END_OBJECT, NULL);
302 : }
303 LBC 0 : PG_FINALLY();
304 : {
305 CBC 5 : Py_DECREF(items);
306 : }
307 5 : PG_END_TRY();
308 :
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.
317 ECB : */
318 : static JsonbValue *
319 GIC 10 : PLySequence_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state)
320 : {
321 ECB : Py_ssize_t i;
322 : Py_ssize_t pcount;
323 CBC 10 : PyObject *volatile value = NULL;
324 :
325 10 : pcount = PySequence_Size(obj);
326 :
327 10 : pushJsonbValue(jsonb_state, WJB_BEGIN_ARRAY, NULL);
328 :
329 10 : PG_TRY();
330 : {
331 28 : for (i = 0; i < pcount; i++)
332 ECB : {
333 GIC 18 : value = PySequence_GetItem(obj, i);
334 CBC 18 : Assert(value);
335 ECB :
336 CBC 18 : (void) PLyObject_ToJsonbValue(value, jsonb_state, true);
337 GIC 18 : Py_XDECREF(value);
338 18 : value = NULL;
339 EUB : }
340 : }
341 UBC 0 : PG_CATCH();
342 EUB : {
343 UIC 0 : Py_XDECREF(value);
344 LBC 0 : PG_RE_THROW();
345 : }
346 CBC 10 : PG_END_TRY();
347 :
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.
355 ECB : */
356 : static JsonbValue *
357 GIC 16 : PLyNumber_ToJsonbValue(PyObject *obj, JsonbValue *jbvNum)
358 ECB : {
359 : Numeric num;
360 CBC 16 : char *str = PLyObject_AsString(obj);
361 :
362 GIC 16 : PG_TRY();
363 : {
364 ECB : Datum numd;
365 :
366 GIC 16 : numd = DirectFunctionCall3(numeric_in,
367 : CStringGetDatum(str),
368 ECB : ObjectIdGetDatum(InvalidOid),
369 : Int32GetDatum(-1));
370 CBC 15 : num = DatumGetNumeric(numd);
371 : }
372 1 : PG_CATCH();
373 : {
374 GIC 1 : ereport(ERROR,
375 : (errcode(ERRCODE_DATATYPE_MISMATCH),
376 ECB : errmsg("could not convert value \"%s\" to jsonb", str)));
377 : }
378 CBC 15 : PG_END_TRY();
379 :
380 GIC 15 : pfree(str);
381 :
382 : /*
383 : * jsonb doesn't allow NaN or infinity (per JSON specification), so we
384 ECB : * have to reject those here explicitly.
385 EUB : */
386 GIC 15 : if (numeric_is_nan(num))
387 UIC 0 : ereport(ERROR,
388 ECB : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
389 EUB : errmsg("cannot convert NaN to jsonb")));
390 GIC 15 : if (numeric_is_inf(num))
391 UIC 0 : ereport(ERROR,
392 : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
393 ECB : errmsg("cannot convert infinity to jsonb")));
394 :
395 GIC 15 : jbvNum->type = jbvNumeric;
396 CBC 15 : jbvNum->val.numeric = num;
397 :
398 GIC 15 : return jbvNum;
399 : }
400 :
401 : /*
402 : * PLyObject_ToJsonbValue(PyObject *obj)
403 : *
404 : * Transform python object to JsonbValue.
405 ECB : */
406 : static JsonbValue *
407 GIC 47 : PLyObject_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state, bool is_elem)
408 : {
409 ECB : JsonbValue *out;
410 :
411 CBC 47 : if (!PyUnicode_Check(obj))
412 ECB : {
413 CBC 40 : if (PySequence_Check(obj))
414 10 : return PLySequence_ToJsonbValue(obj, jsonb_state);
415 GIC 30 : else if (PyMapping_Check(obj))
416 5 : return PLyMapping_ToJsonbValue(obj, jsonb_state);
417 ECB : }
418 :
419 CBC 32 : out = palloc(sizeof(JsonbValue));
420 ECB :
421 CBC 32 : if (obj == Py_None)
422 4 : out->type = jbvNull;
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
428 ECB : * come first.
429 : */
430 CBC 21 : else if (PyBool_Check(obj))
431 ECB : {
432 GIC 5 : out->type = jbvBool;
433 CBC 5 : out->val.boolean = (obj == Py_True);
434 ECB : }
435 GIC 16 : else if (PyNumber_Check(obj))
436 GBC 16 : out = PLyNumber_ToJsonbValue(obj, out);
437 : else
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))));
442 ECB :
443 : /* Push result into 'jsonb_state' unless it is raw scalar value. */
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 : *
452 ECB : * Transform python object to Jsonb datum
453 : */
454 CBC 2 : PG_FUNCTION_INFO_V1(plpython_to_jsonb);
455 : Datum
456 GIC 22 : plpython_to_jsonb(PG_FUNCTION_ARGS)
457 : {
458 ECB : PyObject *obj;
459 : JsonbValue *out;
460 CBC 22 : JsonbParseState *jsonb_state = NULL;
461 ECB :
462 CBC 22 : obj = (PyObject *) PG_GETARG_POINTER(0);
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 : *
470 ECB : * Transform Jsonb datum to PyObject and return it as internal.
471 : */
472 CBC 2 : PG_FUNCTION_INFO_V1(jsonb_to_plpython);
473 : Datum
474 GIC 26 : jsonb_to_plpython(PG_FUNCTION_ARGS)
475 ECB : {
476 : PyObject *result;
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"
482 ECB : * module.
483 : */
484 CBC 26 : if (!decimal_constructor)
485 : {
486 1 : PyObject *decimal_module = PyImport_ImportModule("cdecimal");
487 :
488 1 : if (!decimal_module)
489 ECB : {
490 GIC 1 : PyErr_Clear();
491 CBC 1 : decimal_module = PyImport_ImportModule("decimal");
492 ECB : }
493 GIC 1 : Assert(decimal_module);
494 1 : decimal_constructor = PyObject_GetAttrString(decimal_module, "Decimal");
495 ECB : }
496 :
497 GBC 26 : result = PLyObject_FromJsonbContainer(&in->root);
498 GIC 26 : if (!result)
499 LBC 0 : PLy_elog(ERROR, "transformation from jsonb to Python failed");
500 :
501 GIC 26 : return PointerGetDatum(result);
502 : }
|