From 04aad401867ad3e1519615d8486e32b50dbcb5f5 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter_e@gmx.net>
Date: Tue, 21 Feb 2017 09:27:02 -0500
Subject: [PATCH] Drop support for Python 2.3

There is no specific reason for this right now, but keeping support for
old Python versions around indefinitely increases the maintenance
burden.  The oldest supported Python version is now Python 2.4, which is
still shipped in RHEL/CentOS 5 by default.

In configure, add a check for the required Python version and give a
friendly error message for an old version, instead of relying on an
obscure build error later on.
---
 config/python.m4                              |  1 +
 configure                                     |  4 ++++
 configure.in                                  |  3 +++
 .../expected/hstore_plpython.out              |  8 ++-----
 .../hstore_plpython/sql/hstore_plpython.sql   |  8 ++-----
 doc/src/sgml/installation.sgml                |  6 +----
 src/pl/plpython/expected/plpython_ereport.out | 24 ++++++++-----------
 src/pl/plpython/plpy_typeio.c                 | 10 ++------
 src/pl/plpython/sql/plpython_ereport.sql      |  8 ++-----
 9 files changed, 27 insertions(+), 45 deletions(-)

diff --git a/config/python.m4 b/config/python.m4
index b605212bea1..7f775e77d23 100644
--- a/config/python.m4
+++ b/config/python.m4
@@ -31,6 +31,7 @@ else
 fi
 AC_MSG_CHECKING([Python configuration directory])
 python_majorversion=`${PYTHON} -c "import sys; print(sys.version[[0]])"`
+python_minorversion=`${PYTHON} -c "import sys; print(sys.version[[2]])"`
 python_version=`${PYTHON} -c "import sys; print(sys.version[[:3]])"`
 python_configdir=`${PYTHON} -c "import distutils.sysconfig; print(' '.join(filter(None,distutils.sysconfig.get_config_vars('LIBPL'))))"`
 AC_MSG_RESULT([$python_configdir])
diff --git a/configure b/configure
index 8468417f69b..908109849e6 100755
--- a/configure
+++ b/configure
@@ -7588,6 +7588,7 @@ fi
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking Python configuration directory" >&5
 $as_echo_n "checking Python configuration directory... " >&6; }
 python_majorversion=`${PYTHON} -c "import sys; print(sys.version[0])"`
+python_minorversion=`${PYTHON} -c "import sys; print(sys.version[2])"`
 python_version=`${PYTHON} -c "import sys; print(sys.version[:3])"`
 python_configdir=`${PYTHON} -c "import distutils.sysconfig; print(' '.join(filter(None,distutils.sysconfig.get_config_vars('LIBPL'))))"`
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $python_configdir" >&5
@@ -7698,6 +7699,9 @@ $as_echo "${python_libspec} ${python_additional_libs}" >&6; }
 
 
 
+  if test "$python_majorversion" -lt 3 -a "$python_minorversion" -lt 4; then
+    as_fn_error $? "Python version $python_version is too old (version 2.4 or later is required)" "$LINENO" 5
+  fi
 fi
 
 if test "$cross_compiling" = yes && test -z "$with_system_tzdata"; then
diff --git a/configure.in b/configure.in
index 01b618c931d..9e4fb0fa80a 100644
--- a/configure.in
+++ b/configure.in
@@ -927,6 +927,9 @@ fi
 if test "$with_python" = yes; then
   PGAC_PATH_PYTHON
   PGAC_CHECK_PYTHON_EMBED_SETUP
+  if test "$python_majorversion" -lt 3 -a "$python_minorversion" -lt 4; then
+    AC_MSG_ERROR([Python version $python_version is too old (version 2.4 or later is required)])
+  fi
 fi
 
 if test "$cross_compiling" = yes && test -z "$with_system_tzdata"; then
