--
-- SQL_PROCEDURE
--
-- Tests basic PROCEDURE functionality with SQL and PLPGSQL procedures.
--
SET citus.next_shard_id TO 100500;
CREATE SCHEMA procedure_schema;
SET SEARCH_PATH = procedure_schema;
CREATE TABLE test_table(id integer , org_id integer);
CREATE UNIQUE INDEX idx_table ON test_table(id, org_id);
SELECT create_distributed_table('test_table','id');
 create_distributed_table
---------------------------------------------------------------------

(1 row)

INSERT INTO test_table VALUES(1, 1);
-- test CREATE PROCEDURE
CREATE PROCEDURE test_procedure_delete_insert(id int, org_id int) LANGUAGE SQL AS $$
	DELETE FROM test_table;
	INSERT INTO test_table VALUES(id, org_id);
$$;
CALL test_procedure_delete_insert(2,3);
SELECT * FROM test_table ORDER BY 1, 2;
 id | org_id
---------------------------------------------------------------------
  2 |      3
(1 row)

-- commit/rollback is not allowed in procedures in SQL
-- following calls should fail
CREATE PROCEDURE test_procedure_commit(tt_id int, tt_org_id int) LANGUAGE SQL AS $$
	DELETE FROM test_table;
	COMMIT;
	INSERT INTO test_table VALUES(tt_id, -1);
	UPDATE test_table SET org_id = tt_org_id WHERE id = tt_id;
	COMMIT;
$$;
CALL test_procedure_commit(2,5);
ERROR:  COMMIT is not allowed in a SQL function
CONTEXT:  SQL function "test_procedure_commit" during startup
SELECT * FROM test_table ORDER BY 1, 2;
 id | org_id
---------------------------------------------------------------------
  2 |      3
(1 row)

CREATE PROCEDURE test_procedure_rollback(tt_id int, tt_org_id int) LANGUAGE SQL AS $$
	DELETE FROM test_table;
	ROLLBACK;
	UPDATE test_table SET org_id = tt_org_id WHERE id = tt_id;
    COMMIT;
$$;
CALL test_procedure_rollback(2,15);
ERROR:  ROLLBACK is not allowed in a SQL function
CONTEXT:  SQL function "test_procedure_rollback" during startup
SELECT * FROM test_table ORDER BY 1, 2;
 id | org_id
---------------------------------------------------------------------
  2 |      3
(1 row)

DROP PROCEDURE test_procedure_delete_insert(int, int);
DROP PROCEDURE test_procedure_commit(int, int);
DROP PROCEDURE test_procedure_rollback(int, int);
-- same tests with plpgsql
-- test CREATE PROCEDURE
CREATE PROCEDURE test_procedure_delete_insert(id int, org_id int) LANGUAGE PLPGSQL AS $$
BEGIN
	DELETE FROM test_table;
	INSERT INTO test_table VALUES(id, org_id);
END;
$$;
CALL test_procedure_delete_insert(2,3);
SELECT * FROM test_table ORDER BY 1, 2;
 id | org_id
---------------------------------------------------------------------
  2 |      3
(1 row)

-- notice that the update succeed and committed
CREATE PROCEDURE test_procedure_modify_insert(tt_id int, tt_org_id int) LANGUAGE PLPGSQL AS $$
BEGIN
	UPDATE test_table SET org_id = tt_org_id WHERE id = tt_id;
	COMMIT;
	INSERT INTO test_table VALUES (tt_id, tt_org_id);
	ROLLBACK;
END;
$$;
CALL test_procedure_modify_insert(2,12);
ERROR:  duplicate key value violates unique constraint "idx_table_100503"
DETAIL:  Key (id, org_id)=(2, 12) already exists.
CONTEXT:  while executing command on localhost:xxxxx
SQL statement "INSERT INTO test_table VALUES (tt_id, tt_org_id)"
PL/pgSQL function test_procedure_modify_insert(integer,integer) line 5 at SQL statement
SELECT * FROM test_table ORDER BY 1, 2;
 id | org_id
---------------------------------------------------------------------
  2 |     12
(1 row)

