Home Explore Blog CI



postgresql

12th chunk of `doc/src/sgml/plpython.sgml`
dffd7b006c7b2bbb78bfca8ee8919f3cc469f02bf17f040c0000000100000fa2
 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="https://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 plpython3u;
</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 as e:
    return "other error, SQLSTATE %s" % e.sqlstate
else:
    return "fraction inserted"
$$ LANGUAGE plpython3u;
</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

Title: Trapping Errors and Explicit Subtransactions in PL/Python
Summary
PL/Python functions can encounter errors, which can be trapped using try/except constructs, with specific exception classes defined in the plpy.spiexceptions module, and can be handled based on the SQLSTATE error code, while also introducing explicit subtransactions to recover from errors and maintain data consistency.