How to obtain name of input variable of the procedure (PLSQL) - sql

Let's say I have procedure called myProc(variable varchar2);
Then I call it:
exec myProc(actionVariable);
Is there a way, how to obtain 'actionVariable' as a string in procedure myProc? So when I use procedure differently:
exec myProc(anotherVariable);
I'll obtain 'anotherVariable' as a string in procedure myProc.
Thanks. I only found that I can obtain origin variable name with 'select argument_name from user_arguments....'

You can't determine the name your caller assigned to the variable it passed (as far as I know; maybe you could hack something with PL/Scope but it wold be horrible).
I can see three options, depending on how many variations you need. I'm assuming there aren't many from your comment that the name affects which table the procedure works against.
You could pass the variable name, or the bit you're interested in, as a separate parameter:
procedure myProc(variable varchar2, variable_name varchar2) ...
exec myProc(varIDShop, 'Shop');
You could finesse that a little with wrapper procedures for each variant:
procedure myProc(variable varchar2, variable_name varchar2) ...
procedure myProcShop(variable varchar2) is
begin
myProc(variable, 'Shop');
end;
/
exec myProcShop(varIDShop);
... so your call just has to pick the relevant wrapper function to call.
Or you could declare multiple variables, one for each variant, and only set the one that's relevant:
procedure myProc(shop_variable varchar2, office_variable varchar2, ...) ...
exec myProc(shop_variable => varIDShop);
... and then test which is set within the procedure.
The last two would both mean your call still only has one argument, but they are a bit more complex, and have potential to use the wrong variable name or procedure name (cut-and-paste errors). Although so does the first, I suppose.
None of those directly use the variable name in the caller though. But on the other hand, you could call any of them, e.g. for testing, without having to declare a variable at all - just passing a string literal:
exec myProc('Tesco', 'Shop');
exec myProc(shop_variable => 'Sainsbury');
exec myProcShop('Asda');

Related

Put stored procedure result/output into variable

I have a stored procedure that I'm working with where I'm calling another stored procedure within. The nested procedure accepts a value and returns an ID based on that successfully, but I can't seem to figure out how to cast that result into a variable for another stored procedure in the same wrapper.
Where I am now:
P1: BEGIN ATOMIC
CALL GET_ID_BY_VALUE(P_VALUE);
-- want to store output into V_NEW_ID
CALL NEW_PROCEDURE(V_NEW_ID);
END P1;
Does that make sense?
Arrange for the stored-procedure GET_ID_BY_VALUE to have a second parameter, which is an OUTPUT parameter (and this can be variable V_NEW_ID declared in the scope of your main procedure).
The signature of the GET_ID_BY_VALUE will then be something like GET_ID_BY_VALUE (IN P_VALUE varchar(...) , OUT V_NEW_ID bigint).
When the GET_ID_BY_VALUE sets that parameter V_NEW_ID to some value and returns, the caller has the assigned value accessible in variable V_NEW_ID, which is declared in the caller's scope. The caller can then supply that same variable as an input parameter to NEW_PROCEDURE( V_NEW_ID).

How to pass Array to SQL Stored Procedure

I am trying to pass arrays to a DB2 stored procedure and I am having trouble.
Here are a few code snippets:
create type intArrayType as integer array[];
CREATE OR REPLACE PROCEDURE
array_trial (IN integer_array INTARRAYTYPE)
BEGIN
SELECT UNNEST(integer_array) FROM sysibm.sysdummy1;
END
It compiles, but when I try to call:
CALL array_trial(ARRAY[1,2,3]);
I am getting a -104 error.
When I try to call from RPGLE, I cannot compile because it does not like the array
Any ideas?
UNNEST is used in the from clause as it creates a temporary table...
CREATE OR REPLACE PROCEDURE
array_trial (IN integer_array INTARRAYTYPE)
BEGIN
declare c1 cursor with return to client for
SELECT * FROM UNNEST(integer_array) as rs;
open c1;
END;
Unfortunately, the ARRAY constructor is currently rather limited. The documentation specifically says can only be specified on the right side of a SET variable or assignment-statement. So trying to use it directly like so doesn't work.
CALL array_trial(ARRAY[1,2,3]);
It returns the following message:
SQL State: 428H2
Vendor Code: -20441
Message: [SQ20441] Array type not valid where specified.
Cause . . . . . : An array type was used but is not allowed in the
specified context. Array types can only be used: -- As an argument of an
SQL or JAVA procedure. -- For an SQL variable declared in an SQL procedure.
-- In a CAST specification in an SQL procedure.
Recovery . . . : Remove the reference to the array type. Try the request again.
You can build a driver stored procedure:
create or replace procedure mysp
begin
declare myarray intArrayType;
set myarray = ARRAY[1,2,3];
call array_trial(myarray);
end;
And call that
call mysp;
From what I've been able to find so far An SP with an array parm can be called directly from another SQL procedure or Java...but not RPGLE.

