Can front end can use procedure inside of a service program? - db2

We have a requirement where the front end team what to use the procedures inside of a service program. Can we achieve this with stored procedure calling the procedure inside of a service program.
Front end ---Calling---> Stored procedure ----calling---> procedure inside of a service program
Can this be done?

Yes you can point an external stored procedure to a procedure in a service program.
Key options are the the EXTERNAL NAME, LANGUAGE and PARAMETER STYLE clauses.
LANGUAGE RPGLE
EXTERNAL NAME 'MYLIB/MYSRVPGM(MyReallyCoolProcedure)'
note that you can have mixed-case procedure names in RPG IV. SO be sure to use the name as shown via DSPSRVPGM.
Now lets talk about PARAMETER STYLE, if you're trying to re-use an existing procedure quickly then you'll likely want to use PARAMETER STYLE GENERAL. However, that implies that the stored proc can not and will not be called on null input.
If you want the stored procedure to be CALLED ON NULL INPUT, then you'll need to use a parameter style of SQL or GENERAL WITH NULLS. The problem is that both of those mean that some extra parameters are passed implicitly to the RPG program or procedure. Meaning the RPG code must be designed to accept them.
PARAMETER STYLE SQL provides the best interface.
Specifies that in addition to the parameters on the CALL statement,
several additional parameters are passed to the (external) procedure.
The parameters are defined to be in the following order:
The first n parameters are the parameters that are specified on the CREATE PROCEDURE statement.
n parameters for indicator variables for the parameters.
A CHAR(5) output parameter for SQLSTATE. The SQLSTATE returned indicates the success or failure of the procedure. The SQLSTATE
returned is assigned by the external program. The user may set the
SQLSTATE to any valid value in the external program to return an error
or warning from the procedure.
A VARCHAR(517) input parameter for the fully qualified procedure name.
A VARCHAR(128) input parameter for the specific name.
A VARCHAR(1000) output parameter for the message text.
The following additional parameter may be passed as the last
parameter:
A parameter for the dbinfo structure, if DBINFO was specified on the CREATE PROCEDURE statement.
My preferred practice is to create a new RPGLE wrapper, that accepts the parameter list needed for PARAMETER STYLE SQL and calls the existing RPGLE prcoedure.
This allows me to accept some NULL parameters if needed and allows me better control over what SQLSTATE is passed back if any errors occur.
Another good reference is the IBM Redbook, External Procedures, Triggers, and User-Defined Functions on IBM DB2 for i

Yes you can, here is an example of a stored procedure that calls a procedure in a service program. The procedure is called DeleteTrip in service program CrewWeb, it has one input parameter and two output parameter.
CREATE PROCEDURE CREWCALL.WEB_DELETETRIP (
IN DOCNO DECIMAL(8, 0) ,
OUT ERRORCODE INTEGER ,
OUT ERRORMSG CHAR(100) )
LANGUAGE RPGLE
SPECIFIC CREWCALL.WEB_DELETETRIP
NOT DETERMINISTIC
MODIFIES SQL DATA
EXTERNAL NAME 'CREWOBJ/CREWWEB(DELETETRIP)'
PARAMETER STYLE GENERAL ;

Related

Postgres trigger function with param and OLD/NEW called from another trigger function

Trying to create trigger which is supposed to use NEW and OLD and also accept table name as param, but always getting error:
You cannot directly call a trigger function from SQL or PL/pgSQL. So this line is wrong
EXECUTE function_update(table_name );
for three reasons:
EXECUTE takes a string containing an SQL function as argument. So PostgreSQL wants to call your function and execute the result as an SQL statement. You mean PERFORM function_update(table_name);, but that is also wrong for the following reasons.
You supply an argument to the function, but you defined it without parameters. This causes the error message.
You are trying to call a trigger function in an SQL statement. That will always fail.
You shouldn't have defined function_update as trigger function, but as normal function that can be called from SQL.

Can Firebird SQL procedure know the parent procedure/trigger from which it is called from?

