35
© ComCon and System i Developer, LLC 2006-2011 -1-

RPG Tricks and Techniques Summit - gomitec.com Presentations/Paul Tuohy RPG_Tric… · Record Locking Dynamic memory ... all sequence numbers in the source files must contain valid

Embed Size (px)

Citation preview

Page 1: RPG Tricks and Techniques Summit - gomitec.com Presentations/Paul Tuohy RPG_Tric… · Record Locking Dynamic memory ... all sequence numbers in the source files must contain valid

Paul TuohyComCon System i Developer5, Oakton Court, BallybrackCo. DublinIreland

Phone: +353 1 282 6230e-Mail: [email protected]: www.systemideveloper.com www.ComConAdvisor.com

ComConProducers of the RPG & DB2 Summit

RPG Tricks and Techniques

ComConPaul Tuohy

Paul Tuohy has worked in the development of IBM Midrange applications since the �70s. He has been IT manager for Kodak Ireland Ltd. and Technical Director of Precision Software Ltd. and is currently CEO of ComCon, a midrange consultancy company based in Dublin, Ireland. He has been teaching and lecturing since the mid-�80s.

Paul is the author of "Re-engineering RPG Legacy Applications�, �The Programmers Guide to iSeries Navigator� and the self teach course �iSeries Navigator for Programmers�. He writes regular articles for many publications and is one of the quoted industry experts in the IBM Redbook "Who knew you could do that with RPG IV?".

Paul is one of the co-founders of System i Developer and is also an award winning speaker who speaks regularly at US Common and other conferences throughout the world.

© ComCon and System i Developer, LLC 2006-2011 -1-

Page 2: RPG Tricks and Techniques Summit - gomitec.com Presentations/Paul Tuohy RPG_Tric… · Record Locking Dynamic memory ... all sequence numbers in the source files must contain valid

ComConDisclaimer

This presentation may contain small code examples that are furnished as simple examples to provide an illustration. These examples have not been thoroughly tested under all conditions. We therefore, cannot guarantee or imply reliability, serviceability, or function of these programs.

All code examples contained herein are provided to you "as is". THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY DISCLAIMED.

Have you visited SystemiDeveloper.com?

Check it out for the latest and greatest in System i RPG and database education

ComConAgenda

Basic Guidelines

H Spec

Compiler Directives

Managing Prototypes

Integers

Varying Length Fields

Indicators

Sorting Arrays

A couple of BIFs

Keys in Free Form

MOVE/MOVEL/MOVEA in free form

Static

Use offsets when told to

Record Locking

Dynamic memory

Constraints

© ComCon and System i Developer, LLC 2006-2011 -2-

Page 3: RPG Tricks and Techniques Summit - gomitec.com Presentations/Paul Tuohy RPG_Tric… · Record Locking Dynamic memory ... all sequence numbers in the source files must contain valid

ComCon

CL0N01Factor1+++++++Opcode(E)+Factor2+++++++Result++++++++Len++D+..

C HRS IFLE 40

C HRS MULT(H) RATE PAY 7 2

C ELSE

C RATE MULT 40 REGPAY 7 2

C HRS SUB 40 HRSOT 3 0

C RATE MULT 1.5 OTRATE 3 2

C HRSOT MULT(H) OTRATE OTPAY 7 2

C REGPAY ADD OTPAY PAY 7 2

C ENDIF

CL0N01Factor1+++++++Opcode(E)+Factor2+++++++Result++++++++Len++D+..

C HRS IFLE 40

C HRS MULT(H) RATE PAY 7 2

C ELSE

C RATE MULT 40 REGPAY 7 2

C HRS SUB 40 HRSOT 3 0

C RATE MULT 1.5 OTRATE 3 2

C HRSOT MULT(H) OTRATE OTPAY 7 2

C REGPAY ADD OTPAY PAY 7 2

C ENDIF

CL0N01Factor1++++++Opcode(E)Extended-factor2+++++++++++++++++++++

C if (hours <= 40)

C eval(H) weeklyPay = rate * hours

C else

C eval(H) weeklyPay = ((hours-40) * (rate*1.5))

C + (rate * 40)

C endIf

CL0N01Factor1++++++Opcode(E)Extended-factor2+++++++++++++++++++++

C if (hours <= 40)

C eval(H) weeklyPay = rate * hours

C else

C eval(H) weeklyPay = ((hours-40) * (rate*1.5))

C + (rate * 40)

C endIf

if (hours <= 40);

eval(H) weeklyPay = rate * hours;

else;

eval(H) weeklyPay = ((hours - 40) * (rate * 1.5)) + (rate * 40);

endIf;

if (hours <= 40);

eval(H) weeklyPay = rate * hours;

else;

eval(H) weeklyPay = ((hours - 40) * (rate * 1.5)) + (rate * 40);

endIf;

The Multiple Faces of RPG

ComConBasic Guidelines

Be Free

Code EVERYTHING in free form- Even if you are adding two lines of code to an old RPG II style program

Mind your language

Use proper names- customerName not custNam not cusNo not wkCsNo

- messageType not msgType

Speak the same language as the rest of the world- Table instead of Physical File- Index instead of Logical File

Learn and adopt from other languages- /INCLUDE instead of /COPY

- Constants in capitals

Use the modern tool set (WDSC/RDi)

The tools will help with everything new

Embrace what is new

Learn new habits

© ComCon and System i Developer, LLC 2006-2011 -3-

Page 4: RPG Tricks and Techniques Summit - gomitec.com Presentations/Paul Tuohy RPG_Tric… · Record Locking Dynamic memory ... all sequence numbers in the source files must contain valid

ComConH Spec

The H Spec has a new lease of life in RPG IV

Defaults for program

Debugging

Compiler options

The Compiler will search for the H spec in the following order:

An H spec included in your source

A data area named RPGLEHSPEC in *LIBL

A data area named DFTLEHSPEC in QRPGLE

The search stops when the first of these is found

Use a copy member for any standard H-specs, not a Data Area

© ComCon and System i Developer, LLC 2006-2011 -4-

Page 5: RPG Tricks and Techniques Summit - gomitec.com Presentations/Paul Tuohy RPG_Tric… · Record Locking Dynamic memory ... all sequence numbers in the source files must contain valid

ComCon

H option(*srcStmt :*noDebugIO)

H datFmt(*ISO) timFmt(*ISO) datEdit(*YMD-)

H decPrec(63)

H copyRight('This is mine, all mine!')

H option(*srcStmt :*noDebugIO)

H datFmt(*ISO) timFmt(*ISO) datEdit(*YMD-)

H decPrec(63)

H copyRight('This is mine, all mine!')

Compiler Options on H-spec

Many options for CRTBNDRPG and CRTRPGMOD can now be specified on the H spec as keywords.

The compile options specified will override the ones specified on the CRTxxxxxx command.

Unsupported keywords:

DBGVIEW, OUTPUT, REPLACE, DEFINE, PGM, SRCFILE, SRCMBR, TGTRLS

Use Option(*SrcStmt:*NoDebugIO)

Match program statement numbers with source line numbers !!

Easier for debugging and end user support

Skip the individual field steps for Input and Output specs

Faster step function during debugging

On the H spec, separate multiple options with a colon (:) � e.g OPTIONS( *SRCSTMT : *NODEBUGIO)

