Most of the time it is my curiosity, but there have been occasions, where I needed to know if a program calls a particular service program. It is easy to discover which service a programs call a service program, but what about the service programs that that those service programs calls? Now there is way to do that.
A new SQL Table function, PROGRAM_RESOLVED_ACTIVATIONS, has been added to IBM i as part of latest Technology Refreshes, IBM i 7.6 TR1 and IBM i 7.5 TR7.
PROGRAM_RESOLVED_ACTIVATIONS has five parameters:
- PROGRAM_LIBRARY: Library that contains the object
- PROGRAM_NAME: Name of the program or service program
- OBJECT_TYPE: *PGM if the object is a program, *SRVPGM if it is a service program
- DEFERRED_SERVICE_PROGRAMS: NO do not follow deferred activations, these are service programs that had *DEFER in the BNDSRVPGM parameter of the CRTPGM or CRTSRVPGM command. YES follow the deferred activations, this is the default
- IGNORE_ERRORS: NO if an error is found an error happens. YES a warning is returned; this is the default
I have only used the first three.
I am going to need some programs for examples. The first is the simplest RPG I could write. I called it TESTRPG:
01 **free 02 *inlr = *on ; |
Before this new Table function I used the BOUND_SRVPGM_INFO View to see which service programs are called by this program:
01 SELECT BOUND_SERVICE_PROGRAM_LIBRARY AS "SrvPgm lib", 02 BOUND_SERVICE_PROGRAM AS "SrvPgm name" 03 FROM QSYS2.BOUND_SRVPGM_INFO 04 WHERE PROGRAM_LIBRARY = 'MYLIB' 05 AND PROGRAM_NAME = 'TESTRPG' 06 AND OBJECT_TYPE = '*PGM' |
This gives me the three service programs that are called by this program:
SrvPgm lib SrvPgm name ---------- ----------- QSYS QRNXIE QSYS QRNXUTIL QSYS QLEAWI |
That is just the "first level". What service programs do those service programs call?
That is where PROGRAM_RESOLVED_ACTIVATIONS comes into its own:
01 SELECT * 02 FROM TABLE(QSYS2.PROGRAM_RESOLVED_ACTIVATIONS( 03 PROGRAM_LIBRARY => 'MYLIB', 04 PROGRAM_NAME => 'TESTRPG', 05 OBJECT_TYPE => '*PGM')) |
Whoa, there are so many results, way too many to show here. Below are the first few columns and rows, and the last row.
BOUND_
SERVICE_ BOUND_
PROGRAM_ PROGRAM OBJECT PROGRAM_ SERVICE_
LEVEL LIBRARY _NAME _TYPE LIBRARY PROGRAM
----- -------- --------- -------- --------- --------
1 MYLIB TESTRPG *PGM QSYS QRNXIE
1 MYLIB TESTRPG *PGM QSYS QRNXUTIL
1 MYLIB TESTRPG *PGM QSYS QLEAWI
2 QSYS QRNXIE *SRVPGM QSYS QRNXDUMP
2 QSYS QRNXIE *SRVPGM QSYS QLECWI
2 QSYS QRNXIE *SRVPGM QSYS QC2UTIL1
2 QSYS QRNXIE *SRVPGM QSYS QLEAWI
15 QSYS QCLGASSP *SRVPGM QSYS QLEAWI
|
The LEVEL column denotes how deep this service program was called. Service programs will be listed more than once if they are called by more than one service program.
How many calls are there to service programs that are called by this RPG program, or by the service programs it calls:
01 SELECT COUNT(*) 02 FROM TABLE(QSYS2.PROGRAM_RESOLVED_ACTIVATIONS( 03 PROGRAM_LIBRARY => 'MYLIB', 04 PROGRAM_NAME => 'TESTRPG', 05 OBJECT_TYPE => '*PGM')) |
The number of calls:
00001 ------ 3447 |
That is a lot of calls!
The next example program I wrote, includes a service program I created, I called the program TESTRPG1:
01 **free
02 ctl-opt bnddir('TESTBNDDIR') dftactgrp(*no) ;
04 dcl-pr Procedure1 char(20) ;
05 end-pr ;
06 dcl-pr Procedure2 char(20) ;
07 end-pr ;
08 dsply Procedure1() ;
09 dsply Procedure2() ;
10 *inlr = *on ;
|
Line 1: The binding directory TESTBNDDIR lists the service programs and modules to be included in this program.
Lines 4 – 7: Procedure definitions for the two procedures, in the service program, that I will be calling.
I can see what service program I added to the binding directory using the BINDING_DIRECTORY_INFO View:
01 SELECT ENTRY,ENTRY_LIBRARY,ENTRY_TYPE,ENTRY_ACTIVATION 02 FROM QSYS2.BINDING_DIRECTORY_INFO 03 WHERE BINDING_DIRECTORY_LIBRARY = 'MYLIB' 04 AND BINDING_DIRECTORY = 'TESTBNDDIR' |
Line 1: I am only selecting the columns that appear on the screen when I add binding directories entries.
There is only one result as only one service program was added to the binding directory:
ENTRY_ ENTRY ENTRY_
ENTRY LIBRARY _TYPE ACTIVATION
---------- ------- ------- ----------
TESTSRVPGM *LIBL *SRVPGM *IMMED
|
BOUND_SRVPGM_INFO shows the service programs directly called by TESTRPG1:
01 SELECT BOUND_SERVICE_PROGRAM_LIBRARY AS "SrvPgm lib", 02 BOUND_SERVICE_PROGRAM AS "SrvPgm name" 03 FROM QSYS2.BOUND_SRVPGM_INFO 04 WHERE PROGRAM_LIBRARY = 'MYLIB' 05 AND PROGRAM_NAME = 'MYLIB' 06 AND OBJECT_TYPE = '*PGM' |
Which returns:
SrvPgm lib SrvPgm name ---------- ----------- *LIBL TESTSRVPGM QSYS QRNXIE QSYS QRNXUTIL QSYS QLEAWI |
I see my service program, followed by the same service programs TESTRRPG called.
Rather than show a few of the service programs that are called, I am just going to show the count of those called:
SELECT COUNT(*) FROM TABLE(QSYS2.PROGRAM_RESOLVED_ACTIVATIONS(
PROGRAM_LIBRARY => 'MYLIB',
PROGRAM_NAME => 'TESTRPG1',
OBJECT_TYPE => '*PGM'))
|
Which returns the number:
00001 ------ 3451 |
That number is different from the number for TESTRPG. I can use the following statement to return the rows in the results for TESTRPG1, that are not in TESTRPG.
01 SELECT * FROM TABLE(QSYS2.PROGRAM_RESOLVED_ACTIVATIONS( 02 PROGRAM_LIBRARY => 'MYLIB', 03 PROGRAM_NAME => 'TESTRPG1', 04 OBJECT_TYPE => '*PGM')) 05 EXCEPT 06 SELECT * FROM TABLE(QSYS2.PROGRAM_RESOLVED_ACTIVATIONS( 07 PROGRAM_LIBRARY => 'MYLIB', 08 PROGRAM_NAME => 'TESTRPG', 09 OBJECT_TYPE => '*PGM')) |
The above joins the two statements for the programs into one statement with an EXCEPT. The Except will return all the rows in the results for TESTRPG1 that are not in the results for TESTRPG.
The results are:
BOUND_
SERVICE_ BOUND_
PROGRAM_ PROGRAM OBJECT PROGRAM_ SERVICE_
LEVEL LIBRARY _NAME _TYPE LIBRARY PROGRAM
----- -------- ---------- -------- --------- --------
2 MYLIB TESTSRVPGM *SRVPGM QSYS QLEAWI
2 MYLIB TESTSRVPGM *SRVPGM QSYS QRNXIE
1 MYLIB TESTRPG1 *PGM QSYS QLEAWI
1 MYLIB TESTRPG1 *PGM QSYS QRNXUTIL
2 MYLIB TESTSRVPGM *SRVPGM QSYS QRNXUTIL
1 MYLIB TESTRPG1 *PGM *LIBL TESTSRVPGM
1 MYLIB TESTRPG1 *PGM QSYS QRNXIE
|
The third, fourth, and last result are the equivalent of ones for TESTRPG. First, second, fifth, and sixth are the four additional results for TESTRPG1, which are due to the service program TESTSRVPGM.
If I just want a list of service programs, without having repeats, I can use the Select Distinct statement:
01 SELECT DISTINCT PROGRAM_LIBRARY, PROGRAM_NAME, OBJECT_TYPE 02 FROM TABLE(QSYS2.PROGRAM_RESOLVED_ACTIVATIONS( 03 PROGRAM_LIBRARY => 'MYLIB', 04 PROGRAM_NAME => 'TESTRPG', 05 OBJECT_TYPE => '*PGM')) 06 ORDER BY PROGRAM_LIBRARY, PROGRAM_NAME, OBJECT_TYPE |
Line 1: Select Distinct returns one result for each combination of program library, program name, and object type.
Line 6: I am sorting the results in the same order as the unique selection.
I am not going to show the results of this statement.
The Select Distinct reduces the number of results for TESTRPG from 3447 to 377, which is a easier number of results to analyze.
Having shown examples of discovering all the service programs called by a program, I can do the same for any service program:
01 SELECT * 02 FROM TABLE(QSYS2.PROGRAM_RESOLVED_ACTIVATIONS( 03 PROGRAM_LIBRARY => 'QSYS2', 04 PROGRAM_NAME => 'JOB_NAME', 05 OBJECT_TYPE => '*SRVPGM')) |
Line 4: JOB_NAME is a SQL global variable. Global variables are service programs so I can use PROGRAM_RESOLVED_ACTIVATIONS to discover what other service programs they call.
You can learn more about the PROGRAM_RESOLVED_ACTIVATIONS Table function from the IBM website here.
This article was written for IBM i 7.6 TR1 and 7.5 TR7.




No comments:
Post a Comment
To prevent "comment spam" all comments are moderated.
Learn about this website's comments policy here.
Some people have reported that they cannot post a comment using certain computers and browsers. If this is you feel free to use the Contact Form to send me the comment and I will post it for you, please include the title of the post so I know which one to post the comment to.