Can't use PLSQL ConvertToBlob inside a loop - plsql

I have a problem when trying to convert CLOB from a table to BLOB into another table.
Basically I'm looping inside a PLSQL array, the first call to DBMS_LOB.convertToBlob always works well, but the next iterations either create an empty blob or give me an error ORA-22275: invalid LOB locator specified, depending on whether I initialize my blob inside or outside the loop.
So, if I do :
BEGIN
FOR i IN 1 .. rs.COUNT
LOOP
DBMS_LOB.createTemporary (v_blob, TRUE);
DBMS_LOB.convertToBlob (v_blob,
rs (i).v_clob,
DBMS_LOB.LOBMAXSIZE,
v_in,
v_out,
DBMS_LOB.DEFAULT_CSID,
v_lang,
v_warning);
[...]
DBMS_LOB.freeTemporary(v_blob);
It converts the first blob well but only returns empty blobs for the other ones.
If I do:
BEGIN
DBMS_LOB.CREATETEMPORARY (v_blob, TRUE);
FOR i IN 1 .. rs.COUNT
LOOP
DBMS_LOB.convertToBlob(...);
It also converts the first blob well but I get the ORA-22275: invalid LOB locator specified error after the first iteration.
How could I avoid this? I can't seem to find good explanation for this. Thanks for your help!

Must be a NULL value problem. The CLOB must not be NULL. The following code gives me the error on the third round.
set serveroutput on
declare
TYPE rs_rec_type IS RECORD (
v_clob clob
);
TYPE rs_rec_table_type IS TABLE OF rs_rec_type INDEX BY pls_integer;
rs rs_rec_table_type;
v_blob blob;
v_in integer := 1;
v_out integer := 1;
v_lang integer := 0;
v_warning integer := 0;
BEGIN
rs(1).v_clob := 'foo';
rs(2).v_clob := 'bar';
rs(3).v_clob := null;
FOR i IN 1 .. rs.COUNT
LOOP
DBMS_LOB.createTemporary (v_blob, TRUE);
dbms_output.put_line('i='||i);
DBMS_LOB.convertToBlob (v_blob,
rs (i).v_clob,
DBMS_LOB.LOBMAXSIZE,
v_in,
v_out,
DBMS_LOB.DEFAULT_CSID,
v_lang,
v_warning);
dbms_output.put_line('done i='||i);
DBMS_LOB.freeTemporary(v_blob);
end loop;
end;
Output
Error report:
ORA-06502: PL/SQL: numeric or value error: invalid LOB locator specified: ORA-22275
ORA-06512: at "SYS.DBMS_LOB", line 991
ORA-06512: at line 20
06502. 00000 - "PL/SQL: numeric or value error%s"
*Cause:
*Action:
i=1
done i=1
i=2
done i=2
i=3

I experienced a similar problem when using temporary blobs within a loop.
I resolved it by initialising the in, out, lang and warning parameters each time.
James.

This worked for me:
declare
cursor note is
select id, rtf_clob
from rtf_data
where rtf_clob is not null
for update of rtf_blob;
l_blob blob;
l_amt integer := dbms_lob.lobmaxsize;
l_dest_offset integer := 1;
l_src_offset integer := 1;
l_csid integer := dbms_lob.default_csid;
l_ctx integer := dbms_lob.default_lang_ctx;
l_warn integer;
begin
for note_rec in note loop
l_blob := null;
l_amt := dbms_lob.lobmaxsize;
l_dest_offset := 1;
l_src_offset := 1;
l_csid := dbms_lob.default_csid;
l_ctx := dbms_lob.default_lang_ctx;
l_warn := null;
dbms_lob.createTemporary(l_blob, true);
dbms_lob.convertToBlob(l_blob,
note_rec.rtf_clob,
l_amt,
l_dest_offset,
l_src_offset,
l_csid,
l_ctx,
l_warn );
update rtf_data
set rtf_blob = l_blob
where note_rec.id = id;
dbms_lob.freeTemporary(l_blob);
end loop;
end;
/
Without reinitializing the variables inside the loop, only the first record created a blob.

This error might occur when the CLOB contains NULL.
This means the CLOB variable is passed NULL to the DBMS_LOB.convertToBlob

I had a similar problem to this and I figured something about DBMS_LOB.convertToBlob. The variables that you enter as dest_offset and src_offset change after the go through the procedure in question so you have to reset them after every iteration, in the case that you want to create multiple files.
I don't know if that helps directly with this problem in particular, but keep it in mind, for future reference, in case one uses DBMS_LOB.convertToBlob inside a loop.

Related

How to Declare VARRAY with Integer variable as Number of Elements in Oracle19c