With *SRCSTMT specified, the statement number reported when an error occurs during run time will correspond directly to the SEU sequence number. Without this support, the statement number reported did not correlate directly to the source statement numbers. Therefore, support of end user problems was much more difficult. Many support desks kept compiler listings of all programs just to be able to match the program statement numbers to SEU statement numbers.

*NOSRCSTMT indicates that line numbers are assigned sequentially.

If *SRCSTMT is specified, statement numbers for the listing are generated from the source ID and SEU sequence numbers as follows: stmt_num = source_ID * 1000000 + source_SEU_sequence_number

For example, the main source member has a source ID of 0. If the first line in the source file has sequence number 000100, then the statement number for this specification would be 100. A line from a /COPY file member with source ID 27 and source sequence number 000100 would have statement number 27000100.

Note: When OPTION(*SRCSTMT) is specified, all sequence numbers in the source files must contain valid numeric values. If there are duplicate sequence numbers in the same source file, the behavior of the debugger may be unpredictable and statement numbers for diagnostic messages or cross reference entries may not be meaningful.

If *DEBUGIO is specified, breakpoints are generated for all input and output specifications. *NODEBUGIO indicates that no breakpoints are to be generated for these specifications. This means that during debug sessions, doing a Step function on an IO statement required many steps (one for each field in the format).

© ComCon and System i Developer, LLC 2006-2011 -5-

Page 6: RPG Tricks and Techniques Summit - gomitec.com Presentations/Paul Tuohy RPG_Tric… · Record Locking Dynamic memory ... all sequence numbers in the source files must contain valid

ComConCompiler Directives

New compiler directives

Control which statements in your source are to be used

Set (/DEFINE) and clear (/UNDEFINE) a condition-name

Condition names may also be set on the CRTBNDRPG and CRTRPGMOD commands

Include source based on the status of a condition-name

/IF {NOT} DEFINED(condition-name)

/ELSEIF {NOT} DEFINED(condition-name)

/ENDIF

Skip to the end of the current source

/EOF

Many uses

including maintaining a common source for different versions of an application

Copy Members may be nested

ComConConditional Compilation (1 of 2)

A technique to allow multiple spec types in a single copy member

Uses nested copies- SPSSRON contains free form calculations- FORMATS and PROTOTYPES contain D specs

- STDHSPEC contains H specs

/if defined(DOCSPEC)

/include QCPYSRC,SPSSRON

/elseIf defined(DODSPEC)

/include QCPYSRC,FORMATS

/include QCPYSRC,PROTOTYPES

/if defined(CGIPGM)

/include QCPYSRC,PROTOTYPEB

/include QCPYSRC,USEC

/endIf

/unDefine DODSPEC

/define DOCSPEC

/else

/include STDHSPEC

/define DODSPEC

/endIf

/if defined(DOCSPEC)

/include QCPYSRC,SPSSRON

/elseIf defined(DODSPEC)

/include QCPYSRC,FORMATS

/include QCPYSRC,PROTOTYPES

/if defined(CGIPGM)

/include QCPYSRC,PROTOTYPEB

/include QCPYSRC,USEC

/endIf

/unDefine DODSPEC

/define DOCSPEC

/else

/include STDHSPEC

/define DODSPEC

/endIf

If the member STANDARDcontains this

If the member STANDARDcontains this

© ComCon and System i Developer, LLC 2006-2011 -6-

Page 7: RPG Tricks and Techniques Summit - gomitec.com Presentations/Paul Tuohy RPG_Tric… · Record Locking Dynamic memory ... all sequence numbers in the source files must contain valid

ComConConditional Compilation (2 of 2)

The main program includes multiple copy directives for the same copy member

STANDARD � on the previous slide

/include QCPYSRC,STANDARD

< Only the H spec code will be copied here >

F

F

/include QCPYSRC,STANDARD

< Only the D spec code will be copied here >

D

D

/free

(Lots of most excellent Code)

/include QCPYSRC,STANDARD

< and only the C spec code will be copied here >

/include QCPYSRC,STANDARD

< Only the H spec code will be copied here >

F

F

/include QCPYSRC,STANDARD

< Only the D spec code will be copied here >

D

D

/free

(Lots of most excellent Code)

/include QCPYSRC,STANDARD

< and only the C spec code will be copied here >

Then the copy member can be used like this

Then the copy member can be used like this

© ComCon and System i Developer, LLC 2006-2011 -7-

Page 8: RPG Tricks and Techniques Summit - gomitec.com Presentations/Paul Tuohy RPG_Tric… · Record Locking Dynamic memory ... all sequence numbers in the source files must contain valid

ComConPredefined Definition Names

Compiler has a few predefined definition names

*CRTBNDRPG � if compiling using the CRTBNDRPG command

*CRTRPGMOD � if compiling using the CRTRPGMOD command

*ILERPG � if compiling using the ILE RPG IV compiler- as opposed to the Visualage for RPG compiler

*VxRxMx � the target release version (or later) of OS/400- Starting with a target of V4R4M0

/if defined(*CRTBNDRPG)

H dftActGrp(*NO) actGrp('MYSYS') /endIf

H bndDir('MYBNDDIR') H option(*srcStmt :*noDebugIO) H datFmt(*ISO) timFmt(*ISO) datEdit(*YMD-)

H decPrec(63)

H copyRight('This is mine, all mine!')

/if defined(*CRTBNDRPG)

H dftActGrp(*NO) actGrp('MYSYS') /endIf

H bndDir('MYBNDDIR') H option(*srcStmt :*noDebugIO) H datFmt(*ISO) timFmt(*ISO) datEdit(*YMD-)

H decPrec(63)

H copyRight('This is mine, all mine!')

When you use the Binding Directory support, the details of your directory are included in the module in the same way as the system run-time binding directories that the compiler supplies. You can see the binding directories that will be used by displaying the module (DSPMOD) on the page with Reference System Objects detail.

© ComCon and System i Developer, LLC 2006-2011 -8-

Page 9: RPG Tricks and Techniques Summit - gomitec.com Presentations/Paul Tuohy RPG_Tric… · Record Locking Dynamic memory ... all sequence numbers in the source files must contain valid

ComConManaging Prototypes

SRC-PF FILEPROCSSRC-PF FILEPROCS

D getProductData PR

D setProductData PR

// etc.

D getProductData PR

D setProductData PR

// etc.

D getSalesData PR

D setSalesData PR

// etc.

D getSalesData PR

D setSalesData PR

// etc.

/include FILEPROCS,PFILE01A/include FILEPROCS,PFILE02A/include FILEPROCS,PFILE03A

/include FILEPROCS,PFILE01A/include FILEPROCS,PFILE02A/include FILEPROCS,PFILE03A

PFILE01A

PFILE02A

PFILEPROCS

SRC-PF UTILITYSRC-PF UTILITY

D getMessage PR

D sendMessage PR

// etc.

D getMessage PR

D sendMessage PR

// etc.

/include UTILITY,PUTIL01A/include UTILITY,PUTIL02A

/include UTILITY,PUTIL01A/include UTILITY,PUTIL02A

PUTIL01A

PUTILITY

SRC-PF QPROTOSRCSRC-PF QPROTOSRC

D myProgram001A PRD myProgram001A PR

/include QPROTOSRC,PGM001A/include QPROTOSRC,PGM002A/include QPROTOSRC,PGM003A

/include QPROTOSRC,PGM001A/include QPROTOSRC,PGM002A/include QPROTOSRC,PGM003A

