In hstore_plpython, avoid crashing when return value isn't a mapping.

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
This commit is contained in:
Tom Lane 2023-04-27 11:55:06 -04:00
parent b95f36f861
commit de2dfa0538
3 changed files with 29 additions and 1 deletions

View File

@ -32,6 +32,17 @@ INFO: [('aa', 'bb'), ('cc', None)]
2 2
(1 row) (1 row)
-- test that a non-mapping result is correctly rejected
CREATE FUNCTION test1bad() RETURNS hstore
LANGUAGE plpythonu
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 -- test hstore[] -> python
CREATE FUNCTION test1arr(val hstore[]) RETURNS int CREATE FUNCTION test1arr(val hstore[]) RETURNS int
LANGUAGE plpythonu LANGUAGE plpythonu

View File

@ -133,7 +133,13 @@ plpython_to_hstore(PG_FUNCTION_ARGS)
HStore *volatile out; HStore *volatile out;
dict = (PyObject *) PG_GETARG_POINTER(0); 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, ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE), (errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("not a Python mapping"))); errmsg("not a Python mapping")));

View File

@ -27,6 +27,17 @@ $$;
SELECT test1n('aa=>bb, cc=>NULL'::hstore); SELECT test1n('aa=>bb, cc=>NULL'::hstore);
-- test that a non-mapping result is correctly rejected
CREATE FUNCTION test1bad() RETURNS hstore
LANGUAGE plpythonu
TRANSFORM FOR TYPE hstore
AS $$
return "foo"
$$;
SELECT test1bad();
-- test hstore[] -> python -- test hstore[] -> python
CREATE FUNCTION test1arr(val hstore[]) RETURNS int CREATE FUNCTION test1arr(val hstore[]) RETURNS int
LANGUAGE plpythonu LANGUAGE plpythonu