I have a procedure that populates a BLOB variable with a PDF document. What I am trying to do is add logic to only display the PDF document within a 60 day period from a static date. See below:
check_staticdate number(1);
function DisplayPDF (audit in number) RETURN blob is
person_id person.person_id%type;
z_lob blob;
blob_length NUMBER;
CURSOR getPDF(audit number) IS
select report
from report_table
where report_type = 'PDF'
and job_no = audit order by rec_no;
begin
/* Check Valid ID */
if not package.ValidID(person_id, check_only=>TRUE) then
return z_lob;
end if;
/* Here is the case statement.*/
select case
when exists
(
SELECT 'x' from table
where table_id = person_id
and trunc(sysdate) < trunc(table_static_date + 60)
)
then 1
else 0
end into check_staticdate
from dual;
if (check_staticdate = 0) then
return z_lob;
end if;
open getPDF(audit);
fetch getPDF into z_lob;
close getPDF;
return z_lob;
end DisplayPDF;
The error I am receiving is: ORA-22275: invalid LOB locator specified.
I am new to Oracle SQL, and am unsure why my ValidID check works by returning z_lob but my case statement does not.
Edit: Adding full error stack
Failed to execute target procedure ORA-22275: invalid LOB locator specified
ORA-06512: at "SYS.WPG_DOCLOAD", line 51
ORA-06512: at "User.Package", line 733
ORA-06512: at line 33
Initialise your lob with temporary first
DBMS_LOB.CREATETEMPORARY(z_lob,true); --true if you want it to be cached.
Your function is probably returning (depending on your audit parameter value) a blob with a NULL value to the SYS.WPG_DOCLOAD method which throws the unhandled exception you see.
Perhaps you could modify your return z_lob; to be return nvl(z_lob, empty_blob());
Related
I am trying to take a count of the records in the interactive grid and based on that I am trying to pass a message to the user. However, I am getting error : ORA-06550: line 1, column 141: PLS-00103: Encountered the symbol "NUMBER" when expecting one of the following: := . ( # % ; The symbol "." was substituted for "NUMBER" to continue.Following is my code in the validation. Validation Type is : Function Returning Error Text.
l_count NUMBER := 0;
BEGIN
SELECT COUNT(1)
INTO l_count
FROM ugh
WHERE ugh.pre = :PRE
AND ugh.APP1 = :APP1
AND ugh.APP2 = :APP2
AND ugh.APP3 = :APP3
AND ugh.FINL_APP = :FINL_APP;
IF l_count > 1 THEN
IF END_DATE IS NULL THEN
RETURN 'Error Message to be displayed.';
ELSE
RETURN NULL;
END IF;
ELSE
RETURN NULL;
END IF;
END;
Can anyone please help ?
Looks like you're missing the DECLARE keyword:
DECLARE --> this
l_count NUMBER := 0;
BEGIN
SELECT COUNT (1)
INTO l_count
FROM ugh
Also, what is END_DATE? You never declared it. If it is a page item, then precede it with a colon, :END_DATE
I get this error
23/112 PL/SQL: ORA-00920: invalid relational operator
It's pointing to AND CURRENT OF statement..
CREATE OR replace PROCEDURE alga_uz_pasirodyma(grupe_id in grupes.id%TYPE, alga in out number)
IS
v_kliento_id nariai.asm_kodas%TYPE;
TYPE bendras IS RECORD (
alga number
);
globalus bendras;
CURSOR c_klientai IS
SELECT nariai.asm_kodas
FROM nariai
where nariai.fk_grupe = grupe_id
FOR UPDATE OF nariai.alga;
BEGIN
globalus.alga:= alga;
IF grupe_id <= 0 THEN
raise_application_error(-20101, 'Nepavyko surasti grupes');
END IF;
OPEN c_klientai;
LOOP
FETCH c_klientai INTO v_kliento_id;
EXIT WHEN c_klientai%NOTFOUND;
UPDATE nariai set nariai.alga = nariai.alga * globalus.alga where nariai.asm_kodas = v_kliento_id AND CURRENT OF c_klientai;
END LOOP;
UPDATE grupes set grupes.pasirodymu_kiekis = grupes.pasirodymu_kiekis + 1 where grupes.id = grupe_id;
SELECT max(nariai.alga) into alga from nariai where nariai.fk_grupe = grupe_id;
CLOSE c_klientai;
END alga_uz_pasirodyma;
What should I do? I believe everything is declared correctly in the where statement..
"CURRENT OF" should be by itself in the where clause. It allows you to update or delete the record at the current loop iteration of the cursor.
On a different note, I don't see you doing anything significant in the loop to warrant a cursor. Ignore this note if that will change, otherwise just run the update "where nariai.fk_grupe = grupe_id"
How can I pass a variable value inside the xpath of xmltable?
DECLARE
v NUMBER := 0;
BEGIN
SELECT *
FROM xml_billrun_files t ,
xmltable('/invoice/AR_ITEMS[#elem='||v||']/ITEMS/USAGE_RECORDS/SESSION_INFO'
passing t.update_xmldoc
columns chrg_duration VARCHAR2(20) path 'DURATION',
amount NUMBER path 'AMOUNT') x;
END;
I've tried this one.
DECLARE
v NUMBER := 0;
BEGIN
SELECT *
FROM xml_billrun_files t ,
xmltable('/invoice/AR_ITEMS[#elem=$i]/ITEMS/USAGE_RECORDS/SESSION_INFO'
passing t.update_xmldoc, xmltype(v) as "i"
columns chrg_duration VARCHAR2(20) path 'DURATION',
amount NUMBER path 'AMOUNT') x;
END;
But it returns an error:
ORA-31011: XML parsing failed
ORA-19202: Error occurred in XML processing
LPX-00210: expected '<' instead of '0'
Error at line 1
ORA-06512: at "SYS.XMLTYPE", line 310
ORA-06512: at line 1
ORA-06512: at line 5
31011. 00000 - "XML parsing failed"
*Cause: XML parser returned an error while trying to parse the document.
*Action: Check if the document to be parsed is valid.
thanks for the help.
In my answer to your previous question, I incorrectly used xmltype(lp) as "lp" for your XMLQuery call. Not sure why it didn't complain there, but it didn't actually restrict the match anyway...
For this XMLTable call you can pass the number directly, without conversion/cast, as it's already a number:
SELECT chrg_duration, amount
into ...
FROM xml_billrun_files t ,
xmltable('/invoice/AR_ITEMS[#elem=$i]/ITEMS/USAGE_RECORDS/SESSION_INFO'
passing t.update_xmldoc, v as "i"
columns chrg_duration VARCHAR2(20) path 'DURATION',
amount NUMBER path 'AMOUNT') x;
If you're doing it in a for loop, then the index is the wrong data type, and you need to cast it to number:
SELECT chrg_duration, amount
into ...
FROM xml_billrun_files t ,
xmltable('/invoice/AR_ITEMS[#elem=$i]/ITEMS/USAGE_RECORDS/SESSION_INFO'
passing t.update_xmldoc, cast(v as number) as "i"
columns chrg_duration VARCHAR2(20) path 'DURATION',
amount NUMBER path 'AMOUNT') x;
I have package with Period and TableOfPeriod types:
TYPE Period
IS RECORD
( StartPeriod Date,
EndPeriod Date
);
TYPE TableOfPeriod
IS TABLE OF Period;
and in this package I have three simple function:
FUNCTION Temp1
RETURN TableOfPeriod IS
returnedValue TableOfPeriod := TableOfPeriod();
BEGIN
returnedValue.extend(1);
returnedValue(1).StartPeriod := sysdate-100;
returnedValue(1).EndPeriod := sysdate;
RETURN returnedValue;
END Temp1;
FUNCTION CalculateFine
return VARCHAR2
IS
freewillLockTableRP TableOfPeriod:=TableOfPeriod();
compulsoryLockTableRP TableOfPeriod:=TableOfPeriod();
BEGIN
--for testing
compulsoryLockTableRP:=Temp1();
FOR i IN compulsoryLockTableRP.FIRST..compulsoryLockTableRP.LAST LOOP
IF(((compulsoryLockTableRP(i).EndPeriod - compulsoryLockTableRP(i).StartPeriod)>1)) THEN
BEGIN
-- RAISE_APPLICATION_ERROR(-20001, 'Hello world');
SELECT T111.StartPeriod StartPeriod,
T111.EndPeriod EndPeriod
bulk collect into freewillLockTableFull
FROM TABLE(DistributeDaysByPeriods(compulsoryLockTableRP, 5)) T111;
END;
END IF;
END LOOP;
/*SELECT T111.StartPeriod StartPeriod,
T111.EndPeriod EndPeriod
-- BULK COLLECT INTO compulsoryLockTableRP
bulk collect into freewillLockTableFull
FROM TABLE(DistributeDaysByPeriods(compulsoryLockTableRP, 5)) T111;*/
--===========
--SOME OTHER PROCESSING
RETURN 'Ok '
|| '#r';
EXCEPTION
WHEN No_Data_Found THEN return 'No data found#g';
-- WHEN OTHERS THEN RETURN SQLERRM;
END CalculateFine;
When I execute this function, I have next error:
"ORA-21700: object does not exist or is marked for delete ORA-06512:
at "MyPackageName", line 1181 ORA-06512: at line 1
21700. 00000 - "object does not exist or is marked for delete""
where 1181 line is a line with Select statement of CalculateFine function. Can anybody tell me, whats wrong and how I can solve this problem?
Check if you have in the same folder where your package is located a file with the same name but with an extention *.~sql (e.g.: your_file_name.~sql). I had the same error but after I have deleted an *.~sql file I could compile my package without ORA errors.
I have multiple stored procedures calling multiple stored procedures in my database. To give a small example, I've constructed a fictionalised version of a few of them below. In my example, a Java program calls calculate_bill, which calls calculate_commission, which calls update_record.
I'm hoping to get some advice on how best to propagate the error messages up the stack to the calling Java layer, so the user gets a precise error message corresponding to wherever the error has occurred.
I'm really quite stuck on this. I've played around in my example with raise_application_error and just continually shuffling it up the stack. Is the way I'm doing it below remotely correct? Or is one raise_application_error in the relevant procedure enough, with no pragma exception init etc needed?
To give an idea of what I mean, in the example below, if a user entered a number which corresponded to a record which couldn't be updated because it didn't exist, I'd like them to get the message:
"Error calculating bill. Error calculating commission. No record exists to be updated" or something to that effect.
So two questions:
What is the best practice, most efficient, most tidy way to pass error messages up the stack for the end user in the application layer?
Does anyone have any suggestions on tidier output from the code, i.e. the best way to concatenate these errors to make them more meaningful? I'm really open to any suggestions on how to make this work best as I have absolutely no prior experience in this.
Example:
(Error in code):
-20000 : Error in top level procedure
-20001 : Error in middle level procedure
-20002 : Error in bottom level procedure
Java code:
try {
// call calculate_bill
exception (SQLException ex)
// output oracle code and relevant message.
Oracle code:
create or replace procedure calculate_bill(in_num NUMBER)
is
error_calculating_commission EXCEPTION;
error_updating_record EXCEPTION;
PRAGMA EXCEPTION_INIT (error_calculating_commission, -20001);
PRAGMA EXCEPTION_INIT (error_updating_record , -20002);
begin
if in_num > 2 then
calculate_commission(in_num);
else
raise_application_error(-20000, 'Error calculating bill. ' || 'Record number doesn''t exist.', false);
end if;
exception
when error_calculating_commission then
raise_application_error(SQLCODE, 'Error calculating bill. ' || SQLERRM, false);
when error_updating_record then
raise_application_error(SQLCODE, 'Error calculating bill. ' || SQLERRM, false);
when others then
raise_application_error(-20000, 'Unknown error encountered calculating bill.', false);
end;
create or replace procedure calculate_commission(in_num NUMBER)
is
begin
if in_num < 30 then
raise_application_error(-20001, 'Number too small to calculate commission.', false);
elsif in_num >= 30 and < 40 then
declare
error_storing_record EXCEPTION;
PRAGMA EXCEPTION_INIT (error_storing_record , -20002);
begin
update_record(in_num);
exception
when error_storing_record then
raise_application_error(SQLCODE, 'Error calculating commission. ' || SQLERRM, false);
when others then
raise_application_error(-20001, 'Unknown error encountered calculating commission.', false);
else
raise_application_error(-20001, 'Number too large to calculate commission', false);
end if;
end;
create or replace procedure update_record(in_num NUMBER)
is
begin
//some SQL query with a where clause, where in_num equals something
exception
when no_data_found then
raise_application_error(-20002, 'No record exists to be updated', false);
when others then
raise_application_error(-20002, 'Unknown error encountered updating record.', false);
end if;
end;
Note: I know this example is a little contrived. I was just trying to keep it brief.
The way I would implement this is to use RAISE_APPLICATION_ERROR where the error actually originates, then leave it unhandled in the other layers (or, if you want to do logging in the database, catch it in the OTHERS section, then re-raise using RAISE rather than RAISE_APPLICATION_ERROR.
You're OTHERS sections are still perpetuating the problem raised in your previous question: when an unknown error occurs you're replacing it with a generic, unhelpful message. To paraphrase Tom Kyte, "a WHEN OTHERS that does not end in RAISE is a bug`.
With no logging in the database, I would re-write the provided code like this:
create or replace procedure calculate_bill(in_num NUMBER)
is
begin
if in_num > 2 then
calculate_commission(in_num);
else
raise_application_error(-20000, 'Error calculating bill. ' || 'Record number doesn''t exist.', false);
end if;
end;
create or replace procedure calculate_commission(in_num NUMBER)
is
begin
if in_num < 30 then
raise_application_error(-20001, 'Number too small to calculate commission.', false);
elsif in_num >= 30 and in_num < 40 then
update_record(in_num);
else
raise_application_error(-20001, 'Number too large to calculate commission', false);
end if;
end;
create or replace procedure update_record(in_num NUMBER)
is
v_stub number;
begin
select 1 into v_stub from dual where 1 = 0;
exception
when no_data_found then
raise_application_error(-20002, 'No record exists to be updated', false);
end;
Below is sample input and the stack traces they created:
exec calculate_bill(0)
ORA-20000: Error calculating bill. Record number doesn't exist.
ORA-06512: at "SCHEMANAME.CALCULATE_BILL", line 7
ORA-06512: at line 1
exec calculate_bill(10)
ORA-20001: Number too small to calculate commission.
ORA-06512: at "SCHEMANAME.CALCULATE_COMMISSION", line 5
ORA-06512: at "SCHEMANAME.CALCULATE_BILL", line 5
ORA-06512: at line 1
exec calculate_bill(35)
ORA-20002: No record exists to be updated
ORA-06512: at "SCHEMANAME.UPDATE_RECORD", line 8
ORA-06512: at "SCHEMANAME.CALCULATE_COMMISSION", line 7
ORA-06512: at "SCHEMANAME.CALCULATE_BILL", line 5
ORA-06512: at line 1
exec calculate_bill(100)
ORA-20001: Number too large to calculate commission
ORA-06512: at "SCHEMANAME.CALCULATE_COMMISSION", line 9
ORA-06512: at "SCHEMANAME.CALCULATE_BILL", line 5
ORA-06512: at line 1
If you were to add error logging to these procedures, it would be a simple matter of adding an OTHERS clause to the EXCEPTION sections:
WHEN OTHERS THEN
my_logging (SQLCODE, SQLERRM, DBMS_UTILITY.format_error_backtrace ());
RAISE;
The new exception created by your RAISE_APPLICATION_ERROR calls will be handled transparently by the RAISE.