KnowledgeBase 00094: Dictionary I-Type Records

Introduction

Developers migrating applications to QM from Pick style systems are frequently
confused by dictionary I-type records. Actually, these are very simple and
offer many features that are not possible with correlatives in A/S-type
records.

where elements enclosed in curly brackets are optional. Much of this is very
similar to an A or S-type record but in a different order. The expression
serves broadly the same purpose as a correlative but is much more powerful.

The I-Type Expression

The expression in an I-type dictionary is essentially the same as the right
hand side of a QMBasic assignment statement.
Unlike correlatives, I-type expressions are not limited to integer arithmetic.
With a very few exceptions, all QMBasic functions are available in I-types.
So, a simple expression to calculate the due date for an invoice as 30 days
after its issue might be

ISSUE.DATE + 30

In an I-type expression, names that in QMBasic would be variables relate to
other A/C/I/S type items in the same dictionary. Note that, unlike
correlatives, numeric constants are not quoted. If there is a need to refer to
a field by field number, the data can be extracted from @RECORD using the
normal field operators:

@RECORD<6> + 30

The above examples show why using D/A/S-type items to name all fields leads to
easier maintenance by making it clear to anyone reading the expression what
data it references.

Of course, I-type expressions may be much more complex than this simple
example.

A common use of I-type expressions is to fetch data from another
file using the TRANS() function. This is essentially the same in purpose as a
T conversion. For example:

TRANS(CUSTOMERS, CUST.ID, NAME, 'C')

to fetch the NAME field of the CUSTOMERS record for which the record id is in
the
CUST.ID field of the record being processed.
Unlike many other multivalue products that support I-type
records, QM allows the item being fetched (in this case NAME) to be a
further I-type or other calculated value in the remote file's dictionary.

There is an important action of the TRANS() function that developers need to
be aware of when retrieving multivalued data. Mark characters in the data
returned are demoted by one level (e.g. value marks become subvalue marks).
This is done because the TRANS() function can take a multivalued list of
record ids in its second argument and returns a composite result with the data
for each record appearing as a value in the returned data. There is a similar
RTRANS() function that does not demote mark characters.

Multivalued Data

In Pick style systems, a correlative expression used with multivalued data is
evaluated once for each value. An I-type, on the other hand, is evaluated
just once, processing then entire multivalued data in one operation. To support
this, the QMBasic language has many multivalued functions that operate on
each field, value or subvalue in turn, returning a similarly structured
result. There is a link below to a KnowledgeBase article that describes these.

Compound I-Types

Sometimes it is easier or more efficient to evaluate an I-type expression in
stages. This ability is provided by a "compound I-type" which is simply a
series of expressions separated by semicolons

The result of evaluating the first expression is stored in an internal
variable named @1, the second expression in @2, and so on. Any expression can
use the results of earlier expressions. Using just @ as a data name refers to
the value of the immediately preceding expression. The value of the whole
I-type expression is the value of the final expression.

The above expression, somewhat horrific at first sight, calculates someone's
age in whole years based on a date of birth in field DOB. There are four
expressions. The first two convert the current date and date of birth to
YYYYMMDD format. The third expression calculates the difference in years based
on the YYYY portion only. The final expression adjusts this value depending on
whether the person has had a birthday in the current year. Other commonly used
techniques such as dividing the date difference by 365.25 do not work for
people born on 29 February or when performing the calculation on that date.

Unlike many other multivalue products that support I-type records, QM allows
compound I-types to be nested to any depth.

Compiling I-Type Records

An I-type is just a QMBasic expression and, like the full QMBasic programming
language, it must be compiled before it can be used. The object code is stored
in fields 16 onwards of the dictionary record. The ED and SED editors hide this
object code as it is binary data which could cause unwanted effects when sent
to a terminal device.

The query processor will automatically compile an I-type expression if needed,
however, there is an important consideration when using nested I-types. For
example, if we have an I-type named PROFIT to calculate the profit we make
selling an item

SELLING.PRICE - COST.PRICE

and a second I-type named PCT.PROFIT to calculate this as as percentage of the
cost price

PROFIT / COST.PRICE * 100

the process of compiling the second I-type substitutes the PROFIT expression
such that the actual expression compiled is

(SELLING.PRICE - COST.PRICE) / COST.PRICE * 100

If we were to execute a query that used the PCT.PROFIT item, the query
processor would compile it if it has not been compiled since it was last
changed.

Now consider what happens if we had accidentally entered the PROFIT
expression as

COST.PRICE - SELLING.PRICE

We would notice the error when we run the report using PCT.PROFIT. We could
then correct the definition of PROFIT and repeat the query, however, we would
get the same incorrect result.

Why does this happen? Because nested I-types are resolved at compile time
rather than as a run time subroutine call, changing the PROFIT item does not
have any way to automatically invalidate or recompile the PCT.PROFIT item. We
need to force recompilation using the COMPILE.DICT (short form CD) command. As
a general rule, it is a good idea to compile a dictionary whenever a change is
made to an I-type that might be used by another expression.