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.
This commit is contained in:
parent
21d11e7ee2
commit
dd4cd55c15
@ -1,7 +1,7 @@
|
||||
#
|
||||
# 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
|
||||
@ -30,10 +30,12 @@ else
|
||||
AC_MSG_ERROR([distutils module not found])
|
||||
fi
|
||||
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_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())"`
|
||||
|
||||
AC_SUBST(python_majorversion)[]dnl
|
||||
AC_SUBST(python_version)[]dnl
|
||||
AC_SUBST(python_configdir)[]dnl
|
||||
AC_SUBST(python_includespec)[]dnl
|
||||
|
2
configure
vendored
2
configure
vendored
@ -677,6 +677,7 @@ python_libdir
|
||||
python_includespec
|
||||
python_configdir
|
||||
python_version
|
||||
python_majorversion
|
||||
PYTHON
|
||||
perl_embed_ldflags
|
||||
perl_useshrplib
|
||||
@ -6964,6 +6965,7 @@ $as_echo "$as_me: error: distutils module not found" >&2;}
|
||||
fi
|
||||
{ $as_echo "$as_me:$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_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_includespec=`${PYTHON} -c "import distutils.sysconfig; print('-I'+distutils.sysconfig.get_python_inc())"`
|
||||
|
@ -1,4 +1,4 @@
|
||||
<!-- $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">
|
||||
<title><![%standalone-include[<productname>PostgreSQL</>]]>
|
||||
@ -195,8 +195,12 @@ su - postgres
|
||||
<para>
|
||||
To build the <application>PL/Python</> server programming
|
||||
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.2.
|
||||
installation with the header files and
|
||||
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>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<!-- $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">
|
||||
<title>PL/Python - Python Procedural Language</title>
|
||||
@ -14,7 +14,8 @@
|
||||
|
||||
<para>
|
||||
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>
|
||||
|
||||
<tip>
|
||||
@ -42,6 +43,112 @@
|
||||
</para>
|
||||
</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">
|
||||
<title>PL/Python Functions</title>
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
# -*-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,
|
||||
@ -171,6 +171,7 @@ python_libdir = @python_libdir@
|
||||
python_libspec = @python_libspec@
|
||||
python_additional_libs = @python_additional_libs@
|
||||
python_configdir = @python_configdir@
|
||||
python_majorversion = @python_majorversion@
|
||||
python_version = @python_version@
|
||||
|
||||
krb_srvtab = @krb_srvtab@
|
||||
|
@ -37,7 +37,7 @@
|
||||
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||
* 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 @@
|
||||
*/
|
||||
|
||||
/* yyyymmddN */
|
||||
#define CATALOG_VERSION_NO 200912151
|
||||
#define CATALOG_VERSION_NO 200912161
|
||||
|
||||
#endif
|
||||
|
@ -8,7 +8,7 @@
|
||||
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||
* 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
|
||||
* 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"
|
||||
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 ( "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 */
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $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
|
||||
top_builddir = ../../..
|
||||
@ -36,7 +36,7 @@ override CPPFLAGS := -I$(srcdir) $(python_includespec) $(CPPFLAGS)
|
||||
|
||||
rpathdir = $(python_libdir)
|
||||
|
||||
NAME = plpython
|
||||
NAME = plpython$(python_majorversion)
|
||||
OBJS = plpython.o
|
||||
|
||||
|
||||
@ -56,7 +56,12 @@ endif
|
||||
|
||||
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 = \
|
||||
plpython_schema \
|
||||
plpython_populate \
|
||||
@ -83,13 +88,45 @@ include $(top_srcdir)/src/Makefile.shlib
|
||||
all: all-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
|
||||
|
||||
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
|
||||
$(top_builddir)/src/test/regress/pg_regress --inputdir=$(srcdir) --psqldir=$(PSQLDIR) $(REGRESS_OPTS) $(REGRESS)
|
||||
endif
|
||||
|
||||
.PHONY: submake
|
||||
submake:
|
||||
|
@ -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_3.out Python 2.3, 2.4
|
||||
plpython_unicode_5.out Python 2.5, 2.6
|
||||
|
||||
plpython_types_3.out Python 3.1
|
||||
|
@ -1,4 +1,5 @@
|
||||
-- first some tests of basic functionality
|
||||
CREATE LANGUAGE plpython2u;
|
||||
-- really stupid function just to get the module loaded
|
||||
CREATE FUNCTION stupid() RETURNS text AS 'return "zarkon"' LANGUAGE plpythonu;
|
||||
select stupid();
|
||||
@ -7,6 +8,14 @@ select stupid();
|
||||
zarkon
|
||||
(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
|
||||
CREATE FUNCTION argument_test_one(u users, a1 text, a2 text) RETURNS text
|
||||
AS
|
||||
|
@ -67,7 +67,7 @@ SELECT * FROM users;
|
||||
-- dump trigger data
|
||||
CREATE TABLE trigger_test
|
||||
(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:
|
||||
TD['relid'] = "bogus:12345"
|
||||
|
589
src/pl/plpython/expected/plpython_types_3.out
Normal file
589
src/pl/plpython/expected/plpython_types_3.out
Normal file
@ -0,0 +1,589 @@
|
||||
--
|
||||
-- Test data type behavior
|
||||
--
|
||||
--
|
||||
-- Base/common types
|
||||
--
|
||||
CREATE FUNCTION test_type_conversion_bool(x bool) RETURNS bool AS $$
|
||||
plpy.info(x, type(x))
|
||||
return x
|
||||
$$ LANGUAGE plpythonu;
|
||||
SELECT * FROM test_type_conversion_bool(true);
|
||||
INFO: (True, <class 'bool'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_bool"
|
||||
test_type_conversion_bool
|
||||
---------------------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_type_conversion_bool(false);
|
||||
INFO: (False, <class 'bool'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_bool"
|
||||
test_type_conversion_bool
|
||||
---------------------------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_type_conversion_bool(null);
|
||||
INFO: (None, <class 'NoneType'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_bool"
|
||||
test_type_conversion_bool
|
||||
---------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
-- test various other ways to express Booleans in Python
|
||||
CREATE FUNCTION test_type_conversion_bool_other(n int) RETURNS bool AS $$
|
||||
# numbers
|
||||
if n == 0:
|
||||
ret = 0
|
||||
elif n == 1:
|
||||
ret = 5
|
||||
# strings
|
||||
elif n == 2:
|
||||
ret = ''
|
||||
elif n == 3:
|
||||
ret = 'fa' # true in Python, false in PostgreSQL
|
||||
# containers
|
||||
elif n == 4:
|
||||
ret = []
|
||||
elif n == 5:
|
||||
ret = [0]
|
||||
plpy.info(ret, not not ret)
|
||||
return ret
|
||||
$$ LANGUAGE plpythonu;
|
||||
SELECT * FROM test_type_conversion_bool_other(0);
|
||||
INFO: (0, False)
|
||||
CONTEXT: PL/Python function "test_type_conversion_bool_other"
|
||||
test_type_conversion_bool_other
|
||||
---------------------------------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_type_conversion_bool_other(1);
|
||||
INFO: (5, True)
|
||||
CONTEXT: PL/Python function "test_type_conversion_bool_other"
|
||||
test_type_conversion_bool_other
|
||||
---------------------------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_type_conversion_bool_other(2);
|
||||
INFO: ('', False)
|
||||
CONTEXT: PL/Python function "test_type_conversion_bool_other"
|
||||
test_type_conversion_bool_other
|
||||
---------------------------------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_type_conversion_bool_other(3);
|
||||
INFO: ('fa', True)
|
||||
CONTEXT: PL/Python function "test_type_conversion_bool_other"
|
||||
test_type_conversion_bool_other
|
||||
---------------------------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_type_conversion_bool_other(4);
|
||||
INFO: ([], False)
|
||||
CONTEXT: PL/Python function "test_type_conversion_bool_other"
|
||||
test_type_conversion_bool_other
|
||||
---------------------------------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_type_conversion_bool_other(5);
|
||||
INFO: ([0], True)
|
||||
CONTEXT: PL/Python function "test_type_conversion_bool_other"
|
||||
test_type_conversion_bool_other
|
||||
---------------------------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
CREATE FUNCTION test_type_conversion_char(x char) RETURNS char AS $$
|
||||
plpy.info(x, type(x))
|
||||
return x
|
||||
$$ LANGUAGE plpythonu;
|
||||
SELECT * FROM test_type_conversion_char('a');
|
||||
INFO: ('a', <class 'str'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_char"
|
||||
test_type_conversion_char
|
||||
---------------------------
|
||||
a
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_type_conversion_char(null);
|
||||
INFO: (None, <class 'NoneType'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_char"
|
||||
test_type_conversion_char
|
||||
---------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
CREATE FUNCTION test_type_conversion_int2(x int2) RETURNS int2 AS $$
|
||||
plpy.info(x, type(x))
|
||||
return x
|
||||
$$ LANGUAGE plpythonu;
|
||||
SELECT * FROM test_type_conversion_int2(100::int2);
|
||||
INFO: (100, <class 'int'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_int2"
|
||||
test_type_conversion_int2
|
||||
---------------------------
|
||||
100
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_type_conversion_int2(-100::int2);
|
||||
INFO: (-100, <class 'int'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_int2"
|
||||
test_type_conversion_int2
|
||||
---------------------------
|
||||
-100
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_type_conversion_int2(null);
|
||||
INFO: (None, <class 'NoneType'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_int2"
|
||||
test_type_conversion_int2
|
||||
---------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
CREATE FUNCTION test_type_conversion_int4(x int4) RETURNS int4 AS $$
|
||||
plpy.info(x, type(x))
|
||||
return x
|
||||
$$ LANGUAGE plpythonu;
|
||||
SELECT * FROM test_type_conversion_int4(100);
|
||||
INFO: (100, <class 'int'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_int4"
|
||||
test_type_conversion_int4
|
||||
---------------------------
|
||||
100
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_type_conversion_int4(-100);
|
||||
INFO: (-100, <class 'int'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_int4"
|
||||
test_type_conversion_int4
|
||||
---------------------------
|
||||
-100
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_type_conversion_int4(null);
|
||||
INFO: (None, <class 'NoneType'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_int4"
|
||||
test_type_conversion_int4
|
||||
---------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
CREATE FUNCTION test_type_conversion_int8(x int8) RETURNS int8 AS $$
|
||||
plpy.info(x, type(x))
|
||||
return x
|
||||
$$ LANGUAGE plpythonu;
|
||||
SELECT * FROM test_type_conversion_int8(100);
|
||||
INFO: (100L, <type 'long'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_int8"
|
||||
test_type_conversion_int8
|
||||
---------------------------
|
||||
100
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_type_conversion_int8(-100);
|
||||
INFO: (-100L, <type 'long'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_int8"
|
||||
test_type_conversion_int8
|
||||
---------------------------
|
||||
-100
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_type_conversion_int8(5000000000);
|
||||
INFO: (5000000000L, <type 'long'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_int8"
|
||||
test_type_conversion_int8
|
||||
---------------------------
|
||||
5000000000
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_type_conversion_int8(null);
|
||||
INFO: (None, <class 'NoneType'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_int8"
|
||||
test_type_conversion_int8
|
||||
---------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
CREATE FUNCTION test_type_conversion_numeric(x numeric) RETURNS numeric AS $$
|
||||
plpy.info(x, type(x))
|
||||
return x
|
||||
$$ LANGUAGE plpythonu;
|
||||
/* The current implementation converts numeric to float. */
|
||||
SELECT * FROM test_type_conversion_numeric(100);
|
||||
INFO: (100.0, <class 'float'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_numeric"
|
||||
test_type_conversion_numeric
|
||||
------------------------------
|
||||
100.0
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_type_conversion_numeric(-100);
|
||||
INFO: (-100.0, <class 'float'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_numeric"
|
||||
test_type_conversion_numeric
|
||||
------------------------------
|
||||
-100.0
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_type_conversion_numeric(5000000000.5);
|
||||
INFO: (5000000000.5, <class 'float'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_numeric"
|
||||
test_type_conversion_numeric
|
||||
------------------------------
|
||||
5000000000.5
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_type_conversion_numeric(null);
|
||||
INFO: (None, <class 'NoneType'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_numeric"
|
||||
test_type_conversion_numeric
|
||||
------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
CREATE FUNCTION test_type_conversion_float4(x float4) RETURNS float4 AS $$
|
||||
plpy.info(x, type(x))
|
||||
return x
|
||||
$$ LANGUAGE plpythonu;
|
||||
SELECT * FROM test_type_conversion_float4(100);
|
||||
INFO: (100.0, <class 'float'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_float4"
|
||||
test_type_conversion_float4
|
||||
-----------------------------
|
||||
100
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_type_conversion_float4(-100);
|
||||
INFO: (-100.0, <class 'float'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_float4"
|
||||
test_type_conversion_float4
|
||||
-----------------------------
|
||||
-100
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_type_conversion_float4(5000.5);
|
||||
INFO: (5000.5, <class 'float'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_float4"
|
||||
test_type_conversion_float4
|
||||
-----------------------------
|
||||
5000.5
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_type_conversion_float4(null);
|
||||
INFO: (None, <class 'NoneType'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_float4"
|
||||
test_type_conversion_float4
|
||||
-----------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
CREATE FUNCTION test_type_conversion_float8(x float8) RETURNS float8 AS $$
|
||||
plpy.info(x, type(x))
|
||||
return x
|
||||
$$ LANGUAGE plpythonu;
|
||||
SELECT * FROM test_type_conversion_float8(100);
|
||||
INFO: (100.0, <class 'float'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_float8"
|
||||
test_type_conversion_float8
|
||||
-----------------------------
|
||||
100
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_type_conversion_float8(-100);
|
||||
INFO: (-100.0, <class 'float'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_float8"
|
||||
test_type_conversion_float8
|
||||
-----------------------------
|
||||
-100
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_type_conversion_float8(5000000000.5);
|
||||
INFO: (5000000000.5, <class 'float'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_float8"
|
||||
test_type_conversion_float8
|
||||
-----------------------------
|
||||
5000000000.5
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_type_conversion_float8(null);
|
||||
INFO: (None, <class 'NoneType'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_float8"
|
||||
test_type_conversion_float8
|
||||
-----------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
CREATE FUNCTION test_type_conversion_text(x text) RETURNS text AS $$
|
||||
plpy.info(x, type(x))
|
||||
return x
|
||||
$$ LANGUAGE plpythonu;
|
||||
SELECT * FROM test_type_conversion_text('hello world');
|
||||
INFO: ('hello world', <class 'str'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_text"
|
||||
test_type_conversion_text
|
||||
---------------------------
|
||||
hello world
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_type_conversion_text(null);
|
||||
INFO: (None, <class 'NoneType'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_text"
|
||||
test_type_conversion_text
|
||||
---------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
CREATE FUNCTION test_type_conversion_bytea(x bytea) RETURNS bytea AS $$
|
||||
plpy.info(x, type(x))
|
||||
return x
|
||||
$$ LANGUAGE plpythonu;
|
||||
SELECT * FROM test_type_conversion_bytea('hello world');
|
||||
INFO: (b'hello world', <class 'bytes'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_bytea"
|
||||
test_type_conversion_bytea
|
||||
----------------------------
|
||||
\x68656c6c6f20776f726c64
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_type_conversion_bytea(E'null\\000byte');
|
||||
INFO: (b'null\x00byte', <class 'bytes'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_bytea"
|
||||
test_type_conversion_bytea
|
||||
----------------------------
|
||||
\x6e756c6c0062797465
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_type_conversion_bytea(null);
|
||||
INFO: (None, <class 'NoneType'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_bytea"
|
||||
test_type_conversion_bytea
|
||||
----------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
CREATE FUNCTION test_type_marshal() RETURNS bytea AS $$
|
||||
import marshal
|
||||
return marshal.dumps('hello world')
|
||||
$$ LANGUAGE plpythonu;
|
||||
CREATE FUNCTION test_type_unmarshal(x bytea) RETURNS text AS $$
|
||||
import marshal
|
||||
try:
|
||||
return marshal.loads(x)
|
||||
except ValueError, e:
|
||||
return 'FAILED: ' + str(e)
|
||||
$$ LANGUAGE plpythonu;
|
||||
SELECT test_type_unmarshal(x) FROM test_type_marshal() x;
|
||||
test_type_unmarshal
|
||||
---------------------
|
||||
hello world
|
||||
(1 row)
|
||||
|
||||
--
|
||||
-- Domains
|
||||
--
|
||||
CREATE DOMAIN booltrue AS bool CHECK (VALUE IS TRUE OR VALUE IS NULL);
|
||||
CREATE FUNCTION test_type_conversion_booltrue(x booltrue, y bool) RETURNS booltrue AS $$
|
||||
return y
|
||||
$$ LANGUAGE plpythonu;
|
||||
SELECT * FROM test_type_conversion_booltrue(true, true);
|
||||
test_type_conversion_booltrue
|
||||
-------------------------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_type_conversion_booltrue(false, true);
|
||||
ERROR: value for domain booltrue violates check constraint "booltrue_check"
|
||||
SELECT * FROM test_type_conversion_booltrue(true, false);
|
||||
ERROR: value for domain booltrue violates check constraint "booltrue_check"
|
||||
CONTEXT: while creating return value
|
||||
PL/Python function "test_type_conversion_booltrue"
|
||||
CREATE DOMAIN uint2 AS int2 CHECK (VALUE >= 0);
|
||||
CREATE FUNCTION test_type_conversion_uint2(x uint2, y int) RETURNS uint2 AS $$
|
||||
plpy.info(x, type(x))
|
||||
return y
|
||||
$$ LANGUAGE plpythonu;
|
||||
SELECT * FROM test_type_conversion_uint2(100::uint2, 50);
|
||||
INFO: (100, <class 'int'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_uint2"
|
||||
test_type_conversion_uint2
|
||||
----------------------------
|
||||
50
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_type_conversion_uint2(100::uint2, -50);
|
||||
INFO: (100, <class 'int'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_uint2"
|
||||
ERROR: value for domain uint2 violates check constraint "uint2_check"
|
||||
CONTEXT: while creating return value
|
||||
PL/Python function "test_type_conversion_uint2"
|
||||
SELECT * FROM test_type_conversion_uint2(null, 1);
|
||||
INFO: (None, <class 'NoneType'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_uint2"
|
||||
test_type_conversion_uint2
|
||||
----------------------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
CREATE DOMAIN nnint AS int CHECK (VALUE IS NOT NULL);
|
||||
CREATE FUNCTION test_type_conversion_nnint(x nnint, y int) RETURNS nnint AS $$
|
||||
return y
|
||||
$$ LANGUAGE plpythonu;
|
||||
SELECT * FROM test_type_conversion_nnint(10, 20);
|
||||
test_type_conversion_nnint
|
||||
----------------------------
|
||||
20
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_type_conversion_nnint(null, 20);
|
||||
ERROR: value for domain nnint violates check constraint "nnint_check"
|
||||
SELECT * FROM test_type_conversion_nnint(10, null);
|
||||
ERROR: value for domain nnint violates check constraint "nnint_check"
|
||||
CONTEXT: while creating return value
|
||||
PL/Python function "test_type_conversion_nnint"
|
||||
CREATE DOMAIN bytea10 AS bytea CHECK (octet_length(VALUE) = 10 AND VALUE IS NOT NULL);
|
||||
CREATE FUNCTION test_type_conversion_bytea10(x bytea10, y bytea) RETURNS bytea10 AS $$
|
||||
plpy.info(x, type(x))
|
||||
return y
|
||||
$$ LANGUAGE plpythonu;
|
||||
SELECT * FROM test_type_conversion_bytea10('hello wold', 'hello wold');
|
||||
INFO: (b'hello wold', <class 'bytes'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_bytea10"
|
||||
test_type_conversion_bytea10
|
||||
------------------------------
|
||||
\x68656c6c6f20776f6c64
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_type_conversion_bytea10('hello world', 'hello wold');
|
||||
ERROR: value for domain bytea10 violates check constraint "bytea10_check"
|
||||
SELECT * FROM test_type_conversion_bytea10('hello word', 'hello world');
|
||||
INFO: (b'hello word', <class 'bytes'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_bytea10"
|
||||
ERROR: value for domain bytea10 violates check constraint "bytea10_check"
|
||||
CONTEXT: while creating return value
|
||||
PL/Python function "test_type_conversion_bytea10"
|
||||
SELECT * FROM test_type_conversion_bytea10(null, 'hello word');
|
||||
ERROR: value for domain bytea10 violates check constraint "bytea10_check"
|
||||
SELECT * FROM test_type_conversion_bytea10('hello word', null);
|
||||
INFO: (b'hello word', <class 'bytes'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_bytea10"
|
||||
ERROR: value for domain bytea10 violates check constraint "bytea10_check"
|
||||
CONTEXT: while creating return value
|
||||
PL/Python function "test_type_conversion_bytea10"
|
||||
--
|
||||
-- Arrays
|
||||
--
|
||||
CREATE FUNCTION test_type_conversion_array_int4(x int4[]) RETURNS int4[] AS $$
|
||||
plpy.info(x, type(x))
|
||||
return x
|
||||
$$ LANGUAGE plpythonu;
|
||||
SELECT * FROM test_type_conversion_array_int4(ARRAY[0, 100]);
|
||||
INFO: ([0, 100], <class 'list'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_array_int4"
|
||||
test_type_conversion_array_int4
|
||||
---------------------------------
|
||||
{0,100}
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_type_conversion_array_int4(ARRAY[0,-100,55]);
|
||||
INFO: ([0, -100, 55], <class 'list'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_array_int4"
|
||||
test_type_conversion_array_int4
|
||||
---------------------------------
|
||||
{0,-100,55}
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_type_conversion_array_int4(ARRAY[NULL,1]);
|
||||
INFO: ([None, 1], <class 'list'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_array_int4"
|
||||
test_type_conversion_array_int4
|
||||
---------------------------------
|
||||
{NULL,1}
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_type_conversion_array_int4(ARRAY[]::integer[]);
|
||||
INFO: ([], <class 'list'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_array_int4"
|
||||
test_type_conversion_array_int4
|
||||
---------------------------------
|
||||
{}
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_type_conversion_array_int4(NULL);
|
||||
INFO: (None, <class 'NoneType'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_array_int4"
|
||||
test_type_conversion_array_int4
|
||||
---------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_type_conversion_array_int4(ARRAY[[1,2,3],[4,5,6]]);
|
||||
ERROR: cannot convert multidimensional array to Python list
|
||||
DETAIL: PL/Python only supports one-dimensional arrays.
|
||||
CONTEXT: PL/Python function "test_type_conversion_array_int4"
|
||||
CREATE FUNCTION test_type_conversion_array_bytea(x bytea[]) RETURNS bytea[] AS $$
|
||||
plpy.info(x, type(x))
|
||||
return x
|
||||
$$ LANGUAGE plpythonu;
|
||||
SELECT * FROM test_type_conversion_array_bytea(ARRAY[E'\\xdeadbeef'::bytea, NULL]);
|
||||
INFO: ([b'\xde\xad\xbe\xef', None], <class 'list'>)
|
||||
CONTEXT: PL/Python function "test_type_conversion_array_bytea"
|
||||
test_type_conversion_array_bytea
|
||||
----------------------------------
|
||||
{"\\xdeadbeef",NULL}
|
||||
(1 row)
|
||||
|
||||
CREATE FUNCTION test_type_conversion_array_mixed1() RETURNS text[] AS $$
|
||||
return [123, 'abc']
|
||||
$$ LANGUAGE plpythonu;
|
||||
SELECT * FROM test_type_conversion_array_mixed1();
|
||||
test_type_conversion_array_mixed1
|
||||
-----------------------------------
|
||||
{123,abc}
|
||||
(1 row)
|
||||
|
||||
CREATE FUNCTION test_type_conversion_array_mixed2() RETURNS int[] AS $$
|
||||
return [123, 'abc']
|
||||
$$ LANGUAGE plpythonu;
|
||||
SELECT * FROM test_type_conversion_array_mixed2();
|
||||
ERROR: invalid input syntax for integer: "abc"
|
||||
CONTEXT: while creating return value
|
||||
PL/Python function "test_type_conversion_array_mixed2"
|
||||
CREATE FUNCTION test_type_conversion_array_record() RETURNS type_record[] AS $$
|
||||
return [None]
|
||||
$$ LANGUAGE plpythonu;
|
||||
SELECT * FROM test_type_conversion_array_record();
|
||||
ERROR: PL/Python functions cannot return type type_record[]
|
||||
DETAIL: PL/Python does not support conversion to arrays of row types.
|
||||
CREATE FUNCTION test_type_conversion_array_string() RETURNS text[] AS $$
|
||||
return 'abc'
|
||||
$$ LANGUAGE plpythonu;
|
||||
SELECT * FROM test_type_conversion_array_string();
|
||||
test_type_conversion_array_string
|
||||
-----------------------------------
|
||||
{a,b,c}
|
||||
(1 row)
|
||||
|
||||
CREATE FUNCTION test_type_conversion_array_tuple() RETURNS text[] AS $$
|
||||
return ('abc', 'def')
|
||||
$$ LANGUAGE plpythonu;
|
||||
SELECT * FROM test_type_conversion_array_tuple();
|
||||
test_type_conversion_array_tuple
|
||||
----------------------------------
|
||||
{abc,def}
|
||||
(1 row)
|
||||
|
||||
CREATE FUNCTION test_type_conversion_array_error() RETURNS int[] AS $$
|
||||
return 5
|
||||
$$ LANGUAGE plpythonu;
|
||||
SELECT * FROM test_type_conversion_array_error();
|
||||
ERROR: PL/Python: return value of function with array return type is not a Python sequence
|
||||
CONTEXT: while creating return value
|
||||
PL/Python function "test_type_conversion_array_error"
|
@ -1,7 +1,7 @@
|
||||
/**********************************************************************
|
||||
* 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;
|
||||
#define PyBool_FromLong(x) PyInt_FromLong(x)
|
||||
#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"
|
||||
|
||||
@ -246,7 +288,11 @@ static char *PLy_strdup(const char *);
|
||||
static void PLy_free(void *);
|
||||
|
||||
static PyObject*PLyUnicode_Str(PyObject *unicode);
|
||||
static PyObject*PLyUnicode_Bytes(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 */
|
||||
static Datum PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure *);
|
||||
@ -288,7 +334,7 @@ static PyObject *PLyFloat_FromNumeric(PLyDatumToOb *arg, Datum d);
|
||||
static PyObject *PLyInt_FromInt16(PLyDatumToOb *arg, Datum d);
|
||||
static PyObject *PLyInt_FromInt32(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 *PLyList_FromArray(PLyDatumToOb *arg, Datum d);
|
||||
|
||||
@ -1760,7 +1806,7 @@ PLy_input_datum_func2(PLyDatumToOb *arg, Oid typeOid, HeapTuple typeTup)
|
||||
arg->func = PLyLong_FromInt64;
|
||||
break;
|
||||
case BYTEAOID:
|
||||
arg->func = PLyString_FromBytea;
|
||||
arg->func = PLyBytes_FromBytea;
|
||||
break;
|
||||
default:
|
||||
arg->func = PLyString_FromDatum;
|
||||
@ -1859,13 +1905,13 @@ PLyLong_FromInt64(PLyDatumToOb *arg, Datum d)
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
PLyString_FromBytea(PLyDatumToOb *arg, Datum d)
|
||||
PLyBytes_FromBytea(PLyDatumToOb *arg, Datum d)
|
||||
{
|
||||
text *txt = DatumGetByteaP(d);
|
||||
char *str = VARDATA(txt);
|
||||
size_t size = VARSIZE(txt) - VARHDRSZ;
|
||||
|
||||
return PyString_FromStringAndSize(str, size);
|
||||
return PyBytes_FromStringAndSize(str, size);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
@ -2001,14 +2047,14 @@ PLyObject_ToBytea(PLyTypeInfo *info,
|
||||
|
||||
Assert(plrv != Py_None);
|
||||
|
||||
plrv_so = PyObject_Str(plrv);
|
||||
plrv_so = PyObject_Bytes(plrv);
|
||||
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();
|
||||
{
|
||||
char *plrv_sc = PyString_AsString(plrv_so);
|
||||
size_t len = PyString_Size(plrv_so);
|
||||
char *plrv_sc = PyBytes_AsString(plrv_so);
|
||||
size_t len = PyBytes_Size(plrv_so);
|
||||
size_t size = len + VARHDRSZ;
|
||||
bytea *result = palloc(size);
|
||||
|
||||
@ -2040,22 +2086,30 @@ PLyObject_ToDatum(PLyTypeInfo *info,
|
||||
PLyObToDatum *arg,
|
||||
PyObject *plrv)
|
||||
{
|
||||
PyObject *volatile plrv_so = NULL;
|
||||
PyObject *volatile plrv_bo = NULL;
|
||||
Datum rv;
|
||||
|
||||
Assert(plrv != Py_None);
|
||||
|
||||
if (PyUnicode_Check(plrv))
|
||||
plrv_so = PLyUnicode_Str(plrv);
|
||||
plrv_bo = PLyUnicode_Bytes(plrv);
|
||||
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");
|
||||
|
||||
PG_TRY();
|
||||
{
|
||||
char *plrv_sc = PyString_AsString(plrv_so);
|
||||
size_t plen = PyString_Size(plrv_so);
|
||||
char *plrv_sc = PyBytes_AsString(plrv_bo);
|
||||
size_t plen = PyBytes_Size(plrv_bo);
|
||||
size_t slen = strlen(plrv_sc);
|
||||
|
||||
if (slen < plen)
|
||||
@ -2068,12 +2122,12 @@ PLyObject_ToDatum(PLyTypeInfo *info,
|
||||
}
|
||||
PG_CATCH();
|
||||
{
|
||||
Py_XDECREF(plrv_so);
|
||||
Py_XDECREF(plrv_bo);
|
||||
PG_RE_THROW();
|
||||
}
|
||||
PG_END_TRY();
|
||||
|
||||
Py_XDECREF(plrv_so);
|
||||
Py_XDECREF(plrv_bo);
|
||||
|
||||
return rv;
|
||||
}
|
||||
@ -2368,8 +2422,7 @@ static PyMethodDef PLy_plan_methods[] = {
|
||||
};
|
||||
|
||||
static PyTypeObject PLy_PlanType = {
|
||||
PyObject_HEAD_INIT(NULL)
|
||||
0, /* ob_size */
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
"PLyPlan", /* tp_name */
|
||||
sizeof(PLyPlanObject), /* tp_size */
|
||||
0, /* tp_itemsize */
|
||||
@ -2420,8 +2473,7 @@ static PyMethodDef PLy_result_methods[] = {
|
||||
};
|
||||
|
||||
static PyTypeObject PLy_ResultType = {
|
||||
PyObject_HEAD_INIT(NULL)
|
||||
0, /* ob_size */
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
"PLyResult", /* tp_name */
|
||||
sizeof(PLyResultObject), /* tp_size */
|
||||
0, /* tp_itemsize */
|
||||
@ -2480,6 +2532,15 @@ static PyMethodDef PLy_methods[] = {
|
||||
{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 */
|
||||
static PyObject *
|
||||
@ -3067,6 +3128,15 @@ PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status)
|
||||
* 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
|
||||
*
|
||||
@ -3083,7 +3153,13 @@ _PG_init(void)
|
||||
|
||||
pg_bindtextdomain(TEXTDOMAIN);
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
PyImport_AppendInittab("plpy", PyInit_plpy);
|
||||
#endif
|
||||
Py_Initialize();
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
PyImport_ImportModule("plpy");
|
||||
#endif
|
||||
PLy_init_interp();
|
||||
PLy_init_plpy();
|
||||
if (PyErr_Occurred())
|
||||
@ -3129,7 +3205,11 @@ PLy_init_plpy(void)
|
||||
if (PyType_Ready(&PLy_ResultType) < 0)
|
||||
elog(ERROR, "could not initialize PLy_ResultType");
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
plpy = PyModule_Create(&PLy_module);
|
||||
#else
|
||||
plpy = Py_InitModule("plpy", PLy_methods);
|
||||
#endif
|
||||
plpy_dict = PyModule_GetDict(plpy);
|
||||
|
||||
/* PyDict_SetItemString(plpy, "PlanType", (PyObject *) &PLy_PlanType); */
|
||||
@ -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
|
||||
* caller.
|
||||
*/
|
||||
static PyObject*
|
||||
PLyUnicode_Str(PyObject *unicode)
|
||||
PLyUnicode_Bytes(PyObject *unicode)
|
||||
{
|
||||
PyObject *rv;
|
||||
const char *serverenc;
|
||||
@ -3502,13 +3599,44 @@ PLyUnicode_Str(PyObject *unicode)
|
||||
/*
|
||||
* Convert a Python unicode object to a C string in PostgreSQL server
|
||||
* 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 *
|
||||
PLyUnicode_AsString(PyObject *unicode)
|
||||
{
|
||||
PyObject *o = PLyUnicode_Str(unicode);
|
||||
char *rv = PyString_AsString(o);
|
||||
PyObject *o = PLyUnicode_Bytes(unicode);
|
||||
char *rv = pstrdup(PyBytes_AsString(o));
|
||||
Py_XDECREF(o);
|
||||
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 */
|
||||
|
@ -1,10 +1,15 @@
|
||||
-- first some tests of basic functionality
|
||||
CREATE LANGUAGE plpython2u;
|
||||
|
||||
-- really stupid function just to get the module loaded
|
||||
CREATE FUNCTION stupid() RETURNS text AS 'return "zarkon"' LANGUAGE plpythonu;
|
||||
|
||||
select stupid();
|
||||
|
||||
-- check 2/3 versioning
|
||||
CREATE FUNCTION stupidn() RETURNS text AS 'return "zarkon"' LANGUAGE plpython2u;
|
||||
|
||||
select stupidn();
|
||||
|
||||
-- test multiple arguments
|
||||
CREATE FUNCTION argument_test_one(u users, a1 text, a2 text) RETURNS text
|
||||
|
@ -67,7 +67,7 @@ SELECT * FROM users;
|
||||
CREATE TABLE trigger_test
|
||||
(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:
|
||||
TD['relid'] = "bogus:12345"
|
||||
|
Loading…
x
Reference in New Issue
Block a user