PGM001A

PPGMPROTO

D myProgram002A PRD myProgram002A PR

PGM002A

D myProgram003A PRD myProgram003A PR

PGM003A

SRC-PF QINCLUDESSRC-PF QINCLUDES

/include FILEPROCS,PFILEPROCS/include UTILITY,PUTILITY/include QPROTOSRC,PPGMPROTO

/include FILEPROCS,PFILEPROCS/include UTILITY,PUTILITY/include QPROTOSRC,PPGMPROTO

BASEINFO

Bearing in mind that an application may end up having thousands of prototypes, just one source member containing all prototypes for the application will very quickly become unmanageable. The important point is that all programs should only have one copy directive to include all prototypes. The ability to have nested copy directives makes the maintenance of prototypes very straight forward.

That copy member included in every source contains multiple copy directives to include other members containing prototype definitions. These other members would be:-

� A copy member containing prototypes for all dynamic calls� A copy member per service program containing prototypes for all procedures

exported from the service program

� Or, for very large service programs, have a copy member per module in the service program. Then have a copy member for the service program that contains copy directives for all the module prototype members.

You want to achieve the best of both worlds in that only one copy directive is required to have all prototypes included but the actual prototypes themselves are stored in multiple easy to manage members.

But why stop with just prototypes? Why not include copy members containing standard definitions of data structures and named constants? All of these can be included with just one copy directive.

© ComCon and System i Developer, LLC 2006-2011 -9-

Page 10: RPG Tricks and Techniques Summit - gomitec.com Presentations/Paul Tuohy RPG_Tric… · Record Locking Dynamic memory ... all sequence numbers in the source files must contain valid

ComConUse Integers

Integers have a data type of I or U

I = Signed

U = Unsigned

Replaces the old B (Binary) data type

Has a broader range then binary (for the same storage)

No conversion to/from packed (as with binary)

Use for compatibility with other languages

C, Java and MI (and CL in V5R3)

Used extensively with C functions and APIs

Better performance for math operations

Faster then packed (or zoned or binary)- But would you notice the difference?

Use 10I 0 as your default work field size

If you do not need decimals/fractions

For numeric parameters

ComConInteger Details

RPG Bytes Range API C and Java

3 i 0 1 -128 to 127 BIN1 char

5 i 0 2 -32,768 to 32,767 BIN2 short

10 i 0 4 -2,147,483,648 to 2,147,483,647 BIN4 long or int

20 i 0 8 -9,223,372,036,854,775,808 to9,223,372,036,854,775,807

BIN8 long

3 u 0 1 0 to 255 UBIN1 Unsigned char

5 u 0 2 0 to 65535 UBIN2 short

10 u 0 4 0 to 4,294,967,295 UBIN4 long

20 u 0 8 0 to 18,446,744,073,709,551,615 UBIN8 long long

Integers are identified in different ways in different places

RPG

API documentation

C and Java- The same

© ComCon and System i Developer, LLC 2006-2011 -10-

Page 11: RPG Tricks and Techniques Summit - gomitec.com Presentations/Paul Tuohy RPG_Tric… · Record Locking Dynamic memory ... all sequence numbers in the source files must contain valid

ComConVarying Length Fields

D firstName s 10a inz('Paul')

D lastName s 10a inz('Tuohy')

D v_firstName s 10a varying inz('Paul')

D v_lastName s 10a varying inz('Tuohy')

D fullName s 21a varying

fullName = %trimR(firstName) + ' ' + %trimR(lastName);

fullName = v_firstName + ' ' + v_lastName;

fullName = '';

%len(fullName) = 0;

fullName = *blanks; // fullName now has 21 spaces

D firstName s 10a inz('Paul')

D lastName s 10a inz('Tuohy')

D v_firstName s 10a varying inz('Paul')

D v_lastName s 10a varying inz('Tuohy')

D fullName s 21a varying

fullName = %trimR(firstName) + ' ' + %trimR(lastName);

fullName = v_firstName + ' ' + v_lastName;

fullName = '';

%len(fullName) = 0;

fullName = *blanks; // fullName now has 21 spaces

Excellent for string handling

Reduces requirement for %TRIMx functions

The length is set when data is moved into it

The %LEN function can also be used to reset the length

Be careful using *blanks

ComConCAT Functionality with Varying Fields

The fixed format CAT operation allowed you to specify trailing blanks

F1 field is right trimmed and trailing blanks added

Trailing blanks specified after F2- As a literal or constant

- As a numeric field

Define varying field for padding

Length should be maximum length required

Initialize to all blanks

Set length of padding field to required trailing blanks and concatenate

D firstName s 10a inz('Paul')

D lastName s 10a inz('Tuohy')

D padding s 500a varying inz(*blanks)

%len(padding) = 10;

fullName = %trimR(firstName) + padding + %trimR(lastName);

D firstName s 10a inz('Paul')

D lastName s 10a inz('Tuohy')

D padding s 500a varying inz(*blanks)

%len(padding) = 10;

fullName = %trimR(firstName) + padding + %trimR(lastName);

D firstName s 10a inz('Paul')

D lastName s 10a inz('Tuohy')

C firstName Cat lastName:10 fullName

D firstName s 10a inz('Paul')

D lastName s 10a inz('Tuohy')

C firstName Cat lastName:10 fullName

© ComCon and System i Developer, LLC 2006-2011 -11-

Page 12: RPG Tricks and Techniques Summit - gomitec.com Presentations/Paul Tuohy RPG_Tric… · Record Locking Dynamic memory ... all sequence numbers in the source files must contain valid

ComConA quick word about Varying Length fields

Varying length fields have two components

The current length of the field - a two byte binary (5i 0) or a four byte binary (10i 0)

� Depending on the defined length of the field

The actual data

A varying length field requires two (or four) more bytes of storage then the defined length

Be careful when using varying length fields in data structures

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

00 16 T H I S T H E D A T A

D DS

D webOut 2048a varying

D webDataLen 5i 0 overlay(webOut)

D webData 2048a overlay(webOut:*next)

webOut = 'THIS IS THE DATA';

D DS

D webOut 2048a varying

D webDataLen 5i 0 overlay(webOut)

D webData 2048a overlay(webOut:*next)

webOut = 'THIS IS THE DATA';

© ComCon and System i Developer, LLC 2006-2011 -12-

Page 13: RPG Tricks and Techniques Summit - gomitec.com Presentations/Paul Tuohy RPG_Tric… · Record Locking Dynamic memory ... all sequence numbers in the source files must contain valid

ComConUse Named Indicators

Use real names for your display and print file indicators !

Fdisplay CF E workstn indDS(dsp)

D dsp DS qualified

D F3_Exit n overLay(dsp:3)

D F12_Cancel n overLay(dsp:12)

D resetErrorIndicators...

D 3a overLay(dsp:31)

D error n overLay(dsp:31)

D startDateError...

D n overLay(dsp:32)

D endDateError...

D n overLay(dsp:33)

dsp.resetErrorIndicators = *zeros;

if (startDate < today);

dsp.startDateError = *on ;

endIf;

dsp.endDateError = (endDate < startDate);

dsp.error = dsp.startDateError or dsp.endDateError;

exFmt testRec;

if (dsp.F3Exit or dsp.F12_Cancel);

Fdisplay CF E workstn indDS(dsp)

D dsp DS qualified

D F3_Exit n overLay(dsp:3)

