--
-- TRANSACTIONS
--
BEGIN;
SELECT * 
   INTO TABLE xacttest
   FROM aggtest;
INSERT INTO xacttest (a, b) VALUES (777, 777.777);
END;
-- should retrieve one value--
SELECT a FROM xacttest WHERE a > 100;
  a  
-----
 777
(1 row)

BEGIN;
CREATE TABLE disappear (a int4);
DELETE FROM aggtest;
-- should be empty
SELECT * FROM aggtest;
 a | b 
---+---
(0 rows)

ABORT;
-- should not exist 
SELECT oid FROM pg_class WHERE relname = 'disappear';
 oid 
-----
(0 rows)

-- should have members again 
SELECT * FROM aggtest;
  a  |    b    
-----+---------
  56 |     7.8
 100 |  99.097
   0 | 0.09561
  42 |  324.78
(4 rows)

-- Read-only tests
CREATE TABLE writetest (a int);
CREATE TEMPORARY TABLE temptest (a int);
SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY;
DROP TABLE writetest; -- fail
ERROR:  transaction is read-only
INSERT INTO writetest VALUES (1); -- fail
ERROR:  transaction is read-only
SELECT * FROM writetest; -- ok
 a 
---
(0 rows)

DELETE FROM temptest; -- ok
UPDATE temptest SET a = 0 WHERE a = 1 AND writetest.a = temptest.a; -- ok
PREPARE test AS UPDATE writetest SET a = 0; -- ok
EXECUTE test; -- fail
ERROR:  transaction is read-only
SELECT * FROM writetest, temptest; -- ok
 a | a 
---+---
(0 rows)

CREATE TABLE test AS SELECT * FROM writetest; -- fail
ERROR:  transaction is read-only
START TRANSACTION READ WRITE;
DROP TABLE writetest; -- ok
COMMIT;
-- Subtransactions, basic tests
-- create & drop tables
SET SESSION CHARACTERISTICS AS TRANSACTION READ WRITE;
CREATE TABLE foobar (a int);
BEGIN;
	CREATE TABLE foo (a int);
	SAVEPOINT one;
		DROP TABLE foo;
		CREATE TABLE bar (a int);
	ROLLBACK TO SAVEPOINT one;
	RELEASE SAVEPOINT one;
	SAVEPOINT two;
		CREATE TABLE baz (a int);
	RELEASE SAVEPOINT two;
	drop TABLE foobar;
	CREATE TABLE barbaz (a int);
COMMIT;
-- should exist: barbaz, baz, foo
SELECT * FROM foo;		-- should be empty
 a 
---
(0 rows)

SELECT * FROM bar;		-- shouldn't exist
ERROR:  relation "bar" does not exist
SELECT * FROM barbaz;	-- should be empty
 a 
---
(0 rows)

SELECT * FROM baz;		-- should be empty
 a 
---
(0 rows)

-- inserts
BEGIN;
	INSERT INTO foo VALUES (1);
	SAVEPOINT one;
		INSERT into bar VALUES (1);
ERROR:  relation "bar" does not exist
	ROLLBACK TO one;
	RELEASE SAVEPOINT one;
	SAVEPOINT two;
		INSERT into barbaz VALUES (1);
	RELEASE two;
	SAVEPOINT three;
		SAVEPOINT four;
			INSERT INTO foo VALUES (2);
		RELEASE SAVEPOINT four;
	ROLLBACK TO SAVEPOINT three;
	RELEASE SAVEPOINT three;
	INSERT INTO foo VALUES (3);
COMMIT;
SELECT * FROM foo;		-- should have 1 and 3
 a 
---
 1
 3
(2 rows)

SELECT * FROM barbaz;	-- should have 1
 a 
---
 1
(1 row)

-- test whole-tree commit
BEGIN;
	SAVEPOINT one;
		SELECT foo;
ERROR:  column "foo" does not exist
	ROLLBACK TO SAVEPOINT one;
	RELEASE SAVEPOINT one;
	SAVEPOINT two;
		CREATE TABLE savepoints (a int);
		SAVEPOINT three;
			INSERT INTO savepoints VALUES (1);
			SAVEPOINT four;
				INSERT INTO savepoints VALUES (2);
				SAVEPOINT five;
					INSERT INTO savepoints VALUES (3);
				ROLLBACK TO SAVEPOINT five;
