DB2 external stored procedure fails with CPF9810 when called from client - db2

In a green screen session, caling a program MYLIB/TESTPRG works when my library list is set to QGPL, QTEMP, VENDRLIB1, VENDRLIB2, VENDRLIB3. I can execute call MYLIB/TESTPRG on a green screen command line.
I want to be able to run this command from my Windows client. I created an external stored procedure MYLIB/TESTPROC with external name MYLIB/TESTPRG as I have seen in various articles. My original question stated that I could execute this procedure successfully in STRSQL in a green screen session with my library list as above, but that is false. It does not work. It simply says 'Trigger program or external routine detected an error.' Sorry for the wrong information.
When MYLIB/TESTPROC is called from the client (CALL MYLIB/TESTPROC), it fails with CPF9810 (Library &1 not found). I connected to the database via i Navigator -> Run SQL Scripts. In Connection -> JDBC Settings I had Default SQL schema = 'Use library list of server job' and set Schema list=QGPL,QTEMP,VENDRLIB1,VENDRLIB2,VENDRLIB3. I then executed CALL MYLIB/TESTPROC and got the message as stated above.
What works is when I run the program, i.e. CALL MYLIB/TESTPRG on a green screen command line.
TESTPRG is a C program that takes no arguments. The stored procedure was defined like this:
CREATE PROCEDURE MYLIB/TESTPROC
LANGUAGE C
SPECIFIC MYLIB/TESTPROC
NOT DETERMINISTIC
NO SQL
CALLED ON NULL INPUT
EXTERNAL NAME 'MYLIB/TESTPRG'
PARAMETER STYLE GENERAL ;