D F12_Cancel n overLay(dsp:12)

D resetErrorIndicators...

D 3a overLay(dsp:31)

D error n overLay(dsp:31)

D startDateError...

D n overLay(dsp:32)

D endDateError...

D n overLay(dsp:33)

dsp.resetErrorIndicators = *zeros;

if (startDate < today);

dsp.startDateError = *on ;

endIf;

dsp.endDateError = (endDate < startDate);

dsp.error = dsp.startDateError or dsp.endDateError;

exFmt testRec;

if (dsp.F3Exit or dsp.F12_Cancel);

The display/printer file MUST specify the INDARA keyword

The display/printer file MUST specify the INDARA keyword

Logical Expressions!Logical Expressions!

This example illustrates the use of the INDDS (indicator Data Structure).

Note the definition of the DSPIND data structure. When this is specified on the F spec, the programmer MUST use the indicators in the data structure and NOT the numbered indicators to control this file. For example, in this program, if the programmer turned on indicator *IN31 directly in the program logic, it would have zero impact on the display file. The program logic must refer to the indicator as "Error" to turn it on or off in order for it to have an impact on the display file.

The keyword INDARA must be specified for the display/printer file. This does not normally have any impact on the RPG program. It specifies that indicators should "travel" in their own area, separate from the normal data stream. As a result any CLEAR or RESET operations applied to a record format will not affect indicators in an INDARA file, but would have done so without this keyword. If you use CLEAR and RESET you may need to add aditional code to handel the indicators.

This is the DDS for the display file used in the RPG example:

INDARA

R TESTREC CF03(03) CF12(12)

5 11'Start Date: . .'

STARTDATE L 5 27DATFMT(*USA)

32 DSPATR(RI PC)

32 5 39'Date cannot be earlier than today'

7 11'End Date: . . .'

ENDDATE L 7 27DATFMT(*USA)

33 DSPATR(RI PC)

33 7 39'Date must be later than Start Date'

31 10 21'Please correct above error'

© ComCon and System i Developer, LLC 2006-2011 -13-

Page 14: RPG Tricks and Techniques Summit - gomitec.com Presentations/Paul Tuohy RPG_Tric… · Record Locking Dynamic memory ... all sequence numbers in the source files must contain valid

ComConAn Alternative to Named Indicators

Not in a position to switch your old programs to use INDDS ?

Try using this technique to make your code more obvious

Since indicators can be referenced through the *IN(nn) array- Why not use named constants to identify their purpose

- Like so:

// Indicator Constants

D F3_EXIT C 3

D F12_CANCEL C 12

D ERROR C 31

D START_DATE_ERROR...

D C 32

D END_DATE_ERROR...

D C 33

if (startDate < today);

*In(START_DATE_ERROR) = *On;

endIf;

*in(END_DATE_ERROR) = (endDate < startDate);

*in(ERROR) = *in(START_DATE_ERROR) or *in(END_DATE_ERROR);

// Indicator Constants

D F3_EXIT C 3

D F12_CANCEL C 12

D ERROR C 31

D START_DATE_ERROR...

D C 32

D END_DATE_ERROR...

D C 33

if (startDate < today);

*In(START_DATE_ERROR) = *On;

endIf;

*in(END_DATE_ERROR) = (endDate < startDate);

*in(ERROR) = *in(START_DATE_ERROR) or *in(END_DATE_ERROR);

If you cannot make use of the INDDS, I strongly urge you to try this technique instead. Since all constant references are resolved at compile time, the resulting code is every bit as fast as the old-fashioned *IN04 type references. The nice thing is you can gradually introduce it to existing programs as you are working on them and since the resulting executable code is identical, there is no additional test overhead.

You will be amazed at how much more readable your programs become. And more readable = more maintainable = more productive = more time to do interesting stuff !!

Isn't the intent of the code above a lot more obvious than if we had coded:

C If StartDate < Today

C Eval *In32 = *On

C EndIf

C Eval *In33 = EndDate < StartDate

C Eval *In31 = *In32 or *In33

© ComCon and System i Developer, LLC 2006-2011 -14-

Page 15: RPG Tricks and Techniques Summit - gomitec.com Presentations/Paul Tuohy RPG_Tric… · Record Locking Dynamic memory ... all sequence numbers in the source files must contain valid

ComConBased Data Structures (to V5R4)

A Based DS can be used to define �standard� data

A based DS does not take up memory

Great for API formats

Great for prototypes

Leads to the use of qualified data structures

D baseAddress DS based(baseptr) qualified

D street1 30

D street2 30

D city 20 varying

D state 2

D zip 5

D zipPlus 4

D invoiceInfo DS qualified

D mailAddr likeDS(baseAddress)

D shipAddr likeDS(baseAddress)

D baseAddress DS based(baseptr) qualified

D street1 30

D street2 30

D city 20 varying

D state 2

D zip 5

D zipPlus 4

D invoiceInfo DS qualified

D mailAddr likeDS(baseAddress)

D shipAddr likeDS(baseAddress)

ComConBased Data Structures � Better in V6R1

A Based DS can be defined using TEMPLATE keyword

TEMPLATE allows subfields to be initialized

Cannont �accidently� reference subfields in template

D baseAddress DS template qualified

D street1 30

D street2 30

D city 20 varying

D state 2 inz('TX')

D zip 5

D zipPlus 4

D invoiceInfo DS qualified

D mailAddr likeDS(BaseAddress) inz(*likeDS)

D shipAddr likeDS(BaseAddress) inz(*likeDS)

D baseAddress DS template qualified

D street1 30

D street2 30

D city 20 varying

D state 2 inz('TX')

D zip 5

D zipPlus 4

D invoiceInfo DS qualified

D mailAddr likeDS(BaseAddress) inz(*likeDS)

D shipAddr likeDS(BaseAddress) inz(*likeDS)

© ComCon and System i Developer, LLC 2006-2011 -15-

Page 16: RPG Tricks and Techniques Summit - gomitec.com Presentations/Paul Tuohy RPG_Tric… · Record Locking Dynamic memory ... all sequence numbers in the source files must contain valid

ComConReferencing Based Structures

Where to store based structured

In same member as prototypes- If structure relates to a program or procedure

In �standard� copy members

Should be included in ALL programs/procedures

Same as prototypes

/include QINCLUDES,BASEINFO

D invoiceInfo DS qualified

D mailAddr likeDS(baseAddress) inz(*likeDS)

D shipAddr likeDS(baseAddress) inz(*likeDS)

/include QINCLUDES,BASEINFO

D invoiceInfo DS qualified

D mailAddr likeDS(baseAddress) inz(*likeDS)

D shipAddr likeDS(baseAddress) inz(*likeDS)

ComConOverlaying a DS & Unnamed Fields

Originally, the Overlay keyword only applied to subfields

Now, you can overlay the DS itself

Makes a great alternative to using compile-time data

Initialize the data near the array definition itself

No need to chase to the end of the source member

Note that the DS subfields do not need to be named

But can still have INZ values!

D compileData DS

D 27a inz('January February March ')

D 27a inz('April May June ')

D 27a inz('July August September')

D 27a inz('October November December ')

D monthNames 9a overlay(compileData) dim(12)

D compileData DS

D 27a inz('January February March ')

D 27a inz('April May June ')

D 27a inz('July August September')

D 27a inz('October November December ')

D monthNames 9a overlay(compileData) dim(12)