COMMIT;
COMMIT;		-- should not be in a transaction block
WARNING:  there is no transaction in progress
SELECT * FROM savepoints;
 a 
---
 1
 2
(2 rows)

-- test whole-tree rollback
BEGIN;
	SAVEPOINT one;
		DELETE FROM savepoints WHERE a=1;
	RELEASE SAVEPOINT one;
	SAVEPOINT two;
		DELETE FROM savepoints WHERE a=1;
		SAVEPOINT three;
			DELETE FROM savepoints WHERE a=2;
ROLLBACK;
COMMIT;		-- should not be in a transaction block
WARNING:  there is no transaction in progress
		
SELECT * FROM savepoints;
 a 
---
 1
 2
(2 rows)

-- test whole-tree commit on an aborted subtransaction
BEGIN;
	INSERT INTO savepoints VALUES (4);
	SAVEPOINT one;
		INSERT INTO savepoints VALUES (5);
		SELECT foo;
ERROR:  column "foo" does not exist
COMMIT;
SELECT * FROM savepoints;
 a 
---
 1
 2
(2 rows)

BEGIN;
	INSERT INTO savepoints VALUES (6);
	SAVEPOINT one;
		INSERT INTO savepoints VALUES (7);
	RELEASE SAVEPOINT one;
	INSERT INTO savepoints VALUES (8);
COMMIT;
-- rows 6 and 8 should have been created by the same xact
SELECT a.xmin = b.xmin FROM savepoints a, savepoints b WHERE a.a=6 AND b.a=8;
 ?column? 
----------
 t
(1 row)

-- rows 6 and 7 should have been created by different xacts
SELECT a.xmin = b.xmin FROM savepoints a, savepoints b WHERE a.a=6 AND b.a=7;
 ?column? 
----------
 f
(1 row)

BEGIN;
	INSERT INTO savepoints VALUES (9);
	SAVEPOINT one;
		INSERT INTO savepoints VALUES (10);
	ROLLBACK TO SAVEPOINT one;
		INSERT INTO savepoints VALUES (11);
COMMIT;
SELECT a FROM savepoints WHERE a in (9, 10, 11);
 a  
----
  9
 11
(2 rows)

-- rows 9 and 11 should have been created by different xacts
SELECT a.xmin = b.xmin FROM savepoints a, savepoints b WHERE a.a=9 AND b.a=11;
 ?column? 
----------
 f
(1 row)

BEGIN;
	INSERT INTO savepoints VALUES (12);
	SAVEPOINT one;
		INSERT INTO savepoints VALUES (13);
		SAVEPOINT two;
			INSERT INTO savepoints VALUES (14);
	ROLLBACK TO SAVEPOINT one;
		INSERT INTO savepoints VALUES (15);
		SAVEPOINT two;
			INSERT INTO savepoints VALUES (16);
			SAVEPOINT three;
				INSERT INTO savepoints VALUES (17);
COMMIT;
SELECT a FROM savepoints WHERE a BETWEEN 12 AND 17;
 a  
----
 12
 15
 16
 17
(4 rows)

BEGIN;
	INSERT INTO savepoints VALUES (18);
	SAVEPOINT one;
		INSERT INTO savepoints VALUES (19);
		SAVEPOINT two;
			INSERT INTO savepoints VALUES (20);
	ROLLBACK TO SAVEPOINT one;
		INSERT INTO savepoints VALUES (21);
	ROLLBACK TO SAVEPOINT one;
		INSERT INTO savepoints VALUES (22);
COMMIT;
SELECT a FROM savepoints WHERE a BETWEEN 18 AND 22;
 a  
----
 18
 22
(2 rows)

DROP TABLE savepoints;
-- only in a transaction block:
SAVEPOINT one;
ERROR:  SAVEPOINT may only be used in transaction blocks
ROLLBACK TO SAVEPOINT one;
ERROR:  ROLLBACK TO SAVEPOINT may only be used in transaction blocks
RELEASE SAVEPOINT one;
ERROR:  RELEASE SAVEPOINT may only be used in transaction blocks
-- Only "rollback to" allowed in aborted state
BEGIN;
  SAVEPOINT one;
  SELECT 0/0;
ERROR:  division by zero
  SAVEPOINT two;    -- ignored till the end of ...
ERROR:  current transaction is aborted, commands ignored until end of transaction block
  RELEASE SAVEPOINT one;      -- ignored till the end of ...
