From df38157d94662a64e2f83aa8a0110fd1ee7c4776 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Thu, 27 Apr 2023 11:55:06 -0400 Subject: [PATCH] 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 --- contrib/hstore_plpython/expected/hstore_plpython.out | 11 +++++++++++ contrib/hstore_plpython/hstore_plpython.c | 8 +++++++- contrib/hstore_plpython/sql/hstore_plpython.sql | 11 +++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/contrib/hstore_plpython/expected/hstore_plpython.out b/contrib/hstore_plpython/expected/hstore_plpython.out index bf238701fe..5fb56a2f65 100644 --- a/contrib/hstore_plpython/expected/hstore_plpython.out +++ b/contrib/hstore_plpython/expected/hstore_plpython.out @@ -32,6 +32,17 @@ INFO: [('aa', 'bb'), ('cc', None)] 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 diff --git a/contrib/hstore_plpython/hstore_plpython.c b/contrib/hstore_plpython/hstore_plpython.c index 961579a5ea..310f63c30d 100644 --- a/contrib/hstore_plpython/hstore_plpython.c +++ b/contrib/hstore_plpython/hstore_plpython.c @@ -127,7 +127,13 @@ plpython_to_hstore(PG_FUNCTION_ARGS) 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"))); diff --git a/contrib/hstore_plpython/sql/hstore_plpython.sql b/contrib/hstore_plpython/sql/hstore_plpython.sql index a9cfbbe13e..ebd61e6c46 100644 --- a/contrib/hstore_plpython/sql/hstore_plpython.sql +++ b/contrib/hstore_plpython/sql/hstore_plpython.sql @@ -27,6 +27,17 @@ $$; 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