I noticed strange behaviour of NO_DATA_FOUND
exception when thrown from function used in PLSQL.
Long story short - it does propagate from function when using assignment, and does not propagate (or is handled silently somewhere in between) when used in SELECT INTO
.
So, given function test_me
throwing NO_DATA_FOUND
exception, when invoked as:
v_x := test_me(p_pk);
It throws an exception, while when invoked as:
SELECT test_me(p_pk) INTO v_x FROM dual;
it does not throw exception. This does not occur with other exceptions. Below You can find my test examples.
Could somebody please explain to me this behaviour?
set serveroutput on;
CREATE OR REPLACE FUNCTION test_me(p_pk NUMBER) RETURN NVARCHAR2
IS
v_ret NVARCHAR2(50 CHAR);
BEGIN
BEGIN
SELECT 'PYK' INTO v_ret FROM dual WHERE 1 = 1/p_pk;
EXCEPTION WHEN NO_DATA_FOUND THEN
dbms_output.put_line(chr(9)||chr(9)||chr(9)||' (test_me NO_DATA_FOUND handled and rerised)');
RAISE;
END;
RETURN v_ret;
END;
/
DECLARE
v_x NVARCHAR2(500 CHAR);
v_pk NUMBER;
PROCEDURE test_example(p_pk NUMBER)
IS
BEGIN
BEGIN
dbms_output.put_line(chr(9)||chr(9)||'Test case 1: Select into.');
SELECT test_me(p_pk) INTO v_x FROM dual;
dbms_output.put_line(chr(9)||chr(9)||'Success: '||NVL(v_x,'NULL RETURNED'));
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line(chr(9)||chr(9)||'Failure: NO_DATA_FOUND detected');
WHEN OTHERS THEN
dbms_output.put_line(chr(9)||chr(9)||'Failure: '||SQLCODE||' detected');
END;
dbms_output.put_line(' ');
BEGIN
dbms_output.put_line(chr(9)||chr(9)||'Test case 2: Assignment.');
v_x := test_me(p_pk);
dbms_output.put_line(chr(9)||chr(9)||'Success: '||NVL(v_x,'NULL RETURNED'));
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line(chr(9)||chr(9)||'Failure: NO_DATA_FOUND detected');
WHEN OTHERS THEN
dbms_output.put_line(chr(9)||chr(9)||'Failure: '||SQLCODE||' detected');
END;
END;
BEGIN
dbms_output.put_line('START');
dbms_output.put_line(' ');
dbms_output.put_line(chr(9)||'Example 1: Function throws some exception, both cases throws exception, everything is working as expected.');
test_example(0);
dbms_output.put_line(' ');
dbms_output.put_line(chr(9)||'Example 2: Query returns row, there is no exceptions, everything is working as expected.');
test_example(1);
dbms_output.put_line(' ');
dbms_output.put_line(chr(9)||'Example 3: Query inside function throws NO_DATA_FOUND, strange things happen - one case is throwing exception, the other is not.');
test_example(2);
dbms_output.put_line(' ');
dbms_output.put_line('END');
END;
/
DROP FUNCTION test_me;
1. no_data_found :- Whenever PL/SQL Block having select into clause and also if requested data is not available then oracle server returns an error ora – 1403 : no data found. For handling this error oracle provided no_data_found exception name.
Answer: To prevent the PLSQL code from dropping to the exception code when a record is not found, you'll have to perform a count first to determine the number of records that will be returned. For example: -- Check to make sure that at least one record is returned SELECT COUNT(1) INTO v_count FROM sales.
There are three types of exceptions: Predefined exceptions are error conditions that are defined by PL/SQL. Non-predefined exceptions include any standard TimesTen errors. User-defined exceptions are exceptions specific to your application.
To fix this, re-create tables from the initial controlling database. The good news is that the ORA-01403 error is highly preventable. By creating the proper exceptions, the program should now operate free of the error and remain that way until edited again.
A minimal example is:
CREATE FUNCTION raise_exception RETURN INT
IS
BEGIN
RAISE NO_DATA_FOUND;
END;
/
If you do:
SELECT raise_exception
FROM DUAL;
You will get a single row containing a NULL
value - Ask Tom states:
it has ALWAYS been that way
and then followed up with:
no_data_found is not an error - it is an "exceptional condition". You, the programmer, decide if something is an error by catching the exceptional condition and handling it (making it be "not an error") or ignoring it (making it be an error).
in sql, no data found quite simply means "no data found", stop.
Under the covers, SQL is raising back to the client application "hey buddy -- no_data_found". The client in this case says "ah hah, no data found means 'end of data'" and stops.
So the exception is raised in the function and the SQL client sees this and interprets this as there is no data which is a NULL
value and "handles" the exception.
So
DECLARE
variable_name VARCHAR2(50);
BEGIN
SELECT raise_exception
INTO variable_name
FROM DUAL
END;
/
Will succeed as the DUAL
table has a single row and the exception from the function will be handled (silently) and the variable will end up containing a NULL
value.
However,
BEGIN
DBMS_OUTPUT.PUT_LINE( raise_exception );
END;
/
The exception is this time being passed from the function to a PL/SQL scope - which does not handle the error and passes the exception to the exception handler block (which does not exist) so then gets passed up to the application scope and terminates execution of the program.
And Ask Tom states:
Under the covers, PLSQL is raising back to the client application "hey -- no_data_found. The client in this case says "uh-oh, wasn't expecting that from PLSQL -- sql sure, but not PLSQL. Lets print out the text that goes with this exceptional condition and continue on"
You see -- it is all in the way the CLIENT interprets the ORA-xxxxx message. That message, when raised by SQL, is interpreted by the client as "you are done". That message, when raised by PLSQL and not handled by the PLSQL programmer, is on the other hand interpreted as "a bad thing just happened"
Both PLSQL and SQL actually do the same thing here. It is the CLIENT that is deciding to do something different.
Now, if we change the function to raise a different exception:
CREATE OR REPLACE FUNCTION raise_exception RETURN INT
IS
BEGIN
RAISE ZERO_DIVIDE;
END;
/
Then both:
SELECT raise_exception
FROM DUAL;
and:
BEGIN
DBMS_OUTPUT.PUT_LINE( raise_exception );
END;
/
do not know how to handle the exception and terminate with ORA-01476 divisor is equal to zero
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With