Custom oracle exceptions through JDBC - oracle

In a stored procedure I have used to;
raise_application_error (-20010, 'My Message');
to raise a custom error in a certain situation. What I am trying to do is when I make my JDBC call from java, to be able to identify this error as not just being a SQLException so that I can handle it differently. I though I could identify it by the errorCode, but that seems to always be 17062 and not -20010.
Is there another way to do this, or am I missing something?

you should get 20010 as your errorCode. the ORA-17062 is an error for invalid ref cursors. Are you sure the procedure you are calling throws the custom error ?

Related

Getting ORA-01008 error when trying to execute a function via OCI

We are trying to execute an oracle function that requires parameters and we are getting the error ORA-01008- Not all variables are bound. We are pretty sure the problem is in how we are binding the variable that is supposed to received the result of the function. First we tried the following (a method without parameters):
$tSql:="select staging.FUNC_ORAOCI_TEST() from dual"
$iStatus:=OCIHandleAlloc (envhp;$stmthp;OCI_HTYPE_STMT)
$iStatus:=OCIHandleAlloc (envhp;$errhp;OCI_HTYPE_ERROR)
$iStatus:=OCIStmtPrepare ($stmthp;$errhp;$tSql;OCI_DEFAULT)
$iStatus:=OCIDefineByPos ($stmthp;$bindpp;$errhp;1;->atResults;SQLT_STR;$ORANullIndicator;$ORANullLenArray;$ORANullReturnCodeArray;OCI_DEFAULT)
$iStatus:=OCIStmtExecute (svchp;$stmthp;$errhp;1;0;0;0;OCI_DEFAULT)
And it worked perfectly...
Where we got stuck was when trying to pass parameters to the function (which we had modified on purpose to now accept parameters)
We thought it was because we now had to make the binding by name, but it just did not work. We have tried running a PL/SQL block and still we get the error. Here are our failed attempts:
Using a SQL Statement:
$tSql:="select staging.FUNC_ORAOCI_TEST(:tParamText,:iParamNum) from dual"
$iStatus:=OCIBindByName ($stmthp;$bindpp;$errhp;":tParamText";- >tParamText;SQLT_STR;$ORANullIndicator;$ORANullLenArray;$ORANullReturnCodeArray;1;OCI_DEFAULT;BIND_IN)
$iStatus:=OCIBindByName ($stmthp;$bindpp;$errhp;":iParamNum";->iParamNum;SQLT_INT;$ORANullIndicator;$ORANullLenArray;$ORANullReturnCodeArray;1;OCI_DEFAULT;BIND_IN)
$iStatus:=OCIDefineByPos ($stmthp;$bindpp;$errhp;1;->atResults;SQLT_STR;$ORANullIndicator;$ORANullLenArray;$ORANullReturnCodeArray;OCI_DEFAULT)
$iStatus:=OCIStmtExecute (svchp;$stmthp;$errhp;1;0;0;0;OCI_DEFAULT)
Using a PL/SQL Anonymous Block:
$tSql:="DECLARE vResult VARCHAR2:=''; BEGIN vResult := FUNC_ORAOCI_TEST(:tParamText,:iParamNum); End;"
$iStatus:=OCIBindByName ($stmthp;$bindpp;$errhp;":tParamText";->tParamText;SQLT_STR;$ORANullIndicator;$ORANullLenArray;$ORANullReturnCodeArray;1;OCI_DEFAULT;BIND_IN)
$iStatus:=OCIBindByName ($stmthp;$bindpp;$errhp;":iParamNum";->iParamNum;SQLT_INT;$ORANullIndicator;$ORANullLenArray;$ORANullReturnCodeArray;1;OCI_DEFAULT;BIND_IN)
$iStatus:=OCIBindByName ($stmthp;$bindpp;$errhp;":vResult";->tResult;SQLT_STR;$ORANullIndicator;$ORANullLenArray;$ORANullReturnCodeArray;1;OCI_DEFAULT;BIND_OUT)
$iStatus:=OCIStmtExecute (svchp;$stmthp;$errhp;1;0;0;0;OCI_DEFAULT)
We have looked at similar questions but no one was trying to use the OCI interface to execute the function the way we are doing it.
We are coding in 4D so ignore the weird syntax. We just need guidance as to how build the statement and what would be the proper OCI command to reach a successful binding.
We partially resolved this issue.
We have got it to work only for SQL statements not for PL/SQL. But we got rid of the ORA-01008 error.
The code in case 1 referenced above works as is. We did some clean up of comments and lines that were added for debugging, but they might have altered the sqlstmt somehow, thus affecting the binds.
The code in case 2 now gives an error "Invalid SQL Statement" which we will open a new inquiry for in separate thread.
One very useful information that we found to help us resolve the initial inquiry can be found here:
https://docs.oracle.com/database/121/LNOCI/oci05bnd.htm#LNOCI16368