Trying to declare a PL SQL VARRAY with the 'Number of Elements' as a previously declared variable but I get this error:
PLS-00325: non-integral numeric literal NUM_ENV_COUNT is inappropriate in this context
Here is what I tried:
Declare
num_Env_Count INTEGER := 3;
type arr_text is VARRAY(num_Env_Count) of VARCHAR2(2000);
arr_Env_Type arr_text := ('D','V','P');
Begin
for i in 1..num_Env_Count loop
DBMS_OUTPUT.PUT_LINE(arr_Env_Type(i));
end loop;
end;
No. Have you tried it? If you do/did then you get a PLS-00320 indication that the expression is malformed. I understand the desire to avoid repeating the litteral variable. You can achieve the same wit a nested table.
Declare
type arr_text is table of VARCHAR2(2000) ;
arr_Env_Type arr_text := arr_text('D','V','P');
Begin
for i in 1..arr_Env_Type.count loop
DBMS_OUTPUT.PUT_LINE(arr_Env_Type(i));
end loop;
end;

plsql Result consisted of more than one row. How to handel it

CREATE PROCEDURE book_check(book_Id varchar(64))
begin
declare book_available varchar(64);
select book_id into book_available
from book_copies
where No_of_Copies >0 and book_id=book_Id;
if(book_Id in book_available ) then
select concat ("Book available");
else
select concat ("Book not available");
end if;
end
//
what can i write in place of 'in' . I know the syntax i wrong .
It's easy - try something like this:
create or replace function book_check(book_id varchar) return varchar as
begin
for r in (select 1 from book_copies where no_of_copies > 0 and book_id = book_check.book_id) loop
return 'Book available';
end loop;
return 'Book not available';
end book_check;
/
It's unclear to me what you are trying to do. I assume you want to find out if a book is available or not and return that information to the caller of the function.
Your declaration of the procedure header and the variables is wrong.
Procedure or function parameters are not defined with a length for the datatype.
Inside a procedure or function you don't need declare
you can't have a select statement without putting the result somewhere. * Assigning a constant value to a variable is done using :=
If you want to return information to the caller, use a function, not a procedure
You should not give variables or parameters the same name as a column. A common naming convention in the Oracle world is to give parameters the prefix p_ and local variables the prefix l_ but anything that avoids a name clash between column names and variables is OK - just be consistent.
CREATE function book_check(p_book_id varchar)
return varchar
as
l_count integer;
l_result varchar(20);
begin
select count(*)
into l_count
from book_copies
where No_of_Copies > 0
and book_id = p_book_id;
if l_count > 0 then
l_result := 'Book available';
else
l_result := "Book not available";
end if;
return result;
end;
/
You should really take the time and read the PL/SQL Language reference. All the above is explained there.

Example for SQL_TO_JSON

How to use the function JSON_UTIL_PKG.SQL_TO_JSON? Please give an example.
I tried this:
select JSON_UTIL_PKG.SQL_TO_JSON('select column from table where rownum = 1',100) from dual;
but the result is not ok
The declaration of that function is:
function sql_to_json (p_sql in varchar2,
p_max_rows in number := null,
p_skip_rows in number := null) return json_list
Thus, the result should be a pljson.json_list object. Since the only information you have given is "the result is not okay" I can only assume you are expecting the result to be a JSON string. If that is the case, and your result is stored in a variable named foo, then you can use foo.to_char to generate the string. Or foo.to_clob to return the JSON string as a CLOB.
I tried to execute as:
declare jlist json_list;
Begin
jlist := json_utl_pkg.sql_to_json('select col1, col2 from mytable', 10, 0);
end;
This shows an error like "jlist invalid left assignement"...
Then I tried this one, and it worked:
declare jobj json;
begin
jobj := json_dyn.executeObject('select * from myTable');
jobj.print;
end;
It worked correctly!
Based on:https://github.com/oberstet/pljson/blob/master/examples/ex16.sql
Best regards

Return ref_cursor from for loop of cursor object

