Oracle: automate dropping and recreating connected user - oracle

I am trying to DROP a connected user with Oracle. This question has been asked well over 700 million times, thanks to Oracle being a great software product. All answers I find, however, require user input and can not be automated as posted. The typical answer is well stated here: https://stackoverflow.com/a/15665694/177293
I want to do this in a script without a human having to intervene. So, I'm trying like this:
BEGIN
FOR i IN (
SELECT sid, serial# from v$session where username = 'MYUSER'
) LOOP
EXECUTE IMMEDIATE 'alter system kill session ''' || i.sid || ',' || i.serial# || ''';';
END LOOP;
END;
Which, hilariously, in sqlplus results in:
BEGIN
*
ERROR at line 1:
ORA-00911: invalid character
ORA-06512: at line 5
Trying a slightly different tactic:
BEGIN
FOR i IN (
SELECT
'alter system kill session ''' || sid || ',' || serial# || ''';'
from v$session where username = 'MYUSER'
) LOOP
EXECUTE IMMEDIATE ''|| i ||'';
END LOOP;
END;
results in:
EXECUTE IMMEDIATE ''|| i ||'';
*
ERROR at line 7:
ORA-06550: line 7, column 21:
PLS-00306: wrong number or types of arguments in call to '||'
ORA-06550: line 7, column 3:
PL/SQL: Statement ignored
I'm hoping to have a finished script that looks something like this:
#!/bin/sh
sqlplus system/manager <<EOT
BEGIN
FOR i IN (
SELECT sid, serial# FROM v$session WHERE username = 'MYUSER'
) LOOP
EXECUTE IMMEDIATE 'alter system kill session ''' || i.sid || ',' || i.serial# || ''';';
END LOOP;
END;
DROP USER MYUSER CASCADE;
DROP TABLESPACE MYUSER INCLUDING CONTENTS AND DATAFILES;
CREATE USER MYUSER IDENTIFIED BY password;
ALTER USER MYUSER IDENTIFIED BY password;
GRANT connect, resource TO MYUSER;
exit;
EOT
What is wrong with the above?

The main problem with the above scripts is that ; should not be in dynamic SQL.
But don't let that fool you, this is still an incredibly difficult problem. Unfortunately, it is rare for Oracle systems to frequently drop and re-create users. This means you will run into lots of weird bugs.
I've built scripts to do this, but unfortunately cannot share them here. Here are some of the fun things you have to look forward to:
Killing connected sessions.
Killing other sessions that are blocking objects owned by your session.
Truncating temporary tables because they have weird locking rules. And killing sessions using them, see HOW TO DIAGNOSE AN ORA-14452 DURING DROP OF TEMPORARY TABLE (Doc ID 800506.1).
Using INST_ID in the KILL syntax, for RAC.
Looping through the whole process a few times, in case an application is spawning new sessions that lock objects.
If you're using object-relational code, execute dbms_session.reset_package. Amazingly, some objects can persist even after dropping and recreating their owners.
Expect to write about a hundred lines of code, if it's a complicated application. Expect to spend a few weeks of testing to shake out some weird bugs. Expect it to still fail occasionally.
Yes, this is ridiculous. Oracle spends a lot of effort on ways to quickly create new databases; transportable tablespaces, pluggable databases, virtualization, etc. You'd think there would be a good way to reliably drop and recreate a user!

Related

Oracle 12c View that shows all running processes