© ComCon and System i Developer, LLC 2006-2011 -16-

Page 17: RPG Tricks and Techniques Summit - gomitec.com Presentations/Paul Tuohy RPG_Tric… · Record Locking Dynamic memory ... all sequence numbers in the source files must contain valid

ComConUsing SORTA with Group Fields

Want to sort an array with different keys?

Group fields can provide an answer

Be sure to specify the DIM at the group field level

Then the array can be sorted on any of the subfields

D addressInfo DS

// Note that Dim is specified at the group field level

D addressData 52a dim(1000)

D streetA 30a overlay(addressData)

D cityA 20a overlay(addressData: *next)

D stateA 2a overlay(addressData: *next)

/free

sortA %subArr(cityA:1:noLoaded); // Sort into City sequence

sortA %subArr(stateA:1:noLoaded); // Sort in State sequence

D addressInfo DS

// Note that Dim is specified at the group field level

D addressData 52a dim(1000)

D streetA 30a overlay(addressData)

D cityA 20a overlay(addressData: *next)

D stateA 2a overlay(addressData: *next)

/free

sortA %subArr(cityA:1:noLoaded); // Sort into City sequence

sortA %subArr(stateA:1:noLoaded); // Sort in State sequence

Note that when using this technique all of the other fields in the array (i.e. those that are part of the group) will be "pulled along" with their associated values.

ASCEND or DESCEND can be specified as normal along with the DIM keyword. So, while you can sort on any of the fields in the group, you can only sort ascending OR descending sequence on any given array.

The %SUBARR BIF was introduced in V5R3. In this example NoLoaded contains the number of elements loaded to the array.

© ComCon and System i Developer, LLC 2006-2011 -17-

Page 18: RPG Tricks and Techniques Summit - gomitec.com Presentations/Paul Tuohy RPG_Tric… · Record Locking Dynamic memory ... all sequence numbers in the source files must contain valid

ComConWhy Use Large Arrays to Store Data?

Load Once � Reference Often

Subfiles

Load the data to an array

Load the subfile from the array

Users can select �sort by� columns

An example in a moment

Tables

Instead of constantly chaining to tables

Load the table to an array

Use %Lookup instead of CHAIN

Not a good option for �volatile� data

© ComCon and System i Developer, LLC 2006-2011 -18-

Page 19: RPG Tricks and Techniques Summit - gomitec.com Presentations/Paul Tuohy RPG_Tric… · Record Locking Dynamic memory ... all sequence numbers in the source files must contain valid

ComConUse an Array to Store Subfile Data

Subfiles

Load the data (usually from databases) to an array- Each element is a subfile record and each subfield is an �array mapping�

Load the subfile from the array

Users can select �sort by� columns- Program uses SORTA and reloads the subfile.

d subRecData E Ds extName(SUBSORTD:SUBREC)

d Ds

d allSubRec like(subRecData)

d dim(9999) ascend

d arrOption like(option) overLay(allSubRec)

d arrCode like(code) overLay(allSubRec:*next)

d arrName like(name) overLay(allSubRec:*next)

d subRecData E Ds extName(SUBSORTD:SUBREC)

d Ds

d allSubRec like(subRecData)

d dim(9999) ascend

d arrOption like(option) overLay(allSubRec)

d arrCode like(code) overLay(allSubRec:*next)

d arrName like(name) overLay(allSubRec:*next)

A REF(*LIBL/CUST)

A R SUBREC SFL

A OPTION 1A B 7 3

A 31 DSPATR(RI PC)

A CODE R O 7 11

A NAME R O 7 22

A REF(*LIBL/CUST)

A R SUBREC SFL

A OPTION 1A B 7 3

A 31 DSPATR(RI PC)

A CODE R O 7 11

A NAME R O 7 22

This is the relevant DDS for the display file:-

A R SUBREC SFL

A 54 SFLNXTCHG

A OPTION 1A B 7 3

A 31 DSPATR(RI PC)

A CODE R O 7 11

A NAME R O 7 22

A R SUBCTL SFLCTL(SUBREC)

A OVERLAY

A 51 SFLDSP

A 52 SFLDSPCTL

A 53 SFLCLR

A 51 SFLEND(*MORE)

A SFLSIZ(0050)

A SFLPAG(0014)

A RTNCSRLOC(*RECNAME &CURSORREC &CURSORFLD)

A SFLCSRRRN(&ATRECORD)

A POSITIONAT 4S 0H SFLRCDNBR

A RRN 4S 0H

A ATRECORD 5S 0H

A CSRREC 10 H

A CSRFLD 10 H

© ComCon and System i Developer, LLC 2006-2011 -19-

Page 20: RPG Tricks and Techniques Summit - gomitec.com Presentations/Paul Tuohy RPG_Tric… · Record Locking Dynamic memory ... all sequence numbers in the source files must contain valid

ComConSorting a Subfile Array

d subRecData E Ds extName(SUBSORTD:SUBREC)

d Ds

d allSubRec like(subRecData)

d dim(9999) ascend

d arrOption like(option) overLay(allSubRec)

d arrCode like(code) overLay(allSubRec:*next)

d arrName like(name) overLay(allSubRec:*next)

d subRecData E Ds extName(SUBSORTD:SUBREC)

d Ds

d allSubRec like(subRecData)

d dim(9999) ascend

d arrOption like(option) overLay(allSubRec)

d arrCode like(code) overLay(allSubRec:*next)

d arrName like(name) overLay(allSubRec:*next)

select;

when cursorFld = 'CCODE';

sortA %subArr(arrCode:1:recordsInSubfile);

when cursorFld = 'ANAME';

sortA %subArr(arrName:1:recordsInSubfile);

endSl;

select;

when cursorFld = 'CCODE';

sortA %subArr(arrCode:1:recordsInSubfile);

when cursorFld = 'ANAME';

sortA %subArr(arrName:1:recordsInSubfile);

endSl;

Sort relevant array based on RTNCSRLOC field

Sort relevant array based on RTNCSRLOC field

numRows = 0;

read dataBase;

dow not %EOF(dataBase);

numRows += 1;

allSubRec(numRows) = subRecData;

read dataBase;

endDo;

numRows = 0;

read dataBase;

dow not %EOF(dataBase);

numRows += 1;

allSubRec(numRows) = subRecData;

read dataBase;

endDo;

for RRN = 1 to numRows;

subRecData = allSubRec(RRN);

write subRec;

endFor;

for RRN = 1 to numRows;

subRecData = allSubRec(RRN);

write subRec;

endFor;

Load data to arrayLoad subfile from array

Load data to arrayLoad subfile from array

ComConCode the norm...

Program the exception

monitor;

value = value/rate;

on-error;

value = 0;

endMon;

monitor;

value = value/rate;

on-error;

value = 0;

endMon;

if (rate <> 0);

value = value/rate;

else;

value = 0;

endIf;

if (rate <> 0);

value = value/rate;

else;

value = 0;

endIf;

Old RPG habits die hardOld RPG habits die hard

© ComCon and System i Developer, LLC 2006-2011 -20-

Page 21: RPG Tricks and Techniques Summit - gomitec.com Presentations/Paul Tuohy RPG_Tric… · Record Locking Dynamic memory ... all sequence numbers in the source files must contain valid

ComConFOR Operation

Particularly useful with arrays

Make use of that %ELEM BIF

A FOR Loop can

Increment