I am trying to get a ref_cursor to be assigned to a variable inside a for loop then returned at the end of a function. The loop in going through a local cursor if it gets more than 1 result.
I have noted where the error occurs in the code. I am not sure how to create a loop where i can get a ref_cursor for the current point in the loop, assign it to a variable and then return it to the function. Could someone someone help me figure out how to do that? Below is my i-th attempt at logic based of reading around the google searches.
The error is "PLS-00382: expression is of wrong type" and i know that i obviously am not assigning the correct variably type based on this error but the code below with the error is an illustration of what I want to do and what I need help accomplishing.
FUNCTION GET_PARCEL(p_lat in number, p_long in number) return sys_refcursor
IS
v_distance number(10) := 100000000;
v_shortest_dist number(10) := v_distance;
v_centroid SDO_GEOMETRY;
v_rc_ref_cursor sys_refcursor;
v_ref_geom SDO_GEOMETRY := mdsys.SDO_GEOMETRY(2001, 8311, NULL, SDO_ELEM_INFO_ARRAY(1, 1, 1), SDO_ORDINATE_ARRAY(120.3214, -10.7088));
cursor query_cursor is select * from PARCEL_TABLE where code = 20134;
BEGIN
for query_row in query_cursor loop
v_centroid := SDO_GEOM.SDO_CENTROID(query_row.geometry, 0.05);
IF (v_centroid is not null) then
v_distance := SDO_GEOM.SDO_DISTANCE(v_centroid, v_ref_geom, 0.05);
IF v_distance < v_shortest_dist THEN
v_shortest_dist := v_distance;
v_rc_ref_cursor := query_row; -- Error on this line
END IF;
ELSE
DBMS_OUTPUT.PUT_LINE('Centroid is not initialised for some reason.');
END IF;
end loop;
return v_rc_ref_cursor;
END;
As far as I know, you cannot build up a cursor. Cursors are created and maintained by the database with connections to their source data, transaction and session context, and the like.
I would suggest you declare a type, instantiate it, build up the values in the type.
When you are done, create a cursor by selecting * from table (cast (variable as your_type)).
Munge the cursor into a ref_cursor and return that.
Pro tip is to remember you have data in your table and you can use it for logic. Turns out if I use the IDENTIFIER or ROWID of the row/s which i want then i can do a where clause into a ref cursor that looks up by IDENTIFIER or ROWID.
eg.
open v_rc_ref_cursor for select * from PARCEL_TABLE n where n.identifier = query_row.identifier;
super simple stuff :)

Oracle 11g : When declaring new TYPE as TABLE, must I add "INDEX BY PLS_INTEGER"?

What is the diffecence between adding INDEX BY PLS_INTEGER and not at end of declaration of new table type. Look at this example:
DECLARE
GC_BULK_LIMIT CONSTANT INTEGER := 500;
CURSOR CUR_CLIENTS IS SELECT C.ID, C.NAME FROM CLIENTS C;
TYPE RT_CLIENTS IS TABLE OF CUR_CLIENTS%ROWTYPE;
-- TYPE RT_CLIENTS IS TABLE OF CUR_CLIENTS%ROWTYPE INDEX BY PLS_INTEGER;
LT_CLIENTS RT_CLIENTS;
BEGIN
OPEN CUR_CLIENTS;
LOOP
FETCH CUR_CLIENTS BULK COLLECT INTO LT_CLIENTS LIMIT GC_BULK_LIMIT;
EXIT WHEN LT_CLIENTS.COUNT = 0;
FOR I IN 1..LT_CLIENTS.COUNT LOOP
-- ... SOME LOGIC
END LOOP;
END LOOP;
CLOSE CUR_CLIENTS;
END;
in response to "must i add". The short answer is NO.
the difference is that
TYPE RT_CLIENTS IS TABLE OF CUR_CLIENTS%ROWTYPE;
Is a nested table. This means that for a given variable of this type, we know that the subscripts are sequential. i.e. the subscript starts from 1 and goes up to the array length.
The following loop therefore, is the right way to access a nested table array:
FOR I IN 1..LT_CLIENTS.COUNT LOOP
This however, is called a associative array:
TYPE RT_CLIENTS IS TABLE OF CUR_CLIENTS%ROWTYPE INDEX BY PLS_INTEGER;
(you could also index by a varchar2 if you wanted). The difference is that the subscripts in this case do not have to be sequential, depending on how the array was populated. In your code, they would be (as bulk collect would do that), but its not always the case.
The safe way to access and loop through an index by array is :
v_subscript := t_arr.first;
while v_subscript is not null loop
dbms_output.put_line(v_subscript || ': ' || t_arr(v_subscript));
v_subscript := t_arr.next(v_subscript);
end loop;
where v_subscript is a variable of the same datatype of the index by part.
also with a nested table, you can populate the array quickly with:
declare
type myarr is table of number;
t_arr myarr;
v_subscript number;
begin
t_arr := myarr(1, 12, 44);
whereas with an index by array you'd have to have three lines there to populate it:
t_arr(1):= 1;
t_arr(2):= 12;
t_arr(3):= 44;
for your particular case, without the index by is perfectly fine.
further reading: http://docs.oracle.com/cd/E18283_01/appdev.112/e17126/composites.htm

Resources