I have SQL procedure which should return a bit different result if it is called from one specific procedure. Is it possible for the SQL procedure to detect that it is called from one particular other SQL procedure?
Maybe monitoring mon$... table data can give the answer?
Question applied to Firebird 2.1
E.g. there is mon$call_stack table, but for mostly mon$... tables are empty for Firebird 2.1, they fill up for later versions of Firebird.
Hidden data dependencies are bad idea. There is a reason why programmers see "pure function" as a good thing to pursue. Perhaps not in all situations and not at all costs, but when other factors are not affected it better be so.
https://en.wikipedia.org/wiki/Pure_function
So, Mark is correct that if there is something that affects your procedure logic - then it better be explicitly documented by becoming an explicit function parameter. Unless your explicit goal was exactly to create a hidden backdoor.
This, however, mean that all the "clients" of that procedure, all the places where it can be called from, should be changed as well, and this should be done in concert, both during development and during upgrades at client deployment sites. Which can be complicated.
So I rather would propose creating a new procedure and moving all the actual logic into it.
https://en.wikipedia.org/wiki/Adapter_pattern
Assuming you have some
create procedure old_proc(param1 type1, param2 type2, param3 type3) as
begin
....some real work and logic here....
end;
transform it into something like
create procedure new_proc(param1 type1, param2 type2, param3 type3,
new_param smallint not null = 0) as
begin
....some real work and logic here....
....using new parameter for behavior fine-tuning...
end;
create procedure old_proc(param1 type1, param2 type2, param3 type3) as
begin
execute procedure new_proc(param1, param2, param3)
end;
...and then you explicitly make "one specific procedure" call new_proc(...., 1). Then gradually, one place after another, you would move ALL you programs from calling old_proc to calling new_proc and eventually you would retire the old_proc when all dependencies are moved to new API.
https://www.firebirdsql.org/rlsnotesh/rnfbtwo-psql.html#psql-default-args
There is one more option to pass "hidden backdoor parameter" - that is context variables, introduced in Firebird 2.0
https://www.firebirdsql.org/rlsnotesh/rlsnotes20.html#dml-dsql-context
and then your callee would check like that
.....normal execution
if ( rdb$get_context('USER_TRANSACTION','my_caller') is not null) THEN BEGIN
....new behavior...
end;
However, you would have to make that "one specific procedure" to properly set this variable before calling (which is tedious but not hard) AND properly delete it after the call (and this should be properly framed to properly happen even in case of any errors/exceptions, and this also is tedious and is not easy).
I'm not aware of any such option. If your procedure should exhibit special behaviour when called from a specific procedure, I'd recommend that you make it explicit by adding an extra parameter specifying the type of behaviour, or separating this into two different procedures.
That way, you can also test the behaviour directly.
Although I agree that the best way would probably be to add a parameter to the procedure to help identify where it is being called from, sometimes we don't have the luxury for that. Consider the scenario where the procedure signature can't change because it is in a legacy system and called in many places. In this scenario I would consider the following example;
The stored procedure that needs to know who called it will be called SPROC_A in this example.
First we create a Global Temp Table
CREATE GLOBAL TEMPORARY TABLE GTT_CALLING_PROC
( PKEY INTEGER primary key,
CALLING_PROC VARCHAR(31))
ON COMMIT DELETE ROWS;
Next we create another Stored procedure called SPROC_A_WRAPPER that will wrap the calling to SPROC_A
CREATE OR ALTER PROCEDURE SPROC_A_WRAPPER
AS
DECLARE CALLING_SPROC VARCHAR(31);
BEGIN
DELETE FROM GTT_CALLING_PROC
WHERE GTT_CALLING_PROC.PKEY = 1;
INSERT INTO GTT_CALLING_PROC (
PKEY,
CALLING_PROC)
VALUES (
1,
'SPROC_A_WRAPPPER');
EXECUTE PROCEDURE SPROC_A;
DELETE FROM GTT_CALLING_PROC
WHERE GTT_CALLING_PROC.PKEY = 1;
END
and finally we have SPROC_A
CREATE OR ALTER PROCEDURE SPROC_A
AS
DECLARE CALLING_SPROC VARCHAR(31);
BEGIN
SELECT FIRST 1 CALLING_PROC
FROM GTT_CALLING_PROC
WHERE GTT_CALLING_PROC.PKEY = 1
INTO :CALLING_SPROC;
IF (:CALLING_SPROC = 'SPROC_A_WRAPPER') THEN
BEGIN
/* Do Something */
END
ELSE
BEGIN
/* Do Something Else */
END
END
The SPROC_A_WRAPPER will populate the Temp table, call that SPROC_A and then delete the row from the Temp Table, in case SPROC_A is called from someplace else within the same transaction, it won't think SPROC_A_WRAPPER called it.
Although somewhat crude, I believe this would satisfy your need.

