Commit dd4cd55c authored by Peter Eisentraut's avatar Peter Eisentraut

Python 3 support in PL/Python

Behaves more or less unchanged compared to Python 2, but the new language
variant is called plpython3u.  Documentation describing the naming scheme
is included.
parent 21d11e7e
# #
# Autoconf macros for configuring the build of Python extension modules # Autoconf macros for configuring the build of Python extension modules
# #
# $PostgreSQL: pgsql/config/python.m4,v 1.16 2009/10/14 21:59:15 petere Exp $ # $PostgreSQL: pgsql/config/python.m4,v 1.17 2009/12/15 22:59:53 petere Exp $
# #
# PGAC_PATH_PYTHON # PGAC_PATH_PYTHON
...@@ -30,10 +30,12 @@ else ...@@ -30,10 +30,12 @@ else
AC_MSG_ERROR([distutils module not found]) AC_MSG_ERROR([distutils module not found])
fi fi
AC_MSG_CHECKING([Python configuration directory]) AC_MSG_CHECKING([Python configuration directory])
python_majorversion=`${PYTHON} -c "import sys; print(sys.version[[0]])"`
python_version=`${PYTHON} -c "import sys; print(sys.version[[:3]])"` python_version=`${PYTHON} -c "import sys; print(sys.version[[:3]])"`
python_configdir=`${PYTHON} -c "from distutils.sysconfig import get_python_lib as f; import os; print(os.path.join(f(plat_specific=1,standard_lib=1),'config'))"` python_configdir=`${PYTHON} -c "from distutils.sysconfig import get_python_lib as f; import os; print(os.path.join(f(plat_specific=1,standard_lib=1),'config'))"`
python_includespec=`${PYTHON} -c "import distutils.sysconfig; print('-I'+distutils.sysconfig.get_python_inc())"` python_includespec=`${PYTHON} -c "import distutils.sysconfig; print('-I'+distutils.sysconfig.get_python_inc())"`
AC_SUBST(python_majorversion)[]dnl
AC_SUBST(python_version)[]dnl AC_SUBST(python_version)[]dnl
AC_SUBST(python_configdir)[]dnl AC_SUBST(python_configdir)[]dnl
AC_SUBST(python_includespec)[]dnl AC_SUBST(python_includespec)[]dnl
......
...@@ -677,6 +677,7 @@ python_libdir ...@@ -677,6 +677,7 @@ python_libdir
python_includespec python_includespec
python_configdir python_configdir
python_version python_version
python_majorversion
PYTHON PYTHON
perl_embed_ldflags perl_embed_ldflags
perl_useshrplib perl_useshrplib
...@@ -6964,6 +6965,7 @@ $as_echo "$as_me: error: distutils module not found" >&2;} ...@@ -6964,6 +6965,7 @@ $as_echo "$as_me: error: distutils module not found" >&2;}
fi fi
{ $as_echo "$as_me:$LINENO: checking Python configuration directory" >&5 { $as_echo "$as_me:$LINENO: checking Python configuration directory" >&5
$as_echo_n "checking Python configuration directory... " >&6; } $as_echo_n "checking Python configuration directory... " >&6; }
python_majorversion=`${PYTHON} -c "import sys; print(sys.version[0])"`
python_version=`${PYTHON} -c "import sys; print(sys.version[:3])"` python_version=`${PYTHON} -c "import sys; print(sys.version[:3])"`
python_configdir=`${PYTHON} -c "from distutils.sysconfig import get_python_lib as f; import os; print(os.path.join(f(plat_specific=1,standard_lib=1),'config'))"` python_configdir=`${PYTHON} -c "from distutils.sysconfig import get_python_lib as f; import os; print(os.path.join(f(plat_specific=1,standard_lib=1),'config'))"`
python_includespec=`${PYTHON} -c "import distutils.sysconfig; print('-I'+distutils.sysconfig.get_python_inc())"` python_includespec=`${PYTHON} -c "import distutils.sysconfig; print('-I'+distutils.sysconfig.get_python_inc())"`
......
<!-- $PostgreSQL: pgsql/doc/src/sgml/installation.sgml,v 1.332 2009/12/09 16:16:34 mha Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/installation.sgml,v 1.333 2009/12/15 22:59:53 petere Exp $ -->
<chapter id="installation"> <chapter id="installation">
<title><![%standalone-include[<productname>PostgreSQL</>]]> <title><![%standalone-include[<productname>PostgreSQL</>]]>
...@@ -195,8 +195,12 @@ su - postgres ...@@ -195,8 +195,12 @@ su - postgres
<para> <para>
To build the <application>PL/Python</> server programming To build the <application>PL/Python</> server programming
language, you need a <productname>Python</productname> language, you need a <productname>Python</productname>
installation with the header files and the <application>distutils</application> module. installation with the header files and
The minimum required version is <productname>Python</productname> 2.2. the <application>distutils</application> module. The minimum
required version is <productname>Python</productname>
2.2. <productname>Python 3</productname> is supported with
version 3.1 or later; but see <xref linkend="plpython-python23">
when using Python 3.
</para> </para>
<para> <para>
......
<!-- $PostgreSQL: pgsql/doc/src/sgml/plpython.sgml,v 1.41 2009/12/10 20:43:40 petere Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/plpython.sgml,v 1.42 2009/12/15 22:59:53 petere Exp $ -->
<chapter id="plpython"> <chapter id="plpython">
<title>PL/Python - Python Procedural Language</title> <title>PL/Python - Python Procedural Language</title>
...@@ -14,7 +14,8 @@ ...@@ -14,7 +14,8 @@
<para> <para>
To install PL/Python in a particular database, use To install PL/Python in a particular database, use
<literal>createlang plpythonu <replaceable>dbname</></literal>. <literal>createlang plpythonu <replaceable>dbname</></literal> (but
see also <xref linkend="plpython-python23">).
</para> </para>
<tip> <tip>
...@@ -42,6 +43,112 @@ ...@@ -42,6 +43,112 @@
</para> </para>
</note> </note>
<sect1 id="plpython-python23">
<title>Python 2 vs. Python 3</title>
<para>
PL/Python supports both the Python 2 and Python 3 language
variants. (The PostgreSQL installation instructions might contain
more precise information about the exact supported minor versions
of Python.) Because the Python 2 and Python 3 language variants
are incompatible in some important aspects, the following naming
and transitioning scheme is used by PL/Python to avoid mixing them:
<itemizedlist>
<listitem>
<para>
The PostgreSQL language named <literal>plpython2u</literal>
implements PL/Python based on the Python 2 language variant.
</para>
</listitem>
<listitem>
<para>
The PostgreSQL language named <literal>plpython3u</literal>
implements PL/Python based on the Python 3 language variant.
</para>
</listitem>
<listitem>
<para>
The language named <literal>plpythonu</literal> implements
PL/Python based on the default Python language variant, which is
currently Python 2. (This default is independent of what any
local Python installations might consider to be
their <quote>default</quote>, for example,
what <filename>/usr/bin/python</filename> might be.) The
default will probably be changed to Python 3 in a distant future
release of PostgreSQL, depending on the progress of the
migration to Python 3 in the Python community.
</para>
</listitem>
</itemizedlist>
It depends on the build configuration or the installed packages
whether PL/Python for Python 2 or Python 3 or both are available.
</para>
<para>
This results in the following usage and migration strategy:
<itemizedlist>
<listitem>
<para>
Existing users and users who are currently not interested in
Python 3 use the language name <literal>plpythonu</literal> and
don't have to change anything for the foreseeable future. It is
recommended to gradually <quote>future-proof</quote> the code
via migration to Python 2.6/2.7 to simplify the eventual
migration to Python 3.
</para>
<para>
In practice, many PL/Python functions will migrate to Python 3
with few or no changes.
</para>
</listitem>
<listitem>
<para>
Users who know that they have heavily Python 2 dependent code
and don't plan to ever change it can make use of
the <literal>plpython2u</literal> language name. This will
continue to work into the very distant future, until Python 2
support might be completely dropped by PostgreSQL.
</para>
</listitem>
<listitem>
<para>
Users who want to dive into Python 3 can use
the <literal>plpython3u</literal> language name, which will keep
working forever by today's standards. In the distant future,
when Python 3 might become the default, they might like to
remove the <quote>3</quote> for aesthetic reasons.
</para>
</listitem>
<listitem>
<para>
Daredevils, who want to build a Python-3-only operating system
environment, can change the build scripts to
make <literal>plpythonu</literal> be equivalent
to <literal>plpython3u</literal>, keeping in mind that this
would make their installation incompatible with most of the rest
of the world.
</para>
</listitem>
</itemizedlist>
</para>
<para>
See also the
document <ulink url="http://docs.python.org/dev/3.0/whatsnew/3.0.html">What's
New In Python 3.0</ulink> for more information about porting to
Python 3.
</para>
</sect1>
<sect1 id="plpython-funcs"> <sect1 id="plpython-funcs">
<title>PL/Python Functions</title> <title>PL/Python Functions</title>
......
# -*-makefile-*- # -*-makefile-*-
# $PostgreSQL: pgsql/src/Makefile.global.in,v 1.259 2009/11/03 21:28:10 petere Exp $ # $PostgreSQL: pgsql/src/Makefile.global.in,v 1.260 2009/12/15 22:59:54 petere Exp $
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
# All PostgreSQL makefiles include this file and use the variables it sets, # All PostgreSQL makefiles include this file and use the variables it sets,
...@@ -171,6 +171,7 @@ python_libdir = @python_libdir@ ...@@ -171,6 +171,7 @@ python_libdir = @python_libdir@
python_libspec = @python_libspec@ python_libspec = @python_libspec@
python_additional_libs = @python_additional_libs@ python_additional_libs = @python_additional_libs@
python_configdir = @python_configdir@ python_configdir = @python_configdir@
python_majorversion = @python_majorversion@
python_version = @python_version@ python_version = @python_version@
krb_srvtab = @krb_srvtab@ krb_srvtab = @krb_srvtab@
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.558 2009/12/15 17:57:47 tgl Exp $ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.559 2009/12/15 22:59:54 petere Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -53,6 +53,6 @@ ...@@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 200912151 #define CATALOG_VERSION_NO 200912161
#endif #endif
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/catalog/pg_pltemplate.h,v 1.9 2009/11/29 03:02:27 tgl Exp $ * $PostgreSQL: pgsql/src/include/catalog/pg_pltemplate.h,v 1.10 2009/12/15 22:59:54 petere Exp $
* *
* NOTES * NOTES
* the genbki.sh script reads this file and generates .bki * the genbki.sh script reads this file and generates .bki
...@@ -73,5 +73,7 @@ DATA(insert ( "pltclu" f f "pltclu_call_handler" _null_ _null_ "$libdir/pltcl" ...@@ -73,5 +73,7 @@ DATA(insert ( "pltclu" f f "pltclu_call_handler" _null_ _null_ "$libdir/pltcl"
DATA(insert ( "plperl" t t "plperl_call_handler" "plperl_inline_handler" "plperl_validator" "$libdir/plperl" _null_ )); DATA(insert ( "plperl" t t "plperl_call_handler" "plperl_inline_handler" "plperl_validator" "$libdir/plperl" _null_ ));
DATA(insert ( "plperlu" f f "plperl_call_handler" "plperl_inline_handler" "plperl_validator" "$libdir/plperl" _null_ )); DATA(insert ( "plperlu" f f "plperl_call_handler" "plperl_inline_handler" "plperl_validator" "$libdir/plperl" _null_ ));
DATA(insert ( "plpythonu" f f "plpython_call_handler" _null_ _null_ "$libdir/plpython" _null_ )); DATA(insert ( "plpythonu" f f "plpython_call_handler" _null_ _null_ "$libdir/plpython" _null_ ));
DATA(insert ( "plpython2u" f f "plpython_call_handler" _null_ _null_ "$libdir/plpython2" _null_ ));
DATA(insert ( "plpython3u" f f "plpython_call_handler" _null_ _null_ "$libdir/plpython3" _null_ ));
#endif /* PG_PLTEMPLATE_H */ #endif /* PG_PLTEMPLATE_H */
# $PostgreSQL: pgsql/src/pl/plpython/Makefile,v 1.34 2009/08/14 13:42:16 petere Exp $ # $PostgreSQL: pgsql/src/pl/plpython/Makefile,v 1.35 2009/12/15 22:59:54 petere Exp $
subdir = src/pl/plpython subdir = src/pl/plpython
top_builddir = ../../.. top_builddir = ../../..
...@@ -36,7 +36,7 @@ override CPPFLAGS := -I$(srcdir) $(python_includespec) $(CPPFLAGS) ...@@ -36,7 +36,7 @@ override CPPFLAGS := -I$(srcdir) $(python_includespec) $(CPPFLAGS)
rpathdir = $(python_libdir) rpathdir = $(python_libdir)
NAME = plpython NAME = plpython$(python_majorversion)
OBJS = plpython.o OBJS = plpython.o
...@@ -56,7 +56,12 @@ endif ...@@ -56,7 +56,12 @@ endif
SHLIB_LINK = $(python_libspec) $(python_additional_libs) $(filter -lintl,$(LIBS)) SHLIB_LINK = $(python_libspec) $(python_additional_libs) $(filter -lintl,$(LIBS))
REGRESS_OPTS = --dbname=$(PL_TESTDB) --load-language=plpythonu REGRESS_OPTS = --dbname=$(PL_TESTDB)
# Only load plpythonu with Python 2. The test files themselves load
# the versioned language plpython(2|3)u.
ifeq ($(python_majorversion),2)
REGRESS_OPTS += --load-language=plpythonu
endif
REGRESS = \ REGRESS = \
plpython_schema \ plpython_schema \
plpython_populate \ plpython_populate \
...@@ -83,13 +88,45 @@ include $(top_srcdir)/src/Makefile.shlib ...@@ -83,13 +88,45 @@ include $(top_srcdir)/src/Makefile.shlib
all: all-lib all: all-lib
install: all installdirs install-lib install: all installdirs install-lib
ifeq ($(python_majorversion),2)
cd '$(DESTDIR)$(pkglibdir)' && rm -f plpython$(DLSUFFIX) && $(LN_S) $(shlib) plpython$(DLSUFFIX)
endif
installdirs: installdirs-lib installdirs: installdirs-lib
uninstall: uninstall-lib uninstall: uninstall-lib
ifeq ($(python_majorversion),2)
rm -f '$(DESTDIR)$(pkglibdir)/plpython$(DLSUFFIX)'
endif
ifeq ($(python_majorversion),3)
# Adjust regression tests for Python 3 compatibility
prep3:
$(MKDIR_P) python3 python3/sql python3/expected
for file in $(srcdir)/sql/*.sql $(srcdir)/expected/*.out; do \
sed -e 's/except \([[:alpha:]][[:alpha:].]*\), *\([[:alpha:]][[:alpha:]]*\):/except \1 as \2:/g' \
-e "s/<type 'exceptions\.\([[:alpha:]]*\)'>/<class '\1'>/g" \
-e "s/<type 'long'>/<class 'int'>/g" \
-e "s/\([0-9][0-9]*\)L/\1/g" \
-e 's/\([ [{]\)u"/\1"/g' \
-e "s/\([ [{]\)u'/\1'/g" \
-e "s/def next/def __next__/g" \
-e "s/LANGUAGE plpythonu/LANGUAGE plpython3u/g" \
-e "s/LANGUAGE plpython2u/LANGUAGE plpython3u/g" \
$$file >`echo $$file | sed 's,$(srcdir),python3,'`; \
done
clean3:
rm -rf python3/
installcheck: submake prep3
$(top_builddir)/src/test/regress/pg_regress --inputdir=./python3 --outputdir=./python3 --psqldir=$(PSQLDIR) $(REGRESS_OPTS) $(REGRESS)
clean: clean3
else
installcheck: submake installcheck: submake
$(top_builddir)/src/test/regress/pg_regress --inputdir=$(srcdir) --psqldir=$(PSQLDIR) $(REGRESS_OPTS) $(REGRESS) $(top_builddir)/src/test/regress/pg_regress --inputdir=$(srcdir) --psqldir=$(PSQLDIR) $(REGRESS_OPTS) $(REGRESS)
endif
.PHONY: submake .PHONY: submake
submake: submake:
......
...@@ -8,3 +8,5 @@ plpython_unicode_0.out any version, when server encoding != SQL_ASCII and clien ...@@ -8,3 +8,5 @@ plpython_unicode_0.out any version, when server encoding != SQL_ASCII and clien
plpython_unicode_2.out Python 2.2 plpython_unicode_2.out Python 2.2
plpython_unicode_3.out Python 2.3, 2.4 plpython_unicode_3.out Python 2.3, 2.4
plpython_unicode_5.out Python 2.5, 2.6 plpython_unicode_5.out Python 2.5, 2.6
plpython_types_3.out Python 3.1
-- first some tests of basic functionality -- first some tests of basic functionality
CREATE LANGUAGE plpython2u;
-- really stupid function just to get the module loaded -- really stupid function just to get the module loaded
CREATE FUNCTION stupid() RETURNS text AS 'return "zarkon"' LANGUAGE plpythonu; CREATE FUNCTION stupid() RETURNS text AS 'return "zarkon"' LANGUAGE plpythonu;
select stupid(); select stupid();
...@@ -7,6 +8,14 @@ select stupid(); ...@@ -7,6 +8,14 @@ select stupid();
zarkon zarkon
(1 row) (1 row)
-- check 2/3 versioning
CREATE FUNCTION stupidn() RETURNS text AS 'return "zarkon"' LANGUAGE plpython2u;
select stupidn();
stupidn
---------
zarkon
(1 row)
-- test multiple arguments -- test multiple arguments
CREATE FUNCTION argument_test_one(u users, a1 text, a2 text) RETURNS text CREATE FUNCTION argument_test_one(u users, a1 text, a2 text) RETURNS text
AS AS
......
...@@ -67,7 +67,7 @@ SELECT * FROM users; ...@@ -67,7 +67,7 @@ SELECT * FROM users;
-- dump trigger data -- dump trigger data
CREATE TABLE trigger_test CREATE TABLE trigger_test
(i int, v text ); (i int, v text );
CREATE FUNCTION trigger_data() returns trigger language plpythonu as $$ CREATE FUNCTION trigger_data() RETURNS trigger LANGUAGE plpythonu AS $$
if 'relid' in TD: if 'relid' in TD:
TD['relid'] = "bogus:12345" TD['relid'] = "bogus:12345"
......
This diff is collapsed.
/********************************************************************** /**********************************************************************
* plpython.c - python as a procedural language for PostgreSQL * plpython.c - python as a procedural language for PostgreSQL
* *
* $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.133 2009/12/10 20:43:40 petere Exp $ * $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.134 2009/12/15 22:59:54 petere Exp $
* *
********************************************************************* *********************************************************************
*/ */
...@@ -40,6 +40,48 @@ typedef int Py_ssize_t; ...@@ -40,6 +40,48 @@ typedef int Py_ssize_t;
#define PyBool_FromLong(x) PyInt_FromLong(x) #define PyBool_FromLong(x) PyInt_FromLong(x)
#endif #endif
/*
* Python 2/3 strings/unicode/bytes handling. Python 2 has strings
* and unicode, Python 3 has strings, which are unicode on the C
* level, and bytes. The porting convention, which is similarly used
* in Python 2.6, is that "Unicode" is always unicode, and "Bytes" are
* bytes in Python 3 and strings in Python 2. Since we keep
* supporting Python 2 and its usual strings, we provide a
* compatibility layer for Python 3 that when asked to convert a C
* string to a Python string it converts the C string from the
* PostgreSQL server encoding to a Python Unicode object.
*/
#if PY_VERSION_HEX < 0x02060000
/* This is exactly the compatibility layer that Python 2.6 uses. */
#define PyBytes_AsString PyString_AsString
#define PyBytes_FromStringAndSize PyString_FromStringAndSize
#define PyBytes_Size PyString_Size
#define PyObject_Bytes PyObject_Str
#endif
#if PY_MAJOR_VERSION >= 3
#define PyString_Check(x) 0
#define PyString_AsString(x) PLyUnicode_AsString(x)
#define PyString_FromString(x) PLyUnicode_FromString(x)
#endif
/*
* Python 3 only has long.
*/
#if PY_MAJOR_VERSION >= 3
#define PyInt_FromLong(x) PyLong_FromLong(x)
#endif
/*
* PyVarObject_HEAD_INIT was added in Python 2.6. Its use is
* necessary to handle both Python 2 and 3. This replacement
* definition is for Python <=2.5
*/
#ifndef PyVarObject_HEAD_INIT
#define PyVarObject_HEAD_INIT(type, size) \
PyObject_HEAD_INIT(type) size,
#endif
#include "postgres.h" #include "postgres.h"
...@@ -246,7 +288,11 @@ static char *PLy_strdup(const char *); ...@@ -246,7 +288,11 @@ static char *PLy_strdup(const char *);
static void PLy_free(void *); static void PLy_free(void *);
static PyObject*PLyUnicode_Str(PyObject *unicode); static PyObject*PLyUnicode_Str(PyObject *unicode);
static PyObject*PLyUnicode_Bytes(PyObject *unicode);
static char *PLyUnicode_AsString(PyObject *unicode); static char *PLyUnicode_AsString(PyObject *unicode);
#if PY_MAJOR_VERSION >= 3
static PyObject *PLyUnicode_FromString(const char *s);
#endif
/* sub handlers for functions and triggers */ /* sub handlers for functions and triggers */
static Datum PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure *); static Datum PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure *);
...@@ -288,7 +334,7 @@ static PyObject *PLyFloat_FromNumeric(PLyDatumToOb *arg, Datum d); ...@@ -288,7 +334,7 @@ static PyObject *PLyFloat_FromNumeric(PLyDatumToOb *arg, Datum d);
static PyObject *PLyInt_FromInt16(PLyDatumToOb *arg, Datum d); static PyObject *PLyInt_FromInt16(PLyDatumToOb *arg, Datum d);
static PyObject *PLyInt_FromInt32(PLyDatumToOb *arg, Datum d); static PyObject *PLyInt_FromInt32(PLyDatumToOb *arg, Datum d);
static PyObject *PLyLong_FromInt64(PLyDatumToOb *arg, Datum d); static PyObject *PLyLong_FromInt64(PLyDatumToOb *arg, Datum d);
static PyObject *PLyString_FromBytea(PLyDatumToOb *arg, Datum d); static PyObject *PLyBytes_FromBytea(PLyDatumToOb *arg, Datum d);
static PyObject *PLyString_FromDatum(PLyDatumToOb *arg, Datum d); static PyObject *PLyString_FromDatum(PLyDatumToOb *arg, Datum d);
static PyObject *PLyList_FromArray(PLyDatumToOb *arg, Datum d); static PyObject *PLyList_FromArray(PLyDatumToOb *arg, Datum d);
...@@ -1760,7 +1806,7 @@ PLy_input_datum_func2(PLyDatumToOb *arg, Oid typeOid, HeapTuple typeTup) ...@@ -1760,7 +1806,7 @@ PLy_input_datum_func2(PLyDatumToOb *arg, Oid typeOid, HeapTuple typeTup)
arg->func = PLyLong_FromInt64; arg->func = PLyLong_FromInt64;
break; break;
case BYTEAOID: case BYTEAOID:
arg->func = PLyString_FromBytea; arg->func = PLyBytes_FromBytea;
break; break;
default: default:
arg->func = PLyString_FromDatum; arg->func = PLyString_FromDatum;
...@@ -1859,13 +1905,13 @@ PLyLong_FromInt64(PLyDatumToOb *arg, Datum d) ...@@ -1859,13 +1905,13 @@ PLyLong_FromInt64(PLyDatumToOb *arg, Datum d)
} }
static PyObject * static PyObject *
PLyString_FromBytea(PLyDatumToOb *arg, Datum d) PLyBytes_FromBytea(PLyDatumToOb *arg, Datum d)
{ {
text *txt = DatumGetByteaP(d); text *txt = DatumGetByteaP(d);
char *str = VARDATA(txt); char *str = VARDATA(txt);
size_t size = VARSIZE(txt) - VARHDRSZ; size_t size = VARSIZE(txt) - VARHDRSZ;
return PyString_FromStringAndSize(str, size); return PyBytes_FromStringAndSize(str, size);
} }
static PyObject * static PyObject *
...@@ -2001,14 +2047,14 @@ PLyObject_ToBytea(PLyTypeInfo *info, ...@@ -2001,14 +2047,14 @@ PLyObject_ToBytea(PLyTypeInfo *info,
Assert(plrv != Py_None); Assert(plrv != Py_None);
plrv_so = PyObject_Str(plrv); plrv_so = PyObject_Bytes(plrv);
if (!plrv_so) if (!plrv_so)
PLy_elog(ERROR, "could not create string representation of Python object"); PLy_elog(ERROR, "could not create bytes representation of Python object");
PG_TRY(); PG_TRY();
{ {
char *plrv_sc = PyString_AsString(plrv_so); char *plrv_sc = PyBytes_AsString(plrv_so);
size_t len = PyString_Size(plrv_so); size_t len = PyBytes_Size(plrv_so);
size_t size = len + VARHDRSZ; size_t size = len + VARHDRSZ;
bytea *result = palloc(size); bytea *result = palloc(size);
...@@ -2040,22 +2086,30 @@ PLyObject_ToDatum(PLyTypeInfo *info, ...@@ -2040,22 +2086,30 @@ PLyObject_ToDatum(PLyTypeInfo *info,
PLyObToDatum *arg, PLyObToDatum *arg,
PyObject *plrv) PyObject *plrv)
{ {
PyObject *volatile plrv_so = NULL; PyObject *volatile plrv_bo = NULL;
Datum rv; Datum rv;
Assert(plrv != Py_None); Assert(plrv != Py_None);
if (PyUnicode_Check(plrv)) if (PyUnicode_Check(plrv))
plrv_so = PLyUnicode_Str(plrv); plrv_bo = PLyUnicode_Bytes(plrv);
else else
plrv_so = PyObject_Str(plrv); {
if (!plrv_so) #if PY_MAJOR_VERSION >= 3
PyObject *s = PyObject_Str(plrv);
plrv_bo = PLyUnicode_Bytes(s);
Py_XDECREF(s);
#else
plrv_bo = PyObject_Str(plrv);
#endif
}
if (!plrv_bo)
PLy_elog(ERROR, "could not create string representation of Python object"); PLy_elog(ERROR, "could not create string representation of Python object");
PG_TRY(); PG_TRY();
{ {
char *plrv_sc = PyString_AsString(plrv_so); char *plrv_sc = PyBytes_AsString(plrv_bo);
size_t plen = PyString_Size(plrv_so); size_t plen = PyBytes_Size(plrv_bo);
size_t slen = strlen(plrv_sc); size_t slen = strlen(plrv_sc);
if (slen < plen) if (slen < plen)
...@@ -2068,12 +2122,12 @@ PLyObject_ToDatum(PLyTypeInfo *info, ...@@ -2068,12 +2122,12 @@ PLyObject_ToDatum(PLyTypeInfo *info,
} }
PG_CATCH(); PG_CATCH();
{ {
Py_XDECREF(plrv_so); Py_XDECREF(plrv_bo);
PG_RE_THROW(); PG_RE_THROW();
} }
PG_END_TRY(); PG_END_TRY();
Py_XDECREF(plrv_so); Py_XDECREF(plrv_bo);
return rv; return rv;
} }
...@@ -2368,8 +2422,7 @@ static PyMethodDef PLy_plan_methods[] = { ...@@ -2368,8 +2422,7 @@ static PyMethodDef PLy_plan_methods[] = {
}; };
static PyTypeObject PLy_PlanType = { static PyTypeObject PLy_PlanType = {
PyObject_HEAD_INIT(NULL) PyVarObject_HEAD_INIT(NULL, 0)
0, /* ob_size */
"PLyPlan", /* tp_name */ "PLyPlan", /* tp_name */
sizeof(PLyPlanObject), /* tp_size */ sizeof(PLyPlanObject), /* tp_size */
0, /* tp_itemsize */ 0, /* tp_itemsize */
...@@ -2420,8 +2473,7 @@ static PyMethodDef PLy_result_methods[] = { ...@@ -2420,8 +2473,7 @@ static PyMethodDef PLy_result_methods[] = {
}; };
static PyTypeObject PLy_ResultType = { static PyTypeObject PLy_ResultType = {
PyObject_HEAD_INIT(NULL) PyVarObject_HEAD_INIT(NULL, 0)
0, /* ob_size */
"PLyResult", /* tp_name */ "PLyResult", /* tp_name */
sizeof(PLyResultObject), /* tp_size */ sizeof(PLyResultObject), /* tp_size */
0, /* tp_itemsize */ 0, /* tp_itemsize */
...@@ -2480,6 +2532,15 @@ static PyMethodDef PLy_methods[] = { ...@@ -2480,6 +2532,15 @@ static PyMethodDef PLy_methods[] = {
{NULL, NULL, 0, NULL} {NULL, NULL, 0, NULL}
}; };
#if PY_MAJOR_VERSION >= 3
static PyModuleDef PLy_module = {
PyModuleDef_HEAD_INIT, /* m_base */
"plpy", /* m_name */
NULL, /* m_doc */
-1, /* m_size */
PLy_methods, /* m_methods */
};
#endif
/* plan object methods */ /* plan object methods */
static PyObject * static PyObject *
...@@ -3067,6 +3128,15 @@ PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status) ...@@ -3067,6 +3128,15 @@ PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status)
* language handler and interpreter initialization * language handler and interpreter initialization
*/ */
#if PY_MAJOR_VERSION >= 3
static PyMODINIT_FUNC
PyInit_plpy(void)
{
return PyModule_Create(&PLy_module);
}
#endif
/* /*
* _PG_init() - library load-time initialization * _PG_init() - library load-time initialization
* *
...@@ -3083,7 +3153,13 @@ _PG_init(void) ...@@ -3083,7 +3153,13 @@ _PG_init(void)
pg_bindtextdomain(TEXTDOMAIN); pg_bindtextdomain(TEXTDOMAIN);
#if PY_MAJOR_VERSION >= 3
PyImport_AppendInittab("plpy", PyInit_plpy);
#endif
Py_Initialize(); Py_Initialize();
#if PY_MAJOR_VERSION >= 3
PyImport_ImportModule("plpy");
#endif
PLy_init_interp(); PLy_init_interp();
PLy_init_plpy(); PLy_init_plpy();
if (PyErr_Occurred()) if (PyErr_Occurred())
...@@ -3129,7 +3205,11 @@ PLy_init_plpy(void) ...@@ -3129,7 +3205,11 @@ PLy_init_plpy(void)
if (PyType_Ready(&PLy_ResultType) < 0) if (PyType_Ready(&PLy_ResultType) < 0)
elog(ERROR, "could not initialize PLy_ResultType"); elog(ERROR, "could not initialize PLy_ResultType");
#if PY_MAJOR_VERSION >= 3
plpy = PyModule_Create(&PLy_module);
#else
plpy = Py_InitModule("plpy", PLy_methods); plpy = Py_InitModule("plpy", PLy_methods);
#endif
plpy_dict = PyModule_GetDict(plpy); plpy_dict = PyModule_GetDict(plpy);
/* PyDict_SetItemString(plpy, "PlanType", (PyObject *) &PLy_PlanType); */ /* PyDict_SetItemString(plpy, "PlanType", (PyObject *) &PLy_PlanType); */
...@@ -3475,12 +3555,29 @@ PLy_free(void *ptr) ...@@ -3475,12 +3555,29 @@ PLy_free(void *ptr)
} }
/* /*
* Convert a Python unicode object to a Python string object in * Convert a Unicode object to a Python string.
*/
static PyObject*
PLyUnicode_Str(PyObject *unicode)
{
#if PY_MAJOR_VERSION >= 3
/* In Python 3, this is a noop. */
Py_INCREF(unicode);
return unicode;
#else
/* In Python 2, this means converting the Unicode to bytes in the
* server encoding. */
return PLyUnicode_Bytes(unicode);
#endif
}
/*
* Convert a Python unicode object to a Python string/bytes object in
* PostgreSQL server encoding. Reference ownership is passed to the * PostgreSQL server encoding. Reference ownership is passed to the
* caller. * caller.
*/ */
static PyObject* static PyObject*
PLyUnicode_Str(PyObject *unicode) PLyUnicode_Bytes(PyObject *unicode)
{ {
PyObject *rv; PyObject *rv;
const char *serverenc; const char *serverenc;
...@@ -3502,13 +3599,44 @@ PLyUnicode_Str(PyObject *unicode) ...@@ -3502,13 +3599,44 @@ PLyUnicode_Str(PyObject *unicode)
/* /*
* Convert a Python unicode object to a C string in PostgreSQL server * Convert a Python unicode object to a C string in PostgreSQL server
* encoding. No Python object reference is passed out of this * encoding. No Python object reference is passed out of this
* function. * function. The result is palloc'ed.
*
* Note that this function is disguised as PyString_AsString() when
* using Python 3. That function retuns a pointer into the internal
* memory of the argument, which isn't exactly the interface of this
* function. But in either case you get a rather short-lived
* reference that you ought to better leave alone.
*/ */
static char * static char *
PLyUnicode_AsString(PyObject *unicode) PLyUnicode_AsString(PyObject *unicode)
{ {
PyObject *o = PLyUnicode_Str(unicode); PyObject *o = PLyUnicode_Bytes(unicode);
char *rv = PyString_AsString(o); char *rv = pstrdup(PyBytes_AsString(o));
Py_XDECREF(o); Py_XDECREF(o);
return rv; return rv;
} }
#if PY_MAJOR_VERSION >= 3
/*
* Convert a C string in the PostgreSQL server encoding to a Python
* unicode object. Reference ownership is passed to the caller.
*/
static PyObject *
PLyUnicode_FromString(const char *s)
{
char *utf8string;
PyObject *o;
utf8string = (char *) pg_do_encoding_conversion((unsigned char *) s,
strlen(s),
GetDatabaseEncoding(),
PG_UTF8);
o = PyUnicode_FromString(utf8string);
if (utf8string != s)
pfree(utf8string);
return o;
}
#endif /* PY_MAJOR_VERSION >= 3 */
-- first some tests of basic functionality -- first some tests of basic functionality
CREATE LANGUAGE plpython2u;
-- really stupid function just to get the module loaded -- really stupid function just to get the module loaded
CREATE FUNCTION stupid() RETURNS text AS 'return "zarkon"' LANGUAGE plpythonu; CREATE FUNCTION stupid() RETURNS text AS 'return "zarkon"' LANGUAGE plpythonu;
select stupid(); select stupid();
-- check 2/3 versioning
CREATE FUNCTION stupidn() RETURNS text AS 'return "zarkon"' LANGUAGE plpython2u;
select stupidn();
-- test multiple arguments -- test multiple arguments
CREATE FUNCTION argument_test_one(u users, a1 text, a2 text) RETURNS text CREATE FUNCTION argument_test_one(u users, a1 text, a2 text) RETURNS text
......
...@@ -67,7 +67,7 @@ SELECT * FROM users; ...@@ -67,7 +67,7 @@ SELECT * FROM users;
CREATE TABLE trigger_test CREATE TABLE trigger_test
(i int, v text ); (i int, v text );
CREATE FUNCTION trigger_data() returns trigger language plpythonu as $$ CREATE FUNCTION trigger_data() RETURNS trigger LANGUAGE plpythonu AS $$
if 'relid' in TD: if 'relid' in TD:
TD['relid'] = "bogus:12345" TD['relid'] = "bogus:12345"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment