SET standard_conforming_strings TO ON;
SET client_encoding TO 'UTF-8';
CREATE OR REPLACE FUNCTION committed_nothing() RETURNS VOID LANGUAGE python AS
$python$
def main():
	with xact():
		pass
$python$;
SELECT committed_nothing();
 committed_nothing 
-------------------
 
(1 row)

CREATE OR REPLACE FUNCTION aborted_nothing() RETURNS VOID LANGUAGE python AS
$python$
def main():
	try:
		with xact():
			raise ValueError
	except ValueError:
		pass
$python$;
SELECT aborted_nothing();
 aborted_nothing 
-----------------
 
(1 row)

CREATE OR REPLACE FUNCTION cant_reuse() RETURNS VOID LANGUAGE python AS
$python$
x = xact()

def main():
	with x:
		pass
$python$;
SELECT cant_reuse();
 cant_reuse 
------------
 
(1 row)

SELECT cant_reuse();
ERROR:  function's "main" raised a Python exception
CONTEXT:  [exception from Python]
Traceback (most recent call last):
   File "public.cant_reuse()", line 5, in main
    with x:
 RuntimeError: Postgres.Transaction already used

[public.cant_reuse()]
DROP TABLE IF EXISTS pyxact_test;
NOTICE:  table "pyxact_test" does not exist, skipping
CREATE TABLE pyxact_test (i int);
-- Move everything through the SQL function to avoid
-- a dependency on Postgres.Statement [That will be checked later]
CREATE OR REPLACE FUNCTION insert_val(int) RETURNS VOID LANGUAGE SQL AS
$$
INSERT INTO pyxact_test VALUES ($1);
$$;
CREATE OR REPLACE FUNCTION delete_val(int) RETURNS VOID LANGUAGE SQL AS
$$
DELETE FROM pyxact_test WHERE i = $1;
$$;
CREATE OR REPLACE FUNCTION check_commit() RETURNS VOID LANGUAGE python AS
$python$
from Postgres import CONST, Function, Type

rp = Type(CONST['REGPROCEDUREOID'])
ins = Function(rp('insert_val(int)'))

def main():
	with xact():
		ins(123)
$python$;
SELECT check_commit();
 check_commit 
--------------
 
(1 row)

SELECT i, i = 123 AS should_be_true FROM pyxact_test;
  i  | should_be_true 
-----+----------------
 123 | t
(1 row)

CREATE OR REPLACE FUNCTION check_delete() RETURNS VOID LANGUAGE python AS
$python$
from Postgres import CONST, Function, Type

rp = Type(CONST['REGPROCEDUREOID'])
delv = Function(rp('delete_val(int)'))

def main():
	with xact():
		delv(123)
$python$;
SELECT check_delete();
 check_delete 
--------------
 
(1 row)

SELECT COUNT(i) AS should_be_zero FROM pyxact_test;
 should_be_zero 
----------------
              0
(1 row)

CREATE OR REPLACE FUNCTION check_abort() RETURNS VOID LANGUAGE python AS
$python$
from Postgres import CONST, Function, Type

rp = Type(CONST['REGPROCEDUREOID'])
ins = Function(rp('insert_val(int)'))

def main():
	try:
		with xact():
			ins(321)
			raise ValueError()
	except ValueError:
		pass
$python$;
SELECT check_abort();
 check_abort 
-------------
 
(1 row)

SELECT COUNT(i) AS should_be_zero FROM pyxact_test;
 should_be_zero 
----------------
              0
(1 row)

CREATE OR REPLACE FUNCTION check_abort_from_error() RETURNS VOID LANGUAGE python AS
$python$
from Postgres import CONST, Function, Type

rp = Type(CONST['REGPROCEDUREOID'])
ins = Function(rp('insert_val(int)'))

def main():
	try:
		with xact():
			ins(1013)
			rp('foobaZZZzzz(int17)')
	except:
		pass
	else:
		raise RuntimeError("bad regprocedure did not raise exception")
$python$;
SELECT check_abort_from_error();
 check_abort_from_error 
------------------------
 
(1 row)

SELECT COUNT(i) AS should_be_zero FROM pyxact_test;
 should_be_zero 
----------------
              0
(1 row)

