Oracle 12c CLOB data type is not working as expected - oracle

I have This Oracle 12c Procedure
CREATE OR REPLACE PROCEDURE LOGINCHECK(SQLQRY IN CLOB)
AS
C INTEGER;
N INTEGER;
RC SYS_REFCURSOR;
stmt clob:= To_Clob('begin ' || sqlqry || '; end;');
BEGIN
C := SYS.DBMS_SQL.OPEN_CURSOR;
SYS.DBMS_SQL.PARSE(C,stmt ,DBMS_SQL.native);
N := SYS.DBMS_SQL.EXECUTE(C);
SYS.DBMS_SQL.GET_NEXT_RESULT(C,RC);
SYS.DBMS_SQL.RETURN_RESULT(RC);
EXCEPTION
WHEN NO_DATA_FOUND THEN
NULL;
when OTHERS then
RAISE;
END LOGINCHECK;
I Call This Procedure in Anonymous Block Like This (Download XML Data from here: Link)
declare stmt clob := 'INWARDPKG.MACHINEINWARD_VALIDATING(XMLDOC => XMLTYPE.CREATEXML(paste xml from link))'; --The parameter value is a xml you can download it from above link
begin
LOGINCHECK(SQLQRY => STMT);
end;
But I am getting Error PLS-00172: string literal too long.
If i reduce xml size to 40-50 elements like remove some elements. this works fine.

In your first line declare stmt clob := 'INWARDPKG.MACHINEINWARD_VALIDATING... you are defining your CLOB. Since you are using a string literal to define your CLOB, you are facing the limits of string literals (see Oracle 12c Documenation).
To solve your problem you have to build your CLOB step by step, using the DBMS_LOB package and appending strings not longer than 4000 bytes until your CLOB is complete.
The basic idea:
DECLARE
C CLOB := TO_CLOB('First 4000 bytes');
V VARCHAR2(4000);
BEGIN
V := 'Next 4000 bytes';
DBMS_LOB.WRITEAPPEND(C, LENGTH(V), V);
-- more WRITEAPPEND calls until C is complete
DBMS_OUTPUT.PUT_LINE('CLOB-Length: ' || DBMS_LOB.GETLENGTH(C));
END;

Related

How to execute Oracle procedure with clob parameter in?

I have a procedure
create or replace PROCEDURE PROC_PROJPREL_TEMPL_SERV_MAT(
P_TABELA IN VARCHAR2,
P_COLUNAS IN VARCHAR2,
P_DADOS IN CLOB,
O_CODIGO OUT NUMBER,
O_MENSAGEM OUT VARCHAR2
) IS
BEGIN
o_codigo := 0;
o_mensagem := '';
-- no implementation coded yet
EXCEPTION
WHEN OTHERS THEN
raise_application_error(-20101, 'erro ao executar procedure ');
END PROC_PROJPREL_TEMPL_SERV_MAT;
And I need to execute this in SQL Developer.
I tried using anonymous block
declare
i_tabela varchar2(30);
i_colunas varchar2(4000);
i_dados clob;
o_codigo number;
o_mensagem varchar2(4000);
begin
i_tabela := 'table_name'; -- max 30 characters
i_colunas := 'columns_names'; -- less 4000 characters
i_dados := '45000 characters';
proc_projprel_templ_serv_mat(i_tabela, i_colunas, i_dados, o_codigo, o_mensagem);
end;
But it returns an error "string literal too long"
and I tried using "call" command too.
call proc_projprel_templ_serv_mat('table_name', 'columns_names', &DATAS);
But it returns an error ORA-00972 identifier is too long, Cause: An identifier with more than 30 characters was specified, Action: Specify at most 30 characters.
Somebody can help me?
The maximum length of a string literal in PL/SQL is 32,767 characters. As the error "string literal too long" is saying, you're blowing out this limit here:
i_dados := '45000 characters';
You have to break up that string into sections up to 32,767 characters long and concatenate them together, e.g.:
i_dados := 'first 32767 characters' ||
'remaining 12233 characters';

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.

How to loop through a json string with PL/SQL?