CREATE PROCEDURE test_procedure_modify_insert_commit(tt_id int, tt_org_id int) LANGUAGE PLPGSQL AS $$
BEGIN
	UPDATE test_table SET org_id = tt_org_id WHERE id = tt_id;
	COMMIT;
	INSERT INTO test_table VALUES (tt_id, tt_org_id);
	COMMIT;
END;
$$;
CALL test_procedure_modify_insert_commit(2,30);
ERROR:  duplicate key value violates unique constraint "idx_table_100503"
DETAIL:  Key (id, org_id)=(2, 30) already exists.
CONTEXT:  while executing command on localhost:xxxxx
SQL statement "INSERT INTO test_table VALUES (tt_id, tt_org_id)"
PL/pgSQL function test_procedure_modify_insert_commit(integer,integer) line 5 at SQL statement
SELECT * FROM test_table ORDER BY 1, 2;
 id | org_id
---------------------------------------------------------------------
  2 |     30
(1 row)

-- delete is commited but insert is rolled back
CREATE PROCEDURE test_procedure_rollback(tt_id int, tt_org_id int) LANGUAGE PLPGSQL AS $$
BEGIN
	DELETE FROM test_table;
	COMMIT;
	INSERT INTO test_table VALUES (tt_id, tt_org_id);
	ROLLBACK;
END;
$$;
CALL test_procedure_rollback(2,5);
SELECT * FROM test_table ORDER BY 1, 2;
 id | org_id
---------------------------------------------------------------------
(0 rows)

-- rollback is successfull when insert is on multiple rows
CREATE PROCEDURE test_procedure_rollback_2(tt_id int, tt_org_id int) LANGUAGE PLPGSQL AS $$
BEGIN
	DELETE FROM test_table;
	COMMIT;
	INSERT INTO test_table VALUES (tt_id, tt_org_id), (tt_id+1, tt_org_id+1);
	ROLLBACK;
END;
$$;
CALL test_procedure_rollback_2(12, 15);
SELECT * FROM test_table ORDER BY 1, 2;
 id | org_id
---------------------------------------------------------------------
(0 rows)

-- delete is rolled back, update is committed
CREATE PROCEDURE test_procedure_rollback_3(tt_id int, tt_org_id int) LANGUAGE PLPGSQL AS $$
BEGIN
	DELETE FROM test_table;
	ROLLBACK;
	UPDATE test_table SET org_id = tt_org_id WHERE id = tt_id;
    COMMIT;
END;
$$;
INSERT INTO test_table VALUES (1, 1), (2, 2);
CALL test_procedure_rollback_3(2,15);
SELECT * FROM test_table ORDER BY 1, 2;
 id | org_id
---------------------------------------------------------------------
  1 |      1
  2 |     15
(2 rows)

TRUNCATE test_table;
-- nested procedure calls should roll back normally
CREATE OR REPLACE PROCEDURE test_procedure_rollback(tt_id int, tt_org_id int) LANGUAGE PLPGSQL AS $$
BEGIN
    INSERT INTO test_table VALUES (tt_id+12, tt_org_id+12);
    ROLLBACK;
END;
$$;
CREATE OR REPLACE PROCEDURE test_procedure_rollback_2(tt_id int, tt_org_id int) LANGUAGE PLPGSQL AS $$
BEGIN
    INSERT INTO test_table VALUES (tt_id+2, tt_org_id+1);
    ROLLBACK;
END;
$$;
CREATE OR REPLACE PROCEDURE test_procedure(tt_id int, tt_org_id int) LANGUAGE PLPGSQL AS $$
BEGIN
    CALL test_procedure_rollback(tt_id, tt_org_id);
    CALL test_procedure_rollback_2(tt_id, tt_org_id);
    INSERT INTO test_table VALUES (tt_id+100, tt_org_id+100);
    ROLLBACK;
END;
$$;
SELECT * from test_table;
 id | org_id
---------------------------------------------------------------------
(0 rows)

call test_procedure(1,1);
call test_procedure(20, 20);
SELECT * from test_table;
 id | org_id
---------------------------------------------------------------------
(0 rows)

\set VERBOSITY terse
DROP SCHEMA procedure_schema CASCADE;
NOTICE:  drop cascades to 8 other objects
\set VERBOSITY default
RESET SEARCH_PATH;
