| -- |
| -- Test explicit subtransactions |
| -- |
| |
| -- Test table to see if transactions get properly rolled back |
| |
| CREATE TABLE subtransaction_tbl ( |
| i integer |
| ); |
| |
| 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; |
| |
| SELECT subtransaction_ctx_test(); |
| 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 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 as e: |
| if not swallow: |
| raise |
| plpy.notice("Swallowed %s(%r)" % (e.__class__.__name__, e.args[0])) |
| return "ok" |
| $$ LANGUAGE plpythonu; |
| |
| SELECT subtransaction_nested_test(); |
| 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 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 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; |
| |
| CREATE FUNCTION subtransaction_exit_subtransaction_in_with() RETURNS void |
| AS $$ |
| try: |
| with plpy.subtransaction() as s: |
| s.__exit__(None, None, None) |
| except ValueError as e: |
| raise ValueError(e) |
| $$ 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 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 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 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 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; |
| |
| -- 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; |
| |
| 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; |
| |
| 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; |
| |
| 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; |
| |
| SELECT cursor_in_subxact(); |
| SELECT cursor_aborted_subxact(); |
| SELECT cursor_plan_aborted_subxact(); |
| SELECT cursor_close_aborted_subxact(); |
| |
| -- error report test in subtransaction begin |
| -- prepare function |
| CREATE OR REPLACE FUNCTION test_func() RETURNS SETOF int AS |
| $$ |
| plpy.execute("select pg_backend_pid()") |
| |
| for i in range(0, 5): |
| yield (i) |
| |
| $$ LANGUAGE plpythonu; |
| |
| -- inject fault and wait for trigger |
| select gp_inject_fault_infinite('begin_internal_sub_transaction', 'error', 1); |
| SELECT test_func(); |
| select gp_wait_until_triggered_fault('begin_internal_sub_transaction', 1, 1); |
| select gp_inject_fault('begin_internal_sub_transaction', 'reset', 1); |
| |
| SELECT test_func(); |
| |
| DROP FUNCTION test_func(); |