How to handle parameters in SQL PACKAGE?

I have a table which has columns id, name and password.
I made also a SQL PACKAGE for that which looks like (I omitted Package header, since to emphasis my point more clear):
create or replace PACKAGE BODY MEMBER
is
FUNCTION createWith(v_id, v_name, v_password)
return Number
is
BEGIN
Insert into tbl_member(id, name, password)
Values(v_id, v_name, v_password);
return SQL%ROWCOUNT ;
END createWith;
However, since name column is not necessary column(has no 'not null' option), I sometimes I pass only two parameters(id and password). As far as I know, if the numbers of parameter I sending and numbers of declared parameters in function do not match, it should be 'wrong type or number exception', but It works well.
The thing I wonder is how it can automatically handle empty parameter.
Is anyone who knows useful link or its logic, explain this.
//=============================================================
This is first addition
First of all, I guess my question was not clear enough.
So I going to try again.
step 0, make a table with no constrains and options.(also no default values)
step 1, this is the function header.
function creatWith(v_id in varchar2, v_name in varchar2, v_password in varchar2) return number;
step 2, I call this Package in java(it is web application based on Springframework. However, my company call this a 'solution', so I can not open source code).
However, the method which mapped with this Function has only two parameters. like
public void insert(String id, String pw);
step 3, This is the point of question. It occurs an error or not? In my case, it works. But I do not know My company solution handle or Oracle DB automatically does this.
If Oracle does have, can I get some documents for this policy?
It seems that Oracle tries to use "NULL" values for empty parameters. Change the field to NOT NULL and it will start to return an error.
Try declaring a DEFAULT attribute, like:
FUNCTION createWith(
v_id IN NUMBER,
v_name IN VARCHAR2 DEFAULT 'empty',
v_password IN VARCHAR2)
With this, it will write 'empty' for the Name field when the parameter is empty.
For Oracle Database, declare DEFAULT value in the function definition.
FUNCTION createWith(v_id IN NUMBER,
v_name IN VARCHAR2 DEFAULT 'N.A.',
v_password IN VARCHAR2);

Function as parameter to another function in Postgres

Can I create a user defined function in Postgres either through the C-Language Function API or by using pl/pgsql which accepts a callback function as parameter?
As far as I see there is no way to do this through the C-Language API since it only accepts sql datatypes and there is no datatype for function. But maybe I'm missing something?
Since each function / procedure must have an entry in pg_proc, you can use the primary key for identifying the procedure. This would also eliminate the problems with procedures having the same name but different number of parameters or different parameter types.
Shorthands for this are the types regproc and regprocedure with the associated casts for easier handling. Lookup the manual for these.
Identifying the function and passing it around is no problem:
select 'pg_database_size(oid)'::regprocedure; -- create "reference"
regprocedure
-----------------------
pg_database_size(oid)
Use regprocedure as the parameter type.
The problem I did not yet figure out is how to actually call such a thing in a convenient way.
I think you can't, but since there are no anonymous functions, passing function name should do.
Old question and already has an accepted answer. But it doesn't clearly explain how to do this. So I thought of adding a more clear answer.
Let's assume you pass the callback function's name to your main function as a varchar value.
CREATE OR REPLACE FUNCTION public.get_function_fields(fn_name character varying)
...
...
Now if you want to call this fn_name function inside a query, you need to use EXECUTE command, and properly cast your function name using regproc as below.
EXECUTE 'create temp table if not exists temp_call as select * from ' || fn_name::regproc || '() limit 1';
Important part is this: ...|| fn_name::regproc || '()... As you can see, you have to append the parenthesis and cast the function name with ::regproc.
Hope it will help someone!
--create an being parametered function.
CREATE OR REPLACE FUNCTION public.select1() RETURNS integer
LANGUAGE sql
IMMUTABLE
AS $function$ select 10;
$function$
--create an function with function as input parameter.
CREATE OR REPLACE FUNCTION public.func_func(fn_name text)
RETURNS SETOF parent_tree
LANGUAGE plpgsql
AS $function$
begin
RETURN QUERY EXECUTE
format('select * from parent_tree where parent_id = %s::regprocedure', fn_name);
end
$function$
--Call it.
select * from func_func('select1()');

