Last Updated on 20 August 2020 by Roberto De Pedrini
Third episode of the RPG series – FAQ & Howtos, the previous episodes can be found here;
RPG-FAQ-021: PTF SI73189 (7.3 TR8 and 7.4 TR2) – Timestamp and error MCH4437
Compiling RPG programs that use Timestamp on an updated IBM i partition with PTF SI73189 may generate errors (MCH4437) if the compile object is moved to a partition without that PTF installed.
Of course the best solution would be to update both partitions with the same PTFs … but if it is not possible an alternative is to intervene on an Environment Variable (thanks to Barbara Morris for the suggestion) in the compilation partition (the one updated with the PTF indicated)
ADDENVVAR QIBM_RPG_DISABLE_TIMESTAMP_MICROSEC VALUE ('Y')
if desired, it can also be set at system level
ADDENVVAR QIBM_RPG_DISABLE_TIMESTAMP_MICROSEC VALUE ('Y') LEVEL (* SYS)
remembering to do a signoff-signon to "load" this environment variable
RPG-FAQ-022: Service Program and * DEFER
We all probably know the advantages of service programs in developing ILE applications, but perhaps not everyone knows that services programs, just to ensure the best performance, are all “loaded” when the activation group is created / opened: this means that if the number of service programs becomes “important” we may have performance problems at the very first program load (or creation of the Activation Group).
In addition to the performance problem, another problem could be that of the list of libraries that must be “set” right at the initial loading of the program.
Use the keyword * DEFER when compiling the program in the definition of service programs … in this case the service program will be loaded only at the time of its first use … by requesting the loading in memory at that moment and therefore “lightening” the initial opening and allowing to “set” the list of libraries only for actual use:
CRTPGM PGM (YOURLIB / YOURPGM)
MODULE (* PGM)
BNDSRVPGM ((* LIBL / YOURSRVPGM * DEFER))
or ... when adding the service program to the Bind Directory
ADDBNDDIRE BNDDIR (FAQ400 / MYBINDDIR) OBJ ((FAQ400 / MYSRVPGM * SRVPGM * DEFER))
The library problem can of course also be solved by explicitly indicating it when compiling or adding the Binddire:
CRTPGM PGM (YOURLIB / YOURPGM)
MODULE (* PGM)
BNDSRVPGM ((YOURLIB / YOURSRVPGM))
RPG-FAQ-023: How to avoid MCH3402 error when replacing a program object
It’s been talked about on Midrange.com these days: how to avoid the MCH3402 error when replacing a program object with a new version. We know that if we compile with REPLACE (* YES) the old version of the object is moved to the QRPLOBJ and the error does not occur … but, sometimes, perhaps for quick fixes, it is more convenient and less risky, recover program objects perhaps from a backup using MOVOBJ … often creating the MCH3402 error in the Jobs that attempted to access the old version of the object.
Even the technique of using a “NEWOBJ” library to put in front of the * LIBL doesn’t completely solve the problem …
An interesting solution is to use the QLIRNMO API … perhaps through an interface like that of CARSTEN FLENSBURG in this old, but still valid, post: “APIS BY EXAMPLE: MOVE AND RENAME OBJECT API, AND IBM CL COMMAND REUSE”.
RPG-FAQ-024: Which programs use my service program?
If I want to know which programs are using my service program I can use IBM i services … and, in particular, the QSYS2.BOUND_SRVPGM_INFO … with an SQL statement like this:
SELECT *
FROM QSYS2.BOUND_SRVPGM_INFO
WHERE BOUND_SERVICE_PROGRAM = 'MYSRVPGM';
Another alternative … for slightly older OS versions we can get there via DSPPGMREF:
DSPPGMREF PGM (MYLIBPGM / * ALL) OUTPUT (* OUTFILE) OBJTYPE (* PGM) OUTFILE (MYLIB / MYPGMREF)
select * from MYLIB.MYPGMREF where whfnam = 'MYSRVPGM' and whotyp = '* SRVPGM';
RPG-FAQ-025: What procedures do my service programs export?
A simple command line way is DSPSRVPGM SRVPGM (MYSRVPGM), or by switching between DSPMOD MODULE (MYSRVPGM) DETAIL (* EXPORT) modules
… or using SQL and IBM i Service SQL:
SELECT *
FROM QSYS2.BOUND_SRVPGM_INFO
WHERE BOUND_SERVICE_PROGRAM = 'MYSRVPGM';
RPG-FAQ-026: Search for lines changed in different sources in a certain period
If with FNDSTRPDM we can search for a particular string in a source file, with Rational Rdi and its searches we can also work on multiple source files. Better still with the search functions of Isphere, the excellent Rational Rdi plugin.
However, we do not have the ability to search for all lines modified yesterday, or in a particular date range, on different members and source files.
It was talked about on a Midrange.com mailing list, where Sam Lennon proposed an excellent SQL Stored Procedure to do this type of search:
- First, create a table for the search summary
create table faq400.srcfnd
(plib char (10) not null default '',
pfile char (10) not null default '',
pmember char (10) not null default '',
vsrcseq char (10) not null default '',
vsrcdat char (10) not null default '',
vsrcdta char (100) not null default '');
- Then create a Stored Procedure
CREATE OR REPLACE FUNCTION faq400.src_line_date (
plib varchar (10),
pfile varchar (10),
pmember varchar (10),
pymdfrom numeric (6),
pymdto numeric (6)
)
RETURNS INTEGER
LANGUAGE SQL
MODIFIES SQL DATA
BEGIN
declare SQLCODE Integer Default 0;
declare insert_count integer default 0;
declare vsrcseq numeric (6.0);
declare vsrcdat numeric (6.0);
declare Vsrcdta varchar (256);
declare alias_name varchar (40);
declare skip_recd integer;
declare bad_data condition for SQLSTATE '22023'; --Invalid data
declare src_mbr CURSOR FOR
select * from qtemp.zz_src where srcdat between pymdfrom and pymdto;
declare continue handler for bad_data
set skip_recd = 1;
set alias_name = plib concat '. "' concat pfile
concAT '"("' concat pmember concat '")';
execute immediate 'create alias qtemp.zz_src FOR' concat alias_name;
OPEN src_mbr;
LblLoop:
loop
set skip_recd = 0;
fetch next from src_mbr into vsrcseq, vsrcdat, vsrcdta;
if SQLCODE = 100 then leave LblLoop;
end if;
if skip_recd = 1 then goto LblLoop;
end if;
insert into faq400.srcfnd
values (plib, pfile, pmember, vsrcseq, vsrcdat, vsrcdta);
set insert_count = insert_count + 1;
end Loop;
close src_mbr;
execute immediate 'drop alias qtemp.zz_src';
return insert_count;
end;
--- And now try to searh all memberes with some row modified yesterday (August 15 2020)
WITH MBRS AS
(SELECT SYSTEM_TABLE_SCHEMA AS LIB,
SYSTEM_TABLE_NAME AS FILE,
SYSTEM_TABLE_MEMBER AS MEMBER
FROM QSYS2.SYSPARTITIONSTAT
WHERE TABLE_SCHEMA = 'FAQ400' AND SOURCE_TYPE IS NOT NULL
AND SOURCE_TYPE NOT IN ('PF', 'LF')
--EVFTEMPF is IBM file and contains bad SRCDAT data
AND SYSTEM_TABLE_NAME not like ('EVFTEMPF%')
- fetch first 5 rows only
)
SELECT lib, file, member,
faq400.src_line_date (
lib, file, member, 200815, 200815) Found_Lines
FROM MBRS
ORDER BY Found_Lines desc, lib, file, member;
- And list all rows modified
select * from faq400.srcfnd where vsrcdat = 200815
order by plib, pfile, pmember, vsrcseq;
RPG-FAQ-027: Precision in intermediate results in RPG operations (Precision of intermediate results and Precision Rules for Numeric Operations)
When we add, subtract, multiply and divide numerical variables in RPG calculations we obtain results with a number of digits and decimals depending, of course, on the size of the variables in play according to the rules shown in this page of the IBM manual:
https://www.ibm.com/support/knowledgecenter/ssw_ibm_i_73/rzasd/prectdf.htm#prectdf
This means that the precision of the intermediate results could affect the final result of the operations themselves, their size, length and number of decimals.
Let’s try with a simple RPG program to perform operations on numeric variables and to show the result according to those rules reported in the IBM guide above: in the example shown I try to perform operations on two variables V1 / V2 packed (7: 0) … and then on two variables V3 / V4 with decimal values … let’s see how the lengths (and the number of decimals) of the result (intermediate) change, a temporary variable created by the RPG compiler.
************************************************** **********
* ESE106 Precision rules of numeric operations
*
************************************************** **********
ctl-opt option (* srcstmt: * nodebugio);
dcl-s v1 packed (7: 0);
dcl-s v2 packed (7: 0);
dcl-s v3 packed (5: 2);
dcl-s v4 packed (3: 1);
dcl-s string char (20);
dcl-s inp int (5);
// without decimals
v1 = 100;
v2 = 50;
string = '------ 123456789012345';
dsply string;
string = 'V1 =' +% editc (v1: '4');
dsply string;
string = 'V2 =' +% editc (v2: '4');
dsply string;
string = 'V1 + V2 =' +% editc (v1 + v2: '4');
dsply string;
string = 'V1-V2 =' +% editc (v1-v2: '4');
dsply string;
string = 'V1 * V2 =' +% editc (v1 * v2: '4');
dsply string;
string = 'V1 / V2 =' +% editc (v1 / v2: '4');
dsply string;
// with decimals
v3 = 123.45;
v4 = 6.7;
string = 'v3 =' +% editc (v3: '4');
dsply string;
string = 'v4 =' +% editc (v4: '4');
dsply string;
string = 'v3 + v4 =' +% editc (v3 + v4: '4');
dsply string;
string = 'v3-v4 =' +% editc (v3-v4: '4');
dsply string;
string = 'v3 * v4 =' +% editc (v3 * v4: '4');
dsply string;
string = 'v3 / v4 =' +% editc (v3 / v4: '4');
dsply string;
string = '';
dsply string '* EXT' inp;
exsr end;
// ---------------------------
// end
// ---------------------------
begsr end;
* inlr = * on;
return;
endsr;
And let’s see its result:
As can be seen in the example with the variables V3 (packed (5: 2)) and V4 (packed (3: 1)) … the result changes in length and decimal number in an important way, especially in case of multiplication and division!
--- Roberto De Pedrini Faq400.com