Add traceback information to PL/Python errors
This mimics the traceback information the Python interpreter prints with exceptions. Jan Urbański
This commit is contained in:
parent
bf6848bc8c
commit
2bd78eb8d5
@ -3,4 +3,7 @@ NOTICE: This is plpythonu.
|
|||||||
CONTEXT: PL/Python anonymous code block
|
CONTEXT: PL/Python anonymous code block
|
||||||
DO $$ nonsense $$ LANGUAGE plpythonu;
|
DO $$ nonsense $$ LANGUAGE plpythonu;
|
||||||
ERROR: NameError: global name 'nonsense' is not defined
|
ERROR: NameError: global name 'nonsense' is not defined
|
||||||
CONTEXT: PL/Python anonymous code block
|
CONTEXT: Traceback (most recent call last):
|
||||||
|
PL/Python anonymous code block, line 1, in <module>
|
||||||
|
nonsense
|
||||||
|
PL/Python anonymous code block
|
||||||
|
@ -36,7 +36,10 @@ ERROR: spiexceptions.SyntaxError: syntax error at or near "syntax"
|
|||||||
LINE 1: syntax error
|
LINE 1: syntax error
|
||||||
^
|
^
|
||||||
QUERY: syntax error
|
QUERY: syntax error
|
||||||
CONTEXT: PL/Python function "sql_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
|
/* check the handling of uncaught python exceptions
|
||||||
*/
|
*/
|
||||||
CREATE FUNCTION exception_index_invalid(text) RETURNS text
|
CREATE FUNCTION exception_index_invalid(text) RETURNS text
|
||||||
@ -45,7 +48,10 @@ CREATE FUNCTION exception_index_invalid(text) RETURNS text
|
|||||||
LANGUAGE plpythonu;
|
LANGUAGE plpythonu;
|
||||||
SELECT exception_index_invalid('test');
|
SELECT exception_index_invalid('test');
|
||||||
ERROR: IndexError: list index out of range
|
ERROR: IndexError: list index out of range
|
||||||
CONTEXT: PL/Python function "exception_index_invalid"
|
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
|
/* check handling of nested exceptions
|
||||||
*/
|
*/
|
||||||
CREATE FUNCTION exception_index_invalid_nested() RETURNS text
|
CREATE FUNCTION exception_index_invalid_nested() RETURNS text
|
||||||
@ -59,7 +65,10 @@ LINE 1: SELECT test5('foo')
|
|||||||
^
|
^
|
||||||
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
||||||
QUERY: SELECT test5('foo')
|
QUERY: SELECT test5('foo')
|
||||||
CONTEXT: PL/Python function "exception_index_invalid_nested"
|
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
|
/* a typo
|
||||||
*/
|
*/
|
||||||
CREATE FUNCTION invalid_type_uncaught(a text) RETURNS text
|
CREATE FUNCTION invalid_type_uncaught(a text) RETURNS text
|
||||||
@ -75,7 +84,10 @@ return None
|
|||||||
LANGUAGE plpythonu;
|
LANGUAGE plpythonu;
|
||||||
SELECT invalid_type_uncaught('rick');
|
SELECT invalid_type_uncaught('rick');
|
||||||
ERROR: spiexceptions.UndefinedObject: type "test" does not exist
|
ERROR: spiexceptions.UndefinedObject: type "test" does not exist
|
||||||
CONTEXT: PL/Python function "invalid_type_uncaught"
|
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
|
/* for what it's worth catch the exception generated by
|
||||||
* the typo, and return None
|
* the typo, and return None
|
||||||
*/
|
*/
|
||||||
@ -121,7 +133,10 @@ return None
|
|||||||
LANGUAGE plpythonu;
|
LANGUAGE plpythonu;
|
||||||
SELECT invalid_type_reraised('rick');
|
SELECT invalid_type_reraised('rick');
|
||||||
ERROR: plpy.Error: type "test" does not exist
|
ERROR: plpy.Error: type "test" does not exist
|
||||||
CONTEXT: PL/Python function "invalid_type_reraised"
|
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
|
/* no typo no messing about
|
||||||
*/
|
*/
|
||||||
CREATE FUNCTION valid_type(a text) RETURNS text
|
CREATE FUNCTION valid_type(a text) RETURNS text
|
||||||
@ -140,6 +155,164 @@ SELECT valid_type('rick');
|
|||||||
|
|
||||||
(1 row)
|
(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
|
||||||
|
CONTEXT: PL/Python function "nested_warning"
|
||||||
|
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
|
/* check catching specific types of exceptions
|
||||||
*/
|
*/
|
||||||
CREATE TABLE specific (
|
CREATE TABLE specific (
|
||||||
@ -187,7 +360,10 @@ plpy.execute("rollback to save")
|
|||||||
$$ LANGUAGE plpythonu;
|
$$ LANGUAGE plpythonu;
|
||||||
SELECT manual_subxact();
|
SELECT manual_subxact();
|
||||||
ERROR: plpy.SPIError: SPI_execute failed: SPI_ERROR_TRANSACTION
|
ERROR: plpy.SPIError: SPI_execute failed: SPI_ERROR_TRANSACTION
|
||||||
CONTEXT: PL/Python function "manual_subxact"
|
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
|
/* same for prepared plans
|
||||||
*/
|
*/
|
||||||
CREATE FUNCTION manual_subxact_prepared() RETURNS void AS $$
|
CREATE FUNCTION manual_subxact_prepared() RETURNS void AS $$
|
||||||
@ -199,4 +375,7 @@ plpy.execute(rollback)
|
|||||||
$$ LANGUAGE plpythonu;
|
$$ LANGUAGE plpythonu;
|
||||||
SELECT manual_subxact_prepared();
|
SELECT manual_subxact_prepared();
|
||||||
ERROR: plpy.SPIError: SPI_execute_plan failed: SPI_ERROR_TRANSACTION
|
ERROR: plpy.SPIError: SPI_execute_plan failed: SPI_ERROR_TRANSACTION
|
||||||
CONTEXT: PL/Python function "manual_subxact_prepared"
|
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"
|
||||||
|
@ -36,7 +36,10 @@ ERROR: spiexceptions.SyntaxError: syntax error at or near "syntax"
|
|||||||
LINE 1: syntax error
|
LINE 1: syntax error
|
||||||
^
|
^
|
||||||
QUERY: syntax error
|
QUERY: syntax error
|
||||||
CONTEXT: PL/Python function "sql_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
|
/* check the handling of uncaught python exceptions
|
||||||
*/
|
*/
|
||||||
CREATE FUNCTION exception_index_invalid(text) RETURNS text
|
CREATE FUNCTION exception_index_invalid(text) RETURNS text
|
||||||
@ -45,7 +48,10 @@ CREATE FUNCTION exception_index_invalid(text) RETURNS text
|
|||||||
LANGUAGE plpythonu;
|
LANGUAGE plpythonu;
|
||||||
SELECT exception_index_invalid('test');
|
SELECT exception_index_invalid('test');
|
||||||
ERROR: IndexError: list index out of range
|
ERROR: IndexError: list index out of range
|
||||||
CONTEXT: PL/Python function "exception_index_invalid"
|
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
|
/* check handling of nested exceptions
|
||||||
*/
|
*/
|
||||||
CREATE FUNCTION exception_index_invalid_nested() RETURNS text
|
CREATE FUNCTION exception_index_invalid_nested() RETURNS text
|
||||||
@ -59,7 +65,10 @@ LINE 1: SELECT test5('foo')
|
|||||||
^
|
^
|
||||||
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
||||||
QUERY: SELECT test5('foo')
|
QUERY: SELECT test5('foo')
|
||||||
CONTEXT: PL/Python function "exception_index_invalid_nested"
|
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
|
/* a typo
|
||||||
*/
|
*/
|
||||||
CREATE FUNCTION invalid_type_uncaught(a text) RETURNS text
|
CREATE FUNCTION invalid_type_uncaught(a text) RETURNS text
|
||||||
@ -75,7 +84,10 @@ return None
|
|||||||
LANGUAGE plpythonu;
|
LANGUAGE plpythonu;
|
||||||
SELECT invalid_type_uncaught('rick');
|
SELECT invalid_type_uncaught('rick');
|
||||||
ERROR: spiexceptions.UndefinedObject: type "test" does not exist
|
ERROR: spiexceptions.UndefinedObject: type "test" does not exist
|
||||||
CONTEXT: PL/Python function "invalid_type_uncaught"
|
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
|
/* for what it's worth catch the exception generated by
|
||||||
* the typo, and return None
|
* the typo, and return None
|
||||||
*/
|
*/
|
||||||
@ -121,7 +133,10 @@ return None
|
|||||||
LANGUAGE plpythonu;
|
LANGUAGE plpythonu;
|
||||||
SELECT invalid_type_reraised('rick');
|
SELECT invalid_type_reraised('rick');
|
||||||
ERROR: plpy.Error: type "test" does not exist
|
ERROR: plpy.Error: type "test" does not exist
|
||||||
CONTEXT: PL/Python function "invalid_type_reraised"
|
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
|
/* no typo no messing about
|
||||||
*/
|
*/
|
||||||
CREATE FUNCTION valid_type(a text) RETURNS text
|
CREATE FUNCTION valid_type(a text) RETURNS text
|
||||||
@ -140,6 +155,164 @@ SELECT valid_type('rick');
|
|||||||
|
|
||||||
(1 row)
|
(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
|
||||||
|
CONTEXT: PL/Python function "nested_warning"
|
||||||
|
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
|
/* check catching specific types of exceptions
|
||||||
*/
|
*/
|
||||||
CREATE TABLE specific (
|
CREATE TABLE specific (
|
||||||
@ -187,7 +360,10 @@ plpy.execute("rollback to save")
|
|||||||
$$ LANGUAGE plpythonu;
|
$$ LANGUAGE plpythonu;
|
||||||
SELECT manual_subxact();
|
SELECT manual_subxact();
|
||||||
ERROR: plpy.SPIError: SPI_execute failed: SPI_ERROR_TRANSACTION
|
ERROR: plpy.SPIError: SPI_execute failed: SPI_ERROR_TRANSACTION
|
||||||
CONTEXT: PL/Python function "manual_subxact"
|
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
|
/* same for prepared plans
|
||||||
*/
|
*/
|
||||||
CREATE FUNCTION manual_subxact_prepared() RETURNS void AS $$
|
CREATE FUNCTION manual_subxact_prepared() RETURNS void AS $$
|
||||||
@ -199,4 +375,7 @@ plpy.execute(rollback)
|
|||||||
$$ LANGUAGE plpythonu;
|
$$ LANGUAGE plpythonu;
|
||||||
SELECT manual_subxact_prepared();
|
SELECT manual_subxact_prepared();
|
||||||
ERROR: plpy.SPIError: SPI_execute_plan failed: SPI_ERROR_TRANSACTION
|
ERROR: plpy.SPIError: SPI_execute_plan failed: SPI_ERROR_TRANSACTION
|
||||||
CONTEXT: PL/Python function "manual_subxact_prepared"
|
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"
|
||||||
|
@ -47,7 +47,10 @@ ERROR: spiexceptions.InvalidTextRepresentation: invalid input syntax for intege
|
|||||||
LINE 1: INSERT INTO subtransaction_tbl VALUES ('oops')
|
LINE 1: INSERT INTO subtransaction_tbl VALUES ('oops')
|
||||||
^
|
^
|
||||||
QUERY: INSERT INTO subtransaction_tbl VALUES ('oops')
|
QUERY: INSERT INTO subtransaction_tbl VALUES ('oops')
|
||||||
CONTEXT: PL/Python function "subtransaction_test"
|
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;
|
SELECT * FROM subtransaction_tbl;
|
||||||
i
|
i
|
||||||
---
|
---
|
||||||
@ -56,7 +59,10 @@ SELECT * FROM subtransaction_tbl;
|
|||||||
TRUNCATE subtransaction_tbl;
|
TRUNCATE subtransaction_tbl;
|
||||||
SELECT subtransaction_test('Python');
|
SELECT subtransaction_test('Python');
|
||||||
ERROR: AttributeError: 'module' object has no attribute 'attribute_error'
|
ERROR: AttributeError: 'module' object has no attribute 'attribute_error'
|
||||||
CONTEXT: PL/Python function "subtransaction_test"
|
CONTEXT: Traceback (most recent call last):
|
||||||
|
PL/Python function "subtransaction_test", line 13, in <module>
|
||||||
|
plpy.attribute_error
|
||||||
|
PL/Python function "subtransaction_test"
|
||||||
SELECT * FROM subtransaction_tbl;
|
SELECT * FROM subtransaction_tbl;
|
||||||
i
|
i
|
||||||
---
|
---
|
||||||
@ -93,7 +99,10 @@ ERROR: spiexceptions.InvalidTextRepresentation: invalid input syntax for intege
|
|||||||
LINE 1: INSERT INTO subtransaction_tbl VALUES ('oops')
|
LINE 1: INSERT INTO subtransaction_tbl VALUES ('oops')
|
||||||
^
|
^
|
||||||
QUERY: INSERT INTO subtransaction_tbl VALUES ('oops')
|
QUERY: INSERT INTO subtransaction_tbl VALUES ('oops')
|
||||||
CONTEXT: PL/Python function "subtransaction_ctx_test"
|
CONTEXT: Traceback (most recent call last):
|
||||||
|
PL/Python function "subtransaction_ctx_test", line 6, in <module>
|
||||||
|
plpy.execute("INSERT INTO subtransaction_tbl VALUES ('oops')")
|
||||||
|
PL/Python function "subtransaction_ctx_test"
|
||||||
SELECT * FROM subtransaction_tbl;
|
SELECT * FROM subtransaction_tbl;
|
||||||
i
|
i
|
||||||
---
|
---
|
||||||
@ -102,7 +111,10 @@ SELECT * FROM subtransaction_tbl;
|
|||||||
TRUNCATE subtransaction_tbl;
|
TRUNCATE subtransaction_tbl;
|
||||||
SELECT subtransaction_ctx_test('Python');
|
SELECT subtransaction_ctx_test('Python');
|
||||||
ERROR: AttributeError: 'module' object has no attribute 'attribute_error'
|
ERROR: AttributeError: 'module' object has no attribute 'attribute_error'
|
||||||
CONTEXT: PL/Python function "subtransaction_ctx_test"
|
CONTEXT: Traceback (most recent call last):
|
||||||
|
PL/Python function "subtransaction_ctx_test", line 8, in <module>
|
||||||
|
plpy.attribute_error
|
||||||
|
PL/Python function "subtransaction_ctx_test"
|
||||||
SELECT * FROM subtransaction_tbl;
|
SELECT * FROM subtransaction_tbl;
|
||||||
i
|
i
|
||||||
---
|
---
|
||||||
@ -130,7 +142,10 @@ ERROR: spiexceptions.SyntaxError: syntax error at or near "error"
|
|||||||
LINE 1: error
|
LINE 1: error
|
||||||
^
|
^
|
||||||
QUERY: error
|
QUERY: error
|
||||||
CONTEXT: PL/Python function "subtransaction_nested_test"
|
CONTEXT: Traceback (most recent call last):
|
||||||
|
PL/Python function "subtransaction_nested_test", line 8, in <module>
|
||||||
|
plpy.execute("error")
|
||||||
|
PL/Python function "subtransaction_nested_test"
|
||||||
SELECT * FROM subtransaction_tbl;
|
SELECT * FROM subtransaction_tbl;
|
||||||
i
|
i
|
||||||
---
|
---
|
||||||
@ -230,7 +245,10 @@ with plpy.subtransaction() as s:
|
|||||||
$$ LANGUAGE plpythonu;
|
$$ LANGUAGE plpythonu;
|
||||||
SELECT subtransaction_exit_without_enter();
|
SELECT subtransaction_exit_without_enter();
|
||||||
ERROR: ValueError: this subtransaction has not been entered
|
ERROR: ValueError: this subtransaction has not been entered
|
||||||
CONTEXT: PL/Python function "subtransaction_exit_without_enter"
|
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();
|
SELECT subtransaction_enter_without_exit();
|
||||||
WARNING: forcibly aborting a subtransaction that has not been exited
|
WARNING: forcibly aborting a subtransaction that has not been exited
|
||||||
CONTEXT: PL/Python function "subtransaction_enter_without_exit"
|
CONTEXT: PL/Python function "subtransaction_enter_without_exit"
|
||||||
@ -243,7 +261,10 @@ SELECT subtransaction_exit_twice();
|
|||||||
WARNING: forcibly aborting a subtransaction that has not been exited
|
WARNING: forcibly aborting a subtransaction that has not been exited
|
||||||
CONTEXT: PL/Python function "subtransaction_exit_twice"
|
CONTEXT: PL/Python function "subtransaction_exit_twice"
|
||||||
ERROR: ValueError: this subtransaction has not been entered
|
ERROR: ValueError: this subtransaction has not been entered
|
||||||
CONTEXT: PL/Python function "subtransaction_exit_twice"
|
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();
|
SELECT subtransaction_enter_twice();
|
||||||
WARNING: forcibly aborting a subtransaction that has not been exited
|
WARNING: forcibly aborting a subtransaction that has not been exited
|
||||||
CONTEXT: PL/Python function "subtransaction_enter_twice"
|
CONTEXT: PL/Python function "subtransaction_enter_twice"
|
||||||
@ -256,18 +277,30 @@ CONTEXT: PL/Python function "subtransaction_enter_twice"
|
|||||||
|
|
||||||
SELECT subtransaction_exit_same_subtransaction_twice();
|
SELECT subtransaction_exit_same_subtransaction_twice();
|
||||||
ERROR: ValueError: this subtransaction has already been exited
|
ERROR: ValueError: this subtransaction has already been exited
|
||||||
CONTEXT: PL/Python function "subtransaction_exit_same_subtransaction_twice"
|
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();
|
SELECT subtransaction_enter_same_subtransaction_twice();
|
||||||
WARNING: forcibly aborting a subtransaction that has not been exited
|
WARNING: forcibly aborting a subtransaction that has not been exited
|
||||||
CONTEXT: PL/Python function "subtransaction_enter_same_subtransaction_twice"
|
CONTEXT: PL/Python function "subtransaction_enter_same_subtransaction_twice"
|
||||||
ERROR: ValueError: this subtransaction has already been entered
|
ERROR: ValueError: this subtransaction has already been entered
|
||||||
CONTEXT: PL/Python function "subtransaction_enter_same_subtransaction_twice"
|
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();
|
SELECT subtransaction_enter_subtransaction_in_with();
|
||||||
ERROR: ValueError: this subtransaction has already been entered
|
ERROR: ValueError: this subtransaction has already been entered
|
||||||
CONTEXT: PL/Python function "subtransaction_enter_subtransaction_in_with"
|
CONTEXT: Traceback (most recent call last):
|
||||||
|
PL/Python function "subtransaction_enter_subtransaction_in_with", line 3, in <module>
|
||||||
|
s.__enter__()
|
||||||
|
PL/Python function "subtransaction_enter_subtransaction_in_with"
|
||||||
SELECT subtransaction_exit_subtransaction_in_with();
|
SELECT subtransaction_exit_subtransaction_in_with();
|
||||||
ERROR: ValueError: this subtransaction has already been exited
|
ERROR: ValueError: this subtransaction has already been exited
|
||||||
CONTEXT: PL/Python function "subtransaction_exit_subtransaction_in_with"
|
CONTEXT: Traceback (most recent call last):
|
||||||
|
PL/Python function "subtransaction_exit_subtransaction_in_with", line 3, in <module>
|
||||||
|
s.__exit__(None, None, None)
|
||||||
|
PL/Python function "subtransaction_exit_subtransaction_in_with"
|
||||||
-- Make sure we don't get a "current transaction is aborted" error
|
-- Make sure we don't get a "current transaction is aborted" error
|
||||||
SELECT 1 as test;
|
SELECT 1 as test;
|
||||||
test
|
test
|
||||||
|
@ -47,7 +47,10 @@ ERROR: spiexceptions.InvalidTextRepresentation: invalid input syntax for intege
|
|||||||
LINE 1: INSERT INTO subtransaction_tbl VALUES ('oops')
|
LINE 1: INSERT INTO subtransaction_tbl VALUES ('oops')
|
||||||
^
|
^
|
||||||
QUERY: INSERT INTO subtransaction_tbl VALUES ('oops')
|
QUERY: INSERT INTO subtransaction_tbl VALUES ('oops')
|
||||||
CONTEXT: PL/Python function "subtransaction_test"
|
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;
|
SELECT * FROM subtransaction_tbl;
|
||||||
i
|
i
|
||||||
---
|
---
|
||||||
@ -56,7 +59,10 @@ SELECT * FROM subtransaction_tbl;
|
|||||||
TRUNCATE subtransaction_tbl;
|
TRUNCATE subtransaction_tbl;
|
||||||
SELECT subtransaction_test('Python');
|
SELECT subtransaction_test('Python');
|
||||||
ERROR: AttributeError: 'module' object has no attribute 'attribute_error'
|
ERROR: AttributeError: 'module' object has no attribute 'attribute_error'
|
||||||
CONTEXT: PL/Python function "subtransaction_test"
|
CONTEXT: Traceback (most recent call last):
|
||||||
|
PL/Python function "subtransaction_test", line 13, in <module>
|
||||||
|
plpy.attribute_error
|
||||||
|
PL/Python function "subtransaction_test"
|
||||||
SELECT * FROM subtransaction_tbl;
|
SELECT * FROM subtransaction_tbl;
|
||||||
i
|
i
|
||||||
---
|
---
|
||||||
@ -223,7 +229,10 @@ ERROR: could not compile PL/Python function "subtransaction_exit_subtransaction
|
|||||||
DETAIL: SyntaxError: invalid syntax (line 3)
|
DETAIL: SyntaxError: invalid syntax (line 3)
|
||||||
SELECT subtransaction_exit_without_enter();
|
SELECT subtransaction_exit_without_enter();
|
||||||
ERROR: ValueError: this subtransaction has not been entered
|
ERROR: ValueError: this subtransaction has not been entered
|
||||||
CONTEXT: PL/Python function "subtransaction_exit_without_enter"
|
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();
|
SELECT subtransaction_enter_without_exit();
|
||||||
WARNING: forcibly aborting a subtransaction that has not been exited
|
WARNING: forcibly aborting a subtransaction that has not been exited
|
||||||
CONTEXT: PL/Python function "subtransaction_enter_without_exit"
|
CONTEXT: PL/Python function "subtransaction_enter_without_exit"
|
||||||
@ -236,7 +245,10 @@ SELECT subtransaction_exit_twice();
|
|||||||
WARNING: forcibly aborting a subtransaction that has not been exited
|
WARNING: forcibly aborting a subtransaction that has not been exited
|
||||||
CONTEXT: PL/Python function "subtransaction_exit_twice"
|
CONTEXT: PL/Python function "subtransaction_exit_twice"
|
||||||
ERROR: ValueError: this subtransaction has not been entered
|
ERROR: ValueError: this subtransaction has not been entered
|
||||||
CONTEXT: PL/Python function "subtransaction_exit_twice"
|
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();
|
SELECT subtransaction_enter_twice();
|
||||||
WARNING: forcibly aborting a subtransaction that has not been exited
|
WARNING: forcibly aborting a subtransaction that has not been exited
|
||||||
CONTEXT: PL/Python function "subtransaction_enter_twice"
|
CONTEXT: PL/Python function "subtransaction_enter_twice"
|
||||||
@ -249,12 +261,18 @@ CONTEXT: PL/Python function "subtransaction_enter_twice"
|
|||||||
|
|
||||||
SELECT subtransaction_exit_same_subtransaction_twice();
|
SELECT subtransaction_exit_same_subtransaction_twice();
|
||||||
ERROR: ValueError: this subtransaction has already been exited
|
ERROR: ValueError: this subtransaction has already been exited
|
||||||
CONTEXT: PL/Python function "subtransaction_exit_same_subtransaction_twice"
|
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();
|
SELECT subtransaction_enter_same_subtransaction_twice();
|
||||||
WARNING: forcibly aborting a subtransaction that has not been exited
|
WARNING: forcibly aborting a subtransaction that has not been exited
|
||||||
CONTEXT: PL/Python function "subtransaction_enter_same_subtransaction_twice"
|
CONTEXT: PL/Python function "subtransaction_enter_same_subtransaction_twice"
|
||||||
ERROR: ValueError: this subtransaction has already been entered
|
ERROR: ValueError: this subtransaction has already been entered
|
||||||
CONTEXT: PL/Python function "subtransaction_enter_same_subtransaction_twice"
|
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();
|
SELECT subtransaction_enter_subtransaction_in_with();
|
||||||
ERROR: function subtransaction_enter_subtransaction_in_with() does not exist
|
ERROR: function subtransaction_enter_subtransaction_in_with() does not exist
|
||||||
LINE 1: SELECT subtransaction_enter_subtransaction_in_with();
|
LINE 1: SELECT subtransaction_enter_subtransaction_in_with();
|
||||||
|
@ -47,7 +47,10 @@ ERROR: spiexceptions.InvalidTextRepresentation: invalid input syntax for intege
|
|||||||
LINE 1: INSERT INTO subtransaction_tbl VALUES ('oops')
|
LINE 1: INSERT INTO subtransaction_tbl VALUES ('oops')
|
||||||
^
|
^
|
||||||
QUERY: INSERT INTO subtransaction_tbl VALUES ('oops')
|
QUERY: INSERT INTO subtransaction_tbl VALUES ('oops')
|
||||||
CONTEXT: PL/Python function "subtransaction_test"
|
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;
|
SELECT * FROM subtransaction_tbl;
|
||||||
i
|
i
|
||||||
---
|
---
|
||||||
@ -56,7 +59,10 @@ SELECT * FROM subtransaction_tbl;
|
|||||||
TRUNCATE subtransaction_tbl;
|
TRUNCATE subtransaction_tbl;
|
||||||
SELECT subtransaction_test('Python');
|
SELECT subtransaction_test('Python');
|
||||||
ERROR: AttributeError: 'module' object has no attribute 'attribute_error'
|
ERROR: AttributeError: 'module' object has no attribute 'attribute_error'
|
||||||
CONTEXT: PL/Python function "subtransaction_test"
|
CONTEXT: Traceback (most recent call last):
|
||||||
|
PL/Python function "subtransaction_test", line 13, in <module>
|
||||||
|
plpy.attribute_error
|
||||||
|
PL/Python function "subtransaction_test"
|
||||||
SELECT * FROM subtransaction_tbl;
|
SELECT * FROM subtransaction_tbl;
|
||||||
i
|
i
|
||||||
---
|
---
|
||||||
@ -223,7 +229,10 @@ ERROR: could not compile PL/Python function "subtransaction_exit_subtransaction
|
|||||||
DETAIL: SyntaxError: invalid syntax (<string>, line 3)
|
DETAIL: SyntaxError: invalid syntax (<string>, line 3)
|
||||||
SELECT subtransaction_exit_without_enter();
|
SELECT subtransaction_exit_without_enter();
|
||||||
ERROR: ValueError: this subtransaction has not been entered
|
ERROR: ValueError: this subtransaction has not been entered
|
||||||
CONTEXT: PL/Python function "subtransaction_exit_without_enter"
|
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();
|
SELECT subtransaction_enter_without_exit();
|
||||||
WARNING: forcibly aborting a subtransaction that has not been exited
|
WARNING: forcibly aborting a subtransaction that has not been exited
|
||||||
CONTEXT: PL/Python function "subtransaction_enter_without_exit"
|
CONTEXT: PL/Python function "subtransaction_enter_without_exit"
|
||||||
@ -236,7 +245,10 @@ SELECT subtransaction_exit_twice();
|
|||||||
WARNING: forcibly aborting a subtransaction that has not been exited
|
WARNING: forcibly aborting a subtransaction that has not been exited
|
||||||
CONTEXT: PL/Python function "subtransaction_exit_twice"
|
CONTEXT: PL/Python function "subtransaction_exit_twice"
|
||||||
ERROR: ValueError: this subtransaction has not been entered
|
ERROR: ValueError: this subtransaction has not been entered
|
||||||
CONTEXT: PL/Python function "subtransaction_exit_twice"
|
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();
|
SELECT subtransaction_enter_twice();
|
||||||
WARNING: forcibly aborting a subtransaction that has not been exited
|
WARNING: forcibly aborting a subtransaction that has not been exited
|
||||||
CONTEXT: PL/Python function "subtransaction_enter_twice"
|
CONTEXT: PL/Python function "subtransaction_enter_twice"
|
||||||
@ -249,12 +261,18 @@ CONTEXT: PL/Python function "subtransaction_enter_twice"
|
|||||||
|
|
||||||
SELECT subtransaction_exit_same_subtransaction_twice();
|
SELECT subtransaction_exit_same_subtransaction_twice();
|
||||||
ERROR: ValueError: this subtransaction has already been exited
|
ERROR: ValueError: this subtransaction has already been exited
|
||||||
CONTEXT: PL/Python function "subtransaction_exit_same_subtransaction_twice"
|
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();
|
SELECT subtransaction_enter_same_subtransaction_twice();
|
||||||
WARNING: forcibly aborting a subtransaction that has not been exited
|
WARNING: forcibly aborting a subtransaction that has not been exited
|
||||||
CONTEXT: PL/Python function "subtransaction_enter_same_subtransaction_twice"
|
CONTEXT: PL/Python function "subtransaction_enter_same_subtransaction_twice"
|
||||||
ERROR: ValueError: this subtransaction has already been entered
|
ERROR: ValueError: this subtransaction has already been entered
|
||||||
CONTEXT: PL/Python function "subtransaction_enter_same_subtransaction_twice"
|
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();
|
SELECT subtransaction_enter_subtransaction_in_with();
|
||||||
ERROR: function subtransaction_enter_subtransaction_in_with() does not exist
|
ERROR: function subtransaction_enter_subtransaction_in_with() does not exist
|
||||||
LINE 1: SELECT subtransaction_enter_subtransaction_in_with();
|
LINE 1: SELECT subtransaction_enter_subtransaction_in_with();
|
||||||
|
@ -74,4 +74,7 @@ CONTEXT: PL/Python function "elog_test"
|
|||||||
WARNING: warning
|
WARNING: warning
|
||||||
CONTEXT: PL/Python function "elog_test"
|
CONTEXT: PL/Python function "elog_test"
|
||||||
ERROR: plpy.Error: error
|
ERROR: plpy.Error: error
|
||||||
CONTEXT: PL/Python function "elog_test"
|
CONTEXT: Traceback (most recent call last):
|
||||||
|
PL/Python function "elog_test", line 10, in <module>
|
||||||
|
plpy.error('error')
|
||||||
|
PL/Python function "elog_test"
|
||||||
|
@ -625,7 +625,10 @@ SELECT name, test_composite_table_input(employee.*) FROM employee;
|
|||||||
ALTER TABLE employee DROP bonus;
|
ALTER TABLE employee DROP bonus;
|
||||||
SELECT name, test_composite_table_input(employee.*) FROM employee;
|
SELECT name, test_composite_table_input(employee.*) FROM employee;
|
||||||
ERROR: KeyError: 'bonus'
|
ERROR: KeyError: 'bonus'
|
||||||
CONTEXT: PL/Python function "test_composite_table_input"
|
CONTEXT: Traceback (most recent call last):
|
||||||
|
PL/Python function "test_composite_table_input", line 2, in <module>
|
||||||
|
return e['basesalary'] + e['bonus']
|
||||||
|
PL/Python function "test_composite_table_input"
|
||||||
ALTER TABLE employee ADD bonus integer;
|
ALTER TABLE employee ADD bonus integer;
|
||||||
UPDATE employee SET bonus = 10;
|
UPDATE employee SET bonus = 10;
|
||||||
SELECT name, test_composite_table_input(employee.*) FROM employee;
|
SELECT name, test_composite_table_input(employee.*) FROM employee;
|
||||||
|
@ -625,7 +625,10 @@ SELECT name, test_composite_table_input(employee.*) FROM employee;
|
|||||||
ALTER TABLE employee DROP bonus;
|
ALTER TABLE employee DROP bonus;
|
||||||
SELECT name, test_composite_table_input(employee.*) FROM employee;
|
SELECT name, test_composite_table_input(employee.*) FROM employee;
|
||||||
ERROR: KeyError: 'bonus'
|
ERROR: KeyError: 'bonus'
|
||||||
CONTEXT: PL/Python function "test_composite_table_input"
|
CONTEXT: Traceback (most recent call last):
|
||||||
|
PL/Python function "test_composite_table_input", line 2, in <module>
|
||||||
|
return e['basesalary'] + e['bonus']
|
||||||
|
PL/Python function "test_composite_table_input"
|
||||||
ALTER TABLE employee ADD bonus integer;
|
ALTER TABLE employee ADD bonus integer;
|
||||||
UPDATE employee SET bonus = 10;
|
UPDATE employee SET bonus = 10;
|
||||||
SELECT name, test_composite_table_input(employee.*) FROM employee;
|
SELECT name, test_composite_table_input(employee.*) FROM employee;
|
||||||
|
@ -71,6 +71,7 @@ typedef int Py_ssize_t;
|
|||||||
*/
|
*/
|
||||||
#if PY_MAJOR_VERSION >= 3
|
#if PY_MAJOR_VERSION >= 3
|
||||||
#define PyInt_FromLong(x) PyLong_FromLong(x)
|
#define PyInt_FromLong(x) PyLong_FromLong(x)
|
||||||
|
#define PyInt_AsLong(x) PyLong_AsLong(x)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -217,6 +218,7 @@ typedef struct PLyProcedure
|
|||||||
* type */
|
* type */
|
||||||
bool is_setof; /* true, if procedure returns result set */
|
bool is_setof; /* true, if procedure returns result set */
|
||||||
PyObject *setof; /* contents of result set. */
|
PyObject *setof; /* contents of result set. */
|
||||||
|
char *src; /* textual procedure code, after mangling */
|
||||||
char **argnames; /* Argument names */
|
char **argnames; /* Argument names */
|
||||||
PLyTypeInfo args[FUNC_MAX_ARGS];
|
PLyTypeInfo args[FUNC_MAX_ARGS];
|
||||||
int nargs;
|
int nargs;
|
||||||
@ -342,7 +344,7 @@ static void
|
|||||||
PLy_elog(int, const char *,...)
|
PLy_elog(int, const char *,...)
|
||||||
__attribute__((format(printf, 2, 3)));
|
__attribute__((format(printf, 2, 3)));
|
||||||
static void PLy_get_spi_error_data(PyObject *exc, char **detail, char **hint, char **query, int *position);
|
static void PLy_get_spi_error_data(PyObject *exc, char **detail, char **hint, char **query, int *position);
|
||||||
static char *PLy_traceback(int *);
|
static void PLy_traceback(char **, char **, int *);
|
||||||
|
|
||||||
static void *PLy_malloc(size_t);
|
static void *PLy_malloc(size_t);
|
||||||
static void *PLy_malloc0(size_t);
|
static void *PLy_malloc0(size_t);
|
||||||
@ -1602,6 +1604,7 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
|
|||||||
proc->globals = NULL;
|
proc->globals = NULL;
|
||||||
proc->is_setof = procStruct->proretset;
|
proc->is_setof = procStruct->proretset;
|
||||||
proc->setof = NULL;
|
proc->setof = NULL;
|
||||||
|
proc->src = NULL;
|
||||||
proc->argnames = NULL;
|
proc->argnames = NULL;
|
||||||
|
|
||||||
PG_TRY();
|
PG_TRY();
|
||||||
@ -1788,6 +1791,8 @@ PLy_procedure_compile(PLyProcedure *proc, const char *src)
|
|||||||
* insert the function code into the interpreter
|
* insert the function code into the interpreter
|
||||||
*/
|
*/
|
||||||
msrc = PLy_procedure_munge_source(proc->pyname, src);
|
msrc = PLy_procedure_munge_source(proc->pyname, src);
|
||||||
|
/* Save the mangled source for later inclusion in tracebacks */
|
||||||
|
proc->src = PLy_strdup(msrc);
|
||||||
crv = PyRun_String(msrc, Py_file_input, proc->globals, NULL);
|
crv = PyRun_String(msrc, Py_file_input, proc->globals, NULL);
|
||||||
pfree(msrc);
|
pfree(msrc);
|
||||||
|
|
||||||
@ -1885,6 +1890,8 @@ PLy_procedure_delete(PLyProcedure *proc)
|
|||||||
if (proc->argnames && proc->argnames[i])
|
if (proc->argnames && proc->argnames[i])
|
||||||
PLy_free(proc->argnames[i]);
|
PLy_free(proc->argnames[i]);
|
||||||
}
|
}
|
||||||
|
if (proc->src)
|
||||||
|
PLy_free(proc->src);
|
||||||
if (proc->argnames)
|
if (proc->argnames)
|
||||||
PLy_free(proc->argnames);
|
PLy_free(proc->argnames);
|
||||||
}
|
}
|
||||||
@ -4361,15 +4368,18 @@ failure:
|
|||||||
* the current Python error, previously set by PLy_exception_set().
|
* the current Python error, previously set by PLy_exception_set().
|
||||||
* This should be used to propagate Python errors into PG. If fmt is
|
* This should be used to propagate Python errors into PG. If fmt is
|
||||||
* NULL, the Python error becomes the primary error message, otherwise
|
* NULL, the Python error becomes the primary error message, otherwise
|
||||||
* it becomes the detail.
|
* it becomes the detail. If there is a Python traceback, it is put
|
||||||
|
* in the context.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
PLy_elog(int elevel, const char *fmt,...)
|
PLy_elog(int elevel, const char *fmt,...)
|
||||||
{
|
{
|
||||||
char *xmsg;
|
char *xmsg;
|
||||||
int xlevel;
|
char *tbmsg;
|
||||||
|
int tb_depth;
|
||||||
StringInfoData emsg;
|
StringInfoData emsg;
|
||||||
PyObject *exc, *val, *tb;
|
PyObject *exc, *val, *tb;
|
||||||
|
const char *primary = NULL;
|
||||||
char *detail = NULL;
|
char *detail = NULL;
|
||||||
char *hint = NULL;
|
char *hint = NULL;
|
||||||
char *query = NULL;
|
char *query = NULL;
|
||||||
@ -4385,7 +4395,7 @@ PLy_elog(int elevel, const char *fmt,...)
|
|||||||
}
|
}
|
||||||
PyErr_Restore(exc, val, tb);
|
PyErr_Restore(exc, val, tb);
|
||||||
|
|
||||||
xmsg = PLy_traceback(&xlevel);
|
PLy_traceback(&xmsg, &tbmsg, &tb_depth);
|
||||||
|
|
||||||
if (fmt)
|
if (fmt)
|
||||||
{
|
{
|
||||||
@ -4402,24 +4412,30 @@ PLy_elog(int elevel, const char *fmt,...)
|
|||||||
break;
|
break;
|
||||||
enlargeStringInfo(&emsg, emsg.maxlen);
|
enlargeStringInfo(&emsg, emsg.maxlen);
|
||||||
}
|
}
|
||||||
|
primary = emsg.data;
|
||||||
|
|
||||||
|
/* Since we have a format string, we cannot have a SPI detail. */
|
||||||
|
Assert(detail == NULL);
|
||||||
|
|
||||||
|
/* If there's an exception message, it goes in the detail. */
|
||||||
|
if (xmsg)
|
||||||
|
detail = xmsg;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (xmsg)
|
||||||
|
primary = xmsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
PG_TRY();
|
PG_TRY();
|
||||||
{
|
{
|
||||||
if (fmt)
|
ereport(elevel,
|
||||||
ereport(elevel,
|
(errmsg("%s", primary ? primary : "no exception data"),
|
||||||
(errmsg("%s", emsg.data),
|
(detail) ? errdetail("%s", detail) : 0,
|
||||||
(xmsg) ? errdetail("%s", xmsg) : 0,
|
(tb_depth > 0 && tbmsg) ? errcontext("%s", tbmsg) : 0,
|
||||||
(hint) ? errhint("%s", hint) : 0,
|
(hint) ? errhint("%s", hint) : 0,
|
||||||
(query) ? internalerrquery(query) : 0,
|
(query) ? internalerrquery(query) : 0,
|
||||||
(position) ? internalerrposition(position) : 0));
|
(position) ? internalerrposition(position) : 0));
|
||||||
else
|
|
||||||
ereport(elevel,
|
|
||||||
(errmsg("%s", xmsg),
|
|
||||||
(detail) ? errdetail("%s", detail) : 0,
|
|
||||||
(hint) ? errhint("%s", hint) : 0,
|
|
||||||
(query) ? internalerrquery(query) : 0,
|
|
||||||
(position) ? internalerrposition(position) : 0));
|
|
||||||
}
|
}
|
||||||
PG_CATCH();
|
PG_CATCH();
|
||||||
{
|
{
|
||||||
@ -4427,6 +4443,8 @@ PLy_elog(int elevel, const char *fmt,...)
|
|||||||
pfree(emsg.data);
|
pfree(emsg.data);
|
||||||
if (xmsg)
|
if (xmsg)
|
||||||
pfree(xmsg);
|
pfree(xmsg);
|
||||||
|
if (tbmsg)
|
||||||
|
pfree(tbmsg);
|
||||||
PG_RE_THROW();
|
PG_RE_THROW();
|
||||||
}
|
}
|
||||||
PG_END_TRY();
|
PG_END_TRY();
|
||||||
@ -4435,6 +4453,8 @@ PLy_elog(int elevel, const char *fmt,...)
|
|||||||
pfree(emsg.data);
|
pfree(emsg.data);
|
||||||
if (xmsg)
|
if (xmsg)
|
||||||
pfree(xmsg);
|
pfree(xmsg);
|
||||||
|
if (tbmsg)
|
||||||
|
pfree(tbmsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -4458,9 +4478,47 @@ cleanup:
|
|||||||
Py_XDECREF(spidata);
|
Py_XDECREF(spidata);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the given source line as a palloc'd string
|
||||||
|
*/
|
||||||
static char *
|
static char *
|
||||||
PLy_traceback(int *xlevel)
|
get_source_line(const char *src, int lineno)
|
||||||
|
{
|
||||||
|
const char *s;
|
||||||
|
const char *next;
|
||||||
|
int current = 0;
|
||||||
|
|
||||||
|
next = src;
|
||||||
|
while (current != lineno)
|
||||||
|
{
|
||||||
|
s = next;
|
||||||
|
next = strchr(s + 1, '\n');
|
||||||
|
current++;
|
||||||
|
if (next == NULL)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current != lineno)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
while (s && isspace((unsigned char) *s))
|
||||||
|
s++;
|
||||||
|
|
||||||
|
if (next == NULL)
|
||||||
|
return pstrdup(s);
|
||||||
|
|
||||||
|
return pnstrdup(s, next - s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extract a Python traceback from the current exception.
|
||||||
|
*
|
||||||
|
* The exception error message is returned in xmsg, the traceback in
|
||||||
|
* tbmsg (both as palloc'd strings) and the traceback depth in
|
||||||
|
* tb_depth.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
PLy_traceback(char **xmsg, char **tbmsg, int *tb_depth)
|
||||||
{
|
{
|
||||||
PyObject *e,
|
PyObject *e,
|
||||||
*v,
|
*v,
|
||||||
@ -4472,6 +4530,7 @@ PLy_traceback(int *xlevel)
|
|||||||
PyObject *vob = NULL;
|
PyObject *vob = NULL;
|
||||||
char *vstr;
|
char *vstr;
|
||||||
StringInfoData xstr;
|
StringInfoData xstr;
|
||||||
|
StringInfoData tbstr;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* get the current exception
|
* get the current exception
|
||||||
@ -4483,12 +4542,18 @@ PLy_traceback(int *xlevel)
|
|||||||
*/
|
*/
|
||||||
if (e == NULL)
|
if (e == NULL)
|
||||||
{
|
{
|
||||||
*xlevel = WARNING;
|
*xmsg = NULL;
|
||||||
return NULL;
|
*tbmsg = NULL;
|
||||||
|
*tb_depth = 0;
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
PyErr_NormalizeException(&e, &v, &tb);
|
PyErr_NormalizeException(&e, &v, &tb);
|
||||||
Py_XDECREF(tb);
|
|
||||||
|
/*
|
||||||
|
* Format the exception and its value and put it in xmsg.
|
||||||
|
*/
|
||||||
|
|
||||||
e_type_o = PyObject_GetAttrString(e, "__name__");
|
e_type_o = PyObject_GetAttrString(e, "__name__");
|
||||||
e_module_o = PyObject_GetAttrString(e, "__module__");
|
e_module_o = PyObject_GetAttrString(e, "__module__");
|
||||||
@ -4521,23 +4586,124 @@ PLy_traceback(int *xlevel)
|
|||||||
appendStringInfo(&xstr, "%s.%s", e_module_s, e_type_s);
|
appendStringInfo(&xstr, "%s.%s", e_module_s, e_type_s);
|
||||||
appendStringInfo(&xstr, ": %s", vstr);
|
appendStringInfo(&xstr, ": %s", vstr);
|
||||||
|
|
||||||
|
*xmsg = xstr.data;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now format the traceback and put it in tbmsg.
|
||||||
|
*/
|
||||||
|
|
||||||
|
*tb_depth = 0;
|
||||||
|
initStringInfo(&tbstr);
|
||||||
|
/* Mimick Python traceback reporting as close as possible. */
|
||||||
|
appendStringInfoString(&tbstr, "Traceback (most recent call last):");
|
||||||
|
while (tb != NULL && tb != Py_None)
|
||||||
|
{
|
||||||
|
PyObject *volatile tb_prev = NULL;
|
||||||
|
PyObject *volatile frame = NULL;
|
||||||
|
PyObject *volatile code = NULL;
|
||||||
|
PyObject *volatile name = NULL;
|
||||||
|
PyObject *volatile lineno = NULL;
|
||||||
|
|
||||||
|
PG_TRY();
|
||||||
|
{
|
||||||
|
lineno = PyObject_GetAttrString(tb, "tb_lineno");
|
||||||
|
if (lineno == NULL)
|
||||||
|
elog(ERROR, "could not get line number from Python traceback");
|
||||||
|
|
||||||
|
frame = PyObject_GetAttrString(tb, "tb_frame");
|
||||||
|
if (frame == NULL)
|
||||||
|
elog(ERROR, "could not get frame from Python traceback");
|
||||||
|
|
||||||
|
code = PyObject_GetAttrString(frame, "f_code");
|
||||||
|
if (code == NULL)
|
||||||
|
elog(ERROR, "could not get code object from Python frame");
|
||||||
|
|
||||||
|
name = PyObject_GetAttrString(code, "co_name");
|
||||||
|
if (name == NULL)
|
||||||
|
elog(ERROR, "could not get function name from Python code object");
|
||||||
|
}
|
||||||
|
PG_CATCH();
|
||||||
|
{
|
||||||
|
Py_XDECREF(frame);
|
||||||
|
Py_XDECREF(code);
|
||||||
|
Py_XDECREF(name);
|
||||||
|
Py_XDECREF(lineno);
|
||||||
|
PG_RE_THROW();
|
||||||
|
}
|
||||||
|
PG_END_TRY();
|
||||||
|
|
||||||
|
/* The first frame always points at <module>, skip it. */
|
||||||
|
if (*tb_depth > 0)
|
||||||
|
{
|
||||||
|
char *proname;
|
||||||
|
char *fname;
|
||||||
|
char *line;
|
||||||
|
long plain_lineno;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The second frame points at the internal function, but
|
||||||
|
* to mimick Python error reporting we want to say
|
||||||
|
* <module>.
|
||||||
|
*/
|
||||||
|
if (*tb_depth == 1)
|
||||||
|
fname = "<module>";
|
||||||
|
else
|
||||||
|
fname = PyString_AsString(name);
|
||||||
|
|
||||||
|
proname = PLy_procedure_name(PLy_curr_procedure);
|
||||||
|
plain_lineno = PyInt_AsLong(lineno);
|
||||||
|
|
||||||
|
if (proname == NULL)
|
||||||
|
appendStringInfo(
|
||||||
|
&tbstr, "\n PL/Python anonymous code block, line %ld, in %s",
|
||||||
|
plain_lineno - 1, fname);
|
||||||
|
else
|
||||||
|
appendStringInfo(
|
||||||
|
&tbstr, "\n PL/Python function \"%s\", line %ld, in %s",
|
||||||
|
proname, plain_lineno - 1, fname);
|
||||||
|
|
||||||
|
if (PLy_curr_procedure)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* If we know the current procedure, append the exact
|
||||||
|
* line from the source, again mimicking Python's
|
||||||
|
* traceback.py module behavior. We could store the
|
||||||
|
* already line-split source to avoid splitting it
|
||||||
|
* every time, but producing a traceback is not the
|
||||||
|
* most important scenario to optimize for.
|
||||||
|
*/
|
||||||
|
line = get_source_line(PLy_curr_procedure->src, plain_lineno);
|
||||||
|
if (line)
|
||||||
|
{
|
||||||
|
appendStringInfo(&tbstr, "\n %s", line);
|
||||||
|
pfree(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_DECREF(frame);
|
||||||
|
Py_DECREF(code);
|
||||||
|
Py_DECREF(name);
|
||||||
|
Py_DECREF(lineno);
|
||||||
|
|
||||||
|
/* Release the current frame and go to the next one. */
|
||||||
|
tb_prev = tb;
|
||||||
|
tb = PyObject_GetAttrString(tb, "tb_next");
|
||||||
|
Assert(tb_prev != Py_None);
|
||||||
|
Py_DECREF(tb_prev);
|
||||||
|
if (tb == NULL)
|
||||||
|
elog(ERROR, "could not traverse Python traceback");
|
||||||
|
(*tb_depth)++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return the traceback. */
|
||||||
|
*tbmsg = tbstr.data;
|
||||||
|
|
||||||
Py_XDECREF(e_type_o);
|
Py_XDECREF(e_type_o);
|
||||||
Py_XDECREF(e_module_o);
|
Py_XDECREF(e_module_o);
|
||||||
Py_XDECREF(vob);
|
Py_XDECREF(vob);
|
||||||
Py_XDECREF(v);
|
Py_XDECREF(v);
|
||||||
|
|
||||||
/*
|
|
||||||
* intuit an appropriate error level based on the exception type
|
|
||||||
*/
|
|
||||||
if (PLy_exc_error && PyErr_GivenExceptionMatches(e, PLy_exc_error))
|
|
||||||
*xlevel = ERROR;
|
|
||||||
else if (PLy_exc_fatal && PyErr_GivenExceptionMatches(e, PLy_exc_fatal))
|
|
||||||
*xlevel = FATAL;
|
|
||||||
else
|
|
||||||
*xlevel = ERROR;
|
|
||||||
|
|
||||||
Py_DECREF(e);
|
Py_DECREF(e);
|
||||||
return xstr.data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* python module code */
|
/* python module code */
|
||||||
|
@ -131,6 +131,111 @@ return None
|
|||||||
|
|
||||||
SELECT valid_type('rick');
|
SELECT valid_type('rick');
|
||||||
|
|
||||||
|
/* 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();
|
||||||
|
|
||||||
|
/* 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();
|
||||||
|
|
||||||
|
/* 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();
|
||||||
|
|
||||||
|
/* 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();
|
||||||
|
|
||||||
|
/* 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();
|
||||||
|
SELECT sql_error();
|
||||||
|
SELECT python_from_sql_error();
|
||||||
|
SELECT sql_from_python_error();
|
||||||
|
|
||||||
/* check catching specific types of exceptions
|
/* check catching specific types of exceptions
|
||||||
*/
|
*/
|
||||||
CREATE TABLE specific (
|
CREATE TABLE specific (
|
||||||
|
Loading…
x
Reference in New Issue
Block a user