The following PL/SQL code shows the function ensure(...) and how I will use it in Oracle 11g.
declare
valid boolean;
function ensure(b boolean, failure_message varchar) return boolean is begin
if not b then
dbms_output.put_line(failure_message);
return false;
else
return true;
end if;
end ensure;
begin
valid := true;
valid := valid
and ensure(1=1, 'condition 1 failed')
and ensure(1=2, 'condition 2 failed')
and ensure(2=3, 'condition 3 failed');
if not valid then
dbms_output.put_line('some conditions failed, terminate the program');
return;
end if;
dbms_output.put_line('do the work');
end;
/
I want to use ensure(...) to pre-validate a set of conditions, the program is allowed to do the work only if all conditions pass.
I want the program to evaluate every ensure(...) even if the preceding ensure(...) return false, so that the failure_message will be printed for every conditions that fails.
The problem is Oracle uses short-circuit evaluation and ignore the rest conditions that come after the one that return false. For example, the above program prints the following message.
condition 2 failed
some conditions failed, terminate the program
How to tell Oracle to not use short-circuit evaluation so that the above program print the following message.
condition 2 failed
condition 3 failed
some conditions failed, terminate the program
Try:
declare
valid boolean;
con1 boolean;
con2 boolean;
con3 boolean;
function ensure(b boolean, failure_message varchar) return boolean is begin
if not b then
dbms_output.put_line(failure_message);
return false;
else
return true;
end if;
end ensure;
begin
valid := true;
con1 := ensure(1=1, 'condition 1 failed') ;
con2 := ensure(1=2, 'condition 2 failed') ;
con3 := ensure(2=3, 'condition 3 failed');
valid := con1 AND con2 AND con3;
if not valid then
dbms_output.put_line('some conditions failed, terminate the program');
return;
end if;
dbms_output.put_line('do the work');
end;
/
I usually validate preconditions with assertions. I don't know if this is suitable in the OP's case, but I think it's a worth of mentioning as a viable solution alternative.
Example:
declare
procedure assert(p_cond in boolean, p_details in varchar2 default null) is
begin
if not p_cond then
raise_application_error(-20100, p_details);
end if;
end;
begin
assert(1 = 1, 'first');
assert(1 = 2, 'second');
assert(1 = 1, 'third');
-- all preconditions are valid, processing is "safe"
end;
/
Obviously in real code/logic one must considered how the exceptions should be handled. Also state variables might be used/required.
Related
I have the following function in a package body:
FUNCTION valida_salario(p_salary IN employees.salary%TYPE,
p_department_id IN employees.department_id%TYPE)
RETURN BOOLEAN IS
v_prom_depto NUMBER;
v_var_depto NUMBER;
BEGIN
SELECT AVG(salary), VARIANCE(SALARY) INTO v_prom_depto, v_var_depto
FROM employees
WHERE department_id=p_department_id;
-- GROUP BY department_id;
IF p_salary < v_prom_depto + 3* sqrt(v_var_depto) THEN
RETURN TRUE;
ELSE
RETURN FALSE;
END IF;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line(SQLERRM);
END valida_salario;
And when I am using the function in a stored procedure (the procedure is in the body too), it show errors:
PROCEDURE crea_empleado(p_last_name IN employees.last_name%TYPE,
p_first_name IN employees.first_name%TYPE,
p_email IN employees.email%TYPE,
p_hire_date IN employees.hire_date%TYPE,
p_job_id IN employees.job_id%TYPE,
p_department_id IN employees.department_id%TYPE
DEFAULT 80,
p_resultado OUT NUMBER) AS
e_salario_no_valido EXCEPTION;
BEGIN
IF valida_salario(p_salary, p_department_id) THEN // Here is located the errors
NULL;
ELSE
RAISE e_salario_no_valido;
END IF;
INSERT INTO employees(employee_id, last_name, first_name, email, hire_date, job_id)
VALUES (emp_seq.NEXTVAL, p_last_name, p_first_name, p_email, p_hire_date, p_job_id);
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
p_resultado := 2;
dbms_output.put_line('Datos duplicados');
WHEN e_salario_no_valido THEN
p_resultado := 1;
WHEN OTHERS THEN
dbms_output.put_line(SQLERRM);
ROLLBACK;
END crea_empleado;
The following errors are :
PL/SQL: Statement ignored and
PLS-00201: the identifier 'P_SALARY' must be declared
But I can't see the error in my function, so I don't understand why it shows these errors.
Thanks in advance for your help
You need to pass p_salary into your crea_empleado() procedure. p_department_id is there but no p_salary.
I have a scenario in which, i am writing the exception like this
when others then
rollback;
p_status := 'ERROR'; -- MODIFIED
p_status_dtl := sqlcode||' - '||substr(sqlerrm,1,100);
end;
if i write like this
exception
when others then
p_status := 'ERROR'; -- MODIFIED
p_status_dtl := sqlcode||' - '||substr(sqlerrm,1,100);
rollback;
end;
does it make a difference if yes what is the difference.
The examples are identical if the variable assignments don't throw (i.e. both p_status and p_status_dtl are correct data type with enough storage).
If the variable assignment throws then in the second example rollback is not executed.
In the example below only checkpoint 1 will be printed:
declare
v_foo varchar2(2) := 'AB';
v_bar number;
begin
-- raises ORA-01476: divisor is equal to zero
v_bar := 1/0;
exception
when others then
dbms_output.put_line('checkpoint 1');
-- raises ORA-06502: PL/SQL: numeric or value error: character string buffer too small
v_foo := 'TOO LONG';
dbms_output.put_line('checkpoint 2');
end;
/
hi I wrote this code to create a procedure to return a Boolean value based on the if conditions but when I execute it I got this error:
ORA-06550: line 1, column 7:
PLS-00306: wrong number or types of arguments in call to 'DDPAY_SP'
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
here is my procedure
create or replace procedure DDPAY_SP (
donor_id dd_donor.iddonor%type,
pldgstatus out dd_pledge.idstatus%type,
monthplan out dd_pledge.paymonths%type,
ret out boolean)
IS
begin
select idstatus, paymonths into
pldgstatus, monthplan from dd_pledge
where iddonor = donor_id ;
if (pldgstatus = 10 AND monthplan >0)
then ret:= true;
else
ret:= false;
end if;
end;
and this how I execute it
EXECUTE DDPAY_SP (308);
I didn't put much talk I hope it's clear enough for you
I read online it recommends me to check the naming also the data type which I did but nothing change
any ideas
If you don't need the second and third arguments you could declare those as variables in the procedure instead of arguments, as follows:
CREATE OR REPLACE PROCEDURE DDPAY_SP(DONOR_ID IN DD_DONOR.IDDONOR%TYPE,
RET OUT BOOLEAN)
IS
nPayment_count NUMBER;
BEGIN
SELECT COUNT(*)
INTO nPayment_count
FROM DD_PLEDGE p
WHERE p.IDDONOR = DONOR_ID AND
p.IDSTATUS = 10 AND
p.PAYMONTHS > 0;
IF nPayment_count > 0 THEN
RET := TRUE;
END IF;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('DD_PAY - exception: ' || SQLCODE || ' : ' || SQLERRM);
RAISE;
END DDPAY_SP;
I've included an example of an EXCEPTION handler at the end of DD_PAY. It's always a good idea to include at least this minimal handler so that in the event an exception occurs you'll get some indication of where the problem lies.
Because this procedure returns a BOOLEAN value, and BOOLEANs cannot (to the best of my knowledge) be used from SQL*Plus, you'll have to invoke it from a PL/SQL block, as follows:
DECLARE
bRetval BOOLEAN;
BEGIN
DD_PAY(308, bRetval);
DBMS_OUTPUT.PUT_LINE('Returned value is ' ||
CASE bRetval
WHEN TRUE THEN 'TRUE'
ELSE 'FALSE'
END);
END;
Give that a try.
EDIT: rewrote procedure based on further information from later comments.
Share and enjoy.
In my response 2 rows is different from '.' and will therefor print out, and should increment the "myCounter".
But in both the print outs 1 as in myCounter doesn't gets incremented...
function R_G_cnFormatTrigger return boolean is
myCounter number :=0;
begin
-- Automatically Generated from Reports Builder.
if (mod(myCounter,2) = 0)
then
srw.set_foreground_fill_color('gray8');
srw.set_fill_pattern('solid');
else
srw.set_foreground_fill_color('');
srw.set_fill_pattern('transparant');
end if;
if(:CP_WAYBILL_NO <> '.')
then
myCounter:=(myCounter+1);
srw.message(123,'myCounter:'||myCounter);
return true;
else
return false;
end if;
end;
When you print myCounter it always equals 1, right? This is because you return true; at the end of if(:CP_WAYBILL_NO <> '.').
When you use return in function, it breaks the execution. myCounter is a local variable, so its value isn't remembered.
You can try creating a package with myCounter as global variable or read/write myCounter from/to temporary table.
I have a function which would return a record with type my_table%ROWTYPE, and in the caller, I could check if the returned record is null, but PL/SQL complains the if-statement that
PLS-00306: wrong number or types of arguments in call to 'IS NOT NULL'
Here is my code:
v_record my_table%ROWTYPE;
v_row_id my_table.row_id%TYPE := 123456;
begin
v_record := myfunction(v_row_id)
if (v_record is not null) then
-- do something
end if;
end;
function myfunction(p_row_id in my_table.row_id%TYPE) return my_table%ROWTYPE is
v_record_out my_table%ROWTYPE := null;
begin
select * into v_record_out from my_table
where row_id = p_row_id;
return v_record_out;
end myfunction;
Thanks.
As far as I know, it's not possible. Checking the PRIMARY KEY or a NOT NULL column should be sufficient though.
You can check for v_record.row_id IS NULL.
Your function would throw a NO_DATA_FOUND exception though, when no record is found.
You can't test for the non-existence of this variable so there are two ways to go about it. Check for the existence of a single element. I don't like this as it means if anything changes your code no longer works. Instead why not just raise an exception when there's no data there:
I realise that the others in the exception is highly naughty but it'll only really catch my table disappearing when it shouldn't and nothing else.
v_record my_table%ROWTYPE;
v_row_id my_table.row_id%TYPE := 123456;
begin
v_record := myfunction(v_row_id)
exception when others then
-- do something
end;
function myfunction(p_row_id in my_table.row_id%TYPE) return my_table%ROWTYPE is
v_record_out my_table%ROWTYPE := null;
cursor c_record_out(c_row_id char) is
select *
from my_table
where row_id = p_row_id;
begin
open c_record_out(p_row_id);
fetch c_record_out into v_record_out;
if c_record_out%NOTFOUND then
raise_application_error(-20001,'no data);
end if;
close c_record_out;
return v_record_out;
end myfunction;