Call for a function with a variable name?

I have one package that has different procedures, and one main procedure through which I am calling other procedures.
Through the front end, I am passing the procedure name into main(). Is there any way by which the procedure can be called just writing the parameter name containing('Procedure Name that is need to be called')?
CREATE OR REPLACE PACKAGE BODY UPLOAD_PKG
IS
--This procedure will populate LOG with messages
PROCEDURE PRINT_LOG_PR IS
BEGIN
fnd_file.put_line(fnd_file.LOG,'ABC');
END PRINT_LOG_PR;
--This procedure will populate LOG with messages
PROCEDURE PRINT_LOG1 IS
BEGIN
fnd_file.put_line(fnd_file.LOG, 'XYZ');
END PRINT_LOG1;
PROCEDURE Main( p_obj_type VARCHAR2
, errbuf VARCHAR2
, retcode VARCHAR2) IS
BEGIN
-Is this possible for eg i have passed PRINT_LOG1 here and calling PRINT_LOG1
p_obj_type ;-
END main;
END UPLOAD_PKG
Yes.
PROCEDURE Main( p_obj_type VARCHAR2
, errbuf VARCHAR2
, retcode VARCHAR2) IS
BEGIN
CASE p_obj_type
WHEN 'PRINT_LOG_PR' THEN PRINT_LOG_PR;
WHEN 'PRINT_LOG1' THEN PRINT_LOG1;
END CASE;
END main;
Use:
CREATE OR REPLACE PACKAGE BODY UPLOAD_PKG
IS
--This procedure will populate LOG with messages
PROCEDURE PRINT_LOG_PR IS
BEGIN
fnd_file.put_line(fnd_file.LOG,'ABC');
END PRINT_LOG_PR;
--This procedure will populate LOG with messages
PROCEDURE PRINT_LOG1 IS
BEGIN
fnd_file.put_line(fnd_file.LOG, 'XYZ');
END PRINT_LOG1;
PROCEDURE MAIN( p_obj_type VARCHAR2
, errbuf VARCHAR2
, retcode VARCHAR2) IS
BEGIN
CASE p_obj_type
WHEN 'PRINT_LOG_PR' THEN UPLOAD_PKG.PRINT_LOG_PR;
WHEN 'PRINT_LOG1' THEN UPLOAD_PKG.PRINT_LOG1;
END CASE;
END MAIN;
END UPLOAD_PKG
The CASE statement I used in the MAIN stored procedure is the PLSQL CASE statement, not the ANSI SQL CASE. You can tell because the PLSQL version needs END CASE to end the CASE statement.
If you don't want to use dynamic pl/sql, then I would suggest to use constant variables in package header to choose between procedures (avoiding hard coding much as possible).
Main procedure call would be for example:
UPLOAD_PKG.MAIN(UPLOAD_PKG.C_PRINT_LOG_PR, v_errbuf, v_retcode);
And in the Main body you would use case like this:
CASE p_obj_type
WHEN C_PRINT_LOG_PR THEN UPLOAD_PKG.PRINT_LOG_PR;
WHEN C_PRINT_LOG1 THEN UPLOAD_PKG.PRINT_LOG1;
ELSE RAISE SOME_ERROR;
END CASE;
In header you can define constant variables to contain whatever:
CREATE OR REPLACE PACKAGE UPLOAD_PKG
IS
C_PRINT_LOG_PR CONSTANT VARCHAR2(22) := 'What ever';
C_PRINT_LOG1 CONSTANT VARCHAR2(22) := 'What ever2';
...
But some cases client applications cannot refer global variables of packages, so you need to create function for every constant variable to return those. But this will go little bit too complicated, if you just could call those correct procedures...
But for the curiosity can you tell us why do you need to use package this way?
As well as the CASE solution, it is possible to do this using dynamic PL/SQL.
PROCEDURE MAIN( p_obj_type VARCHAR2
, errbuf VARCHAR2
, retcode VARCHAR2) IS
BEGIN
EXECUTE IMMEDIATE 'begin upload_pkg.'||p_obj_type|| '; end;';
END MAIN;
Simple parameters (Date, Varhar2, Number) can be passed IN OUT with the USING command.
The key question is whether it is advisable.
As with any dynamic language, it leaves scope for errors that will only be found at runtime, rather than compile time - i.e. passing in a value for p_obj_type that does not exist. You could mitigate this through constants or abstract data types.
Also each dynamic sql or pl/sql command incurs a small parsing overhead vs actual compiled code. This overhead is small, but becomes noticeable if performed inside a loop.
Lastly, the called code must have the same parameter signature.