diff --git a/internal/db/schema/migrations/oss/postgres/29/01_cancel_session_null_fkey.up.sql b/internal/db/schema/migrations/oss/postgres/29/01_cancel_session_null_fkey.up.sql index 40f11144fc..ffd89cb3cb 100644 --- a/internal/db/schema/migrations/oss/postgres/29/01_cancel_session_null_fkey.up.sql +++ b/internal/db/schema/migrations/oss/postgres/29/01_cancel_session_null_fkey.up.sql @@ -9,6 +9,7 @@ drop function cancel_session(in sessionId text); -- Sessions can progress directly to terminated without going through the canceling state -- cancel_session will insert a cancel state for the session, if there's isn't -- a canceled or terminated state already. It's used by cancel_session_with_null_fk. +-- Replaced in 91/07_cancel_session_trigger.up.sql create function cancel_session(in sessionId text) returns void as $$ declare diff --git a/internal/db/schema/migrations/oss/postgres/91/07_cancel_session_trigger.up.sql b/internal/db/schema/migrations/oss/postgres/91/07_cancel_session_trigger.up.sql new file mode 100644 index 0000000000..6f0f74e5e3 --- /dev/null +++ b/internal/db/schema/migrations/oss/postgres/91/07_cancel_session_trigger.up.sql @@ -0,0 +1,24 @@ +-- Copyright (c) HashiCorp, Inc. +-- SPDX-License-Identifier: BUSL-1.1 + +begin; + + -- Replaces function from 29/01_cancel_session_null_fkey.up.sql + drop function cancel_session; + create function cancel_session(sessionid text) returns void + as $$ + declare + rows_affected numeric; + begin + insert into session_state(session_id, state) + values (sessionId, 'canceling'); + exception when unique_violation + or foreign_key_violation + or check_violation + then + -- Do nothing. Any one of these violations would indicate that the session + -- either already is canceled, or is in a terminated state and cannot + -- be canceled. + end; + $$ language plpgsql; +commit; diff --git a/internal/db/sqltest/tests/session/cancel_session.sql b/internal/db/sqltest/tests/session/cancel_session.sql new file mode 100644 index 0000000000..51b02c6bd7 --- /dev/null +++ b/internal/db/sqltest/tests/session/cancel_session.sql @@ -0,0 +1,114 @@ +-- Copyright (c) HashiCorp, Inc. +-- SPDX-License-Identifier: BUSL-1.1 + +begin; + select plan(22); + + -- Canceling a terminated session should cause no errors, but should not change state. + prepare cancel_terminated as + select cancel_session('s1_____cindy'); + -- to start s1_____cindy should have 2 states, and the terminated should be the active state. + select is(count(*), 2::bigint) + from session_state + where session_id = 's1_____cindy'; + select is(count(*), 1::bigint) + from session_state + where session_id = 's1_____cindy' + and state = 'terminated' + and upper(active_time_range) is null; + -- now attempt to cancel + select lives_ok('cancel_terminated'); + -- there should be no changes to the state + select is(count(*), 2::bigint) + from session_state + where session_id = 's1_____cindy'; + select is(count(*), 1::bigint) + from session_state + where session_id = 's1_____cindy' + and state = 'terminated' + and upper(active_time_range) is null; + + -- Canceling a canceling session should cause no errors, but should not change state. + prepare cancel_canceling as + select cancel_session('s1_____ciara'); + -- to start s1_____ciara should have 2 states, and the canceling should be the active state. + select is(count(*), 2::bigint) + from session_state + where session_id = 's1_____ciara'; + select is(count(*), 1::bigint) + from session_state + where session_id = 's1_____ciara' + and state = 'canceling' + and upper(active_time_range) is null; + -- now attempt to cancel + select lives_ok('cancel_canceling'); + -- there should be no changes to the state + select is(count(*), 2::bigint) + from session_state + where session_id = 's1_____ciara'; + select is(count(*), 1::bigint) + from session_state + where session_id = 's1_____ciara' + and state = 'canceling' + and upper(active_time_range) is null; + + -- Canceling an active session should cause no errors and should result the state changing to canceling. + prepare cancel_active as + select cancel_session('s1_____carly'); + -- to start s1_____carly should have 2 states, and the active should be the active state. + select is(count(*), 2::bigint) + from session_state + where session_id = 's1_____carly'; + select is(count(*), 1::bigint) + from session_state + where session_id = 's1_____carly' + and state = 'active' + and upper(active_time_range) is null; + -- now attempt to cancel + select lives_ok('cancel_active'); + -- there should be a new state and active should no long be the active state. + select is(count(*), 3::bigint) + from session_state + where session_id = 's1_____carly'; + select is(count(*), 0::bigint) + from session_state + where session_id = 's1_____carly' + and state = 'active' + and upper(active_time_range) is null; + select is(count(*), 1::bigint) + from session_state + where session_id = 's1_____carly' + and state = 'canceling' + and upper(active_time_range) is null; + + -- Canceling a pending session should cause no errors and should result the state changing to canceling. + prepare cancel_pending as + select cancel_session('s1_____clare'); + -- to start s1_____clare should have 2 states, and the active should be the active state. + select is(count(*), 1::bigint) + from session_state + where session_id = 's1_____clare'; + select is(count(*), 1::bigint) + from session_state + where session_id = 's1_____clare' + and state = 'pending' + and upper(active_time_range) is null; + -- now attempt to cancel + select lives_ok('cancel_pending'); + -- there should be a new state and pending should no long be the active state. + select is(count(*), 2::bigint) + from session_state + where session_id = 's1_____clare'; + select is(count(*), 0::bigint) + from session_state + where session_id = 's1_____clare' + and state = 'pending' + and upper(active_time_range) is null; + select is(count(*), 1::bigint) + from session_state + where session_id = 's1_____clare' + and state = 'canceling' + and upper(active_time_range) is null; + + select * from finish(); +rollback;