Пример #1
0
/*
 * 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);
}
Пример #2
0
/*
 * 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);
}
Пример #3
0
/*
 * 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);
}
Пример #4
0
/*
 * 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);
}
Пример #5
0
/*
 * 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);
}
Пример #6
0
/*
 * 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;
}
Пример #7
0
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);
}