Oracle PL/SQL: How to detect if a procedure is ALREADY running? - oracle

Please suppose that we have a procedure inside a package:
MY_PACKAGE.MY_PROCEDURE
This procedure could be launched from many users.
How can I modify the procedure in order to detect if the procedure is at present running since launched from another user?
What is the safest way to detect it?
Thank you for considering my request.
EDIT 01: "It'll depend on why you need to know if a proc is already running or not" ==> If the procedure is at present running, it WON'T be launched again.

You can use the DBMS_APPLICATION_INFO package for such information.
PROCEDURE MY_PROCEDURE(..) IS
BEGIN
DBMS_APPLICATION_INFO.SET_CLIENT_INFO('MY_PACKAGE.MY_PROCEDURE running');
... All your stuff
DBMS_APPLICATION_INFO.SET_CLIENT_INFO(NULL);
EXCEPTION
WHEN OTHERS THEN
DBMS_APPLICATION_INFO.SET_CLIENT_INFO(NULL);
RAISE;
END MY_PROCEDURE;
In order to check it, you can select V$SESSION View:
SELECT *
FROM v$session
WHERE client_info = 'MY_PACKAGE.MY_PROCEDURE running';
If you get any records then the procedure is running.

Based on what others have mentioned and a quick perusal of the DBMS_LOCK package header it appears that you can use the various DBMS_LOCK routines to accomplish what you're trying to do. If I'm reading the header comments correctly you'd want to call ALLOCATE_UNIQUE to get a handle to a unique, named lock, then you'd call REQUEST with the locking mode set to 'x' (Exclusive) to try to grab the lock. If the REQUEST call returns 0 you can go ahead and run your routine. When done, call RELEASE to make the lock available to the next caller.
Best of luck.

Related

Prevent Oracle package from being executed in production

