-- Here we ensure that we clean up resource queue in-memory state gracefully
-- in the face of deadlocks and statement cancellations, when there is more than
-- one active portal in the session.
0:CREATE RESOURCE QUEUE rq_multi_portal WITH (active_statements = 2);
0:CREATE ROLE role_multi_portal RESOURCE QUEUE rq_multi_portal;

1:SET ROLE role_multi_portal;
2:SET ROLE role_multi_portal;

--
-- Scenario 1:
-- Multiple explicit cursors active in the same session with a deadlock.
--

1:BEGIN;
1:DECLARE c1 CURSOR FOR SELECT 1;

2:BEGIN;
2:DECLARE c2 CURSOR FOR SELECT 1;

-- There should be 2 active statements.
0:SELECT rsqcountlimit, rsqcountvalue FROM pg_resqueue_status WHERE rsqname = 'rq_multi_portal';

-- This should block as it will exceed the active statements limit.
1&:DECLARE c3 CURSOR FOR SELECT 1;

-- There should be 2 active statements.
0:SELECT rsqcountlimit, rsqcountvalue FROM pg_resqueue_status WHERE rsqname = 'rq_multi_portal';

-- This should cause a deadlock.
2:DECLARE c4 CURSOR FOR SELECT 1;

-- After the deadlock report, one session should have ERRORed out with the
-- deadlock report and aborted, while the other session should remain active
-- and idle in transaction. The active statement count should be 2, contributed
-- to by the session that is idle in transaction.
0:SELECT rsqcountlimit, rsqcountvalue FROM pg_resqueue_status WHERE rsqname = 'rq_multi_portal';
0:SELECT count(*) from pg_stat_activity WHERE query LIKE 'DECLARE c% CURSOR FOR SELECT 1;'
    AND state = 'idle in transaction';
0:SELECT count(*) from pg_stat_activity WHERE query LIKE 'DECLARE c% CURSOR FOR SELECT 1;'
    AND state = 'idle in transaction (aborted)';

-- After ending the transactions, there should be 0 active statements.
1<:
1:END;
2:END;
0:SELECT rsqcountlimit, rsqcountvalue FROM pg_resqueue_status WHERE rsqname = 'rq_multi_portal';

--
-- Scenario 2:
-- Multiple explicit cursors active in the same session with a self deadlock.
--
1:BEGIN;
1:DECLARE c1 CURSOR FOR SELECT 1;
1:DECLARE c2 CURSOR FOR SELECT 1;

-- There should be 2 active statements.
0:SELECT rsqcountlimit, rsqcountvalue FROM pg_resqueue_status WHERE rsqname = 'rq_multi_portal';

-- This should cause a self-deadlock and session 1 should error out, aborting
-- its transaction.
1:DECLARE c3 CURSOR FOR SELECT 1;

-- There should be 0 active statements following the transaction abort.
0:SELECT rsqcountlimit, rsqcountvalue FROM pg_resqueue_status WHERE rsqname = 'rq_multi_portal';

1:END;

--
-- Scenario 3:
-- Multiple explicit cursors active in the same session with cancellation.
--
1:BEGIN;
1:DECLARE c1 CURSOR FOR SELECT 1;

2:BEGIN;
2:DECLARE c2 CURSOR FOR SELECT 1;

-- This should block as it will exceed the active statements limit.
1&:DECLARE c3 CURSOR FOR SELECT 1;

-- There should be 2 active statements.
0:SELECT rsqcountlimit, rsqcountvalue FROM pg_resqueue_status WHERE rsqname = 'rq_multi_portal';

-- Cancel session 1's transaction.
0:SELECT pg_cancel_backend(pid) FROM pg_stat_activity
    WHERE query = 'DECLARE c3 CURSOR FOR SELECT 1;';

-- There should now only be one active statement, following the abort of session
-- 1's transaction. The active statement is contributed by session 2.
0:SELECT rsqcountlimit, rsqcountvalue FROM pg_resqueue_status WHERE rsqname = 'rq_multi_portal';
0:SELECT query, state from pg_stat_activity
  WHERE query = 'DECLARE c2 CURSOR FOR SELECT 1;';

-- After ending the transactions, there should be 0 active statements.
1<:
1:END;
2:END;
0:SELECT rsqcountlimit, rsqcountvalue FROM pg_resqueue_status WHERE rsqname = 'rq_multi_portal';

-- Cleanup
0:DROP ROLE role_multi_portal;
0:DROP RESOURCE QUEUE rq_multi_portal;
