ALTER SYSTEM is not executing in jruby - oracle

I am trying to execute plsql block in which I have ALTER USER and then ALTER SYSTEM statements one after other. ALTER USER is executing but ALTER SYSTEM is not.
My code does the following:
1. Connects to the database using jdbc driver.
2. Changes the login user of a particular user (here I am using ALTER USER statement).
3. Kills the session of that particular user before dropping it (here I use ALTER SYSTEM statement which is not running).
Below is the code fragment which is not running for me.
users = ['ABC', 'XYZ']
$users.each do|x|
if( is_usr?("#{x}") )
puts "Working on #{x}"
stmt = <<-EOF
DECLARE
id NUMber(10);
s_num NUMBER(10);
CURSOR cur IS
SELECT sid, serial#
FROM v$session WHERE username like ('#{x}');
BEGIN
OPEN cur;
LOOP
FETCH cur INTO id, s_num;
dbms_output.put_line(id||' '||s_num);
EXECUTE IMMEDIATE 'ALTER USER #{x} IDENTIFIED BY dummypass';
EXECUTE IMMEDIATE 'ALTER SYSTEM KILL SESSION ''' ||id|| ',' ||s_num||'''';
EXIT when sess_cur%notfound;
END LOOP;
CLOSE cur;
END;
EOF
puts "#{stmt}"
plsql = #conn.create_statement
plsql.execute_update(stmt)
else
puts "#{x} does not exist"
end
end
Output looks like this:
Working on ABC
DECLARE
id NUMber(10);
s_num NUMBER(10);
CURSOR cur IS
SELECT sid, serial#
FROM v$session WHERE username like ('ABC');
BEGIN
OPEN cur;
LOOP
FETCH cur INTO id, s_num;
dbms_output.put_line(id||' '||s_num);
EXECUTE IMMEDIATE 'ALTER USER ABC IDENTIFIED BY dummypass';
EXECUTE IMMEDIATE 'ALTER SYSTEM KILL SESSION ''' ||id|| ',' ||s_num||'''';
EXIT when sess_cur%notfound;
END LOOP;
CLOSE cur;
END;
Working on XYZ
DECLARE
id NUMber(10);
s_num NUMBER(10);
CURSOR cur IS
SELECT sid, serial#
FROM v$session WHERE username like ('XYZ');
BEGIN
OPEN cur;
LOOP
FETCH cur INTO id, s_num;
dbms_output.put_line(id||' '||s_num);
EXECUTE IMMEDIATE 'ALTER USER XYZ IDENTIFIED BY dummypass';
EXECUTE IMMEDIATE 'ALTER SYSTEM KILL SESSION ''' ||id|| ',' ||s_num||'''';
EXIT when sess_cur%notfound;
END LOOP;
CLOSE cur;
END;
Observation after running this script:
1. I am able to login to db as ABC and XYZ with 'dummypass' as password. Hence I know that ALTER USER statement has been run successfully.
2. I still see all the same session ids and serial#s for 'ABC' and 'XYZ'. Hence I know that ALTER SYSTEM statement did not run.
I am struggling in fixing this one since past 3-days. Please help me in fixing this. Thanks in advance.

ALTER SYSTEM KILL SESSION does not instantly get rid of the session. It only marks it for being killed. Normally a background process will get rid of it in a minute. If there are many uncommitted changes it can take a very long time to rollback everything.
There is no way to speed up the process. The best you can do it ignore those sessions with a predicate like where v$session.status <> 'KILLED'.

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.

ORA-01031: insufficient privileges when executing rebuild index from Stored Procedure [duplicate]

Here is the definition of the stored procedure:
CREATE OR REPLACE PROCEDURE usp_dropTable(schema VARCHAR, tblToDrop VARCHAR) IS
BEGIN
DECLARE v_cnt NUMBER;
BEGIN
SELECT COUNT(*)
INTO v_cnt
FROM all_tables
WHERE owner = schema
AND table_name = tblToDrop;
IF v_cnt > 0 THEN
EXECUTE IMMEDIATE('DROP TABLE someschema.some_table PURGE');
END IF;
END;
END;
Here is the call:
CALL usp_dropTable('SOMESCHEMA', 'SOME_TABLE');
For some reason, I keep getting insufficient privileges error for the EXECUTE IMMEDIATE command. I looked online and found out that the insufficient privileges error usually means the oracle user account does not have privileges for the command used in the query that is passes, which in this case is DROP. However, I have drop privileges. I am really confused and I can't seem to find a solution that works for me.
Thanks to you in advance.
SOLUTION:
As Steve mentioned below, Oracle security model is weird in that it needs to know explicitly somewhere in the procedure what kind of privileges to use. The way to let Oracle know that is to use AUTHID keyword in the CREATE OR REPLACE statement. If you want the same level of privileges as the creator of the procedure, you use AUTHID DEFINER. If you want Oracle to use the privileges of the user currently running the stored procedure, you want to use AUTHID CURRENT_USER. The procedure declaration looks as follows:
CREATE OR REPLACE PROCEDURE usp_dropTable(schema VARCHAR, tblToDrop VARCHAR)
AUTHID CURRENT_USER IS
BEGIN
DECLARE v_cnt NUMBER;
BEGIN
SELECT COUNT(*)
INTO v_cnt
FROM all_tables
WHERE owner = schema
AND table_name = tblToDrop;
IF v_cnt > 0 THEN
EXECUTE IMMEDIATE('DROP TABLE someschema.some_table PURGE');
END IF;
END;
END;
Thank you everyone for responding. This was definitely very annoying problem to get to the solution.
Oracle's security model is such that when executing dynamic SQL using Execute Immediate (inside the context of a PL/SQL block or procedure), the user does not have privileges to objects or commands that are granted via role membership. Your user likely has "DBA" role or something similar. You must explicitly grant "drop table" permissions to this user. The same would apply if you were trying to select from tables in another schema (such as sys or system) - you would need to grant explicit SELECT privileges on that table to this user.
You should use this example with AUTHID CURRENT_USER :
CREATE OR REPLACE PROCEDURE Create_sequence_for_tab (VAR_TAB_NAME IN VARCHAR2)
AUTHID CURRENT_USER
IS
SEQ_NAME VARCHAR2 (100);
FINAL_QUERY VARCHAR2 (100);
COUNT_NUMBER NUMBER := 0;
cur_id NUMBER;
BEGIN
SEQ_NAME := 'SEQ_' || VAR_TAB_NAME;
SELECT COUNT (*)
INTO COUNT_NUMBER
FROM USER_SEQUENCES
WHERE SEQUENCE_NAME = SEQ_NAME;
DBMS_OUTPUT.PUT_LINE (SEQ_NAME || '>' || COUNT_NUMBER);
IF COUNT_NUMBER = 0
THEN
--DBMS_OUTPUT.PUT_LINE('DROP SEQUENCE ' || SEQ_NAME);
-- EXECUTE IMMEDIATE 'DROP SEQUENCE ' || SEQ_NAME;
-- ELSE
SELECT 'CREATE SEQUENCE COMPTABILITE.' || SEQ_NAME || ' START WITH ' || ROUND (DBMS_RANDOM.VALUE (100000000000, 999999999999), 0) || ' INCREMENT BY 1'
INTO FINAL_QUERY
FROM DUAL;
DBMS_OUTPUT.PUT_LINE (FINAL_QUERY);
cur_id := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.parse (cur_id, FINAL_QUERY, DBMS_SQL.v7);
DBMS_SQL.CLOSE_CURSOR (cur_id);
-- EXECUTE IMMEDIATE FINAL_QUERY;
END IF;
COMMIT;
END;
/
you could use "AUTHID CURRENT_USER" in body of your procedure definition for your requirements.
Alternatively you can grant the user DROP_ANY_TABLE privilege if need be and the procedure will run as is without the need for any alteration. Dangerous maybe but depends what you're doing :)

Stored procedure to kill Oracle sessions by user name

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;
/
}

Dropping connected users in Oracle database

I want to drop some users in Oracle DB using sqlplus but I am getting error:
SQL> DROP USER test CASCADE;
DROP USER test CASCADE
*
ERROR at line 1:
ORA-01940: cannot drop a user that is currently connected
I followed the link in SO to find out the sessions - Dropping a connected user from an Oracle 10g database schema
But when I ran the command I am not getting any results:
SQL> select sid,serial# from v$session where username = 'test';
no rows selected
Please help me how to drop users in this case.
Users are all capitals in v$session (and data dictionary views). If you match with capitals you should find your session to kill.
SELECT s.sid, s.serial#, s.status, p.spid
FROM v$session s, v$process p
WHERE s.username = 'TEST' --<<<--
AND p.addr(+) = s.paddr
/
Pass actual SID and SERIAL# values for user TEST then drop user...:
ALTER SYSTEM KILL SESSION '<SID>, <SERIAL>'
/
Solution :
login as sysdaba:
sqlplus / as sysdba
then:
sql>Shutdown immediate;
sql>startup restrict;
sql>drop user TEST cascade;
If you want to re-activate DB normally either reset the server or :
sql>Shutdown immediate;
sql>startup;
:)
Issue has been fixed using below procedure :
DECLARE
v_user_exists NUMBER;
user_name CONSTANT varchar2(20) := 'SCOTT';
BEGIN
LOOP
FOR c IN (SELECT s.sid, s.serial# FROM v$session s WHERE upper(s.username) = user_name)
LOOP
EXECUTE IMMEDIATE
'alter system kill session ''' || c.sid || ',' || c.serial# || ''' IMMEDIATE';
END LOOP;
BEGIN
EXECUTE IMMEDIATE 'drop user ' || user_name || ' cascade';
EXCEPTION WHEN OTHERS THEN
IF (SQLCODE = -1940) THEN
NULL;
ELSE
RAISE;
END IF;
END;
BEGIN
SELECT COUNT(*) INTO v_user_exists FROM dba_users WHERE username = user_name;
EXIT WHEN v_user_exists = 0;
END;
END LOOP;
END;
/
Do a query:
SELECT * FROM v$session s;
Find your user and do the next query (with appropriate parameters):
ALTER SYSTEM KILL SESSION '<SID>, <SERIAL>';
If you use RAC then you need to use GV$* views instead V$*.
Try to find your session by
select * from gv$session where username = 'test';
and then you can kill the session by
alter system kill session 'sid, serial#, #inst_id' immediate;
This can be as simple as:
SQL> ALTER SYSTEM ENABLE RESTRICTED SESSION;
SQL> DROP USER test CASCADE;
SQL> ALTER SYSTEM DISABLE RESTRICTED SESSION;
go to services in administrative tools and select oracleserviceSID and restart it
I was trying to follow the flow described here - but haven't luck to completely kill the session.. Then I fond additional step here:
http://wyding.blogspot.com/2013/08/solution-for-ora-01940-cannot-drop-user.html
What I did:
1. select 'alter system kill session ''' || sid || ',' || serial# || ''';' from v$session where username = '<your_schema>'; - as described below.Out put will be something like this:alter system kill session '22,15' immediate;
2. alter system disconnect session '22,15' IMMEDIATE ; - 22-sid, 15-serial - repeat the command for each returned session from previous command
3. Repeat steps 1-2 while select... not return an empty table
4. Call
drop user...
What was missed - call alter system disconnect session '22,15' IMMEDIATE ; for each of session returned by select 'alter system kill session '..
Sometimes Oracle drop user takes long time to execute. In that case user might be connected to the database. Better you can kill user session and drop the user.
SQL> select 'alter system kill session ''' || sid || ',' || serial# || ''' immediate;' from v$session where username ='&USERNAME';
SQL> DROP USER barbie CASCADE;
I had the same problem, Oracle config in default affects letter register. In exact my Scheme_Name was written all Capital letters. You can see your Scheme_Name on "Other Users" tab, if you are using Oracle S
Basically I believe that killing all sessions should be the solution, but...
I found similar discussion - https://community.oracle.com/thread/1054062 to my problem and that was I had no sessions for that users, but I still received the error. I tried also second the best answer:
sql>Shutdown immediate;
sql>startup restrict;
sql>drop user TEST cascade;
What worked for me at the end was to login as the user, drop all tables manually - select for creating drop statements is
select 'drop table ' || TABLE_NAME || ';' from user_tables;
(Needs to be re-run several times because of references)
I have no idea how is that related, I dropped also functions and sequences (because that was all I had in schema)
When I did that and I logged off, I had several sessions in v$session table and when I killed those I was able to drop user.
My DB was still started in restricted mode (not sure if important or not).
Might help someone else.
BTW: my Oracle version is Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
//'SYS' is username from where you wanted to kill session'
SELECT * FROM DBA_TAB_PRIVS WHERE GRANTEE = 'SYS';
**Step 1:**
CREATE OR REPLACE PROCEDURE sys.kill_session(p_sid NUMBER, p_serial NUMBER)
AS
v_user VARCHAR2(30);
BEGIN
SELECT MAX(username)
INTO v_user
FROM v$session
WHERE sid = p_sid
AND serial# = p_serial;
**Step 2**
create or replace procedure kill_session( p_sid in number, p_serial# in number)
is v_count pls_integer;
BEGIN
select count(*) into v_count
from V$session
where username = 'SYS'
and sid = p_sid
and serial# = p_serial# ;
if ( v_count = 1 )
then
execute immediate '
alter system kill session ''' ||
to_char(p_sid,'999999')||','||
to_char(p_serial#,'999999')||'''';
else
raise_application_error( -20001,
'You do not own session ''' ||
p_sid || ',' || p_serial# ||
'''' );
end if;
END;
/
**Step 3**
grant execute on kill_session to SYS;
**Step 4**
select inst_id, sid, serial#, username, action, program, service_name, con_id from gv$session where username like 'FCM_469';
Check there will be no sessions now
**Step 5**
DROP USER USER_345 CASCADE;
Output:User Dropped
Here's how I "automate" Dropping connected users in Oracle database:
# A shell script to Drop a Database Schema, forcing off any Connected Sessions (for example, before an Import)
# Warning! With great power comes great responsibility.
# It is often advisable to take an Export before Dropping a Schema
if [ "$1" = "" ]
then
echo "Which Schema?"
read schema
else
echo "Are you sure? (y/n)"
read reply
[ ! $reply = y ] && return 1
schema=$1
fi
sqlplus / as sysdba <<EOF
set echo on
alter user $schema account lock;
-- Exterminate all sessions!
begin
for x in ( select sid, serial# from v\$session where username=upper('$schema') )
loop
execute immediate ( 'alter system kill session '''|| x.Sid || ',' || x.Serial# || ''' immediate' );
end loop;
dbms_lock.sleep( seconds => 2 ); -- Prevent ORA-01940: cannot drop a user that is currently connected
end;
/
drop user $schema cascade;
quit
EOF

Dropping Oracle connections using a query

I wanted to drop/kill connections made to specific schema of a database. Could you please sugest a prudent way to do this?
Cheers
In order to prevent a user from connecting you could lock the account:
ALTER USER usr ACCOUNT LOCK;
If you want to disconnect all sessions of a user, you could use the method described in another SO:
BEGIN
FOR x IN (SELECT Sid, Serial# FROM v$session WHERE username = 'USR') LOOP
EXECUTE IMMEDIATE 'Alter System Kill Session ''' || x.Sid || ','
|| x.Serial# || ''' IMMEDIATE';
END LOOP;
END;

Resources