--
-- Here's a version of Nitasha's transaction test (which turned up lots and lots of
-- interesting issues). 
--
-- Create some tables with some data
create table tabcd (c1 text) distributed randomly;
insert into tabcd values ('a'), ('b'), ('c'), ('d');

create table t1234 (c1 integer) distributed randomly;
insert into t1234 values (1),(2),(3),(4);

create table tabcd_orig as select * from tabcd distributed randomly;
create table t1234_orig as select * from t1234 distributed randomly;

-- Intermix selects, and truncates with calls to a plpgsql function,
-- functions like this are an interesting challenge, since they get
-- dispatched with a single command-id.
--
-- This function does a series of updates.
--create language plpgsql;
CREATE OR REPLACE FUNCTION transaction_test_cursor_nit() RETURNS void AS '
DECLARE
	ref_abcd refcursor;
	ref_1234_1 refcursor;
	ref_1234_2 refcursor;
	abcd_var varchar;
	t_1234_var_1 int;
	t1234_var_2 int;
	i int;
	j int;
	arr_1234 int [4];
	arr_abcd varchar [4];
BEGIN
	arr_1234[1]:=1;
	arr_1234[2]:=2;
	arr_1234[3]:=3;
	arr_1234[4]:=4;

	open ref_1234_1 FOR SELECT c1 FROM t1234 order by 1;

	BEGIN
		j:=1;
		open ref_abcd FOR SELECT c1 FROM tabcd order by 1;
		fetch ref_abcd into abcd_var;
		while abcd_var is not null loop
		      arr_abcd[j]:=abcd_var;
		      BEGIN
				open ref_1234_2 FOR SELECT c1 FROM t1234 order by 1;
				fetch ref_1234_2 into t1234_var_2;
				i:=1;
				while t1234_var_2 is not null loop
				      update tabcd set c1=c1||t1234_var_2 where c1=arr_abcd[j];
		      		      arr_abcd[j]:=arr_abcd[j]||t1234_var_2;
				      arr_1234[i]:=arr_1234[i]+10;
		  		      i:=i+1;
				      fetch ref_1234_2 into t1234_var_2;
				end loop;
				close ref_1234_2;
			END;
			fetch ref_abcd into abcd_var;
			j:=j+1;
		end loop;
		close ref_abcd;

	        close ref_1234_1;
		open ref_1234_1 FOR SELECT c1 FROM t1234 order by 1;
		fetch ref_1234_1 into t_1234_var_1;

		while t_1234_var_1 is not null loop
--		      raise notice ''in while set index % % arg where %'', t_1234_var_1,  arr_1234[t_1234_var_1], t_1234_var_1;
		      update t1234 set c1 = arr_1234[t_1234_var_1] where c1 = t_1234_var_1;
		      fetch ref_1234_1 into t_1234_var_1;
	      end loop;

      END;
      close ref_1234_1;
END;
' LANGUAGE plpgsql MODIFIES SQL DATA;

-- encourage reader-gangs to win races:
SET gp_enable_slow_writer_testmode=on;

-- Now here's the main piece of the test.
TRUNCATE tabcd;
TRUNCATE t1234;
INSERT INTO tabcd SELECT * from tabcd_orig;
INSERT INTO t1234 SELECT * from t1234_orig;
SELECT transaction_test_cursor_nit();
SELECT * from tabcd order by 1;
SELECT * from t1234 order by 1;

BEGIN;
TRUNCATE tabcd;
TRUNCATE t1234;
INSERT INTO tabcd SELECT * from tabcd_orig;
INSERT INTO t1234 SELECT * from t1234_orig;
SELECT * from t1234 order by 1;
SELECT transaction_test_cursor_nit();
SELECT * from tabcd order by 1;
SELECT * from t1234 order by 1;
COMMIT;

BEGIN;
set transaction isolation level serializable;
TRUNCATE tabcd;
TRUNCATE t1234;
INSERT INTO tabcd SELECT * from tabcd_orig;
INSERT INTO t1234 SELECT * from t1234_orig;
SELECT * from t1234 order by 1;
SELECT transaction_test_cursor_nit();
SELECT * from tabcd order by 1;
SELECT * from t1234 order by 1;
COMMIT;

SELECT * from tabcd order by 1;
SELECT * from t1234 order by 1;

-- refresh the data 
TRUNCATE t1234;
INSERT INTO t1234 SELECT * from t1234_orig;


create table t3456 as select c1 from t1234_orig distributed by (c1);

-- Create some reader gangs.
select a.c1 from t1234 a, t3456 b where a.c1 = b.c1 order by b.c1;

BEGIN;
TRUNCATE t1234;
select a.c1 from t1234 a, t3456 b where a.c1 = b.c1 order by b.c1; -- should return 0 rows.

INSERT INTO t1234 SELECT * from t1234_orig;
TRUNCATE t3456;
select a.c1 from t1234 a, t3456 b where a.c1 = b.c1 order by b.c1; -- should return 0 rows.
ABORT;

select a.c1 from t1234 a, t3456 b where a.c1 = b.c1 order by b.c1;

