Step 1: Create the Package Specification
This defines the "Public Interface"—the two commands your team is allowed to use.
CREATE OR REPLACE PACKAGE admin_kill_tools AS
/****************************************************************
* ADMIN_KILL_TOOLS
* ----------------
* Centralized session management for Oracle RDS.
* Includes safety rails for background processes and system users.
****************************************************************/
-- Sniper Mode: Kill a specific SID/Serial
PROCEDURE kill_session(
p_sid IN NUMBER,
p_serial IN NUMBER
);
-- Purge Mode: Kill ALL sessions for a specific user
PROCEDURE purge_user(
p_username IN VARCHAR2
);
END admin_kill_tools;
/
Step 2: Create the Package Body
This contains the "Brain." It hides the complexity of checking for scheduler jobs, verifying background processes, and performing the kill.
CREATE OR REPLACE PACKAGE BODY admin_kill_tools AS
-- PRIVATE HELPER: The actual logic that performs the kill
-- (Not accessible directly by users)
PROCEDURE do_kill_logic(p_sid NUMBER, p_serial NUMBER, p_program VARCHAR2, p_status VARCHAR2) IS
l_job_name VARCHAR2(128);
l_check NUMBER;
BEGIN
DBMS_OUTPUT.PUT_LINE(' ..Targeting SID: ' || p_sid || ' | Serial: ' || p_serial);
-- 1. SCHEDULER JOB CHECK
IF p_program LIKE '%(J%' THEN
BEGIN
SELECT job_name INTO l_job_name FROM dba_scheduler_running_jobs WHERE session_id = p_sid;
DBMS_OUTPUT.PUT_LINE('Detected Scheduler Job: ' || l_job_name);
DBMS_SCHEDULER.STOP_JOB(l_job_name, force => TRUE);
DBMS_OUTPUT.PUT_LINE('STOP_JOB executed.');
RETURN;
EXCEPTION WHEN OTHERS THEN NULL;
END;
END IF;
-- 2. ALREADY DEAD CHECK
IF p_status = 'KILLED' THEN
DBMS_OUTPUT.PUT_LINE('Already KILLED status. Escaling to NUCLEAR PROCESS kill.');
rdsadmin.rdsadmin_util.kill(p_sid, p_serial, 'PROCESS');
DBMS_OUTPUT.PUT_LINE('OS Process terminated.');
RETURN;
END IF;
-- 3. STANDARD KILL
BEGIN
rdsadmin.rdsadmin_util.kill(p_sid, p_serial, 'IMMEDIATE');
DBMS_OUTPUT.PUT_LINE('KILL IMMEDIATE sent.');
-- Wait 2 seconds and check
DBMS_LOCK.SLEEP(2);
SELECT COUNT(*) INTO l_check FROM v$session WHERE sid = p_sid AND serial# = p_serial;
IF l_check > 0 THEN
rdsadmin.rdsadmin_util.kill(p_sid, p_serial, 'PROCESS');
DBMS_OUTPUT.PUT_LINE('Session stubborn -> OS Process terminated.');
END IF;
EXCEPTION WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Error during kill: ' || SQLERRM);
END;
END do_kill_logic;
-------------------------------------------------------------------------
-- PUBLIC PROCEDURE 1: KILL_SESSION (Sniper)
-------------------------------------------------------------------------
PROCEDURE kill_session(p_sid NUMBER, p_serial NUMBER) IS
CURSOR c_target IS
SELECT sid, serial#, username, type, program, status
FROM v$session WHERE sid = p_sid AND serial# = p_serial;
r c_target%ROWTYPE;
BEGIN
OPEN c_target; FETCH c_target INTO r;
IF c_target%NOTFOUND THEN
DBMS_OUTPUT.PUT_LINE('Session ' || p_sid || ',' || p_serial || ' not found.');
CLOSE c_target; RETURN;
END IF;
CLOSE c_target;
-- Safety: Background Process
IF r.type = 'BACKGROUND' THEN
DBMS_OUTPUT.PUT_LINE('BLOCKED: Cannot kill background process.');
RETURN;
END IF;
-- Safety: Self-Kill
IF r.sid = SYS_CONTEXT('USERENV', 'SID') THEN
DBMS_OUTPUT.PUT_LINE('BLOCKED: You cannot kill your own current session.');
RETURN;
END IF;
DBMS_OUTPUT.PUT_LINE('=== SNIPER KILL START ===');
do_kill_logic(r.sid, r.serial#, r.program, r.status);
DBMS_OUTPUT.PUT_LINE('=== FINISHED ===');
END kill_session;
-------------------------------------------------------------------------
-- PUBLIC PROCEDURE 2: PURGE_USER (Shotgun)
-------------------------------------------------------------------------
PROCEDURE purge_user(p_username IN VARCHAR2) IS
v_user_clean VARCHAR2(128) := UPPER(TRIM(p_username));
v_count NUMBER := 0;
BEGIN
DBMS_OUTPUT.PUT_LINE('=== PURGE USER START: ' || v_user_clean || ' ===');
-- Safety: Privileged Accounts
IF v_user_clean IN ('SYS','SYSTEM','RDSADMIN','DBSNMP','XDB','AUDSYS') THEN
DBMS_OUTPUT.PUT_LINE('BLOCKED: Cannot mass-kill privileged account: ' || v_user_clean);
RETURN;
END IF;
-- Safety: Self-Purge
IF v_user_clean = USER THEN
DBMS_OUTPUT.PUT_LINE('BLOCKED: You cannot purge yourself ('||USER||') while connected.');
RETURN;
END IF;
-- Loop and Kill
FOR r IN (
SELECT sid, serial#, username, type, program, status
FROM v$session
WHERE username = v_user_clean
AND type != 'BACKGROUND'
AND sid != SYS_CONTEXT('USERENV', 'SID')
) LOOP
do_kill_logic(r.sid, r.serial#, r.program, r.status);
v_count := v_count + 1;
END LOOP;
IF v_count = 0 THEN
DBMS_OUTPUT.PUT_LINE('No active sessions found for user: ' || v_user_clean);
ELSE
DBMS_OUTPUT.PUT_LINE('=== PURGE COMPLETE. Killed ' || v_count || ' sessions. ===');
END IF;
END purge_user;
END admin_kill_tools;
/
Step 3: Deployment & Usage
Once you compile the package above, here is how you use it.
1. Enable Output (Important!) Since this runs inside a procedure, you must turn on output to see the logs.
SET SERVEROUTPUT ON;
2. Scenario A: A user "SCOTT" is blocking a deployment.
EXEC admin_kill_tools.purge_user('SCOTT');
3. Scenario B: A specific rogue query (SID 123, Serial 99).
EXEC admin_kill_tools.kill_session(123, 99);
Step 4: Permissions (Optional but Recommended)
If you want other team members to use this without giving them the powerful DBA role or ALTER SYSTEM privilege, grant them execute access on this package only.
GRANT EXECUTE ON admin_kill_tools TO junior_dba_user;
Would you like a companion "View" that shows exactly who is currently blocking others, so you know who to kill?
/
DECLARE
v_username VARCHAR2(128) := 'SCOTT'; -- Manually set username
v_sql_id VARCHAR2(13) := 'abc123xyz4567'; -- Manually set sql_id
BEGIN
FOR session_rec IN (
SELECT sid, serial#
FROM v$session
WHERE username = UPPER(v_username)
AND sql_id = v_sql_id
AND status = 'ACTIVE'
)
LOOP
BEGIN
rdsadmin.rdsadmin_util.kill(
sid => session_rec.sid,
serial => session_rec.serial#
);
DBMS_OUTPUT.PUT_LINE('Session killed successfully: SID=' || session_rec.sid || ', SERIAL#=' || session_rec.serial#);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Error killing session SID=' || session_rec.sid || ', SERIAL#=' || session_rec.serial# || ': ' || SQLERRM);
CONTINUE;
END;
END LOOP;
IF SQL%ROWCOUNT = 0 THEN
DBMS_OUTPUT.PUT_LINE('No active sessions found for username=' || UPPER(v_username) || ' and sql_id=' || v_sql_id);
END IF;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Unexpected error: ' || SQLERRM);
END;
/
No comments:
Post a Comment