/* * PLyString_ToJsonbValue * * Transform Python string to JsonbValue. */ static void PLyString_ToJsonbValue(PyObject *obj, JsonbValue *jbvElem) { jbvElem->type = jbvString; jbvElem->val.string.val = PLyObject_AsString(obj); jbvElem->val.string.len = strlen(jbvElem->val.string.val); }
/* * Convert a Python string to composite, using record_in. */ static Datum PLyString_ToComposite(PLyObToDatum *arg, PyObject *string, bool inarray) { char *str; /* * Set up call data for record_in, if we didn't already. (We can't just * use DirectFunctionCall, because record_in needs a fn_extra field.) */ if (!OidIsValid(arg->u.tuple.recinfunc.fn_oid)) fmgr_info_cxt(F_RECORD_IN, &arg->u.tuple.recinfunc, arg->mcxt); str = PLyObject_AsString(string); /* * If we are parsing a composite type within an array, and the string * isn't a valid record literal, there's a high chance that the function * did something like: * * CREATE FUNCTION .. RETURNS comptype[] AS $$ return [['foo', 'bar']] $$ * LANGUAGE plpython; * * Before PostgreSQL 10, that was interpreted as a single-dimensional * array, containing record ('foo', 'bar'). PostgreSQL 10 added support * for multi-dimensional arrays, and it is now interpreted as a * two-dimensional array, containing two records, 'foo', and 'bar'. * record_in() will throw an error, because "foo" is not a valid record * literal. * * To make that less confusing to users who are upgrading from older * versions, try to give a hint in the typical instances of that. If we * are parsing an array of composite types, and we see a string literal * that is not a valid record literal, give a hint. We only want to give * the hint in the narrow case of a malformed string literal, not any * error from record_in(), so check for that case here specifically. * * This check better match the one in record_in(), so that we don't forbid * literals that are actually valid! */ if (inarray) { char *ptr = str; /* Allow leading whitespace */ while (*ptr && isspace((unsigned char) *ptr)) ptr++; if (*ptr++ != '(') ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("malformed record literal: \"%s\"", str), errdetail("Missing left parenthesis."), errhint("To return a composite type in an array, return the composite type as a Python tuple, e.g., \"[('foo',)]\"."))); } return InputFunctionCall(&arg->u.tuple.recinfunc, str, arg->typoid, arg->typmod); }
/* * Generic conversion function: Convert PyObject to cstring and * cstring into PostgreSQL type. */ static Datum PLyObject_ToDatum(PLyObToDatum *arg, int32 typmod, PyObject *plrv) { Assert(plrv != Py_None); return InputFunctionCall(&arg->typfunc, PLyObject_AsString(plrv), arg->typioparam, typmod); }
/* * PLyObject_ToJsonbValue(PyObject *obj) * * Transform python object to JsonbValue. */ static JsonbValue * PLyObject_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state, bool is_elem) { JsonbValue buf; JsonbValue *out; if (!(PyString_Check(obj) || PyUnicode_Check(obj))) { if (PySequence_Check(obj)) return PLySequence_ToJsonbValue(obj, jsonb_state); else if (PyMapping_Check(obj)) return PLyMapping_ToJsonbValue(obj, jsonb_state); } /* Allocate JsonbValue in heap only if it is raw scalar value. */ if (*jsonb_state) out = &buf; else out = palloc(sizeof(JsonbValue)); if (obj == Py_None) out->type = jbvNull; else if (PyString_Check(obj) || PyUnicode_Check(obj)) PLyString_ToJsonbValue(obj, out); /* * PyNumber_Check() returns true for booleans, so boolean check should * come first. */ else if (PyBool_Check(obj)) { out = palloc(sizeof(JsonbValue)); out->type = jbvBool; out->val.boolean = (obj == Py_True); } else if (PyNumber_Check(obj)) out = PLyNumber_ToJsonbValue(obj, out); else ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errmsg("Python type \"%s\" cannot be transformed to jsonb", PLyObject_AsString((PyObject *) obj->ob_type))))); /* Push result into 'jsonb_state' unless it is raw scalar value. */ return (*jsonb_state ? pushJsonbValue(jsonb_state, is_elem ? WJB_ELEM : WJB_VALUE, out) : out); }
/* * Generic output conversion function: convert PyObject to cstring and * cstring into PostgreSQL type. */ static Datum PLyObject_ToScalar(PLyObToDatum *arg, PyObject *plrv, bool *isnull, bool inarray) { char *str; if (plrv == Py_None) { *isnull = true; return (Datum) 0; } *isnull = false; str = PLyObject_AsString(plrv); return InputFunctionCall(&arg->u.scalar.typfunc, str, arg->u.scalar.typioparam, arg->typmod); }
/* * PLyNumber_ToJsonbValue(PyObject *obj) * * Transform python number to JsonbValue. */ static JsonbValue * PLyNumber_ToJsonbValue(PyObject *obj, JsonbValue *jbvNum) { Numeric num; char *str = PLyObject_AsString(obj); PG_TRY(); { Datum numd; numd = DirectFunctionCall3(numeric_in, CStringGetDatum(str), ObjectIdGetDatum(InvalidOid), Int32GetDatum(-1)); num = DatumGetNumeric(numd); } PG_CATCH(); { ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), (errmsg("could not convert value \"%s\" to jsonb", str)))); } PG_END_TRY(); pfree(str); /* * jsonb doesn't allow NaN (per JSON specification), so we have to prevent * it here explicitly. (Infinity is also not allowed in jsonb, but * numeric_in above already catches that.) */ if (numeric_is_nan(num)) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), (errmsg("cannot convert NaN to jsonb")))); jbvNum->type = jbvNumeric; jbvNum->val.numeric = num; return jbvNum; }
Datum plpython_to_hstore(PG_FUNCTION_ARGS) { PyObject *dict; volatile PyObject *items_v = NULL; int32 pcount; HStore *out; dict = (PyObject *) PG_GETARG_POINTER(0); if (!PyMapping_Check(dict)) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("not a Python mapping"))); pcount = PyMapping_Size(dict); items_v = PyMapping_Items(dict); PG_TRY(); { int32 buflen; int32 i; Pairs *pairs; PyObject *items = (PyObject *) items_v; pairs = palloc(pcount * sizeof(*pairs)); for (i = 0; i < pcount; i++) { PyObject *tuple; PyObject *key; PyObject *value; tuple = PyList_GetItem(items, i); key = PyTuple_GetItem(tuple, 0); value = PyTuple_GetItem(tuple, 1); pairs[i].key = PLyObject_AsString(key); pairs[i].keylen = hstoreCheckKeyLen(strlen(pairs[i].key)); pairs[i].needfree = true; if (value == Py_None) { pairs[i].val = NULL; pairs[i].vallen = 0; pairs[i].isnull = true; } else { pairs[i].val = PLyObject_AsString(value); pairs[i].vallen = hstoreCheckValLen(strlen(pairs[i].val)); pairs[i].isnull = false; } } Py_DECREF(items_v); pcount = hstoreUniquePairs(pairs, pcount, &buflen); out = hstorePairs(pairs, pcount, buflen); } PG_CATCH(); { Py_DECREF(items_v); PG_RE_THROW(); } PG_END_TRY(); PG_RETURN_POINTER(out); }