How to handle exception in oracle package? - oracle

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.

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.

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

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.

Clear Oracle session state

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).

COMMIT WRITE BATCH NOWAIT in Hibernate

Is it possible to execute COMMIT WRITE BATCH NOWAIT in Hibernate?
I didn't search extensively but I couldn't find any evidence that you can access this functionality at the JDBC driver level.
And this leaves you with the option to specify the COMMIT_WRITE parameter at the instance or session level, if this makes sense for you.
Just in case, let me quote this blog post (I'm pasting the content for reference because the original site is either unavailable or dead and I had to use Google Cache):
Using "Commit Write Batch Nowait" from within JDBC
Anyone who has used the new
asynchronous commit feature of Oracle
10.2 will be aware that it's very useful for transaction processing
systems that would traditionally be
bound by log_file_sync wait events.
COMMIT WRITE BATCH NOWAIT is faster
because it doesn't wait for a message
assuring it that the transaction is
safely in the redo log - instead it
assumes it will make it. This nearly
eliminates log_file_sync events. It
also arguably undermines the whole
purpose of commit, but there are many
situations where the loss of a
particular transaction (say to delete
a completed session) is perfectly
survivable and far more preferable
than being unable to serve incoming
requests because all your connections
are busy with log_file_sync wait
events.
The problem anyone using Oracle's JDBC
driver is that neither the 10.2 or
11.1 drivers have any extensions which allow you to access this functionality
easily - while Oracle have lots of
vendor specific extensions for all
sorts of things support for async
commit is missing.
This means you can:
Turn on async commit at the instance level by messing with the
COMMIT_WRITE init.ora parameter.
There's a really good chance this will
get you fired, as throughout the
entire system COMMIT will be
asynchronous. While we think this is
insane for production systems there
are times where setting it on a
development box makes sense, as if you
are 80% log file sync bound setting
COMMIT_WRITE to COMMIT WRITE BATCH
NOWAIT will allow you to see what
problems you face if you can somehow
fix your current ones.
Change COMMIT_WRITE at the session level. This isn't as dangerous as
doing it system wide but it's hard to
see it being viable for a real world
system with transactions people care
about.
Prepare and use a PL/SQL block that goes "BEGIN COMMIT WRITE BATCH NOWAIT;
END". This is safer than the first
two ideas but still involves a network
round trip.
Wrap your statement in an anonymous block with an asynchronous commit.
This is the best approach we've seen.
Your code will look something like
this:
BEGIN
--
insert into generic_table
(a_col, another_col, yet_another_col)
values
(?,?,?);
--
COMMIT WRITE BATCH NOWAIT;
--
END;
I was looking for a way to do this but couldn't get it working in a test. The reason for my hold up was that I was expecting the wrong results from my test. I was testing by manually acquiring a shared table lock to simulate adding an index - but in this case, the insert query acquires the lock, not the commit. So it doesn't actually solve the problem I was looking to solve. I got round my problem by moving these insertions into a background queue, so that they don't hold up the main web request.
Anyway I think you can still do asynchronous commits in Hibernate. Basically you can use the Session.doWork() method to get access to the native Connection object (or in older versions of Hibernate, the Session.connection() method). I also moved the commit SQL into a strategy interface, so that we can run our HSQLDB-based tests which wouldn't understand the Oracle specific SQL.
In fact, it may be fine to use Session.createSQLQuery and give that the SQL, avoiding having to directly use Connection. Try it and see how it works.
private NativeStrategy nativeStrategy = new OracleStrategy();
interface NativeStrategy {
String commit();
}
public static final class OracleStrategy implements NativeStrategy {
public String commit() {
return "COMMIT WRITE BATCH NOWAIT";
}
}
public void saveAsynchronously(MyItem item) {
session.save(item);
session.flush();
// Try to issue an asynchronous commit where supported.
session.doWork(new Work() {
public void execute(Connection connection) throws SQLException {
Statement commit = connection.createStatement();
try {
commit.execute( nativeStrategy.commit() );
} finally {
commit.close();
}
}
});
}

Resources