CREATE OR REPLACE FUNCTION foo_func() RETURNS void AS '
BEGIN
update dtm_plpg_foo set a = a + 3;
insert into dtm_plpg_baz select dtm_plpg_bar.b from dtm_plpg_bar where dtm_plpg_bar.b >= (select max(a) from dtm_plpg_foo);
update dtm_plpg_foo set a = a + 3;
insert into dtm_plpg_baz select dtm_plpg_bar.b from dtm_plpg_bar where dtm_plpg_bar.b >= (select max(a) from dtm_plpg_foo);
END;
' LANGUAGE plpgsql MODIFIES SQL DATA;

create table dtm_plpg_foo (a int) distributed randomly;
create table dtm_plpg_bar (b int) distributed randomly;
create table dtm_plpg_baz (c int) distributed randomly;

insert into dtm_plpg_foo values (1), (2), (3), (4), (5), (6);
insert into dtm_plpg_bar values (5), (6), (7), (8), (9), (10);

insert into dtm_plpg_baz select dtm_plpg_bar.b from dtm_plpg_bar where dtm_plpg_bar.b >= (select max(a) from dtm_plpg_foo);
select * from dtm_plpg_baz order by 1;

truncate dtm_plpg_baz;
begin;
update dtm_plpg_foo set a = a + 3;
insert into dtm_plpg_baz select dtm_plpg_bar.b from dtm_plpg_bar where dtm_plpg_bar.b >= (select max(a) from dtm_plpg_foo);
select * from dtm_plpg_baz order by 1;
abort;

truncate dtm_plpg_baz;
begin;
set transaction isolation level serializable;
update dtm_plpg_foo set a = a + 3;
insert into dtm_plpg_baz select dtm_plpg_bar.b from dtm_plpg_bar where dtm_plpg_bar.b >= (select max(a) from dtm_plpg_foo);
select * from dtm_plpg_baz order by 1;
update dtm_plpg_foo set a = a + 3;
insert into dtm_plpg_baz select dtm_plpg_bar.b from dtm_plpg_bar where dtm_plpg_bar.b >= (select max(a) from dtm_plpg_foo);
select * from dtm_plpg_baz order by 1;
abort;

truncate dtm_plpg_baz;
begin;
select * from dtm_plpg_foo order by 1;
select * from dtm_plpg_bar order by 1;
select foo_func();
select * from dtm_plpg_foo order by 1;
select * from dtm_plpg_baz order by 1;
abort;

truncate dtm_plpg_baz;
begin;
set transaction isolation level serializable;
select * from dtm_plpg_foo order by 1;
select * from dtm_plpg_bar order by 1;
select foo_func();
select * from dtm_plpg_foo order by 1;
select * from dtm_plpg_baz order by 1;
abort;

DROP TABLE dtm_plpg_foo;

-- Need to check what these tests wish to validate, better to use more
-- deterministic way than sleep. GP_ENABLE_SLOW_CURSOR_TESTMODE GUC was used
-- here to slow down reader gangs, removed the same as its not good way to write
-- the tests.

BEGIN;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
CREATE TABLE dtm_plpg_foo (a int, b int);
INSERT INTO dtm_plpg_foo VALUES (0, 1);
DECLARE c1 NO SCROLL CURSOR FOR SELECT b FROM dtm_plpg_foo;
UPDATE dtm_plpg_foo SET b = 2;
FETCH ALL FROM c1;
COMMIT;

DROP TABLE dtm_plpg_foo;

BEGIN;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
CREATE TABLE dtm_plpg_foo (a int, b int);
INSERT INTO dtm_plpg_foo VALUES (0, 1);
UPDATE dtm_plpg_foo SET b = 2;
DECLARE c1 NO SCROLL CURSOR FOR SELECT b FROM dtm_plpg_foo;
UPDATE dtm_plpg_foo SET b = 3;
FETCH ALL FROM c1;
COMMIT;

DROP TABLE dtm_plpg_foo;

BEGIN;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
CREATE TABLE dtm_plpg_foo (a int, b int);
INSERT INTO dtm_plpg_foo VALUES (0, 1);
UPDATE dtm_plpg_foo SET b = 2;
DECLARE c1 NO SCROLL CURSOR FOR SELECT b FROM dtm_plpg_foo;
DELETE FROM dtm_plpg_foo;
INSERT INTO dtm_plpg_foo VALUES (0, 3);
UPDATE dtm_plpg_foo SET b = 4;
INSERT INTO dtm_plpg_foo VALUES (0, 5);
FETCH ALL FROM c1;
COMMIT;

-- Add some additional testing for combocid-issues
DROP TABLE dtm_plpg_foo;
BEGIN;
CREATE TABLE dtm_plpg_foo (C_CUSTKEY INTEGER, C_NAME VARCHAR(25), C_ADDRESS VARCHAR(40))
partition by range (c_custkey) (partition p1 start(0) end(100000) every(20000));

INSERT INTO dtm_plpg_foo SELECT * FROM dtm_plpg_foo LIMIT 10000;
COMMIT;