Disclaimer: I do not venture into the realm of database administration often, and usually stick to data analytics. However, I am trying to educate myself and build my DBA skillset on a small sample database I've created. I am NOT trying to edit anything in production.
This is an extension of this question:
How do I show running processes in Oracle DB?
I would like to create a view for my users that shows a read-only copy of all processes running on my database (similar to what you can do in the terminal of a linux operating system).
I do NOT want to give my users the ability to alter or kill processes that do not belong to them, I just want to give them the ability to easily see how busy the database server is at any given time (I.E.-"read-only" access).
I know to run the below command and allow users to access the view, I need to modify the database permissions. Is there a permission I can enable on the user accounts that will allow them to "select * from WHAT_IS_RUNNING_ON_DB", but not alter/kill processes that they don't own?
CREATE VIEW WHAT_IS_RUNNING_ON_DB AS
SELECT
sess.process, sess.status, sess.username, sess.schemaname,
sql.sql_text
FROM v$session sess,
v$sql sql
WHERE sql.sql_id(+) = sess.sql_id
AND sess.type = 'USER'
AND sess.status = 'ACTIVE'
If you only want them to view what is running, simply:
grant select on v$session to USER1;
grant select on v$sql to USER1;
Then create the view in their schemas. Alternatively, if you don't want them querying on these v$ views directly (because you want to hide the other columns or you only want to keep the code for the view all in one place), you can create a user, grant the above to this new user, create the view in that schema, then grant whoever select against the view.
I do NOT want to give my users the ability to alter or kill processes
that do not belong to them
Although you didn't explicitly ask for it, I interpret this as, you may want to grant users the ability to kill processes that they do own.
Asktom has a good article how to do this:
Create the following procedure in that high privileged user's schema:
create or replace procedure kill_session( p_sid in varchar2,
p_serial# in varchar2)
is
cursor_name pls_integer default dbms_sql.open_cursor;
ignore pls_integer;
BEGIN
select count(*) into ignore
from V$session
where username = USER
and sid = p_sid
and serial# = p_serial# ;
if ( ignore = 1 )
then
dbms_sql.parse(cursor_name,
'alter system kill session '''
||p_sid||','||p_serial#||'''',
dbms_sql.native);
ignore := dbms_sql.execute(cursor_name);
else
raise_application_error( -20001,
'You do not own session ''' ||
p_sid || ',' || p_serial# ||
'''' );
end if;
END;
/
The owner of this procedure needs to have
o SELECT on v_$session granted to them by SYS. This grant must be
directly to them, not via a role.
o ALTER SYSTEM granted directly to them -- not via a role.
You would then grant execute on this procedure to anyone you want. It
would allow them to kill any session they own (running under their
username). You would probably want to "grant select on v_$session"
when connected as SYS to these people as well so they can 'see' the
v$session dynamic performance view to get their sid/serial# pairs

Oracle kill inactive sessions stored procedure

I would like to write kill_inactive sessions stored procedure for Oracle.
something like
create or replace procedure kill_inactive_sessions as
begin
for rec in (select sid, serial# from sys.v_$session where status = 'INACTIVE')
loop
execute immediate 'alter system kill session '''|| rec.sid || ',' || rec.serial# || ''' IMMEDIATE';
end loop;
end kill_inactive_sessions;
Previous doesn't work (table or view does not exists).
But next select is working perfectly for the same user:
select sid, serial# from sys.v_$session where status = 'INACTIVE';
What I'm missing?
The ORA-00942: table or view does not exist error almost certainly indicates that your access to the v$session view is via a role rather than a direct grant.
If you want to write a definer's rights stored procedure, the owner of the procedure must have the necessary privileges granted directly to the user, not via a role (and remember that DBA is just another role). Most likely, if you disabled roles in your session, the SELECT statement outside of the procedure would not work. In SQL*Plus, for example
SQL> set role none;
SQL> select sid, serial# from sys.v_$session where status = 'INACTIVE';
will likely throw the same ORA-00942 error. Assuming it does, you need to grant the user that owns the stored procedure privileges directly. For example
GRANT SELECT ANY DICTIONARY
TO user_that_owns_the_procedure;
The same thing will also apply to the ALTER SYSTEM command that you are building and running. The owner of the stored procedure will need to have privileges to run that command via a direct grant not via a role.
All that said, a procedure that kills all inactive sessions is highly problematic. The vast majority of sessions are going to be inactive the vast majority of the time. That doesn't mean that they should be killed. Even if you're cleaning up after an application server crash, you really ought to be applying some additional predicates (for example, looking for sessions from a particular machine that are logged in as a particular user that were established before the application server crashed). Long-term, though, I'd suggest enabling dead connection detection so that the database can automatically take care of closing sessions when the client process dies unexpectedly.

How to see and kill a list of sessions being made to an ORACLE schema?

I am attempting to diagnose an issue and for that need to see the list of connections being made to a specific Oracle Schema.
Assuming that I have DBA privileges, what are the queries that I should run to :
List the sessions (active and otherwise) to an Oracle schema and,
Kill these sessions
Thank you!
Here are the queries you will need to execute:
-- 1. Check connected sessions
select sid, serial#, username, machine,
to_char(logon_time+5/24,'ddMon hh24:mi') login,
SQL_HASH_VALUE, PREV_HASH_VALUE,
status
from v$session
where
lower(username) like '%SCHEMA_NAME%'
--and lower(status) not like '%killed%'
--and machine like '%SOURCE_MACHINE_NAME%'
order by logon_time;
-- 2. Same as above, but just show the count of sessions
select count(1)
from v$session
where lower(username) like lower('%SCHEMA_NAME%')
--and lower(status) not like '%inactive%'
order by logon_time;
-- 3. Kill connected sessions
ALTER SYSTEM ENABLE RESTRICTED SESSION;
begin
for x in (
select Sid, Serial#, machine, program
from v$session
where lower(username) like '%SCHEMA_NAME%'
) loop
execute immediate 'Alter System Kill Session '''|| x.Sid
|| ',' || x.Serial# || ''' IMMEDIATE';
end loop;
end;
ALTER SYSTEM DISABLE RESTRICTED SESSION;
-- May have to wait for a bit for the killed sessions to be cleaned up
I have been using the above on an Oracle 11g database so would expect them to work for you too.
Note that I have included some commented out where clauses in the first two queries which would allow you to refine the search criteria.
Hope this is what you were looking for.

Oracle procedure to kill jobs that do not complete in given time

I have created an oracle procedure to reschedule job that do not complete in a given amount of time:
create or replace procedure kill_stuck_jobs
as
begin
for x in (
select j.sid,
s.spid,
s.serial#,
j.log_user,
j.job,
j.broken,
j.failures,
j.last_date,
j.this_date,
j.next_date,
j.next_date - j.last_date interval,
j.what
from
(select
djr.SID,
dj.LOG_USER,
dj.JOB,
dj.BROKEN,
dj.FAILURES,
dj.LAST_DATE,
dj.LAST_SEC,
dj.THIS_DATE, dj.THIS_SEC,
dj.NEXT_DATE, dj.NEXT_SEC, dj.INTERVAL, dj.WHAT
from dba_jobs dj, dba_jobs_running djr
where dj.job = djr.job ) j,
(select p.spid, s.sid, s.serial#
from v$process p, v$session s
where p.addr = s.paddr ) s
where j.sid = s.sid and
j.next_date+15/1440 < sysdate
) loop
EXEC DBMS_JOB.BROKEN(x.job,TRUE);
execute immediate 'alter system disconnect session '''|| x.sid|| ',' || x.serial# || ''' immediate';
EXEC DBMS_JOB.BROKEN(x.job,FALSE);
dbms_output.put_line( 'Alter session done' );
end loop;
end;
But this procedure compiles with error:
PLS-00103: Encountered the symbol "DBMS_JOB" when expecting one of the following:
:= . ( # % ;
The symbol ":=" was substituted for "DBMS_JOB" to continue.
Here came the discussion on dbms_job and dbms_scheduler_job. Actually the problem here is I created materialized view using linked db but sometime the query is stuck for whole day in session with SQL*Net more data from dblink . I used the above procedure to kill jobs that are created while creating materialized view and I am killing the session using :
create or replace procedure kill_stuck_refresh as
begin
for x in (
select username, osuser, sid, serial#, seconds_in_wait,
event, state, wait_class
from v$session
where username is not null
and seconds_in_wait > 600
and event = 'SQL*Net more data from dblink'
) loop
execute immediate 'alter system disconnect session '''|| x.sid
|| ',' || x.serial# || ''' immediate';
dbms_output.put_line( 'Alter session done' );
end loop;
end; -- end of kill_stuck_refresh;
Your error stack all derives from the fact that your cursor is invalid. The cause is the ORA-00942. This can mean the table name is spelled wrong, but as you're using the data dictionary it usually points to a permissions problem, that is, the tables (or views in this case) do not exist in your scope.
Which brings us to your comment:
"I can run the select query independently I think that mean I have
those priviledge. "
This suggests to me that your permissions have been granted to a role you have rights on. We can use permissions granted through roles in SQL but cannot use them to build permanent objects such as views or stored procedures. For this to work you need to have permissions on those views granted directly to your user. There is no workaround, this is the way the Oracle security model works.
Incidentally, you should investigate why these jobs are taking too long. I'm assuming this is a persistent problem (otherwise why are you building a stored proc to kill these jobs?). Killing jobs is a blunt instrument which wastes resources and obliterates helpful evidence. A better idea would be to find out the underlying cause of the poor performance and fix it. Perhaps there's a problem with blocking sessions and you need a locking strategy. Maybe you've got soem poorly tuned SQL. Some other job may be running at the same time and sucks up all the resources, in which case you need a decent scheduler. Or possibly you've just got more data now - the price of success - and you need to give the jobs more time to complete.
You do not need EXEC in this context (in a stored procedure/function). It's only needed when you want to call a stored procedure from SQL (from SQL*Plus). Just call dbms_job.broken() directly:
...
DBMS_JOB.BROKEN(x.job,TRUE);
execute immediate 'alter system disconnect session '''|| x.sid|| ',' || x.serial# || ''' immediate';
DBMS_JOB.BROKEN(x.job,FALSE);
dbms_output.put_line( 'Alter session done' );
...
As #David Aldridge mentioned, DBMS_Scheduler is largely meant to replace DBMS_Job (starting with 10g, actually). What he didn't mention is why this would help you.
DBMS_Scheduler provides a lot more control over it's jobs, including better logging. However, most significantly for you, DBMS_Scheduler adds a max_run_duration parameter to the job. If a job's run-time surpasses that duration, the job is automatically ended (and probably in a more graceful fashion than killing the session). Then, as long as the max_failures parameter isn't set, the job will be attempted again at it's next scheduled time.
If you can migrate your current jobs to DBMS_Scheduler, you can save yourself the trouble of writing code to provide functionality that already exists.
If you are using Oracle 11g then you ought to be using DBMS_Scheduler, not DBMS_Job. Look first at migrating to the new package, and rethink your requirement in that context.

Dropping a connected user from an Oracle 10g database schema

Is there a better way to forcefully disconnect all users from an Oracle 10g database schema than restarting the Oracle database services?
We have several developers using SQL Developer connecting to the same schema on a single Oracle 10g server. The problem is that when we want to drop the schema to rebuild it, inevitably someone is still connected and we cannot drop the database schema or the user while someone is still connected.
By the same token, we do not want to drop all connections to other schemas because other people may still be connected and testing with those schemas.
Anyone know of a quick way to resolve this?
To find the sessions, as a DBA use
select sid,serial# from v$session where username = '<your_schema>'
If you want to be sure only to get the sessions that use SQL Developer, you can add and program = 'SQL Developer'. If you only want to kill sessions belonging to a specific developer, you can add a restriction on os_user
Then kill them with
alter system kill session '<sid>,<serial#>'
(e.g. alter system kill session '39,1232')
A query that produces ready-built kill-statements could be
select 'alter system kill session ''' || sid || ',' || serial# || ''';' from v$session where username = '<your_schema>'
This will return one kill statement per session for that user - something like:
alter system kill session '375,64855';
alter system kill session '346,53146';
Find existing sessions to DB using this query:
SELECT s.inst_id,
s.sid,
s.serial#,
p.spid,
s.username,
s.program
FROM gv$session s
JOIN gv$process p ON p.addr = s.paddr AND p.inst_id = s.inst_id
WHERE s.type != 'BACKGROUND';
you'll see something like below.
Then, run below query with values extracted from above results.
ALTER SYSTEM KILL SESSION '<put above s.sid here>,<put above s.serial# here>';
Ex:
ALTER SYSTEM KILL SESSION '93,943';
my proposal is this simple anonymous block:
DECLARE
lc_username VARCHAR2 (32) := 'user-name-to-kill-here';
BEGIN
FOR ln_cur IN (SELECT sid, serial# FROM v$session WHERE username = lc_username)
LOOP
EXECUTE IMMEDIATE ('ALTER SYSTEM KILL SESSION ''' || ln_cur.sid || ',' || ln_cur.serial# || ''' IMMEDIATE');
END LOOP;
END;
/
Make sure that you alter the system and enable restricted session before you kill them or they will quickly log back into the database before you get your work completed.
Just my two cents : the best way (but probably not the quickest in the short term) would probably be for each developer to work on his own database instance (see rule #1 for database work).
Installing Oracle on a developer station has become a no brainer since Oracle Database 10g Express Edition.
Have you tried ALTER SYSTEM KILL SESSION? Get the SID and SERIAL# from V$SESSION for each session in the given schema, then do
ALTER SCHEMA KILL SESSION sid,serial#;
just use SQL :
disconnect;
conn tiger/scott as sysdba;

Resources