Best way to return "expected" Oracle exceptions to Java Groovy/Grails

Background:
In my Oracle database, I have plenty of database calls which can cause exceptions. I currently have exception handlers for all these, which call an error package. To cut a long story short, a raise_application_error is eventually raised, for expected errors, or a raise for unexpected errors, and this is sent back to the calling Java Groovy/Grails application layer.
So, for example, if a user enters an id and clicks search, I run a select query from the database. If the id doesn't exist, I have a NO_DATA_FOUND exception which performs a raise_application_error with a custom error message (i.e. "ID entered cannot be found.")
However, the application development team say they're struggling with this. They are trying to perform unit testing in Groovy and ideally want a variable returned. The SQL exceptions I am currently returning cause all tests to fail as it is an exception. Their code looks like this:
void nameOfProcedure() {
String result = storedProcedure.callDBProcedure(ConnectionType.MSSQL, val1, val2)
log.info "SQL Procedure query result value: "+ result
assertEquals("1", result)
}
They can add something like this above the test:
#Test (expected = SQLException.class)
But this means all returning SQLExceptions will pass, regardless of whether they are the right exceptions for the issue at hand.
Question:
What is the best solution to this issue? I'm being pressed to return variables from my exception blocks, rather than raise_application_errors - but I'm very reluctant to do this, as I've always been told this is simply terrible practice. Alternatively, they could make changes on their end, but are obviously reluctant to.
What's the next step? Should I be coding to return "expected" errors as variables, as opposed to exceptions? For example, if someone enters an ID that isn't found:
BEGIN
SELECT id
FROM table
WHERE id = entered_id
EXCEPTION
WHEN NO DATA FOUND THEN
RETURN 'ID cannot be found';
END
Or alternatively, should they be following a guide like this which advises using Hamcrest matchers to create their own custom exception property, which they can check against in their JUnit testing. What is best practice here?
You're right, it's terrible practice. It just 'wagging the dog'; they're being lazy to work good and wish you to spoil application design in order to please them.
Generally, unit test with exception returned should looks something like this:
try {
String result = callDBProcedure();
fail("Result instead of exception");}
catch (OracleSQLException e) {
assertEquals(e.errorCode, RAISE_APPLICATION_ERROR_CODE);}
catch (Throwable t) {
fail("Unexpected error");
}
They can upgrade this as they wish. For example, they can develop procedure 'call the SP and convert exception to anything they wish' and use it in their tests. But they should not affect application design outside testing. Never.

checking for specific error code in PL/SQL exception block

I am getting the ORA-06503: PL/SQL: Function returned without value error in a pl/sql trigger code. I actualy want to do some specific exception handling only if this error occurs else do the regular exception handling in my code.
My question is how do I capture the specific ORA codes in exception blocks. Like If I want to add a if condition like
If(oracode = something) then ...
else ...
the SQLERRM gives the ora code with error message. how do I use this in IF condition ?
Need help with this
Thanks.
You are looking for SQLCODE - these are negative numbers when there is an error.
if SQLCODE = -6503 then ....

Error handling in pl/sql

