Clear Oracle session state - oracle

A database connection on Oracle can have session state that persists for the lifetime of the connection, i.e. in the form of package variables.
Is there a way of flushing/clearing all that state between calls during a connection without killing the connection and reestablishing a new connection.
I.e. consider a package variable first set in the package init, and later modified within some procedure in that package: how to "reset" the package so that multiple calls to the procedure from 1 connection always lead to a reinit of the package?
In general: how to "reset" any session state between execution of statements from a client on that connection?

dbms_session.reset_package is the closest I can think of. See this tahiti link.

Other than dbms_session.reset_package (proposed in René Nyffenegger's answer), which resets all packages, you'll have to write your own package procedure to reset the state of a single package only. The procedure would just set all package variables to NULL (or whatever is appropriate).

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 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 PL/SQL: How to detect if a procedure is ALREADY running?

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.

How to keep a persistent connection to SQL Server using Ruby Sequel and Tiny_TDS while in a loop

I have a ruby script that needs to run continually on the server. I've daemonized it using the daemon gem, and in my script I have it running in an infinite loop, since the daemon gem handles starting and stopping of the process that kicks off my script. In my script, I start out by setting up my DB instance using the Sequel gem and tiny_tds. Like so:
DB = Sequel.connect(adapter: 'tinytds', host: MSSQLHost, database: MSSQLDatabase, user: MSSQLUser, password: MSSQLPassword)
Then I have a loop do that is my infinite loop. Inside that, I test to see if I have a connection using DB.test_connection and then I query the DB every second or so to check if there is new content using a query such as:
DB['SELECT * FROM dbo.[MyTable]'].all do |row|
# MY logic here
# As part of my logic I test to see if I need to delete this row in the table and if so I use
DB.run('DELETE FROM dbo.[MyTable] WHERE some condition')
end
Then at the end of my logic, just before I loop again, I do:
sleep 1
DB.disconnect
All of this works great for about an hour to an hour and a half with everything checking the table, doing the logic, deleting rows, etc., then it dies and gives me this error message TinyTds::Error: Adaptive Server connection timed out
My question, why is that happening? Do I need to reformat my code in a different way? Why doesn't the DB.test_connection do what it is advertised to do? The documentation on that says it checks for a connection in the connection pool, and uses it if it finds it, and creates a new one otherwise.
Any help would be much appreciated
DB.test_connection just acquires a connection from the connection pool, it doesn't check that the connection is still valid (it must have been valid at one point or it wouldn't be in the pool). There's no way that a connection is still valid without actually sending a query. You can use the connection_validator extension that ships with Sequel if you want to do that automatically.
If you are loading Sequel before forking, you need to make sure you call DB.disconnect before forking, otherwise you can end up with multiple forked processes sharing the same connection, which can cause many different issues.
I finally ended up just putting a rescue statement in there that caught this, and re-ran my line of code to create the DB instance, yes, it puts a warning in my log about already setting that instance, but I guess I could just make that not a contstant an that would go away. Anyway, it appears to be working now, and the times it does timeout, I'm recovering gracefully from those. I just wish I could have figured out why it was/is disconnecting like it is.

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