ERROR:  current transaction is aborted, commands ignored until end of transaction block
  ROLLBACK TO SAVEPOINT one;
  SELECT 1;
 ?column? 
----------
        1
(1 row)

COMMIT;
SELECT 1;			-- this should work
 ?column? 
----------
        1
(1 row)

-- check non-transactional behavior of cursors
BEGIN;
	DECLARE c CURSOR FOR SELECT unique2 FROM tenk1;
	SAVEPOINT one;
		FETCH 10 FROM c;
 unique2 
---------
       0
       1
       2
       3
       4
       5
       6
       7
       8
       9
(10 rows)

	ROLLBACK TO SAVEPOINT one;
		FETCH 10 FROM c;
 unique2 
---------
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
(10 rows)

	RELEASE SAVEPOINT one;
	FETCH 10 FROM c;
 unique2 
---------
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
(10 rows)

	CLOSE c;
	DECLARE c CURSOR FOR SELECT unique2/0 FROM tenk1;
	SAVEPOINT two;
		FETCH 10 FROM c;
ERROR:  division by zero
	ROLLBACK TO SAVEPOINT two;
	-- c is now dead to the world ...
		FETCH 10 FROM c;
ERROR:  portal "c" cannot be run
	ROLLBACK TO SAVEPOINT two;
	RELEASE SAVEPOINT two;
	FETCH 10 FROM c;
ERROR:  portal "c" cannot be run
COMMIT;
--
-- Check that "stable" functions are really stable.  They should not be
-- able to see the partial results of the calling query.  (Ideally we would
-- also check that they don't see commits of concurrent transactions, but
-- that's a mite hard to do within the limitations of pg_regress.)
--
select * from xacttest;
  a  |    b    
-----+---------
  56 |     7.8
 100 |  99.097
   0 | 0.09561
  42 |  324.78
 777 | 777.777
(5 rows)

create or replace function max_xacttest() returns smallint language sql as
'select max(a) from xacttest' stable;
begin;
update xacttest set a = max_xacttest() + 10 where a > 0;
select * from xacttest;
  a  |    b    
-----+---------
   0 | 0.09561
 787 |     7.8
 787 |  99.097
 787 |  324.78
 787 | 777.777
(5 rows)

rollback;
-- But a volatile function can see the partial results of the calling query
create or replace function max_xacttest() returns smallint language sql as
'select max(a) from xacttest' volatile;
begin;
update xacttest set a = max_xacttest() + 10 where a > 0;
select * from xacttest;
  a  |    b    
-----+---------
   0 | 0.09561
 787 |     7.8
 797 |  99.097
 807 |  324.78
 817 | 777.777
(5 rows)

rollback;
-- Now the same test with plpgsql (since it depends on SPI which is different)
create or replace function max_xacttest() returns smallint language plpgsql as
'begin return max(a) from xacttest; end' stable;
begin;
update xacttest set a = max_xacttest() + 10 where a > 0;
select * from xacttest;
  a  |    b    
-----+---------
   0 | 0.09561
 787 |     7.8
 787 |  99.097
 787 |  324.78
 787 | 777.777
(5 rows)

rollback;
create or replace function max_xacttest() returns smallint language plpgsql as
'begin return max(a) from xacttest; end' volatile;
begin;
update xacttest set a = max_xacttest() + 10 where a > 0;
select * from xacttest;
  a  |    b    
-----+---------
   0 | 0.09561
 787 |     7.8
 797 |  99.097
 807 |  324.78
 817 | 777.777
(5 rows)

rollback;
-- test case for problems with dropping an open relation during abort
BEGIN;
	savepoint x;
		CREATE TABLE koju (a INT UNIQUE);
NOTICE:  CREATE TABLE / UNIQUE will create implicit index "koju_a_key" for table "koju"
		INSERT INTO koju VALUES (1);
		INSERT INTO koju VALUES (1);
ERROR:  duplicate key violates unique constraint "koju_a_key"
	rollback to x;
	CREATE TABLE koju (a INT UNIQUE);
NOTICE:  CREATE TABLE / UNIQUE will create implicit index "koju_a_key" for table "koju"
	INSERT INTO koju VALUES (1);
	INSERT INTO koju VALUES (1);
ERROR:  duplicate key violates unique constraint "koju_a_key"
ROLLBACK;
DROP TABLE foo;
DROP TABLE baz;
DROP TABLE barbaz;