My example only returns BMW 2010.
How do I get it to return AUDI 2000 and BMW 2010?
declare
sample_json varchar2 (32767)
:= '
[{"NAME":"AUDI","YEAR":"2000"},{"NAME":"BMW","YEAR":"2010"}]
';
begin
apex_json.parse (sample_json);
dbms_output.put_line (apex_json.get_varchar2 ('NAME'));
dbms_output.put_line (apex_json.get_varchar2 ('YEAR'));
end;
TL;DR - You cannot as you have duplicate keys in an object.
From JSON Standard - RFC 7159
Objects
An object structure is represented as a pair of curly brackets
surrounding zero or more name/value pairs (or members). A name is a
string. A single colon comes after each name, separating the name
from the value. A single comma separates a value from a following
name. The names within an object SHOULD be unique.
(Added emphasis)
{"NAME":"AUDI","YEAR":"2000","NAME":"BMW","YEAR":"2010"}
While technically it is syntactically correct JSON, it does not make sense as you are duplicating keys so most (every) JSON parsers following RFC 7159 will overwrite the first instance of a key with later occurrences so your JSON is effectively:
{"NAME":"BMW","YEAR":"2010"}
And you cannot get AUDI/2000 from the output (unless you parse the JSON by hand).
If you want to send multiple values then you should use an array:
[{"NAME":"AUDI","YEAR":"2000"},{"NAME":"BMW","YEAR":"2010"}]
Update
You can try:
declare
sample_json varchar2(32767) := '{"data":[{"NAME":"AUDI","YEAR":"2000"},{"NAME":"BMW","YEAR":"2010"}]}';
begin
apex_json.parse (sample_json);
dbms_output.put_line (apex_json.get_varchar2 ('data[1].NAME'));
dbms_output.put_line (apex_json.get_varchar2 ('data[1].YEAR'));
dbms_output.put_line (apex_json.get_varchar2 ('data[2].NAME'));
dbms_output.put_line (apex_json.get_varchar2 ('data[2].YEAR'));
end;
or (if apex will accept an array as its outer object):
declare
sample_json varchar2(32767) := '[{"NAME":"AUDI","YEAR":"2000"},{"NAME":"BMW","YEAR":"2010"}]';
begin
apex_json.parse (sample_json);
FOR i IN 1 .. 2 LOOP
dbms_output.put_line (apex_json.get_varchar2(p_path=>'[%d].NAME',p0=>i));
dbms_output.put_line (apex_json.get_varchar2(p_path=>'[%d].YEAR',p0=>i));
END LOOP;
end;
Since you're running Oracle 12c, you don't need Apex_json. You may use standard Oracle's JSON functions.
set serveroutput on
declare
sample_json varchar2 (32767)
:= '[{"NAME":"AUDI","YEAR":"2000"},{"NAME":"BMW","YEAR":"2010"}]';
BEGIN
for rec IN (
select j.name,j.year
from json_table(sample_json,'$[*]' COLUMNS
name varchar2(20) PATH '$.NAME',
year NUMBER PATH '$.YEAR'
) j )
LOOP
dbms_output.put_line (rec.name||','||rec.year);
END LOOP;
END;
/
AUDI,2000
BMW,2010
PL/SQL procedure successfully completed.

Oracle - How to handle 32K+ string length in variables

I am using oracle 11g. Whenever I encountered strings larger than varchar2 size limit, In sql server I use to split the data into multiple variables as below and then join them while execution. However Oracle seems to be expecting 32K combined size before execution. I am getting "ORA-20000: ORU-10028: line length overflow, limit of 32767 bytes per line" error.
I am using these variables in an oralce script (not stored procs). Last 2 statements are throwing the above error and individually I am able to show the value.
Thanks in advance.
DECLARE
sViewQuery varchar2(32000);
sViewSelectQuery varchar2(32000);
BEGIN
---Assign values of 32,000 letter string (dynamic query)
sViewSelectQuery:='32K string...';
sViewQuery:='32K string..';
DBMS_OUTPUT.PUT_LINE(sViewQuery||sViewSelectQuery);
EXECUTE IMMEDIATE sViewQuery||sViewSelectQuery;
END;
You can use DBMS_SQL Package for this:
DECLARE
stmt DBMS_SQL.VARCHAR2A;
c number;
res number;
BEGIN
stmt(1) := 'create view view_a (';
stmt(2) := 'col_a, ';
stmt(3) := 'col_b, ';
stmt(4) := 'col_c) as '
stmt(5) := 'select ';
stmt(6) := 'col_bb, ';
stmt(7) := 'col_cc + col_ee + DECODE(...), ';
stmt(8) := 'col_dd) ';
stmt(9) := 'from table_b ';
stmt(10) := 'where ... ';
-- each element can have up to 32K characters, number of elements is (almost) unlimited
c := DBMS_SQL.open_cursor;
DBMS_SQL.parse(c, stmt, 1,10, TRUE, DBMS_SQL.NATIVE);
res := DBMS_SQL.execute(c);
DBMS_SQL.close_cursor(c);
END;
You should use a CLOB, Character Large OBject. You can handle 32K+ string length, since CLOB can contain up to 4GB of data.
For more info: http://docs.oracle.com/javadb/10.3.3.0/ref/rrefclob.html

Will oracle stored procedure support strcspn and strncpy inside the procedure?

Will oracle stored procedure support the use of 'strcspn' and 'strncpy' inside the procedure ?
they are c functions not pl/sql. to get the IP + port from the string you've supplied its like this anonymous block sample
(sample code assumes there's a "-" in the string, you may want to validate this up front!)
declare
v_str varchar2(1000) := '4000-10.1.1.1';
v_port pls_integer;
v_ip varchar2(50);
begin
v_port := substr(v_str, 1, instr(v_str, '-')-1);
v_ip := substr(v_str, instr(v_str, '-') + 1);
dbms_output.put_line(v_port);
dbms_output.put_line(v_ip);
end;
/

Resources