PL/SQL DBMS_XMLQUERY max size - oracle

My oracle version is 11g release 2
Our system is using DBMS_XMLQUERY to transform sql result into xml, but recently the data is becoming large and we get this error: ORA-06502: PL/SQL: numeric or value error
The reason seems to be DBMS_XMLQUERY cannot handle too many records, but oracle's official document doesn't show the limitation. So maybe I have done something wrong. You can reproduce the problem in the following steps:
step1:
create table XMLDATA ( data_id int primary key, data_str
VARCHAR2(100) );
step2:
INSERT INTO XMLDATA values(1, 'test0123456789');
INSERT INTO XMLDATA values(2, 'test0123456789');
INSERT INTO XMLDATA values(3, 'test0123456789');
....
INSERT INTO XMLDATA values(500, 'test0123456789');
step3:
CREATE OR REPLACE
function test(total in int) return clob is
i int;
vn_ctx DBMS_XMLQUERY.ctxHandle;
BEGIN
vn_ctx := DBMS_XMLQUERY.NEWCONTEXT('select data_id, data_str from XMLDATA where rownum <= ' || total);
DBMS_XMLQuery.propagateOriginalException(vn_ctx,true);
DBMS_XMLQUERY.useNullAttributeIndicator(vn_ctx,true);
DBMS_XMLQUERY.SETROWTAG(vn_ctx, 'ITEM');
DBMS_XMLQUERY.SETROWSETTAG(vn_ctx, 'PODATA');
return DBMS_XMLQUERY.GETXML(vn_ctx);
END;
step4:
execute function test with a number greater than 400. Then you'll get 'ORA-06502: PL/SQL: numeric or value error'
thanks in advance
EDIT
Really sorry... I got the old code, someone adds a log statement without noticing me. The log statement can only accept a maximum of 32767 characters in one line, so the error is raised. The above function is executed by a debug tool, which gives the same error, so it's the tool's problem, not oracle.
Thanks for your answering and sorry for my naive mistakes...

I get no errors if the variable to hold the return value is big enough.
declare
rv1 clob;
rv2 varchar2(32000);
begin
rv1 := test(400); -- No error
rv2 := test(200); -- No error - returned XML is < 32000 in length.
rv2 := test(400); -- ORA-06502: PL/SQL: numeric or value error'
end;

Related

How can I test my package with one function?

I made a package that compiles fine but when I try to test it it gives me "invalid data type".
I've tried two different ways, first one like this
select pkg_contabilidad.f_totalizar_Detalle(100) FROM DUAL;
It gives me the ORA-00902 'invalid data type'
Also I've tried this
DECLARE
TYPE r_registro IS RECORD
(rubro_contable CN_RUBROS_CONTABLES.COD_RUBRO%TYPE,
tipo VARCHAR2(1),
monto NUMBER(16));
resultao r_registro;
numero NUMBER :=100;
BEGIN
resultao := pkg_contabilidad.f_totalizar_detalle(numero);
END;
It gives me another error PLS-00382 'expression is of wrong type'
I don't know what am I doing wrong, cause my function receives just one parameter and is of type NUMBER, so I dont know where's my mistake. I'll leave the code of my package just in case
CREATE OR REPLACE PACKAGE pkg_contabilidad AS
TYPE r_registro IS RECORD
(rubro_contable CN_RUBROS_CONTABLES.COD_RUBRO%TYPE,
tipo VARCHAR2(1),
monto NUMBER(16));
TYPE t_detalle IS TABLE OF
r_registro INDEX BY BINARY_INTEGER;
FUNCTION f_totalizar_detalle(p_clave NUMBER)RETURN t_detalle;
END pkg_contabilidad;
/
CREATE OR REPLACE PACKAGE BODY pkg_contabilidad AS
B_detalle t_detalle;
i integer :=1;
FUNCTION f_totalizar_detalle(p_clave NUMBER) RETURN t_detalle IS
v_detalle t_detalle;
CURSOR c_facturado IS
SELECT c.cod_rubro, 'H', CASE WHEN SUM(d.gravada)=0 THEN SUM(d.iva) ELSE SUM(d.gravada) END
FROM fn_documentos_det d JOIN fn_conceptos c ON d.cod_concepto = c.cod_concepto
WHERE d.clave_doc=p_clave
GROUP BY c.cod_rubro;
CURSOR c_datos IS
SELECT SUM(d.total_doc), 'D',r.cod_rubro
FROM fn_documentos d JOIN fn_cajas_ctas r ON d.num_caja_cta = r.num_caja_cta
WHERE d.clave_doc = p_clave
GROUP BY r.cod_rubro;
BEGIN
open c_datos;
LOOP
FETCH c_datos INTO v_detalle(1);
END LOOP;
CLOSE c_datos;
FOR fila IN c_facturado LOOP
i := i + 1;
v_detalle(i) := fila;
END LOOP;
END;
END PKG_CONTABILIDAD;
The function returns a pkg_contabilidad.t_detalle, so the test needs to be:
declare
resultao pkg_contabilidad.t_detalle;
numero number := 100;
begin
resultao := pkg_contabilidad.f_totalizar_detalle(numero);
end;
It doesn't work in SQL because pkg_contabilidad.t_detalle is a PL/SQL type, not a SQL type (create or replace type). The database can perform some automatic conversions, but there are still limitations.
By the way, this loop will never complete because it lacks an exit condition:
open c_datos;
loop
fetch c_datos into v_detalle(1);
end loop;
close c_datos;
Your function returns a PL/SQL table type, with a table of a PL/SQL record type, which is defined in your package, which plain SQL doesn't know about and can't display - hence your invalid datatype error. If you need to call the function and access the data from SQL you can create schema-level object and collection types instead.
In your anonymous block you are a declaring a new record type. That looks the same to you because the structure is the same, but Oracle expects the exact type the function returns. That makes your test code shorter and simpler though. But you are also trying to return the whole collection into a single record.
DECLARE
l_detalle pkg_contabilidad.t_detalle;
l_registro pkg_contabilidad.r_registro;
l_idx pls_integer;
numero NUMBER :=100;
BEGIN
l_detalle := pkg_contabilidad.f_totalizar_detalle(numero);
l_idx := l_detalle.FIRST;
WHILE l_idx is not null LOOP
l_registro := l_detalle(l_idx);
-- do something with this record
dbms_output.put_line(l_registro.tipo);
l_idx := l_detalle.NEXT(l_idx);
END LOOP;
END;
db<>fiddle with dummy cursors.
Your function is a bit strange and probably isn't doing quite what you want; but also has two fatal problems: it isn't returning anything, and it has an infinite loop. I've fixed those for the fiddle but not anything else, as this seems to be an exercise.

