Synon 2e Development Standards - (Hints and Tips RTVOBJ)

Synon 2E Development Standards - RTVOBJ


Update: 05/12/2025 - I have created a page for all standards related posts.

I thought I'd have a break from the 2e naming conventions thread that has occupied most of the last month and launch into a series of smaller posts that talk about some of the hints and tips associated with the 2e function types.

Today's topic is the RTVOBJ. One of the most useful function types in 2e. One which is often misused or understood.

The following are some hints and tips I have learnt, taught, embellished and/or stolen over the years.

RTVOBJ Tips

Naming. Most RTVOBJ functions can be divided into one of two types. Single record GETs to bring back attributes for a given key value. Multiple reads to perform some sort of process logic or test. Different naming conventions, 'GET By ...' or 'RTV Last four transactions' will identify these two types. See previous post re:naming standards.

Getting Records. A GET will be based upon an ACP with a unique key, usually the RTV and pass in a fully RST key value. The function will only return database attributes + the xxx Record Found Y/N flag (optional for your site). The return code will not typically be explicitly modified. If the record is not found then output parameters must be cleared. Parameters, both INPUT key fields and OUPUT attributes should be defined using the same access path (ACP) that the function is based upon. Avoid using the individual field definitions unless they are not scoped by an access path.

On certain files with fixed key values the RST key may be set to NEITHER and primed inside the RTVOBJ at initialisation. Examples here are system tables that have one record.

GET's will return ALL attributes including spare fields. (A future post will talk about the benefits or otherwise of using spare fields). If the standard *GET will do, then use it!!!. There may be other ‘Get xxxxxxxx’ functions currently in your models which may have a subset of the fields. You can replace with a single *GET if preferred.

Other types of RTVOBJs will perform various processing requirements over multiple records these could be prefixed as RTV.

General Hints. When testing for certain conditions you should *QUIT the RTVOBJ for performance reasons once the condition has been found thus minimising I/O and decreasing function runtime. There are exceptions here for *Arrays. (future post).

In general try to avoid changing the default PGM.*Return Code setting, although there may be circumstances where this is justified. If you do need to identify a specific condition other than normal the user CND.*User Quit Requested appears to be a standard I have seen at many companies.

If the RTVOBJ is built over a RSQ ACP and uses RST/POS key fields then explicitly define the key fields using a KEY structure. This will allow the developer to zoom into the key structure and understand what the key fields are.

Do not leave the fields as MAP. There is no relevance for a RTVOBJ and it only confuses new developers. CA please change this. I know it doesn't generate anything!!!!!!

Default RTVOBJ's i.e. *GET should have parameters passed as RCD. Get By and RTV's over alternative access paths should use the KEY/RCD method.

If using a RTVOBJ to retrieve reference data. It is best to not do the call if the value is optional. Saves an unnecessary I/O.

Gotcha's

Best Practice

There is a well-known bug (feature) in a 2E generated RTVOBJ in that USER:Exit Processing is executed if no records are found and there is no coding inside USER:Record Not Found. Therefore if any action diagram coding is added to USER:Exit Processing then you must also code something (comment or *QUIT) inside USER:Record Not Found. This ensures that the function always behaves in a predictable manner.

If the data passed in as RST or POS parameter is of a different domain and is alphabetic for a shorter length then unpredictable results may occur. This is because the generated code is failing to clear fields when building the RPG key list. – I could not replicate in the latest release.

Best Practice

If you change the ACP that the RTVOBJ is based upon then also remember to change parameter definition. It is best to keep these consistent for developer readability.

Version 7.0 of 2E allows RTVOBJ's to be written over physical files. This reads the file in relative record number order. This is quicker than using a logical. This technique is useful when you need to read an entire file. A fix program would be a good example.

I will cover CRTOBJ in the next blog posting.

Thanks for reading the
Synon 2E and Plex Emporium by Lee Dare (A developer blog). 
Lee.

Synon 2e Development Standards - (Further Naming Conventions)

Synon 2E Development Standards - Naming


Update: 05/12/2025 - I have created a page for all standards related posts.

So here we go. Another part in the CA 2E development standards series. Here I discuss/publish some naming conventions I have found useful with common functions in a 2E model.

I have worked at a few sites over the years that have varying levels of standards applied to the model and different approaches aiming to solve the same problem.

