Hi Community,
PostgreSQL's plpython supports both Python 2 and Python 3 from 9.0, while Greenplum supports only Python2 for plpython as many tools in Greenplum are using python 2.7, like gpstart, gpinitsystem etc.
This proposal supports both plpython2u(plpythonu) and plpython3u in same cluster. (Not same session, as pointed from
https://www.postgresql.org/docs/9.1/static/plpython-python23.html,
It is not allowed to use PL/Python based on Python 2 and PL/Python based on Python 3 in the same session, because the symbols in the dynamic modules would clash, which could result in crashes of the PostgreSQL server process.)
Step 1: build plpython3.s0
First step is to build plpython3.so using Python 3. PR
#5052 With this PR, we could build plpython3.so even if system python is 2.7
PYTHON=/path/to/python3 ./configure --with-python ...
Step 2: Active python 3 for plpython3u
virtualenv has an excellent 'activate_this.py' script which will activate specific python env for running process. By using it, we could activate python 3 for plpython3u on both master and segments. (As Greenplum use python 2.7 by default, so no need to activate python 2, while we could if want)
We defined 2 UDFs activate_python2() and activate_python3() to activate python2 and python3 separately. (Needs to enhance them to avoid hardcoded path)
TODO: execute those UDF on all segments and master, by leveraging 'EXECUTE ON MASTER' and 'EXECUTE ON ALL SEGMENTS', it is trivial to do.
CREATE OR REPLACE FUNCTION activate_python2()
RETURNS text
AS $$
activate_this_file = "/home/gpadmin/.venv/python2/bin/activate_this.py"
execfile(activate_this_file, dict(__file__=activate_this_file))
return "succeed"
$$ LANGUAGE plpythonu
CREATE OR REPLACE FUNCTION activate_python3()
RETURNS text
AS $$
def execfile(filepath, globals=None, locals=None):
'''execfile is removed from python3, redefine it'''
if globals is None:
globals = {}
globals.update({
"__file__": filepath,
"__name__": "__main__",
})
with open(filepath, 'rb') as file:
exec(compile(file.read(), filepath, 'exec'), globals, locals)
activate_this_file = "/home/gpadmin/.venv/python3/bin/activate_this.py"
execfile(activate_this_file, dict(__file__=activate_this_file))
return "succeed"
$$ LANGUAGE plpython3u
Step 3: Verification
gpadmin=# Create extension plpython3u;
gpadmin=# SELECT * from activate_python3();
activate_python3
------------------
succeed
(1 row)
CREATE OR REPLACE FUNCTION python3version ()
RETURNS text
AS $$
import sys
return sys.version
$$ LANGUAGE plpython3u;
gpadmin=# SELECT * from python3version();
python3version
-----------------------------------------
3.6.5 (default, Apr 10 2018, 17:08:37) +
[GCC 4.8.5 20150623 (Red Hat 4.8.5-16)]
(1 row)
gpadmin=# DROP TYPE IF EXISTS named_value;
gpadmin=# CREATE TYPE named_value AS (
name text,
value integer
);
--Returning a set of results using SETOF
gpadmin=# CREATE OR REPLACE FUNCTION make_pair_sets (name text)
RETURNS SETOF named_value
AS $$
import numpy as np // Note, numpy is located under python3's site-package.
return ((name, i) for i in np.arange(3))
$$ LANGUAGE plpython3u;
gpadmin=# SELECT * from make_pair_sets('test');
name | value
------+-------
test | 0
test | 1
test | 2
(3 rows)
--