\set ECHO all
--
-- 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 OR REPLACE FUNCTION subtransaction_test(what_error text) 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":
            plpy.attribute_error
    except:
        exc = False
        subxact.__exit__(*sys.exc_info())
        raise
finally:
    if exc:
        subxact.__exit__(None, None, None)
$$ LANGUAGE plpythonu;

SELECT subtransaction_test(NULL);
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 OR REPLACE FUNCTION subtransaction_ctx_test(what_error text) 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":
        plpy.attribute_error
$$ LANGUAGE plpythonu;

SELECT subtransaction_ctx_test(NULL);
SELECT * FROM subtransaction_tbl;
TRUNCATE subtransaction_tbl;
SELECT subtransaction_ctx_test('SPI');
SELECT * FROM subtransaction_tbl;
TRUNCATE subtransaction_tbl;
SELECT subtransaction_ctx_test('Python');
SELECT * FROM subtransaction_tbl;
TRUNCATE subtransaction_tbl;

-- Nested subtransactions

-- CREATE OR REPLACE FUNCTION subtransaction_nested_test(swallow boolean) 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 %r" % e)
--return "ok"
--$$ LANGUAGE plpythonu;

--SELECT subtransaction_nested_test('f');
--SELECT * FROM subtransaction_tbl;
--TRUNCATE subtransaction_tbl;

--SELECT subtransaction_nested_test('t');
--SELECT * FROM subtransaction_tbl;
--TRUNCATE subtransaction_tbl;

-- Nested subtransactions that recursively call code dealing with
-- subtransactions

--CREATE OR REPLACE 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;

--SELECT subtransaction_deeply_nested_test();
--SELECT * FROM subtransaction_tbl;
--TRUNCATE subtransaction_tbl;

-- Error conditions from not opening/closing subtransactions

CREATE OR REPLACE FUNCTION subtransaction_exit_without_enter() RETURNS void
AS $$
plpy.subtransaction().__exit__(None, None, None)
$$ LANGUAGE plpythonu;

CREATE OR REPLACE FUNCTION subtransaction_enter_without_exit() RETURNS void
AS $$
plpy.subtransaction().__enter__()
$$ LANGUAGE plpythonu;

CREATE OR REPLACE 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 OR REPLACE FUNCTION subtransaction_enter_twice() RETURNS void
AS $$
plpy.subtransaction().__enter__()
plpy.subtransaction().__enter__()
$$ LANGUAGE plpythonu;

CREATE OR REPLACE 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 OR REPLACE 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 OR REPLACE FUNCTION subtransaction_enter_subtransaction_in_with() RETURNS void
AS $$
with plpy.subtransaction() as s:
    s.__enter__()
$$ LANGUAGE plpythonu;

CREATE OR REPLACE FUNCTION subtransaction_exit_subtransaction_in_with() RETURNS void
AS $$
with plpy.subtransaction() as s:
    s.__exit__(None, None, None)
$$ LANGUAGE plpythonu;

SELECT subtransaction_exit_without_enter();
SELECT subtransaction_enter_without_exit();
SELECT subtransaction_exit_twice();
SELECT subtransaction_enter_twice();
SELECT subtransaction_exit_same_subtransaction_twice();
SELECT subtransaction_enter_same_subtransaction_twice();
SELECT subtransaction_enter_subtransaction_in_with();
SELECT subtransaction_exit_subtransaction_in_with();

-- Make sure we don't get a "current transaction is aborted" error
SELECT 1 as test;

-- Mix explicit subtransactions and normal SPI calls

--CREATE OR REPLACE 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;

--SELECT subtransaction_mix_explicit_and_implicit();
--SELECT * FROM subtransaction_tbl;
--TRUNCATE subtransaction_tbl;

-- Alternative method names for Python <2.6

CREATE OR REPLACE FUNCTION subtransaction_alternative_names() RETURNS void
AS $$
s = plpy.subtransaction()
s.enter()
s.exit(None, None, None)
$$ LANGUAGE plpythonu;

SELECT subtransaction_alternative_names();

-- try/catch inside a subtransaction block

CREATE OR REPLACE 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;

SELECT try_catch_inside_subtransaction();
SELECT * FROM subtransaction_tbl;
TRUNCATE subtransaction_tbl;

--ALTER TABLE subtransaction_tbl ADD PRIMARY KEY (i);

--CREATE OR REPLACE 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;

--SELECT pk_violation_inside_subtransaction();
--SELECT * FROM subtransaction_tbl;

DROP TABLE subtransaction_tbl;