I have worked at sites that like to use an incremental prefix i.e. RTV56 etc, some that leave the defaults and others that follow naming convention prefixes. As with any standard it is not the actual standard that it is important. The area of concern for any development shop is the continued adherence of the standards.

What I have found works best for me is to use a common set of naming standards for the core functions in a model. Today we will discuss core functions and their naming conventions.

I will finish with a couple of examples of why I settled on my approach for 2E naming standards and as always I expect these to cause some debate in the community.

Default Create Object

I prefer that the default create object (CRTOBJ) on a file is named as '*CRT'. This function will have all the relevant processing that is appropriate to the most common use of the function. Some shops will have auditing and others use surrogate systems. The default will be named as above but other variations may be named 'CRT with Audit' or 'CRT (No Audit)'. However, if most of the time you only need to perform your sites default processing when creating a record, the '*CRT' is much easier for everyone to remember.

The important fact here in the *CRT means the default behaviour. The others are variations and are therefore not classed as default functions and will be named slightly differently. i.e. without the *. The CRT prefix should remain.

As only one function of any name can appear on any file, for *Arrays the standard is extended slightly to be '*CRT Array name' as all arrays share the same file. They simply use different definitions for the access path (sorry, array structure).

Default Change Object

Follows the same rules as the create object but will use *CHG and CHG prefixes as appropriate.

Default Delete Object

Follows the same rules as the create object but will use the *DLT and DLT prefixes as appropriate.

Also a *DLT (Cascade) can also be created. This will actually be based on a RTVOBJ and will delete all child records before deleting the parent key. Thus reducing orphaned records.

Default Retrieve Objects

As a retrieve object (RTVOBJ) can be used in many ways. I will refer to the two most common usages. The fetch of a record and the checking for a record's existence. Also remember that a RTVOBJ is also used for processing files. The next two default functions are based on a fully restricted key.

I have used the *GET naming convention for a full record being fetched. This function will be based on the default retrieval access path. Variations that bring back fewer fields or use an alternative access path will begin with GET. If using another access path use the standard of 'GET By Access Path Name'. If getting a subset of fields use the 'GET subset' - replace subset with a meaningful description. Once again I am highlighting the default usage of this function type.

I have worked at one site that prefers to bring back a Record Found Y/N flag rather than use the return code. This is site specific and I'll leave you to decide on the correct usage of the flag versus the *Return Code. See my other blog which provides further details.

Another usage of the RTVOBJ is for checking for the existence of a record. I have seen this implemented as either a *CHK or and *XST. Either are fine as long as you are consistent.

Again, note that for both of these functions types the array version will need to supply the array name as a qualifier.

