Python 3 changed the behavior of PyMapping_Check(), breaking the
test in plpython_to_hstore() that verifies whether a function result
to be transformed is acceptable. A backwards-compatible fix is to
first verify that the object doesn't pass PySequence_Check().
Perhaps accidentally, our other uses of PyMapping_Check() already
follow uses of PySequence_Check(), so that no other bugs were
created by this change.
Per bug #17908 from Alexander Lakhin. Back-patch to all supported
branches.
Dmitry Dolgov and Tom Lane
Discussion: https://postgr.es/m/17908-
3f19a125d56a11d6@postgresql.org
2
(1 row)
+-- test that a non-mapping result is correctly rejected
+CREATE FUNCTION test1bad() RETURNS hstore
+LANGUAGE plpython3u
+TRANSFORM FOR TYPE hstore
+AS $$
+return "foo"
+$$;
+SELECT test1bad();
+ERROR: not a Python mapping
+CONTEXT: while creating return value
+PL/Python function "test1bad"
-- test hstore[] -> python
CREATE FUNCTION test1arr(val hstore[]) RETURNS int
LANGUAGE plpython3u
HStore *volatile out;
dict = (PyObject *) PG_GETARG_POINTER(0);
- if (!PyMapping_Check(dict))
+
+ /*
+ * As of Python 3, PyMapping_Check() is unreliable unless one first checks
+ * that the object isn't a sequence. (Cleaner solutions exist, but not
+ * before Python 3.10, which we're not prepared to require yet.)
+ */
+ if (PySequence_Check(dict) || !PyMapping_Check(dict))
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("not a Python mapping")));
SELECT test1n('aa=>bb, cc=>NULL'::hstore);
+-- test that a non-mapping result is correctly rejected
+CREATE FUNCTION test1bad() RETURNS hstore
+LANGUAGE plpython3u
+TRANSFORM FOR TYPE hstore
+AS $$
+return "foo"
+$$;
+
+SELECT test1bad();
+
+
-- test hstore[] -> python
CREATE FUNCTION test1arr(val hstore[]) RETURNS int
LANGUAGE plpython3u