Stored procedure in db2 with cursor return type

I am developing a stored procedure (SP) in db2 which will return some data in the form of output cursor but field lengths for different field may vary. I am facing issues as I am not able to make SP compile for this requirement. Below is the code for reference
create table employee(id bigint,first_name varchar(128),last_name varchar(128));
create table employee_designation(id bigint, emp_id bigint,
designation varchar(128));
create type empRowType as row(first_name varchar(128),last_name varchar(128),
designation varchar(128));
create type empCursorType as empRowType cursor;
create procedure emp_designation_lookup(in p_last_name varchar(128), out emp_rec empCursorType)
result sets 0
language SQL
begin
set emp_rec = cursor for select a.first_name,a.last_name,b.designation
from employee a, employee_designation b
where a.id=b.EMP_ID
and a.LAST_NAME = p_last_name;
end;
the above SP compiles and return the result as intended. However if I change the row definition as below
create type empRowType as row(first_name varchar(120),last_name varchar(128),
designation varchar(128));
On recompiling the SP, I get the following error
BMS sample -- [IBM][CLI Driver][DB2/NT64] SQL0408N A value is not compatible with the
data type of its assignment target. Target name is "EMP_REC". LINE NUMBER=5. SQLSTATE=42821
The error is coming as first_name defined in cursor is not of same length in the table employee(cursor has 120 whereas table has 128)
However for my actual work, I need the return values computed based on some logic and hence the length specified in the cursor will be different from what is there in the table. Also I have some new columns in the cursor which are not related with table's column (for example determining the bonus amount or should the employee be promoted etc).
I want to know if there is indeed some solution to such scenario specifically to db2. I am new to db2 and am using version 10.5.7. I also explored multiple articles in IBM docs but not able to find the exact resolution. Any help of pointers will be of great help.
When you use a strongly typed cursor, then any assignment involving that cursor must exactly match the relevant type. Otherwise the compiler will throw an exception, which is your symptom.
Db2 SQL PL also supports weak cursors, and an SQL PL procedure output parameter type can be a weak cursor. This means that a stored procedure declaration can use ...OUT p_cur CURSOR (so there is no preassigned user defined type linked to that cursor) , and then assign that output parameter from different queries ( set p_cur = CURSOR FOR SELECT ... ) . In my case the caller is always SQL (not jdbc), but you might experiment with jdbc as IBM gives an example in the Db2-LUW v11.5 documentation.
Most people use simple result-sets (instead of returned cursors) to harvest the output from queries in stored procedures. These result-sets are consumable by all kinds of client applications (jdbc, odbc, cli) and languages that uses those interfaces (java, .net, python, php, perl, javascript, command-line/scripting etc.). So the simple result sets offer more general purpose usability that returned cursor parameters.
IBM publishes various Db2 samples in different places (on github, in the samples directory-tree of your Db2 server instance directory, in the Knowledge Center etc.).

Not able to drop schema in DB2 Using ADMIN_DROP_SCHEMA Stored Procedure

