pg_amcheck: Fix test failure on Windows with non-existing role
[pgsql.git] / contrib / jsonb_plpython / jsonb_plpython.c
bloba625727c5e8ca1d857c09e394505f5070987279c
1 #include "postgres.h"
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"
10 PG_MODULE_MAGIC;
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.
36 void
37 _PG_init(void)
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",
43 true, NULL);
44 AssertVariableIsOfType(&PLyUnicode_FromStringAndSize, PLyUnicode_FromStringAndSize_t);
45 PLyUnicode_FromStringAndSize_p = (PLyUnicode_FromStringAndSize_t)
46 load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLyUnicode_FromStringAndSize",
47 true, NULL);
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",
51 true, NULL);
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)
61 * PLyUnicode_FromJsonbValue
63 * Transform string JsonbValue to Python string.
65 static PyObject *
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.
78 static void
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.
91 static PyObject *
92 PLyObject_FromJsonbValue(JsonbValue *jsonbValue)
94 switch (jsonbValue->type)
96 case jbvNull:
97 Py_RETURN_NONE;
99 case jbvBinary:
100 return PLyObject_FromJsonbContainer(jsonbValue->val.binary.data);
102 case jbvNumeric:
104 Datum num;
105 char *str;
107 num = NumericGetDatum(jsonbValue->val.numeric);
108 str = DatumGetCString(DirectFunctionCall1(numeric_out, num));
110 return PyObject_CallFunction(decimal_constructor, "s", str);
113 case jbvString:
114 return PLyUnicode_FromJsonbValue(jsonbValue);
116 case jbvBool:
117 if (jsonbValue->val.boolean)
118 Py_RETURN_TRUE;
119 else
120 Py_RETURN_FALSE;
122 default:
123 elog(ERROR, "unexpected jsonb value type: %d", jsonbValue->type);
124 return NULL;
129 * PLyObject_FromJsonbContainer
131 * Transform JsonbContainer to PyObject.
133 static PyObject *
134 PLyObject_FromJsonbContainer(JsonbContainer *jsonb)
136 JsonbIteratorToken r;
137 JsonbValue v;
138 JsonbIterator *it;
139 PyObject *result;
141 it = JsonbIteratorInit(jsonb);
142 r = JsonbIteratorNext(&it, &v, true);
144 switch (r)
146 case WJB_BEGIN_ARRAY:
147 if (v.val.array.rawScalar)
149 JsonbValue tmp;
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);
158 else
160 PyObject *volatile elem = NULL;
162 result = PyList_New(0);
163 if (!result)
164 return NULL;
166 PG_TRY();
168 while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
170 if (r != WJB_ELEM)
171 continue;
173 elem = PLyObject_FromJsonbValue(&v);
175 PyList_Append(result, elem);
176 Py_XDECREF(elem);
177 elem = NULL;
180 PG_CATCH();
182 Py_XDECREF(elem);
183 Py_XDECREF(result);
184 PG_RE_THROW();
186 PG_END_TRY();
188 break;
190 case WJB_BEGIN_OBJECT:
192 PyObject *volatile result_v = PyDict_New();
193 PyObject *volatile key = NULL;
194 PyObject *volatile val = NULL;
196 if (!result_v)
197 return NULL;
199 PG_TRY();
201 while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
203 if (r != WJB_KEY)
204 continue;
206 key = PLyUnicode_FromJsonbValue(&v);
207 if (!key)
209 Py_XDECREF(result_v);
210 result_v = NULL;
211 break;
214 if ((r = JsonbIteratorNext(&it, &v, true)) != WJB_VALUE)
215 elog(ERROR, "unexpected jsonb token: %d", r);
217 val = PLyObject_FromJsonbValue(&v);
218 if (!val)
220 Py_XDECREF(key);
221 key = NULL;
222 Py_XDECREF(result_v);
223 result_v = NULL;
224 break;
227 PyDict_SetItem(result_v, key, val);
229 Py_XDECREF(key);
230 key = NULL;
231 Py_XDECREF(val);
232 val = NULL;
235 PG_CATCH();
237 Py_XDECREF(result_v);
238 Py_XDECREF(key);
239 Py_XDECREF(val);
240 PG_RE_THROW();
242 PG_END_TRY();
244 result = result_v;
246 break;
248 default:
249 elog(ERROR, "unexpected jsonb token: %d", r);
250 return NULL;
253 return result;
257 * PLyMapping_ToJsonbValue
259 * Transform Python dict to JsonbValue.
261 static JsonbValue *
262 PLyMapping_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state)
264 Py_ssize_t pcount;
265 PyObject *volatile items;
266 JsonbValue *volatile out;
268 pcount = PyMapping_Size(obj);
269 items = PyMapping_Items(obj);
271 PG_TRY();
273 Py_ssize_t i;
275 pushJsonbValue(jsonb_state, WJB_BEGIN_OBJECT, NULL);
277 for (i = 0; i < pcount; i++)
279 JsonbValue jbvKey;
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 */
285 if (key == Py_None)
287 jbvKey.type = jbvString;
288 jbvKey.val.string.len = 0;
289 jbvKey.val.string.val = "";
291 else
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);
303 PG_FINALLY();
305 Py_DECREF(items);
307 PG_END_TRY();
309 return out;
313 * PLySequence_ToJsonbValue
315 * Transform python list to JsonbValue. Expects transformed PyObject and
316 * a state required for jsonb construction.
318 static JsonbValue *
319 PLySequence_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state)
321 Py_ssize_t i;
322 Py_ssize_t pcount;
323 PyObject *volatile value = NULL;
325 pcount = PySequence_Size(obj);
327 pushJsonbValue(jsonb_state, WJB_BEGIN_ARRAY, NULL);
329 PG_TRY();
331 for (i = 0; i < pcount; i++)
333 value = PySequence_GetItem(obj, i);
334 Assert(value);
336 (void) PLyObject_ToJsonbValue(value, jsonb_state, true);
337 Py_XDECREF(value);
338 value = NULL;
341 PG_CATCH();
343 Py_XDECREF(value);
344 PG_RE_THROW();
346 PG_END_TRY();
348 return pushJsonbValue(jsonb_state, WJB_END_ARRAY, NULL);
352 * PLyNumber_ToJsonbValue(PyObject *obj)
354 * Transform python number to JsonbValue.
356 static JsonbValue *
357 PLyNumber_ToJsonbValue(PyObject *obj, JsonbValue *jbvNum)
359 Numeric num;
360 char *str = PLyObject_AsString(obj);
362 PG_TRY();
364 Datum numd;
366 numd = DirectFunctionCall3(numeric_in,
367 CStringGetDatum(str),
368 ObjectIdGetDatum(InvalidOid),
369 Int32GetDatum(-1));
370 num = DatumGetNumeric(numd);
372 PG_CATCH();
374 ereport(ERROR,
375 (errcode(ERRCODE_DATATYPE_MISMATCH),
376 errmsg("could not convert value \"%s\" to jsonb", str)));
378 PG_END_TRY();
380 pfree(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))
387 ereport(ERROR,
388 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
389 errmsg("cannot convert NaN to jsonb")));
390 if (numeric_is_inf(num))
391 ereport(ERROR,
392 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
393 errmsg("cannot convert infinity to jsonb")));
395 jbvNum->type = jbvNumeric;
396 jbvNum->val.numeric = num;
398 return jbvNum;
402 * PLyObject_ToJsonbValue(PyObject *obj)
404 * Transform python object to JsonbValue.
406 static JsonbValue *
407 PLyObject_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state, bool is_elem)
409 JsonbValue *out;
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));
421 if (obj == Py_None)
422 out->type = jbvNull;
423 else if (PyUnicode_Check(obj))
424 PLyUnicode_ToJsonbValue(obj, out);
427 * PyNumber_Check() returns true for booleans, so boolean check should
428 * come first.
430 else if (PyBool_Check(obj))
432 out->type = jbvBool;
433 out->val.boolean = (obj == Py_True);
435 else if (PyNumber_Check(obj))
436 out = PLyNumber_ToJsonbValue(obj, out);
437 else
438 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))));
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) :
446 out);
450 * plpython_to_jsonb
452 * Transform python object to Jsonb datum
454 PG_FUNCTION_INFO_V1(plpython_to_jsonb);
455 Datum
456 plpython_to_jsonb(PG_FUNCTION_ARGS)
458 PyObject *obj;
459 JsonbValue *out;
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));
468 * jsonb_to_plpython
470 * Transform Jsonb datum to PyObject and return it as internal.
472 PG_FUNCTION_INFO_V1(jsonb_to_plpython);
473 Datum
474 jsonb_to_plpython(PG_FUNCTION_ARGS)
476 PyObject *result;
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"
482 * module.
484 if (!decimal_constructor)
486 PyObject *decimal_module = PyImport_ImportModule("cdecimal");
488 if (!decimal_module)
490 PyErr_Clear();
491 decimal_module = PyImport_ImportModule("decimal");
493 Assert(decimal_module);
494 decimal_constructor = PyObject_GetAttrString(decimal_module, "Decimal");
497 result = PLyObject_FromJsonbContainer(&in->root);
498 if (!result)
499 PLy_elog(ERROR, "transformation from jsonb to Python failed");
501 return PointerGetDatum(result);