mirror of https://github.com/postgres/postgres
there's no shared libpython. Test suite works as well. Also, add some documentation.REL7_2_STABLE
parent
02549a2d2c
commit
bbc3920fe9
@ -1,50 +1,88 @@ |
|||||||
# |
# |
||||||
# Autoconf macros for configuring the build of Python extension modules |
# Autoconf macros for configuring the build of Python extension modules |
||||||
# |
# |
||||||
# $Header: /cvsroot/pgsql/config/python.m4,v 1.1 2000/06/10 18:01:35 petere Exp $ |
# $Header: /cvsroot/pgsql/config/python.m4,v 1.2 2001/05/12 17:49:32 petere Exp $ |
||||||
# |
# |
||||||
|
|
||||||
# PGAC_PROG_PYTHON |
# PGAC_PATH_PYTHON |
||||||
# ---------------- |
# ---------------- |
||||||
# Look for Python and set the output variable `PYTHON' |
# Look for Python and set the output variable `PYTHON' |
||||||
# to `python' if found, empty otherwise. |
# to `python' if found, empty otherwise. |
||||||
AC_DEFUN([PGAC_PROG_PYTHON], |
AC_DEFUN([PGAC_PATH_PYTHON], |
||||||
[AC_CHECK_PROG(PYTHON, python, python)]) |
[AC_PATH_PROG(PYTHON, python) |
||||||
|
if test x"$PYTHON" = x""; then |
||||||
|
AC_MSG_ERROR([Python not found]) |
||||||
|
fi |
||||||
|
]) |
||||||
|
|
||||||
|
|
||||||
|
# _PGAC_CHECK_PYTHON_DIRS |
||||||
|
# ----------------------- |
||||||
|
# Determine the name of various directory of a given Python installation. |
||||||
|
AC_DEFUN([_PGAC_CHECK_PYTHON_DIRS], |
||||||
|
[AC_REQUIRE([PGAC_PATH_PYTHON]) |
||||||
|
python_version=`${PYTHON} -c "import sys; print sys.version[[:3]]"` |
||||||
|
python_prefix=`${PYTHON} -c "import sys; print sys.prefix"` |
||||||
|
python_execprefix=`${PYTHON} -c "import sys; print sys.exec_prefix"` |
||||||
|
python_configdir="${python_execprefix}/lib/python${python_version}/config" |
||||||
|
python_moduledir="${python_prefix}/lib/python${python_version}" |
||||||
|
python_includedir="${python_prefix}/include/python${python_version}" |
||||||
|
python_dynlibdir="${python_execprefix}/lib/python${python_version}/lib-dynload" |
||||||
|
|
||||||
|
AC_SUBST(python_version)[]dnl |
||||||
|
AC_SUBST(python_prefix)[]dnl |
||||||
|
AC_SUBST(python_execprefix)[]dnl |
||||||
|
AC_SUBST(python_configdir)[]dnl |
||||||
|
AC_SUBST(python_moduledir)[]dnl |
||||||
|
AC_SUBST(python_includedir)[]dnl |
||||||
|
AC_SUBST(python_dynlibdir)[]dnl |
||||||
|
])# _PGAC_CHECK_PYTHON_DIRS |
||||||
|
|
||||||
|
|
||||||
# PGAC_PATH_PYTHONDIR |
# PGAC_CHECK_PYTHON_MODULE_SETUP |
||||||
# ------------------- |
# ------------------------------ |
||||||
# Finds the names of various install dirs and helper files |
# Finds things required to build a Python extension module, in |
||||||
# necessary to build a Python extension module. |
# particular the makefile. |
||||||
# |
# |
||||||
# It would be nice if we could check whether the current setup allows |
# It would be nice if we could check whether the current setup allows |
||||||
# the build of the shared module. Future project. |
# the build of the shared module. Future project. |
||||||
AC_DEFUN([PGAC_PATH_PYTHONDIR], |
AC_DEFUN([PGAC_CHECK_PYTHON_MODULE_SETUP], |
||||||
[AC_REQUIRE([PGAC_PROG_PYTHON]) |
[AC_REQUIRE([_PGAC_CHECK_PYTHON_DIRS]) |
||||||
[if test "${PYTHON+set}" = set ; then |
AC_MSG_CHECKING([for makefile to build Python module]) |
||||||
python_version=`${PYTHON} -c "import sys; print sys.version[:3]"` |
python_makefile_pre_in="${python_configdir}/Makefile.pre.in" |
||||||
python_prefix=`${PYTHON} -c "import sys; print sys.prefix"` |
|
||||||
python_execprefix=`${PYTHON} -c "import sys; print sys.exec_prefix"` |
if test -f "${python_makefile_pre_in}" ; then |
||||||
python_configdir="${python_execprefix}/lib/python${python_version}/config" |
AC_MSG_RESULT([${python_makefile_pre_in}]) |
||||||
python_moduledir="${python_prefix}/lib/python${python_version}" |
else |
||||||
python_extmakefile="${python_configdir}/Makefile.pre.in"] |
|
||||||
|
|
||||||
AC_MSG_CHECKING(for Python extension makefile) |
|
||||||
if test -f "${python_extmakefile}" ; then |
|
||||||
AC_MSG_RESULT(found) |
|
||||||
else |
|
||||||
AC_MSG_RESULT(no) |
AC_MSG_RESULT(no) |
||||||
AC_MSG_ERROR( |
AC_MSG_ERROR( |
||||||
[The Python extension makefile was expected at \`${python_extmakefile}\' |
[The file |
||||||
but does not exist. This means the Python module cannot be built automatically.]) |
${python_makefile_pre_in} |
||||||
fi |
required to build Python modules does not exist. Make sure that you have |
||||||
|
a full Python installation and that this is the right location.]) |
||||||
AC_SUBST(python_version) |
fi |
||||||
AC_SUBST(python_prefix) |
|
||||||
AC_SUBST(python_execprefix) |
AC_SUBST(python_makefile_pre_in)[]dnl |
||||||
AC_SUBST(python_configdir) |
])# PGAC_CHECK_PYTHON_MODULE_SETUP |
||||||
AC_SUBST(python_moduledir) |
|
||||||
AC_SUBST(python_extmakefile) |
|
||||||
else |
# PGAC_CHECK_PYTHON_EMBED_SETUP |
||||||
AC_MSG_ERROR([Python not found]) |
# ----------------------------- |
||||||
fi])# PGAC_PATH_PYTHONDIR |
# Courtesy of the INN 2.3.1 package... |
||||||
|
AC_DEFUN([PGAC_CHECK_PYTHON_EMBED_SETUP], |
||||||
|
[AC_REQUIRE([_PGAC_CHECK_PYTHON_DIRS]) |
||||||
|
AC_MSG_CHECKING([how to link an embedded Python application]) |
||||||
|
|
||||||
|
_python_libs=`grep '^LIBS=' $python_configdir/Makefile | sed 's/^.*=//'` |
||||||
|
_python_libc=`grep '^LIBC=' $python_configdir/Makefile | sed 's/^.*=//'` |
||||||
|
_python_libm=`grep '^LIBM=' $python_configdir/Makefile | sed 's/^.*=//'` |
||||||
|
_python_liblocalmod=`grep '^LOCALMODLIBS=' $python_configdir/Makefile | sed 's/^.*=//'` |
||||||
|
_python_libbasemod=`grep '^BASEMODLIBS=' $python_configdir/Makefile | sed 's/^.*=//'` |
||||||
|
|
||||||
|
pgac_tab=" " # tab character |
||||||
|
python_libspec=`echo X"-L$python_configdir $_python_libs $_python_libc $_python_libm -lpython$python_version $_python_liblocalmod $_python_libbasemod" | sed -e 's/^X//' -e "s/[[ $pgac_tab]][[ $pgac_tab]]*/ /g"` |
||||||
|
|
||||||
|
AC_MSG_RESULT([${python_libspec}]) |
||||||
|
|
||||||
|
AC_SUBST(python_libspec)[]dnl |
||||||
|
])# PGAC_CHECK_PYTHON_EMBED_SETUP |
||||||
|
@ -0,0 +1,152 @@ |
|||||||
|
<!-- $Header: /cvsroot/pgsql/doc/src/sgml/plpython.sgml,v 1.1 2001/05/12 17:49:32 petere Exp $ --> |
||||||
|
|
||||||
|
<chapter id="plpython"> |
||||||
|
<title>PL/Python - Python Procedural Language</title> |
||||||
|
|
||||||
|
<note> |
||||||
|
<para> |
||||||
|
This chapter is not fully developed yet. |
||||||
|
</para> |
||||||
|
</note> |
||||||
|
|
||||||
|
<sect1 id="plpython-install"> |
||||||
|
<title>Installation</title> |
||||||
|
|
||||||
|
<para> |
||||||
|
... needs to be worked out. |
||||||
|
</para> |
||||||
|
</sect1> |
||||||
|
|
||||||
|
<sect1 id="plpython-using"> |
||||||
|
<title>Using</title> |
||||||
|
|
||||||
|
<para> |
||||||
|
There are sample functions in |
||||||
|
<filename>plpython_function.sql</filename>. The Python code you |
||||||
|
write gets transformed into a function. E.g., |
||||||
|
<programlisting> |
||||||
|
CREATE FUNCTION myfunc(text) RETURNS text AS |
||||||
|
'return args[0]' |
||||||
|
LANGUAGE 'plpython'; |
||||||
|
</programlisting> |
||||||
|
|
||||||
|
gets transformed into |
||||||
|
|
||||||
|
<programlisting> |
||||||
|
def __plpython_procedure_myfunc_23456(): |
||||||
|
return args[0] |
||||||
|
</programlisting> |
||||||
|
|
||||||
|
where 23456 is the Oid of the function. |
||||||
|
</para> |
||||||
|
|
||||||
|
<para> |
||||||
|
If you do not provide a return value, Python returns the default |
||||||
|
<symbol>None</symbol> which may or may not be what you want. The |
||||||
|
language module translates Python's None into SQL NULL. |
||||||
|
</para> |
||||||
|
|
||||||
|
<para> |
||||||
|
PostgreSQL function variables are available in the global |
||||||
|
<varname>args</varname> list. In the <function>myfunc</function> |
||||||
|
example, args[0] contains whatever was passed in as the text |
||||||
|
argument. For <literal>myfunc2(text, int4)</literal>, args[0] |
||||||
|
would contain the text variable and args[1] the int4 variable. |
||||||
|
</para> |
||||||
|
|
||||||
|
<para> |
||||||
|
The global dictionary SD is available to store data between |
||||||
|
function calls. This variable is private static data. The global |
||||||
|
dictionary GD is public data, available to all python functions |
||||||
|
within a backend. Use with care. When the function is used in a |
||||||
|
trigger, the triggers tuples are in TD["new"] and/or TD["old"] |
||||||
|
depending on the trigger event. Return 'None' or "OK" from the |
||||||
|
python function to indicate the tuple is unmodified, "SKIP" to |
||||||
|
abort the event, or "MODIFIED" to indicate you've modified the |
||||||
|
tuple. If the trigger was called with arguments they are available |
||||||
|
in TD["args"][0] to TD["args"][(n -1)] |
||||||
|
</para> |
||||||
|
|
||||||
|
<para> |
||||||
|
Each function gets its own restricted execution object in the |
||||||
|
Python interpreter, so that global data and function arguments from |
||||||
|
<function>myfunc</function> are not available to |
||||||
|
<function>myfunc2</function>. The exception is the data in the GD |
||||||
|
dictionary, as mentioned above. |
||||||
|
</para> |
||||||
|
|
||||||
|
<para> |
||||||
|
The PL/Python language module automatically imports a Python module |
||||||
|
called <literal>plpy</literal>. The functions and constants in |
||||||
|
this module are available to you in the Python code as |
||||||
|
<literal>plpy.<replaceable>foo</replaceable></literal>. At present |
||||||
|
<literal>plpy</literal> implements the functions |
||||||
|
<literal>plpy.error("msg")</literal>, |
||||||
|
<literal>plpy.fatal("msg")</literal>, |
||||||
|
<literal>plpy.debug("msg")</literal>, and |
||||||
|
<literal>plpy.notice("msg")</literal>. They are mostly equivalent |
||||||
|
to calling <literal>elog(<replaceable>LEVEL</>, "msg")</literal>, |
||||||
|
where <replaceable>LEVEL</> is DEBUG, ERROR, FATAL or NOTICE. |
||||||
|
<function>plpy.error</function> and <function>plpy.fatal</function> |
||||||
|
actually raise a Python exception which, if uncaught, causes the |
||||||
|
PL/Python module to call <literal>elog(ERROR, msg)</literal> when |
||||||
|
the function handler returns from the Python interpreter. Long |
||||||
|
jumping out of the Python interpreter is probably not good. |
||||||
|
<literal>raise plpy.ERROR("msg")</literal> and <literal>raise |
||||||
|
plpy.FATAL("msg")</literal> are equivalent to calling |
||||||
|
<function>plpy.error</function> or <function>plpy.fatal</function>. |
||||||
|
</para> |
||||||
|
|
||||||
|
<para> |
||||||
|
Additionally, the plpy module provides two functions called |
||||||
|
<function>execute</function> and <function>prepare</function>. |
||||||
|
Calling <function>plpy.execute</function> with a query string, and |
||||||
|
an optional limit argument, causes that query to be run, and the |
||||||
|
result returned in a result object. The result object emulates a |
||||||
|
list or dictionary object. The result object can be accessed by |
||||||
|
row number, and field name. It has these additional methods: |
||||||
|
<function>nrows()</function> which returns the number of rows |
||||||
|
returned by the query, and <function>status</function> which is the |
||||||
|
<function>SPI_exec</function> return variable. The result object |
||||||
|
can be modified. |
||||||
|
|
||||||
|
<programlisting> |
||||||
|
rv = plpy.execute("SELECT * FROM my_table", 5) |
||||||
|
</programlisting> |
||||||
|
returns up to 5 rows from my_table. Ff my_table has a column |
||||||
|
my_field it would be accessed as |
||||||
|
<programlisting> |
||||||
|
foo = rv[i]["my_field"] |
||||||
|
</programlisting> |
||||||
|
The second function <function>plpy.prepare</function> is called |
||||||
|
with a query string, and a list of argument types if you have bind |
||||||
|
variables in the query. |
||||||
|
<programlisting> |
||||||
|
plan = plpy.prepare("SELECT last_name FROM my_users WHERE first_name = $1", [ "text" ]) |
||||||
|
</programlisting> |
||||||
|
text is the type of the variable you will be passing as $1. After |
||||||
|
preparing you use the function <function>plpy.execute</function> to |
||||||
|
run it. |
||||||
|
<programlisting> |
||||||
|
rv = plpy.execute(plan, [ "name" ], 5) |
||||||
|
</programlisting> |
||||||
|
The limit argument is optional in the call to |
||||||
|
<function>plpy.execute</function>. |
||||||
|
</para> |
||||||
|
|
||||||
|
<para> |
||||||
|
When you prepare a plan using the PL/Python module it is |
||||||
|
automatically saved. Read the SPI documentation (<xref |
||||||
|
linkend="spi">) for a description of what this means. The take |
||||||
|
home message is if you do |
||||||
|
<programlisting> |
||||||
|
plan = plpy.prepare("SOME QUERY") |
||||||
|
plan = plpy.prepare("SOME OTHER QUERY") |
||||||
|
</programlisting> |
||||||
|
you are leaking memory, as I know of no way to free a saved plan. |
||||||
|
The alternative of using unsaved plans it even more painful (for |
||||||
|
me). |
||||||
|
</para> |
||||||
|
</sect1> |
||||||
|
|
||||||
|
</chapter> |
@ -1,9 +1,25 @@ |
|||||||
<html> |
In no particular order... |
||||||
<head><title>TODO</title></head> |
|
||||||
<body bgcolor="white"> |
* Allow arrays as function arguments and return values. (almost done) |
||||||
In no special order<br> |
|
||||||
<li>allow arrays as function arguments and return values (almost done). |
* Create a new restricted execution class that will allow me to pass |
||||||
<li>more and better documentation. |
function arguments in as locals. Passing them as globals means |
||||||
<li>improve/automate configuration. |
functions cannot be called recursively. |
||||||
<li>??? |
|
||||||
</body> |
* Functions cache the input and output functions for their arguments, |
||||||
|
so the following will make PostgreSQL unhappy: |
||||||
|
|
||||||
|
create table users (first_name text, last_name text); |
||||||
|
create function user_name(user) returns text as 'mycode' language 'plpython'; |
||||||
|
select user_name(user) from users; |
||||||
|
alter table add column user_id integer; |
||||||
|
select user_name(user) from users; |
||||||
|
|
||||||
|
You have to drop and create the function(s) each time its arguments |
||||||
|
are modified (not nice), or don't cache the input and output functions |
||||||
|
(slower?), or check if the structure of the argument has been |
||||||
|
altered (is this possible, easy, quick?) and recreate cache. |
||||||
|
|
||||||
|
* Better documentation |
||||||
|
|
||||||
|
* Add a DB-API compliant interface on top of the SPI interface. |
||||||
|
Loading…
Reference in new issue