diff --git a/contrib/hstore_plpython/expected/hstore_plpython.out b/contrib/hstore_plpython/expected/hstore_plpython.out
index b0025c04a81..df49cd5f373 100644
--- a/contrib/hstore_plpython/expected/hstore_plpython.out
+++ b/contrib/hstore_plpython/expected/hstore_plpython.out
@@ -6,9 +6,7 @@ LANGUAGE plpythonu
 TRANSFORM FOR TYPE hstore
 AS $$
 assert isinstance(val, dict)
-i = list(val.items())
-i.sort()
-plpy.info(i)
+plpy.info(sorted(val.items()))
 return len(val)
 $$;
 SELECT test1('aa=>bb, cc=>NULL'::hstore);
@@ -24,9 +22,7 @@ LANGUAGE plpython2u
 TRANSFORM FOR TYPE hstore
 AS $$
 assert isinstance(val, dict)
-i = list(val.items())
-i.sort()
-plpy.info(i)
+plpy.info(sorted(val.items()))
 return len(val)
 $$;
 SELECT test1n('aa=>bb, cc=>NULL'::hstore);
diff --git a/contrib/hstore_plpython/sql/hstore_plpython.sql b/contrib/hstore_plpython/sql/hstore_plpython.sql
index d55bedaf505..911bbd67fed 100644
--- a/contrib/hstore_plpython/sql/hstore_plpython.sql
+++ b/contrib/hstore_plpython/sql/hstore_plpython.sql
@@ -7,9 +7,7 @@ LANGUAGE plpythonu
 TRANSFORM FOR TYPE hstore
 AS $$
 assert isinstance(val, dict)
-i = list(val.items())
-i.sort()
-plpy.info(i)
+plpy.info(sorted(val.items()))
 return len(val)
 $$;
 
@@ -22,9 +20,7 @@ LANGUAGE plpython2u
 TRANSFORM FOR TYPE hstore
 AS $$
 assert isinstance(val, dict)
-i = list(val.items())
-i.sort()
-plpy.info(i)
+plpy.info(sorted(val.items()))
 return len(val)
 $$;
 
diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml
index 182c801bd52..be0931326bc 100644
--- a/doc/src/sgml/installation.sgml
+++ b/doc/src/sgml/installation.sgml
@@ -193,11 +193,7 @@ su - postgres
       language, you need a <productname>Python</productname>
       installation with the header files and
       the <application>distutils</application> module.  The minimum
-      required version is <productname>Python</productname> 2.3.
-      (To work with function arguments of type <type>numeric</>, a 2.3.x
-      installation must include the separately-available <filename>cdecimal</>
-      module; note the <application>PL/Python</> regression tests
-      will not pass if that is missing.)
+      required version is <productname>Python</productname> 2.4.
       <productname>Python 3</productname> is supported if it's
       version 3.1 or later; but see
       <![%standalone-include[the <application>PL/Python</> documentation]]>
diff --git a/src/pl/plpython/expected/plpython_ereport.out b/src/pl/plpython/expected/plpython_ereport.out
index 13bd0ab3352..1dafd94c721 100644
--- a/src/pl/plpython/expected/plpython_ereport.out
+++ b/src/pl/plpython/expected/plpython_ereport.out
@@ -94,26 +94,22 @@ kwargs = {
     "column_name": _column_name, "datatype_name": _datatype_name,
     "constraint_name": _constraint_name
 }
-# ignore None values - should work on Python2.3
-dict = {}
-for k in kwargs:
-    if kwargs[k] is not None:
-        dict[k] = kwargs[k]
-plpy.error(**dict)
+# ignore None values
+plpy.error(**dict((k, v) for k, v in iter(kwargs.items()) if v))
 $$ LANGUAGE plpythonu;
 SELECT raise_exception('hello', 'world');
 ERROR:  plpy.Error: hello
 DETAIL:  world
 CONTEXT:  Traceback (most recent call last):