I created an Oracle PL/SQL package which I want to prevent being executed in a production environment, or on a specific database, which could be dangerous. Indeed, it turns out I have admin rights and could inadvertently compile the Developement package in a production environment.
I tried checking the context in my package's body with something similar to this:
create or replace package body my_test_package is
context varchar2(64);
function get_context return varchar2 is
begin
-- return context: DEV or PROD
...
end;
-- list of other functions & procedures ....
begin
if context = 'PROD' then
dbms_standard.raise_application_error(-20001, 'production context, prevent execution of this package');
end if;
end;
However, I know it is bad solution because the initialization time takes place only once, as stated by Oracle documentation:
The initialization part of a package plays a minor role because, unlike subprograms, a package cannot be called or passed parameters. As a result, the initialization part of a package is run only once, the first time you reference the package.
So, that means all subsequent procedure calls following the first one will be executed, even in a production environment. E.g:
-- production environment
begin
my_test_package.dangerous_procedure();
exception when others then
dbms_output.put_line('bypass context exception');
end;
my_test_package.dangerous_procedure(); ---> EXECUTED IN PROD :(
Is there a common idiom or a known approach to prevent a package from being executed in a particular environment? (e.g. without having to copy the same piece of code in each procedure/function of the package, to check it has the right to execute).
Thanks
It is common to have this requirement in the opposite direction: i.e., you have processes that run in PROD that you do not want to run (or not run the same way) in DEV. For example, you might have a program that generates a file and FTPs it to a trading partner. You wouldn't want that to run in DEV by accident after a clone from PROD.
We build the implementation for requirements into our code, rather than relying on database-level things like dropping objects in certain environments (or constantly re-installing things in DEV instances after a clone) and/or revoking security. By building things into our code, we have the flexibility not just to prevent something from running in one instance or another, but to let it run but run differently (e.g., generate the FTP file, but send it to a test server instead of the trading partner).
To do this, we have a piece of data that has the name of the production database (we use an application feature called "profile values" for this, but you can just put it in a custom table).
Then, in any environment-sensitive process:
BEGIN
l_db_name := xxcust_common_utils_pkg.get_production_dbname; -- you write this function based on where you put the production database name...
IF sys_context('USERENV','DB_NAME') = l_db_name THEN
... act like you want to in production
ELSE
... act like you want to in non-production
END IF;
END;
It's very simple, but unfortunately does require coding.

How to get the screen to stop "egg timing", move along while processing code in the background

I have a small application express app. It runs some code that takes about 12 mins to execute. (I don't need any help with that side of things, thanks) but what I'm wondering is if there's a means to get the screen to move on, while that code completes?
I have an "After Submit" Process, this is of type "PL/SQL Code" and under the execution options, I have "After Submit." The code is an anonymous block that does some checks and then executes some stored procedures.
The trigger for this is a "NEXT" button in the app. However, the clicking of the "NEXT" button results in a basic 'hang' effect, while the spinny animation "egg timer" thing just runs and runs until the code runs its course.
Is there a way to get the screen to move forward to the next page whilst the code is executed in the background, and the user doesn't have to wait? Thanks!
Sure; schedule the procedure as a database job. A simple option is to use DBMS_JOB. For example, if you put that code of yours into a stored procedure (let's call it P_SCOUSE), then you'd
declare
l_job number;
begin
dbms_job.submit(job => l_job,
what => p_scouse,
next_date => sysdate,
interval => null);
commit;
end;
The procedure would run in the background, letting you do whatever you want in the Apex application.
Also, check what DBMS_SCHEDULER offers. It is newer than DBMS_JOB, has some additional options, but - as I said - for such a simple thing, DBMS_JOB does the job.

How to handle exception in oracle package?

I have a oracle package consisting of many procedures.
e.g.
pkg(
proc 1
proc 2
proc 3
);
while executing the package proc 1 gets called first and within proc 1,proc 2 is called.
SO what if i face an exception in proc 2 then i want to rollback all the DML's done in proc 1.
You don't have to do anything, Oracle will perform rollback for you. Just don't commit anywhere within those procedures - let the caller decide whether to commit or not, after everything is done.
Also, don't DDL as it'll implicitly commit everything that has been done so far.
Adding to what Littlefoot mentioned, please please have in consideration whatever IDE you are working with. There are some settings of auto-commit that I recommend to you that should be turn off. Otherwise, rollbacks won't help.

Oracle DBMS_OUTPUT not returning anything if I call a specific procedure

I have a PL/SQL block I'm running that looks something like this
BEGIN
dbms_output.put_line('11');
schema.some_package.process_data(i);
dbms_output.put_line('22');
END;
When I run this block, the DBMS Output returns absolutely nothing. If I comment out the process_data procedure call, then it returns the 11, 22 as expected. The procedure is not raising any exceptions and appears to be processing correctly.
The process_data procedure does call many other procedures and too much code to share here, but does anyone have suggestions as to what I should be looking for that would be clearing ALL queued output, even output called after the procedure in my block?
The behaviour you describe would happen if something in your stack calls dbms_output.disable(). That suppresses all output until you call dbms_output.enable().
Re-enabling output doesn't recover the suppressed output because disable() purges the buffer as well as disabling subsequent calls to put_line(), get_line(), etc

Is it possible to kill a single query in oracle without killing the session?

I would like to be able to kill a user's query in Oracle 10.2.0.4 without killing their entire session. This would allow the query to end, but not log that user out of their session, so they can continue making other queries. Is this possible at all? Or is the blunt hammer of killing the session the only way to go about ending a query's execution?
I found a trick. I have no idea how safe this is to play with, but it does work. There is an Oracle event, 10237, which is described as "simulate ^C (for testing purposes)".
You have to have the SID and SERIAL# of the session you want to interrupt.
Call SYS.DBMS_SYSTEM.SET_EV( sid, serial#, 10237, 1, '' ) to activate the event in the target session. Any currently executing statement should be interrupted (receiving "ORA-01013: user requested cancel of current operation"). As long as the event is set, any further statements the session attempts to execute will immediately terminate with the same error.
To deactivate the event, make the same call with the fourth parameter set to "0". The session will then be able to execute statements again.
Note that the target session has to detect that the event is set, which may take time, or may never happen, depending on what it is doing. So you can't just quickly toggle the event on and off. You would need to turn it on, verify that the statement in question has stopped, then turn it off.
Here's some sample code. This is meant to be run as an anonymous block in SQLPlus, with substitution variables "sid" and "serial" defined appropriately. You could turn it into a stored procedure with those as its parameters.
DECLARE
l_status v$session.status%TYPE;
BEGIN
dbms_system.set_ev( &sid, &serial, 10237, 1, '');
LOOP
SELECT status INTO l_status FROM v$session
WHERE sid = &sid and serial# = &serial;
EXIT WHEN l_status='INACTIVE';
END LOOP;
dbms_system.set_ev( &sid, &serial, 10237, 0, '');
END;
I suspect it might be possible since you can do this in TOAD. Whilst a query is running a Cancel dialog comes up which you can hit to stop the query.
How it's implemented I don't know, but would be very interested to find out too.
If you're using java there's the java.sql.Statement cancel() method which is supposed to do this. See here for some notes and limitations...
http://download.oracle.com/docs/cd/B14117_01/java.101/b10979/tips.htm#BACDAICJ
You could look at Resource Limits:
"If a user exceeds a call-level resource limit, then Oracle halts the processing of the statement, rolls back the statement, and returns an error. However, all previous statements of the current transaction remain intact, and the user's session remains connected."
That assumes the reason for cancelling the SQL is a resource limit, rather than it updating the wrong set of rows (for example). I suspect you wouldn't be able to affect currently running SQL through adding a resource limit.
http://download.oracle.com/docs/cd/B19306_01/server.102/b14220/security.htm#i13767
http://download.oracle.com/docs/cd/B19306_01/server.102/b14231/dbrm.htm#i1010776
Ideally the user's application should have the ability to cancel a query (via OCICancel or equivalent).
Otherwise, the best would be to use Resource Manager (if on Oracle EE), you can use DBMS_RESOURCE_MANAGER.SWITCH_CONSUMER_GROUP_FOR_SESS to set the target session to consumer group CANCEL_SQL.
Another hack for cancelling other sessions query, which doesn't work with sqlplus windows client sessions though, would be to send urgent signal to the Oracle process using kill -URG (it's a hack, not for everyday use)
I've written about why it works here:
http://blog.tanelpoder.com/2010/02/17/how-to-cancel-a-query-running-in-another-session/

Resources