I have a package specification:
G_PKG_NAME CONSTANT VARCHAR2(30) := 'XX_CUST_PKG';
PROCEDURE customer_load
( errbuff OUT NOCOPY VARCHAR2
, retcode OUT NOCOPY VARCHAR2);
And body with procedure which calls to HZ_PARTY_V2PUB API. It uses cursor to take data from a table and then sends it to API :
PROCEDURE create_customer
( errbuff OUT NOCOPY VARCHAR2
, retcode OUT NOCOPY VARCHAR2)
IS
ERR_SOURCE CONSTANT VARCHAR2(100) := G_PKG_NAME ||'.create_customer';
CURSOR c_load
IS
SELECT rowid row_id
, person_first_name
, person_last_name
, title
, known_as
, person_identifier
, gender
FROM xx_customer_info
WHERE NVL(status_flag, 'X') <> 'S';
r_load c_load%ROWTYPE;
--p_init_msg_list VARCHAR2(1) := FND_API.G_TRUE;
v_gender VARCHAR2(30); --hz_parties.sex%TYPE;
v_title VARCHAR2(60); --hz_parties.title%TYPE;
--API record type
person_rec HZ_PARTY_V2PUB.PERSON_REC_TYPE;
-- API output variables
x_return_status VARCHAR2(1);
x_msg_count NUMBER;
x_msg_data VARCHAR2(2000);
x_party_id NUMBER;
x_party_number VARCHAR2(30);
x_profile_id NUMBER;
EXC_VALDN_ERR EXCEPTION;
BEGIN
errbuff := ' ';
retcode := RTN_SUCCESS;
msg_log ('Inside '||ERR_SOURCE);
FOR r_load in c_load LOOP
BEGIN
x_msg_data := NULL;
x_return_status := fnd_api.G_RET_STS_SUCCESS;
fnd_msg_pub.initialize;
-- example validation:
IF r_load.person_first_name IS NULL THEN
x_msg_data := ' "First name" cannot be null';
RAISE EXC_VALDN_ERR;
END IF;
-- Same validation for person_last_name here
-- Record Type:
person_rec.person_first_name := r_load.person_first_name;
person_rec.person_last_name := r_load.person_last_name;
person_rec.person_title := v_title;
person_rec.known_as := null;
person_rec.gender := v_gender;
person_rec.created_by_module := 'TCA_V2_API';
HZ_PARTY_V2PUB.create_person ( p_init_msg_list => FND_API.G_TRUE
, p_person_rec => person_rec
, x_party_id => x_party_id
, x_party_number => x_party_number
, x_profile_id => x_profile_id
, x_return_status => x_return_status
, x_msg_count => x_msg_count
, x_msg_data => x_msg_data);
msg_log('==========================');
msg_log('first name / last_name : '||r_load.person_first_name||' | '||r_load.person_last_name);
msg_log('x_return_status: '||x_return_status);
msg_log('x_msg_count: '||x_msg_count);
msg_log('x_msg_data: '||x_msg_data);
IF NVL(x_return_status, FND_API.G_RET_STS_ERROR) <> FND_API.G_RET_STS_SUCCESS THEN
IF NVL(x_msg_count, 0) > 1 THEN
FOR i IN 1..x_msg_count LOOP
x_msg_data := x_msg_data||i||'. '||substr(fnd_msg_pub.get(p_encoded => fnd_api.g_false ), 1, 255)||' , ';
msg_log(x_msg_data);
END LOOP;
END IF;
END IF;
msg_log('==========================');
EXCEPTION
WHEN OTHERS THEN
x_msg_data := 'EXC: '||NVL(x_msg_data, SQLERRM);
x_return_status := FND_API.G_RET_STS_ERROR;
END;
UPDATE xx_customer_info
SET status_flag = x_return_status
, error_message = x_msg_data
WHERE rowid = r_load.row_id;
END LOOP;
COMMIT;
msg_log ('Exit '||ERR_SOURCE);
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
msg_log('ERROR : '||ERR_SOURCE||' : '||NVL(SQLERRM, x_msg_data));
errbuff := 'ERROR : '||ERR_SOURCE||' : '||NVL(SQLERRM, x_msg_data);
retcode := RTN_ERROR;
END create_customer;
It should return errors or success.
When I test and run this in anonymous block:
begin
XX_CUST_PKG.create_customer;
end;
I get error message PLS-00306: wrong number or types of arguments in call to 'CREATE_CUSTOMER'. I can't see clearly where this error is referring to. I only have 2 OUT parameters, it should only give errbuff (which is x_msg_data) and retcode which is RTN_SUCCESS, RTN_WARNING or RTN_ERROR (I have this declared as constants '0', '1', '2') to output.
This was rewritten from initial package to the above example code, so that it handles exceptions, and few things had to be modified, but now I'm confused when testing it.
What did I leave out?
Any help?
The error you get is a PL/SQL compilation error, as you can see the error code starts with PLS-XXXX.. So it is not returned from your procedure. You missed to send the variables to hold the values from your procedure (out arguments)
Declare
errbuf varchar2(4000);
retcode varchar2(10);
begin
XX_CUST_PKG.create_customer(errbuf,retcode);
--printing the values
DBMS_OUTPUT.PUT_LINE(retcode||' '||errbuf);
end;
/
Related
I want to run filter but with 2 package since both getting different form of input in aspx
So I have this code , when I run it on Toad Oracle, it says error
I think I know the source of error is V_cursor, since I dont know how to store V_cursor on empty table
Can u guys help me please ?
CREATE OR REPLACE PACKAGE BODY GIA.Package_AUTOCOMPLETE IS
procedure get_model
(
P_STRINGMODEL IN VARCHAR2
,P_STRINGMERK IN VARCHAR2
,p_CURSOR OUT Types.ref_cursor
,p_STATUS OUT VARCHAR2
,p_ERROR_MESSAGE OUT VARCHAR2
)
is
BEGIN
PACKAGE_AUTOCOMPLETE.GET_MERK
(
P_STRINGMERK
,V_CURSOR
,p_STATUS
,p_ERROR_MESSAGE
)
Open P_CURSOR FOR
select * from V_CURSOR
WHERE V_CURSOR.MODEL_NAME = P_STRINGMODEL
;
p_status := '1';
p_error_message := 'ok';
EXCEPTION
WHEN OTHERS THEN
p_status := '0';
p_error_message := SUBSTR(SQLERRM, 1, 100);
END get_model;
procedure get_merk
(
P_STRINGMERK IN VARCHAR2
,p_CURSOR OUT Types.ref_cursor
,p_STATUS OUT VARCHAR2
,p_ERROR_MESSAGE OUT VARCHAR2
)
is
BEGIN
open p_cursor for
SELECT MERK_ID, MERK_NAME, TYPE_NAME, MODEL_NAME, MODEL_DESC
FROM TABLE_MERK_MODEL
WHERE MERK_ID=P_STRINGMERK
;
p_status := '1';
p_error_message := 'ok';
EXCEPTION
WHEN OTHERS THEN
p_status := '0';
p_error_message := SUBSTR(SQLERRM, 1, 100);
END get_merk;
end package_AUTOCOMPLETE ;
/
Is it possible to dereference a string as a variable in PL/SQL?
I'm looking for something like this:
declare
my_var CONSTANT varchar2(50) := 'test';
my_var_ref CONSTANT varchar2(50) := 'my_var';
begin
-- This of course doesn't work, it's just to illustrate what I'm looking for:
DBMS_OUTPUT.PUT_LINE(&my_var_ref) -- Prints test
end;
Pass the name as a procedure parameter:
declare
my_var CONSTANT varchar2(50) := 'test';
my_var_ref CONSTANT varchar2(50) := 'my_var';
begin
my_func(&my_var_ref) -- Pass my_var instead of my_var_ref
end;
[TL;DR] No, you cannot dereference a PL/SQL variable. However, an alternative option might be to use an associative array.
If you try to dereference it using EXECUTE IMMEDIATE then the PL/SQL block that is being dynamically evaluated has no knowledge of the context from the calling block so cannot get the value of the dereferenced variable.
For example:
DECLARE
my_var CONSTANT varchar2(50) := 'test';
my_var_ref CONSTANT varchar2(50) := 'my_var';
value VARCHAR2(50);
plsql_block VARCHAR2(100) := 'BEGIN :value := ' || my_var_ref || '; END;';
BEGIN
DBMS_OUTPUT.PUT_LINE( plsql_block );
EXECUTE IMMEDIATE plsql_block USING IN OUT value;
DBMS_OUTPUT.PUT_LINE( value );
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE( SQLERRM );
END;
/
Outputs:
BEGIN :value := my_var; END;
ORA-06550: line 1, column 17:
PLS-00201: identifier 'MY_VAR' must be declared
It has got the correct variable name but the variable is not defined in the context within which the dynamic evaluation of the PL/SQL block is occurring so it doesn't work.
However, if you try to pass in the variable's value (rather than trying to dereference the variable) then this demonstrates that, apart from dereferencing, the rest of the code would work:
DECLARE
my_var CONSTANT varchar2(50) := 'test';
my_var_ref CONSTANT varchar2(50) := 'my_var';
value VARCHAR2(50);
plsql_block VARCHAR2(100) := 'BEGIN :value := ''' || my_var || '''; END;';
BEGIN
DBMS_OUTPUT.PUT_LINE( plsql_block );
EXECUTE IMMEDIATE plsql_block USING IN OUT value;
DBMS_OUTPUT.PUT_LINE( value );
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE( SQLERRM );
END;
/
Outputs:
BEGIN :value := 'test'; END;
test
However, you could use a PL/SQL associative array instead:
DECLARE
TYPE MAP IS TABLE OF VARCHAR2(50) INDEX BY VARCHAR2(50);
my_var_ref CONSTANT VARCHAR2(50) := 'my_var';
v_map MAP;
BEGIN
v_map('my_var') := 'test';
DBMS_OUTPUT.PUT_LINE( v_map( my_var_ref) );
END;
/
Which outputs:
test
db<>fiddle here
You can using packages.
create or replace package test_pkg as
my_var1 varchar2(50);
my_var2 varchar2(50);
my_var3 varchar2(50);
procedure set_val(p_var in varchar2,
p_val in varchar2);
function get_val(p_var in varchar2)
return varchar2;
end test_pkg;
/
create or replace package body test_pkg as
procedure set_val(p_var in varchar2,
p_val in varchar2) is
begin
execute immediate 'begin
test_pkg.' || p_var || ' := :x;
end;'
using p_val;
end set_val;
function get_val(p_var in varchar2)
return varchar2 is
v_value varchar2(50);
begin
execute immediate 'begin
:x := test_pkg.' || p_var || ';
end;'
using out v_value;
return v_value;
end get_val;
end test_pkg;
/
Then
set serveroutput on
begin
for i in 1 .. 3 loop
test_pkg.set_val('my_var' || i, 'Test ' || i);
end loop;
for i in 1 .. 3 loop
dbms_output.put_line('my_var' || i || ' = ' || test_pkg.get_val('my_var' || i));
end loop;
end;
Output:
my_var1 = Test 1
my_var2 = Test 2
my_var3 = Test 3
I am getting error while I compile this function:
PLS-00049: bad bind variable 'RETURN_VALUE'
I need help with this please.
FUNCTION GECM_ROUND_FNC
(P_FIELD_VALUE IN NUMBER,
P_ORG_ID IN NUMBER)
RETURN VARCHAR2
IS
v_ret_val NUMBER := P_FIELD_VALUE;
V_OU_NAME hr_operating_units.name%type;
v_round_dec NUMBER;
BEGIN
BEGIN
SELECT NAME INTO V_OU_NAME
FROM HR_ALL_ORGANIZATION_UNITS
WHERE ORGANIZATION_ID = P_ORG_ID;
EXCEPTION WHEN OTHERS THEN
V_OU_NAME := '';
END;
--
IF V_OU_NAME IS NOT NULL AND NVL(GECM_ICP_PKG.gecm_get_process_value_fnc('GECM_ERP_VALIDATIONS',V_OU_NAME,'','',''),'N')='Y' THEN
BEGIN
IF NVL(GECM_ICP_PKG.GECM_GET_PARAMETER_VALUE_FNC('GECM_ERP_VALIDATIONS','DECIMAL_ROUNDING_INTERFACES',V_OU_NAME,'',''),0) = 'Y' THEN
BEGIN
SELECT NVL(GECM_ICP_PKG.GECM_GET_PARAMETER_VALUE_FNC('GECM_ERP_VALIDATIONS','DECIMAL_PLACE_LIMIT',V_OU_NAME,'',''),0)
INTO v_round_dec from dual;
EXCEPTION
WHEN OTHERS THEN
v_round_dec := 0;
END;
IF v_round_dec <> 0 THEN
v_ret_val:= TO_CHAR(ROUND(v_ret_val,v_round_dec));
ELSE
v_ret_val:=v_ret_val;
END IF;
END IF;
END;
END IF;
SELECT to_char(decode(sign(v_ret_val-1),-1,decode('0'||to_number(v_ret_val),'00','0','0'||to_number(v_ret_val)),v_ret_val)) INTO v_ret_val FROM DUAL;
:return_value := v_ret_val;
END;
Instead of
:return_value := v_ret_val;
use
return v_ret_val;
Hi I have the following procedure:
create or replace procedure
SP_DELETE_FROM_TABLE(pTableName in VARCHAR2, pFieldName in VARCHAR2,
pFieldValue in VARCHAR2,pFieldType in VARCHAR2) is
querystring VARCHAR2(500);
begin
queryString := 'DELETE FROM ' ||pTableName||
' WHERE '||pFieldName ||' = DECODE(:pFieldType,integer,:pFieldValue)' ;
EXECUTE IMMEDIATE queryString USING pFieldType,pFieldValue;
end SP_DELETE_FROM_TABLE;
all my Parameters are of Type VARCHAR2, What I am trying to do is: When I call the procedure with the following values ('users_table','users_id','11','integer')
so by using DECODE I would like to check if pFieldValue is of type pFieldTypeand if yes return pFieldValue
so if pFieldValue is:11 and pfieldType is:integer it should delete users_id 11 if fieldType is string do nothing..
I'd create a function that checks parameter has correct type
and then use it in main procedure
--main procedure
create or replace procedure SP_DELETE_FROM_TABLE(pTableName in VARCHAR2, pFieldName in VARCHAR2,pFieldValue in VARCHAR2,pFieldType in VARCHAR2) is
querystring VARCHAR2(500);
begin
if 'Y' = is_type_correct(pFieldValue, pFieldType ) then
queryString := 'DELETE FROM ' ||pTableName|| ' WHERE '
||pFieldName ||' = :pFieldValue';
EXECUTE IMMEDIATE queryString USING pFieldValue;
end
else
null; --incorrect type and parameter, do nothing
end;
end SP_DELETE_FROM_TABLE;
--check function
CREATE OR REPLACE FUNCTION is_type_correct( p_val IN VARCHAR2, p_type varchar2 )
RETURN VARCHAR2 DETERMINISTIC PARALLEL_ENABLE
IS
l_num NUMBER;
l_date date;
BEGIN
if 'integer' = p_type
then
l_num := to_number( p_val );
elsif 'date' = p_type then
l_date := to_date(p_val, 'YYYY.MM.DD');
elsif 'varchar2' then
null;//do nothing
else
return 'N'; //uknown type
end if;
RETURN 'Y';
EXCEPTION
WHEN value_error THEN
RETURN 'N';
END is_number;
Just try to convert a string to a number, and when an exception occurs, do nothing:
querystring VARCHAR2(500);
some_number number;
begin
some_number := to_number( pFieldValue ); /* trying to convert to a number */
queryString := 'DELETE FROM ' ||pTableName||
' WHERE '||pFieldName ||' = :x' ;
EXECUTE IMMEDIATE queryString USING some_number;
EXCEPTION
WHEN VALUE_ERROR THEN null; /* do nothing when pFieldValue is not a number*/
end SP_DELETE_FROM_TABLE;
For some reason, I have certain fields in a table, that are collections of chars. Chars of different length. Example:
create or replace type t_charray_1 as varray(5) of char(1);
create or replace type t_charray_2 as varray(5) of char(2);
create or replace type t_charray_3 as varray(5) of char(3);
create or replace type t_charray_4 as varray(5) of char(4);
create table mytable (
field1 number,
field2 t_charray_1,
field3 t_charray_3,
Also, I have a function that returns a (fixed length) string representation of a mytable record. This function calls other functions that are returning the string representation of a given collection-typed field. Examples:
function to_chr(
p_array in t_charray_1,
pad_length in number,
p_list_length in number
) return char as
v_res varchar2(255) := '';
begin
for i in 1 .. p_list_length loop
if p_array is not null and p_array.exists(i) and p_array(i) is not null then
v_res := v_res || rpad(p_array(i), pad_length, ' ');
else
v_res := v_res || rpad(' ', pad_length, ' ');
end if;
end loop;
return v_res;
end to_chr;
------------------------------------------------------------------------------
function to_chr(
p_array in t_charray_2,
pad_length in number,
p_list_length in number
) return char as
v_res varchar2(255) := '';
begin
for i in 1 .. p_list_length loop
if p_array is not null and p_array.exists(i) and p_array(i) is not null then
v_res := v_res || rpad(p_array(i), pad_length, ' ');
else
v_res := v_res || rpad(' ', pad_length, ' ');
end if;
end loop;
return v_res;
end to_chr;
Note that these functions are overloaded versions of each other. The only difference in their signature is the type of the p_array argument.
Please also note that the bodies of these functions are identical.
Motivation
I want to eliminate duplicate code. What are my choices?
EDIT I have heard of sys.anydata but never used it. Can it be a solution?
You could write a procedure that takes the largest type, and explicitly CAST the smaller types to the larger type before passing them. Note that CAST can only be used in SQL
DECLARE
x t_charray_1 := t_charray_1();
y t_charray_2 := t_charray_2();
PROCEDURE foo( p_foo t_charray_2 )
AS
BEGIN
FOR i IN p_foo.FIRST..p_foo.LAST loop
dbms_output.put_line( p_foo(i) );
END LOOP;
END;
BEGIN
x.EXTEND;
x.EXTEND;
x(1) := 'A';
x(2) := 'B';
y.EXTEND;
y.EXTEND;
y(1) := 'AA';
y(2) := 'BB';
foo(y);
SELECT CAST(x AS t_charray_2) INTO y FROM dual;
foo(y);
END;
/
Try:
create or replace function to_chr(p_array in anydata,
pad_length in number,
p_list_length in number) return char as
v_res varchar2(255) := '';
x number;
v_array t_charray_4;
v_array1 t_charray_1;
v_array2 t_charray_2;
v_array3 t_charray_3;
begin
dbms_output.put_line(p_array.GetTypeName);
case p_array.GetTypeName
when '<schema>.T_CHARRAY_1' then
x := p_array.GetCollection(v_array1);
select cast(v_array1 as t_charray_4) into v_array from dual;
when '<schema>.T_CHARRAY_2' then
x := p_array.GetCollection(v_array2);
select cast(v_array2 as t_charray_4) into v_array from dual;
when '<schema>.T_CHARRAY_3' then
x := p_array.GetCollection(v_array3);
select cast(v_array3 as t_charray_4) into v_array from dual;
when '<schema>.T_CHARRAY_4' then
x := p_array.GetCollection(v_array);
end case;
for i in 1 .. p_list_length loop
if v_array is not null and v_array.exists(i) and v_array(i) is not null then
v_res := v_res || rpad(v_array(i), pad_length, ' ');
else
v_res := v_res || rpad(' ', pad_length, ' ');
end if;
end loop;
return v_res;
end to_chr;
you can run it like this:
declare
p_array anydata;
v_array t_charray_3 := new t_charray_3('aaa', 'bbb');
v_res varchar2(255);
begin
p_array := anydata.convertcollection(v_array);
v_res := to_chr(p_array => p_array, pad_length => 2, p_list_length => 3);
end;