Remove support for Python older than 2.6
Supporting very old Python versions is a maintenance burden, especially with the several variant test files to maintain for Python <2.6. Since we have dropped support for older OpenSSL versions in 7b283d0e1d1d79bf1c962d790c94d2a53f3bb38a, RHEL 5 is now effectively desupported, and that was also the only mainstream operating system still using Python versions before 2.6, so it's a good time to drop those as well. Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us> Discussion: https://www.postgresql.org/message-id/flat/98b69261-298c-13d2-f34d-836fd9c29b21%402ndquadrant.com
This commit is contained in:
parent
f5d28710c7
commit
37f21ed132
@ -37,8 +37,8 @@ python_majorversion=`echo "$python_fullversion" | sed '[s/^\([0-9]*\).*/\1/]'`
|
|||||||
python_minorversion=`echo "$python_fullversion" | sed '[s/^[0-9]*\.\([0-9]*\).*/\1/]'`
|
python_minorversion=`echo "$python_fullversion" | sed '[s/^[0-9]*\.\([0-9]*\).*/\1/]'`
|
||||||
python_version=`echo "$python_fullversion" | sed '[s/^\([0-9]*\.[0-9]*\).*/\1/]'`
|
python_version=`echo "$python_fullversion" | sed '[s/^\([0-9]*\.[0-9]*\).*/\1/]'`
|
||||||
# Reject unsupported Python versions as soon as practical.
|
# Reject unsupported Python versions as soon as practical.
|
||||||
if test "$python_majorversion" -lt 3 -a "$python_minorversion" -lt 4; then
|
if test "$python_majorversion" -lt 3 -a "$python_minorversion" -lt 6; then
|
||||||
AC_MSG_ERROR([Python version $python_version is too old (version 2.4 or later is required)])
|
AC_MSG_ERROR([Python version $python_version is too old (version 2.6 or later is required)])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
AC_MSG_CHECKING([for Python distutils module])
|
AC_MSG_CHECKING([for Python distutils module])
|
||||||
|
4
configure
vendored
4
configure
vendored
@ -9616,8 +9616,8 @@ python_majorversion=`echo "$python_fullversion" | sed 's/^\([0-9]*\).*/\1/'`
|
|||||||
python_minorversion=`echo "$python_fullversion" | sed 's/^[0-9]*\.\([0-9]*\).*/\1/'`
|
python_minorversion=`echo "$python_fullversion" | sed 's/^[0-9]*\.\([0-9]*\).*/\1/'`
|
||||||
python_version=`echo "$python_fullversion" | sed 's/^\([0-9]*\.[0-9]*\).*/\1/'`
|
python_version=`echo "$python_fullversion" | sed 's/^\([0-9]*\.[0-9]*\).*/\1/'`
|
||||||
# Reject unsupported Python versions as soon as practical.
|
# Reject unsupported Python versions as soon as practical.
|
||||||
if test "$python_majorversion" -lt 3 -a "$python_minorversion" -lt 4; then
|
if test "$python_majorversion" -lt 3 -a "$python_minorversion" -lt 6; then
|
||||||
as_fn_error $? "Python version $python_version is too old (version 2.4 or later is required)" "$LINENO" 5
|
as_fn_error $? "Python version $python_version is too old (version 2.6 or later is required)" "$LINENO" 5
|
||||||
fi
|
fi
|
||||||
|
|
||||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for Python distutils module" >&5
|
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for Python distutils module" >&5
|
||||||
|
@ -196,7 +196,7 @@ su - postgres
|
|||||||
language, you need a <productname>Python</productname>
|
language, you need a <productname>Python</productname>
|
||||||
installation with the header files and
|
installation with the header files and
|
||||||
the <application>distutils</application> module. The minimum
|
the <application>distutils</application> module. The minimum
|
||||||
required version is <productname>Python</productname> 2.4.
|
required version is <productname>Python</productname> 2.6.
|
||||||
<productname>Python 3</productname> is supported if it's
|
<productname>Python 3</productname> is supported if it's
|
||||||
version 3.1 or later; but see
|
version 3.1 or later; but see
|
||||||
<xref linkend="plpython-python23"/>
|
<xref linkend="plpython-python23"/>
|
||||||
|
@ -1335,9 +1335,8 @@ $$ LANGUAGE plpythonu;
|
|||||||
|
|
||||||
<para>
|
<para>
|
||||||
Context managers syntax using the <literal>with</literal> keyword
|
Context managers syntax using the <literal>with</literal> keyword
|
||||||
is available by default in Python 2.6. If using PL/Python with an
|
is available by default in Python 2.6. For compatibility with
|
||||||
older Python version, it is still possible to use explicit
|
older Python versions, you can call the
|
||||||
subtransactions, although not as transparently. You can call the
|
|
||||||
subtransaction manager's <literal>__enter__</literal> and
|
subtransaction manager's <literal>__enter__</literal> and
|
||||||
<literal>__exit__</literal> functions using the
|
<literal>__exit__</literal> functions using the
|
||||||
<literal>enter</literal> and <literal>exit</literal> convenience
|
<literal>enter</literal> and <literal>exit</literal> convenience
|
||||||
@ -1367,17 +1366,6 @@ plpy.execute(plan, [result])
|
|||||||
$$ LANGUAGE plpythonu;
|
$$ LANGUAGE plpythonu;
|
||||||
</programlisting>
|
</programlisting>
|
||||||
</para>
|
</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="https://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>
|
</sect2>
|
||||||
</sect1>
|
</sect1>
|
||||||
|
|
||||||
|
@ -1,12 +1,8 @@
|
|||||||
Guide to alternative expected files:
|
Guide to alternative expected files:
|
||||||
|
|
||||||
plpython_error_0.out Python 2.4 and older
|
|
||||||
plpython_error_5.out Python 3.5 and newer
|
plpython_error_5.out Python 3.5 and newer
|
||||||
|
|
||||||
plpython_unicode.out server encoding != SQL_ASCII
|
plpython_unicode.out server encoding != SQL_ASCII
|
||||||
plpython_unicode_3.out server encoding == SQL_ASCII
|
plpython_unicode_3.out server encoding == SQL_ASCII
|
||||||
|
|
||||||
plpython_subtransaction_0.out Python 2.4 and older (without with statement)
|
|
||||||
plpython_subtransaction_5.out Python 2.5 (without with statement)
|
|
||||||
|
|
||||||
plpython_types_3.out Python 3.x
|
plpython_types_3.out Python 3.x
|
||||||
|
@ -1,447 +0,0 @@
|
|||||||
-- test error handling, i forgot to restore Warn_restart in
|
|
||||||
-- the trigger handler once. the errors and subsequent core dump were
|
|
||||||
-- interesting.
|
|
||||||
/* Flat out Python syntax error
|
|
||||||
*/
|
|
||||||
CREATE FUNCTION python_syntax_error() RETURNS text
|
|
||||||
AS
|
|
||||||
'.syntaxerror'
|
|
||||||
LANGUAGE plpythonu;
|
|
||||||
ERROR: could not compile PL/Python function "python_syntax_error"
|
|
||||||
DETAIL: SyntaxError: invalid syntax (line 2)
|
|
||||||
/* With check_function_bodies = false the function should get defined
|
|
||||||
* and the error reported when called
|
|
||||||
*/
|
|
||||||
SET check_function_bodies = false;
|
|
||||||
CREATE FUNCTION python_syntax_error() RETURNS text
|
|
||||||
AS
|
|
||||||
'.syntaxerror'
|
|
||||||
LANGUAGE plpythonu;
|
|
||||||
SELECT python_syntax_error();
|
|
||||||
ERROR: could not compile PL/Python function "python_syntax_error"
|
|
||||||
DETAIL: SyntaxError: invalid syntax (line 2)
|
|
||||||
/* Run the function twice to check if the hashtable entry gets cleaned up */
|
|
||||||
SELECT python_syntax_error();
|
|
||||||
ERROR: could not compile PL/Python function "python_syntax_error"
|
|
||||||
DETAIL: SyntaxError: invalid syntax (line 2)
|
|
||||||
RESET check_function_bodies;
|
|
||||||
/* Flat out syntax error
|
|
||||||
*/
|
|
||||||
CREATE FUNCTION sql_syntax_error() RETURNS text
|
|
||||||
AS
|
|
||||||
'plpy.execute("syntax error")'
|
|
||||||
LANGUAGE plpythonu;
|
|
||||||
SELECT sql_syntax_error();
|
|
||||||
ERROR: spiexceptions.SyntaxError: syntax error at or near "syntax"
|
|
||||||
LINE 1: syntax error
|
|
||||||
^
|
|
||||||
QUERY: syntax error
|
|
||||||
CONTEXT: Traceback (most recent call last):
|
|
||||||
PL/Python function "sql_syntax_error", line 1, in <module>
|
|
||||||
plpy.execute("syntax error")
|
|
||||||
PL/Python function "sql_syntax_error"
|
|
||||||
/* check the handling of uncaught python exceptions
|
|
||||||
*/
|
|
||||||
CREATE FUNCTION exception_index_invalid(text) RETURNS text
|
|
||||||
AS
|
|
||||||
'return args[1]'
|
|
||||||
LANGUAGE plpythonu;
|
|
||||||
SELECT exception_index_invalid('test');
|
|
||||||
ERROR: IndexError: list index out of range
|
|
||||||
CONTEXT: Traceback (most recent call last):
|
|
||||||
PL/Python function "exception_index_invalid", line 1, in <module>
|
|
||||||
return args[1]
|
|
||||||
PL/Python function "exception_index_invalid"
|
|
||||||
/* check handling of nested exceptions
|
|
||||||
*/
|
|
||||||
CREATE FUNCTION exception_index_invalid_nested() RETURNS text
|
|
||||||
AS
|
|
||||||
'rv = plpy.execute("SELECT test5(''foo'')")
|
|
||||||
return rv[0]'
|
|
||||||
LANGUAGE plpythonu;
|
|
||||||
SELECT exception_index_invalid_nested();
|
|
||||||
ERROR: spiexceptions.UndefinedFunction: function test5(unknown) does not exist
|
|
||||||
LINE 1: SELECT test5('foo')
|
|
||||||
^
|
|
||||||
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
|
||||||
QUERY: SELECT test5('foo')
|
|
||||||
CONTEXT: Traceback (most recent call last):
|
|
||||||
PL/Python function "exception_index_invalid_nested", line 1, in <module>
|
|
||||||
rv = plpy.execute("SELECT test5('foo')")
|
|
||||||
PL/Python function "exception_index_invalid_nested"
|
|
||||||
/* a typo
|
|
||||||
*/
|
|
||||||
CREATE FUNCTION invalid_type_uncaught(a text) RETURNS text
|
|
||||||
AS
|
|
||||||
'if "plan" not in SD:
|
|
||||||
q = "SELECT fname FROM users WHERE lname = $1"
|
|
||||||
SD["plan"] = plpy.prepare(q, [ "test" ])
|
|
||||||
rv = plpy.execute(SD["plan"], [ a ])
|
|
||||||
if len(rv):
|
|
||||||
return rv[0]["fname"]
|
|
||||||
return None
|
|
||||||
'
|
|
||||||
LANGUAGE plpythonu;
|
|
||||||
SELECT invalid_type_uncaught('rick');
|
|
||||||
ERROR: spiexceptions.UndefinedObject: type "test" does not exist
|
|
||||||
CONTEXT: Traceback (most recent call last):
|
|
||||||
PL/Python function "invalid_type_uncaught", line 3, in <module>
|
|
||||||
SD["plan"] = plpy.prepare(q, [ "test" ])
|
|
||||||
PL/Python function "invalid_type_uncaught"
|
|
||||||
/* for what it's worth catch the exception generated by
|
|
||||||
* the typo, and return None
|
|
||||||
*/
|
|
||||||
CREATE FUNCTION invalid_type_caught(a text) RETURNS text
|
|
||||||
AS
|
|
||||||
'if "plan" not in SD:
|
|
||||||
q = "SELECT fname FROM users WHERE lname = $1"
|
|
||||||
try:
|
|
||||||
SD["plan"] = plpy.prepare(q, [ "test" ])
|
|
||||||
except plpy.SPIError, ex:
|
|
||||||
plpy.notice(str(ex))
|
|
||||||
return None
|
|
||||||
rv = plpy.execute(SD["plan"], [ a ])
|
|
||||||
if len(rv):
|
|
||||||
return rv[0]["fname"]
|
|
||||||
return None
|
|
||||||
'
|
|
||||||
LANGUAGE plpythonu;
|
|
||||||
SELECT invalid_type_caught('rick');
|
|
||||||
NOTICE: type "test" does not exist
|
|
||||||
invalid_type_caught
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
/* for what it's worth catch the exception generated by
|
|
||||||
* the typo, and reraise it as a plain error
|
|
||||||
*/
|
|
||||||
CREATE FUNCTION invalid_type_reraised(a text) RETURNS text
|
|
||||||
AS
|
|
||||||
'if "plan" not in SD:
|
|
||||||
q = "SELECT fname FROM users WHERE lname = $1"
|
|
||||||
try:
|
|
||||||
SD["plan"] = plpy.prepare(q, [ "test" ])
|
|
||||||
except plpy.SPIError, ex:
|
|
||||||
plpy.error(str(ex))
|
|
||||||
rv = plpy.execute(SD["plan"], [ a ])
|
|
||||||
if len(rv):
|
|
||||||
return rv[0]["fname"]
|
|
||||||
return None
|
|
||||||
'
|
|
||||||
LANGUAGE plpythonu;
|
|
||||||
SELECT invalid_type_reraised('rick');
|
|
||||||
ERROR: plpy.Error: type "test" does not exist
|
|
||||||
CONTEXT: Traceback (most recent call last):
|
|
||||||
PL/Python function "invalid_type_reraised", line 6, in <module>
|
|
||||||
plpy.error(str(ex))
|
|
||||||
PL/Python function "invalid_type_reraised"
|
|
||||||
/* no typo no messing about
|
|
||||||
*/
|
|
||||||
CREATE FUNCTION valid_type(a text) RETURNS text
|
|
||||||
AS
|
|
||||||
'if "plan" not in SD:
|
|
||||||
SD["plan"] = plpy.prepare("SELECT fname FROM users WHERE lname = $1", [ "text" ])
|
|
||||||
rv = plpy.execute(SD["plan"], [ a ])
|
|
||||||
if len(rv):
|
|
||||||
return rv[0]["fname"]
|
|
||||||
return None
|
|
||||||
'
|
|
||||||
LANGUAGE plpythonu;
|
|
||||||
SELECT valid_type('rick');
|
|
||||||
valid_type
|
|
||||||
------------
|
|
||||||
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
/* error in nested functions to get a traceback
|
|
||||||
*/
|
|
||||||
CREATE FUNCTION nested_error() RETURNS text
|
|
||||||
AS
|
|
||||||
'def fun1():
|
|
||||||
plpy.error("boom")
|
|
||||||
|
|
||||||
def fun2():
|
|
||||||
fun1()
|
|
||||||
|
|
||||||
def fun3():
|
|
||||||
fun2()
|
|
||||||
|
|
||||||
fun3()
|
|
||||||
return "not reached"
|
|
||||||
'
|
|
||||||
LANGUAGE plpythonu;
|
|
||||||
SELECT nested_error();
|
|
||||||
ERROR: plpy.Error: boom
|
|
||||||
CONTEXT: Traceback (most recent call last):
|
|
||||||
PL/Python function "nested_error", line 10, in <module>
|
|
||||||
fun3()
|
|
||||||
PL/Python function "nested_error", line 8, in fun3
|
|
||||||
fun2()
|
|
||||||
PL/Python function "nested_error", line 5, in fun2
|
|
||||||
fun1()
|
|
||||||
PL/Python function "nested_error", line 2, in fun1
|
|
||||||
plpy.error("boom")
|
|
||||||
PL/Python function "nested_error"
|
|
||||||
/* raising plpy.Error is just like calling plpy.error
|
|
||||||
*/
|
|
||||||
CREATE FUNCTION nested_error_raise() RETURNS text
|
|
||||||
AS
|
|
||||||
'def fun1():
|
|
||||||
raise plpy.Error("boom")
|
|
||||||
|
|
||||||
def fun2():
|
|
||||||
fun1()
|
|
||||||
|
|
||||||
def fun3():
|
|
||||||
fun2()
|
|
||||||
|
|
||||||
fun3()
|
|
||||||
return "not reached"
|
|
||||||
'
|
|
||||||
LANGUAGE plpythonu;
|
|
||||||
SELECT nested_error_raise();
|
|
||||||
ERROR: plpy.Error: boom
|
|
||||||
CONTEXT: Traceback (most recent call last):
|
|
||||||
PL/Python function "nested_error_raise", line 10, in <module>
|
|
||||||
fun3()
|
|
||||||
PL/Python function "nested_error_raise", line 8, in fun3
|
|
||||||
fun2()
|
|
||||||
PL/Python function "nested_error_raise", line 5, in fun2
|
|
||||||
fun1()
|
|
||||||
PL/Python function "nested_error_raise", line 2, in fun1
|
|
||||||
raise plpy.Error("boom")
|
|
||||||
PL/Python function "nested_error_raise"
|
|
||||||
/* using plpy.warning should not produce a traceback
|
|
||||||
*/
|
|
||||||
CREATE FUNCTION nested_warning() RETURNS text
|
|
||||||
AS
|
|
||||||
'def fun1():
|
|
||||||
plpy.warning("boom")
|
|
||||||
|
|
||||||
def fun2():
|
|
||||||
fun1()
|
|
||||||
|
|
||||||
def fun3():
|
|
||||||
fun2()
|
|
||||||
|
|
||||||
fun3()
|
|
||||||
return "you''ve been warned"
|
|
||||||
'
|
|
||||||
LANGUAGE plpythonu;
|
|
||||||
SELECT nested_warning();
|
|
||||||
WARNING: boom
|
|
||||||
nested_warning
|
|
||||||
--------------------
|
|
||||||
you've been warned
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
/* AttributeError at toplevel used to give segfaults with the traceback
|
|
||||||
*/
|
|
||||||
CREATE FUNCTION toplevel_attribute_error() RETURNS void AS
|
|
||||||
$$
|
|
||||||
plpy.nonexistent
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
SELECT toplevel_attribute_error();
|
|
||||||
ERROR: AttributeError: 'module' object has no attribute 'nonexistent'
|
|
||||||
CONTEXT: Traceback (most recent call last):
|
|
||||||
PL/Python function "toplevel_attribute_error", line 2, in <module>
|
|
||||||
plpy.nonexistent
|
|
||||||
PL/Python function "toplevel_attribute_error"
|
|
||||||
/* Calling PL/Python functions from SQL and vice versa should not lose context.
|
|
||||||
*/
|
|
||||||
CREATE OR REPLACE FUNCTION python_traceback() RETURNS void AS $$
|
|
||||||
def first():
|
|
||||||
second()
|
|
||||||
|
|
||||||
def second():
|
|
||||||
third()
|
|
||||||
|
|
||||||
def third():
|
|
||||||
plpy.execute("select sql_error()")
|
|
||||||
|
|
||||||
first()
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
CREATE OR REPLACE FUNCTION sql_error() RETURNS void AS $$
|
|
||||||
begin
|
|
||||||
select 1/0;
|
|
||||||
end
|
|
||||||
$$ LANGUAGE plpgsql;
|
|
||||||
CREATE OR REPLACE FUNCTION python_from_sql_error() RETURNS void AS $$
|
|
||||||
begin
|
|
||||||
select python_traceback();
|
|
||||||
end
|
|
||||||
$$ LANGUAGE plpgsql;
|
|
||||||
CREATE OR REPLACE FUNCTION sql_from_python_error() RETURNS void AS $$
|
|
||||||
plpy.execute("select sql_error()")
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
SELECT python_traceback();
|
|
||||||
ERROR: spiexceptions.DivisionByZero: division by zero
|
|
||||||
CONTEXT: Traceback (most recent call last):
|
|
||||||
PL/Python function "python_traceback", line 11, in <module>
|
|
||||||
first()
|
|
||||||
PL/Python function "python_traceback", line 3, in first
|
|
||||||
second()
|
|
||||||
PL/Python function "python_traceback", line 6, in second
|
|
||||||
third()
|
|
||||||
PL/Python function "python_traceback", line 9, in third
|
|
||||||
plpy.execute("select sql_error()")
|
|
||||||
PL/Python function "python_traceback"
|
|
||||||
SELECT sql_error();
|
|
||||||
ERROR: division by zero
|
|
||||||
CONTEXT: SQL statement "select 1/0"
|
|
||||||
PL/pgSQL function sql_error() line 3 at SQL statement
|
|
||||||
SELECT python_from_sql_error();
|
|
||||||
ERROR: spiexceptions.DivisionByZero: division by zero
|
|
||||||
CONTEXT: Traceback (most recent call last):
|
|
||||||
PL/Python function "python_traceback", line 11, in <module>
|
|
||||||
first()
|
|
||||||
PL/Python function "python_traceback", line 3, in first
|
|
||||||
second()
|
|
||||||
PL/Python function "python_traceback", line 6, in second
|
|
||||||
third()
|
|
||||||
PL/Python function "python_traceback", line 9, in third
|
|
||||||
plpy.execute("select sql_error()")
|
|
||||||
PL/Python function "python_traceback"
|
|
||||||
SQL statement "select python_traceback()"
|
|
||||||
PL/pgSQL function python_from_sql_error() line 3 at SQL statement
|
|
||||||
SELECT sql_from_python_error();
|
|
||||||
ERROR: spiexceptions.DivisionByZero: division by zero
|
|
||||||
CONTEXT: Traceback (most recent call last):
|
|
||||||
PL/Python function "sql_from_python_error", line 2, in <module>
|
|
||||||
plpy.execute("select sql_error()")
|
|
||||||
PL/Python function "sql_from_python_error"
|
|
||||||
/* check catching specific types of exceptions
|
|
||||||
*/
|
|
||||||
CREATE TABLE specific (
|
|
||||||
i integer PRIMARY KEY
|
|
||||||
);
|
|
||||||
CREATE FUNCTION specific_exception(i integer) RETURNS void AS
|
|
||||||
$$
|
|
||||||
from plpy import spiexceptions
|
|
||||||
try:
|
|
||||||
plpy.execute("insert into specific values (%s)" % (i or "NULL"));
|
|
||||||
except spiexceptions.NotNullViolation, e:
|
|
||||||
plpy.notice("Violated the NOT NULL constraint, sqlstate %s" % e.sqlstate)
|
|
||||||
except spiexceptions.UniqueViolation, e:
|
|
||||||
plpy.notice("Violated the UNIQUE constraint, sqlstate %s" % e.sqlstate)
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
SELECT specific_exception(2);
|
|
||||||
specific_exception
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT specific_exception(NULL);
|
|
||||||
NOTICE: Violated the NOT NULL constraint, sqlstate 23502
|
|
||||||
specific_exception
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT specific_exception(2);
|
|
||||||
NOTICE: Violated the UNIQUE constraint, sqlstate 23505
|
|
||||||
specific_exception
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
/* SPI errors in PL/Python functions should preserve the SQLSTATE value
|
|
||||||
*/
|
|
||||||
CREATE FUNCTION python_unique_violation() RETURNS void AS $$
|
|
||||||
plpy.execute("insert into specific values (1)")
|
|
||||||
plpy.execute("insert into specific values (1)")
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
CREATE FUNCTION catch_python_unique_violation() RETURNS text AS $$
|
|
||||||
begin
|
|
||||||
begin
|
|
||||||
perform python_unique_violation();
|
|
||||||
exception when unique_violation then
|
|
||||||
return 'ok';
|
|
||||||
end;
|
|
||||||
return 'not reached';
|
|
||||||
end;
|
|
||||||
$$ language plpgsql;
|
|
||||||
SELECT catch_python_unique_violation();
|
|
||||||
catch_python_unique_violation
|
|
||||||
-------------------------------
|
|
||||||
ok
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
/* manually starting subtransactions - a bad idea
|
|
||||||
*/
|
|
||||||
CREATE FUNCTION manual_subxact() RETURNS void AS $$
|
|
||||||
plpy.execute("savepoint save")
|
|
||||||
plpy.execute("create table foo(x integer)")
|
|
||||||
plpy.execute("rollback to save")
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
SELECT manual_subxact();
|
|
||||||
ERROR: plpy.SPIError: SPI_execute failed: SPI_ERROR_TRANSACTION
|
|
||||||
CONTEXT: Traceback (most recent call last):
|
|
||||||
PL/Python function "manual_subxact", line 2, in <module>
|
|
||||||
plpy.execute("savepoint save")
|
|
||||||
PL/Python function "manual_subxact"
|
|
||||||
/* same for prepared plans
|
|
||||||
*/
|
|
||||||
CREATE FUNCTION manual_subxact_prepared() RETURNS void AS $$
|
|
||||||
save = plpy.prepare("savepoint save")
|
|
||||||
rollback = plpy.prepare("rollback to save")
|
|
||||||
plpy.execute(save)
|
|
||||||
plpy.execute("create table foo(x integer)")
|
|
||||||
plpy.execute(rollback)
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
SELECT manual_subxact_prepared();
|
|
||||||
ERROR: plpy.SPIError: SPI_execute_plan failed: SPI_ERROR_TRANSACTION
|
|
||||||
CONTEXT: Traceback (most recent call last):
|
|
||||||
PL/Python function "manual_subxact_prepared", line 4, in <module>
|
|
||||||
plpy.execute(save)
|
|
||||||
PL/Python function "manual_subxact_prepared"
|
|
||||||
/* raising plpy.spiexception.* from python code should preserve sqlstate
|
|
||||||
*/
|
|
||||||
CREATE FUNCTION plpy_raise_spiexception() RETURNS void AS $$
|
|
||||||
raise plpy.spiexceptions.DivisionByZero()
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
DO $$
|
|
||||||
BEGIN
|
|
||||||
SELECT plpy_raise_spiexception();
|
|
||||||
EXCEPTION WHEN division_by_zero THEN
|
|
||||||
-- NOOP
|
|
||||||
END
|
|
||||||
$$ LANGUAGE plpgsql;
|
|
||||||
/* setting a custom sqlstate should be handled
|
|
||||||
*/
|
|
||||||
CREATE FUNCTION plpy_raise_spiexception_override() RETURNS void AS $$
|
|
||||||
exc = plpy.spiexceptions.DivisionByZero()
|
|
||||||
exc.sqlstate = 'SILLY'
|
|
||||||
raise exc
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
DO $$
|
|
||||||
BEGIN
|
|
||||||
SELECT plpy_raise_spiexception_override();
|
|
||||||
EXCEPTION WHEN SQLSTATE 'SILLY' THEN
|
|
||||||
-- NOOP
|
|
||||||
END
|
|
||||||
$$ LANGUAGE plpgsql;
|
|
||||||
/* test the context stack trace for nested execution levels
|
|
||||||
*/
|
|
||||||
CREATE FUNCTION notice_innerfunc() RETURNS int AS $$
|
|
||||||
plpy.execute("DO LANGUAGE plpythonu $x$ plpy.notice('inside DO') $x$")
|
|
||||||
return 1
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
CREATE FUNCTION notice_outerfunc() RETURNS int AS $$
|
|
||||||
plpy.execute("SELECT notice_innerfunc()")
|
|
||||||
return 1
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
\set SHOW_CONTEXT always
|
|
||||||
SELECT notice_outerfunc();
|
|
||||||
NOTICE: inside DO
|
|
||||||
CONTEXT: PL/Python anonymous code block
|
|
||||||
SQL statement "DO LANGUAGE plpythonu $x$ plpy.notice('inside DO') $x$"
|
|
||||||
PL/Python function "notice_innerfunc"
|
|
||||||
SQL statement "SELECT notice_innerfunc()"
|
|
||||||
PL/Python function "notice_outerfunc"
|
|
||||||
notice_outerfunc
|
|
||||||
------------------
|
|
||||||
1
|
|
||||||
(1 row)
|
|
||||||
|
|
@ -5,71 +5,6 @@
|
|||||||
CREATE TABLE subtransaction_tbl (
|
CREATE TABLE subtransaction_tbl (
|
||||||
i integer
|
i integer
|
||||||
);
|
);
|
||||||
-- Explicit case for Python <2.6
|
|
||||||
CREATE FUNCTION subtransaction_test(what_error text = NULL) RETURNS text
|
|
||||||
AS $$
|
|
||||||
import sys
|
|
||||||
subxact = plpy.subtransaction()
|
|
||||||
subxact.__enter__()
|
|
||||||
exc = True
|
|
||||||
try:
|
|
||||||
try:
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES (1)")
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES (2)")
|
|
||||||
if what_error == "SPI":
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES ('oops')")
|
|
||||||
elif what_error == "Python":
|
|
||||||
raise Exception("Python exception")
|
|
||||||
except:
|
|
||||||
exc = False
|
|
||||||
subxact.__exit__(*sys.exc_info())
|
|
||||||
raise
|
|
||||||
finally:
|
|
||||||
if exc:
|
|
||||||
subxact.__exit__(None, None, None)
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
SELECT subtransaction_test();
|
|
||||||
subtransaction_test
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT * FROM subtransaction_tbl;
|
|
||||||
i
|
|
||||||
---
|
|
||||||
1
|
|
||||||
2
|
|
||||||
(2 rows)
|
|
||||||
|
|
||||||
TRUNCATE subtransaction_tbl;
|
|
||||||
SELECT subtransaction_test('SPI');
|
|
||||||
ERROR: spiexceptions.InvalidTextRepresentation: invalid input syntax for type integer: "oops"
|
|
||||||
LINE 1: INSERT INTO subtransaction_tbl VALUES ('oops')
|
|
||||||
^
|
|
||||||
QUERY: INSERT INTO subtransaction_tbl VALUES ('oops')
|
|
||||||
CONTEXT: Traceback (most recent call last):
|
|
||||||
PL/Python function "subtransaction_test", line 11, in <module>
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES ('oops')")
|
|
||||||
PL/Python function "subtransaction_test"
|
|
||||||
SELECT * FROM subtransaction_tbl;
|
|
||||||
i
|
|
||||||
---
|
|
||||||
(0 rows)
|
|
||||||
|
|
||||||
TRUNCATE subtransaction_tbl;
|
|
||||||
SELECT subtransaction_test('Python');
|
|
||||||
ERROR: Exception: Python exception
|
|
||||||
CONTEXT: Traceback (most recent call last):
|
|
||||||
PL/Python function "subtransaction_test", line 13, in <module>
|
|
||||||
raise Exception("Python exception")
|
|
||||||
PL/Python function "subtransaction_test"
|
|
||||||
SELECT * FROM subtransaction_tbl;
|
|
||||||
i
|
|
||||||
---
|
|
||||||
(0 rows)
|
|
||||||
|
|
||||||
TRUNCATE subtransaction_tbl;
|
|
||||||
-- Context manager case for Python >=2.6
|
|
||||||
CREATE FUNCTION subtransaction_ctx_test(what_error text = NULL) RETURNS text
|
CREATE FUNCTION subtransaction_ctx_test(what_error text = NULL) RETURNS text
|
||||||
AS $$
|
AS $$
|
||||||
with plpy.subtransaction():
|
with plpy.subtransaction():
|
||||||
|
@ -1,448 +0,0 @@
|
|||||||
--
|
|
||||||
-- Test explicit subtransactions
|
|
||||||
--
|
|
||||||
-- Test table to see if transactions get properly rolled back
|
|
||||||
CREATE TABLE subtransaction_tbl (
|
|
||||||
i integer
|
|
||||||
);
|
|
||||||
-- Explicit case for Python <2.6
|
|
||||||
CREATE FUNCTION subtransaction_test(what_error text = NULL) RETURNS text
|
|
||||||
AS $$
|
|
||||||
import sys
|
|
||||||
subxact = plpy.subtransaction()
|
|
||||||
subxact.__enter__()
|
|
||||||
exc = True
|
|
||||||
try:
|
|
||||||
try:
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES (1)")
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES (2)")
|
|
||||||
if what_error == "SPI":
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES ('oops')")
|
|
||||||
elif what_error == "Python":
|
|
||||||
raise Exception("Python exception")
|
|
||||||
except:
|
|
||||||
exc = False
|
|
||||||
subxact.__exit__(*sys.exc_info())
|
|
||||||
raise
|
|
||||||
finally:
|
|
||||||
if exc:
|
|
||||||
subxact.__exit__(None, None, None)
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
SELECT subtransaction_test();
|
|
||||||
subtransaction_test
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT * FROM subtransaction_tbl;
|
|
||||||
i
|
|
||||||
---
|
|
||||||
1
|
|
||||||
2
|
|
||||||
(2 rows)
|
|
||||||
|
|
||||||
TRUNCATE subtransaction_tbl;
|
|
||||||
SELECT subtransaction_test('SPI');
|
|
||||||
ERROR: spiexceptions.InvalidTextRepresentation: invalid input syntax for type integer: "oops"
|
|
||||||
LINE 1: INSERT INTO subtransaction_tbl VALUES ('oops')
|
|
||||||
^
|
|
||||||
QUERY: INSERT INTO subtransaction_tbl VALUES ('oops')
|
|
||||||
CONTEXT: Traceback (most recent call last):
|
|
||||||
PL/Python function "subtransaction_test", line 11, in <module>
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES ('oops')")
|
|
||||||
PL/Python function "subtransaction_test"
|
|
||||||
SELECT * FROM subtransaction_tbl;
|
|
||||||
i
|
|
||||||
---
|
|
||||||
(0 rows)
|
|
||||||
|
|
||||||
TRUNCATE subtransaction_tbl;
|
|
||||||
SELECT subtransaction_test('Python');
|
|
||||||
ERROR: Exception: Python exception
|
|
||||||
CONTEXT: Traceback (most recent call last):
|
|
||||||
PL/Python function "subtransaction_test", line 13, in <module>
|
|
||||||
raise Exception("Python exception")
|
|
||||||
PL/Python function "subtransaction_test"
|
|
||||||
SELECT * FROM subtransaction_tbl;
|
|
||||||
i
|
|
||||||
---
|
|
||||||
(0 rows)
|
|
||||||
|
|
||||||
TRUNCATE subtransaction_tbl;
|
|
||||||
-- Context manager case for Python >=2.6
|
|
||||||
CREATE FUNCTION subtransaction_ctx_test(what_error text = NULL) RETURNS text
|
|
||||||
AS $$
|
|
||||||
with plpy.subtransaction():
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES (1)")
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES (2)")
|
|
||||||
if what_error == "SPI":
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES ('oops')")
|
|
||||||
elif what_error == "Python":
|
|
||||||
raise Exception("Python exception")
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
ERROR: could not compile PL/Python function "subtransaction_ctx_test"
|
|
||||||
DETAIL: SyntaxError: invalid syntax (line 3)
|
|
||||||
SELECT subtransaction_ctx_test();
|
|
||||||
ERROR: function subtransaction_ctx_test() does not exist
|
|
||||||
LINE 1: SELECT subtransaction_ctx_test();
|
|
||||||
^
|
|
||||||
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
|
||||||
SELECT * FROM subtransaction_tbl;
|
|
||||||
i
|
|
||||||
---
|
|
||||||
(0 rows)
|
|
||||||
|
|
||||||
TRUNCATE subtransaction_tbl;
|
|
||||||
SELECT subtransaction_ctx_test('SPI');
|
|
||||||
ERROR: function subtransaction_ctx_test(unknown) does not exist
|
|
||||||
LINE 1: SELECT subtransaction_ctx_test('SPI');
|
|
||||||
^
|
|
||||||
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
|
||||||
SELECT * FROM subtransaction_tbl;
|
|
||||||
i
|
|
||||||
---
|
|
||||||
(0 rows)
|
|
||||||
|
|
||||||
TRUNCATE subtransaction_tbl;
|
|
||||||
SELECT subtransaction_ctx_test('Python');
|
|
||||||
ERROR: function subtransaction_ctx_test(unknown) does not exist
|
|
||||||
LINE 1: SELECT subtransaction_ctx_test('Python');
|
|
||||||
^
|
|
||||||
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
|
||||||
SELECT * FROM subtransaction_tbl;
|
|
||||||
i
|
|
||||||
---
|
|
||||||
(0 rows)
|
|
||||||
|
|
||||||
TRUNCATE subtransaction_tbl;
|
|
||||||
-- Nested subtransactions
|
|
||||||
CREATE FUNCTION subtransaction_nested_test(swallow boolean = 'f') RETURNS text
|
|
||||||
AS $$
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES (1)")
|
|
||||||
with plpy.subtransaction():
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES (2)")
|
|
||||||
try:
|
|
||||||
with plpy.subtransaction():
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES (3)")
|
|
||||||
plpy.execute("error")
|
|
||||||
except plpy.SPIError, e:
|
|
||||||
if not swallow:
|
|
||||||
raise
|
|
||||||
plpy.notice("Swallowed %s(%r)" % (e.__class__.__name__, e.args[0]))
|
|
||||||
return "ok"
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
ERROR: could not compile PL/Python function "subtransaction_nested_test"
|
|
||||||
DETAIL: SyntaxError: invalid syntax (line 4)
|
|
||||||
SELECT subtransaction_nested_test();
|
|
||||||
ERROR: function subtransaction_nested_test() does not exist
|
|
||||||
LINE 1: SELECT subtransaction_nested_test();
|
|
||||||
^
|
|
||||||
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
|
||||||
SELECT * FROM subtransaction_tbl;
|
|
||||||
i
|
|
||||||
---
|
|
||||||
(0 rows)
|
|
||||||
|
|
||||||
TRUNCATE subtransaction_tbl;
|
|
||||||
SELECT subtransaction_nested_test('t');
|
|
||||||
ERROR: function subtransaction_nested_test(unknown) does not exist
|
|
||||||
LINE 1: SELECT subtransaction_nested_test('t');
|
|
||||||
^
|
|
||||||
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
|
||||||
SELECT * FROM subtransaction_tbl;
|
|
||||||
i
|
|
||||||
---
|
|
||||||
(0 rows)
|
|
||||||
|
|
||||||
TRUNCATE subtransaction_tbl;
|
|
||||||
-- Nested subtransactions that recursively call code dealing with
|
|
||||||
-- subtransactions
|
|
||||||
CREATE FUNCTION subtransaction_deeply_nested_test() RETURNS text
|
|
||||||
AS $$
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES (1)")
|
|
||||||
with plpy.subtransaction():
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES (2)")
|
|
||||||
plpy.execute("SELECT subtransaction_nested_test('t')")
|
|
||||||
return "ok"
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
ERROR: could not compile PL/Python function "subtransaction_deeply_nested_test"
|
|
||||||
DETAIL: SyntaxError: invalid syntax (line 4)
|
|
||||||
SELECT subtransaction_deeply_nested_test();
|
|
||||||
ERROR: function subtransaction_deeply_nested_test() does not exist
|
|
||||||
LINE 1: SELECT subtransaction_deeply_nested_test();
|
|
||||||
^
|
|
||||||
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
|
||||||
SELECT * FROM subtransaction_tbl;
|
|
||||||
i
|
|
||||||
---
|
|
||||||
(0 rows)
|
|
||||||
|
|
||||||
TRUNCATE subtransaction_tbl;
|
|
||||||
-- Error conditions from not opening/closing subtransactions
|
|
||||||
CREATE FUNCTION subtransaction_exit_without_enter() RETURNS void
|
|
||||||
AS $$
|
|
||||||
plpy.subtransaction().__exit__(None, None, None)
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
CREATE FUNCTION subtransaction_enter_without_exit() RETURNS void
|
|
||||||
AS $$
|
|
||||||
plpy.subtransaction().__enter__()
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
CREATE FUNCTION subtransaction_exit_twice() RETURNS void
|
|
||||||
AS $$
|
|
||||||
plpy.subtransaction().__enter__()
|
|
||||||
plpy.subtransaction().__exit__(None, None, None)
|
|
||||||
plpy.subtransaction().__exit__(None, None, None)
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
CREATE FUNCTION subtransaction_enter_twice() RETURNS void
|
|
||||||
AS $$
|
|
||||||
plpy.subtransaction().__enter__()
|
|
||||||
plpy.subtransaction().__enter__()
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
CREATE FUNCTION subtransaction_exit_same_subtransaction_twice() RETURNS void
|
|
||||||
AS $$
|
|
||||||
s = plpy.subtransaction()
|
|
||||||
s.__enter__()
|
|
||||||
s.__exit__(None, None, None)
|
|
||||||
s.__exit__(None, None, None)
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
CREATE FUNCTION subtransaction_enter_same_subtransaction_twice() RETURNS void
|
|
||||||
AS $$
|
|
||||||
s = plpy.subtransaction()
|
|
||||||
s.__enter__()
|
|
||||||
s.__enter__()
|
|
||||||
s.__exit__(None, None, None)
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
-- No warnings here, as the subtransaction gets indeed closed
|
|
||||||
CREATE FUNCTION subtransaction_enter_subtransaction_in_with() RETURNS void
|
|
||||||
AS $$
|
|
||||||
with plpy.subtransaction() as s:
|
|
||||||
s.__enter__()
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
ERROR: could not compile PL/Python function "subtransaction_enter_subtransaction_in_with"
|
|
||||||
DETAIL: SyntaxError: invalid syntax (line 3)
|
|
||||||
CREATE FUNCTION subtransaction_exit_subtransaction_in_with() RETURNS void
|
|
||||||
AS $$
|
|
||||||
with plpy.subtransaction() as s:
|
|
||||||
s.__exit__(None, None, None)
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
ERROR: could not compile PL/Python function "subtransaction_exit_subtransaction_in_with"
|
|
||||||
DETAIL: SyntaxError: invalid syntax (line 3)
|
|
||||||
SELECT subtransaction_exit_without_enter();
|
|
||||||
ERROR: ValueError: this subtransaction has not been entered
|
|
||||||
CONTEXT: Traceback (most recent call last):
|
|
||||||
PL/Python function "subtransaction_exit_without_enter", line 2, in <module>
|
|
||||||
plpy.subtransaction().__exit__(None, None, None)
|
|
||||||
PL/Python function "subtransaction_exit_without_enter"
|
|
||||||
SELECT subtransaction_enter_without_exit();
|
|
||||||
WARNING: forcibly aborting a subtransaction that has not been exited
|
|
||||||
subtransaction_enter_without_exit
|
|
||||||
-----------------------------------
|
|
||||||
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT subtransaction_exit_twice();
|
|
||||||
WARNING: forcibly aborting a subtransaction that has not been exited
|
|
||||||
ERROR: ValueError: this subtransaction has not been entered
|
|
||||||
CONTEXT: Traceback (most recent call last):
|
|
||||||
PL/Python function "subtransaction_exit_twice", line 3, in <module>
|
|
||||||
plpy.subtransaction().__exit__(None, None, None)
|
|
||||||
PL/Python function "subtransaction_exit_twice"
|
|
||||||
SELECT subtransaction_enter_twice();
|
|
||||||
WARNING: forcibly aborting a subtransaction that has not been exited
|
|
||||||
WARNING: forcibly aborting a subtransaction that has not been exited
|
|
||||||
subtransaction_enter_twice
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT subtransaction_exit_same_subtransaction_twice();
|
|
||||||
ERROR: ValueError: this subtransaction has already been exited
|
|
||||||
CONTEXT: Traceback (most recent call last):
|
|
||||||
PL/Python function "subtransaction_exit_same_subtransaction_twice", line 5, in <module>
|
|
||||||
s.__exit__(None, None, None)
|
|
||||||
PL/Python function "subtransaction_exit_same_subtransaction_twice"
|
|
||||||
SELECT subtransaction_enter_same_subtransaction_twice();
|
|
||||||
WARNING: forcibly aborting a subtransaction that has not been exited
|
|
||||||
ERROR: ValueError: this subtransaction has already been entered
|
|
||||||
CONTEXT: Traceback (most recent call last):
|
|
||||||
PL/Python function "subtransaction_enter_same_subtransaction_twice", line 4, in <module>
|
|
||||||
s.__enter__()
|
|
||||||
PL/Python function "subtransaction_enter_same_subtransaction_twice"
|
|
||||||
SELECT subtransaction_enter_subtransaction_in_with();
|
|
||||||
ERROR: function subtransaction_enter_subtransaction_in_with() does not exist
|
|
||||||
LINE 1: SELECT subtransaction_enter_subtransaction_in_with();
|
|
||||||
^
|
|
||||||
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
|
||||||
SELECT subtransaction_exit_subtransaction_in_with();
|
|
||||||
ERROR: function subtransaction_exit_subtransaction_in_with() does not exist
|
|
||||||
LINE 1: SELECT subtransaction_exit_subtransaction_in_with();
|
|
||||||
^
|
|
||||||
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
|
||||||
-- Make sure we don't get a "current transaction is aborted" error
|
|
||||||
SELECT 1 as test;
|
|
||||||
test
|
|
||||||
------
|
|
||||||
1
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
-- Mix explicit subtransactions and normal SPI calls
|
|
||||||
CREATE FUNCTION subtransaction_mix_explicit_and_implicit() RETURNS void
|
|
||||||
AS $$
|
|
||||||
p = plpy.prepare("INSERT INTO subtransaction_tbl VALUES ($1)", ["integer"])
|
|
||||||
try:
|
|
||||||
with plpy.subtransaction():
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES (1)")
|
|
||||||
plpy.execute(p, [2])
|
|
||||||
plpy.execute(p, ["wrong"])
|
|
||||||
except plpy.SPIError:
|
|
||||||
plpy.warning("Caught a SPI error from an explicit subtransaction")
|
|
||||||
|
|
||||||
try:
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES (1)")
|
|
||||||
plpy.execute(p, [2])
|
|
||||||
plpy.execute(p, ["wrong"])
|
|
||||||
except plpy.SPIError:
|
|
||||||
plpy.warning("Caught a SPI error")
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
ERROR: could not compile PL/Python function "subtransaction_mix_explicit_and_implicit"
|
|
||||||
DETAIL: SyntaxError: invalid syntax (line 5)
|
|
||||||
SELECT subtransaction_mix_explicit_and_implicit();
|
|
||||||
ERROR: function subtransaction_mix_explicit_and_implicit() does not exist
|
|
||||||
LINE 1: SELECT subtransaction_mix_explicit_and_implicit();
|
|
||||||
^
|
|
||||||
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
|
||||||
SELECT * FROM subtransaction_tbl;
|
|
||||||
i
|
|
||||||
---
|
|
||||||
(0 rows)
|
|
||||||
|
|
||||||
TRUNCATE subtransaction_tbl;
|
|
||||||
-- Alternative method names for Python <2.6
|
|
||||||
CREATE FUNCTION subtransaction_alternative_names() RETURNS void
|
|
||||||
AS $$
|
|
||||||
s = plpy.subtransaction()
|
|
||||||
s.enter()
|
|
||||||
s.exit(None, None, None)
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
SELECT subtransaction_alternative_names();
|
|
||||||
subtransaction_alternative_names
|
|
||||||
----------------------------------
|
|
||||||
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
-- try/catch inside a subtransaction block
|
|
||||||
CREATE FUNCTION try_catch_inside_subtransaction() RETURNS void
|
|
||||||
AS $$
|
|
||||||
with plpy.subtransaction():
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES (1)")
|
|
||||||
try:
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES ('a')")
|
|
||||||
except plpy.SPIError:
|
|
||||||
plpy.notice("caught")
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
ERROR: could not compile PL/Python function "try_catch_inside_subtransaction"
|
|
||||||
DETAIL: SyntaxError: invalid syntax (line 3)
|
|
||||||
SELECT try_catch_inside_subtransaction();
|
|
||||||
ERROR: function try_catch_inside_subtransaction() does not exist
|
|
||||||
LINE 1: SELECT try_catch_inside_subtransaction();
|
|
||||||
^
|
|
||||||
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
|
||||||
SELECT * FROM subtransaction_tbl;
|
|
||||||
i
|
|
||||||
---
|
|
||||||
(0 rows)
|
|
||||||
|
|
||||||
TRUNCATE subtransaction_tbl;
|
|
||||||
ALTER TABLE subtransaction_tbl ADD PRIMARY KEY (i);
|
|
||||||
CREATE FUNCTION pk_violation_inside_subtransaction() RETURNS void
|
|
||||||
AS $$
|
|
||||||
with plpy.subtransaction():
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES (1)")
|
|
||||||
try:
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES (1)")
|
|
||||||
except plpy.SPIError:
|
|
||||||
plpy.notice("caught")
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
ERROR: could not compile PL/Python function "pk_violation_inside_subtransaction"
|
|
||||||
DETAIL: SyntaxError: invalid syntax (line 3)
|
|
||||||
SELECT pk_violation_inside_subtransaction();
|
|
||||||
ERROR: function pk_violation_inside_subtransaction() does not exist
|
|
||||||
LINE 1: SELECT pk_violation_inside_subtransaction();
|
|
||||||
^
|
|
||||||
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
|
||||||
SELECT * FROM subtransaction_tbl;
|
|
||||||
i
|
|
||||||
---
|
|
||||||
(0 rows)
|
|
||||||
|
|
||||||
DROP TABLE subtransaction_tbl;
|
|
||||||
-- cursor/subtransactions interactions
|
|
||||||
CREATE FUNCTION cursor_in_subxact() RETURNS int AS $$
|
|
||||||
with plpy.subtransaction():
|
|
||||||
cur = plpy.cursor("select * from generate_series(1, 20) as gen(i)")
|
|
||||||
cur.fetch(10)
|
|
||||||
fetched = cur.fetch(10);
|
|
||||||
return int(fetched[5]["i"])
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
ERROR: could not compile PL/Python function "cursor_in_subxact"
|
|
||||||
DETAIL: SyntaxError: invalid syntax (line 3)
|
|
||||||
CREATE FUNCTION cursor_aborted_subxact() RETURNS int AS $$
|
|
||||||
try:
|
|
||||||
with plpy.subtransaction():
|
|
||||||
cur = plpy.cursor("select * from generate_series(1, 20) as gen(i)")
|
|
||||||
cur.fetch(10);
|
|
||||||
plpy.execute("select no_such_function()")
|
|
||||||
except plpy.SPIError:
|
|
||||||
fetched = cur.fetch(10)
|
|
||||||
return int(fetched[5]["i"])
|
|
||||||
return 0 # not reached
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
ERROR: could not compile PL/Python function "cursor_aborted_subxact"
|
|
||||||
DETAIL: SyntaxError: invalid syntax (line 4)
|
|
||||||
CREATE FUNCTION cursor_plan_aborted_subxact() RETURNS int AS $$
|
|
||||||
try:
|
|
||||||
with plpy.subtransaction():
|
|
||||||
plpy.execute('create temporary table tmp(i) '
|
|
||||||
'as select generate_series(1, 10)')
|
|
||||||
plan = plpy.prepare("select i from tmp")
|
|
||||||
cur = plpy.cursor(plan)
|
|
||||||
plpy.execute("select no_such_function()")
|
|
||||||
except plpy.SPIError:
|
|
||||||
fetched = cur.fetch(5)
|
|
||||||
return fetched[2]["i"]
|
|
||||||
return 0 # not reached
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
ERROR: could not compile PL/Python function "cursor_plan_aborted_subxact"
|
|
||||||
DETAIL: SyntaxError: invalid syntax (line 4)
|
|
||||||
CREATE FUNCTION cursor_close_aborted_subxact() RETURNS boolean AS $$
|
|
||||||
try:
|
|
||||||
with plpy.subtransaction():
|
|
||||||
cur = plpy.cursor('select 1')
|
|
||||||
plpy.execute("select no_such_function()")
|
|
||||||
except plpy.SPIError:
|
|
||||||
cur.close()
|
|
||||||
return True
|
|
||||||
return False # not reached
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
ERROR: could not compile PL/Python function "cursor_close_aborted_subxact"
|
|
||||||
DETAIL: SyntaxError: invalid syntax (line 4)
|
|
||||||
SELECT cursor_in_subxact();
|
|
||||||
ERROR: function cursor_in_subxact() does not exist
|
|
||||||
LINE 1: SELECT cursor_in_subxact();
|
|
||||||
^
|
|
||||||
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
|
||||||
SELECT cursor_aborted_subxact();
|
|
||||||
ERROR: function cursor_aborted_subxact() does not exist
|
|
||||||
LINE 1: SELECT cursor_aborted_subxact();
|
|
||||||
^
|
|
||||||
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
|
||||||
SELECT cursor_plan_aborted_subxact();
|
|
||||||
ERROR: function cursor_plan_aborted_subxact() does not exist
|
|
||||||
LINE 1: SELECT cursor_plan_aborted_subxact();
|
|
||||||
^
|
|
||||||
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
|
||||||
SELECT cursor_close_aborted_subxact();
|
|
||||||
ERROR: function cursor_close_aborted_subxact() does not exist
|
|
||||||
LINE 1: SELECT cursor_close_aborted_subxact();
|
|
||||||
^
|
|
||||||
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
|
@ -1,448 +0,0 @@
|
|||||||
--
|
|
||||||
-- Test explicit subtransactions
|
|
||||||
--
|
|
||||||
-- Test table to see if transactions get properly rolled back
|
|
||||||
CREATE TABLE subtransaction_tbl (
|
|
||||||
i integer
|
|
||||||
);
|
|
||||||
-- Explicit case for Python <2.6
|
|
||||||
CREATE FUNCTION subtransaction_test(what_error text = NULL) RETURNS text
|
|
||||||
AS $$
|
|
||||||
import sys
|
|
||||||
subxact = plpy.subtransaction()
|
|
||||||
subxact.__enter__()
|
|
||||||
exc = True
|
|
||||||
try:
|
|
||||||
try:
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES (1)")
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES (2)")
|
|
||||||
if what_error == "SPI":
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES ('oops')")
|
|
||||||
elif what_error == "Python":
|
|
||||||
raise Exception("Python exception")
|
|
||||||
except:
|
|
||||||
exc = False
|
|
||||||
subxact.__exit__(*sys.exc_info())
|
|
||||||
raise
|
|
||||||
finally:
|
|
||||||
if exc:
|
|
||||||
subxact.__exit__(None, None, None)
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
SELECT subtransaction_test();
|
|
||||||
subtransaction_test
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT * FROM subtransaction_tbl;
|
|
||||||
i
|
|
||||||
---
|
|
||||||
1
|
|
||||||
2
|
|
||||||
(2 rows)
|
|
||||||
|
|
||||||
TRUNCATE subtransaction_tbl;
|
|
||||||
SELECT subtransaction_test('SPI');
|
|
||||||
ERROR: spiexceptions.InvalidTextRepresentation: invalid input syntax for type integer: "oops"
|
|
||||||
LINE 1: INSERT INTO subtransaction_tbl VALUES ('oops')
|
|
||||||
^
|
|
||||||
QUERY: INSERT INTO subtransaction_tbl VALUES ('oops')
|
|
||||||
CONTEXT: Traceback (most recent call last):
|
|
||||||
PL/Python function "subtransaction_test", line 11, in <module>
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES ('oops')")
|
|
||||||
PL/Python function "subtransaction_test"
|
|
||||||
SELECT * FROM subtransaction_tbl;
|
|
||||||
i
|
|
||||||
---
|
|
||||||
(0 rows)
|
|
||||||
|
|
||||||
TRUNCATE subtransaction_tbl;
|
|
||||||
SELECT subtransaction_test('Python');
|
|
||||||
ERROR: Exception: Python exception
|
|
||||||
CONTEXT: Traceback (most recent call last):
|
|
||||||
PL/Python function "subtransaction_test", line 13, in <module>
|
|
||||||
raise Exception("Python exception")
|
|
||||||
PL/Python function "subtransaction_test"
|
|
||||||
SELECT * FROM subtransaction_tbl;
|
|
||||||
i
|
|
||||||
---
|
|
||||||
(0 rows)
|
|
||||||
|
|
||||||
TRUNCATE subtransaction_tbl;
|
|
||||||
-- Context manager case for Python >=2.6
|
|
||||||
CREATE FUNCTION subtransaction_ctx_test(what_error text = NULL) RETURNS text
|
|
||||||
AS $$
|
|
||||||
with plpy.subtransaction():
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES (1)")
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES (2)")
|
|
||||||
if what_error == "SPI":
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES ('oops')")
|
|
||||||
elif what_error == "Python":
|
|
||||||
raise Exception("Python exception")
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
ERROR: could not compile PL/Python function "subtransaction_ctx_test"
|
|
||||||
DETAIL: SyntaxError: invalid syntax (<string>, line 3)
|
|
||||||
SELECT subtransaction_ctx_test();
|
|
||||||
ERROR: function subtransaction_ctx_test() does not exist
|
|
||||||
LINE 1: SELECT subtransaction_ctx_test();
|
|
||||||
^
|
|
||||||
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
|
||||||
SELECT * FROM subtransaction_tbl;
|
|
||||||
i
|
|
||||||
---
|
|
||||||
(0 rows)
|
|
||||||
|
|
||||||
TRUNCATE subtransaction_tbl;
|
|
||||||
SELECT subtransaction_ctx_test('SPI');
|
|
||||||
ERROR: function subtransaction_ctx_test(unknown) does not exist
|
|
||||||
LINE 1: SELECT subtransaction_ctx_test('SPI');
|
|
||||||
^
|
|
||||||
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
|
||||||
SELECT * FROM subtransaction_tbl;
|
|
||||||
i
|
|
||||||
---
|
|
||||||
(0 rows)
|
|
||||||
|
|
||||||
TRUNCATE subtransaction_tbl;
|
|
||||||
SELECT subtransaction_ctx_test('Python');
|
|
||||||
ERROR: function subtransaction_ctx_test(unknown) does not exist
|
|
||||||
LINE 1: SELECT subtransaction_ctx_test('Python');
|
|
||||||
^
|
|
||||||
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
|
||||||
SELECT * FROM subtransaction_tbl;
|
|
||||||
i
|
|
||||||
---
|
|
||||||
(0 rows)
|
|
||||||
|
|
||||||
TRUNCATE subtransaction_tbl;
|
|
||||||
-- Nested subtransactions
|
|
||||||
CREATE FUNCTION subtransaction_nested_test(swallow boolean = 'f') RETURNS text
|
|
||||||
AS $$
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES (1)")
|
|
||||||
with plpy.subtransaction():
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES (2)")
|
|
||||||
try:
|
|
||||||
with plpy.subtransaction():
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES (3)")
|
|
||||||
plpy.execute("error")
|
|
||||||
except plpy.SPIError, e:
|
|
||||||
if not swallow:
|
|
||||||
raise
|
|
||||||
plpy.notice("Swallowed %s(%r)" % (e.__class__.__name__, e.args[0]))
|
|
||||||
return "ok"
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
ERROR: could not compile PL/Python function "subtransaction_nested_test"
|
|
||||||
DETAIL: SyntaxError: invalid syntax (<string>, line 4)
|
|
||||||
SELECT subtransaction_nested_test();
|
|
||||||
ERROR: function subtransaction_nested_test() does not exist
|
|
||||||
LINE 1: SELECT subtransaction_nested_test();
|
|
||||||
^
|
|
||||||
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
|
||||||
SELECT * FROM subtransaction_tbl;
|
|
||||||
i
|
|
||||||
---
|
|
||||||
(0 rows)
|
|
||||||
|
|
||||||
TRUNCATE subtransaction_tbl;
|
|
||||||
SELECT subtransaction_nested_test('t');
|
|
||||||
ERROR: function subtransaction_nested_test(unknown) does not exist
|
|
||||||
LINE 1: SELECT subtransaction_nested_test('t');
|
|
||||||
^
|
|
||||||
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
|
||||||
SELECT * FROM subtransaction_tbl;
|
|
||||||
i
|
|
||||||
---
|
|
||||||
(0 rows)
|
|
||||||
|
|
||||||
TRUNCATE subtransaction_tbl;
|
|
||||||
-- Nested subtransactions that recursively call code dealing with
|
|
||||||
-- subtransactions
|
|
||||||
CREATE FUNCTION subtransaction_deeply_nested_test() RETURNS text
|
|
||||||
AS $$
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES (1)")
|
|
||||||
with plpy.subtransaction():
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES (2)")
|
|
||||||
plpy.execute("SELECT subtransaction_nested_test('t')")
|
|
||||||
return "ok"
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
ERROR: could not compile PL/Python function "subtransaction_deeply_nested_test"
|
|
||||||
DETAIL: SyntaxError: invalid syntax (<string>, line 4)
|
|
||||||
SELECT subtransaction_deeply_nested_test();
|
|
||||||
ERROR: function subtransaction_deeply_nested_test() does not exist
|
|
||||||
LINE 1: SELECT subtransaction_deeply_nested_test();
|
|
||||||
^
|
|
||||||
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
|
||||||
SELECT * FROM subtransaction_tbl;
|
|
||||||
i
|
|
||||||
---
|
|
||||||
(0 rows)
|
|
||||||
|
|
||||||
TRUNCATE subtransaction_tbl;
|
|
||||||
-- Error conditions from not opening/closing subtransactions
|
|
||||||
CREATE FUNCTION subtransaction_exit_without_enter() RETURNS void
|
|
||||||
AS $$
|
|
||||||
plpy.subtransaction().__exit__(None, None, None)
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
CREATE FUNCTION subtransaction_enter_without_exit() RETURNS void
|
|
||||||
AS $$
|
|
||||||
plpy.subtransaction().__enter__()
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
CREATE FUNCTION subtransaction_exit_twice() RETURNS void
|
|
||||||
AS $$
|
|
||||||
plpy.subtransaction().__enter__()
|
|
||||||
plpy.subtransaction().__exit__(None, None, None)
|
|
||||||
plpy.subtransaction().__exit__(None, None, None)
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
CREATE FUNCTION subtransaction_enter_twice() RETURNS void
|
|
||||||
AS $$
|
|
||||||
plpy.subtransaction().__enter__()
|
|
||||||
plpy.subtransaction().__enter__()
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
CREATE FUNCTION subtransaction_exit_same_subtransaction_twice() RETURNS void
|
|
||||||
AS $$
|
|
||||||
s = plpy.subtransaction()
|
|
||||||
s.__enter__()
|
|
||||||
s.__exit__(None, None, None)
|
|
||||||
s.__exit__(None, None, None)
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
CREATE FUNCTION subtransaction_enter_same_subtransaction_twice() RETURNS void
|
|
||||||
AS $$
|
|
||||||
s = plpy.subtransaction()
|
|
||||||
s.__enter__()
|
|
||||||
s.__enter__()
|
|
||||||
s.__exit__(None, None, None)
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
-- No warnings here, as the subtransaction gets indeed closed
|
|
||||||
CREATE FUNCTION subtransaction_enter_subtransaction_in_with() RETURNS void
|
|
||||||
AS $$
|
|
||||||
with plpy.subtransaction() as s:
|
|
||||||
s.__enter__()
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
ERROR: could not compile PL/Python function "subtransaction_enter_subtransaction_in_with"
|
|
||||||
DETAIL: SyntaxError: invalid syntax (<string>, line 3)
|
|
||||||
CREATE FUNCTION subtransaction_exit_subtransaction_in_with() RETURNS void
|
|
||||||
AS $$
|
|
||||||
with plpy.subtransaction() as s:
|
|
||||||
s.__exit__(None, None, None)
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
ERROR: could not compile PL/Python function "subtransaction_exit_subtransaction_in_with"
|
|
||||||
DETAIL: SyntaxError: invalid syntax (<string>, line 3)
|
|
||||||
SELECT subtransaction_exit_without_enter();
|
|
||||||
ERROR: ValueError: this subtransaction has not been entered
|
|
||||||
CONTEXT: Traceback (most recent call last):
|
|
||||||
PL/Python function "subtransaction_exit_without_enter", line 2, in <module>
|
|
||||||
plpy.subtransaction().__exit__(None, None, None)
|
|
||||||
PL/Python function "subtransaction_exit_without_enter"
|
|
||||||
SELECT subtransaction_enter_without_exit();
|
|
||||||
WARNING: forcibly aborting a subtransaction that has not been exited
|
|
||||||
subtransaction_enter_without_exit
|
|
||||||
-----------------------------------
|
|
||||||
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT subtransaction_exit_twice();
|
|
||||||
WARNING: forcibly aborting a subtransaction that has not been exited
|
|
||||||
ERROR: ValueError: this subtransaction has not been entered
|
|
||||||
CONTEXT: Traceback (most recent call last):
|
|
||||||
PL/Python function "subtransaction_exit_twice", line 3, in <module>
|
|
||||||
plpy.subtransaction().__exit__(None, None, None)
|
|
||||||
PL/Python function "subtransaction_exit_twice"
|
|
||||||
SELECT subtransaction_enter_twice();
|
|
||||||
WARNING: forcibly aborting a subtransaction that has not been exited
|
|
||||||
WARNING: forcibly aborting a subtransaction that has not been exited
|
|
||||||
subtransaction_enter_twice
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT subtransaction_exit_same_subtransaction_twice();
|
|
||||||
ERROR: ValueError: this subtransaction has already been exited
|
|
||||||
CONTEXT: Traceback (most recent call last):
|
|
||||||
PL/Python function "subtransaction_exit_same_subtransaction_twice", line 5, in <module>
|
|
||||||
s.__exit__(None, None, None)
|
|
||||||
PL/Python function "subtransaction_exit_same_subtransaction_twice"
|
|
||||||
SELECT subtransaction_enter_same_subtransaction_twice();
|
|
||||||
WARNING: forcibly aborting a subtransaction that has not been exited
|
|
||||||
ERROR: ValueError: this subtransaction has already been entered
|
|
||||||
CONTEXT: Traceback (most recent call last):
|
|
||||||
PL/Python function "subtransaction_enter_same_subtransaction_twice", line 4, in <module>
|
|
||||||
s.__enter__()
|
|
||||||
PL/Python function "subtransaction_enter_same_subtransaction_twice"
|
|
||||||
SELECT subtransaction_enter_subtransaction_in_with();
|
|
||||||
ERROR: function subtransaction_enter_subtransaction_in_with() does not exist
|
|
||||||
LINE 1: SELECT subtransaction_enter_subtransaction_in_with();
|
|
||||||
^
|
|
||||||
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
|
||||||
SELECT subtransaction_exit_subtransaction_in_with();
|
|
||||||
ERROR: function subtransaction_exit_subtransaction_in_with() does not exist
|
|
||||||
LINE 1: SELECT subtransaction_exit_subtransaction_in_with();
|
|
||||||
^
|
|
||||||
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
|
||||||
-- Make sure we don't get a "current transaction is aborted" error
|
|
||||||
SELECT 1 as test;
|
|
||||||
test
|
|
||||||
------
|
|
||||||
1
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
-- Mix explicit subtransactions and normal SPI calls
|
|
||||||
CREATE FUNCTION subtransaction_mix_explicit_and_implicit() RETURNS void
|
|
||||||
AS $$
|
|
||||||
p = plpy.prepare("INSERT INTO subtransaction_tbl VALUES ($1)", ["integer"])
|
|
||||||
try:
|
|
||||||
with plpy.subtransaction():
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES (1)")
|
|
||||||
plpy.execute(p, [2])
|
|
||||||
plpy.execute(p, ["wrong"])
|
|
||||||
except plpy.SPIError:
|
|
||||||
plpy.warning("Caught a SPI error from an explicit subtransaction")
|
|
||||||
|
|
||||||
try:
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES (1)")
|
|
||||||
plpy.execute(p, [2])
|
|
||||||
plpy.execute(p, ["wrong"])
|
|
||||||
except plpy.SPIError:
|
|
||||||
plpy.warning("Caught a SPI error")
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
ERROR: could not compile PL/Python function "subtransaction_mix_explicit_and_implicit"
|
|
||||||
DETAIL: SyntaxError: invalid syntax (<string>, line 5)
|
|
||||||
SELECT subtransaction_mix_explicit_and_implicit();
|
|
||||||
ERROR: function subtransaction_mix_explicit_and_implicit() does not exist
|
|
||||||
LINE 1: SELECT subtransaction_mix_explicit_and_implicit();
|
|
||||||
^
|
|
||||||
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
|
||||||
SELECT * FROM subtransaction_tbl;
|
|
||||||
i
|
|
||||||
---
|
|
||||||
(0 rows)
|
|
||||||
|
|
||||||
TRUNCATE subtransaction_tbl;
|
|
||||||
-- Alternative method names for Python <2.6
|
|
||||||
CREATE FUNCTION subtransaction_alternative_names() RETURNS void
|
|
||||||
AS $$
|
|
||||||
s = plpy.subtransaction()
|
|
||||||
s.enter()
|
|
||||||
s.exit(None, None, None)
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
SELECT subtransaction_alternative_names();
|
|
||||||
subtransaction_alternative_names
|
|
||||||
----------------------------------
|
|
||||||
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
-- try/catch inside a subtransaction block
|
|
||||||
CREATE FUNCTION try_catch_inside_subtransaction() RETURNS void
|
|
||||||
AS $$
|
|
||||||
with plpy.subtransaction():
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES (1)")
|
|
||||||
try:
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES ('a')")
|
|
||||||
except plpy.SPIError:
|
|
||||||
plpy.notice("caught")
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
ERROR: could not compile PL/Python function "try_catch_inside_subtransaction"
|
|
||||||
DETAIL: SyntaxError: invalid syntax (<string>, line 3)
|
|
||||||
SELECT try_catch_inside_subtransaction();
|
|
||||||
ERROR: function try_catch_inside_subtransaction() does not exist
|
|
||||||
LINE 1: SELECT try_catch_inside_subtransaction();
|
|
||||||
^
|
|
||||||
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
|
||||||
SELECT * FROM subtransaction_tbl;
|
|
||||||
i
|
|
||||||
---
|
|
||||||
(0 rows)
|
|
||||||
|
|
||||||
TRUNCATE subtransaction_tbl;
|
|
||||||
ALTER TABLE subtransaction_tbl ADD PRIMARY KEY (i);
|
|
||||||
CREATE FUNCTION pk_violation_inside_subtransaction() RETURNS void
|
|
||||||
AS $$
|
|
||||||
with plpy.subtransaction():
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES (1)")
|
|
||||||
try:
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES (1)")
|
|
||||||
except plpy.SPIError:
|
|
||||||
plpy.notice("caught")
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
ERROR: could not compile PL/Python function "pk_violation_inside_subtransaction"
|
|
||||||
DETAIL: SyntaxError: invalid syntax (<string>, line 3)
|
|
||||||
SELECT pk_violation_inside_subtransaction();
|
|
||||||
ERROR: function pk_violation_inside_subtransaction() does not exist
|
|
||||||
LINE 1: SELECT pk_violation_inside_subtransaction();
|
|
||||||
^
|
|
||||||
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
|
||||||
SELECT * FROM subtransaction_tbl;
|
|
||||||
i
|
|
||||||
---
|
|
||||||
(0 rows)
|
|
||||||
|
|
||||||
DROP TABLE subtransaction_tbl;
|
|
||||||
-- cursor/subtransactions interactions
|
|
||||||
CREATE FUNCTION cursor_in_subxact() RETURNS int AS $$
|
|
||||||
with plpy.subtransaction():
|
|
||||||
cur = plpy.cursor("select * from generate_series(1, 20) as gen(i)")
|
|
||||||
cur.fetch(10)
|
|
||||||
fetched = cur.fetch(10);
|
|
||||||
return int(fetched[5]["i"])
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
ERROR: could not compile PL/Python function "cursor_in_subxact"
|
|
||||||
DETAIL: SyntaxError: invalid syntax (<string>, line 3)
|
|
||||||
CREATE FUNCTION cursor_aborted_subxact() RETURNS int AS $$
|
|
||||||
try:
|
|
||||||
with plpy.subtransaction():
|
|
||||||
cur = plpy.cursor("select * from generate_series(1, 20) as gen(i)")
|
|
||||||
cur.fetch(10);
|
|
||||||
plpy.execute("select no_such_function()")
|
|
||||||
except plpy.SPIError:
|
|
||||||
fetched = cur.fetch(10)
|
|
||||||
return int(fetched[5]["i"])
|
|
||||||
return 0 # not reached
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
ERROR: could not compile PL/Python function "cursor_aborted_subxact"
|
|
||||||
DETAIL: SyntaxError: invalid syntax (<string>, line 4)
|
|
||||||
CREATE FUNCTION cursor_plan_aborted_subxact() RETURNS int AS $$
|
|
||||||
try:
|
|
||||||
with plpy.subtransaction():
|
|
||||||
plpy.execute('create temporary table tmp(i) '
|
|
||||||
'as select generate_series(1, 10)')
|
|
||||||
plan = plpy.prepare("select i from tmp")
|
|
||||||
cur = plpy.cursor(plan)
|
|
||||||
plpy.execute("select no_such_function()")
|
|
||||||
except plpy.SPIError:
|
|
||||||
fetched = cur.fetch(5)
|
|
||||||
return fetched[2]["i"]
|
|
||||||
return 0 # not reached
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
ERROR: could not compile PL/Python function "cursor_plan_aborted_subxact"
|
|
||||||
DETAIL: SyntaxError: invalid syntax (<string>, line 4)
|
|
||||||
CREATE FUNCTION cursor_close_aborted_subxact() RETURNS boolean AS $$
|
|
||||||
try:
|
|
||||||
with plpy.subtransaction():
|
|
||||||
cur = plpy.cursor('select 1')
|
|
||||||
plpy.execute("select no_such_function()")
|
|
||||||
except plpy.SPIError:
|
|
||||||
cur.close()
|
|
||||||
return True
|
|
||||||
return False # not reached
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
ERROR: could not compile PL/Python function "cursor_close_aborted_subxact"
|
|
||||||
DETAIL: SyntaxError: invalid syntax (<string>, line 4)
|
|
||||||
SELECT cursor_in_subxact();
|
|
||||||
ERROR: function cursor_in_subxact() does not exist
|
|
||||||
LINE 1: SELECT cursor_in_subxact();
|
|
||||||
^
|
|
||||||
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
|
||||||
SELECT cursor_aborted_subxact();
|
|
||||||
ERROR: function cursor_aborted_subxact() does not exist
|
|
||||||
LINE 1: SELECT cursor_aborted_subxact();
|
|
||||||
^
|
|
||||||
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
|
||||||
SELECT cursor_plan_aborted_subxact();
|
|
||||||
ERROR: function cursor_plan_aborted_subxact() does not exist
|
|
||||||
LINE 1: SELECT cursor_plan_aborted_subxact();
|
|
||||||
^
|
|
||||||
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
|
||||||
SELECT cursor_close_aborted_subxact();
|
|
||||||
ERROR: function cursor_close_aborted_subxact() does not exist
|
|
||||||
LINE 1: SELECT cursor_close_aborted_subxact();
|
|
||||||
^
|
|
||||||
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
|
@ -242,12 +242,6 @@ PLy_traceback(PyObject *e, PyObject *v, PyObject *tb,
|
|||||||
|
|
||||||
PG_TRY();
|
PG_TRY();
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
* Ancient versions of Python (circa 2.3) contain a bug whereby
|
|
||||||
* the fetches below can fail if the error indicator is set.
|
|
||||||
*/
|
|
||||||
PyErr_Clear();
|
|
||||||
|
|
||||||
lineno = PyObject_GetAttrString(tb, "tb_lineno");
|
lineno = PyObject_GetAttrString(tb, "tb_lineno");
|
||||||
if (lineno == NULL)
|
if (lineno == NULL)
|
||||||
elog(ERROR, "could not get line number from Python traceback");
|
elog(ERROR, "could not get line number from Python traceback");
|
||||||
|
@ -59,16 +59,6 @@
|
|||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
|
||||||
* Py_ssize_t compat for Python <= 2.4
|
|
||||||
*/
|
|
||||||
#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
|
|
||||||
typedef int Py_ssize_t;
|
|
||||||
|
|
||||||
#define PY_SSIZE_T_MAX INT_MAX
|
|
||||||
#define PY_SSIZE_T_MIN INT_MIN
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Python 2/3 strings/unicode/bytes handling. Python 2 has strings
|
* Python 2/3 strings/unicode/bytes handling. Python 2 has strings
|
||||||
* and unicode, Python 3 has strings, which are unicode on the C
|
* and unicode, Python 3 has strings, which are unicode on the C
|
||||||
@ -80,15 +70,6 @@ typedef int Py_ssize_t;
|
|||||||
* string to a Python string it converts the C string from the
|
* string to a Python string it converts the C string from the
|
||||||
* PostgreSQL server encoding to a Python Unicode object.
|
* 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
|
#if PY_MAJOR_VERSION >= 3
|
||||||
#define PyString_Check(x) 0
|
#define PyString_Check(x) 0
|
||||||
#define PyString_AsString(x) PLyUnicode_AsString(x)
|
#define PyString_AsString(x) PLyUnicode_AsString(x)
|
||||||
@ -104,16 +85,6 @@ typedef int Py_ssize_t;
|
|||||||
#define PyInt_AsLong(x) PyLong_AsLong(x)
|
#define PyInt_AsLong(x) PyLong_AsLong(x)
|
||||||
#endif
|
#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
|
|
||||||
|
|
||||||
/* Python 3 removed the Py_TPFLAGS_HAVE_ITER flag */
|
/* Python 3 removed the Py_TPFLAGS_HAVE_ITER flag */
|
||||||
#if PY_MAJOR_VERSION >= 3
|
#if PY_MAJOR_VERSION >= 3
|
||||||
#define Py_TPFLAGS_HAVE_ITER 0
|
#define Py_TPFLAGS_HAVE_ITER 0
|
||||||
|
@ -8,43 +8,6 @@ CREATE TABLE subtransaction_tbl (
|
|||||||
i integer
|
i integer
|
||||||
);
|
);
|
||||||
|
|
||||||
-- Explicit case for Python <2.6
|
|
||||||
|
|
||||||
CREATE FUNCTION subtransaction_test(what_error text = NULL) RETURNS text
|
|
||||||
AS $$
|
|
||||||
import sys
|
|
||||||
subxact = plpy.subtransaction()
|
|
||||||
subxact.__enter__()
|
|
||||||
exc = True
|
|
||||||
try:
|
|
||||||
try:
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES (1)")
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES (2)")
|
|
||||||
if what_error == "SPI":
|
|
||||||
plpy.execute("INSERT INTO subtransaction_tbl VALUES ('oops')")
|
|
||||||
elif what_error == "Python":
|
|
||||||
raise Exception("Python exception")
|
|
||||||
except:
|
|
||||||
exc = False
|
|
||||||
subxact.__exit__(*sys.exc_info())
|
|
||||||
raise
|
|
||||||
finally:
|
|
||||||
if exc:
|
|
||||||
subxact.__exit__(None, None, None)
|
|
||||||
$$ LANGUAGE plpythonu;
|
|
||||||
|
|
||||||
SELECT subtransaction_test();
|
|
||||||
SELECT * FROM subtransaction_tbl;
|
|
||||||
TRUNCATE subtransaction_tbl;
|
|
||||||
SELECT subtransaction_test('SPI');
|
|
||||||
SELECT * FROM subtransaction_tbl;
|
|
||||||
TRUNCATE subtransaction_tbl;
|
|
||||||
SELECT subtransaction_test('Python');
|
|
||||||
SELECT * FROM subtransaction_tbl;
|
|
||||||
TRUNCATE subtransaction_tbl;
|
|
||||||
|
|
||||||
-- Context manager case for Python >=2.6
|
|
||||||
|
|
||||||
CREATE FUNCTION subtransaction_ctx_test(what_error text = NULL) RETURNS text
|
CREATE FUNCTION subtransaction_ctx_test(what_error text = NULL) RETURNS text
|
||||||
AS $$
|
AS $$
|
||||||
with plpy.subtransaction():
|
with plpy.subtransaction():
|
||||||
|
Loading…
x
Reference in New Issue
Block a user