VARCHAR2(32767) not able to handle strings in stored procedure

I am concatenating string using cursor (to form query to execute later). Here, the query that will be formed is going to be way bigger that what VARCHAR2(32767) can handle. There fore, I am getting error on proc execution - ORA-06502: PL/SQL: numeric or value error: character string buffer too small.
I used CLOB data type as well bu got error ORA-06502: PL/SQL: numeric or value error.
My code is here below:
CREATE OR REPLACE PROCEDURE sp_Market
IS
Names VARCHAR2(32767);
BEGIN
DECLARE CURSOR cur IS ('Select ID, Order_of, field_name
FROM pld_medicare_config');
BEGIN
FOR i IN cur
LOOP
Names := Names || i.sqql;
END LOOP;
dbms_output.put_line(Names);
END;
END sp_Market;
How can I handle my string of queries and what data type is there to accomplish the task?
CLOB is OK (as far as I can tell); I doubt queries you store in there are that big.
Remove dbms_output.put_line call from the procedure; I suspect it is the one that raises the error.
I'm not sure how you got any runtime error, as your procedure won't compile.
The valid PL/SQL version would look something like this:
create or replace procedure sp_market is
names varchar2(32767);
begin
for r in (
select id, order_of, field_name
from pld_medicare_config
)
loop
names := names || ' ' || r.field_name;
end loop;
names := ltrim(names);
dbms_output.put_line(names);
end sp_market;
If names needs to be longer, change the datatype to clob.
Use the CLOB datatype and append data using the dbms_lob.writeappend procedure. This is the reference (Oracle 18c).
The error probably origins with the dbms_output.put_line call. The procedure is defined for varchar2 arguments only which means that an implicit conversion takes place during the call. It will fail for clob contents longer than 32767 chars/bytes.
Alternatively you may declare a collection over varchar2(4000) and fill the collection elements sequentially:
CREATE OR REPLACE PROCEDURE sp_Market
IS
TYPE tLongString IS TABLE OF VARCHAR2(4000) INDEX BY BINARY_INTEGER;
cNames tLongString;
BEGIN
DECLARE CURSOR cur IS Select ID, Order_of, field_name, sqql FROM pld_medicare_config;
BEGIN
FOR i IN cur
LOOP
cNames(cNames.COUNT+1) := i.sqql;
END LOOP;
END;
END sp_Market;
Note
Rectified code, will compile now.

'ORA-00913: too many values' while reading data from xml to user-defined record (PL/SQL)

I get this error while executing the follwing PL/SQL code and I can't figure out why.
PLS-00382: expression is of wrong type
PL/SQL: ORA-00913: too many values
PL/SQL: SQL Statement ignored
I understand that the cause of ORA-00913 is a mismatch of values in the two sets of data being referred to in the SELECT statement.
The code was working fine before I added the checkVar variable in the record. Just wanted to know if there is any way to fix this without taking out the checkVar from testRec record?
DECLARE
X XMLType:=XMLType( '<Testmain>
<TeamSel>
<code>abc123</code>
<name>awrqer</name>
</TeamSel>
<TeamSel>
<code>abc678</code>
<name>gokhg</name>
</TeamSel>
</Testmain>
');
TYPE testRec IS RECORD(
name VARCHAR2(512),
code VARCHAR2(512),
checkVar BOOLEAN:=FALSE
);
TYPE testRecTab IS TABLE OF testRec INDEX BY BINARY_INTEGER;
testList testRecTab;
BEGIN
SELECT EXTRACTVALUE(VALUE(xml_list),'//Name') AS lname,
EXTRACTVALUE(VALUE(xml_list),'//Code') AS lcode
BULK COLLECT
INTO testList
FROM TABLE(XMLSEQUENCE(EXTRACT(X,'Testmain/TeamSel'))) xml_list;
--rest of the code
END;
You can't select into a Boolean variable so I think there are two options:
a) Remove the boolean from your record type
b) Populate your table in a loop like the example below
DECLARE
X XMLType:=XMLType( '<Testmain>
<TeamSel>
<code>abc123</code>
<name>awrqer</name>
</TeamSel>
<TeamSel>
<code>abc678</code>
<name>gokhg</name>
</TeamSel>
</Testmain>
');
TYPE testRec IS RECORD(
name VARCHAR2(512),
code VARCHAR2(512),
checkVar BOOLEAN:=FALSE
);
TYPE testRecTab IS TABLE OF testRec INDEX BY BINARY_INTEGER;
lTestRec testRec;
testList testRecTab;
BEGIN
FOR cRecord IN (
SELECT EXTRACTVALUE(VALUE(xml_list),'//name') lname,
EXTRACTVALUE(VALUE(xml_list),'//code') lcode
FROM TABLE(XMLSEQUENCE(EXTRACT(X,'Testmain/TeamSel'))) xml_list
)
LOOP
lTestRec.name := cRecord.lname;
lTestRec.code := cRecord.lcode;
testList(testList.count + 1) := lTestRec;
END LOOP;
--rest of the code
END;

Oracle RawToHex function - what happens if return value exceeds varchar2 limit?

The RawToHex function in Oracle 11g returns a hexadecimal representation of any raw value.
This function returns the Hex Value as a varchar2.
What happens if I pass a BLOB into the RawToHex() function that will result in a hex representation that exceeds the varchar2 limit of 4000?
Is there any way to convert very big BLOBs into a Hex representation?
UPDATE:
I did some investigation and found an answer for the first part of my question.
I can pass a BLOB into the RawToHex function and this one will execute successfully as long as you won't hit the boundaries of the Raw DataType. Oracle seems to convert from BLOB to Raw implicitly.
DECLARE
a varchar2(32767);
b blob;
BEGIN
select blob_column into b from a_table where a_table_id = 1;
dbms_output.put_line(dbms_lob.getlength(b)); --> output: 216
dbms_output.put_line(rawtohex(empty_blob())); --> converted blob
select blob_column into b from a_table where a_table_id = 2;
dbms_output.put_line(dbms_lob.getlength(b)); --> output: 140000
dbms_output.put_line(rawtohex(empty_blob())); --> ORA-06502: PL/SQL: numeric or value error
END;
Description of this error according to ora-code.com
ORA-06502: PL/SQL: numeric or value error string
Cause: An arithmetic, numeric, string, conversion, or constraint error occurred. For example, this error occurs if an attempt is made to assign the value NULL to a variable declared NOT NULL, or if an attempt is made to assign an integer larger than 99 to a variable declared NUMBER(2).
Action: Change the data, how it is manipulated, or how it is declared so that values do not violate constraints.
UPDATE 2:
I've got a solution for this issue. Splitting the blob into smaller blocks and converting them step by step. It delivers the correct result. Is this a correct approach or could this solution fall over at some point?
function BlobToHex(data in blob) return clob
is
v_clob clob;
v_start pls_integer := 1;
v_buffer pls_integer := 4000;
begin
if data is null
then
return '""';
end if;
dbms_lob.createtemporary(v_clob, true);
dbms_lob.append(v_clob, '0x');
for i in 1..ceil(dbms_lob.getlength(data) / v_buffer)
loop
dbms_lob.append(v_clob, rawtohex(DBMS_LOB.SUBSTR(data, v_buffer, v_start)));
v_start := v_start + v_buffer;
end loop;
return v_clob;
end;
You'll get ORA-00932
SQL> select rawtohex(empty_blob()) from dual;
select rawtohex(empty_blob()) from dual
*
ERROR at line 1:
ORA-00932: inconsistent datatypes: expected - got BLOB
SQL>
because
As a SQL built-in function, RAWTOHEX accepts an argument of any scalar data type other than LONG, LONG RAW, CLOB, BLOB, or BFILE.
As stated by the documentation.

Output results of Oracle stored proc from SQL Developer

I'm trying to call an Oracle stored proc using SQL Developer. The proc outputs results using a sys_refcursor. I right click in the proc window which brings up the Run PL/SQL window. When I choose the proc I want it creates all the input params etc for me. Below is the code I'm using to try and loop through the sys_refcursor and output the results, but I'm getting an error on the 'v_rec v_Return%rowtype;' line :
ORA-06550: line 6 column 9:
PLS-00320: the declaration of the type of this expression is incomplete or malformed.
ORA-06550: line 6 column 9:
PL/SQL: Item ignored
vendor code 6550
I found the looping code on a couple of other websites and it seems to be the way to do it but it's not working for me no matter what I try. Another question - on the DBMS_OUTPUT.PUT_LINE('name = ' || v_rec.ADM) am I referencing the v_rec correctly i.e. is v_rec."column_name" the correct way??
I'm not that used to Oracle and have never used SQL plus. Any suggestions appreciated.
DECLARE
P_CAE_SEC_ID_N NUMBER;
P_PAGE_INDEX NUMBER;
P_PAGE_SIZE NUMBER;
v_Return sys_refcursor;
v_rec v_Return%rowtype;
BEGIN
P_CAE_SEC_ID_N := NULL;
P_PAGE_INDEX := 0;
P_PAGE_SIZE := 25;
CAE_FOF_SECURITY_PKG.GET_LIST_FOF_SECURITY(
P_CAE_SEC_ID_N => P_CAE_SEC_ID_N,
P_PAGE_INDEX => P_PAGE_INDEX,
P_PAGE_SIZE => P_PAGE_SIZE,
P_FOF_SEC_REFCUR => v_Return
);
-- Modify the code to output the variable
-- DBMS_OUTPUT.PUT_LINE('P_FOF_SEC_REFCUR = ');
loop
fetch v_Return into v_rec;
exit when v_Return%notfound;
DBMS_OUTPUT.PUT_LINE('name = ' || v_rec.ADM);
end loop;
END;
Your problem is here:
v_Return sys_refcursor;
v_rec v_Return%rowtype;
v_Return is a cursor variable and has no specific structure (list of columns), so v_Return%rowtype is not a valid record structure to declare v_rec. It is even possible for different calls to the procedure to return cursors with different structures.
You know what you are expecting the structure of the returned cursor to be (but Oracle doesn't) so you need to explicitly define the appropriate record structure e.g.
type t_row is record (empno number, ename varchar2(30));
v_rec t_row;
You need a strongly typed ref cursor to be able to define it as a %ROWTYPE.
Example here
#Tony Andrews thanks for this it gave me a better idea where I was going wrong. Still having problems though - here's a shortened version of my proc. It's a bit complex in that it's selecting all fields from a subquery and 2 other values:
open p_fof_sec_refcur for
SELECT *
FROM(
SELECT securities.*, rownum rnum, v_total_count
FROM
(
SELECT
CFS.CAE_SEC_ID,
CFS.FM_SEC_CODE,
...
FROM
CAEDBO.CAE_FOF_SECURITY CFS
INNER JOIN caedbo.CAE_DATA_SET_ELEMENT CDSE_STAT
ON (CDSE_STAT.DATA_SET_ELEMENT_ID = CFS.APPR_STATUS)
...
WHERE APPR_STATUS = NVL(p_appr_status, APPR_STATUS)
...
)securities
)
WHERE rnum between v_pgStart and v_pgEnd;
I explicitly defined the output structure as below to match the return fields from the proc but I'm still getting an error:
v_Return sys_refcursor;
type t_row is record (CAE_SEC_ID NUMBER,FM_SEC_CODE VARCHAR2(7),...rnum number, v_total_count number);
v_rec t_row;
The error I get is
ORA-06504: PL/SQL: Return types of Result Set variables or query do not match
ORA-06512: at line 45
I'm just wondering is the "rownum rnum, v_total_count" part tripping me up. I'm pretty sure I have all the other fields in the output structure correct as I copied them directly from the proc.

Resources