BEGIN;
DROP TABLE dtm_plpg_foo;
CREATE TABLE dtm_plpg_foo (C_CUSTKEY INTEGER, C_NAME VARCHAR(25), C_ADDRESS VARCHAR(40))
partition by range (c_custkey) (partition p1 start(0) end(100000) every(20000));
SELECT count(*) from dtm_plpg_foo;
INSERT INTO dtm_plpg_foo SELECT * FROM dtm_plpg_foo LIMIT 10000;
COMMIT;

BEGIN;
DROP TABLE dtm_plpg_foo;
CREATE TABLE dtm_plpg_foo (C_CUSTKEY INTEGER, C_NAME VARCHAR(25), C_ADDRESS VARCHAR(40))
partition by range (c_custkey) (partition p1 start(0) end(100000) every(20000));
DECLARE c0 CURSOR FOR SELECT * FROM dtm_plpg_foo;
INSERT INTO dtm_plpg_foo SELECT * FROM dtm_plpg_foo LIMIT 10000;
FETCH c0;
CLOSE c0;
COMMIT;

BEGIN;
DROP TABLE dtm_plpg_foo;
CREATE TABLE dtm_plpg_foo (C_CUSTKEY INTEGER, C_NAME VARCHAR(25), C_ADDRESS VARCHAR(40))
partition by range (c_custkey) (partition p1 start(0) end(100000) every(20000));
DECLARE c0 CURSOR WITH HOLD FOR SELECT * FROM dtm_plpg_foo;
INSERT INTO dtm_plpg_foo SELECT * FROM dtm_plpg_foo LIMIT 10000;
FETCH c0;
CLOSE c0;
COMMIT;
--
-- Sequence server test for an extended query
--

CREATE OR REPLACE function parse_arr (p_array text)
	returns int[] IMMUTABLE
	AS $dbvis$
	declare
		v_return int[][];
		v_text text;
		v_first int; 
	BEGIN
		if p_array is null 
		then 
			return null;
		end if;  

		v_first := 1;
		for v_text in
			select unnest(string_to_array(p_array, ';'))
		loop 
			v_return := v_return || ARRAY[(string_to_array(regexp_replace(v_text, '[)( ]', '', 'g'), ',')::int[])];
		end loop;
   
		RETURN v_return;
	END; 
$dbvis$ LANGUAGE plpgsql;

CREATE TABLE test_parse_arr (a bigserial, b int[]);

INSERT INTO test_parse_arr (b)
        SELECT parse_arr(x) FROM
                                (
                                  SELECT '(1, 2, 3)' AS x
                                  UNION ALL
                                  SELECT NULL AS x
                                  UNION ALL
                                  SELECT '(4, 5, 6)' AS x
                                  ORDER BY x
                                ) AS q;

SELECT a FROM test_parse_arr ORDER BY a;

SELECT b FROM test_parse_arr ORDER BY b;

--
-- Test if sequence server information outlives a plpgsql exception and corresponding subtransaction rollback (MPP-25193)
--

CREATE OR REPLACE FUNCTION date_converter(input_date character varying) RETURNS date
    AS $$
declare
 v_date date;
BEGIN
	return input_date::date;
exception
	when others then
		return '1900-01-01';
END;
$$
LANGUAGE plpgsql SECURITY DEFINER;

CREATE TABLE source_table
(
id int,
created text
);

insert into source_table select generate_series(1,10), '2015-01-06'::date;
insert into source_table values (11, '0000-00-00');
insert into source_table select generate_series(12,21), '2015-01-06'::date;

CREATE SEQUENCE test_seq START 1;

CREATE TABLE dest_table
(
id int,
created timestamp with time zone
);

insert into dest_table select nextval('test_seq'::regclass), date_converter(created) from source_table;
select count(1) from dest_table;


RESET gp_enable_slow_writer_testmode;

--
-- The PL/pgSQL EXCEPTION block opens a subtransaction.
-- If it's in reader, it was messing up relcache previously.
--
create table stran_foo(a, b) as values(1, 10), (2, 20);
create or replace function stran_func(a int) returns int as $$
declare
  x int;
begin
  begin
    select 1 + 2 into x;
  exception
    when division_by_zero then
      raise info 'except';
  end;
  return x;
end;
$$ language plpgsql;

create table stran_tt as select stran_func(b) from stran_foo;


--
-- Check quoting when dispatching savepoints. (Not really PL/pgSQL related,
-- but here for lack of a better place.)
--
BEGIN;
CREATE TEMPORARY TABLE savepointtest (t text);
INSERT INTO savepointtest VALUES ('before savepoints');
SAVEPOINT "evil""savepoint1";
INSERT INTO savepointtest VALUES ('after sp 1');
SAVEPOINT "evil""savepoint2";
INSERT INTO savepointtest VALUES ('after sp 2');
ROLLBACK TO SAVEPOINT "evil""savepoint2";
RELEASE SAVEPOINT "evil""savepoint1";
INSERT INTO savepointtest VALUES ('back to top transaction');
COMMIT;
SELECT * FROM savepointtest;
