I have the following stored procedure, doing an API call and then inserting the response as a Clob in a target table:
create or replace procedure api_call(
time_id in varchar2,
repository in varchar2,
api_query in varchar2)
as
date_from varchar2 := to_char(to_date(time_id, 'yyyymmdd', 'nls_date_language = English'));
date_to varchar2 := to_char(to_date(time_id, 'yyyymmdd', 'nls_date_language = English') + 1);
req utl_http.req;
res utl_http.resp;
response_json clob;
url varchar2(2000) := 'url/'||repository||'/query';
query_string varchar2(4000) := '{"queryString":"'||api_query||'","start":'||date_from||',"end":'||date_to||', "isLive":false}';
begin
DBMS_LOB.createtemporary(response_json, false);
req := utl_http.begin_request(url, 'POST', ' HTTP/1.1');
utl_http.set_header(req, 'Authorization: Bearer', 'someAPItoken');
utl_http.set_header(req, 'content-type', 'application/json');
utl_http.set_header(req, 'Accept', 'application/json');
utl_http.write_text(req, query_string);
res := utl_http.get_response(req);
begin
loop
utl_http.read_text(res);
DBMS_LOB.writeappend(response_json);
end loop;
exception
when utl_http.end_of_body then
utl_http.end_response(res);
end;
insert into http_response_table (time_id, repository, json_data)
values (time_id, repository, response_json);
DBMS_LOB.freetemporary(reponse_json);
exception
when others then
utl_http.end_response(res);
DBMS_LOB.freetemporary(reponse_json);
raise;
end api_call;
For some reason, when trying to test the procedure, it claims the object is invalid. Although it creates the stored procedure without any error messages.
Any ideas what is wrong in the stored procedure? Formatting- or syntax-wise?
Your code has lots of issues. When you create a procedure, Oracle will tell you if it has an issue "Warning: Procedure created with compilation errors."
So, you ask for the errors by doing a
SQL> show err
Then you rectify them one by one. For example, in your code, you have a variable called response_json, and you refer to it as such or as reponse_json. So rectify this. Then you'll get an error about dbms_lob.writeappend not having the required params, rectify that, and see if there any any remaining ones, ....
Putting your code into a db<>fiddle and then running:
SELECT * FROM USER_ERRORS;
Gives the errors:
NAME
TYPE
SEQUENCE
LINE
POSITION
TEXT
ATTRIBUTE
MESSAGE_NUMBER
API_CALL
PROCEDURE
1
7
18
PLS-00215: String length constraints must be in range (1 .. 32767)
ERROR
215
API_CALL
PROCEDURE
2
8
16
PLS-00215: String length constraints must be in range (1 .. 32767)
ERROR
215
API_CALL
PROCEDURE
3
31
12
PLS-00306: wrong number or types of arguments in call to 'READ_TEXT'
ERROR
306
API_CALL
PROCEDURE
4
31
12
PL/SQL: Statement ignored
ERROR
0
API_CALL
PROCEDURE
5
32
12
PLS-00306: wrong number or types of arguments in call to 'WRITEAPPEND'
ERROR
306
API_CALL
PROCEDURE
6
32
12
PL/SQL: Statement ignored
ERROR
0
API_CALL
PROCEDURE
7
42
33
PLS-00201: identifier 'REPONSE_JSON' must be declared
ERROR
201
API_CALL
PROCEDURE
8
42
10
PL/SQL: Statement ignored
ERROR
0
API_CALL
PROCEDURE
9
47
35
PLS-00201: identifier 'REPONSE_JSON' must be declared
ERROR
201
API_CALL
PROCEDURE
10
47
12
PL/SQL: Statement ignored
ERROR
0
You need to fix them all for the package to compile and be valid.
Related
I am new to pl sql and i am trying to call a procedure but i am getting the error ora-06575 package or function is in an invalid state.
What do you think i am doing wrong? Thanks
select * from NB_tfr_USERS; --previous table created
create or replace procedure nb_tfr_add_user
(
i_user_name in nb_tfr_users.name%type,
i_address in nb_tfr_users.address%type,
i_birthdate in nb_tfr_users.birthdate%type,
o_userid out nb_tfr_users.id%type,
o_errm out varchar2
) AS
--Variables
l_user_id number;
--Parameters invalid exception
error_params exception;
BEGIN
--validate parameters
--- name can not be empty
--- birthdate can be empty
if i_user_name is null or length(i_user_name) = 0 then
raise error_params;
end if;
--initialize variables
l_user_id := nb_seq_tfr_user_id.nextval;
--insert new user
insert into NB_tfr_USERS (id, name, address, birthdate)
values (l_user_id, i_user_name, i_address, i_birthdate);
--deal with exceptions
exception
when error_params then
o_errm := 'Invalid Parameters!';
dbms_output.put_line(o_errm);
when others then
dbms_output.put_line('Error: '||sqlerrm);
o_errm:=substr(1,150,sqlerrm);
raise;
END NB_tfr_ADD_USER;
call
nb_tfr_add_user(54,'tf','lx',12121989);
So when i try to call the procedure i get the error.
Well, it looks OK - apart from the fact that you incorrectly called the procedure.
Here's a test case.
SQL> create table nb_tfr_users
2 (id number, name varchar2(10), address varchar2(10), birthdate date);
Table created.
SQL> create sequence nb_seq_tfr_user_id;
Sequence created.
The procedure:
SQL> create or replace procedure nb_tfr_add_user
2 (
3 i_user_name in nb_tfr_users.name%type,
4 i_address in nb_tfr_users.address%type,
5 i_birthdate in nb_tfr_users.birthdate%type,
6 o_userid out nb_tfr_users.id%type,
7 o_errm out varchar2
8 ) AS
9 --Variables
10 l_user_id number;
11 --Parameters invalid exception
12 error_params exception;
13 BEGIN
14 --validate parameters
15 --- name can not be empty
16 --- birthdate can be empty
17 if i_user_name is null or length(i_user_name) = 0 then
18 raise error_params;
19 end if;
20
21 --initialize variables
22 l_user_id := nb_seq_tfr_user_id.nextval;
23
24 --insert new user
25 insert into NB_tfr_USERS (id, name, address, birthdate)
26 values (l_user_id, i_user_name, i_address, i_birthdate);
27
28 --deal with exceptions
29 exception
30 when error_params then
31 o_errm := 'Invalid Parameters!';
32 dbms_output.put_line(o_errm);
33
34 when others then
35 dbms_output.put_line('Error: '||sqlerrm);
36 o_errm:=substr(1,150,sqlerrm);
37 raise;
38 END NB_tfr_ADD_USER;
39 /
Procedure created.
SQL>
Testing - it shows how you should have called it. As there are 3 IN and 2 OUT parameters, that's what you should have done as well.
SQL> set serveroutput on
SQL> declare
2 l_userid nb_tfr_users.id%type;
3 l_Errm varchar2(200);
4 begin
5 nb_tfr_add_user(i_user_name => 'tf',
6 i_address => 'lx',
7 i_birthdate => sysdate,
8 o_userid => l_userid,
9 o_errm => l_errm);
10 dbms_output.put_Line(l_userid ||' '||l_errm);
11 end;
12 /
PL/SQL procedure successfully completed.
SQL> select * from nb_tfr_users;
ID NAME ADDRESS BIRTHDAT
---------- ---------- ---------- --------
1 tf lx 09.09.19
SQL>
If you got the error, it - obviously - means that there's something wrong. I don't know what, but - maybe you forgot to create the sequence.
Anyway: after creating stored procedures, check whether everything is OK by running (in SQL*Plus) show err, e.g.
SQL> create or replace procedure p_test is
2 begin
3 insert into abc (id) values (seq_does_not_exist.nextval);
4 end;
5 /
Warning: Procedure created with compilation errors.
SQL> show err
Errors for PROCEDURE P_TEST:
LINE/COL ERROR
-------- -----------------------------------------------------------------
3/3 PL/SQL: SQL Statement ignored
3/15 PL/SQL: ORA-00942: table or view does not exist
SQL>
Or, query user_source:
SQL> select line, text, attribute from user_errors where name = 'P_TEST';
LINE TEXT ATTRIBUTE
----- -------------------------------------------------- ---------
3 PL/SQL: ORA-00942: table or view does not exist ERROR
3 PL/SQL: SQL Statement ignored ERROR
SQL>
I suggest you do the same and see what's wrong with the procedure.
I am typically a SQL Server developer however am now working with a system that uses Oracle. I have created a new procedure and am getting a runtime error. Here is the procedure:
CREATE OR REPLACE PROCEDURE CHK_LASTAPPTIME
(
LASTAPPTIME OUT VARCHAR2
)
IS
v_appappid varchar2(20) null;
v_lastapptime number null;
BEGIN
select max(APPID) into v_appappid from applicationtable;
select trunc(v_lastapptime = (((sysdate - capturedate) * 24)) * 60) from applicationtable where APPID = v_appappid;
LASTAPPTIME := to_char(v_lastapptime);
END CHK_LASTAPPTIME;
Here is the error that I am getting:
SQL> var x varchar2;
SQL> exec CHK_LASTAPPTIME(:x);
BEGIN CHK_LASTAPPTIME(:x); END;
*
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: character string buffer too small
ORA-06512: at "CAPDEV.CHK_LASTAPPTIME", line 19
ORA-06512: at line 1
Your proecedure doesn't have 19 lines, so the message is odd, or you've shown different code to what you're actually running. Since what you've shown doesn't compile I'm assuming you've tried to cut it down for the question, and I'll also assume your actual procedure is valid and does what you want.
The problem is in your run-time variable declaration. You haven't specified a size for x, so it uses a default. On my DB, in SQL*Plus that seems to allow three characters but errors with four or more:
var x varchar2;
exec :x := 'a';
PL/SQL procedure successfully completed.
exec :x := 'ab';
PL/SQL procedure successfully completed.
exec :x := 'abc';
PL/SQL procedure successfully completed.
exec :x := 'abcd';
BEGIN :x := 'abcd'; END;
*
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: character string buffer too small
ORA-06512: at line 1
The limit might be related to your character set, perhaps. SQL Developer allows more by default.
Anyway, specify the size for your variable:
var x varchar2(30);
exec CHK_LASTAPPTIME(:x);
You seem to be generating a number in your procedure, and then returning that as a string. Using the same data type would make this a bit simpler.
I have this oracle code
FUNCTION get_enc_val(p_in IN VARCHAR2,
p_key IN VARCHAR2
)
RETURN raw
IS
p_title_procedure_name VARCHAR2(100) := 'get_enc_val';
l_enc_val RAW(2000);
l_mod PLS_INTEGER := DBMS_CRYPTO.ENCRYPT_AES128 + DBMS_CRYPTO.CHAIN_CBC + DBMS_CRYPTO.PAD_PKCS5;
v_key VARCHAR2(16);
encryption_ex Exception;
BEGIN
v_key := RPAD(SUBSTR(TRIM(p_key), 1, 16), 16, '0');
l_enc_val := DBMS_CRYPTO.encrypt(UTL_RAW.cast_to_raw(p_in), l_mod, UTL_RAW.cast_to_raw(v_key));
RAISE encryption_ex;
RETURN l_enc_val;
EXCEPTION
WHEN OTHERS THEN
service_func.log_error(p_title_package_name || '.' || p_title_procedure_name, 'Proc', NULL, SYSDATE, SQLERRM, p_in || '~' || p_key);
RETURN 'Encryption_ERROR';
END;
When I run this I get ORA-06510 Unhandled user-defined exception while it should really return the string 'Encryption_ERROR'.What gives? It goes to the Exception block, because I see the result of log_error function. Question is, isn't the Exception block supposed to HANDLE ANY exception?
I am a bit confused.
The problem is that your second RETURN statement is returning a VARCHAR2 while your function is declared to return a RAW. You could fix that by calling UTL_RAW.CAST_TO_RAW, i.e.
EXCEPTION
WHEN OTHERS THEN
service_func.log_error(p_title_package_name || '.' ||
p_title_procedure_name,
'Proc',
NULL,
SYSDATE,
SQLERRM,
p_in || '~' || p_key);
RETURN utl_raw.cast_to_raw( 'Encryption_ERROR' );
END;
If I declare two functions, one that returns a hard-coded string and one that returns a RAW, you'll see the difference (I'm removing the DBMS_CRYPTO calls and the LOG_ERROR call). If I declare a function that returns a RAW, you get a result back (though a human would have to convert the raw back into a string to make sense of the result)
SQL> ed
Wrote file afiedt.buf
1 create or replace function throw_exception
2 return raw
3 is
4 my_exception exception;
5 begin
6 raise my_exception;
7 exception
8 when others then
9 return utl_raw.cast_to_raw( 'Foo' );
10* end;
SQL> /
Function created.
SQL> select throw_exception
2 from dual;
THROW_EXCEPTION
-----------------------------------------------------------------------------
466F6F
If I just return a string, I'll get the same exception you were getting
SQL> ed
Wrote file afiedt.buf
1 create or replace function throw_exception2
2 return raw
3 is
4 my_exception exception;
5 begin
6 raise my_exception;
7 exception
8 when others then
9 return 'Foo';
10* end;
SQL> /
Function created.
SQL> select throw_exception2
2 from dual;
select throw_exception2
*
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: hex to raw conversion error
ORA-06512: at "SCOTT.THROW_EXCEPTION2", line 9
ORA-06510: PL/SQL: unhandled user-defined exception
Of course, the other option would be to declare that the function returns a VARCHAR2. But I would much rather have hashes and encrypted data in a RAW than a VARCHAR2 so that you never have to worry about things like character set conversion issues mangling the data.
Since you are sure that service_func.log_error(...); is not raising the error, the error must be thrown by the calling procedure/function.
And here's something that raises the same error.
create or replace function abc return raw is
begin
return 'Encryption_ERROR';
end;
/
declare
r raw(50);
begin
r := abc;
end;
/
How can we define output parameter size in stored procedure?
You can't. Of course, you are in control of how much data you put into the OUT parameter in the stored procedure. If you want you can create a sized local variable to hold the data and then assign the value of that variable to the OUT parameter.
The calling program determines the size of the variable that receives the OUT parameter.
Here is a simple package which declares and uses a subtype:
SQL> create or replace package my_pkg as
2 subtype limited_string is varchar2(10);
3 procedure pad_string (p_in_str varchar
4 , p_length number
5 , p_out_str out limited_string);
6 end my_pkg;
7 /
Package created.
SQL> create or replace package body my_pkg as
2 procedure pad_string
3 (p_in_str varchar
4 , p_length number
5 , p_out_str out limited_string)
6 as
7 begin
8 p_out_str := rpad(p_in_str, p_length, 'A');
9 end pad_string;
10 end my_pkg;
11 /
Package body created.
SQL>
However, if we call PAD_STRING() in such a way that the output string exceeds the subtype's precision it still completes successfully. Bother!
SQL> var out_str varchar2(128)
SQL>
SQL> exec my_pkg.pad_string('PAD THIS!', 12, :out_str)
PL/SQL procedure successfully completed.
SQL>
SQL> select length(:out_str) from dual
2 /
LENGTH(:OUT_STR)
----------------
12
SQL>
This is annoying but it's the way PL/SQL works so we have to live with it.
The way to resolve the situaton is basically to apply DBC principles and validate our parameters. So, we can assert business rules against the inputs like this:
SQL> create or replace package body my_pkg as
2 procedure pad_string
3 (p_in_str varchar
4 , p_length number
5 , p_out_str out limited_string)
6 as
7 begin
8 if length(p_in_str) + p_length > 10 then
9 raise_application_error(
10 -20000
11 , 'Returned string cannot be longer than 10 characters!');
12 end if;
13 p_out_str := rpad(p_in_str, p_length, 'A');
14 end pad_string;
15 end my_pkg;
16 /
Package body created.
SQL>
SQL> exec my_pkg.pad_string('PAD THIS!', 12, :out_str)
BEGIN my_pkg.pad_string('PAD THIS!', 12, :out_str); END;
*
ERROR at line 1:
ORA-20000: Returned string cannot be longer than 10 characters!
ORA-06512: at "APC.MY_PKG", line 9
ORA-06512: at line 1
SQL>
Or we can assert business rules against the output like this:
SQL> create or replace package body my_pkg as
2 procedure pad_string
3 (p_in_str varchar
4 , p_length number
5 , p_out_str out limited_string)
6 as
7 l_str limited_string;
8 begin
9 l_str := rpad(p_in_str, p_length, 'A');
10 p_out_str := l_str;
11 end pad_string;
12 end my_pkg;
13 /
Package body created.
SQL>
SQL> exec my_pkg.pad_string('PAD THIS!', 12, :out_str)
BEGIN my_pkg.pad_string('PAD THIS!', 12, :out_str); END;
*
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: character string buffer too small
ORA-06512: at "APC.MY_PKG", line 9
ORA-06512: at line 1
SQL>
In most scenarios we should do both. This is the polite way to build interfaces, because it means other routines can call our procedures with the confidence that they will return the values they say they will.
You could use a subtype in a package header and type check that in the body...
CREATE OR REPLACE PACKAGE my_test
AS
SUBTYPE my_out IS VARCHAR2( 10 );
PROCEDURE do_something( pv_variable IN OUT my_out );
END;
/
CREATE OR REPLACE PACKAGE BODY my_test
AS
PROCEDURE do_something( pv_variable IN OUT my_out )
IS
lv_variable my_out;
BEGIN
-- Work on a local copy of the variable in question
lv_variable := 'abcdefghijklmnopqrstuvwxyz';
pv_variable := lv_variable;
END do_something;
END;
/
Then when you run this
DECLARE
lv_variable VARCHAR2(30);
BEGIN
my_test.do_something( lv_variable );
DBMS_OUTPUT.PUT_LINE( '['||lv_variable||']');
END;
/
You would get the error
ORA-06502: PL/SQL: numeric or value error: character string buffer too small
Seems to go against the spirit of using an out parameter, but after Tony's comment this was the only thing I could think of to control data within the called code.
I have a table containing a column of type Number
create table tmp (
/*other fields*/
some_field Number
)
and in a PL SQL script, I want to convert that field to a varchar. However, i don't know its length, so I get an exception
Exception message is ORA-06502:
PL/SQL: numeric or value error:
character string buffer too small
v_some_field varchar(21);
/*...*/
v_some_field := TO_CHAR(some_field,'999999999999999999999');
How should i declare the v_some_field buffer? Setting it to varchar(32767) seems quite brute, is there any alternative?
you're getting an error not because the number is too large but because the result of your to_char is 22 characters long (21x"9"+one character for the sign):
SQL> DECLARE
2 some_field NUMBER := 123;
3 v_some_field VARCHAR(21);
4 BEGIN
5 v_some_field := TO_CHAR(some_field, '999999999999999999999');
6 END;
7 /
ORA-06502: PL/SQL: numeric or value error: character string buffer too small
ORA-06512: at line 6
SQL> DECLARE
2 some_field NUMBER := 123;
3 v_some_field VARCHAR(22);
4 BEGIN
5 v_some_field := TO_CHAR(some_field, '999999999999999999999');
6 END;
7 /
PL/SQL procedure successfully completed
You could determine the maximum length of your converted varchar2 by converting a negative value with integral and fractional digits:
set serveroutput on
declare
n number;
begin
n := -4/3;
dbms_output.put_line(length(to_char(n)));
end;
/
Output is 41 for me.