Decrement

Have whatever incremental step you want

D price S 3p 2 dim(5)

D qty S 3p 0 dim(5)

D total S 11p 0

for i = 1 to %elem(price);

total = total + ( price(i) * qty(i) );

endFor;

D price S 3p 2 dim(5)

D qty S 3p 0 dim(5)

D total S 11p 0

for i = 1 to %elem(price);

total = total + ( price(i) * qty(i) );

endFor;

FOR <index-name> { = <starting-value> }

{ BY <increment-value> }

{ TO | DOWNTO <limit-value> }

{ loop body }

ENDFOR | END

FOR <index-name> { = <starting-value> }

{ BY <increment-value> }

{ TO | DOWNTO <limit-value> }

{ loop body }

ENDFOR | END

The FOR operation begins a group of operations and indicates the number of times the group will be processed. To indicate the number of times the group of operations is to be processed, specify an index name, a starting value, an increment value, and a limit value. The optional starting, increment, and limit values can be a free-form expressions. An associated END or ENDFOR statement marks the end of the group.

The index name must be the name of a scalar, numeric variable with zero decimal positions. It cannot be an indexed array. The starting-value, increment-value, and limit-value can be numeric values or expressions with zero decimal positions. The increment value, if specified, cannot be zero.

The BY and TO (or DOWNTO) clauses can be specified in either order. Both "BY 2 TO 10" and "TO 10 BY 2" are allowed.

Remember the following when specifying the FOR operation:

The index name cannot be declared on the FOR operation. Use the D spec to define it. � An indexed array element is not allowed as the index field in a FOR operation. � If no starting value is specified, the index name retains the same value it had before

the start of the loop. � If no limit value is specified, the loop repeats indefinitely until it encounters a

statement that exits the loop (such as a LEAVE or GOTO) or that ends the program or procedure (such as a RETURN).

� The LEAVE Operation works in FOR loops just as it does in DO groups.

© ComCon and System i Developer, LLC 2006-2011 -21-

Page 22: RPG Tricks and Techniques Summit - gomitec.com Presentations/Paul Tuohy RPG_Tric… · Record Locking Dynamic memory ... all sequence numbers in the source files must contain valid

ComConEditing BIFs

Have the same effect as using an edit code or word on output

BUT - you can work with the resulting field !

%EDITC ( numexpr : editcode { : option } )

Returns the edited value of the numeric expression- option can be *ASTFILL, *CURSYM, or a character literal representing a currency symbol

%EDITW ( numexpr : editword )

The edit word is applied to the value of the numeric expression.

%EDITC and %EDITW are invaluable with data for the web

D text S 50a

D balance S 9p 2

text = 'Your balance of ' + %trim(%editC(balance:'A':*curSym))

+ ' is now seriously overdue';

D text S 50a

D balance S 9p 2

text = 'Your balance of ' + %trim(%editC(balance:'A':*curSym))

+ ' is now seriously overdue';

ComCon"Mail Merge" with %REPLACE

%REPLACE makes string handling easier & more flexible.

When used in conjunction with other built-in functions, such as %SCAN and %EDITC, this is particularly powerful

For example - code similar to the following could be used to substitute the marker &S with the content of the variable Salutation in a mail-merge type of operation

Although it is one of the �trickier� BIFs, %REPLACE is well worth the effort to master

D message S 100a varying

D salutation S 50a varying

D baseText C 'Dear &S, thank you for your +

D recent order'

read customer;

if not %EOF(customer);

Salutation = %trimR(title) + ' ' + %trimR(firstName);

message = %replace( %trimR(salutation): baseText

: %scan('&S': baseText): 2 );

// Other process

read customer;

endIf;

D message S 100a varying

D salutation S 50a varying

D baseText C 'Dear &S, thank you for your +

D recent order'

read customer;

if not %EOF(customer);

Salutation = %trimR(title) + ' ' + %trimR(firstName);

message = %replace( %trimR(salutation): baseText

: %scan('&S': baseText): 2 );

// Other process

read customer;

endIf;

© ComCon and System i Developer, LLC 2006-2011 -22-

Page 23: RPG Tricks and Techniques Summit - gomitec.com Presentations/Paul Tuohy RPG_Tric… · Record Locking Dynamic memory ... all sequence numbers in the source files must contain valid

ComConKeys in Free Form

Keys are easier to use in Free Form

FSalesHist IF E K Disk

D salesData Ds likeRec(salesHistR)

chain (custCode: prodCode: 'AB' + Input) salesHist salesData;

FSalesHist IF E K Disk

D salesData Ds likeRec(salesHistR)

chain (custCode: prodCode: 'AB' + Input) salesHist salesData;

FSalesHist IF E K Disk

D salesKey E Ds extname(salesHist:*Key)

D qualified

D salesData Ds likeRec(salesHistR)

chain %KDS(salesKey) salesHist salesData;

FSalesHist IF E K Disk

D salesKey E Ds extname(salesHist:*Key)

D qualified

D salesData Ds likeRec(salesHistR)

chain %KDS(salesKey) salesHist salesData;

FSalesHist IF E K Disk

D salesData Ds likeRec(salesHistR)

C keyList KList

C KFld custCode

C KFld prodCode

C KFld invoice

C keyList chain salesHist salesData

FSalesHist IF E K Disk

D salesData Ds likeRec(salesHistR)

C keyList KList

C KFld custCode

C KFld prodCode

C KFld invoice

C keyList chain salesHist salesData

Regardless of whether you use fixed form or free form, file operation codes may now specify a data structure to be used with the operation. In these examples the record is placed in the SalesData data structure: the data structure must be defined using the LIKEREC keyword and it is implicitly qualified.

The first example shows the traditional fixed form method of defining a key list to be used with a file where the key consists of a number of fields.

The second example shows how a key list may be emulated in the D specs. An externally defined data structure is defined using the optional parameter of *KEY on the EXTNAME key word to indicate that only key fields from the external file are to be included in the data structure. This data structure is then used as an argument for the %KDS BIF on the CHAIN operation. This is slightly better than a key list in that the %KDS keyword makes it clear that a data structure is being used to define the key.

But the third example is the preferred way to do it. Simply provide the list of key fields to be used. You can even use a literal and/or an expression as one of the key fields!

© ComCon and System i Developer, LLC 2006-2011 -23-

Page 24: RPG Tricks and Techniques Summit - gomitec.com Presentations/Paul Tuohy RPG_Tric… · Record Locking Dynamic memory ... all sequence numbers in the source files must contain valid

ComConThe �problem� with MOVE/MOVEL

There is a problem with MOVE/MOVEL?

You must know what the fields are

You must know the definition of the fields

(thanks to Chris Bucko for asking me to cover this)

C MOVE '3' BARTYP 3

C MOVEL BARTYP LASTYP 3 0

C MOVE 1 LASTYP

C MOVE '1' *INLR

C MOVE '3' BARTYP 3

C MOVEL BARTYP LASTYP 3 0

C MOVE 1 LASTYP

C MOVE '1' *INLR

C MOVE '3' BARTYP

C MOVEL BARTYP LASTYP

C MOVE 1 LASTYP

C MOVE '1' *INLR

C MOVE '3' BARTYP

C MOVEL BARTYP LASTYP

C MOVE 1 LASTYP

C MOVE '1' *INLR

ComConMove in Free Form Guidelines

Character to character:-

EVAL is the same as MOVEL with padding