There are a further two usages of the *GET and *CHK/*XST function type. This is only related to files that use the 'Qualified By' file relation to describe the key. You will know that the 'Qualified By' is used for file structures that may have a term or a date to identify a value in the key. i.e. A product price file might be qualfied by a field like 'Product Effect Date'. This saves you having to have a record for each day that a price is valid.

You will also know that this relation will automatically read backwards or forwards to find the correct record. Note this only works for the default retrieval access path. Therefore a *GET or the *CHK/*XST will generally find a record but your business logic is trying to determine if a record exists for the actual date. There are several ways to do this. You can either create an alternate RTV access path and write a 'GET By RTV 2' type function. This is perfectly reasonable.

However, and there always is one, I would suggest that a function named '*GET (Exact)' which is only used for files with a 'Qualified By' relationship will do additional processing to determine if the KEY is equal to DB1. This will help to identify to a developer when they are programming, that the file has a 'Qualified By' key but also negates the need for an unnecessary access path to be created.

This pretty much summarises my recommended standards for internal default functions. There are a couple of regularly used external function types that might also be useful and form part of my DBA recommendations of functions that should be created for every file.

The rule of thumb for creating default functions should be.

All Files - *GET, *CRT, *CHG, *DLT, *CHK/*XST and *GETEXT.
Files with a qualified By - *GET (Exact) and *CHK (Exact)/*XST (Exact)
Reference Files - '*SLT filename'
Parent/Child - *DLT (Cascade)

Default External Functions

The *SLT function is named this way as it will be used by the generator when building implicit field prompting code. The routine is also used to validate a record existence in relation checking. As such a default select must be first on the file and also never have its parameter interface changed unless the keys to the file changes.

The *GETEXT (An EXTEXFFUN with a parameter interfaces the same as the *GET) is often used for several reasons.

RPG Limits. Used to limit the number of subroutines or files in a compiled program, not a fully recommended reason. There are other specialist reasons why you would want to use this routine.
Cursor awareness. Wanting to re-read an access path whilst reading the same access path. , therefore not corrupting the cursor.
Externalisation or Program API. Externalising a RTVOBJ function for use within a non Synon environment i.e. CL Program.

If externalising based on another access path call the function 'GETEXT By access path'.

Best Practice

If creating these functions ask yourself this simple question. Am I creating this because of a file limit and if so, is this really the best section of code to externalise? Remember reducing a program from 51 files to 50 (so it compiles) only leaves a maintenance issue for the next developer, and it might be you. Afterall, there is nothing worse than seeing a string of externalised RTVOBJ functions.

Summary

I have generally preferred a prefix and/or suffix when naming functions. The prefix has been used to identify format of the function and the suffix as a sub group. This will become clearer as you see the examples build up on this blog.

You will notice that some are prefixed with an asterisk (*). This is usually reserved to indicate internal Synon objects in a model. I have chosen to indicate my core functions using this method because the * for default function types means that are all grouped them nicely on pick lists. This is done to ensure that they appear at the top of the list of functions. I am aware of other sites using numeric or other acceptable characters to attain the same result.

Note: If you choose to use this approach a known issue is that searching for a *CRT in the open functions screen doesn’t work. This is the only known limitation of this approach from a technical perspective. Other may well say that this encroaches on 2E objects. I fully agree that files should not start with * but the odd function that is core to your development model, no worries.

My next 2e naming conventions post will focus on ideas for other non-default functions.

Thanks for reading the
Synon 2E and Plex Emporium by Lee Dare (A developer blog). 
Lee.

2e Development Standards - (Extra Naming and General Standards Part 1)

Synon 2E Development Standards - Naming & Bonus Standards


Update: 05/12/2025 - I have created a page for all standards related posts.

Continuing with the series on 2E development standards.
A model-naming standard is very important, particularly for large models, to aid understanding and navigation. There are several types of naming standards to consider.

Model Names

A CA:2E data model has a 25-character object text names associated with files, access paths, fields, field conditions, functions and messages etc. The guidelines for each object type are detailed below. As a general rule:

Apart from files, try and only use 24 characters for the field, function, access path etc. This make it a lot easier to navigate around in the model and to use the ‘?’ prompting facility.

Capitalise all words except articles, prepositions, conjunctions, and the 'to' in infinitives. E.g. 'Currency of Invoice'

Avoid punctuation.

Try to establish a vocabulary of preferred names. Where abbreviations are necessary then they should conform to a common abbreviations list. It is recommended this list be generic across all of your development models and other languages and environments.

OS/400 Object Names

OS/400 object names and DDS names are required for actual implementation. CA:2E will assign names for all object types according to its own naming standard. Messages, DDS format names and fields will default to CA:2E assigned names.

If using a Multi-Version Multi-Model setup extra care needs to be taken to ensure that and new model objects are created equally if coding into multiple models i.e. Bug fixing a current and development release at the same time.

Queries can be written to keep tabs on objects and their names that do not cross reference satisfactorily. I.e. Model Name to Model Name as well as Implementation Name to Implementation Name. This forms part of a wider blog on model management techniques. Something for the future?

Some development shops have chosen to replace the 2e naming conventions for some objects in a development model. The Pro’s and Con’s of this approach are as follows:-

Pro's

Nicely named objects. Great for when calling programs from a menu or command line.

Encourages a standard form and reinforces the principle of the naming conventions.

Easy to detect if not renamed as 2e prefix is still associated to object.

Con's

After a while it is difficult to get a unique and meaningful object name, therefore, some names can seem ambiguous.

Extra work for a developer. Often forgotton.

Will generally require a mnemonic standard i.e. XXYYYYnnZ so may as well have the defaults.

Model Files

All new files and file changes should be managed by the Database Administrator (DBA) or a central owner for that model. This of course, depends on the size of your development shop but it good practice to separate the roles. This will allow good control of the model.

Implement a TLA?. I have implemented a Three-letter Acronym (TLA) following the name at some sites I have worked at. E.g. ‘My Sample File MSF’, The TLA will occupy the last 3 bytes of the available space for the file. Note this is the only model object type where I have deliberately use the last byte.

These 3 letters should precede all fields for that file.

File descriptions should represent a single instance of an entity, e.g. 'Customer' not 'Customers'. However, if the name describes many attributes of a single record then a plural may be ok, e.g. 'Account Parameters'.

Avoid using descriptive names like 'Details' or 'File'. E.g. 'Customer' rather than 'Customer Details File'. However, sometimes it may be necessary to differentiate between different levels of detail, e.g. 'Order Header' and 'Order Line'.

Short names will avoid truncation when assigned by CA:2E to default DBF functions or foreign key fields using 'For' text although the DBA processes of replacing or renaming of fields should negate the need for the developer to be to concerned.

Try to group Parent/Child file relationships by using common name prefixes.

Future blogs will expand on the area of model management and database administration guides.

Access Paths

Unlike files, access paths can be created by a developer with *PGMR access. It is recommended that you query the 2E model files and keep an eye on any new access paths that are created and their purpose. These should be reviewed from time to time to ensure that best practice has been considered when access path was created.

Do NOT create new access paths without thinking if there is an alternative e.g. Using one that is similar and/or doing selection within the functions. If in doubt, talk it through with your DBA or a senior developer.

Virtuals should, in general, not be used, and should NEVER be on standard access paths. There may be times when they are the best solution e.g. For resequencing a query access path rather than creating a work file.

The description should try and explain the purpose of the access path.

In general there should only be 1 UPD (Update) style access path per file.

In general access path relations should NOT be dropped. If this is used then the default access paths should be clean and an alternative RTV or RSQ created.

No Virtual Virtuals. Avoid multiple nested levels of virtual fields if you are going to use them.

It is highly recommended that default access paths should never contain Virtuals or have Select/Omit. Use an alternative RSQ or RTV if virtual fields are deemed necessary.

If creating an access path over an assimilated file from another model then care must be taken with the default naming of the access path.

Next time we will talk about functions and I will provide some recommendations for naming standards as well as some standards for default functions.

Thanks for reading the
Synon 2E and Plex Emporium by Lee Dare (A developer blog). 
Lee.

Synon 2E - Development Standards (That damn Return Code ;-))

Synon 2E Development Standards - Return Code


Update: 05/12/2025 - I have created a page for all standards related posts.

Yet another post debating some of the finer points of application development using CA 2E (Synon).

Over the years apart from justifying my use of 4GL's and model based development tools for rapid development of applications (CA 2E and CA Plex) to the non believers. Oh the arguments that this has caused have been blogged before. See my series on the 3GL v 4GL debate.

The biggest single point of contention from within the 2e community itself is relating to the correct usage and trust factor of the simply named field, "PGM.*Return Code". It may as well have been named Devil's spawn with the amount of hot air I have seen it generate.

For those that don't know, the return code is a floating variable within a program that indicates the current state of the program at the time it is queried? Roughly speaking

When programming a RTVOBJ as a full record fetch type function or as a check existence style function the developer generally has two options. They can either use the return code that is set implicitly by 2e (this can be overridden by the developer) or declare an explicit field like Record Found Y/N and pass back a value indicating success or otherwise depending if the record was found.

Below I indicate the Pro's and Con's as I see them.

PGM.*Return Code Method

Pro's

A return code is the default way to test for success or otherwise when calling a program.

Requires no additional developer intervention as nearly all the 2e function types automatically set the return code to the appropriate value.

Con's

A Return code is a global variable within the function and is only as accurate as the last line of code that set it. If additional code is added between the called program and the testing point then the original context of checking the return code is broken. Therefore developer beware.

Explicit Field Method

Pro's

Means you can save the value in a LCL field.

If other code is inserted into the action diagram. As long as the value is not overridden you can check this value later in the action diagram or pass the value into another function.

Con's

Adds extra work to default functions that previously required no extra coding. i.e. a record existence check.

If you have two or more calls the original call may need to be stored. If this is required then the flag can be set on by querying the return code anyhow.

This approach doesn’t work for execute messages or user programs which would generally use a return code so the practice doesn’t fit all scenarios.

But what do i think?

Personally. I always preach the KISS principle and to use the tools as they were designed.
Therefore, you will see me using the return code out of choice, but I would as always, follow any incumbent standards even if they are wrong but I would definitely have a go at explaining why they are wrong.......

Thanks for reading the
Synon 2E and Plex Emporium by Lee Dare (A developer blog). 
Lee.

Synon 2E - Development Standards (Ad-Hoc Tips)

Synon 2E Development Standards - Adhoc Tips


Update: 05/12/2025 - I have created a page for all standards related posts.

One of my new colleagues has been reading these posts whilst he is learning to program in 2E.

This part is a collection of some general tips in 2E around Format Relations, Function Options, Function Wrapping and Sharing Subroutines.

As always, any comments good and bad are welcomed as are requests for subject matter.

Format Relations Considerations

For certain function types relations can be dropped (PMTRCD and PRTFIL). This is useful to make your functions as efficient as possible.

For other functions you will generally have the option to influence the amount of default code and functionality that will be generated by the 2E code generators by detailing the level of referential integrity you wish the function to have.

Careful consideration should be given to setting the format relations to the desired level. These are MANDATORY, OPTIONAL, USER, NO ERROR and of course, DROPPED. I fully recommend the best practice of RTFM.

Function Options

Most function options are self explanatory and most of us the industry standard which don't deviate too far away from the model defaults. Some function options in particular are important to understand how they are used.

Close down program when set to ‘Y’ will close the program. If this program is likely to me called repeatedly in an iteration or a data loop (i.e. RTVOBJ) then consideration should be given to setting this to ‘N’

Reclaim resources is generally set for functions that are on menu’s.

Share Subroutine. See chapter on setting this value.

Function Wrapping

Function wrapping is the process of converting snippets of action diagram logic into a standalone function. This can be either an EXCEXTFUN or an EXTINTFUN. By copying the action diagram code into the developers notepad you have the option to convert the code into the standalone function.

At this point 2e will create parameter interfaces for each of the function contexts that are used in the code snippet and reference these as duplicate parameters using the PR1 to PR9 special contexts.

Whilst this works perfectly well you will notice that the function parameter interface is quite unwieldy. If this is the case sometimes you might find it easier to build the function yourself. You choose?, flip a coin.

To minimise this, the developer must manually make alterations.

Review the fields in the WRK and LCL contexts and determine if they are local to the code snippet only or need to be passed into the new function. If local;-

Replace all action diagram logic referring to the PRx contect of the fields with WRK or LCL.
Remove unused fields from the parameter interface.
If it turns out thar all fields are not needed then remove the parameter line entry for the context.

Note the restrictions of the EXCEXTFUN and EXCINTFUN function types when wrapping code. This chapter will be coming soon.....

Share and share alike – Subroutines that is

Any internal database function can be generated as a shared or reusable subroutine. The system option (YSHRSBR) will be set to NO, therefore the individual function option must be used to identify a shared subroutine. Additionally an EXCINTFUN can be implemented as a subroutine rather than inline code and thus subsequently also shared.

The decision to make an internal function shared is subjective. In many cases there will be no particular advantage because the generated code is relatively small or because the number of times the function is called is small. Sharing a function will also in itself add extra lines of code for parameter passing.

You should only consider using shared subroutines where there is an obvious benefit in reducing the number of lines of generated code.

The answer to a large function with many lines of code may be to redesign the call structure rather than to just use the easy option of using shared subroutines. Note that a function called many times may not be the best one to make as shared. It may be a higher level function in the call structure, which will provide the most benefit.

A shared subroutine cannot cope with specific indicators. Therefore, a shared subroutine function should not send error messages to a screen. It will be unable to identify the correct screen field error indicator so that the field cannot be highlighted and the cursor cannot be positioned.

It is good programming style that any function, which has output parameters, should always perform a logic path that initialises or sets output fields. E.g. A RTVOBJ to GET attributes should initialise or set all output parameters whether a record is found or not. A shared subroutine will always cause the output fields to be set with some value but this is not the case if the subroutine is not shared. Thus if a subroutine is shared and contains output parameters that are not set they may contain unpredictable data.

An EXCINTFUN may be made into a subroutine but not necessarily shared. This enables a *QUIT to be added. The *Quit will jump to the end of the subroutine.

As a matter of best practice a model file's default CHGOBJ, CRTOBJ or DLTOBJ should remain unshared.

Next time the great PGM.*Return code debate.........

Thanks for reading the
Synon 2E and Plex Emporium by Lee Dare (A developer blog). 
Lee.