CREATE OR REPLACE FUNCTION entered_xact() RETURNS VOID LANGUAGE python AS
$python$
def main():
	x = xact()
	x.__enter__()
$python$;
SELECT entered_xact();
ERROR:  function failed to exit all subtransactions
DETAIL:  The 1 remaining subtransactions have been aborted.
CREATE OR REPLACE FUNCTION entered_exc_xact() RETURNS VOID LANGUAGE python AS
$python$
def main():
	x = xact()
	x.__enter__()
	raise ValueError("xact WARNING should get thrown")
$python$;
SELECT entered_exc_xact();
WARNING:  function failed to exit all subtransactions
DETAIL:  The 1 remaining subtransactions have been aborted.
ERROR:  function's "main" raised a Python exception
CONTEXT:  [exception from Python]
Traceback (most recent call last):
   File "public.entered_exc_xact()", line 5, in main
    raise ValueError("xact WARNING should get thrown")
 ValueError: xact WARNING should get thrown

[public.entered_exc_xact()]
CREATE OR REPLACE FUNCTION exited_xact() RETURNS VOID LANGUAGE python AS
$python$
def main():
	x = xact()
	x.__exit__(None, None, None)
$python$;
SELECT exited_xact();
ERROR:  function's "main" raised a Python exception
CONTEXT:  [exception from Python]
Traceback (most recent call last):
   File "public.exited_xact()", line 4, in main
    x.__exit__(None, None, None)
 RuntimeError: Postgres.Transaction not entered

[public.exited_xact()]
CREATE OR REPLACE FUNCTION out_of_order() RETURNS VOID LANGUAGE python AS
$python$
def main():
	x1 = xact()
	x2 = xact()
	x1.__enter__()
	x2.__enter__()
	x1.__exit__(None, None, None)
$python$;
SELECT out_of_order();
WARNING:  function failed to exit all subtransactions
DETAIL:  The 2 remaining subtransactions have been aborted.
ERROR:  out-of-order commit attempted on subtransaction 1
DETAIL:  Subtransaction 2 was expected to exit next.
CONTEXT:  [exception from Python]
Traceback (most recent call last):
   File "public.out_of_order()", line 7, in main
    x1.__exit__(None, None, None)
 Postgres.Exception

[public.out_of_order()]
-- Finally, test the pl_ist_count global's recursion safety(handler called by handler).
CREATE OR REPLACE FUNCTION xsubsub() RETURNS VOID LANGUAGE python AS
$python$
def main():
	x = xact()
	x.__enter__()
	raise ValueError("eek")
	# Should cause an error, but trap in outer and recover accordingly.
$python$;
CREATE OR REPLACE FUNCTION call_broken_but_recover() RETURNS VOID LANGUAGE python AS
$python$
from Postgres import CONST, Function, Type, Exception as pg_exc

rp = Type(CONST['REGPROCEDUREOID'])
ins = Function(rp('insert_val(int)'))
s = Function(rp('entered_exc_xact()'))

def main():
	with xact():
		ins(8080)
		try:
			with xact():
				s()
		except pg_exc:
			pass
$python$;
SELECT call_broken_but_recover();
WARNING:  function failed to exit all subtransactions
DETAIL:  The 1 remaining subtransactions have been aborted.
 call_broken_but_recover 
-------------------------
 
(1 row)

SELECT i FROM pyxact_test WHERE i = 8080;
  i   
------
 8080
(1 row)

-- Same as above, but do the insert in a block (might be a useless test..)
CREATE OR REPLACE FUNCTION call_broken_but_recover_noxact() RETURNS VOID LANGUAGE python AS
$python$
from Postgres import CONST, Function, Type, Exception as pg_exc

rp = Type(CONST['REGPROCEDUREOID'])
s = Function(rp('entered_exc_xact()'))

def main():
	try:
		with xact():
			s()
	except pg_exc:
		pass
$python$;
BEGIN;
SELECT call_broken_but_recover_noxact();
WARNING:  function failed to exit all subtransactions
DETAIL:  The 1 remaining subtransactions have been aborted.
 call_broken_but_recover_noxact 
--------------------------------
 
(1 row)

INSERT INTO pyxact_test VALUES (2020);
COMMIT;
SELECT i FROM pyxact_test WHERE i = 2020;
  i   
------
 2020
(1 row)

