<para>
To install PL/Python in a particular database, use
- <literal>createlang plpythonu <replaceable>dbname</></literal> (but
+ <literal>CREATE EXTENSION plpythonu</> (but
see also <xref linkend="plpython-python23">).
</para>
</tip>
<para>
- As of <productname>PostgreSQL</productname> 7.4, PL/Python is only
- available as an <quote>untrusted</> language, meaning it does not
- offer any way of restricting what users can do in it. It has
- therefore been renamed to <literal>plpythonu</>. The trusted
- variant <literal>plpython</> might become available again in future,
- if a new secure execution mechanism is developed in Python. The
+ PL/Python is only available as an <quote>untrusted</> language, meaning
+ it does not offer any way of restricting what users can do in it and
+ is therefore named <literal>plpythonu</>. A trusted
+ variant <literal>plpython</> might become available in the future
+ if a secure execution mechanism is developed in Python. The
writer of a function in untrusted PL/Python must take care that the
function cannot be used to do anything unwanted, since it will be
able to do anything that could be done by a user logged in as the
</listitem>
</itemizedlist>
+ This scheme is analogous to the recommendations in <ulink
+ url="http://www.python.org/dev/peps/pep-0394/">PEP 394</ulink> regarding the
+ naming and transitioning of the <command>python</command> command.
+ </para>
+
+ <para>
It depends on the build configuration or the installed packages
whether PL/Python for Python 2 or Python 3 or both are available.
</para>
<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
+ environment, can change the contents of
+ <link linkend="catalog-pg-pltemplate"><structname>pg_pltemplate</structname></link>
+ 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.
<sect2>
<title>Data Type Mapping</title>
<para>
- Function arguments are converted from their PostgreSQL type to a
- corresponding Python type:
+ When a PL/Python function is called, its arguments are converted from
+ their PostgreSQL data type to a corresponding Python type:
+
<itemizedlist>
<listitem>
<para>
<para>
PostgreSQL <type>smallint</type> and <type>int</type> are
converted to Python <type>int</type>.
- PostgreSQL <type>bigint</type> is converted
+ PostgreSQL <type>bigint</type> and <type>oid</type> are converted
to <type>long</type> in Python 2 and to <type>int</type> in
Python 3.
</para>
<listitem>
<para>
- PostgreSQL <type>real</type>, <type>double</type>,
- and <type>numeric</type> are converted to
- Python <type>float</type>. Note that for
- the <type>numeric</type> this loses information and can lead to
- incorrect results. This might be fixed in a future
- release.
+ PostgreSQL <type>real</type> and <type>double</type> are converted to
+ Python <type>float</type>.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ PostgreSQL <type>numeric</type> is converted to
+ Python <type>Decimal</type>. This type is imported from
+ the <literal>cdecimal</literal> package if that is available.
+ Otherwise,
+ <literal>decimal.Decimal</literal> from the standard library will be
+ used. <literal>cdecimal</literal> is significantly faster
+ than <literal>decimal</literal>. In Python 3.3 and up,
+ however, <literal>cdecimal</literal> has been integrated into the
+ standard library under the name <literal>decimal</literal>, so there is
+ no longer any difference.
</para>
</listitem>
</para>
<para>
- Function return values are converted to the declared PostgreSQL
- return data type as follows:
+ When a PL/Python function returns, its return value is converted to the
+ function's declared PostgreSQL return data type as follows:
+
<itemizedlist>
<listitem>
<para>
<para>
When the PostgreSQL return type is <type>bytea</type>, the
return value will be converted to a string (Python 2) or bytes
- (Python 3) using the respective Python builtins, with the
- result being converted <type>bytea</type>.
+ (Python 3) using the respective Python built-ins, with the
+ result being converted to <type>bytea</type>.
</para>
</listitem>
<listitem>
<para>
- For all other PostgreSQL return types, the returned Python
- value is converted to a string using the Python
- builtin <literal>str</literal>, and the result is passed to the
- input function of the PostgreSQL data type.
+ For all other PostgreSQL return types, the return value is converted
+ to a string using the Python built-in <literal>str</literal>, and the
+ result is passed to the input function of the PostgreSQL data type.
+ (If the Python value is a <type>float</>, it is converted using
+ the <literal>repr</> built-in instead of <literal>str</literal>, to
+ avoid loss of precision.)
</para>
<para>
return type and the Python data type of the actual return object
are not flagged; the value will be converted in any case.
</para>
-
- <tip>
- <para>
- <application>PL/Python</application> functions cannot return
- either type <type>RECORD</type> or <type>SETOF RECORD</type>. A
- workaround is to write a <application>PL/pgSQL</application>
- function that creates a temporary table, have it call the
- <application>PL/Python</application> function to fill the table,
- and then have the <application>PL/pgSQL</application> function
- return the generic <type>RECORD</type> from the temporary table.
- </para>
- </tip>
</sect2>
<sect2>
<title>Null, None</title>
<para>
If an SQL null value<indexterm><primary>null value</primary><secondary
- sortas="PL/Python">PL/Python</secondary></indexterm> is passed to a
+ sortas="PL/Python">in PL/Python</secondary></indexterm> is passed to a
function, the argument value will appear as <symbol>None</symbol> in
- Python. The above function definition will return the wrong answer for null
+ Python. For example, the function definition of <function>pymax</function>
+ shown in <xref linkend="plpython-funcs"> will return the wrong answer for null
inputs. We could add <literal>STRICT</literal> to the function definition
to make <productname>PostgreSQL</productname> do something more reasonable:
if a null value is passed, the function will not be called at all,
<para>
SQL array values are passed into PL/Python as a Python list. To
return an SQL array value out of a PL/Python function, return a
- Python sequence, for example a list or tuple:
+ Python list:
<programlisting>
CREATE FUNCTION return_arr()
RETURNS int[]
AS $$
-return (1, 2, 3, 4, 5)
+return [1, 2, 3, 4, 5]
$$ LANGUAGE plpythonu;
SELECT return_arr();
(1 row)
</programlisting>
+ Multidimensional arrays are passed into PL/Python as nested Python lists.
+ A 2-dimensional array is a list of lists, for example. When returning
+ a multi-dimensional SQL array out of a PL/Python function, the inner
+ lists at each level must all be of the same size. For example:
+
+<programlisting>
+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[[1,2,3],[4,5,6]]);
+INFO: ([[1, 2, 3], [4, 5, 6]], <type 'list'>)
+ test_type_conversion_array_int4
+---------------------------------
+ {{1,2,3},{4,5,6}}
+(1 row)
+</programlisting>
+
+ Other Python sequences, like tuples, are also accepted for
+ backwards-compatibility with PostgreSQL versions 9.6 and below, when
+ multi-dimensional arrays were not supported. However, they are always
+ treated as one-dimensional arrays, because they are ambiguous with
+ composite types. For the same reason, when a composite type is used in a
+ multi-dimensional array, it must be represented by a tuple, rather than a
+ list.
+ </para>
+ <para>
Note that in Python, strings are sequences, which can have
undesirable effects that might be familiar to Python programmers:
CREATE FUNCTION make_pair (name text, value integer)
RETURNS named_value
AS $$
- return [ name, value ]
- # or alternatively, as tuple: return ( name, value )
+ return ( name, value )
+ # or alternatively, as tuple: return [ name, value ]
$$ LANGUAGE plpythonu;
</programlisting>
To return a SQL null for any column, insert <symbol>None</symbol> at
the corresponding position.
</para>
+ <para>
+ When an array of composite types is returned, it cannot be returned as a list,
+ because it is ambiguous whether the Python list represents a composite type,
+ or another array dimension.
+ </para>
</listitem>
</varlistentry>
</varlistentry>
</variablelist>
</para>
+
+ <para>
+ Functions with <literal>OUT</literal> parameters are also supported. For example:
+<programlisting>
+CREATE FUNCTION multiout_simple(OUT i integer, OUT j integer) AS $$
+return (1, 2)
+$$ LANGUAGE plpythonu;
+
+SELECT * FROM multiout_simple();
+</programlisting>
+ </para>
</sect2>
<sect2>
- <title>Set-Returning Functions</title>
+ <title>Set-returning Functions</title>
<para>
A <application>PL/Python</application> function can also return sets of
scalar or composite types. There are several ways to achieve this because
$$ LANGUAGE plpythonu;
</programlisting>
- <warning>
- <para>
- Due to Python
- <ulink url="http://bugs.python.org/issue1483133">bug #1483133</ulink>,
- some debug versions of Python 2.4
- (configured and compiled with option <literal>--with-pydebug</literal>)
- are known to crash the <productname>PostgreSQL</productname> server
- when using an iterator to return a set result.
- Unpatched versions of Fedora 4 contain this bug.
- It does not happen in production versions of Python or on patched
- versions of Fedora 4.
- </para>
- </warning>
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
+
+ <para>
+ Set-returning functions with <literal>OUT</literal> parameters
+ (using <literal>RETURNS SETOF record</literal>) are also
+ supported. For example:
+<programlisting>
+CREATE FUNCTION multiout_simple_setof(n integer, OUT integer, OUT integer) RETURNS SETOF record AS $$
+return [(1, 2)] * n
+$$ LANGUAGE plpythonu;
+
+SELECT * FROM multiout_simple_setof(3);
+</programlisting>
+ </para>
</sect2>
</sect1>
data between function calls. This variable is private static data.
The global dictionary <varname>GD</varname> is public data,
available to all Python functions within a session. Use with
- care.<indexterm><primary>global data</><secondary>in
- PL/Python</></indexterm>
+ care.<indexterm><primary>global data</>
+ <secondary>in PL/Python</></indexterm>
</para>
<para>
<literal>plpy.<replaceable>foo</replaceable></literal>.
</para>
+ <sect2>
+ <title>Database Access Functions</title>
+
<para>
- The <literal>plpy</literal> 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
- to be returned in a result object. The result object emulates a
- list or dictionary object. The result object can be accessed by
- row number and column 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_execute()</function> return value. The result object
- can be modified.
+ The <literal>plpy</literal> module provides several functions to execute
+ database commands:
</para>
- <para>
- For example:
+ <variablelist>
+ <varlistentry>
+ <term><literal>plpy.<function>execute</function>(<replaceable>query</replaceable> [, <replaceable>max-rows</replaceable>])</literal></term>
+ <listitem>
+ <para>
+ Calling <function>plpy.execute</function> with a query string and an
+ optional row limit argument causes that query to be run and the result to
+ be returned in a result object.
+ </para>
+
+ <para>
+ The result object emulates a list or dictionary object. The result
+ object can be accessed by row number and column name. For example:
<programlisting>
rv = plpy.execute("SELECT * FROM my_table", 5)
</programlisting>
- returns up to 5 rows from <literal>my_table</literal>. If
- <literal>my_table</literal> has a column
- <literal>my_column</literal>, it would be accessed as:
+ returns up to 5 rows from <literal>my_table</literal>. If
+ <literal>my_table</literal> has a column
+ <literal>my_column</literal>, it would be accessed as:
<programlisting>
foo = rv[i]["my_column"]
</programlisting>
- </para>
+ The number of rows returned can be obtained using the built-in
+ <function>len</function> function.
+ </para>
- <para>
- <indexterm><primary>preparing a query</><secondary>in PL/Python</></indexterm>
- The second function, <function>plpy.prepare</function>, prepares
- the execution plan for a query. It is called with a query string
- and a list of parameter types, if you have parameter references in
- the query. For example:
+ <para>
+ The result object has these additional methods:
+ <variablelist>
+ <varlistentry>
+ <term><literal><function>nrows</function>()</literal></term>
+ <listitem>
+ <para>
+ Returns the number of rows processed by the command. Note that this
+ is not necessarily the same as the number of rows returned. For
+ example, an <command>UPDATE</command> command will set this value but
+ won't return any rows (unless <literal>RETURNING</literal> is used).
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal><function>status</function>()</literal></term>
+ <listitem>
+ <para>
+ The <function>SPI_execute()</function> return value.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal><function>colnames</function>()</literal></term>
+ <term><literal><function>coltypes</function>()</literal></term>
+ <term><literal><function>coltypmods</function>()</literal></term>
+ <listitem>
+ <para>
+ Return a list of column names, list of column type OIDs, and list of
+ type-specific type modifiers for the columns, respectively.
+ </para>
+
+ <para>
+ These methods raise an exception when called on a result object from
+ a command that did not produce a result set, e.g.,
+ <command>UPDATE</command> without <literal>RETURNING</literal>, or
+ <command>DROP TABLE</command>. But it is OK to use these methods on
+ a result set containing zero rows.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal><function>__str__</function>()</literal></term>
+ <listitem>
+ <para>
+ The standard <literal>__str__</literal> method is defined so that it
+ is possible for example to debug query execution results
+ using <literal>plpy.debug(rv)</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ <para>
+ The result object can be modified.
+ </para>
+
+ <para>
+ Note that calling <literal>plpy.execute</literal> will cause the entire
+ result set to be read into memory. Only use that function when you are
+ sure that the result set will be relatively small. If you don't want to
+ risk excessive memory usage when fetching large results,
+ use <literal>plpy.cursor</literal> rather
+ than <literal>plpy.execute</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>plpy.<function>prepare</function>(<replaceable>query</replaceable> [, <replaceable>argtypes</replaceable>])</literal></term>
+ <term><literal>plpy.<function>execute</function>(<replaceable>plan</replaceable> [, <replaceable>arguments</replaceable> [, <replaceable>max-rows</replaceable>]])</literal></term>
+ <listitem>
+ <para>
+ <indexterm><primary>preparing a query</><secondary>in PL/Python</></indexterm>
+ <function>plpy.prepare</function> prepares the execution plan for a
+ query. It is called with a query string and a list of parameter types,
+ if you have parameter references in the query. For example:
<programlisting>
-plan = plpy.prepare("SELECT last_name FROM my_users WHERE first_name = $1", [ "text" ])
+plan = plpy.prepare("SELECT last_name FROM my_users WHERE first_name = $1", ["text"])
</programlisting>
- <literal>text</literal> is the type of the variable you will be
- passing for <literal>$1</literal>. After preparing a statement, you
- use the function <function>plpy.execute</function> to run it:
+ <literal>text</literal> is the type of the variable you will be passing
+ for <literal>$1</literal>. The second argument is optional if you don't
+ want to pass any parameters to the query.
+ </para>
+ <para>
+ After preparing a statement, you use a variant of the
+ function <function>plpy.execute</function> to run it:
<programlisting>
-rv = plpy.execute(plan, [ "name" ], 5)
+rv = plpy.execute(plan, ["name"], 5)
</programlisting>
- The third argument is the limit and is optional.
- </para>
+ Pass the plan as the first argument (instead of the query string), and a
+ list of values to substitute into the query as the second argument. The
+ second argument is optional if the query does not expect any parameters.
+ The third argument is the optional row limit as before.
+ </para>
- <para>
- Query parameters and result row fields are converted between
- PostgreSQL and Python data types as described
- in <xref linkend="plpython-data">. The exception is that composite
- types are currently not supported: They will be rejected as query
- parameters and are converted to strings when appearing in a query
- result. As a workaround for the latter problem, the query can
- sometimes be rewritten so that the composite type result appears as
- a result row rather than as a field of the result row.
- Alternatively, the resulting string could be parsed apart by hand,
- but this approach is not recommended because it is not
- future-proof.
- </para>
+ <para>
+ Alternatively, you can call the <function>execute</function> method on
+ the plan object:
+<programlisting>
+rv = plan.execute(["name"], 5)
+</programlisting>
+ </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.
- In order to make effective use of this across function calls
- one needs to use one of the persistent storage dictionaries
- <literal>SD</literal> or <literal>GD</literal> (see
- <xref linkend="plpython-sharing">). For example:
+ <para>
+ Query parameters and result row fields are converted between PostgreSQL
+ and Python data types as described in <xref linkend="plpython-data">.
+ </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. In order to make effective use of this
+ across function calls one needs to use one of the persistent storage
+ dictionaries <literal>SD</literal> or <literal>GD</literal> (see
+ <xref linkend="plpython-sharing">). For example:
<programlisting>
CREATE FUNCTION usesavedplan() RETURNS trigger AS $$
- if SD.has_key("plan"):
+ if "plan" in SD:
plan = SD["plan"]
else:
plan = plpy.prepare("SELECT 1")
# rest of function
$$ LANGUAGE plpythonu;
</programlisting>
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>plpy.<function>cursor</function>(<replaceable>query</replaceable>)</literal></term>
+ <term><literal>plpy.<function>cursor</function>(<replaceable>plan</replaceable> [, <replaceable>arguments</replaceable>])</literal></term>
+ <listitem>
+ <para>
+ The <literal>plpy.cursor</literal> function accepts the same arguments
+ as <literal>plpy.execute</literal> (except for the row limit) and returns
+ a cursor object, which allows you to process large result sets in smaller
+ chunks. As with <literal>plpy.execute</literal>, either a query string
+ or a plan object along with a list of arguments can be used, or
+ the <function>cursor</function> function can be called as a method of
+ the plan object.
+ </para>
+
+ <para>
+ The cursor object provides a <literal>fetch</literal> method that accepts
+ an integer parameter and returns a result object. Each time you
+ call <literal>fetch</literal>, the returned object will contain the next
+ batch of rows, never larger than the parameter value. Once all rows are
+ exhausted, <literal>fetch</literal> starts returning an empty result
+ object. Cursor objects also provide an
+ <ulink url="http://docs.python.org/library/stdtypes.html#iterator-types">iterator
+ interface</ulink>, yielding one row at a time until all rows are
+ exhausted. Data fetched that way is not returned as result objects, but
+ rather as dictionaries, each dictionary corresponding to a single result
+ row.
+ </para>
+
+ <para>
+ An example of two ways of processing data from a large table is:
+<programlisting>
+CREATE FUNCTION count_odd_iterator() RETURNS integer AS $$
+odd = 0
+for row in plpy.cursor("select num from largetable"):
+ if row['num'] % 2:
+ odd += 1
+return odd
+$$ LANGUAGE plpythonu;
+
+CREATE FUNCTION count_odd_fetch(batch_size integer) RETURNS integer AS $$
+odd = 0
+cursor = plpy.cursor("select num from largetable")
+while True:
+ rows = cursor.fetch(batch_size)
+ if not rows:
+ break
+ for row in rows:
+ if row['num'] % 2:
+ odd += 1
+return odd
+$$ LANGUAGE plpythonu;
+
+CREATE FUNCTION count_odd_prepared() RETURNS integer AS $$
+odd = 0
+plan = plpy.prepare("select num from largetable where num % $1 <> 0", ["integer"])
+rows = list(plpy.cursor(plan, [2])) # or: = list(plan.cursor([2]))
+
+return len(rows)
+$$ LANGUAGE plpythonu;
+</programlisting>
+ </para>
+
+ <para>
+ Cursors are automatically disposed of. But if you want to explicitly
+ release all resources held by a cursor, use the <literal>close</literal>
+ method. Once closed, a cursor cannot be fetched from anymore.
+ </para>
+
+ <tip>
+ <para>
+ Do not confuse objects created by <literal>plpy.cursor</literal> with
+ DB-API cursors as defined by
+ the <ulink url="http://www.python.org/dev/peps/pep-0249/">Python
+ Database API specification</ulink>. They don't have anything in common
+ except for the name.
+ </para>
+ </tip>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ </sect2>
+
+ <sect2 id="plpython-trapping">
+ <title>Trapping Errors</title>
+
+ <para>
+ Functions accessing the database might encounter errors, which
+ will cause them to abort and raise an exception. Both
+ <function>plpy.execute</function> and
+ <function>plpy.prepare</function> can raise an instance of a subclass of
+ <literal>plpy.SPIError</literal>, which by default will terminate
+ the function. This error can be handled just like any other
+ Python exception, by using the <literal>try/except</literal>
+ construct. For example:
+<programlisting>
+CREATE FUNCTION try_adding_joe() RETURNS text AS $$
+ try:
+ plpy.execute("INSERT INTO users(username) VALUES ('joe')")
+ except plpy.SPIError:
+ return "something went wrong"
+ else:
+ return "Joe added"
+$$ LANGUAGE plpythonu;
+</programlisting>
+ </para>
+
+ <para>
+ The actual class of the exception being raised corresponds to the
+ specific condition that caused the error. Refer
+ to <xref linkend="errcodes-table"> for a list of possible
+ conditions. The module
+ <literal>plpy.spiexceptions</literal> defines an exception class
+ for each <productname>PostgreSQL</productname> condition, deriving
+ their names from the condition name. For
+ instance, <literal>division_by_zero</literal>
+ becomes <literal>DivisionByZero</literal>, <literal>unique_violation</literal>
+ becomes <literal>UniqueViolation</literal>, <literal>fdw_error</literal>
+ becomes <literal>FdwError</literal>, and so on. Each of these
+ exception classes inherits from <literal>SPIError</literal>. This
+ separation makes it easier to handle specific errors, for
+ instance:
+<programlisting>
+CREATE FUNCTION insert_fraction(numerator int, denominator int) RETURNS text AS $$
+from plpy import spiexceptions
+try:
+ plan = plpy.prepare("INSERT INTO fractions (frac) VALUES ($1 / $2)", ["int", "int"])
+ plpy.execute(plan, [numerator, denominator])
+except spiexceptions.DivisionByZero:
+ return "denominator cannot equal zero"
+except spiexceptions.UniqueViolation:
+ return "already have that fraction"
+except plpy.SPIError, e:
+ return "other error, SQLSTATE %s" % e.sqlstate
+else:
+ return "fraction inserted"
+$$ LANGUAGE plpythonu;
+</programlisting>
+ Note that because all exceptions from
+ the <literal>plpy.spiexceptions</literal> module inherit
+ from <literal>SPIError</literal>, an <literal>except</literal>
+ clause handling it will catch any database access error.
+ </para>
+
+ <para>
+ As an alternative way of handling different error conditions, you
+ can catch the <literal>SPIError</literal> exception and determine
+ the specific error condition inside the <literal>except</literal>
+ block by looking at the <literal>sqlstate</literal> attribute of
+ the exception object. This attribute is a string value containing
+ the <quote>SQLSTATE</quote> error code. This approach provides
+ approximately the same functionality
+ </para>
+ </sect2>
+ </sect1>
+
+ <sect1 id="plpython-subtransaction">
+ <title>Explicit Subtransactions</title>
+
+ <para>
+ Recovering from errors caused by database access as described in
+ <xref linkend="plpython-trapping"> can lead to an undesirable
+ situation where some operations succeed before one of them fails,
+ and after recovering from that error the data is left in an
+ inconsistent state. PL/Python offers a solution to this problem in
+ the form of explicit subtransactions.
</para>
+
+ <sect2>
+ <title>Subtransaction Context Managers</title>
+
+ <para>
+ Consider a function that implements a transfer between two
+ accounts:
+<programlisting>
+CREATE FUNCTION transfer_funds() RETURNS void AS $$
+try:
+ plpy.execute("UPDATE accounts SET balance = balance - 100 WHERE account_name = 'joe'")
+ plpy.execute("UPDATE accounts SET balance = balance + 100 WHERE account_name = 'mary'")
+except plpy.SPIError, e:
+ result = "error transferring funds: %s" % e.args
+else:
+ result = "funds transferred correctly"
+plan = plpy.prepare("INSERT INTO operations (result) VALUES ($1)", ["text"])
+plpy.execute(plan, [result])
+$$ LANGUAGE plpythonu;
+</programlisting>
+ If the second <literal>UPDATE</literal> statement results in an
+ exception being raised, this function will report the error, but
+ the result of the first <literal>UPDATE</literal> will
+ nevertheless be committed. In other words, the funds will be
+ withdrawn from Joe's account, but will not be transferred to
+ Mary's account.
+ </para>
+
+ <para>
+ To avoid such issues, you can wrap your
+ <literal>plpy.execute</literal> calls in an explicit
+ subtransaction. The <literal>plpy</literal> module provides a
+ helper object to manage explicit subtransactions that gets created
+ with the <literal>plpy.subtransaction()</literal> function.
+ Objects created by this function implement the
+ <ulink url="http://docs.python.org/library/stdtypes.html#context-manager-types">
+ context manager interface</ulink>. Using explicit subtransactions
+ we can rewrite our function as:
+<programlisting>
+CREATE FUNCTION transfer_funds2() RETURNS void AS $$
+try:
+ with plpy.subtransaction():
+ plpy.execute("UPDATE accounts SET balance = balance - 100 WHERE account_name = 'joe'")
+ plpy.execute("UPDATE accounts SET balance = balance + 100 WHERE account_name = 'mary'")
+except plpy.SPIError, e:
+ result = "error transferring funds: %s" % e.args
+else:
+ result = "funds transferred correctly"
+plan = plpy.prepare("INSERT INTO operations (result) VALUES ($1)", ["text"])
+plpy.execute(plan, [result])
+$$ LANGUAGE plpythonu;
+</programlisting>
+ Note that the use of <literal>try/catch</literal> is still
+ required. Otherwise the exception would propagate to the top of
+ the Python stack and would cause the whole function to abort with
+ a <productname>PostgreSQL</productname> error, so that the
+ <literal>operations</literal> table would not have any row
+ inserted into it. The subtransaction context manager does not
+ trap errors, it only assures that all database operations executed
+ inside its scope will be atomically committed or rolled back. A
+ rollback of the subtransaction block occurs on any kind of
+ exception exit, not only ones caused by errors originating from
+ database access. A regular Python exception raised inside an
+ explicit subtransaction block would also cause the subtransaction
+ to be rolled back.
+ </para>
+ </sect2>
+
+ <sect2>
+ <title>Older Python Versions</title>
+
+ <para>
+ Context managers syntax using the <literal>with</literal> keyword
+ is available by default in Python 2.6. If using PL/Python with an
+ older Python version, it is still possible to use explicit
+ subtransactions, although not as transparently. You can call the
+ subtransaction manager's <literal>__enter__</literal> and
+ <literal>__exit__</literal> functions using the
+ <literal>enter</literal> and <literal>exit</literal> convenience
+ aliases. The example function that transfers funds could be
+ written as:
+<programlisting>
+CREATE FUNCTION transfer_funds_old() RETURNS void AS $$
+try:
+ subxact = plpy.subtransaction()
+ subxact.enter()
+ try:
+ plpy.execute("UPDATE accounts SET balance = balance - 100 WHERE account_name = 'joe'")
+ plpy.execute("UPDATE accounts SET balance = balance + 100 WHERE account_name = 'mary'")
+ except:
+ import sys
+ subxact.exit(*sys.exc_info())
+ raise
+ else:
+ subxact.exit(None, None, None)
+except plpy.SPIError, e:
+ result = "error transferring funds: %s" % e.args
+else:
+ result = "funds transferred correctly"
+
+plan = plpy.prepare("INSERT INTO operations (result) VALUES ($1)", ["text"])
+plpy.execute(plan, [result])
+$$ LANGUAGE plpythonu;
+</programlisting>
+ </para>
+
+ <note>
+ <para>
+ Although context managers were implemented in Python 2.5, to use
+ the <literal>with</literal> syntax in that version you need to
+ use a <ulink
+ url="http://docs.python.org/release/2.5/ref/future.html">future
+ statement</ulink>. Because of implementation details, however,
+ you cannot use future statements in PL/Python functions.
+ </para>
+ </note>
+ </sect2>
</sect1>
<sect1 id="plpython-util">
<title>Utility Functions</title>
<para>
The <literal>plpy</literal> module also provides the functions
- <literal>plpy.debug(<replaceable>msg</>)</literal>,
- <literal>plpy.log(<replaceable>msg</>)</literal>,
- <literal>plpy.info(<replaceable>msg</>)</literal>,
- <literal>plpy.notice(<replaceable>msg</>)</literal>,
- <literal>plpy.warning(<replaceable>msg</>)</literal>,
- <literal>plpy.error(<replaceable>msg</>)</literal>, and
- <literal>plpy.fatal(<replaceable>msg</>)</literal>.<indexterm><primary>elog</><secondary>in PL/Python</></indexterm>
- <function>plpy.error</function> and
- <function>plpy.fatal</function> actually raise a Python exception
- which, if uncaught, propagates out to the calling query, causing
- the current transaction or subtransaction to be aborted.
- <literal>raise plpy.Error(<replaceable>msg</>)</literal> and
+ <simplelist>
+ <member><literal>plpy.debug(<replaceable>msg, **kwargs</>)</literal></member>
+ <member><literal>plpy.log(<replaceable>msg, **kwargs</>)</literal></member>
+ <member><literal>plpy.info(<replaceable>msg, **kwargs</>)</literal></member>
+ <member><literal>plpy.notice(<replaceable>msg, **kwargs</>)</literal></member>
+ <member><literal>plpy.warning(<replaceable>msg, **kwargs</>)</literal></member>
+ <member><literal>plpy.error(<replaceable>msg, **kwargs</>)</literal></member>
+ <member><literal>plpy.fatal(<replaceable>msg, **kwargs</>)</literal></member>
+ </simplelist>
+ <indexterm><primary>elog</><secondary>in PL/Python</></indexterm>
+ <function>plpy.error</function> and <function>plpy.fatal</function>
+ actually raise a Python exception which, if uncaught, propagates out to
+ the calling query, causing the current transaction or subtransaction to
+ be aborted. <literal>raise plpy.Error(<replaceable>msg</>)</literal> and
<literal>raise plpy.Fatal(<replaceable>msg</>)</literal> are
- equivalent to calling
- <function>plpy.error</function> and
- <function>plpy.fatal</function>, respectively.
- The other functions only generate messages of different
- priority levels.
+ equivalent to calling <literal>plpy.error(<replaceable>msg</>)</literal> and
+ <literal>plpy.fatal(<replaceable>msg</>)</literal>, respectively but
+ the <literal>raise</literal> form does not allow passing keyword arguments.
+ The other functions only generate messages of different priority levels.
Whether messages of a particular priority are reported to the client,
written to the server log, or both is controlled by the
<xref linkend="guc-log-min-messages"> and
<xref linkend="guc-client-min-messages"> configuration
variables. See <xref linkend="runtime-config"> for more information.
</para>
+
+ <para>
+ The <replaceable>msg</> argument is given as a positional argument. For
+ backward compatibility, more than one positional argument can be given. In
+ that case, the string representation of the tuple of positional arguments
+ becomes the message reported to the client.
+ </para>
+
+ <para>
+ The following keyword-only arguments are accepted:
+ <simplelist>
+ <member><literal>detail</literal></member>
+ <member><literal>hint</literal></member>
+ <member><literal>sqlstate</literal></member>
+ <member><literal>schema_name</literal></member>
+ <member><literal>table_name</literal></member>
+ <member><literal>column_name</literal></member>
+ <member><literal>datatype_name</literal></member>
+ <member><literal>constraint_name</literal></member>
+ </simplelist>
+ The string representation of the objects passed as keyword-only arguments
+ is used to enrich the messages reported to the client. For example:
+
+<programlisting>
+CREATE FUNCTION raise_custom_exception() RETURNS void AS $$
+plpy.error("custom exception message",
+ detail="some info about exception",
+ hint="hint for users")
+$$ LANGUAGE plpythonu;
+
+=# SELECT raise_custom_exception();
+ERROR: plpy.Error: custom exception message
+DETAIL: some info about exception
+HINT: hint for users
+CONTEXT: Traceback (most recent call last):
+ PL/Python function "raise_custom_exception", line 4, in <module>
+ hint="hint for users")
+PL/Python function "raise_custom_exception"
+</programlisting>
+ </para>
+
+ <para>
+ Another set of utility functions are
+ <literal>plpy.quote_literal(<replaceable>string</>)</literal>,
+ <literal>plpy.quote_nullable(<replaceable>string</>)</literal>, and
+ <literal>plpy.quote_ident(<replaceable>string</>)</literal>. They
+ are equivalent to the built-in quoting functions described in <xref
+ linkend="functions-string">. They are useful when constructing
+ ad-hoc queries. A PL/Python equivalent of dynamic SQL from <xref
+ linkend="plpgsql-quote-literal-example"> would be:
+<programlisting>
+plpy.execute("UPDATE tbl SET %s = %s WHERE key = %s" % (
+ plpy.quote_ident(colname),
+ plpy.quote_nullable(newvalue),
+ plpy.quote_literal(keyvalue)))
+</programlisting>
+ </para>
</sect1>
<sect1 id="plpython-envar">
<listitem>
<para><envar>PYTHONUSERBASE</envar></para>
</listitem>
+
+ <listitem>
+ <para><envar>PYTHONHASHSEED</envar></para>
+ </listitem>
</itemizedlist>
(It appears to be a Python implementation detail beyond the control