Stored procedure to kill Oracle sessions by user name - oracle

This has been asked several times on the web, but none of the answers I found on Google could solve my problem.
I'd like to create a stored procedure that kills Oracle sessions. The only parameter the procedure accepts is the user name of the owner of the sessions to kill.
This is my attempt:
CREATE OR REPLACE PROCEDURE kill_user_session (
username IN NVARCHAR2
)
AS
stmt varchar(5000);
CURSOR get_sessions
IS
SELECT s.sid sid, s.serial# ser
FROM v$session s, v$process p
WHERE s.username = username
AND p.addr(+) = s.paddr;
session_rec get_sessions%ROWTYPE;
BEGIN
FOR session_rec in get_sessions LOOP
BEGIN
stmt := 'ALTER SYSTEM KILL SESSION ''' || session_rec.sid || ',' || session_rec.ser || '''';
EXECUTE IMMEDIATE stmt;
--EXCEPTION WHEN others THEN
-- dbms_output.put_line('Error killing session: ' || stmt);
-- dbms_output.put_line(SQLERRM);
END;
END LOOP;
END;
/
If I execute it like this
exec kill_user_session('myuser');
I get an error:
ERROR at line 1:
ORA-00911: invalid character
ORA-06512: at "SYSTEM.KILL_USER_SESSION", line 17
ORA-06512: at line 1
If I change line 17 to
stmt := 'ALTER SYSTEM KILL SESSION "' || session_rec.sid || ',' || session_rec.ser || '"';
then I get
ERROR at line 1:
ORA-00026: missing or invalid session ID
ORA-06512: at "SYSTEM.KILL_USER_SESSION", line 17
ORA-06512: at line 1
I have granted the following rights to SYSTEM:
GRANT SELECT ON v$session TO SYSTEM;
GRANT ALTER SYSTEM TO SYSTEM;
But that didn't help.
EDIT: I added a dbms_output.putline to print out the stmt variable before executing it. Here's an example:
ALTER SYSTEM KILL SESSION "34,91"
If I execute this statement outside of the stored procedure, it runs fine and the session is killed. But not from inside.

First off, you shouldn't have a semicolon in the SQL statement you pass to EXECUTE IMMEDIATE. That would cause the ORA-00911 error.
Second, it is always helpful to print out the dynamic SQL statement that you've built before you execute it. The extra semicolon may be the only error. Or there may be other errors. Those errors will inevitably be easier to debug if you can see the SQL statement that you've built (and execute it separately) rather than just looking at the code that builds the statement.

You can use this procedure:
{
CREATE PROCEDURE kill_user_session (users IN VARCHAR2)
AS
stmt VARCHAR (5000);
CURSOR get_sessions
IS
SELECT s.sid sid, s.serial# ser
FROM v$session s, v$process p
WHERE s.username = users AND p.addr(+) = s.paddr;
BEGIN
FOR session_rec IN get_sessions
LOOP
BEGIN
stmt := 'ALTER SYSTEM KILL SESSION ''' || session_rec.sid || ',' || session_rec.ser || '''' || ' IMMEDIATE';
--dbms_output.put_line(stmt);
BEGIN
EXECUTE IMMEDIATE stmt;
--EXCEPTION WHEN others THEN
-- dbms_output.put_line('Error killing session: ' || stmt);
-- dbms_output.put_line(SQLERRM);
EXCEPTION
WHEN OTHERS
THEN
-- You probably need to log this error properly here.
-- I will just re-raise it.
CONTINUE;
END;
END;
END LOOP;
END;
/
}

Related

How to delete sequences and procedures during logoff trigger?

Could you please help me in a unique situation I am in. I am receiving "ORA-30511: invalid DDL operation in system triggers" when dropping sequences and procedures during logoff trigger.
I need to delete tables, sequences and procedures of users before logoff event happens. I am writing the table details in DB_OBJECTS table upon create using a separate trigger. Below is my logoff trigger - could you please help me where I am doing wrong. Dropping tables is working fine in the below code. Only Dropping sequences and procedures is giving me "ORA-30511: invalid DDL operation in system triggers" error.
CREATE OR REPLACE TRIGGER DELETE_BEFORE_LOGOFF
BEFORE LOGOFF ON DATABASE
DECLARE
USER_ID NUMBER := SYS_CONTEXT('USERENV', 'SESSIONID');
BEGIN
FOR O IN (SELECT USER, OBJECT_NAME, OBJECT_TYPE
FROM DB_OBJECTS WHERE SID = USER_ID
AND USERNAME = USER AND SYSDATE > CREATED_DTTM) LOOP
IF O.OBJECT_TYPE = 'TABLE' THEN
EXECUTE IMMEDIATE 'DROP TABLE ' || O.USER || '.' || O.OBJECT_NAME || ' CASCADE CONSTRAINTS';
ELSIF O.OBJECT_TYPE = 'SEQUENCE' THEN
EXECUTE IMMEDIATE 'DROP SEQUENCE ' || O.USER || '.' || O.OBJECT_NAME;
ELSIF O.OBJECT_TYPE = 'PROCEDURE' THEN
EXECUTE IMMEDIATE 'DROP PROCEDURE ' || O.USER || '.' || O.OBJECT_NAME;
END IF;
END LOOP;
EXCEPTION WHEN NO_DATA_FOUND THEN NULL;
END;
/
That's a simple one.
Error code: ORA-30511
Description: invalid DDL operation in system triggers
Cause: An attempt was made to perform an invalid DDL operation in a system trigger. Most DDL operations currently are not supported in system triggers. The only currently supported DDL operations are table operations and ALTER/COMPILE operations.
Action: Remove invalid DDL operations in system triggers.
That's why only
Dropping tables is working fine
succeeded.
Therefore, you can't do that using trigger.
You asked (in a comment) how to drop these objects, then. Manually, as far as I can tell. Though, that's quite unusual - what if someone accidentally logs off? You'd drop everything they created. If you use that schema for educational purposes (for example, every student gets their own schema), then you could create a "clean-up" script you'd run once class is over. Something like this:
SET SERVEROUTPUT ON;
DECLARE
l_user VARCHAR2 (30) := 'SCOTT';
l_str VARCHAR2 (200);
BEGIN
IF USER = l_user
THEN
FOR cur_r IN (SELECT object_name, object_type
FROM user_objects
WHERE object_name NOT IN ('EMP',
'DEPT',
'BONUS',
'SALGRADE'))
LOOP
BEGIN
l_str :=
'drop '
|| cur_r.object_type
|| ' "'
|| cur_r.object_name
|| '"';
DBMS_OUTPUT.put_line (l_str);
EXECUTE IMMEDIATE l_str;
EXCEPTION
WHEN OTHERS
THEN
NULL;
END;
END LOOP;
END IF;
END;
/
PURGE RECYCLEBIN;
It is far from being perfect; I use it to clean up my Scott schema I use to answer questions on various sites so - once it becomes a mess, I run that PL/SQL code several times (because of possible foreign key constraint).
Other option is to keep a create user script(s) (along with all grant statements) and - once class is over - drop existing user and simply recreate it.
Or, if that user contains some pre-built tables, keep export file (I mean, result of data pump export) and import it after the user is dropped.
There are various options - I don't know whether I managed to guess correctly, but now you have something to think about.

How to ignore compilation error in Oracle pl/sql when script is executed by certain types of user

I wanted a simple method of automatically confirming which customer a schema belongs to when my team members log into SQLPlus, in order to avoid mistakes.
I configured glogin.sql as follows and it works great when logged in as a schema owner:
set echo off
set serveroutput on
set linesize 200
Declare
sysDesc varchar2(255);
BEGIN
if USER like '%SCHEMAOWNER%' then
select DESCRIPTION into sysDesc from SCHEMA_INFO;
else
sysDesc := '(DBA User)';
end if;
dbms_output.put_line('*******************************************************************');
dbms_output.put_line('WARNING - THIS IS A CUSTOMER SYSTEM!!!');
dbms_output.put_line('*******************************************************************' || chr(13) || chr(10));
dbms_output.put_line('- Description: ' || SysDesc);
dbms_output.put_line('- User: ' || USER);
dbms_output.put_line('- Database: ' || sys_context('USERENV','DB_NAME') || chr(13) || chr(10));
dbms_output.put_line('*******************************************************************');
END;
/
Sometimes, however, we need to log in as a DBA user. DBA users can't see the SCHEMA_INFO table, so the user gets this error instead:
ERROR at line 9:
ORA-06550: line 9, column 43:
PL/SQL: ORA-00942: table or view does not exist
ORA-06550: line 9, column 5:
PL/SQL: SQL Statement ignored
I tried to add an Exception handler but then realised that it was a compilation error that I was getting so this was of no use. I also tried to use execute immediate, but couldn't find a way to use the output of the select statement.
Does anyone know how I can ignore the compilation error or run a different script/block depending on the user type?
Create a custom exception for a non-existent table exception and then catch it if it occurs:
set echo off
set serveroutput on
set linesize 200
Declare
sysDesc varchar2(255);
BEGIN
if USER like '%SCHEMAOWNER%' then
DECLARE
table_or_view_does_not_exist EXCEPTION;
PRAGMA EXCEPTION_INIT( table_or_view_does_not_exist, -942 );
BEGIN
EXECUTE IMMEDIATE 'select DESCRIPTION from SCHEMA_INFO' into sysDesc;
EXCEPTION
WHEN table_or_view_does_not_exist THEN
-- You should validate that the user is a DBA here.
sysDesc := '(DBA User)';
END;
else
sysDesc := '(DBA User)';
end if;
dbms_output.put_line('*******************************************************************');
dbms_output.put_line('WARNING - THIS IS A CUSTOMER SYSTEM!!!');
dbms_output.put_line('*******************************************************************' || chr(13) || chr(10));
dbms_output.put_line('- Description: ' || SysDesc);
dbms_output.put_line('- User: ' || USER);
dbms_output.put_line('- Database: ' || sys_context('USERENV','DB_NAME') || chr(13) || chr(10));
dbms_output.put_line('*******************************************************************');
END;
/

Error code ORA-00933 for my stored procedure

I get the following error message in a stored procedure that I created:
ORA-00933: SQL command not properly ended
ORA-06512: at line 11
I have tried Googling it, but could not find anything applicable as it tells my to try to eliminate any 'ORDER BY'.
declare
cursor a_tab is
select table_name
from all_tables
where owner = 'OFFERINGWORKSPACE'
and (TABLE_NAME like 'EBA_%' or TABLE_NAME = 'SURVEY_2.0');
v_tab_name varchar2(500);
begin
open a_tab;
loop
fetch a_tab into v_tab_name;
exit when a_tab%notfound;
EXECUTE IMMEDIATE 'delete ' || v_tab_name;
end Loop;
close a_tab;
open a_tab;
Loop
fetch a_tab into v_tab_name;
Exit when a_tab%notfound;
EXECUTE IMMEDIATE 'insert into ' || v_tab_name || '(select * from OFFERINGWORKSPACE.'||v_tab_name ||')';
End Loop;
Close a_tab;
End;
There is a clue in your cursor query:
... TABLE_NAME = 'SURVEY_2.0');
The period in that breaks the database object naming rules:
Nonquoted identifiers can only contain alphanumeric characters from your database character set and the underscore (_). Database links can contain periods (.) and "at" signs (#).
so that table name must be a quoted identifier. You therefore need to quote it in your statements:
EXECUTE IMMEDIATE 'delete "' || v_tab_name || '"';
and
EXECUTE IMMEDIATE 'insert into "' || v_tab_name
|| '"(select * from OFFERINGWORKSPACE."'||v_tab_name ||"')';
db<>fiddle showing the errors from those commands (simplified to one schema, and static SQL), and how adding the double quotes fixes them.

Exit execution when error occurs PL/SQL

I would like to know, how can I exit the execution when an error occurs. In Microsoft SQL Server there is a RETURN clause, which does the work. But I would like to know similar functionality in Oracle. I am using Oracle Sql Developer. Here is the script I am using:
First block throws error due to Unique Key Violation, even though it throws error the execution goes to next block and executes the insert statement. I want to end the execution or exit at first block of code itself.
Please help me to write the code.
First anonymous PL/SQL block:
set serveroutput on;
BEGIN
insert into test values(1);
insert into test values(1);
COMMIT;
dbms_output.put_line('PRINT SOMETHING 1');
EXCEPTION
WHEN OTHERS THEN
if sqlcode <> 0
then
dbms_output.put_line(SQLCODE || ' ' || SQLERRM);
RAISE;
end if;
return;
END;
/
Second anonymous PL/SQL block:
set serveroutput on;
BEGIN
insert into test values(6);
COMMIT;
dbms_output.put_line('PRINT SOMETHING');
EXCEPTION
WHEN OTHERS THEN
if sqlcode <> 0
then
dbms_output.put_line(SQLCODE || ' ' || SQLERRM);
RAISE;
end if;
return;
END;
/
If you create a stored procedure, you have more control and can exit whenever you like with a return statement.
So create a stored proc:
create or replace procedure myProc as
begin
dbms_ouput.put_line('i am here');
return;
dbms_ouput.put_line('and not here');
end;
Then in sqlplus or developer:
exec myProc();
You can nest the blocks into a single 'program unit'.
In this way an exception in the first block will stop the whole program unit from executing, rather than just being limited in scope to the first block.
set serveroutput on;
BEGIN
BEGIN
insert into test values(1);
insert into test values(1);
COMMIT;
dbms_output.put_line('PRINT SOMETHING 1');
EXCEPTION
WHEN OTHERS THEN
if sqlcode <> 0
then
dbms_output.put_line(SQLCODE || ' ' || SQLERRM);
RAISE;
end if;
return;
END;
BEGIN
insert into test values(6);
COMMIT;
dbms_output.put_line('PRINT SOMETHING');
EXCEPTION
WHEN OTHERS THEN
if sqlcode <> 0
then
dbms_output.put_line(SQLCODE || ' ' || SQLERRM);
RAISE;
end if;
return;
END;
END;
/
You should be able to use "exit" - see the Oracle documentation here: http://docs.oracle.com/cd/B19306_01/server.102/b14357/ch12023.htm
Note that this will end your SqlPlus session, but I don't know of another way of doing it aside from using a single block or stored procedure.
Another useful statement is:
WHENEVER SQLERROR EXIT SQL.SQLCODE
Oracle documentation: http://docs.oracle.com/cd/B19306_01/server.102/b14357/ch12052.htm
Thanks for your valuable comments.
JoshL, i tried using EXIT but i am ending up with error. Please correct my code( I am new to PL/SQL). "WHENEVER SQLERROR EXIT" is good to use but my issue is that I use these sql scriptsd in InstallShield, so InstallShield installers does not recognize these statements and throws error.
set serveroutput on;
BEGIN
insert into test values(1);
insert into test values(1);
COMMIT;
dbms_output.put_line('PRINT SOMETHING 1');
EXCEPTION
WHEN OTHERS THEN
if sqlcode <> 0
then
dbms_output.put_line(SQLCODE || ' ' || SQLERRM);
RAISE;
exit;
end if;
END;
/
The EXIT command is only for use within a loop in PL/SQL. The EXIT command leaves the loop at that point. If you use the EXIT command outside a loop in PL/SQL the compiler throws an error.
The EXIT command in SQLPlus exits the SQLPlus session.
This is confusing, because they are two different Oracle products. SQL*Plus can run PL/SQL and the EXIT statement is a valid statement in both products, but with different contexts.

error while truncating tables from oracle db

I am doing something like this in a procedure to clear all data from all tables in my database.
LOOP
dbms_utility.exec_ddl_statement('alter table ' || c.owner || '.' || c.table_name || ' disable constraint ' || c.constraint_name);
END LOOP;
.
.
.
LOOP
EXECUTE IMMEDIATE 'TRUNCATE TABLE ' || t.table_name ;
END LOOP;
Now , this throws the following error :
ORA-03291: Invalid truncate option - missing STORAGE keyword
ORA-06512: at "MYSCHEMA.CLEAR_DATA", line 15
ORA-06512: at line 2
Process exited.
Disconnecting from the database MYDB.
Why is a storage keyword mandatory? I thought DROP STORAGE was the default.
Even specifying storage close, as in,
EXECUTE IMMEDIATE 'TRUNCATE TABLE ' || t.table_name || 'DROP STORAGE';
doesn't help. The error is the same.
I thought it might have something to do with foreign constraints on some tables. Hence, the 'disable constraint' earlier in the script
I would suggest that you build the command you are executing in a string variable, output it using dbms_output, then execute it. This way you will see exactly what it is trying to execute that is generating the error.
One thing that could be causing this error is if you have a table name with a space in it (yes, it's possible). The solution if that is the case is to wrap the table name in double quotes.
dev> create table "dave exp" (x number);
Table created.
dev> truncate table dave exp;
truncate table dave exp
*
ERROR at line 1:
ORA-03291: Invalid truncate option - missing STORAGE keyword
dev> truncate table "dave exp";
Table truncated.
Change your program:
put your truncate command in a PL/SQL variable prior to execution
add an exception handler that outputs the truncate statements via dbms_output or utl_file (fflush after each one) when you encounter an exception:
LOOP
BEGIN
...
v_sql := 'TRUNCATE TABLE ' || t.table_name ;
EXECUTE IMMEDIATE v_sql;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line(SQLERRM);
dbms_output.put_line(v_sql);
END;
END LOOP;
This should show you the statement causing the issue.
truncate table Table_NAME drop storage;

Resources