-  PL/Python function "raise_exception", line 13, in <module>
-    plpy.error(**dict)
+  PL/Python function "raise_exception", line 9, in <module>
+    plpy.error(**dict((k, v) for k, v in iter(kwargs.items()) if v))
 PL/Python function "raise_exception"
 SELECT raise_exception('message text', 'detail text', _sqlstate => 'YY333');
 ERROR:  plpy.Error: message text
 DETAIL:  detail text
 CONTEXT:  Traceback (most recent call last):
-  PL/Python function "raise_exception", line 13, in <module>
-    plpy.error(**dict)
+  PL/Python function "raise_exception", line 9, in <module>
+    plpy.error(**dict((k, v) for k, v in iter(kwargs.items()) if v))
 PL/Python function "raise_exception"
 SELECT raise_exception(_message => 'message text',
                        _detail => 'detail text',
@@ -128,8 +124,8 @@ ERROR:  plpy.Error: message text
 DETAIL:  detail text
 HINT:  hint text
 CONTEXT:  Traceback (most recent call last):
-  PL/Python function "raise_exception", line 13, in <module>
-    plpy.error(**dict)
+  PL/Python function "raise_exception", line 9, in <module>
+    plpy.error(**dict((k, v) for k, v in iter(kwargs.items()) if v))
 PL/Python function "raise_exception"
 SELECT raise_exception(_message => 'message text',
                        _hint => 'hint text',
@@ -139,8 +135,8 @@ SELECT raise_exception(_message => 'message text',
 ERROR:  plpy.Error: message text
 HINT:  hint text
 CONTEXT:  Traceback (most recent call last):
-  PL/Python function "raise_exception", line 13, in <module>
-    plpy.error(**dict)
+  PL/Python function "raise_exception", line 9, in <module>
+    plpy.error(**dict((k, v) for k, v in iter(kwargs.items()) if v))
 PL/Python function "raise_exception"
 DO $$
 DECLARE
diff --git a/src/pl/plpython/plpy_typeio.c b/src/pl/plpython/plpy_typeio.c
index b9c6d64baaf..06743e46ed5 100644
--- a/src/pl/plpython/plpy_typeio.c
+++ b/src/pl/plpython/plpy_typeio.c
@@ -521,15 +521,9 @@ PLy_input_datum_func2(PLyDatumToOb *arg, MemoryContext arg_mcxt, Oid typeOid, He
 static PyObject *
 PLyBool_FromBool(PLyDatumToOb *arg, Datum d)
 {
-	/*
-	 * We would like to use Py_RETURN_TRUE and Py_RETURN_FALSE here for
-	 * generating SQL from trigger functions, but those are only supported in
-	 * Python >= 2.4, and we support older versions.
-	 * http://docs.python.org/api/boolObjects.html
-	 */
 	if (DatumGetBool(d))
-		return PyBool_FromLong(1);
-	return PyBool_FromLong(0);
+		Py_RETURN_TRUE;
+	Py_RETURN_FALSE;
 }
 
 static PyObject *
diff --git a/src/pl/plpython/sql/plpython_ereport.sql b/src/pl/plpython/sql/plpython_ereport.sql
index 2612e933876..889293d33c9 100644
--- a/src/pl/plpython/sql/plpython_ereport.sql
+++ b/src/pl/plpython/sql/plpython_ereport.sql
@@ -55,12 +55,8 @@ kwargs = {
     "column_name": _column_name, "datatype_name": _datatype_name,
     "constraint_name": _constraint_name
 }
-# ignore None values - should work on Python2.3
-dict = {}
-for k in kwargs:
-    if kwargs[k] is not None:
-        dict[k] = kwargs[k]
-plpy.error(**dict)
+# ignore None values
+plpy.error(**dict((k, v) for k, v in iter(kwargs.items()) if v))
 $$ LANGUAGE plpythonu;
 
 SELECT raise_exception('hello', 'world');
-- 
GitLab