I found at several places to be able to drop a schema in DB2 along with all of its contents (indexes, SPs, triggers, sequences etc) using
CALL SYSPROC.ADMIN_DROP_SCHEMA('schema_name', NULL, 'ERRORSCHEMA', 'ERRORTAB');
However, I am getting the following error while using this command:
1) [Code: -469, SQL State: 42886] The parameter mode OUT or INOUT is not valid for a parameter in the routine named "ADMIN_DROP_SCHEMA" with specific name "ADMIN_DROP_SCHEMA" (parameter number "3", name "ERRORTABSCHEMA").. SQLCODE=-469, SQLSTATE=42886, DRIVER=4.22.29
2) [Code: -727, SQL State: 56098] An error occurred during implicit system action type "2". Information returned for the error includes SQLCODE "-469", SQLSTATE "42886" and message tokens "ADMIN_DROP_SCHEMA|ADMIN_DROP_SCHEMA|3|ERRORTABSCHEMA".. SQLCODE=-727, SQLSTATE=56098, DRIVER=4.22.29
Can anyone help me suggest what's wrong here? I tried to look at several places but didn't get any idea. It doesn't seem it's an authorization issue. Using DB2 version 11.5.
You are using the ADMIN_DROP_SCHEMA procedure parameters incorrectly, assuming you are CALLing the procedure from SQL and not the CLP.
The third and fourth parameters cannot be a literal (despite the documentation giving such an example), instead they must be host-variables (because the the procedure requires them to be input/output parameters).
If the stored-procedure completes without errors it sets these parameters to NULL. so your code should check for this.
If the stored-procedure detects errors, it creates and adds rows to the specified table and leaves the values of these parameters unchanged, and you must then query that table to list the error(s). You should drop this table before calling the stored procedure otherwise the procedure will fail with -601.
Example:
--#SET TERMINATOR #
drop table errschema.errtable#
set serveroutput on#
begin
declare v_errschema varchar(20) default 'ERRSCHEMA';
declare v_errtab varchar(20) default 'ERRTABLE';
CALL SYSPROC.ADMIN_DROP_SCHEMA('SOMESCHEMA', NULL, v_errschema, v_errtab);
if v_errschema is null and v_errtab is null
then
call dbms_output.put_line('The admin_drop_schema reported success');
else
call dbms_output.put_line('admin_drop_schema failed and created/populated table '||rtrim(v_errschema)||'.'||rtrim(v_errtab) );
end if;
end#
You can use global variables if you would like to use ADMIN_DROP_SCHEMA outside of compound SQL
E.g.
CREATE OR REPLACE VARIABLE ERROR_SCHEMA VARCHAR(128) DEFAULT 'SYSTOOLS';
CREATE OR REPLACE VARIABLE ERROR_TAB VARCHAR(128) DEFAULT 'ADS_ERRORS';
DROP TABLE IF EXISTS SYSTOOLS.ADS_ERRORS;
CALL ADMIN_DROP_SCHEMA('MY_SCHEMA', NULL, ERROR_SCHEMA, ERROR_TAB);

Parameters in the FormsOf function and SQL injection

Is the following SQL susceptible to SQL injection via the #SearchWord parameter?
I want to use parameters with the FormsOf function, but the only guide to doing so I've found is in this Stack Overflow question: How to pass parameter to FormsOf function in sql server
However the solution seems to be to use a bit of dynamic SQL, and I was wondering if that would be susceptible to SQL injection. What would happen in the following example if #searchWord contained a SQL injection type string? Is it not a problem because it's still within a parameter, passed as an argument to FREETEXTTABLE?
The solution given is:
DECLARE #SearchWord nvarchar(max)
SET #SearchWord = 'tax'
DECLARE #SearchString nvarchar(max)
SET #SearchString = 'FormsOf(INFLECTIONAL, "' + #SearchWord + '")'
SELECT listing_id, RANK, name, address, city, zip, heading, phone
FROM listings a,
FREETEXTTABLE(listings, *, #SearchString)
WHERE [KEY] = a.listing_id
ORDER BY RANK DESC, name
No, it's not susceptible. There's no dynamic SQL here (that would require either using EXEC or sp_executesql), so there's no vector for SQL injection.
In order for a SQL injection vulnerability to exist, the user-supplied string (in this case #SearchWord) must actually be inserted directly into the text of the SQL statement. Here, it's only being used to construct another string variable, which is subsequently used as a parameter to another SQL statement.
This statement can, however, fail if the user inputs an "invalid" search word, i.e. one containing single quotes, so you should probably still escape whatever value is passed to #SearchWord. But it cannot be used to execute arbitrary SQL.
I haven't tested this, but I don't think the interpreter is simply pasting the value of #SearchString into the statement. It should parse #SearchString using the rules that FREETEXTTABLE expects--that's the way other parameters work.