CPF9810 - Library &1 not found means that something is trying to access Library &1 (whatever that is, you didn't tell us) and the library as typed is not on the system anywhere. &1 is not the name of the library, it is a substitution variable which will display the library name in the job log. Look at the real library spelling in the job log. Check your spelling. Check the connection to make sure all the libraries are specified correctly. The message will tell you exactly which library is causing the problem.
If indeed the program works in green screen when the library list is set properly, then I would expect the problem to be in your connection where it is trying to set a library list. You cannot add a non-existent library to the library list. That is why it works in green screen, your library is necessarily typed correctly there, or it wouldn't be in the library list. You would get a similar error (same text, different error code) if you tried to add library with a spelling error to the library list in green screen.
Figure out the full text of the message (look in the job log), and you will see just what is throwing the error, and what the library is. Hint, it is not likely SQL throwing the error as those errors all look like SQL#### or SQ#####. More likely a CL command or it's processing program being called by an IBM server that is sending a CPF message.

As you have discovered, you can directly call simple programs without defining an external SQL procedure based on this documentation from IBM:
https://www.ibm.com/support/knowledgecenter/en/ssw_ibm_i_73/db2/rbafzcallsta.htm
I believe the recommendation to create your own external procedure definition for simple programs is primarily to reduce ambiguity. If you have programs and procedures that happen to have matching names, you need to know the rules list to figure out which one is being called for instance.
Also, the rules for external functions are different than external stored procedures and those get confused as well.

Per my comment, I usually make my procedure calls with the library within the call command.
In a terminal session using CALL PGM(MYLIB/TESTPROC). Or in a SQL session using CALL MYLIB.TESTPROC.
This can prevent someone from inadvertently putting your procedure in a personal library or the like. I usually do not specify a session library list on my SQL clients, accepting the system library list.

I had promised to accept Douglas Korinke's comment as an answer. However, I was experimenting a lot and I am no longer sure of what I knew and when I knew it. My problem had something to do with parameter passing to the C program. If I can reproduce it with a simple case I will ask another question.

In a Java program it is possible to set the libraries by using the following method :
ds.setLibraries("list of libraries");
Example :
ds.setServerName("server1");
ds.setPortNumber(1780);
ds.setDatabaseName("DBTEST");
ds.setLibraries("*LIBL,DAT452BS,DAT452BP");

Related

ILE RPG Bind by reference using CRTSQLRPGI

I've been trying a solution for this, but. I cannot find it.
What I'm trying to do, is work with the "bind by reference" ability, but working with ILE RPG written with embedded sql.
I can use the BNDDIR ctl opt in my source. And everything works correctly.
But that means a "bind by copy" method. Checked deleting the SRVPGM and even the BINDDIR. And the caller program still works.
So, is there any way to use "bind by reference" in an ILERPGSQL program?
After my question, an example:
Program SNILOG is a module, that conains several procedures. Part of them, exported.
In QSRVSRC I set the exported procedures, with a source with the same name: SNILOG. Something like this:
STRPGMEXP PGMLVL(*CURRENT)
/************************************************** ******************/
/* *MODULE SNILOG INIGREDI 04/10/21 15:25:30 */
/************************************************** ******************/
EXPORT SYMBOL("GETDIAG_TOSTRING")
EXPORT SYMBOL("GETDIAGNOSTICS")
EXPORT SYMBOL("GRABAR_LOG")
EXPORT SYMBOL("SNILOG")
ENDPGMEXP
As part of the procedures are programmed with embedded sql, the compilation must be done with CRTSQLRPGI, using the parameter OBJTYPE(*SRVPGM).
So, I finally get a SRVPGM called SNILOG, with those 4 procedures exported.
Once I've got the SRVPGM, I add it to a BNDDIR called SNI_BNDDIR.
Ok, let's go to the caller program: SNI600V.
Defined with
dftactgrp(*no)
, of course!.
And compiled with CRTSQLRPGI and parameter OBJTYPE(*PGM).
Here, if I use the control spec
bnddir('SNI_BNDDIR')
, it works fine.
But not fine enough, as this is a "bind by copy" method (I can delete the SRVPGM or the BNDDIR, and it is still working fine).
When I'm not working with SQL, I can use the CRTPGM command, and I can set the BNDSRVPGM parameter, to set the SRVPGM the program is going to be called. Well, just their procedures...
But I cannot find any similar option in CRTSQLRPGI command.
Nor in opt codes in ctl-opt sentence (We have BNDDIR, but not BNDSRVPGM option).
Any idea?
I'm running V7R3M0 with TR level: 6
Thanks in advance!
the use of
bnddir('SNI_BNDDIR')
Is the way to bind by reference OR bind by copy.
The key is what does your BNDDIR look like?
If you want to bind by reference, then it should include *SRVPGM objects.
If you want to bind by copy, then it should include *MODULE objects.
Generally, you want a *BNDDIR for every *SRVPGM that includes the modules (and maybe a utility *SRVPGM or two) needed for building a specific *SRVPGM.
Then one or more *BNDDIR that includes just *SRVPGM objects that are used to build the programs that use those *SRVPGMs.

How can you call programs from a stored procedure?

I have followed other examples on stack exchange to call a built-in program from a stored procedure, but continue to get an error.
Working off this example (Looking for a working example of any OS/400 API wrapped in an external SQL stored procedure wrapped in a user defined SQL function) I built the following to attempt to create a wrapper command to allow me to change object security (my issue is that objects I create, regardless of library) are not always accessible to others in my some function, I must manually set to a common security group.
CREATE OR REPLACE PROCEDURE XX.TST( IN XOBJ CHAR(32), IN XOBJTYPE CHAR(10), IN XNEWOWN CHAR(10))
LANGUAGE CL
SPECIFIC XX.TST
NOT DETERMINISTIC
NO SQL
CALLED ON NULL INPUT
EXTERNAL NAME 'QSYS/CHGOBJOWN'
PARAMETER STYLE GENERAL;
CALL XX . TST('XX/TBL1','*FILE','GRPFRIENDS');
I get the following error:
External program CHGOBJOWN in QSYS not found
But have confirmed that going to the CL of the terminal emulator and typing QSYS/CHGOBJOWN takes me into the parameter input screen
You are trying to define a command as a program, and that just won't work. A command object (*CMD) and a program object (*PGM) are two different things, and cannot be invoked the same way. All is not lost though. There is a DB2 service that allows you to execute commands. You just have to build the proper command string.
Instead of defining a stored procedure, you can call the existing DB2 service like this:
call qsys2.qcmdexec('CHGOBJOWN OBJ(XX/TBL1) OBJTYPE(*FILE) NEWOWN(GRPFRIENDS)');
There are a whole list of services. Documentation can be found here.

MATLAB compiler says some functions in my app are using not licensed for compilation functions

I want to compile my app using matlab-compiler it does so, but with issues...
It says there are some functions that are not licensed for compilation.
The problem is that I haven't used those functions (one of them is fimath.m) in my app.
I think these functions are used inside some of my functions which I don't know.
My question is how to find out which one of my functions are using those functions in order to remove them or replace them with other functions.
There are more than 50 functions in my app and it's not possible to check them one by one.
For every returned "unlicensed" function you can execute the following command,
dbstop in <function name> % without the <>
and afterwards run your code normally for several typical inputs/cases. If it stops at one of these breakpoints, look at the call stack (using either dbstack or the Editor tab of the MATLAB GUI), and identify the entry point from your own code.
If none of the breakpoints is ever hit, it could mean that these functions are referred-to inside the code, but some logic is preventing their execution (turning them, practically, to "unreachable code"). In this case, you will likely need to remove these references manually. To know where from, using information from the link posted by VTodorov you can list the dependencies of each file using
[fList,pList] = matlab.codetools.requiredFilesAndProducts('myFun.m');
which can be called on the output of dir (after some minor conversion). It could be useful to use the toponly flag.

COBOL -> COBOL/DB2 -> COBOL -> COBOL/DB2 pgm call

Lets say like PGM1(cobol) calls--> PGM2(cobol-db2) calls--> PGM3(cobol)--> calls PGM4(cobol-db2).
1Q. PGM3 is modified, which is purely COBOL progam. Do we compile only PGM3 and promote it to production or should we do a BIND again as its being called by and calls cobol-db2 program.
2Q. If PGM4 is modifieed, then what has to be done. (I'm using PACKAGE -> PLAN concept) ?
Also, can any one please explain me bind with package concept when we have cobol-cobol/db2 call.
Ashok,
Its definitely a question of how you making calls.A call can be static and dynamic.
With Dynamic call you do not need to compile main program is sub program changes.
But with Static call you need to compile Main program too.
Ans1 :- Static call in all calls - yes you must compile all programs.
Dynamic call used - just compile sub program.
Ans 2 :- See full details below for package and plan concept.
If you bound the old versions of DBRMs directly into your plan,
· Identify all the DBRMs that are bound directly into that plan for both the changed programs and any unchanged programs, and bind them all into the plan again.
·While you are binding the DBRMs into the plan, applications cannot use the plan to access DB2.
If you bound the old versions of the DBRMs for the changed application programs into packages
·You do not need to bind any other packages or directly-bound DBRMs into the plan again.
·You simply bind the new versions of the DBRMs for the changed application programs into packages with the same names as the old versions.
·You do not need to bind the plan again--it locates the new versions of the packages.
·While you are changing the packages, application programs can still use the other packages and directly-bound DBRMs in the plan.
Hope this helps!!.
As a rule of thumb, if the "consistency token" changes, you should rebind. That is say, if a new DBRM is produced. Draw a picture. It will help. Linking is really a red herring here. If you don't know what a consistency token is you will after your -805. Ask a peer for help (in the first instance).
Also ask you peers about impact analysis. (What else am I not recompiling that I should ?).
If the subroutine contains static SQL statements then it will produce a DBRM when compiled. This changes the consistency token and thus requires the module to be rebound to the database to avoid an 818 consistency token error. If the subroutine contains no SQL then it does not ever need to be bound to the database because no DBRM is ever created for it.
Even a program that contains only dynamic SQL will still create a DBRM that must be bound to the database. The DBRM itself will be pretty much empty apart from the consistency token.
This holds true regardless of whether this is mainframe COBOL or distributed COBOL using DB2 or LUW.
It's been a while since I had to write any COBOL but we always had two relevant rule of thumbs.
Only use Static Calls - your code should be performance tested and if there is no need for a dynamic call for a very specific purpose avoid it at all costs.
Rebind everything when something is changed and check the access paths created PRIOR to putting it live
If you need to wait for a period of outage to complete the task and flip in the updated code in production I would be patient and plan one in and complete the bind then...or get a DBA to do it and get them to confirm it was successful in your outage window or roll back immediately.
If your development environment is sufficiently sophisticated complete the bind in a lower pre-production environment using the stats for the DB2 tables from production (Copy the data in if you can - or get a DBA to do it). And check that none of the access paths for any of the DB2 calls have changed.
Hope this helps
First use this DB2 SQL to get the CONTOKENs
SELECT SUBSTR(COLLID,1,12) AS COLLID ,
SUBSTR(NAME,1,8) AS NAME ,
HEX(CONTOKEN) AS CONTOKEN ,
SUBSTR(OWNER,1,8) AS OWNER ,
SUBSTR(CREATOR,1,8) AS CREATOR ,
PDSNAME ,BINDTIME
FROM SYSIBM.SYSPACKAGE
WHERE NAME= 'program name';
Get the DB2 CONTOKEN (example below)
1ADB70E30768F694
0768F6941ADB70E3 (then reversed contoken 4bytes+4bytes)
Check #1 use reversed token search
Use token 0768F6941ADB70E3 (reversed)
CONTROL.???????.CICSLIB
Should be found
Check #2 use non-reversed token into DBRMLIB
CONTROL.????????.CIC.DBRMLIB
-Use token 1ADB70E30768F694
Should be found
If found then your bind is good.

how to invoke OS system call on DB2?

I'm trying to invoke some C++ code in case a trigger is called inside my db2 DB.
to do so i thought of compiling the C++ code to an executable and to run it as a system call from DB2.
ps: I'm new to DB in general.
thanks in advance !
I think you want to use a DB2 system call:
http://www.ibm.com/developerworks/data/library/techarticle/0303stolze/0303stolze.html
EDIT:
Specifically, it appears that you could just re-use the system call solution referenced in the "Making system calls" section to call an arbitrary command from your trigger:
http://www.ibm.com/developerworks/data/library/techarticle/0303stolze/0303stolze.html#section5
Generally, the from the docs I gather that you will need to call an external UDF (User Defined Function) from your trigger. The UDF itself defines the call to your external program and needs to be created and configured in such a way that DB2 will recognize it.
Here's a PDF that covers UDFs is some detail. The "External UDFs" section on page 453 might be useful.
http://www.redbooks.ibm.com/redbooks/pdfs/sg246503.pdf
This article may also be helpful. It shows a solution for integrating a Java function as a UDF to be called from a trigger.
http://www.ibm.com/developerworks/data/library/techarticle/0205bhogal/0205bhogal.html#download