I want to know about error handling in PL/SQL. Can anyone help me to find brief description on this topic?
Every block can have an exception handler. Example:
DECLARE
/* declare your variables */
BEGIN
/*Here is your code */
EXCEPTION
WHEN NO_DATA_FOUND THEN
/* HAndle an error that gets raised when a query returns nothing */
WHEN TOO_MANY_ROWS THEN
/* HAndle the situation when too much data is returned such as with a select-into */
WHEN OTHERS THEN
/* Handle everything else*/
END;
This link will tell you more: http://download.oracle.com/docs/cd/B13789_01/appdev.101/b10807/07_errs.htm
That link will show you more detail than I did, as well as examples on how to create your own exception names.
One item that always trips me up is that if you have a function and you fail to return a value in the exception handler, an exception gets thrown in the calling function. Not a big deal but I always seem to forget that one.
The Oracle article referenced in the other answer is well worth reading.
A couple of extra things to throw in - catching a PL/SQL exception loses the error stack - i.e. the information about exactly which line raised the exception.
This can make it difficult to debug blocks of code that contain multiple places that could raise the same exception (i.e. if you have more than one SQL statement that could return NO_DATA_FOUND). One option here is to log the full error stack as part of your exception handler.
EXCEPTION
WHEN TOO_MANY_ROWS THEN
myLogger('Some useful information',DBMS_UTILITY.FORMAT_ERROR_STACK);
END;
If you do need to catch exceptions, keep your exception handling as local as possible to the point you want to catch, and only use WHEN OTHERS in the last resort.
You can also 'do something and re-raise the same exception'
EXCEPTION
WHEN TOO_MANY_ROWS THEN
closeSmtpConnection;
RAISE;
END;
One of the most useful features is the ability to name and catch Oracle SQL internal exceptions.
DECLARE
recompile_failed EXCEPTION;
PRAGMA EXCEPTION_INIT (recompile_failed,-24344);
BEGIN
. . . . . .
EXCEPTION
WHEN recompile_failed THEN
emailErrors(pObjectType,pObjectName);
END;
The flipside to this is the ability to raise user defined 'SQL' exceptions
RAISE_APPLICATION_ERROR(-20001,'my text')
This is the only way to propagate user defined text to a calling application, as user-defined pl/sql exceptions do not cross the 'scope' boundary.
Unfortunately, despite the documentation saying that the range -20000 to -20999 is available for user-defined exceptions, some of the Oracle extension packages use these serials, so you cannot depend on serial alone to identify an error in the calling language.
(Most people tend to wrap RAISE_APPLICATION_ERROR in other code to also log the error, and often to derive the error text from a table)
One trick I've found useful is to create a package with 'stateful' variables in the package body, and simple setter and getter functions. Unlike database updates, information in packages is NOT rolled back on error.
At the point of error, set information in your package, then retrieve it using getters in your calling language, to construct a 'native' exception.
As for user-defined pl/sql exceptions - these can be useful in local code, but in many cases they can be avoided by using a different control structure (i.e. avoid using them as an alternative GOTO).
Creating global exceptions on package headers, to specify the possible exceptions a package may return seems like a good idea, but the end result is that your calling code ends up with having to handle every potential exception that could be cast in any of the underlying packages.
Having gone down this route myself in the past, I would now recommend against it - make packages self-contained and either use RAISE_APPLICATION_ERROR or pass back errors as text.

ORA-28579: network error during callback from external procedure agent

Has anyone seen this error when trying to call an external C function from an Oracle query? I'm using Oracle 10g and get this error every time I try to call one of the two functions in the library. A call to the other function returns fine every time, though the function that works is all self-contained, no calls to any OCI* functions.
Here's the stored procedure that is used to call the failing C code:
CREATE OR REPLACE PROCEDURE index_procedure(text in clob, tokens in out nocopy clob, location_needed in boolean)
as language c
name "c_index_proc"
library lexer_lib
with context
parameters
(
context,
text,
tokens,
location_needed
);
Any help would be appreciated. Everything I've found on this error message says that the action to take is: Contact Oracle customer support.
Edit: I've narrowed it down to the point that I know that there is a segfault deep in libclntsh after I call OCILobTrim (to truncate it down to 0 length) on the tokens clob. Here is the code I've been using to call this procedure.
declare text CLOB; tokens CLOB;
begin
dbms_lob.createtemporary(tokens, TRUE);
dbms_lob.append(tokens, 'token');
dbms_lob.createtemporary(text, TRUE);
dbms_lob.append(text, '<BODY>Test Document</BODY>');
index_procedure(text, tokens, FALSE);
dbms_output.put_line(tokens);
end;
/
Is there something wrong with this setup that might be causing OCILobTrim problems?
It looks like this is one of those errors that essentially means any number of things could have gone wrong with the external procedure.
There is a known bug in 10.2.0.3, no idea if it's relevant:
ORA-28579 occurs when trying to select
data from a pipelined table function
implemented in "C" using the
ODCITable/ANYDATASET interface.
ODCITableDescribe works fine but
ODCITableFetch generates an ORA-28579
error.
I would suggest:
Look in the database server
trace directories, and the directory
where the external proc is located,
for any log or trace files generated
when the error occurs.
Instrument your external proc in
some way so that you can try to
trace its execution yourself.
Contact Oracle support
Well, an upgrade to 10.2.0.4 (was using 10.2.0.1) at least gave me an understandable error instead of a fairly useless core file and the ORA-28579.
It turns out that the code I was debugging was assuming that calling OCILobRead would return all of the data in one pass. This is the case for any client using a fixed width character set.
For clients using a variable width character set, this isn't the case, OCILobRead was actually reading part of the data and returning OCI_NEED_DATA and future calls to OCILobTrim and OCILobWrite were failing because of the still pending call to OCILobRead. The solution was to loop OCILobRead calls until OCI_NEED_DATA was no longer returned and we had all of the needed data in our buffer.
A call to OCIBreak also would have allowed the OCILobTrim and OCILobWrite functions to continue, though we wouldn't have had all of the needed input data.

Resources