EVALR is the same as MOVE with padding

Use %SUBSTR if different lengths and no padding- %substr(char10: 1: 6) = char6; // MOVEL

- %substr(char10: 5: 6) = char6; // MOVE

Number to number

Use EVAL

Watch out for numeric overflow- Use MONITOR if in doubt

Number to character

Use %CHAR, %EDITC, %EDITW and, if necessary, %SUBSTR- char10 = %char(number);

- %substr(char10: 1: 6) = %char(number)

Character to number

Use %INT, %DEC, %DECH

© ComCon and System i Developer, LLC 2006-2011 -24-

Page 25: RPG Tricks and Techniques Summit - gomitec.com Presentations/Paul Tuohy RPG_Tric… · Record Locking Dynamic memory ... all sequence numbers in the source files must contain valid

ComConMOVE in Free Form

Self Explanatory

Don�t need to know field definitions

D charField 3

D numberField 3 0 inz(5)

D integerField 5i 0

D dateField d

D charDate 6 inz('021408')

D numberDate 6 0

charField = %char(numberField); // charField = '5 '

charField = %editc(numberField:'3'); // charField = ' 5'

charField = %editc(numberField:'X'); // charField = '005'

integerField = %int(charField); // integerField = 5

numberField = %dec(charField: 3: 0); // numberField = 5

dateField = %date(charDate:*MDY0)

charDate = %char(%date():*ISO0);

numberDate = %dec(%date():*ISO);

D charField 3

D numberField 3 0 inz(5)

D integerField 5i 0

D dateField d

D charDate 6 inz('021408')

D numberDate 6 0

charField = %char(numberField); // charField = '5 '

charField = %editc(numberField:'3'); // charField = ' 5'

charField = %editc(numberField:'X'); // charField = '005'

integerField = %int(charField); // integerField = 5

numberField = %dec(charField: 3: 0); // numberField = 5

dateField = %date(charDate:*MDY0)

charDate = %char(%date():*ISO0);

numberDate = %dec(%date():*ISO);

ComConWhat About MOVEA?

Why was MOVEA array being used?

To emulate string handling- Use string BIFs instead

Multi dimensional arrays- Use data structure arrays

Storing copies of data- Use %subArr() BIF

But some MOVEA operations are more complex

Make use of data structures- Overlay arrays onto character fields

Write a subprocedure- Which would also make it easier to understand

© ComCon and System i Developer, LLC 2006-2011 -25-

Page 26: RPG Tricks and Techniques Summit - gomitec.com Presentations/Paul Tuohy RPG_Tric… · Record Locking Dynamic memory ... all sequence numbers in the source files must contain valid

ComConStatic

The oft ignored STATIC keyword

Used in subprocedure D specs

A subprocedures memory is reallocated on every call

Except for data defined with the STATIC keyword

STATIC mean that fields...

That are specific to the subprocedure

And whose state needs to be maintained between calls

Do NOT have to be defined globally

P putSource2...

P B

d PI

d nextSeq s 10i 0 static

/free

nextSeq += 1;

s2.srcSeq = nextSeq;

write source2 s2;

return;

/end-Free

P E

P putSource2...

P B

d PI

d nextSeq s 10i 0 static

/free

nextSeq += 1;

s2.srcSeq = nextSeq;

write source2 s2;

return;

/end-Free

P E

© ComCon and System i Developer, LLC 2006-2011 -26-

Page 27: RPG Tricks and Techniques Summit - gomitec.com Presentations/Paul Tuohy RPG_Tric… · Record Locking Dynamic memory ... all sequence numbers in the source files must contain valid

ComConAlways Use Offsets!

Many IBM supplied programs have special �buffers�

APIs

Triggers

Usually use Data Structures in RPG

If documentation mentions an offset � USE IT!

Do NOT hard code positions

Bad things can happen

An example

Prior to V5R1 many programmers hard coded from and to positions for before/after image fields in the trigger buffer

In V5R1, IBM changed the rules for the buffer- Each image now starts on a 16 byte boundary

The �lucky� ones got a decimal data error

Although IBM have been warning programmers for years to use the offset values to map trigger buffers, this was not the easiest thing to do in RPG - particularly in RPG/400.

In V5R1, and later releases, failure to heed this warning can burn you badly as the before and after image buffers are not necessarily contiguous. There may be up to 15 bytes of "padding" between the two in order to ensure that the beginning of the buffer starts on a 16 byte boundary.

© ComCon and System i Developer, LLC 2006-2011 -27-

Page 28: RPG Tricks and Techniques Summit - gomitec.com Presentations/Paul Tuohy RPG_Tric… · Record Locking Dynamic memory ... all sequence numbers in the source files must contain valid

ComConMapping the Before and After Images

A very practical use of pointers

Use externally described DS(s) to map record images to field names

Use pointers to base the DS and map it to the trigger buffer

Much simpler and safer than laying out the whole buffer

And changes in V5 render many such hard coded solutions obsolete

In fact they only stand a 1 in 16 chance of working !!

D oldFile E DS extName(fileName) qualified

D based(oldPtr)

D newFile E DS extName(fileName) qualified

D based(newPtr)

D oldPtr S *

D newPtr S *

oldPtr = %addr(triggerBuffer) + triggerBuffer.oldOffset ;

newPtr = %addr(triggerBuffer) + triggerBuffer.newOffset ;

D oldFile E DS extName(fileName) qualified

D based(oldPtr)

D newFile E DS extName(fileName) qualified

D based(newPtr)

D oldPtr S *

D newPtr S *

oldPtr = %addr(triggerBuffer) + triggerBuffer.oldOffset ;

newPtr = %addr(triggerBuffer) + triggerBuffer.newOffset ;

We haven't got the time to teach you about Triggers, but those of you familiar with them may find this example of mapping the Before and After buffers useful

Through the use of pointers and based externally described data structures, as illustrated in this example, you can make the programming to map the trigger buffer record images to record format field names a breeze! Note that the data structures are qualified to allow for differentiation between field names in the before and after images.

If your records include null capable fields, you can map the null flag array in a similar fashion.

© ComCon and System i Developer, LLC 2006-2011 -28-

Page 29: RPG Tricks and Techniques Summit - gomitec.com Presentations/Paul Tuohy RPG_Tric… · Record Locking Dynamic memory ... all sequence numbers in the source files must contain valid

ComConRecord Locking

How do you handle record locking in Interactive programs?

Just lock the records without error recovery - A wealth of possible problems. Batch jobs crashing etc.

Lock the records with error recovery - A better solution but do all programs have error recovery?

- Bottom line with error recovery: You ain�t getting the record

Do not lock the records and hope for the best - If you track the fields with have been changed by the current user and output only those

fields you MIGHT be ok. How do you track those changes?

- Your user will not be aware of concurrent changes- If you do not track changes you may overwrite data changed by another user with older

screen information.

Do not lock the records and program for concurrent maintenance- Best solution yet!

© ComCon and System i Developer, LLC 2006-2011 -29-

Page 30: RPG Tricks and Techniques Summit - gomitec.com Presentations/Paul Tuohy RPG_Tric… · Record Locking Dynamic memory ... all sequence numbers in the source files must contain valid

ComConLogic for Record Locking

This technique allows for

Same field names on display file and database file

Concurrent maintenance

FmyFile UF A E K Disk

FmyScreen CF E WorkStn

D IOData E Ds extName(myFile)

D recordImage Ds likeRec(myFileR) dim(2)

chain(N) key myFile;

recordImage(1) = IOData;

exFmt screen;

chain key myFile recordImage(2);

if recordImage(1) = recordImage(2); // Or compare individual fields

update myFileR;

else;

unLock myFile; // And do any error handling

endIf;

FmyFile UF A E K Disk

FmyScreen CF E WorkStn

D IOData E Ds extName(myFile)

D recordImage Ds likeRec(myFileR) dim(2)

chain(N) key myFile;

recordImage(1) = IOData;

exFmt screen;

chain key myFile recordImage(2);

if recordImage(1) = recordImage(2); // Or compare individual fields

update myFileR;

else;

unLock myFile; // And do any error handling

endIf;

Same field namesSame field names

The basic logic is as follows:

� chain(N) key myFile; - the initial row is retrieved. It is automatically read into the IOData DS

� recordImage(1) = IOData; - store the original image for later comparison

� exFmt screen; display and read the screen. Data in IOData will be overwritten with data from the screen

� chain key myFile recordImage(2); get the record again. But this time lock the record and specify a target for the read so the data in IOData is not overwritten

� if recordImage(1) = recordImage(2); check that the row has not been changed since the program originally retrieved it. This can be a comple row comparison, individual columns etc.

� update myFileR; or unLock myFile; depending on the results of the comparison

© ComCon and System i Developer, LLC 2006-2011 -30-

Page 31: RPG Tricks and Techniques Summit - gomitec.com Presentations/Paul Tuohy RPG_Tric… · Record Locking Dynamic memory ... all sequence numbers in the source files must contain valid

ComConExample of Dynamic Memory Allocation

This example shows the use of dynamic memory allocation for an array that may have more then 32767 elements

The array is �repositioned� every 32767 elements

D dataArray S * dim(32767)

D based(pDataArray)

// Allocate memory for the array of pointers

pDataArray = %alloc((%size(pDataPtr) * numberInList));

// Build the array of pointers to the path entries

for i = 1 to numberInList;

j = j + 1;

if (j > %elem(dataArray));

pDataArray = pDataArray +

(%Size(dataArray) * %Elem(dataArray));

j = 1;

endIf;

dataArray(j) = pDataPtr;

D dataArray S * dim(32767)

D based(pDataArray)

// Allocate memory for the array of pointers

pDataArray = %alloc((%size(pDataPtr) * numberInList));

// Build the array of pointers to the path entries

for i = 1 to numberInList;

j = j + 1;

if (j > %elem(dataArray));

pDataArray = pDataArray +

(%Size(dataArray) * %Elem(dataArray));

j = 1;

endIf;

dataArray(j) = pDataPtr;

© ComCon and System i Developer, LLC 2006-2011 -31-

Page 32: RPG Tricks and Techniques Summit - gomitec.com Presentations/Paul Tuohy RPG_Tric… · Record Locking Dynamic memory ... all sequence numbers in the source files must contain valid

ComConOr Use a User Space

This example shows an array being mapped to a User Space

D getSpace PR extPgm('QUSPTRUS')

D spaceName 20 const

D pSpacePtr *

D errorCode const Options(*NOPASS)

D like(standardAPIError)

D dataArray S * dim(32767)

D based(pDataArray)

// Map the array to the User Space

getSpace(SpaceName:pDataArray);

// Build the array

for i = 1 to numberInList;

j = j + 1;

if (j > %Elem(dataArray));

pDataArray = pDataArray +

(%Size(Data) * %Elem(dataArray));

j = 1;

endIf;

dataArray(j) = data;

D getSpace PR extPgm('QUSPTRUS')

D spaceName 20 const

D pSpacePtr *

D errorCode const Options(*NOPASS)

D like(standardAPIError)

D dataArray S * dim(32767)

D based(pDataArray)

// Map the array to the User Space

getSpace(SpaceName:pDataArray);

// Build the array

for i = 1 to numberInList;

j = j + 1;

if (j > %Elem(dataArray));

pDataArray = pDataArray +

(%Size(Data) * %Elem(dataArray));

j = 1;

endIf;

dataArray(j) = data;

Of course the prototype for QUSPTRUS would be in a copy member.

The user space must exist (see the QUSCRTUS API) before calling QUSPTRUS.

© ComCon and System i Developer, LLC 2006-2011 -32-

Page 33: RPG Tricks and Techniques Summit - gomitec.com Presentations/Paul Tuohy RPG_Tric… · Record Locking Dynamic memory ... all sequence numbers in the source files must contain valid

ComCon

%alloc assigns 1,120,000 bytes

2. pDataArray= %alloc((%size(dataArray)*numberInList));

< 1 ------------------------------------------------------- 1,120,000 >

< 1 ---------------- 524,272 > < 524,273 ------- 1,048,544 >

dataArraydataArray

�Walking� a Large Array

1. numberInList = 70,000

%Size(dataArray) = 16

dataArraydataArray

pDataArray = address of allocated memory

3. Program loops through 32767 elements of dataArray

4. �Move� the position of the array pDataArray = pDataArray + (%Size(dataArray) * %elem(dataArray));

5. etc.

< 1,048,545 --------- 1,572,816 >

ComConUsing Large Arrays

But what about sorting and scanning large arrays?

Use the C functions qsort and bsearch as more powerful versions of SORTA and LOOKUP

Refer to the Red Book "Who Knew You Could Do That With RPG IV?" for details and examples.

D sortIt PR extProc('qsort')

D dataStart * value

D elements 10u 0 value

D size 10u 0 value

D function * procPtr value

D findIt PR * extProc('bsearch')

D lookFor * value

D dataStart * value

D elements 10u 0 value

D size 10u 0 value

D function * procPtr value

D sortIt PR extProc('qsort')

D dataStart * value

D elements 10u 0 value

D size 10u 0 value

D function * procPtr value

D findIt PR * extProc('bsearch')

D lookFor * value

D dataStart * value

D elements 10u 0 value

D size 10u 0 value

D function * procPtr value

© ComCon and System i Developer, LLC 2006-2011 -33-

Page 34: RPG Tricks and Techniques Summit - gomitec.com Presentations/Paul Tuohy RPG_Tric… · Record Locking Dynamic memory ... all sequence numbers in the source files must contain valid

ComConCheck Out Check Constraints

See the articles

"The Ins and Outs of Using Constraints" (http://search400.techtarget.com/tip/1,289483,sid3_gci1032813,00.html) and

"Name That Constraint" (http://search400.techtarget.com/tip/1,289483,sid3_gci1004244,00.html)

© ComCon and System i Developer, LLC 2006-2011 -34-

Page 35: RPG Tricks and Techniques Summit - gomitec.com Presentations/Paul Tuohy RPG_Tric… · Record Locking Dynamic memory ... all sequence numbers in the source files must contain valid

ComConSummary

There you have it!

I hope you find a few of the tips and techniques useful and applicable

ComConBy the Speaker

�Re-Engineering RPG Legacy Applications�

ISBN 1-58347-006-9

�The Programmers Guide to iSeries Navigator�

ISBN 1-58347-047-6

www.mcpressonline.com

www.midrange.com

www.amazon.com

etc.

iSeries Navigator for Programmers

A self teach course

www.lab400.com

Article links at

www.comconadvisor.com

© ComCon and System i Developer, LLC 2006-2011 -35-