Contents

QA Build Developer's Guide

Introduction

QA Build, or QAB for short, is the DP4 environment for producing application programs quickly. The QA Build program editor consists of a series of menus and questions which determine how each part of your program is to work. Your responses are used to generate a program that is analysed as partially compiled with the QA Build compiler, and then executed, either with the standard QA Build Interpreter (PROGRUN), or a version of the Interpreter that you have extended by adding to it your own C functions called from QAB, or which contains C functions that call QAB programs.

If you are new to QAB and would like a brief overview of what it is and how to use it you should refer to the appropriate sections of the Introduction to DP4, which can be found here .

Contents List

  • What's New
  • Overview of QA Build
    • Components of a QA Build Program
      • Procedure Trees
      • Place Holders
    • Sharing Program Components
    • Components of QAB
    • Key Concepts
  • QA Build Program Setup
    • Using the Program Editor
      • Starting the Program Editor
      • Program Editor Command Tails
      • Program Editor Header Information
      • PROGMAKE Facilities Menu
      • Editing a QAB Program
        • Selecting a Program to Work On
        • Program Editing Functions
        • Program Summary
          • Screen Layout Map
        • Program Output Devices
        • Program Flow Diagram
        • Inserting Procedures
          • Conditional Entry to a Procedure
        • Deleting Procedures
        • Moving Procedures
        • Copying Procedures
        • Procedure Editing Menu
        • Procedure Body Editing Menu
        • Procedure Overview
        • Table Processing Menu
          • Main Table Processing
          • Generate Parent Checks
          • Related Table Processing
          • Additional Table Processing
          • Related or Additional Table Processing Dialog
        • Main Maps
          • Screen Design
          • Form Design
        • Field processing
          • Automatic Generation of Field Processing
          • Field Editing
          • Field Details
          • Totalling
          • Local Fields
          • Standard Variable Reference
    • Database Management
      • Database Opening Options
      • Reading Records
      • Searching Tables
      • Updating the Database
      • Adding, Amending and Deleting Records
      • Securing Changes
      • Commit Frequency
      • Change Verify
      • Record Locking
      • Batch Processing
      • Multi User Access
    • Maps
      • Mapsets and QAB
      • Language Support
      • Message Maps
      • Multiple Choice Maps
      • Window Access
      • Message Assignment Menu
      • Map Assignment menu
      • Map Editing
    • Menus and Function Keys
    • Running Other Programs & Special Functions
    • Printing
    • Conditions and Formulas
    • Function Definition
  • Design Decisions
    • Structure of Procedure Trees
    • Libraries
    • Overlays
    • Maps and Mapsets
    • Macro Formulas
  • Using QA Build with C
    • Calling QA Build Programs from C
    • Adding Custom Functions to PROGRUN
    • Combining Procedure Bodies and Functions
    • Testing a QA Build Program with C Exits
    • Changing the Maximum Program Size
  • Compiling a QA Build Program
  • Running a QA Build Program
  • Execution of a QA Build Program
  • The QA Build Utility Programs
    • QABUTIL
    • Transferring QA Build Updates
    • Renaming
    • Analysis
      • Identifying the Impact of Database Changes
      • Identifying Missing Maps
    • Hiding Programs
    • Deleting Programs
    • Locating Formulas and Identifiers
    • Debugging Programs
    • Automatic Program Generation
    • QA Build Program Extract Files


What's New in the QA Build Developer's Manual

This page will tell you about recent changes to this manual. The most recent changes are mentioned first.

New in 4.624

  • 2 May 2013 Some information about the previously undocumented G type field processing has been added to Field Details.
  • 25 Feb 2009 Two new functions LENGTH_OF and TRIMMED have been added to the QAB formula processor to make it easier to manipulate character variables.
  • 15 Aug 2008 The theoretical maximum program size has been increased to 1Gb or above (the upper limit will depend on the operating system). The standard progrun is now built with a 4Mb program size, and progcomp defaults to this size.

New in 4.623

  • 15 May 2008 New variables: added QERROR_TEXT1 and QERROR_TEXT2 to standard variable reference.
  • 16 Jul 2007 New variable added QEXIT_CODE to standard variable reference.
  • 11 Apr 2007 Extra information added to the Print Control Variables page.
  • 7 Jun 2005 Added QPR_DEVICE to standard variable reference. This variable allows you to control the printer to be used by QAB programmatically.

New in 4.621

  • 20 Oct 2004 Added QSHOW_DISABLED to standard variable reference. This variable allows you to improve the user interface of a QAB program by indicating a field is disabled.
  • 20 July 2004 Documentation of operator precedences corrected.
  • 7 May 2004 Documentation of READ_TAIL and READ_TOGGLE corrected. Added QPICK_FLAGS to standard variable reference.
  • 19 Aug 2003 New standard variable qsmart_position introduced. Set this variable TRUE for better handling of Windows access using a non unique secondary index.

New in 4.620

  • 14 Feb 2003 This manual has now been extensively revised, though the work is not yet complete. The portions of the manual dealing with editing QAB have been rearranged and largely rewritten.
  • 04 Feb 2003 Documentation for new "Missing maps" report added to QABUTIL
  • 23 Dec 2002 Formatting improved
  • 17 Oct 2002 What's New Page added


Components of a QA Build Program

A QA Build program consists of a program header (edited using the Summary and Output Devices screens) and a procedure tree that determines the procedures of which it is composed and the order in which they are executed. A procedure tree is represented in the program editor by a program flow diagram and consists of procedures and procedure bodies.

Procedures specify a procedure body and define subsidiary procedures to be executed, that is other procedures called by the current procedure. Each procedure may be called by any other procedure and hence re-used in many programs.

The Procedure Body is the basic building block for QA Build programs. A procedure body describes some action to be performed, using table processing (to read and write to the database), and maps interact with the user on screen (or print data to a file, printer or other device), and field processing to define the details of the procedure logic. A procedure body may do little more than update a single variable, or clear some part of the display, or on the other hand it may contain a significant amount of processing - it is possible to write static data maintenance programs using a single procedure body. Like procedures a procedure body can be used by more than one procedure and re-used in many programs.

Each program, procedure, and procedure body has a short description associated with it, to aid programmers when they select an existing procedure or procedure body to include in a procedure tree. It is also possible to create a more extensive documentation record in each case.

Another component of QAB, existing independently of programs, is Function Definitions. These allow you to define such things as extensions to QAB's formula evaluation facilties (either via C, or just as macros that encapsulate complex or frequently used formulas).

Maps

As with other DP4 programs all input and output, whether using the screen, or any other device is done via a map. Maps are stored independently of QAB programs. You group a set of related maps into mapsets. The maps in a mapset share a common mapname and are distinguished by a unique map number.

Maps are usually created using the Map Editor which will be started at appropriate points as you create your QA Build programs. Maps are introduced in the Introduction to DP4 here.


Procedure Trees

Every program has one root procedure which may have one or more subsidiary procedures linked to it. These subsidiary procedures, in turn, may have their own subsidiary procedures and so on, thus forming the procedure tree. The entire procedure tree for a program can be displayed as the Program Flow Diagram.

If you have only one procedure in your program, the Program Flow Diagram will contain only that procedure's name. If you add a second procedure to your program, you must set up a link to the existing procedure. Links are shown on the Program Flow Diagram as a connection between two procedures:

The precedence given to the execution of procedures is top to bottom, then left to right. In the following procedure tree, the procedures are executed in the order in which they are numbered:

The program flow diagram helps you navigate around your program by making the links between the procedures visible. For example in the diagram aboive it is easy to see that PROC4 belongs to PROC2 and that PROC7 belongs to PROC6.

Where a line branches below a Menu-type procedure, the program can follow one of several different paths, depending on the option selected by the user.

A Problem and its Solution

The procedure tree architecture has a consequence that can be very annoying. Imagine that in the example above with seven procedures PROC4 is a procedure that handles some piece of processing that may need to be different for different customers - for example it might handle a local sales tax that varied from state to state, or a staff discount facility that was customer specific. We might want to create two programs one with PROC4A and one with PROC4B but otherwise identical. However the only way we can easily do this is also to have a PROC1A, PROC1B and a PROC2A,PROC2B. If later on we need to insert another subsidiary procedure of PROC2 to the right of PROC4 we will need to do the work twice. It would be convenient not to have to worry about this type of situation.

One way to deal with this type of problem is to have an empty procedure in PROC4 and to include PROC4A and PROC4B as subsidiaries controlled by entry conditions that depend on some kind of configuration table. However this type of solution leads to an increase in the size and complexity of your programs and can become unwieldy. An alternative is to use Place Holders which allow you to build multiple variants of a procedure and then switch between them using a configuration program called PROGBIND. You can find out mode about Place Holders here.


Place Holders

The Program Editor provides limited support for the use of place holders. A place holder is an empty, non-executable procedure, distinguished by a letter P in the Program Flow Diagram. At compile time it may be substituted by any of a number of functional procedures, depending on requirements. These substitute procedures belong to variant libraries. The function of linking groups of procedures to place holders is called binding. Binding and detaching are controlled from the System Build menu.

The function of place holders is to permit the development of variations of a program in a controlled manner. One QA Build program may provide a variety of functions depending on the variant libraries selected. Or one source program may be used to produce compatible software for a diversity of till types.

For full support you need Itim Technology Solutions StoreView.


Sharing Program Components

QAB allows you to re-use many components of your program, potentially saving you a lot of time when it comes to modifying the system.

Procedures and Procedure Bodies

Procedures and procedure bodies may be shared between programs. Most of the time sharing procedures and procedure bodies will save you time because you when you want to make a change you will only need to make it once, and all the relevant rpograms will be updated. On the other hand, a change to a procedure to enhance one program may sometimes have an undesirable effect in another program which also uses it. If a procedure is shared, changes made anywhere in the tree structure below it will affect any other programs using that procedure.

When procedure bodies are shared, check the use of table processing, maps and local fields carefully. The precautions that you should take when sharing a procedure are the same as when moving procedures.

The recommended way of working with a shared procedure is to "tie" it to a library program. Changes to the procedure or the procedure body are normally made from within the library program; a warning message is issued if a user attempts to edit it from within any other program.

An alternative to sharing a procedure or procedure body is to copy it. The copy is no longer shared and so may be edited without having undesirable effects elsewhere. The disadvantage is that each copy must be maintained separately.

Formulas and Conditions

QAB allows you to re-use the same formula and condition several times within a procedure body. While this is often very useful you should probably not bother to share simple formulas (such as assigning 0 to initialise several fields in a row or something similar). Little will be gained because the QAB Compiler "folds" identical formulas and conditions in any case so your program will not be any bigger. One reason to avoid sharing formulas like this is that it might happen later on that one of the fields needs to be assigned to a different value. Careless editing of the formula night cause the other fields to be assigned the new value as well. You should probably confine sharing formulas and conditions when your business and program logic dictate that they are likely to remain the same in future.

There are various techniques you could use when editing formulas and conditions to avoid problems with sharing:

  • Add comments to your formulas to indicate that they are shared
  • Use the Search Facilities of QABUTIL (available from the Program Flow Diagram) to see where a particular formula is used. At the time of writing you cannot access this facility directly, you need to use one of the "Find in Formula" options and find the formula you are interested in that way, and then choose the Show Usage option that becomes available. (It is planned to add a show usage option to the Assignment menu itself in the near future.)
  • If you are nervous about the effect changing a particular formula or condition may have change your program to use a new formula or condition, and copy and edit the existing one rather than editing the existing one directly.

Sometimes the fact that you can only share formulas within a procedure may be annoying - you can use macros to effectively share complicated formulas across your system.

Maps

Maps (at least message maps) be used more than one in the same procedure or procedure body, and one map may be used by many procedures, so many of the same issues that arise with sharing formulas or procedure bodies can also arise with maps.

Once again, you can use the Search Facilities of QABUTIL to find all the places where a particular map is used.


Components of QAB

You will use the following QAB components when creating and running QAB programs:

  • PROGMAKE, the QAB Program Editor, is used to create and edit QAB programs.
  • PROGCOMP, the QAB Compiler, is used to analyse and check your program to prepare it for execution with PROGRUN or PROGRUND. It is also used to generate interface code for linking C functions and procedures into your own version of PROGRUN.
  • PROGRUN, the QAB Interpreter is used to execute QAB programs. Although QA programs are interpreted, in general they will perform as well as an equivalent C program, because only a tiny proportion of the execution time of a typical DP4 program lies in application code.
  • PROGRUND, the debugging QAB Interpreter, is used to execute QAB programs with debug facilities, such as the ability to set breakpoints or inspect and change data.
  • QABUTIL is a collection of utility programs used with QAB. Amongst other programs it contains facilities to analyse and search your QAB programs in various ways, facilities to create transfer files that can be used to update QAB programs stored on an older version of the database, and facilities to delete unwanted QAB related data.
  • PROGPRN is used to create a print-out of QAB programs and other data. In particular it can be used to create a pseudo-code representation of a QAB program that may be useful as part of a program specification if you decide to rewrite a QAB program using another programming language.
  • AUTOQAB is a generator that can create in a customisable way QAB programs for maintaining static data tables. You may find it particularly useful as a way of proto-typing the maps for data entry programs.
  • PROGBIND is a program that is used to maintain QAB programs that use Place Holders. It allows you to globally change your programs by attaching a different set of procedures to the Place Holders. It also contains several reports related to the use of Place Holders.

Related Programs

The following programs, though not specifically part of QAB, are also often used by QAB programmers or system integrators.
  • MAPEDIT, the map editor, is used to create the maps used by your QAB programs. In 4.6xx releases you may also want to use MAPMAKE to gain access to some additional facilties.
  • MAKEDB and the other DP4 database creation tools are likely to be useful. You will probably need to make changes to your database structure as your system develops.
  • DFSETUP, the DP4 system set up program, will be useful if you are developing programs that use an OSDI.
  • DBERA, is used to create "Era" records, that can be used by QABUTIL to exports parts of a QAB program changed since a specific point in time.


Key QA Build Concepts

This topic briefly introduces some concepts that are very important in QAB Programming.

  • Most QA Build programs will update a database at some point. When attempting to update a database it is important to do it in such a way that when updates that are logically dependent on one another are all made simultaneously, so that either all the updates succeed or all fail. Such a group of updates is called a transaction. A simple example might be that in an order processing program the order header record might maintain an order value field based on the total value of the order lines. (Purists would probably object to this, but this type of thing is very common in practice, to avoid the need to have to read all the order lines just to discover the total value of an order.) If the order header and the order line records are not updated in a single transaction two things may go wrong:

    • Another program that was reading order details, might perceive inconsistent data, through reading an updated order line record, but an older order header.
    • If there is a system crash the database may be left in an inconsistent state.

    In simple QAB programs each procedure body is treated as a transaction. This means the QAB program may have to update several database tables in order to keep the database consistent. If your application needs a transaction that cannot be conveniently handled like this you need to use the Private Database facilty, which allows a transaction to be spread over as many procedure bodies as you need.

  • Your first QAB programs will probably mainly be interactive - allowing the user to read or update data from the database using dialogs. Later on you will want to produce report programs that send output to a printer, or create a file that can be read by another program such as a Word Processor. You may want to export data from your database into a file that can be sent to a mainframe computer. Finally you may want to use specialised hardware in your programs, for example POS hardware such as a bar code scanner, or non standard displays such as two line operator and customer displays. All these operations are handled in QA Build through printing. A QAB program selects (opens) a particular print device and then uses various types of print procedure bodies to send output to it. Instead of, or as well as, selecting a print device, a program can opt to send output to a file.

  • Because of the way QAB programs are stored on the database, it is not possible for a complete system to be contained in one QAB program. In fact the maximum compiled size of a QAB program is currently limited to 64K code+data. If you are used to writing C++ or Basic programs this may sound tiny, though in fact it is more than enough for quite a large and complex program that might be many times larger in other languages. One reason QAB programs are small is that the user interface is stored completely separately from the program logic. Another is that the stored QAB program only has to contain the "business logic" of the application - all the executable code is in the QA Build Interpreter and the DP4 libraries it depends on.

    However, sooner or later you will probably find that one of your QAB programs breaks the 64K limit, and you will need to modify your program to use one or more overlays. Moving code from your main program into an overlay is quite simple and the operation of the program will be more or less unaffected, because the two parts of the program can share each other's data seamlessly.


Using the Program Editor

Introduction

The Program Editor (PROGMAKE) is the program you run to create or amend a QA Build program on your database. A program is created by answering questions in a series of dialogs with (usually) sensible default answers. Most parts of a QAB program can be re-used in other programs, allowing you quickly to build up most of the application programs you need.

This section covers the following topics:

  • Using the Program Editor
    • Starting the Program Editor
    • Program Editor Command Tails
    • Program Editor Header Information
    • PROGMAKE Facilities Menu
    • Editing a QAB Program
      • Selecting a Program to Work On
      • Program Editing Functions
      • Program Summary
        • Screen Layout Map
      • Program Output Devices
      • Program Flow Diagram
      • Inserting Procedures
        • Conditional Entry to a Procedure
      • Deleting Procedures
      • Moving Procedures
      • Copying Procedures
      • Procedure Editing Menu
      • Procedure Body Editing Menu
      • Procedure Overview
        • Iteration
        • Error Subsidiaries
      • Table Processing Menu
        • Main Table Processing
          • Auto-Increment
        • Generate Parent Checks
        • Related Table Processing
        • Additional Table Processing
        • Related or Additional Table Processing Dialog
        • General Remarks on Table Processing
          • Roles
          • Updating Tables
      • Main Maps
        • Screen Design
          • Output Method
          • Function Key Area Map
        • Form Design
          • Form Output Methods
      • Field processing
        • Automatic Generation of Field Processing
        • Field Editing
        • Field Details
          • Field Validation
        • Totalling
        • Local Fields
        • Standard Variable Reference
  • Database Management
  • Maps
    • Mapsets and QAB
    • Language Support
    • Message Maps
      • Automatically Generated Maps
    • Multiple Choice Maps
    • Window Access
    • Message Assignment Menu
    • Map Assignment menu
    • Map Editing
      • Editing Maps Outside QA Build
  • Menus and Function Keys
    • Pop-up Menus
    • Action Bar Menus and Pull-Down Menus
    • Function Keys with Data Entry
    • Hot Key Selection
    • Menu Options
    • Use of Menus for Program Control
    • Standard Message Menus
  • Running Other Programs & Special Functions
    • Calling QA Build Programs
    • Calling Non-QA Build Programs
    • Special and User Access Functions
  • Printing
    • Initialising Printing at Startup
    • Initialising Printing in a Procedure Body
    • Subdevice Output
    • Print Control Variables
    • Print Error Handling
    • Input Source Variable
  • Conditions and Formulas
    • Using Conditions and Formulas
    • Formula Editing Menu
    • Condition Entry Dialog
    • Formula Entry Dialog
    • Formula Syntax
    • Operators and Functions by Category
    • Operator Precedence
    • Operator/Function Reference
    • Function Definition
      • Functions and Macros
      • Constants
      • External C Functions
      • Word Strings


Starting the Program Editor

To run the Program Editor, select QAB Facilities/Editor from the DP4 menu, or enter the utility name at a command prompt: PROGMAKE

PROGMAKE will start and you will be prompted for the following startup details:

Database name | Enter the name of the application database on which you wish to work or double-click on one in the list provided
Initials | Enter your initials for identification purposes. They are stored in the programs you edit.

After this the PROGMAKE Facilities Menu is displayed. To create or edit a program select the "Edit Programs" option, and then select the program to work on.

You may use Command Tails to specify the database, your initials, and program to open and bypass these startup details and the Facilities menu.


Program Editor Command Tails

If you start the Program Editor from the command line, you can use the following command tails:

Command Tail | Description
-DB | Specify the database name. This allows the default database to be overridden
-PROG | Specify the program name. The Program Editor is set up to only edit the specified program
-ID | Specify the developer's initials. The specified initials are stored with the program for management purposes
-SHARE | For multi-user environments, this allows more than one developer to work on the database at the same time. Only use this option if you need it, because there is a performance penalty

For example, to change the program CUSTOMER on the database TIME2, supplying the initials XYZ, and allowing other developers to access the database, you would type the following on the command line:

PROGMAKE -DB TIME2 -ID XYZ -PROG CUSTOMER -SHARE


Program Editor Header Information

The main menus and data entry screens in the Program Editor have a header area like the following:

QAB Set up | Database retail2 | Program | MYPROG |
 |  | Created on | 24 Sep 1997 | by RSA
 |  | Last edited | 24 Sep 1997 | by RSA

In some parts of the Program Editor the header area also lists other programs, procedures or procedure bodies that are affected by your current activity. Because program components (such as Procedures) may be shared between programs, editing one procedure may affect several programs.


PROGMAKE Facilities Menu

The PROGMAKE facilities menu contains the following options:

1 Edit program

Use this option to edit existing programs and create new ones. (To delete programs use the facilities in QABUTIL). When you select it focus moves to the Program entry field for you to select the program to work on.

Once you have chosen the program to work on you will be taken to the Program Editing Functions menu. Program Editing is described in detail in Setting up a QA Build Program and the following pages.

2 Function definition

Use this option to create or edit macros and functions for use in QAB formula processing.

3 PlaceHolder management

Use this option to open the PlaceHolder Management menu (in the PROGBIND utility).

8 Exit [X]

Use this option to leave the Program Editor.

9 Help

Select this option for a quick help screen on starting and using the Program Editor.


Setting up a QA Build Program

A top-down view of the process of setting up a QA Build program is something like this:

  1. Select Edit Programs from the the PROGMAKE Facilities menu and enter the name for the new program as described in Selecting the Program to Work on.

  2. When you have answered Yes to the question about Creating a new program to start the process of adding the new program to the database.

  3. On the Program Editing Functions menu that appears either:

    • Select the Summary option and create the new program's summary as described in Program Summary
    or:

    • You can also create a new program by using the Copy another Program option. In this case you need to be careful when making changes as you may affect the program copied from.

  4. Use the Program Flow Diagram to add a procedure to the Procedure Tree as described in Linking and Inserting Procedures.

  5. Add a procedure body for the procedure using the Procedure Body EditingMenu. You will first need to set up the procedure body overview, and usually all of the following:

    • For procedures that use the database set up the table processing for the main table and related or additional tables using the Table Processing menu.
    • Set up processing for the procedures main map using either the Screen Design or, for print procedures, the Form Design dialogs.
    • Set up the field processing.

Repeat steps 4 and 5 as necessary to add more procedures to your program.


Selecting a Program to Work on

When you select Edit Program from the PROGMAKE Facilities menu focus moves to the Program entry field. Do one of the following:

  • Create a new program by entering a name that is not currently used (see below for how to find out which have been). QAB will create an empty program having that name, which you can edit.

  • Enter the name of an existing program

  • Press <Ctrl+P> or click on the Prompt button for a menu of existing programs, like the one shown below:

    You can select one by double clicking on it or by using the arrowed cursor keys to highlight the required program and then pressing <Enter>.

In all three cases once you have chosen the program to work on you will be taken to the Program Editing Functions menu.


Program Editing Functions Menu

The menu contains these options:

1 Summary

Use this option to enter essential details about the type and function of the program as described here.

2 Output Devices

Use this option to select the default output devices your program will use in print procedures as described Output Devices.

3 Program Flow Diagram

Use this option to display the procedure tree for your program and to access the main program design and editing tools as described here.

This option is not initially available for a new program - you must create the program summary first using options 1 or 6.

5 Documentation

Use this option to open the DP4 text editor, which you can use to create on-line documentation for your programs.

6 Copy another program

Use this option to copy another program into the program you are creating.

8 Exit (X)

Use this option to return to the Facilities Menu.


Program Summary

  1. Select the Summary option on the Program Editing Functions menu. The following dialog appears:

    In the Description field enter a summary, describing the intended purpose of the program. Your description will be displayed in menus of programs, so make it clear. The maximum length is 75 characters.

  2. Click in (or <Tab> to) the Program type. A menu of Program types appears. Choose the most appropriate type for your program. The following table explains the six types briefly:

    Program Type | Description
    Data Entry | Update the database interactively. For example: an order entry program. Use this program type unless another type is more appropriate.
    Enquiry | Inspect the records on the database without updating it. For example: a customer balance enquiry program.
    Batch Update | Update the database with little or no user interaction. For example: a program for creating order invoices.
    Report | Print details of records without updating the database. For example: an invoice printing program. Note that any program type can use the printer.
    Library | A dummy program holding and maintaining procedures for use in other programs. Libraries are explained in more detail here
    Variant Library

    Dummy programs which contains procedures attached to PlaceHolders by PROGBIND. They can be edited in the usual way, but cannot be compiled or executed.

  3. Click on the OK button (or press Enter or Tab to complete the dialog). PROGMAKE will set The contents of these fields will be considered later.

Once you have successfully completed the Program Summary dialog you can begin to create the procedure tree for the program using the Program Flow Diagram.

Description field

Use this field to describe the intended function of the program. Your description will be displayed in menus of programs, so make it clear and concise. The maximum length is 75 characters.

Initialise printer/output file

Enable this option if the program needs to print or send output to a file and you wish to open the print device at program start up time. Note that you can also initialise printers and output devices in later procedures. This may be useful if the printer is required only occasionally.

The output device(s) to be used are set in the Default Output Devices dialog.

See Printing for information about print devices and printing in QA Build.

Exclusive access required

Enable this option to prevent other programs from using the database while this program is running. This facility is normally used only for Month End-type programs.

Stop program if update fails

Enable this option if you want the program to stop if an update request fails. An update request will only fail if another user has locked or updated the record that you are trying to update. If you choose not to stop when an update fails, all the records to be updated are reread from the database.

Suppress update fail messages

Normally, when an update fails, a system message is displayed informing the user of the failure and the reason for it. Enable this option if you do not want these messages displayed.

Private database updates

Some programs, such as order entry programs, may need the facility to confirm or abandon a large number of updates at once. Enable this option if you need such a facility. If you do this, updates will take place only in those procedures which secure changes, but your program will behave as though the updates had been made at once.

Ask questions with menus

This question is used to control the default style of dialog. DP4 programs typically interact with users in one of two ways:

  • They offer users the opportunity to perform actions via questions such as Do you want to ...

  • They offer menus of possible actions from which the user chooses one.

Whether a dialog takes the form of a question or a menu determines the design of the map in which it takes place.

Required read authority

Enter the read authority required to run this program. The default is 0. At run time PROGRUN checks this authority in addition to all the other security checks.

Required write authority

Enter the write authority required to run this program. The default is 0. At run time PROGRUN checks this authority in addition to all the other security checks.

Screen layout mapname

By default, error and information messages in a QAB program are displayed as pop ups in whatever part of the screen is empty or has been least recently used. If you would prefer to display these messages in a fixed position on the screen, answer this question and the following one which assign a map whose fields determine the position of these messages, and which you also can use for positioning other maps used by your program. This is known as a Screen Layout Map. Enter the name of the mapset containing the map to be used as the layout map.

Screen layout map number

This field is not entered directly but using the Map Assignment Menu See the page Screen Layout Map for how to set up and use this facility in a QAB program.


Screen Layout Map

You may optionally assign a screen layout map to a program in the Program Summary.

By default, error and information messages in a QAB program are displayed as pop-ups in a part of the screen that is empty or has not been used recently. If you would prefer to display these messages in a part of the screen reserved for them, assign a screen layout map by identifying its mapset and map number in the appropriate part of the Program Summary. Using this facility, you can design a standard layout for all your programs ensuring that messages of a certain type always appear in the same place.

The fields in the screen layout map determine the position of messages as follows:

  • 101 function key area
  • 102 information messages
  • 103 error messages
  • 104 question menus
  • If you include field 109 in the map, any scrolling maps will end at or before that field whenever possible.

Other numbers between 100 and 120 may be used in the future for specifying positions on the layout map.

If you do not want to specify the position for any of these types, omit the appropriate positioning field from the map.

Your screen layout map may also include attachment fields to which other maps in the program will be attached. You can position main maps using the Attach to another map output method and specifying the layout map number and an appropriate field. Multiple choice maps can be attached by inclusing a field numbered 100+map number in the layout map.

You can also use the screen layout map for adding standard text that remains on the screen, such as the name of a company. However this prevents the layout map from being displayed as an invisible map, which is often an advantage.

To define a screen layout for your program:

  1. Select Screen layout map number on the Program Summary screen and select Edit map
  2. On the layout map, add a field (usually an attach point control) in the position you want the message maps to appear. Number the field according to the message type (for example, field 103 for error messages).


Default Output devices

The Default Output devices dialog looks like this:

This screen selects the default output devices your program will use in print procedures. The user may, however, override this setting from the command line or from within PROGRUN. You can set this information up whether or not a program initialises a print device in the Program Summary - the values set here provide the initial values for the standard variables concerned with printing. See Printing for more information on printing from QAB.

Screen

Enable this option to send output to the screen. How this works will depend on the print device selected and how DP4 is configured. Traditionally this just writes output directly to the screen as though it were a serial device, using the area not occupied by screen based maps to scroll in. For many programs this is of little use other than as "comfort output", to show that the program is working. On Windows based computers this can significantly slow down the creation of a report because scrolling the screen is a very slow operation. It is probably better to create a file and invoke a program such as the DP4 VIEW utility to allow the user to examine the file.

If you print to DFPRINT screen output goes to a temporary file that the user can view or print. It is intended to make a similar facility available for other print devices in the near future.

Printer

Enable this option to send output to one of the printers.

File

Enable this option to send output to a file.

On a multi-user machine (such as a machine running Unix with Telnet or dumb terminal users, (or a machine running Windows Terminal Services) you should always arrange for each user to have a unique login directory, which should be the current directory when DP4 programs are run. In DFSETUP the PRN directory should be left blank or should not have a leading /, to ensure file output is sent to a directory that does not conflict with other users. If you don't do this two users may try to create the same file at the same time.

Print device

Enter the printer name that selects characteristics to be used for hard copy and for formatting the print file. A drop-down menu lists the print devices available. If you

Output file

Enter the filename to be used for the print file.

Spool output

Enable this option to delay printer output until the program ends. This may be useful for programs that produce only a small amount of output but which take a long time to run. In fact using this feature makes no difference on current releases of DP4.

Restart file

Enable this option to ensure that the old output file is deleted and a new one created each time the program is run. Disable this option if you require the output during each session to be appended to an existing file.

Select at run time

Enable this option if you wish the user to be asked to select a print device at run time using the standard DP4 printer selection dialog. If you disable this option, the settings entered here will be used automatically unless over-ridden from the command line.

No control output

Enable this option if you want your output files not to contain printer control sequences. Output sent to the printer is unaffected. This option will be useful if you want output files to be be plain text files. If the control output is included in the files the file may look strange if opened by a program such as a word processor. The file may really only be suitable for copying direct to the appropriate print device.

Enable reselection

When a program attempts to select a print device that is unavailable or off-line, the system displays an error message allowing the user to correct the problem and continue. If, instead of this, the user prefers to cancel the operation, the standard behaviour is to present the Select Output Devices dialog, which allows different output devices to be selected. If you disable this option, the whole of the procedure tree which selected the print device is aborted.


Program Flow Diagram

The Program Flow Diagram displays the procedures in your program and shows the links between them.

The following symbols are used in the Program Flow Diagram:

? | Execution of this procedure is conditional
* | Repeated procedure
M | Procedure above M is a menu ; procedures below are menu options
>> | More procedures off the screen to the right
<< | More procedures off the screen to the left
T | Procedure is tied to this program
X | Procedure is tied to another program and should not be edited in this program
P | Procedure is a place holder

When you first enter the flow diagram, the cursor will be positioned on the first procedure in the program (the root).

You can navigate round the flow diagram in several ways:

  • Pressing the <←>, <→>, <↑> or <↓> keys takes you one procedure to the left, right, up or down. The <←>, <→>, and <↓> keys can also be used to create a new, empty, node in the procedure tree ready for you to insert a procedure there (they are automatically deleted again if you do not use them).
  • Pressing the space bar moves the cursor to each procedure in turn, following the logical order of execution
  • You can click on any procedure with the mouse
  • You can use the Find option to find a named procedure, or the next option to find the next instance of the current procedure.
  • You can use the Search options to find procedures meeting a specific criterion.

At the top right of the program flow diagram the editor displays the programs or procedures that would be affected by changes made at the currently highlighted part of the procedure tree. It is a good idea to keep an eye on this area, especially if you have incorporated procedures from another program in the one you are editing now, to guard against making unintentional changes to other programs.

Beneath the program flow diagram a menu bar is displayed that you use to edit your procedure tree.

Menu Bar Options

To move between the program flow diagram and the menu bar:

  • When focus is in the program flow diagram press <Enter> to move the cursor to the menu bar.
  • When focus is in the menu bar press <Esc> to move the cursor to the program flow diagram.

Alternatively you can simply click on either the program flow diagram or the menu bar, or for buttons that have a mnemonic letter underlined you can press the mnemonic key directly eevn when focus is in the program flow diagram.

The menu bar options displayed at the bottom of the Program Flow Diagram are:

Link | Use this to edit the information linked to the currently highlighted position. For further information see Inserting Procedures.
Ins | Use this to insert a new procedure to the left of the currently highlighted position. For further information see Inserting Procedures.
Del | Use this to delete the currently highlighted procedure (from this location). For further information see Deleting Procedures.
Procedure... | Use this to edit the highlighted procedure. For further information see Procedure Editing Menu
Body... | Use this to edit the highlighted procedure body. For further information see Procedure_Body_Editing_Menu.htm
Doc | Use this option to edit or create the on-line documentation for the currently highlighted procedure or procedure body in the program flow diagram.
PlaceHolder | Use this to Insert a PlaceHolder procedure to the left of the currently selected procedure
Find | Use this to find the named procedure or procedure body in the Program Flow Diagram
Next | Use this to find the next occurrence of the previously found procedure or procedure body
Search | Launches the QAB Find utility which allows you to search the current program or the database for occurrences of procedure names, field or variable names. For further information see Locating Formulas and Identifiers
Zoom+ | Use this to zoom in on the highlighted procedure, that is to move it to the top left-hand corner of the display and show only its subsidiaries beneath it. This is shown as a
Zoom- | Use this to return to normal display after using zoom
View>> | Toggles the names displayed in the program flow diagram between procedure names and procedure body names. This also affects what the Doc button edits.
Affects>> | Toggles the names displayed in Affects section of the status display between procedure names and program names
Tie | If the currently selected procedure is shared with other programs, use this option to tie it to the current program to discourage users from editing it elsewhere. For further information see Sharing Program Components
Untie | Cancels the tie on the currently selected procedure so that it can be edited elsewhere
Open+- | Toggles the currently procedure between showing its subsidiaries or not. Procedures where the subsidiaries are hidden are coloured differently from others.
Exit (X) | Returns you to the Program Editing Functions Menu.
Help | Use this to display a key to the program flow diagram.
Entry>> | Toggles the display between showing entry conditions of procedures or not as you move round the flow diagram.
Build... | Opens a menu that allows you to quickly compile or run your program.

The next few pages describe in more detail the some of the most common tasks you will need to perform when editing your procedure trees:

  • Inserting Procedures
  • Deleting Procedures
  • Moving Procedures
  • Copying Procedures


Linking/inserting new procedures

Linking or inserting new procedures

There are two ways you can insert a procedure into the program flow diagram. Either:

  • To insert the procedure to the right of or below the currently highlighted procedure, use the <→> or <↓> key to move the highlight to the required location and then select Link.

Or:
  • To insert the procedure to the left of the currently highlighted procedure, select Ins or press the <Insert> key.

Either operation leads to the Link edit dialog.

Inserting above the Root

To insert a procedure above the root of the tree, delete the root of the tree, insert the new procedure in its place and then re-insert the old root below the new procedure. Note that if you press <Escape> before re-inserting the root procedure, it may be lost. You can protect yourself against this by pressing the Tie button to tie the original root procedure before you delete it.

Another safe way of inserting above the root is to use the zoom facility:

  1. Select Zoom+ from the menu bar
  2. Delete the root procedure. (You may safely press <Escape> at this point to abandon the changes)
  3. Insert the new procedure as described above.
  4. Press the <↓> key and enter the name of the original root procedure in the Link Edit dialog. The procedure tree will be displayed correctly with the new procedure above and the old procedure tree below (but the change has not yet been made to the real program).
  5. Select Cancel zoom to return to the Program Flow Diagram.
  6. Navigate to the root link and use the Link option to replace the root procedure with the new root procedure.

The new procedure will appear as the root procedure with the original procedure tree below it.

Link edit dialog

If the procedure is beneath a menu type procedure enter the option number. Optionally, enter the option number to be selected when the new procedure ends.

In the name field enter the name of the procedure to be inserted. Press <Ctrl+P> for a menu of existing procedures. For an existing procedure, the subsidiary procedures and their associated procedure bodies will be inserted into your program. Your program will share these components with any other programs that use the same procedure. The concept of sharing components is explained in Sharing Program Components. To create a new procedure enter a name that is not currently used by any procedure. The Program Editor will confirm that the procedure does not exist and will ask you to confirm that you wish to create it.

Create a Condition to Enter if required, and then close the Link Editing Dialog.

Completion of the Link edit dialog takes you to the Procedure Editing menu.


Conditional Entry to a Procedure

Before executing subsidiary procedures, QA Build tests the return_value of the calling procedure. Under some circumstances, for example if the user pressed <Esc> in an interactive procedure body, or a batch access database procedure ran out of records to process, execution leaves that part of the procedure tree. Also if a database procedure deleted a record or if a record could not be found, or a database update failed, subsidiary procedures will not normally be executed. You can force subsidiary procedures to be executed in some additional cases using the Error Subsidiaries option in the procedure overview. For more information on how QA Build decides whether to execute subsidiaries refer to Procedure Execution.

In addition to this built-in processing, you can specify your own conditions to determine whether a particular subsidiary procedure should be executed, using a Condition to Enter set up in the Link dialog.

The condition to enter follows the logic:

  if (the condition to enter is satisfied) then
    execute procedure.

For procedures with a repeated procedure body you should note that the Entry Condition is only tested on initial entry into the procedure, and not before the procedure obdy is repeated.

For menus and action bars the entry conditions of all the subsidiary procedures are tested when the menu is displayed. This is done so that there is a visible indication of which options are not available. This is called greying out, and is explained in more detail in Menu Options.

To set up a condition to enter:

  1. In the Program flow diagram select the node in the procedure
  2. Select the Link option on the Program Flow action bar
  3. Move to the condition to enter and follow the instructions press E to edit a condition

Note that the condition to enter is a property of the calling procedure and not of the subsidiary itself - the same procedure can appear in different places in a procedure tree with different conditions to enter each time. Also the condition will be preserved if the procedure body used by the calling procedure is changed.

Useful variables

Two of QABs standard variables may be useful when creating conditions to enter:

option_nr | Provides the return value from the calling procedure's procedure body
last_return_value | Provides the return value from the last procedure body executed. This is particularly useful where you want to test the return value of the procedure on the left (assuming it did not have any subsidiaries).


Deleting Procedures

When you delete a procedure or procedure body from a program, it is not removed from the database immediately and if it is used elsewhere in this program or in another program, it will not be removed from the database at all.

If you delete a procedure by accident, it is not necessarily lost. Up to 32 deleted procedures may remain available and may be re-inserted. However, this is not guaranteed and the deleted procedures will be lost when you exit the Program Flow Diagram.

You will sometimes want to delete a procedure from one place in the procedure tree and move it to another part. The safe way to do this is explained in Moving Procedures.

You can protecting procedures from accidental deletion from the database by adding them to a library program, or by tying them to a program. If a procedure is tied it will not be deleted even if it is (temporarily) deleted from the program it is tied to.

To delete a procedure:

  1. Position the cursor on the procedure to be deleted
  2. If the procedure is tied to this program and you do not want it to kept if it is not being used anywhere else, then use the Untie button to untie it
  3. Press the <Delete> key, or click on the Del button in the menu bar and confirm that you want to delete the procedure


Moving Procedures

Before you move or copy a procedure to another place in a program or to a different program you should check the following:

  • Formulas: Are the fields and variables used in the formulas available at this point in the program's execution?
  • Maps: If you entered <Blank> for the mapset, is the correct mapset loaded? Are maps declared as On Screen Already available?
  • Print procedures: Is the printer selected?
  • Table Processing: Are tables declared as Already fetched available?

The QA Build compiler will notify you of most problems that can be caused by moving a procedure.

To move a procedure:

  1. Create a new link at the point of insertion and type in the name of the procedure to be moved.
  2. Delete the procedure from its original position.

It may sometimes happen that you want to change the root procedure of your program, and make the current root procedure a subsidiary of the new root. The best and safest way to do this is as follows:

  1. Enter Zoom Mode
  2. Delete the root procedure of the Zoomed Procedure Tree
  3. Create the new root procedure as described in
  4. Insert the original root procedure as a subsidiary of the new procedure
  5. Cancel Zoom Mode
  6. Navigate to the root and select the Link option
  7. Change the procedure name for the link to the new root procedure


Copying Procedures

You may copy any QA Build procedure, but a word of warning. The copying process copies only the named procedure itself, not its subsidiaries. Any subsidiaries are now shared with the original procedure. See Sharing Program Components and Moving Procedures for precautions that you should take.

If you want an entirely separate working version of a procedure tree, you must copy each procedure and procedure body in the tree in turn.

To copy a procedure not already in your program:

  1. On the Program Flow Diagram insert a new procedure
  2. Type in a new name for the procedure
  3. Select the Copy option from the Procedure Editing menu
  4. Type in the name of the procedure to copy from, or select one from a list of the procedures on the database (press <Ctrl+P>)

To replace a procedure by a copy:

  1. On the Program Flow Diagram navigate to the procedure to be replaced with a copy
  2. Use the Link Edit Dialog to change the name of the procedure linked to the desired name for the new procedure and close the dialog.
  3. Select the Copy option from the Procedure Editing menu that appears: The name of the original procedure will be displayed in the edit box. Press Enter to proceed with the copy.

To copy a procedure body:

  1. Add a procedure body to the database
  2. On the Procedure Body Editing menu, select the Copy option
  3. Type in the name of the procedure body to copy from, or select one from a list of the procedure bodies on the database

Replacing a procedure body by a copy can be done in a similarly to replacing a procedure by a copy above, i.e. In the Procedure Edit dialog replace the name of the procedure body to be replaced with the new name, and accept the Copy option in the following menu.


Procedure Editing Menu

The menu contains these options:

5 Documentation

Select this option to enter the text editor in order to write on-line documentation for your procedure.

6 Copy

Select this option to copy an existing procedure into your procedure. If the procedure is not new

7 Edit

Select this option to edit or create your procedure. In the Procedure editing dialog enter a brief description (maximum 80 characters) of the overall function of your procedure and its subsidiaries. This description appears at the foot of the program flow diagram when the procedure is highlighted.

Next enter the name of the procedure body. The procedure body is the part of the procedure that describes its database and screen operations, but not its subsidiaries. By default it has the same name as the whole procedure, but you may give it a different name. This may be helpful if you wish to use the same body within a different procedure. If, for example, the main procedure of each program displays a title and the time before calling subsidiaries to do the rest of the work, all the main procedures may use the same procedure body.

On completion of this dialog you are taken to the Procedure Body Editing Menu .

8 Exit (X)

Select this option to return to your previous location in the program.


Procedure Body Editing Menu

1 Procedure overview

This option opens the Procedure Overview dialog, used to set the procedure type (for example, interactive, batch), name the maps to be used by the procedure, describe iteration behaviour and various other properties. The Procedure Overthis procedure's relation to the program's flow.

2 Table processing...

This option opens the Table Processing Menu which allows you to select which tables are processed by the procedure, whether records may be amended, created and deleted and how records may be accessed.

3 Screen/Form Design

This option opens either the Screen Design or, for print procedure bodies, the Form Design dialog. These allow you to describe how the main map is displayed or output, and lead to the the Map assignment menu and thence the Map Editor.

Note that, if this is a database procedure, you must set up Table processing before you can choose this option.

4 Field processing

Field processing is the facility in a QA Build procedure body that you use to enter, display and change data fields. You must define field processing for input fields and all other fields that are processed by the procedure body.

Use this option to set up the processing associated with each field in the procedure's main map. Note that you must set up Table processing and screen/form design first. There is no field processing for menu procedures.

5 Documentation

Select this option to enter the text editor and write documentation for your procedure body.

6 Copy procedure body

Select this option to copy an existing procedure body into your new procedure body. Enter the procedure body name or press <Ctrl+P> for a menu of existing procedure bodies.

8 Exit (X)

Select this option to return to your previous location in the program.


Procedure Overview

When you define a new procedure body, you must set up its description, type and other fundamental properties in the procedure overview using the dialog below:

Description

Enter a brief description of the procedure body. Initially the description from the procedure is copied to this field, but you may change it if you wish; the procedure and procedure body descriptions are independent.

Procedure Type

Select the required procedure type from the menu. The types are as follows:

Procedure Body Type | Description
Interactive | Data entry procedure
Batch | Procedure without data entry or printing.
Menu | Procedure offering the user a choice of actions from a simple menu without pull-downs
Print | Procedure to send output to a print device
Combined menu/entry | Data entry procedure terminated using a function key to select an action
Action bar menu | Procedure offering the user a choice of actions from a menu with pull-downs
Save/exit menu | Procedure to confirm or abandon changes before the database is updated - for programs where the private database facility is being used
Sub device output | Procedure to select or print to a different sub device, and for multi-output devices
Device select/print | Procedure to select an output device interactively, at run-time
Run another program | Procedure to call another QA Build program, non-QA Build program, or use a set of built-in commands
Link to C | Procedure which selects a procedure body written in C
Hot key set up | Procedure to set up hot key actions which can be called when executing subsequent procedures

The links in the table above will help you fill in the remaining fields in this dialog as appropriate for that procedure type.

Depending on the program type, and the procedures above this one in your procedure tree, some procedure types may be disabled.

Output sub-device

This field is only used for procedure bodies of types Device select/print and Sub device output.

  • For Device select/print procedures you must enter the name of one of the available "Device Initialisation" PODs stored on your system database. Press <Ctrl+P> to choose from a list of the available devices.

    If you select the "default_printer" device, at run time QAB ignores this device and requests the print device specified in the C variable pr_device. This will either be the device specified in the Default Output Devices dialog, or if another device select print procedure that does not use "default_printer" has already been executed, it will have been replaced by the device specified in the last such procedure executed.

  • For Sub device/output procedures you must enter the name of one of the PODs belonging to the same device group as your main print device. Press <Ctrl+P> to choose from a list of the available devices. This procedure type is most commonly used with devices that control an OSDI. For example your program may need to switch between "receipt" and "journal" printers belonging to the same device.

Input sub-device

Enter (as an integer) the required bit mask for input devices. Press <Ctrl+P> to choose from the available input devices.

Escape option

In some menus you may wish to prevent the user from escaping by pressing <Esc> or you may wish to perform some special processing when <Esc> is pressed. You can do this by forcing the <Esc> key to be treated as a regular option number. If you do this, however, ensure that you set a condition to repeat for the procedure, so that the user has a way out.

Mapnames

Each procedure body can use maps from two distinct mapsets:

  • The main map, any function key area map, and pop-up windows used for picking records all use maps in the main mapset.
  • Error messages, and message dialogs (for instance those used to offer the chance to delete a record) all live in the remarks mapset.

Either or both mapnames can be blank, in which the name used is inherited from the procedures above. You may use the same mapset for both groups of map if you wish. More information and advice on using mapnames can be found in Mapsets and QAB.

Main mapname

Enter the name of the main mapset to be used by this procedure. If you leave this field blank, the procedure will inherit the mapset name from the procedure above (or the mapset name it inherited) , and so use the same mapset as it. If mapnames are blank all the way to the top of the procedure tree the program name is used as a mapset. A blank mapname is useful when the same procedure body is used in several programs but with minor differences, such as headings, in the maps. The mapset name that will be inherited is displayed at the top of the screen, to the left of the information about programs that are affected by changes to the procedure.

If you enter the name of a mapset, that mapset will be loaded. This is useful if the procedure always uses the same maps; it also simplifies maintenance.

Remarks mapname

The Remarks mapname is treated similarly to the main mapname - if it is blank, it is inherited from See the notes on Main mapname above. If you leave this field blank, the procedure will use the currently loaded mapset. You may, however, prefer to keep all your messages in a special mapset shared by all programs to ensure, for example, consistency of error messages.

Database procedure

Enable this option if the procedure uses the database. Disable this option only if the procedure does not access or update any tables. The most likely use of such non-database procedures is as pause routines and to display headings. (Non-database procedures, however, may refer to previously defined database fields.)

Update database

Enable this option if this procedure updates or deletes any records in the database.

Confirm updates

Enable this option if the user is required to confirm that the data is correct before the database is updated.

Secure changes

Enable this option if you wish to ensure that changes made up to this point in the program will be preserved in the event of a crash. Think carefully about your answer because disabling this option may mean that transactions are lost in a crash, while enabling it will slow the program down. Usually you should enable the option at the top level of a multi-level program, but disable it in batch update programs.

Repeated procedure

Enable this option if you require the procedure to repeat until either the user selects an escape option (in an interactive program) or there are no more records to process (in a batch program). Be careful not to repeat procedures such as heading routines which do not access the database, unless they are interactive or a condition to repeat is set.

Condition to repeat

Enable this option if the procedure is repeated but you wish to make the repetition conditional. Select the condition from the Formula Editing Menu .

Error subsidiaries

Normally subsidiary procedures are not executed if the user chooses to delete the record being processed, if there is a checkpoint failure or if the record cannot be processed. Enable this option if you wish to execute subsidiaries even under these circumstances. You must, however, ensure that there are suitable entry conditions on each subsidiary.

Command

If the procedure is of the Run another program type, enter the command line for the program you want to run. You can find full information on the command syntax in Running Other Programs and Special Commands.


Iteration

When iteration is required in a procedure, for example when a number of orders are to be entered or a number of records batch processed, you must set the procedure to repeat by selecting Repeated Procedure in the Procedure overview. A * symbol will appear in front of the procedure's name on the flow diagram to indicate that this is a repeating procedure.

Program execution will will automatically leave a procedure under certain conditions, for example when a batch process that scans through a table finds it has processed all the records, or the user escapes from an interactive procedure. You may also want to impose some other condition which must be satisfied for the procedure to repeat. This is done using the Condition to Repeat in the Procedure Overview.

In the following diagram PROC1 and the whole tree associated with it is repeated: for each iteration the body for the procedure and any subsidiary procedures are executed:

This structure can be stated as:

  do
  {
    execute procedure 1
    if procedure 1 completes appropriately†
       execute procedure 2
  }
  while (the condition to repeat procedure 1 is satisfied)

†The circumstances under which the subsidiary procedures are executed depends on the setting of the Error Subsidiaries flag in procedure 1.

The program would have a different structure if the second procedure is to the right of the repeated procedure:

The structure is now:

  do
  {
    execute procedure 2
  }
  while the condition to repeat procedure 2 is satisfied
  execute procedure 3

To enter a condition to repeat:

  1. Select the Body option on the Program Flow menu bar
  2. On the Procedure Overview select the Repeat procedure option and move the focus to the Condition to Repeat
  3. Use the Formula Editing Menu to select or create an appropriate condition
  4. If necesary edit the condition using the Condition Editing dialog to set up the condition.

Useful Variables

The QAB standard variable return may be useful to you in a condition to repeat. It gives the return value from the procedure body. You can also use option_nr for the same purpose. Normally it represents the return value from the calling procedure's procedure body, but it is set to the procedure body's own return value for testing the condition to repeat.


Error Subsidiaries

As no

You may set up procedures to handle error conditions; these are called error subsidiaries. If you set the error subsidiary flag, the subsidiary procedure (or group of procedures) is executed only if the current procedure body is terminated by any of the following events:

  • The user deletes a record
  • A checkpoint failure occurs
  • A record could not be processed

The procedure(s) are not executed, however, if:

  • The user presses <Esc>
  • A record could not be found

To set up an error subsidiary:

  1. In the Procedure Overview for the procedure body in which the error condition could arise set the Error subsidiaries? Flag to yes
  2. Add the subsidiary procedure to be executed


Table Processing

Table Processing Menu

1 Main table processing

For each procedure body in your program that accesses the database, you must select a main table. The processing you set up for this table determines how (and whether) records are read, updated, or deleted in this table.

Use this option to set up or edit the main table processing for this procedure.

2 Validate tables

When you select the validate tables option the Program Editor will examine the data input screens for all the tables used in the procedure body. The process will stop if an answer is inappropriate for some reason. You can then answer the question appropriately.

  • You have made changes to the database structure which may affect the tables accessed by your program.
  • There were errors when you compiled the program.
  • You have changed the table used as the main table.

3 Generate parent checks

Use this option to set up automatically the validations usually performed when records on the main table are updated. You do not have to do this; you may prefer to set up your own validations using option 4.

4 Related tables...

Parent tables of the main table and their parents and grandparents are accessed using Related Table Processing. QA Build works out how and when to access these tables based on the processing you set up: for example when a record is read from the main table, all the corresponding records in the parent tables can be read, whereas if a record in a parent table is selected via a secondary index theprimary key will be copied back to the corresponding fields in the main table.

Note that only parent tables are accessed as related tables - if you find yourself wanting to access a child table of the main table then you need to do one of the following:

  • If you want to access all the child records (order lines for an order for example) then you should use a subsidiary procedure with the child table as the main table and this procedures main table as an "Already Fetched" related table.
  • Change the main table of this procedure to the child table.
  • Access the child table as an additional table. This would be unusual, but it is conceivable, for example you might want to provide an indication that a main table record had at least one child.

5 Additional tables...

Use this option to set up or edit the processing of tables not related to the main table using the Additional Table Processing screen. You will probably not use this type of processing when first learning QAB, but you will find it useful in more complex programs. You may also use this option to set up or edit the processing of related tables if the relationship to the main table is not being used.

To read records from additional tables you must always set up the key fields in the Field Processing tab - there is no automatic copting of fields from one table to another such as occurs for related tables.

8 Exit (X)

Use this option to return to the Procedure Body Editing Menu .


Main Table Processing

For each procedure body in your program that accesses the database, you must select a main table. You can determine whether the procedure may add, delete or amend records in the main table. The Main Table dialog looks like this:

Table name

Enter the name of the main table you want to process. Press <Ctrl+P> for a menu of tables in the database.

Identifying role

Some programs need more than one copy of a table. When this is true, each copy must have a different role. (For example, in the TIME database the EMPLOYEE table is one of its own parents, because each employee has a manager who is also an employee. The employee data entry program needs two copies of the table, so that each employee's manager can be read without overwriting the base employee record.) Press <Ctrl+P> for a menu of other copies of the table.

Access mode

Select from the menu the mode in which the database is accessed:

1 Already fetched

Select this option when the record to be processed has already been read from the database or has been set up in an earlier procedure.

2 Batch (Automatic)

Select this option to process a group of records from the table or all the records in the table. With this option a record is read before any other processing takes place. It should normally be used only in repeated procedures.

3 Normal

Select this option when the record to be processed is selected during the procedure, for example, when the user enters the key.

4 Line (Auto & Normal)

Select this option to combine batch and normal access. It provides an inelegant way of writing programs for breakdown line processing and should be used only in repeated procedures. Your program will be better if you use Window or Cleared window access.

5 Window

This option is useful for breakdown line processing. Select it if you want to offer the user a menu of existing records through which to scroll (perhaps with an Add new record option). This differs from Normal and Multiple choice access in that the user does not enter the key first. The menu remains fixed to the screen throughout the procedure.

You may put a menu of actions in the window, so that the use may select a record with the cursor and an action with the mouse or by typing a mnemonic or using a function key. To do this, in the choice map for the window add field 101 where you want the menu displayed and create a function key area map as a menu with fields for each possible action. In Interactive-type procedures the possible actions are 1 Amend, 2 Delete and 8 Exit window. In Combined menu/entry-type procedures, Option 1 is assumed to be Amend and option 2 Delete, if the procedure body does this processing. You may set up other processing options on other numbers. If a hot key function is installed, F1 is assumed to be Amend, F2 Delete and F8 Exit Window. You may use other hot keys for other processing options.

6 Cleared window

This is the same as Window access, except that the menu of records disappears from the screen after a choice has been made.

7 Set up mode

Use this mode if you want to set up fields in a table which has not yet been read from the database and which you do not want to read now. It is useful when you want to initialise key fields prior to batch access in later procedures.

8 Preview window

Use this option if you want to fill a window with a list of records without the user having to select one of them. There are three types of preview window selected from a sub-menu:

1 Predisplay window – Use this option to fill in a window without any user intervention.

2 Scrolling preview – This is like an ordinary window, but the cursor keys all scroll the window and the user cannot make a selection.

3 Cleared preview – This is like an ordinary cleared window, but the cursor keys all scroll the window and the user cannot make a selection.

9 Run time sort

Select this option, available only in batch and print procedures, to process a group of records from the table or all the records in the table where there is no suitable index allowing the records to be processed in the right order. With this option all the records are read and sorted before any other processing takes place. This option should be used only in repeated procedures. You will be asked to specify the remaining details on this screen as though batch access had been selected – this allows you to select the most efficient index for your intended sort fields. In field processing, you should set the Sort? Option to the appropriate value for the field on which you wish to sort. You may sort on any fields, including calculated fields and fields from other tables. Note that while you may sort and process records in one procedure, because of the way in which PROGRUN works, execution is usually faster if separate procedures are used for sorting and processing.

10 Grid

This option is useful for breakdown line processing. Select it if you want to offer the user a menu of existing records through which to scroll. It differs from Window access in that there is no record selection phase. Data entry can take place in any of the lines in the window.

Typically a grid should have a function key area map containing an OK button as field 1 and a Cancel button numbered as field 2. The choice list map should have a field numbered 101 to position the function key area map. At run time, moving the cursor up from the first record or down from the last record takes it on to the buttons.

A grid procedure should normally secure changes and can only be used in a program which enables the private database.

Batch/preview type

Select the type of batch or preview access from the menu:

First and onwards | Select this option to process all the records in the table. If parent records were selected, all the selected records are processed, starting with the first record.
Equal and onwards | Select this option to process all the selected records, starting from a specified record.
Equal and backwards | Select this option to process all the selected records, starting from a specified record and working backwards.
Last and backwards | Select this option to process all the records in the table, starting with the last and working backwards. If parent records were selected, all the selected records are processed, starting with the last record.

The "backwards" methods of scanning through a table may be considerably slower than the "onwards" methods.

Update main table

Select this option if the record being processed is posted to the database (insert or update).

If you find this option is disabled you need to change your answers to some earlier questions as described here.

Condition to update

You can impose a condition which must be satisfied for this table to be updated. Select the required action from the Formula Editing menu.

Process existing records

Always enable this option unless you are updating this table and wish to prevent the user from amending records that are already on the database.

Allow new record

Enable this option if the user is allowed to add new records to the database.

New record dialog

Use this option to set up the dialog that will inform the user that he is processing a new record and asking him to confirm his actions. This dialog is optional when using Exact match search. You can use either a Yes/No question or a menu (if you generate the message automatically it will use the format determined by your answer to the Ask questions with menus option in the Program Summary). Option 1 means Add this record, option 2 means Scan/choose and option 8 Rekey. Option 2 is meaningless in Exact match searches. Select the required action from the Message Assignment menu.

Auto-increment Table

This field, and the next two are only relevant to procedures that add records in the main table to the database. For a detailed discussion of what auto-incrementing is and how to use it, refer to Auto-Increment.

Search method

Select the required record search method from the menu:

1 Exact match | This seeks a record which matches the key supplied by the user exactly
2 Scan if no match | This seeks a matching record, but if none is found it prompts the user with other records in the table, one by one, allowing him to scan forwards or backwards at will.
3 Always offer scan | This is similar to the previous option, but it always allows the user to scan, even if a matching record is found.
4 Next match | This looks for a matching record, but if none is found selects the next record.
5 Previous match | This looks for a matching record, but if none is found selects the previous record.
6 Multiple choice | This looks for a matching record, but if none is found allows the user to select from a scrolling display of available records.

Search index

Enter the index to be used. Press <Ctrl+P> for a menu of indexes for this table.

Search depth

This is the number of keys which must match on any scans. Enter null (the default) if you want the Program Compiler to set the search depth to an appropriate value automatically.

Message if no record found

You may arrange for a message to appear when a search fails to find a matching record. Normal practice is to produce a separate message for each table that the program accesses. Select the required action from the Message Assignment menu .

Message when record found

You may arrange for a message to appear when a matching record is found. Normally the message is required for the main table and for other tables where there should not be a matching record. Select the required action from the Message Assignment menu.

Record scan dialog

If you have given the user the option of scanning through records, you must set up a message inviting him to accept the current record or select another one. You may choose to use the same message throughout the program or you may have different messages to suit different searches. Select the required action from the Message Assignment menu.

Map to display choice list

If you have given the user multiple-choice search, you must enter the number of the map that will display the list of multiple-choice items. You will be prompted with an unused map number if no map has yet been assigned to this part of the procedure. If a suitable map has already been set up, you may enter its number. Choose the map number and, if necessary, design the map via the Map Assignment menu .

Map to display item in list

If you have given the user multiple-choice search, you may enter the number of the map showing how a single item in the multiple-choice list should be displayed. You will be prompted with an unused map number if no map has yet been assigned to this part of the procedure. If a suitable map has already been set up, you may enter its number. If each item consists of only a single field, no such map is necessary. Choose the map number and, if necessary, design the map via the Map Assignment menu .

Map to attach choice list to

If you have given the user multiple-choice search, you may attach the map containing the choice list to another map. Enter the number of the map but ensure that it contains a field numbered (100 + the number of the choice list map).

Condition to access

Sometimes records should be read only under certain circumstances. For example, in the TIME database at least one employee must be at the top of the management hierarchy and so will not have his or her own manager. So the employee maintenance program makes this validation conditional. Select the required action from the Formula Editing menu .

Condition on record

You can impose a condition which must be satisfied by the record being processed. Such conditions may be used in a number of ways, for example, to filter the records to be handled by a batch process or offered when scanning or treat blank or null keys as requests for pop ups. Select the required action from the Formula Editing menu .

Apply to record not on db

Do you want to evaluate the condition on the table for new records? Normally disable this option, because the condition has to be evaluated before all the fields have been processed. However, if the condition involves only key fields, then it may be sensible to evaluate it.

If you enable it and you are using a scan or choose access method, the program will go straight into scans when the condition is not satisfied. This allows you to treat values such as Null and Space as prompt requests.

Record Locking

The final three questions in the table procesing dialogs relate to record locking. Before using record locking you are advised to read about Record Locking in QAB.

Lock existing record

Enable this option to lock the record which will be fetched at this point in the program.

Disallow locked record

Enable this option to abort processing of the procedure when a locked record is encountered in this program. (There is no point in enabling this option unless the system attempts to lock records in this table.)

Locked record retry question

If you wish to inform the user that the record to be processed has been locked by another user, select the appropriate action from the Message Assignment menu.


Auto-Increment

If the last key component of the main table is a number, the auto increment feature of QA Build can provide a default value for creating new records:

  • For tables that are set to auto-increment using MAKEDB the default number is generated by the database manager.

  • For other tables QAB generates the auto-increment number itself. It uses a value one higher than the key of the last record in the table where all the other keys (if any) match the current values. If there is no such record the lowest valid value is used.

This type of processing can be very useful. For example in an order entry program, you would make the order table (with a single part key consisting of just the order number) a MAKEDB auto-increment table. Then the DP4 database manager that each user will get a different order number for adding a new order (whereas if you relied on QAB auto-incrementing all users would get the same number and suffer from contention problems). The order lines would be keyed on order number and line number and would not be a MAKEDB auto-increment table. QAB auto-incrementing will then number the lines within each order starting from 1 (assuming you set the low value for the line keyfield to 1 in MAKEDB). Assuming your system is designed to allow only one user to work on an order at once, then there is no need for the contention avoiding benefits of a "real" auto-increment table. Also, if you did use a real auto-increment table, the line numbers would not start from 1 for each order, but would be unique across the whole database.

Auto-increment Table

Enable this option to use auto-incrementing as described above.

Must use auto-inc for new

Enabling this option forces the user either to enter the number of an existing record or set up a new record using the auto-increment number.

Normally enable this option to ensure (for MAKEDB auto-increment tables at least) that no two users can attempt to create a record with the same key.

'Must Use Auto-inc' error

If required, set up a message using the Message Assignment menu that is displayed for display when the user enters a number that is not the auto-incremented number nor the key of an existing record.

Networking Issues with Auto-Increment Tables

There are some problems with using genuine auto-increment tables if applications are going to be run in a resilient configuration with more than one server. It is impossible to maintain a guaranteed consistent auto-increment numbers across the whole system because of the possibility of network failures of various types, and some changes are required to make the numbers work at all in a desirable way. A common solution is to change the key of tables that you would like to be auto-increment. For example instead of keying the order table just on an order number you might key it on a ws_id (denoting a unique code for the computer where the order was generated), and then ws_id_order number. Now QAB auto-increment can keep order numbers unique. For environments that use split servers, this facility may not work as expected. Please consult the network and resilience documentation.


Generate Parent Checks

The Generate parent checks option on the Table Processing menu automatically sets up the related tables for the main table of a procedure body.

The related table settings will depend upon the table processing of previous procedure bodies. For instance, the access mode will be set to Already fetched if a previous procedure body has accessed that table.

Use this feature with discretion. It sets up all of the direct parents of the main parent, whether or not you wish to update them or display their field values.

There can be a severe performance penalty if you use Window access or Multiple Choice, because all the parents have to be fetched for every line in the window. You must fetch the parent if any of its fields are displayed in the window, otherwise it is preferable to use Additional table processing instead of Related table processing if the program logic allows this. You then set up the key fields in Field processing and only the parent for your selected record need be fetched.


Related Table Processing

If your procedure body is to read parent records, you must set up Related Table Processing. A list of the parent tables for the main table will be displayed when you enter this part of the Program Editor.

Tables for which Related Table Processing already exists are marked with a *. Parents linked via other tables are indicated by the => symbol.

Use the cursor keys or mouse to highlight a table to work on. Click on the buttons at the foot of the screen to select an action to be applied to the selected table as shown.

Button | Action
Edit | Edit the table processing for the selected parent table. This option takes you to the Related or Additional Table Processing dialog.
Delete | Delete processing for a parent table.
Validate/Auto set up | Use this option to set up automatically the validations usually performed when records on the main table are updated. The comments relating to Generate Parent Checks should be borne in mind when using this option to set up processing for a new parent table.
Exit | Return to the Table Processing menu.


Additional Table Processing

You can process records from other tables on your database that are not parent records of the main table. You may also use this option for processing records from parent tables where you do not wish to use the parent relationship.


Related or Additional Table Processing

The dialog box is similar to the Main Table Processing dialog, but with a limited set of options, because, for example, records cannot be created or deleted. It is used both with Related Tables and Additional Tables. Refer to Main Table Processing for a description of the options in this dialog not described here.

Table name

This is read-only for related tables.

Enter the name of the additional table you want to process. Press <Ctrl+P> for a menu of tables in the database.

Identifying role

Multiple copies of a table within a program must each have a different identifying role. For example: both employee and manager details might be read from a staff table, and using the same identifying role would cause the employee record to be corrupted when reading the record for the employee's manager.

Enter the role you want to give to this copy of the table. Press <Ctrl+P> for a menu of copies of the table used in procedure bodies above this one.

The table name and identifying role identify a unique memory area, that is reserved throughout a program - so it is critical to use the correct identifying role.

Access mode

The access modes for parent tables are as follow:

Access mode | Use for
Already fetched | Using a record that has been set up or used in an earlier procedure body. It is often important to this type of parent up, as QAB will use the information to correctly limit the records read by the procedure - for example to read just the order lines for a previously selected order number.
Check parent on database | Checking there is a matching parent record on the database when you enter the key fields of the parent - for example when entering a product code in an order detail line
Join parent from database | Displaying fields from the parent table without entering key fields. Where existing records are being displayed, this option is more efficient than Check parent. Do not use this option for adding new records.
Prohibit parent | Ensuring no matching parent record exists on the database. For example you might use this type of processing to refuse purchases made using stolen credit cards, or to stop an employee being scheduled for a job on a holiday.

The access modes for records for additional tables are very similar:

Access mode | Use for
Already fetched | Using a record that has been set up or used in an earlier procedure body
Read record | Reading the record from the database when the user enters the key or setting up the key with a formula
Setup record | Initialising key fields for access in later batch procedures
Prohibit record | Ensuring no matching record exists on the database when the user enters the key or you set up the key with a formula

Update table

Enable this option if you want to update this record on the database during this procedure. For example, you need to update the timesheet for a timesheet line during line entry if the total is stored on the header.

If you find this option is disabled you need to change your answers to some earlier questions as described here.

Verify table

Enable this option to check that this record is not altered by another program during this procedure.

Update condition

You can impose a condition which must be satisfied for this table to be updated. Select the required action from the Formula Editing menu.

Allow initial join fail

Enable this option if processing is allowed to continue after a join fail. For example, it may be possible to continue entering a timesheet for a given employee even if there is no manager record.

Warning validation only

Enable this option for a warning validation only. For example, allow entry of order lines for a non-existent product or of timesheets even if the employee has holiday booked.

Validation fail question

When a warning validation fails, you can ask the user if this is what he wants. Select the required action from the Message Assignment menu .

Check parent/child match

This option and the next field are for related tables only.

Enable this option to ensure that the parent really is the previously selected record. For tables where the fields being compared contain type M fields, take care when using this option within related table processing. The comparison of the child record and parent record is case sensitive. To overcome this, specify a condition on the child record as follows:

UPPERCASE(child.m_field) == UPPERCASE(parent.m_field)

Wrong parent message

You can assign a message if the parent is not the previously selected record. Select the required action from the Message Assignment menu.


Roles

Roles are used for both tables and database fields in QA Build. A table role is specified in table processing and both field and table (join path) roles are given for field processing.

If two fields are processed which store the same type of information – for example, two NAME fields – you must distinguish them by assigning a different role to each. Field roles are defined when a table is set up.

Sometimes you may need to process two copies of the same table; for example, in an EMPLOYEE table each employee may be assigned a MANAGER who is himself an employee. Each copy of the table must be assigned a separate role. Table roles are defined in Table Processing.

Occasions when you might want to define a new table role are:

  • A table is its own parent (an employee table defining the manager, a store table also defining the head office), and you want to update the parent record without overwriting the base record
  • A copy of the table is required to allow a comparison between existing records and updated records (for example, for calculation of the difference between values). The second copy of the table is set up as an additional table.

For further information on the use of table and field roles see Field Referencing.


Updating Tables

Whether or not a procedure body can update the database depends on the program type (as set in the Program Summary) and the procedure body type and other answers to questions in the Procedure Overview dialog:

.

The following table shows which program and procedure body types may update the database:

Selection | May update the database | May not update the database
Program Type | Batch Update Data Entry Library Variant Library | Enquiry Report
Procedure Body type | Batch Combined Menu/Entry Device select/print Interactive Print Sub device output | Action bar menu Menu Link to C* Run another program Save/Exit menu

*A Save/Exit menu will secure or abandon changes to the database but cannot update the database otherwise. A Link to C program can call a C function that updates the database.

If your procedure body is required to update the database, you must enable both of the Database procedure and Update database options in the procedure overview.


Main Map

Each procedure may have one Main map, set up using the Screen/Form Design option of the Procedure Body Editing Menu. This map determines the appearance of, and strongly influences the processing of, the fields that will be processed in the procedure body's field processing. Refer to Map Editing for a more detailed discussion of this topic.

For screen based procedures the main map is set up using the Screen Design dialog, while for procedures that print the similar Form Design dialog is used instead.


Screen Design

Main map

This is where you select, design or edit the main map used by this procedure as explained in the Map Assignment menu.

Select No Map if your procedure body does not have a main map.

Output method

This option controls the way in which the main map is displayed on the screen. Select the required option from the Map Output Method menu.

Attach to map and field

These two fields are is only available if you select Attach to another map as the output method.

Map

Enter the number of the map to which you wish to attach this map. The map must be already on the screen and should not usually have been a repeated map.

Field

This is only available if you select output method 4. Enter the number of the field to which you wish to attach this map. Enter 0 or Null if you have chosen window access and want to attach the map to the selected field in the window, or you have set the attach_field variable.

A map can be attached to any type of field in another map (except a field that corresponds to an embedded field). Usually, at least in 4.6xx, you will use an attach point. In 4.5xx edit fields of length 1 are used instead.

Clear before subsidiaries

Enable this option if you wish to remove the map when the procedure body is completed, but before subsidiaries are executed.

Clear after subsidiaries

Enable this option if you wish to remove the map when the procedure body and its subsidiaries are completed.

Function key area map

A function key area map is a map containing information about what various keys do, which is normally displayed at the bottom of the screen. It can be switched on and off by the user, like help messages.

Retain window on escape

When you access the main table via window access, the map of choices is normally cleared if the user presses Escape. Enable this option if you want the map to be kept on screen even if the user presses Escape.

Predisplay function key area map

Enable this option if you need a function key map that is displayed with the window, but which is not attached to it. By default unattached function key areas are only displayed after the window selection has been made.


Map Output Methods

Map Output Method | Description
Top of screen | Clear the screen (apart from the screen layout map) and display the map at the top
Header (Retain) | Display information that stays on the screen (such as a company name). The map stays on the screen until the map is cleared - it won't scroll off the screen.
Repeated (Scroll) | Display repeated lines. The lines scroll up when the display area is full (you can use a screen layout map to control how much of the display area is available for repeated maps). This type of map is often used for screen based reports or journal information.
Attach to another map | Attach to a field in a map that is already on the screen. This lets you control the position of the map.
On screen already | Use a map displayed earlier in the program.
Pop-up Window | Display a menu or other pop-up map in a free part of the screen. The pop-up map overlays what is on the screen – when a pop-up map is removed, what was underneath will reappear.

Unless a main map is displayed as a pop-up window it may move other maps up the screen to make room for itself when it is displayed. Only old repeated maps can be removed from the screen when this happen, and usually only repeated maps will move, but other maps may move as well if they were displayed after a repeated map which is now going to be removed.

Unless they are attached, maps centred horizontally or left justified, depending on the user preferences. Centred maps usually look best.

If you select the Attach to Another Map method, you will need to specify both the map and the number of the field in that map to which the current map is to be attached (see Screen Design).


Function Key Area Map

If the user can select an action using function keys, you may wish to provide some text to indicate the function keys available. This may be placed on the main map or provided as a help message. You may, however, define a function key area map. This is simply one or more lines of text which explain what the function keys do at the current point in the program.

Function key area maps have the following characteristics:

  • The text appears (by default) at the bottom of the screen
  • The display of the text can be switched on and off by the user
  • The position of the function key area map can be defined in the screen layout map

You use the function key area map to display the options available:

<F1> Help <F2> Print Report <Esc> Exit

You might have a more complex set of options which require two or more lines of text.

To change the position of the function key area map:

  1. Add a screen layout map to your program (or edit the existing one)
  2. Add field 101 where the top left corner of the function key area map is to appear

By default, the function key area map is turned on and off using the <Ctrl> <Shift> and <Minus> keys, or using <Shift> and <F1> if you are running DP4 using the SAA option.

To change the default key:

  1. Run DFSETUP and select Keyboard customisation
  2. Change the <Dis> key (this is the function key area display key)


Screen Design

Form Design

Main map

This is where you select, design or edit the main map used by this procedure as explained in the Map Assignment menu.

Select No Map if your procedure body does not have a main map (you might need this for a procedure that selects a print device but does not have any output itself).

Output method

This option controls the way in which the main map is displayed on the screen. Select the required option from the Form Output Method menu.

Beside/Over Form

These field is only available if you select Beside another or Over another as the output method.

Enter the number of the form to be printed over or beside. The form must already have been output using the Delay output option (See Below).

New page at end

Enable this option to start a new page when this procedure is completed. The page is started after the procedure body finishes but before any subsidiary procedures are executed.

Delay Output

Normally forms are output as soon as a procedure body completes. However, if you want to print over or beside a form, or continue to print in it in a later procedure, enable this option to delay output.


Form Output Methods

Output Method | Description
Top of form | Start a new page of printing. QA Build sends a form feed instruction to the printer before the procedure output
Normal | Begin printing at the current position on the page
Beside another | Print beside a specified form. The form is printed beside a form that has been sent to print in an earlier procedure
Over another | Print on top of a specified form. The form is printed on top of a form that has been sent to print in an earlier procedure
Already output | Send an existing form to the printer. Use for filling in a form that has been sent to print in an earlier procedure

You can only use the Already output or Beside or Over another output methods if you selected the Delay Output option when setting up the earlier form.


Field processing

1 Field editing...

Select this option to set up the processing of the fields in this procedure using the Field Editing screen.

You can process both fields that are on the procedure's map and any other field defined as a local field or a field in the procedure body's tables.

For any field you can

  • Assign a value to the field or give it an initial value only if a new record is being created.
  • Total the field into another field.

For each field on a map field you can:

  • Display the field or ask the user to enter a value for it.
  • For an input field validate the entry made by the user.

You can also use field processing to change the contents of database and local fields that are not displayed on a map.

2 Validate fields

Select this option to validate the processing of the fields in this procedure. Do this after making changes to the table processing or to the database.

3 Generate field processing

Select this option to set up field processing automatically, based on the map being used. This is only useful for database procedures. See Automatic Generation of Field Processing for further information and advice about this option.

8 Exit (X)

Select this option to return to the Procedure Body Editing menu.


Automatic Generation of Field Processing

QA Build will generate field processing automatically for all the database fields set up on the main map. Select the Generate Field Processing option on the Field Processing tab. This saves you the effort of specifying each field individually. It also ensures that all the fields you placed on the map are set up.

You may still add additional processing, for example, for initialising and validating a field.

You should check the field processing that the Program Editor generates; inevitably it sometimes has to "guess" the required options, based on program type and procedure type. For example, it may set up a field as an input field, although the user should not be allowed to input data there.


Field Editing

The field editing screen looks like this:

The screen displays the fields in the procedure in numerical order. Use the cursor keys to highlight the field to be edited and then select the required option using the buttons at the bottom of the window. You may insert a new field at any position by highlighting the field currently at that position and pressing the <Insert> key. The field previously at that position and any subsequent fields move down one position. A New field option at the end allows for the addition of extra fields at the end of the sequence. You may delete any field by highlighting it and either pressing the <Delete> key or clicking on the Delete button at the bottom of the window.

When the field processing list is on the screen, the main map is also displayed in the bottom half of the screen. As you move the cursor through the list of fields, the corresponding map field is highlighted for identification purposes.

You may also display any formulas set up for the fields. If the "Formulas Shown" status message is visible (turned on and off with the View toggle), any formula for the currently highlighted field is displayed, overlaying the map.

Buttons at the bottom of the screen provide the following options:

  • Field details
  • Delete
  • Auto set-up/Validate
  • Totalling
  • Input
  • View
  • Exit

Note that the sequence of fields is very important. The fields are processed in the order in which they are listed in the Program Editor which may or may not be the sequence of the corresponding mapfield numbers in the Map Editor. Clearly input fields must be processed before any other fields whose processing depend on the input.

The buttons at the bottom of the window offer the following actions:

Field details

Select this option to access the Field Details dialog in which you can edit the properties of the highlighted field.

Delete

Select this option to delete the currently highlighted field.

Auto set up/Validate

Select this option to validate the processing of the current field, or to automatically generate the processing for a new field. If there is a problem the Field Details dialog is displayed with the focus at the appropriate point.

Totalling

Select this option to access the Totalling dialog which offers options for automatic totalling of the quantities processed in the current field.

Input

This button is provided to save time when modifying field processing that has benn automatically generated or for a previously created procedure. It saves you having to open the Field Details dialog to toggle the input field property. Select this option to toggle the status of the current field as an input field. Input should be enabled if the user is required to enter a value for this field.

View

Select this option to toggle the display of formulas for those fields which use them.

Exit

Select this option to return to the Field Processing menu .


Field Details

For each field you wish to modify, display or use in a formula, you must specify a step number, a mapfield number and the name and type of the field:

Field processing is set up for the fields displayed on the main map, to assign values to fields that are not displayed and to initialise fields that are to be used in a formula. Remember that many aspects of field processing are at least partially determined by the main map as summarised in Map Editing.

The following table indicates where fields should be defined in the field processing list:

Field Type | Field Processing Required | Field Processing Not Required
Key Field | Normal access Additional tables When setting up the keys for an Equal and onwards search | Batch access (all records on a table) Related tables Window access
Non Key Fields | If the field is displayed on the main map A value is assigned to the field To declare a local field for use in a formula | Field on table not used in this procedure

Step

The step number determines the sequence in which the fields are processed. This sequence is often consistent with the mapfield sequence, but need not be, provided that input fields are processed in sequence and before the input is needed.

Mapfield

Enter the map field number in which this field is entered, processed and/or displayed. Enter Null to enter the field from a menu. Enter 0 if the field is neither displayed nor entered.

Type

Select the field type from the menu:

1 Database field
2 Local field
3 Button field
4 Picture field

Although processing a field as a picture fields may sometimes be useful, as it allows you to dynamically change a bitmap or icon displayed in the map, the vast majority of your processing will use either database or local fields.

Database fields should be fairly self explanatory - they are the fields in the tables declared in this procedure's table processing.

Local fields include any field that is not a database field. There are three basic types of local field:

  • Fields that are purely local or temporary in character, for example fields that are used to display the result of a calculation that is not used anywhere else. Such fields should be procesed use the @ field name.
  • Fields that are referred to elsewhere in the program, but are not stored in the database. Typically these are used for control purposes, or storing the results of calculations that will be used later on, or for entry fields, that are "massaged" in some way before being stored in a database field.
  • You may also access any of the QAB Standard Variables. There are a considerable number of these, used for a wide variety of purposes. Most of them are introduced in this manual where they are most relevant, but they are all documented in the QAB Standard Variable Reference.

Field

Enter the field name. For database fields normally this should be the same as the validating field name. Enter a blank name for a menu of existing field names. Press a function key for a menu of fields in the main table. If the field has not been previously defined, give it any unique name, preferably one which reflects its function.

Role

If the field occurs with more than one role, select the required role from the menu.

Table

If the field occurs in more than one table, select the required table from the menu.

Role

If the table occurs with more than one role, select the required role from the menu.

Data type

Possible data types are:

C/M mixed-case character

D date

G resource name

I integer

L long integer

N number

P pseudo numeric (digit-only
character field)

Q color scheme

T time

U upper-case character

Y yes/no

Type G fields can be used to change the image displayed in a bitmap or button control. Usually the value of such a field will be the name of the image file to be displayed in the corresponding control.

Type Q fields are only available from 4.625. They allow the color scheme associated with a control to be changed. This may be useful if it is desired to do things like changing the color of a field representing a currency amount depending on whether the amount is positive or negative.

Length

Enter the width of this field (only necessary for character and type G fields). For type G fields you should set this to 255 or the length of the longest resource/file name you will ever display in the field if you know that and it is shorter. Note that there is a minor bug in releases of PROGMAKE made before 2 May 2013, and the length you specify for a type G field will be forgotten each time you edit the field, and set to a computed value.

Precision

Enter the number of decimal places you want stored.

Scale factor

How many places is the decimal point shifted to the left for internal storage? For example, with a scale factor of 2 the number 1.51 is stored internally as 151. This process eliminates rounding errors.

Low occurs & High occurs

When a field has more than one occurrence, you may only be interested in part of it. For example, if you are interested in only lines 2 to 4 of a 5-line address, enter a low occurs of 2 and a high occurs of 4. Unless the field is processed in mapfield 0, QAB will automatically deduce the appropriate high occurs to use from the low value you enter and the depth of the mapfield. For non-database fields there is no predefined number of occurrences and so the first reference defines the number of occurrences.

Sort

Enable this option if you would like the main table to be sorted on this field. Possible values are:

1 | None | No sort
2 | Ascending | Sort into ascending field values. This is the default
3 | Descending | Sort into descending field values.

You can sort on any fields, including calculated fields and those from other tables. To sort on a QA Build global variable, sort on a calculated variable with a formula equal to the global variable.

Input field

Enable this option if the user enters a value for this field.

Prevent change

Enable this option if the user enters this value for new records only and is prevented from changing the value in existing records.

Formula

Is this field calculated with a formula? You can use formulas with fields which are entered by the user to provide more helpful defaults than the normal database default. Select the required action from the Formula Editing menu .

Calculate always

Enable this option if the value of the field is always set by the formula specified above. If disabled, the formula is used as an initialiser only, applied when the procedure is a non-database procedure, or if the procedure has not yet identified the record in the main table or if the procedure is processing a new record in the main table.

Initialisers are applied only once per field each time a procedure is called. Consequently, if a user moves back to a field that has already been processed, the initialiser is not recalculated. It is dangerous to have an initialiser on a non-input field following an input field that has a validation condition or a database access that is validated: the initialiser will never be applied if the user enters invalid data into the input field. It is safer to use a calculate-always formula such as:
if newitem then 0 else $.

Validation condition

Does the input need any special validation other than normal range checking? Select the required action from the Formula Editing menu .

Field to go back to

Normally a validation for a field only applies to the current input field, but sometimes it may apply to a group of fields. If so, you may wish to go back to the first of these fields when the validation condition fails. Enter 0 or Null if the validation applies to this field only. Otherwise enter the mapfield number of the field to which you wish to go back.

Input/Output condition

Is the input or output operation in this field processed only when certain conditions are satisfied? Select the appropriate action from the Formula Editing menu.

Display always

Sometimes an input/output condition for a field is relevant only to input and not to output. If so, you may wish to display the value of the field even when the input/output condition is not satisfied. Disable this option if the field is totally irrelevant, for example MAIDEN_NAME for a single man. Enable this option if the field is relevant even when it is not entered.


Field Validation

Field validation is set up using the Validation condition option in the field editing screen. You do not need validation conditions to check a field is in a fixed range. Numeric, Date and Time entry fields can be automatically validated against upper and lower limits: either defined in MAKEDB for the validating database field; or, in 4.6xx only, in the edit control for the field.

If you set up a validation condition, you may define a field to go back to (set focus to) if the validation fails. If the field to go back to is left at 0 processing returns to the same input field. You may want to specify a previous field if a group of entry fields are closely related and a validation condition invoves several of them.

A calculate always formula for a field is not re-evaluated if input is returned to an input field via the go back to field 0 mechanism. The formula will be re-evaluated if you specify an explicit field number to go back to.


Totalling

Totalling is a facility for automatically maintaining derived field values. When a field is updated as a result of totalling it will be updated on screen automatically provided that the QA Build compiler can determine the appropriate map and field number to use.

You can total from a source field into a target field using one of three methods:

  • Update totalling

    Update totalling adds the change in the value of the field to the target. Typically, it might be used to maintain the total transaction value as the sum of the value of transaction lines.

  • Downdate totalling

    Downdate totalling subtracts the change from the target. Typically it might be used to maintain a stock table as items are sold.

  • Report totalling

    Report totalling adds the final value of the field to the target.

Update and downdate totalling work appropriately to maintain the total correctly depending on how records are being processed: for example the initial value is always 0 for a new record (so the change is simply the final value), and when a record is deleted update totalling subtracts the initial value from the total rather than adding the changed value.

You may wish to set up local variables for displaying totalling results (such as subtotals). If you use a local field, initialise the value in an earlier procedure before the field is used for totalling.

There are various items that you may specify for the processing of a totalling field:

  • Condition to total

    A condition to total allows you only to perform totalling conditionally. For example, you might wish to total a transaction line value into one of several fields depending on the item sold. You could do this by specifying several parts of totalling for the same source field using mutually exclusive conditions to total

  • Condition on total

    A condition on total is used to check that the target value is within a specified range

    If the condition on total is not satisfied, the procedure will abort. Wherever possible, set up validation for the target field in an earlier part of the procedure

  • Occurs

    This is only used when totalling a field into a target field with more occurrences and is used to specify the occurrence number to start totalling into (usually 1)

Using Totalling for a field

  1. On the Field Editing screen add the required source field to the list of fields to be processed
  2. Select the Totalling option for the source field
  3. Select the type of totalling, and name the target field
  4. Add any conditions for the totalling field


Local Fields

Local fields have two kinds of use: displaying data and holding data

Displaying data

  • Displaying a value (such as a subtotal) that is not stored
  • Accessing standard variables (such as showing the current date)

Holding data

  • Calculating a value using the information in the database
  • Sharing data between procedures without updating the database
  • Storing data until an action is confirmed
  • Setting up program parameters

Local fields can only be accessed in the procedures below or to the right of the one in which they are defined.

The data type of a local field is defined as one of the following (as for database fields):

Character | C
Date | D
Integer | I
Long integer | L
Mixed case character | M
Number | N
Pseudo numeric (digit only character field) | P
Time | T
Upper case character | U
Word string | W
Yes/No | Y

To set up a local field for display purposes:

  1. Under Screen/Form design, edit the main map for the procedure and add a local field
  2. Under Field Processing, set up a local field with the same map field number. Enter a name for the variable
  3. Select the Formula option and enter a value for the field to display, or a formula to calculate the value

To modify a variable that is not displayed:

  1. Under Field Processing, set up a local field
    Set the map field number to 0
    Enter the name of the local field or standard variable you wish to modify (enter <Ctrl+P> for a pop-up list of local fields on the database)

Select the Formula option and enter a new value for the field or a formula to calculate the value

Temporary Field @

The local field @ is provided for use as a temporary holding variable. You may redefine this variable any number of times in your program and as any type or length. You may redefine @ within a single procedure.

@ is an efficient method of displaying fields that do not need to be remembered, such as the result of a calculation or the input field for a Press any key to continue message. Do not use it in totalling or for testing entered values.


Standard Variable Reference

The following list shows each variable with its name and data type. The r/w column indicates whether the variable can be read (r) or written to (w). Some are are read-only. You are not prevented from altering variables that are specified as read only, and you may be able to achieve special effects by altering some of them. However, there is no guarantee the variables will behave in the same way in future versions of DP4.

Variable name | Type | r/w | Use for | Values
abandon_changes | Y3 | w | For a private database, same as selecting Abandon changes on the exit menu | Set to TRUE to discard changes
attach_field | I3 | w | Specify a field for attaching a pop-up map (if Field left blank for Attach output method) | Field number
break Y3 w Allowing a program that has no input or output to be interrupted Default is FALSE
Set to TRUE to allow interrupts
broken | Y3 | w | Check if network server is running. Application should reset to FALSE to continue | TRUE if attempt to access server fails
changed | Y3 | w | Test if a value has been entered, for non clearfirst fields only. (Provided for backwards compatibility.) | Set to FALSE before the procedure. TRUE if a value was changed
clock | T5 | r | Obtain current time | Time in the format specified in user preferences
commit_frequency I3 w Controlling update frequency Default is 1.
1 = Commit the changes after every update.
10 = Commit the changes after every 10 updates.
dbname | U8 | r | Reading the name of the database being used | If DATADB is used, the name returned is of the data database
debug | I3 | w | Set debug level for testing specific procedures. | 0 = No debug
decommit | Y3 | w | Same as abandon_changes decommit the changes.
end_key I3 w Read the value of the last selected function key for Combined menu/entry procedures. Set this variable to select an option 1 = F1
2 = F2
12 = F12
fail_code I3 r Read the result of an attempted update 0 = Successful
3 = Commit failure
5 = Lock failure
field_nr | I3 | r | Read the current field number
file_name | U12 | w | Read the current output filename. You can set up the filename, provided this is done before printer selection | Filename
first_item | Y3 | r | Check if first cycle through a batch procedure | TRUE if first time through
first_time | Y3 | r | Same as first_item
force_option | I3 | w | Forces an option on a menu to be executed | Same as the link option number of the procedure
ip_source | I3 | r | Read the number of the subdevice which provided the last input | 1−16 for input device
jump | Y3 | w | Check if <Tab> was pressed to move past the current screen. You can set this variable to FALSE to prevent moving past the current screen | TRUE if <Tab> pressed (this is set to FALSE at the start of a procedure)
last_return_value I3 r Obtain the return value of the last procedure body 0 = Successful completion,
>0 = Function key or menu selection,
−1 = Escape pressed,
−3 = Record deleted
line_nr | I3 | r | Read the current line number (for pagination in printing programs)
logged_on | Y3 | r | Checking whether a user is logged on (perhaps via a different DP4 program) | TRUE if a user is logged on
math_error_mode | I3 | w | Controlling behaviour on a divide by zero error | See pr_error_mode
new_item | Y3 | r | Check if record is a new record (a record is new until it has been posted to and re read from the database) | TRUE if new record
next_option | I3 | w | Set up the default menu option for the next menu to be displayed | Number of option.
no_grey | Y3 | r | Prevent default greying out of menu options OR with condition to enter for the procedure
now | T5 | r | same as clock
option_nr | I3 | r | Obtain the value of the menu option selected in the calling procedure (above the current procedure in the tree)
output_device I3 w Select output device combination add/subtract device selection 1 = Screen
2 = Printer
4 = File
page_depth | I3 | r | Check page length of current output device
page_number | I3 | r | Check current page number when printing
pagewidth | I | r | Page width of print device, as set up in DFSETUP
pr_error_mode I3 w Set up program response if a printer error occurs Modes: (default is 0)
0 = Report error and terminate
1 = Report error and continue
2 = Ignore error
print_error I3 r Check if print was successful 0 = Successful
<>0 = Unsuccessful
qcheck_flags | I3 | w | Read or modify the flags passed to db_update() in procedures that secure changes | 8192 = Reset to previous value on exiting this part of the procedure tree. Other values as specified in dp4dbflg.h . Setting this to 0 will allow a private database program to update the database using a commit() instead of a checkpoint(). This may improve performance considerably, but you need to make sure that it is safe to do so.
qeatfunckeys | Y | w | Control function key shortcuts in QA Build programs | If this is TRUE (it is initially FALSE) it prevents a function key that is pressed in once procesure from causing a short cut through a menu in a subsequent procedure. Setting this TRUE can help prevent unexpected behaviour being caused by function keys.
qerror_text1
qerror_text2 		C255 r/w Add additional information to QAB error messages

These variables can be used to add custom information to error messages. For example you could add information such as the range of permited values if a validation condition for an entry field verified that the entered value was between two values. (e.g. by using numtochar() to set up Qerror_text1 and Qerror_text2. The custom information of Qerror_text1 is only displayed if the error message has an (output only) edit field or embedded data item numbered 101, and the information for Qerror_text2 is only displayed if field/embedded data item 102 is present. Typically you will set up these variables in field processing just prior to the field with the validation condition. For entry conditions the fields will need to be set up in a prior procedure body.

You can only use these values in messages that are considered to be "error" messages, so they are not displayed in messages such as update/delete confirmation messages, or scan dialogs etc.

These variables were added to 4.623 on May 15th 2008, and are only supported in 32 bit versions of QAB, not in legacy versions.

qexitcode | I5 | r | (From 4.623) Return code from last command run by a "Run another program" procedure body that specifies %WAIT. This variable is NOT set if %WAIT is not specified. On NT based operating systems it is also not set if a 16 bit Windows program is executed.
qhot_fieldnr I3 r Obtain the number of the mapfield processed when the key was pressed >0 = Number of mapfield
<0 = Number of field in a window
qkey_used | Y3 | w | Check if a hot key selected an option. If set, the function key is not passed back to the calling procedure | FALSE before the hot key procedure. TRUE if an option was selected
qkill_flags | I3 | w | Check/modify the flags passed to kill() | 8192 = Reset to previous value, n Other values as specified in dp4dbflg.h
qmenu_escape_ok | Y3 | w | Check/define whether escape enabled initially set by DFSETUP question Allow user to escape from DP4 menu | TRUE = Enable user to escape, FALSE = Disable escape
qpick_flags | I4 | w | (From 4.525/4.621) Modify the flags passed to pick_record() in Window processing. | 2048 = NEW_NORMAL. Allows the "New Item" line to be treated like other lines in the Window. Without this flag you cannot select the "New Item" line using a function key.
qpost_flags | I3 | w | Check/modify the flags passed to post() | 8192 = Reset to previous value, Other values as specified in dp4dbflg.h
qprdevice I w

Introduced from 4.623. Equivalent to C global variable pr_device. If you set this variable to the device number of one of the devices on the system database prior to a device select/ print procedure that uses the "default printer" device the corresponding printer will be be used to do printing instead of the default printer.
qprhotkey | I | w | Hot key option to be executed of check_printer() finds print_error is non-zero with pr_error_mode set to 3
qprog_description | C76 | r | Obtain the description of the currently running QA Build program or overlay | Program description as defined in PROGMAKE
qprog_name | U8 | r | Obtain the name of the currently running QA Build program or overlay | Program name as set up in PROGMAKE
qshow_disabled I1 w Modifies appearance of controls that are input fields, but which are currently disabled. Available from 4.622 and as an enhancement for 4.621 If 0, then there is no visible indication a control is disabled.
If 1, then the control's appearance will be updated to show it is not available if the user tries to navigate to it but it cannot be changed because the prevent change flag is set, or an input/output condition is FALSE. The behaviour for qshow_disabled=2 is currently the same as for show_disabled=1, but it is intended that in future QAB will attempt to update the screen whenever the value of an input/output condition changes as a result of user input in an earlier field.
qsmart_position Y3 w Controls how Window access behaves when a non unique secondary index is used. Setting the variable TRUE will usually give better results. qsmart_position is initially FALSE to maintain compatibility with previous releases. If FALSE, then the Window will always highlight the first record with a matching key (assuming there is one)
If TRUE, then the Window will highlight an exactly matching record if there is one, and but fall back on the first matching record if there is no such record.
read_access | I3 | r | Obtain user access level | 0 99
return | I3 | r | Same as end_key | As for last_return_value
reverse_window | Y3 | w | Reverse the order in which records are displayed in a selection window
start_date | D11 | r | Date program was loaded
start_time | T5 | r | Time program was loaded
sys_clock | T5 | r | Same as clock
sys_date | D11 | r | Current date | Date in format specified in user preferences
there | Y3 | r | Indicates whether the record exists or not for the table containing the field. In a condition on a table, use table.role.there. In the formula for the field, you can use if there then .. | TRUE if record exists
today | D11 | r | Same as sys_date
user_language | I3 | r | Use as an additional key component for storing language specific information on the database | Returns the language number currently in use
user_name | U8 | r | Read user name
write_access | I3 | r | Obtain user write access level | 0 99


Database Management

Your QA Build program may access and update the database in which it resides. The manner in which it accesses it is determined by options you select in the Program Summary, Procedure Overview and Table Processing screens. QA Build supports the full range of database access and retrieval controls, including setting read/write access levels and validation of entries.

This section covers the following topics:

  • Database Opening Options
    • Exclusive Access
    • Read-Only Access
    • Private Database
  • Reading Records
  • Searching Tables
  • Updating the Database
  • Adding, Amending and Deleting Records
  • Securing Changes
  • Commit Frequency
  • Change Verify
  • Record Locking
  • Batch Processing
  • Multi User Access


Database Opening Options

Your database can be opened with three options:

  • Exclusive access to the database
  • Read-only mode
  • Private Database access

Exclusive Access

Exclusive access may be required in a multi-user system. This prevents any other user from opening the database. All programs accessing the database must be terminated before an exclusive access program will successfully load.

To set up exclusive access specify exclusive access in the Program Summary.

Read-Only Access

The database is opened in read-only mode by the Enquiry and Report program types:

  • Enquiry programs allow the user to see the records and perform record enquiries without updating the database
  • Report programs allow the user to print out records from the database, perform calculations and print results, usually with little user interation.


Private Database

The private database option lets the user access and update records in a private copy of the application database. The information is private because any changes to the database records made by the user are not accessible to other users. This allows information to be validated and confirmed prior to its incorporation into the application database. The changes made to the private database are not made public until the program explicitly updates the database in one of two ways:

Secure changes | A procedure uses the option to secure the changes
Save/Exit menu | A procedure of the type Save/Exit menu is used to save the changes

A Save/Exit menu prompts the user either to accept the changes made so far or to abandon them. The Save Changes option updates the records from the private database to the application database. The Abandon Changes option cancels the database update, so any changes are discarded.

The Esc option causes the menu to disappear with no action. One of the save or abandon options must be selected in order to exit.

If your program does not secure changes before exiting, and a Save/Exit menu is not presented, any updates will be lost.

The first figure below shows User A and User B adding records to the application database. The second figure shows the result of securing the changes to the application database.

Private Database

User A User B

User A adds 5 records, and User B adds 3 records. These remain private to the individual users until the records are updated to the application database.

Application Database - Public

To set up a private database:

  1. In the Program Summary answer yes to Private Database Update ?
  2. Set up one or more procedures to use the private database which do not secure changes. At the end of the processing, you will need one procedure to update the application database

To set up a procedure body to access and update the private database:

  1. In the Procedure Overview answer yes to Update database ? and no to Secure changes ?
  2. In the main table processing answer yes to Update main table ?

To update the application database with user confirmation:

Select the Save/exit menu procedure body type in the Procedure Overview. This selection causes the Save/Exit menu to be displayed to enable the user to accept or reject the changes

To set up a procedure to update the application database without user confirmation:

  1. In the Procedure Overview answer yes to Database procedure ? and Update database ? Answer no to Confirm updates ? and yes to Secure changes ?
  2. Set up the table processing

Abandon Changes

You have the option to throw away changes to a private database automatically, without user intervention. Do this by setting the standard variable abandon_changes to TRUE before or during a procedure which secures changes. This makes your program behave as if the user selected the menu option Abandon changes.


Reading Records

The way in which records are read from the database is determined by the access mode. The access modes for records on the main table are as follows:

Access Mode | Description
Already fetched | Process a record which has already been read from the database or has been set up in an earlier procedure body
Batch | Process a sequential group of records automatically. Use this in a repeated procedure – the next record in the selected direction is read automatically
Normal | Read a record when all the key components have been filled in during the procedure
Line (Auto and normal) | Combine normal and batch access for use in repeated procedures, but Window access is the preferred method
Window | Display a list of existing records for the user to select and update records. If new records are allowed, an Add new record option will be offered. This option is useful for processing a child table
Cleared window | As Window access (above), but clear the screen when a selection is made
Set up mode | Initialise fields for access in later batch procedures
Preview window | Display a group of records for preview purposes. For example, all the order lines for an order could be displayed before the order is selected for amending

Reading a Group of Records

There are four ways of limiting the records that are read from the database:

Option to fetch record | Description
Condition on record | The condition specified must be satisfied for a record to be processed. Set this up in Main Table Processing
Check parent or Join parent | This will only fetch records with matching parents on the database. Select the Check Parent option in the Related Table Processing for the parent of the table
Parent already fetched | This limits the search to records that have already been selected in an earlier procedure. Select the Already fetched option in the Related Table Processing for the parent of the table
Formula on key field | For records where the user enters the key, the formula can be used to determine whether the record is fetched. Set this up under Field Processing for the key field


Searching Tables

You must ensure that your program allows for the possibility of a user entering as a key a record that is not on the database. When this happens, the program should offer a helpful alternative course of action. The following options are available and may be used singly or in combinations:

  • Tell the user that the record has not been found
  • Offer the next logical record in the sequence
  • Allow the user to scan through all the available records
  • Allow the user to select a record from a pop-up window

The behaviour of your program if the selected record is not found is determined by the search method:

Search method | Description
Exact match | Search for a record which exactly matches the key supplied by the application. If a record is not found, a message you set up for this situation is displayed
Scan if no match | Look for a matching record – if one is not found and other records are available, the next (or previous) record is displayed and the user may scan forwards and backwards in search of a record to select
Always offer scan | Similar to Scan if no match, except that the option to scan forwards and backwards is available even if a match is found. The matching record is displayed first
Next match | Looks for the record – if it is not found, the next record (in the numerical or alphabetical sequence) is displayed
Previous match | Similar to Next record except that the previous record (in the numerical or alphabetical sequence) is displayed
Multiple Choice | Looks for a matching record. If one is not found and if other records are available, a pop-up choice list of several records is displayed

The following additional controls are provided:

Control | Description | Example
Search Index | Specify which index to use for the search | Offer the records in customer name sequence instead of customer number
Search Depth | Specify the number of keys which must match – if you leave this blank, QA Build will select the number of keys automatically | If one key of a two-part key matches, the user can be offered the option to select a record on the second key. For example, if a matching order is found, scan through the order lines
Message if no record found | Inform the user when a record has not been found | Provide a message if there are no records on the database, such as Can't find matching customer record
Message when record found | Inform the user when a match has been found | Inform the user when a record already exists on the database (for example, Existing customer record)
Record Scan dialog | Inform the user to scan the records | Invite the user to cursor through the records
Map to display choice list | Set up a pop-up window for selecting a record – see on Multiple Choice Maps | Display a pop-up list of items and descriptions where the item code is not known


Updating the Database

If transaction and rollback logging were set up in the Console/Transaction Logging option in DFSETUP, a program updates the database in three stages:

  1. Post or Kill

    Each new or amended record is posted to a buffer and each deleted record is flagged as "killed". The buffer is usually a workfile with the extension .$$$ on disk. In a system crash changes that have been posted but not committed or secured are lost.

  2. Commit

    The records in the post buffer are checked. If the commit is successful, the records are written to the transaction log and database buffers in memory. The roll-back log buffer is updated with the "before image" of the record. Buffers are written to disk when the space is required for other purposes. In a system crash the last few committed transactions may be lost.

  3. Secure changes

    This stage secures the updates made by your program. The transaction log and rollback buffers are flushed to disk. In a system crash the rollback log can be used to restore the database to its state before the crash and the transaction log re-applies the transactions to the database. If the database is corrupted, the transaction log can be applied to an earlier version of the database to recover the changes made so far. See Securing changes for more information.

If you do not set up your program to use the Private Database facility, QA Build performs these actions at the following points in your program:

  1. Post or Kill

    Records are posted during the execution of a procedure body, when all the fields for a record have been processed

  2. Commit

    A commit occurs at the end of the procedure body, before any subsidiary procedures are executed. Records are checked using the validation set up for the database and using the validation mode in the table processing for the procedure body (see Table Processing, Related Table Processing and Additional Table Processing). For batch procedures, you can control the frequency at which records are committed by changing the value of the standard variable commit_frequency. Used carefully, this feature may give you a performance improvement

  3. Secure changes

    Changes are secured at the end of a procedure that sets the Secure changes flag in the Procedure Body Overview. For procedures with subsidiary procedures, the subsidiaries are executed before the changes are secured. If you do set up your program to use the Private Database, records are posted as above, but they are only committed and secured when a procedure body set up to Secure changes is executed. This means that a large number of changes can be stored and then saved or abandoned in one logical transaction. For further information see Private Database.

Whether or not you use the Private Database, you can prevent changes from being committed or secured at a specific point in your program by modifying the standard variable abandon_changes (or decommit).


Adding, Amending and Deleting Records

You may add, amend and delete records on the main table selected for your procedure body. Note that, if the user deletes a record, normally subsidiary procedures will not be executed (see Error Subsidiaries ).

The following controls in the Main Table dialog can be set up:

Controls | Description
Update main table | This option is particularly important for batch procedures which delete records. Yes: records may be added, amended or deleted No: records may be deleted, but not added or amended
Process existing records | This option determines whether records on the database are processed, in addition to newly created records Yes: records on the database are processed No: records on the database are not processed
Allow new records | Determines whether new records may be added
Allow record deletion | Determines whether existing records may be deleted

A record is regarded as a new record until it has been posted and re-read from the database

To set up record deletion for a batch update program:

  1. In the Procedure Overview answer yes to Database procedure and Update database
  2. Answer no to Update main table? and yes to Allow record deletion


Securing Changes

You have the option of securing the changes when updating a record to the database. This means that following a crash, every change up to the last record updated can be recovered, provided that rollback recovery is enabled.

If you do not secure the changes, the database can still be recovered, but the last few updates are likely to be lost.

As there is a performance penalty for securing changes, you are advised to secure changes selectively. In an interactive program, you may decide to do this at the end of every transaction. For a batch process, it may be sufficient to secure the changes at the end of a process.


Commit Frequency

Using the standard variable commit_frequency, you can control the number of times updates are made before the records are committed to the database. A batch program which updates very large numbers of records will often run more efficiently if this facility is used.

One repetition of a procedure body that updates the database is counted as one update. Where more than one table is updated in a procedure body, this still counts as one update.

Do not use this feature in interactive programs or where the auto-increment feature is used

Procedures that secure changes to the database always perform a commit, and so this flag will not affect them.

To set the commit_frequency flag:

  1. Add the standard variable to the field processing list for the procedure
  2. Use the formula to set its value to n, where n represents the number of updates to be made before a commit


Change Verify

The DP4 change verify facility prevents a record from being updated twice if it is in contention . If an update fails because a record has been updated by another user and your program does not provide a way to recover from the situation, the program will halt and any pending transactions will be lost.

You can use the change verify facility to handle an update failure in two ways:

  • Give an error message and continue
  • Provide a recovery routine in your program

If you choose to provide a recovery routine, you should test the variable fail_code which is returned after an attempted update. Use the value of the return code to determine whether the recovery routine is executed:

0 | Update successful
3 | Contention failure on one of the records
4 | Orphan check failure
5 | One of the records updated was locked by another user

The fail code is returned at the end of a procedure body which secures changes. In applications which use the private database, the code is returned only when records are updated to the public database.

You may display a fail message following an unsuccessful attempt to update the database, depending on your answer to the Suppress update fail messages question.

To halt the program if an update fails:

In the Program Summary answer yes to the question Stop program if update fails ?

To display an error message and continue if an update fails:

  1. In the Program Summary answer no to the question Stop program if update fails ?
  2. To provide automatic fail messages, set the Suppress fail messages question on the Program Summary to No

To provide a recovery routine if an update fails:

  1. In the Program Summary set the Stop Program option to No
  2. Add a procedure body to display a specific message and perform any recovery actions under the procedure that is likely to fail. The condition to enter should test the value of fail_code.


Record Locking in QAB

Record locking gives the user exclusive access to the record at run-time. If a record is locked, no other user may update it until the user who locked it releases the lock. Record locking is one means of avoiding contention.

You can find out more about how record locking works in DP4 and its advantages and disadvantages here but we advise you to use record locking sparingly. You can find out about some alternatives methods of controlling contention here (this is a link to some C programming documentation, but you do not need to understand C to read it.)

In QAB a record is locked when it is read from the database and only unlocked when the rpogram next secures changes.

You can specify whether a locked record can be read from the database. For a report program, you can decide to produce the report even if a record is locked (and therefore not available for update) or to allow the operator to retry or abort the operation.

To lock a record:

In the table proessing dialog enable the Lock existing record option

To prevent locked records from being read:

In the table proessing dialog enable the Disallow locked record option

To present a Retry/Abort option if a record is locked:

  1. In the table proessing dialog enable the Disallow locked record option In Main Table Processing answer yes to Disallow locked record ?
  2. Create a map for the retry/abort question using the Message Assignment menu.


Batch Processing

If your procedure body is intended to process a set of records without user input, you must set it up in a repeated procedure of type Batch using one of the batch access methods for the main table.

You can select some or all of the records in a table for processing:

  • Process all the records in the table
  • Process all records for which the parent has been selected
  • Process a group of records, starting from a pre-defined record

The batch/preview type is used to determine the direction of the search:

Batch Access Method | Use for
First and onwards | Processing all the records in a table. If parent records were selected, all the selected records are processed, starting with the first record
Equal and onwards | Processing all the selected records starting from a specified record
Equal and backwards | Processing all the selected records starting from a specified record and working backwards
Last and backwards | Processing all records in the table, starting with the last and working backwards. If parent records were selected, all the selected records are processed starting with the last record

For the Equal and onwards and Equal and backwards batch/preview type, you will need to add a procedure body that sets up the starting point for the search.

To process a group of records from the first key onwards or last key backwards:

  1. Add a repeated procedure for processing the child records
  2. Use the Batch (Automatic) access mode for the main table
  3. Specify First and onwards or Last and backwards for the Batch/Preview type

To process a group of records from a specified starting point:

  1. Add a procedure body to select the starting point
  2. Use the Set up mode access for the main table
  3. Specify the record to start from by setting up related table processing to fill in keys from parent tables or set up the key fields under Field Processing
  4. Add a repeated procedure for processing the records
  5. Use the Batch (Automatic) access mode for the main table
  6. Specify Equal and onwards or Equal and backwards for the Batch type


Multi User Access

If you are writing a program for a multi-user system, you must allow for contention, when two or more users attempt to change the same record at the same time. DP4 does most of the hard work. If a record is in contention, the transaction of which this record is a part will not be allowed to update the database and a fail code will be returned. You must determine how your program responds.

An Example of Contention

User A reads a record from the database, makes changes to it and then before it is updated to the database, user B reads the same record and also makes changes to it. The first of the users to finish making changes will be able to update the database, but when the second attempts an update, it will not be successful.

If you are developing programs to run on a DP4 special resilience system, using two or more servers, you will need to consult your documentation for an explanation of how contention is handled.

Two mechanisms are provided for handling multi-user access:

  • Change Verify
  • Record Locking


Maps, Mapsets and Languages

Maps define the appearance of your program's screens. They allow you to position text, edit fields and other controls (such as buttons and icons) on the screen in any colour and format you wish. Maps are introduced in the Introduction to DP4 here.

Maps are grouped into mapsets sharing a common mapset name or mapname. Each map has a different map number within the mapset. You can dind out more about how mapsets are used in QAB in Mapsets and QAB.

The table below summarises the various ways maps are used in QA Build:

Screen Layout Map | An optional map, defined in the Program Summary, which determines the position on the screen of messages and other maps used by the program
Main Map | The main map for a procedure body, defined in the Screen Design or Form Design dialogs, provides a large part of the user interface in interactive and other screen based procedures and the layout of a form for printing in print procedures.
Multiple Choice Maps | These are used to display list boxes from which the user is invited to choose a value or record. They are set up in the various Table Processing dialogs.
Message Maps | Messages and remarks set up in Table Processing are also stored as maps.
Help Messages | Help messages (for fields on maps) are not actually maps themselves but are stored and edited in a very similar way.
Function Key Area Map |
Window Access Maps |
Multiple choice maps |
Word Strings | Word Strings are used to avoid the need for hard coding text into formulas.

Languages

Each map can exist in multiple variants using numeric language codes: whenever a DP4 program runs it has a preferred language code (read from the user preferences) from which it will try to load its maps - if the map does not exist in the current language code DP4 will use the language 0 version of the map (you should always create this first).

As name suggests language codes are usually used to store different national language versions of a map (which is one of the advantages of using them in the first place). However you can equally well use languages for other purposes - you might use one language for creating a set of maps for running your application on a regular PC screen, and another set for running on a Handheld or Palm Top size screen. You could also create a set of maps with some fields missed out (for users with low security perhaps), or with extra help information for trainee users.

Map Editing

When you need to design a map, the Program Editor launches its companion utility, the Map Editor, which allows you to create the map (see Map Editing). This is usually done via either the Message Assignment Menu or the similar Map Assignment Menu.

You can also use the Map Editor to create help messages for each edit field (or any other control that may receive focus).


Mapset Selection

Mapsets and QAB

As discussed in Procedure Overview each procedure body in a QA Build program can use maps from two distinct mapsets: the "Main" mapset and the "Remarks" mapset. In principle you can use different mapsets for each in every procedure body, or you can use the same name everywhere.

Some people like to use the same mapset name for remarks throughout a system, because different programs will often need to display the same error messages, and using the same mapset may help you to provide a complete list of error messages in your system documentation, which may be useful both for users and support staff.

You may find the following table helpful when deciding what mapnames to use:

If... | Action | Result
The procedure only appears in the current program Example: A data entry map | Use the program name for the mapset (this is initally the default, though it may not be if you have changed the mapname in a procedure body further up the procedure tree.) | Map is stored with the program mapset
The procedure appears in several programs always using the same map Example: Pop-up maps that are used in more than one program to select a record | Use a mapname that is not specific to any one program | Map is stored with named mapset
The procedure shares maps with other procedures Example: Procedures that use "Already Output" maps | Use the same mapname as the procedure that displayed the map. (In fact this is not strictly necessary at runtime, but it will be confusing both for programmers and QA Build itself if you use the wrong mapname) | Map is stored with named mapset
The procedure is to be used in several programs with different maps Example: A procedure is used to display a report heading which is shared by several programs, but with slightly different text | Enter <blank> | Map is stored in the mapset inherited from the procedures above.

There are a couple of points to consider when using a blank mapname:

  • If you move or reuse a procedure with a blank mapname elsewhere in the procedure tree, ensure that the inherited mapset has correct maps for the procedure.

  • If you subsequently make changes to a procedure with a blank mapname in one place in the procedure tree, and change the map in some way, remember to edit the procedure again in its other locations and make any corresponsing changes necessary to the other maps.


Language Support

Because of the way in which QA Build programs are organised, it is very simple to change the language of the user interface. You do not need to modify or recompile the program itself. The alternative language affects only the maps on your database.

To add maps in a second language to your program:

  1. Run the Map Editor and select the mapset you wish to translate
  2. Select the number of the second language
  3. Edit each map in turn, translating the text as you go

To run your program in a new language:

  1. Change the default language in DFSETUP
  2. Run your program. The text will be displayed in the new language

To set up your program to run in more than one language:

  1. If you have not already done so, add an option to your program to prompt the user to log on (see under User Access and Program Calling Facilities )
  2. Add a new user to your application database, and select the new language
  3. Run your program, and log on as the new user – the text will appear in the new language


Message Maps

Messages are set up as maps in much the same way as any of the other maps used in a QAB program. The only differences are:

  • Messages can be stored in a separate mapset from other maps
  • QA Build can automatically generate some messages for you

Messages are set up under Main Table Processing, Related Table Processing, Additional Table Processing, and, optionally, when a condition is not satisfied. The Message Assignment menu pops up wherever you specify a dialog, error message, question or condition on any of these screens.

QA Build can generate an appropriate message for your table processing if required. Two types of messages can be generated:

  • Specific Message: This is a message which refers to the table by name. For example: Add this Customer record?

  • General Message: This is a message which can be used by all tables. For example: Add this record?

It is quite easy for the number of messages to proliferate unduly if you use the Specific Message facility a lot. You should re-use messages where possible by selecting a suitable existing message.

You can edit a generated message in the same way as any other map, but if you would like to change the way a particular type of message is generated you can do so by editing the map it is based on. For further information see Automatically Generated Maps.

For messages you create yourself, such as error messages on a condition failure, you will usually need to add a button or a simple edit field your map to ensure that the message stays on the screen until the user has pressed a key. This is not always necessary, for example in error messages for input fields, but you may well want to use a button anyway as otherwise the user may not notice the message.

To generate a message automatically:

  1. Select the Specific message or General Message option.
  2. Use the Edit option to view what is in the map if desired.

To enter your own message:

  1. Select the Edit option to enter or modify the message.
  2. For interactive procedures, add a button or input field to the map.

To (re-)use an existing message:

  1. Select option 1.
  2. Enter the number of an existing message map or use the prompt button or press <Ctrl+P> to review existing maps.


Automatically Generated Maps

The maps and messages generated by the Program Editor and AutoQAB (when you select General or Specific message) are generated from maps 700 to 743 of the PROGMAKE mapset on the system database. If you want to customise or translate these message maps into another language, you only need to change the system maps.

You can generate maps for more than one language by using the DP4 language facility. Add maps 700 to 743 for each of the languages you are using. To generate messages for more than one language, log on as a user in the next language and re generate the maps.

The maps generated by AUTOQAB can be modified in the same way, except that it is not possible to generate maps automatically for more than one language (unless you have translated your database field headings using MAKEDB). For further information, refer to the section Automatic Program Generation.


Multiple Choice Maps

Use multiple choice maps where you wish to offer the user a list of fields for selection. Multiple choice maps are defined where Window or Cleared window is specified for the table access mode and where the search method is Multiple choice.

Set up a multiple choice list by setting up the following maps:

Display multiple choice list | You specify the position of the choices in the pop-up selection. Add a local field for each line of the list. If the number of lines in the list could exceed the number of local fields add a field number 100 to permit the display of scroll bars: (100) (1) (2) (3) (4) (5) (6)
Display fields from record | You can select one or more database fields to display in each line of the multiple choice list:
Select map to attach to | You can specify a map to attach the multiple choice list to

To set up a multiple choice list displaying the key field:

  1. Select Map to display choice list in the Table Processing screen for the table
  2. Add a map, and set up a database field for displaying the item. Duplicate this field and position the fields on the map
    Add a field number 100 (25 characters in length) for displaying the more above/below message
  3. Set up the rows and columns on the Map Properties dialog to ensure that the cursor keys work intuitively

To set up a multiple choice list displaying more than one field:

  1. Add a Map to display choice list as above
  2. Use the local field option when specifying the position of the field, and make the size of the field large enough to accommodate all the fields you wish to display from the table
    Add a field number 100 as above
  3. Select Map to display item in list in Table Processing
  4. Add the fields that you wish to display from the table
  5. Set up the rows and columns in Map Properties dialog to make the cursor keys work intuitively

To select a map to attach to:

  1. Select Map to attach choice list to in Table Processing
    Specify the number of an existing map which will be displayed at this point in your program
  2. Add a field numbered (100 + the number of the choice list map) on the map to attach to

If you wish to use a multiple choice map for a non input field that is set up using a formula, you must set the search depth to the number of keys that must match. If you do not set the search depth, the multiple choice list will not be displayed


Window Access

If records are to be amended or deleted in a window, you must select Window or Cleared window access mode for the main table processing. The map to define the window is set up as a multiple choice map.

Where a record is to be selected from a window, and modified in the same procedure, you will need to set up a main map. If the changes are to be made in the window itself, specify the number of the Map to display item in list as the main map.


Message Assignment Menu

The Message Assignment menu appears whenever you have the option to set up a message or simple Yes/No OK/Cancel type dialog in a QAB program.

You can find some hints on using this menu in Message Maps.

The menu contains the following options/facilities:

1 Message Nr

Select the number of the message to be used here. You can view all the maps in the current remark mapset, allowing you to share messages that are required in more than one place.

2 Delete a map

Select the number of a map to be deleted. Caution! Do not delete a map unless you are certain that it is no longer used by any program or procedure. No warning is given if you delete a map that is used elsewhere. You can use the Weed facility of QABUTIL to automatically delete maps that are no longer used anywhere.

3 No message

Select this option if you do not want to use a message here. You will be warned if the absence of a message will affect your program significantly.

4 Specific message

Select this option to create a specific generated message, for example Existing time sheet record.

5 General message

Select this option to create a general-purpose message, for example Existing record.

6 Copy map...

Select this option if you want a copy a message used elsewhere. You may copy maps from any mapset into the current mapset. You may assign the copy a new number. The copy is independent of the original.

7 Edit map...

Select this option to edit the map. This option starts the Map Editor application and loads the selected map number into it. You may edit the text and fields of the map and may also set cosmetic properties such as colour.

8 Edit/Continue [X]

Select this option to return to your previous location when you have made the required changes to the message.


Map Assignment Menu

The Map Assignment Menu works just like the Message Assignment Menu except that it does not have any options to generate a map automatically. Refer to the Message Assignment Menu for a more detailed explanation of the options available.

It is displayed whenever you get to a point in your program where you can display or use a map from the current main mapset.

You use option 1 (Map Number) to choose a different map number from the default for a new map, or to reuse a previously created map, and option 7, to enter the Map Editor.


Map Editing

The Map Editor allows you to position text and fields on a map. The main components of the Map Editor are:

Map Editor Screen | This is opened for you automatically when you ask to edit a map. You can use it to add new controls to a map, and move, size, edit or delete existing ones.
Map Properties Dialog | It is very important to set up the Map Properties correctly. In particular you need to set the map rows and columns correctly if you want to be able to navigate round the fields in an input map. (See Cursor Movement in Maps.) You should also select the map type appropriately as this will simplify the provision of a consistent user interface.
Control Properties | The control property dialogs allow you to set up fields on a map, modify the size, position and colour of a field, and set up some field validation and formatting information
Help Editor | The help editor allows you to set up a pop-up help message for each field. Save your map, then select the appropriate control and then start the Help Editor from the Edit menu or by pressing <F9>.

You can open and edit other maps and databases if you desire while you are in the Map Editor, and access all its other facilities. When you have finished editing maps close the Map Editor and the program editor will reappear. It is a good idea to save your work quite regularly when using the Map Editor. You should also use the Integrity Check option of the MAPMAKE program from time to time.

You can also edit maps from outside the Program Editor as described here.

Unfortunately the Map Editor is a completely different program depending on whether you are using a 4.5xx or 4.6xx release of DP4. The instructions on this page refer to release 4.6xx, and currently (rather old) only printed help is available for release 4.5xx.

Map Fields

Although adding fields to a procedure's main map does not in itself cause any processing to be generated, it is certainly the case that the properties of the fields on the map have a strong influence on what is possible in field processing. Also maps control many aspects of processing that until you are familiar with QAB you might expect to have controlled in the field processing itself. The following table sets out some of the key influences fields in a map have on the field processing.

Setup Screen | Feature | Description & Comments
Map Properties | Number of rows Number of columns Column Major | Enable intuitive cursor movement between input fields on a data entry screen. See Cursor Movement in Maps. If this is not set up correctly your program may not work as you expect.
Control Properties (General Tab) | Field Nr | Inputs Fields must be processed in field number sequence during field processing, so make sure fields are numbered logically in your map. For example fields used to define the record to be processed (key fields) must come first.
Edit/Checkbox Properties (Data tab) | Field type Name | Edit and Checkbox controls can be associated with database fields. Paticularly for edit fields you should generally ensure the field processed in field processing matches the field declared in the map.
Edit/Checkbox Properties (Data tab) | Display length Depth Scale factor | These properties should generally match the corresponding properties of the fields processed in the map field (depth -1 should equal high-low occurs). It is acceptable to use a different length except for character type fields - for these using the correct length is important, though for an output only field you may truncate the length for a single occurs field or an edit field validated with the correct database field.
Edit Properties (General tab) | Clear First | If this is property is set then the field will be set to the default value (as determined by the database default or the Yes then the default is shown for database fields, and local fields are shown as blank. If this is No, the last value is displayed
 | Auto Enter | Move to the next field when the field is full
 | Null Banned | Do not allow a null value for a field
Edit Properties Access tab) | Read access, Write access | Define the access level a user must have to Read/Change the contents of this field
Edit Properties Colour tab) | Colour scheme | Select the colour scheme for the field

This is not an exhaustive list of the features that can be controlled from the Map Properties and Edit Properties dialogs. For further information see the Map Editor help


Editing Maps Outside QA Build

The instructions for editing maps outside QAB are different depending on whether you are using a 4.5xx or 4.6xx release of DP4.

4.6xx

You already have access to the full range of MAPEDIT facilities when you run it from QAB. However there are some additional useful facilites, that are currently built into another program called MAPMAKE, which is not accessible from the standard DP4 menu until release 4.621 (In 4.621 Select Screen and Form Utilities and then Utilities).

MAPMAKE has the following additional facilities:

  • Deleting maps from more than one language at once
  • Copying mapsets from more than one language at once
  • Printing maps - either in MAPPOST format for transferring to other databases, translator format, for translation of maps into other languages without the use of MAPEDIT, or documentation format.
  • Making change files (minimal MAPPOST files for distributing changes to a map to live systems or comparing two versions of a database)
  • It contains an Integrity check that validates the map information. It is a good idea to run this from time to time, as it can help prevent maps being lost through information becoming corrupted. (Obviously this should not really be necessary, but software bugs, or "hacking" the map tables with BROWSER, have caused problems like this before now.)

You can select maps to edit using MAPMAKE, which then starts MAPEDIT much as it is started from the Program Editor. You may find you prefer to use MAPMAKE than MAPEDIT directly as it guides you through selecting maps to work on.

4.5xx

You can use the Map Editor outside QA Build. Either select the map editor from the Other Facilities section of the DP4 system menu, or run MAPEDIT.

When you run the Map Editor outside QA Build, it offers the following additional operations:

  • Deleting maps
  • Copying mapsets
  • Printing maps (as for 4.6xx)
  • Making change files (as for 4.6xx)
  • Setting up maps in other languages


Menus and Function Keys

Users of QA Build programs select options either from a menu or by using function keys (perhaps keys on a special keyboard designed for the application, or perhaps buttons on a touch screen). Menus are preferred by newer users because they make the available options clear. Function keys provide shortcuts for experienced users. When you create a QA Build program, the setting up of menus and function keys is very similar. In the Program Flow Diagram the procedure providing the choice of options is distinguished by an M below it; its subsidiary procedures define the options available.

A menu procedure must have one of the following four procedure types:

Procedure Type | Function
Menu/switch | This procedure type is used for simple menus and for branching on a computed value. See Pop-up Menus
Combined menu/entry | This procedure type allows the use of function keys to select a menu option on a data entry screen.See Function keys with Data Entry
Action bar menu | This procedure type provides an action bar with drop down menus. See Action Bar Menus
Hot key setup | This procedure type sets up hot keys which can be used in any data entry or window access screen as required. The use of hot keys is described in Hot Key Selection

There are two main steps to creating a menu or function key selection in your program:

  1. Add a menu procedure to your program using one of the four types of menu above (instructions are given for each on the relevant page). No field processing is required in menu procedures, although you may process non database fields if you wish.
  2. Add the subsidiary procedures corresponding to each menu option. (You will need to specify the option number in the link edit dialog for the subsidiary). Option numbers are different depending on whether you are using an Action Bar menu or one of the other menu types - see the pages for each menu type for details. You can of course invoke any procedure type allowed by your program as a menu option. One of the most common procedure types used in menus is "Run Another Program". This type of procedure can be used for calling other QAB programs or overlays, or running a completely different program such as a Web Browser. This type of procedure can also be used for various "special" functions. See Running Programs & Special Commands for further details.

Increasing Flexibility

The discussion above may have given you the idea that function keys and menus are mutually exclusive. This is not really true. It is perfectly possible to create your program in such a way that an experienced user can select actions using function keys, while for a user who prefers to use a menu the menu will be displayed for him to select an action that way. To create this type of program you will need to understand something about the internal QAB variables that you can access in a procedure's field processing to control this aspect of the user interface.

This section covers the following topics:

  • Menu Options
  • Use of Menus for Program Control
  • Standard Message Menus


Pop-up Menus

This is the menu style used throughout DP4 for displaying menus. The options appear as a list in a pop-up window that overlays what is on the screen.

To set up a pop-up menu:

  1. Add a procedure with a procedure body of type Menu/switch specified in the Procedure Overview

  2. Using the Screen design dialog:

    1. Use the Map Editor to create a map to display the menu. The details will depend on whether you are using 4.5xx or 4.6xx:

      • In 4.5xx enter a title for the menu (if desired), and then add the text for the options. Then add one field for each menu option, positioned over the text for the option, and numbered appropriately - field 1 for the first option, field 2 for the second and so on.

        Typically the options are arranged in a vertical line, with a blank line between each if there are not too many options, and the fields are all made the same width, beginning one character position before the option text and ending one character position after the widest option. You can use the mnemonic editor within the field editor to assign a mnemonic for each option. We recommend that arrange the text and fields so that the fields begin in column 2 and the option text begins in column 3. Use the map properties dialog to draw a box around the menu and set the rows and columns an dwhen saving the map ensure that at least one space is left after the end of the fields

      • In 4.6xx create a text control for each menu option, and give each text control an associated field number, using field number 1 for the first option, field 2 for the second and so on. You can assign a mnemonic for a menu option by prefixing the appropriate character with a &.

        If desired you can also use icons, buttons or bitmaps to select options from the menu. These should be numbered in the same way as text options: if you like you can use the same field number in both an icon or bitmap and a text option, to give the user two ways to select the option.

        Before saving the map, remember to use the Map Properties dialog to give a title to the menu if desired, and, more importantly, to set the rows and columns (typically you set the columns to 1, and the rows to the number of the highest option).

    2. Select the Pop up window or Attach to another map output method to display the menu, to prevent maps already displayed from scrolling up to make room for the menu.

    3. Enable the clear before subsidiaries option to clear the menu once the user has selected an option.

  3. Return to the Program Flow diagram and add the subsidiary procedures as described in Inserting Procedures. Usually you will need one subsidiary per option. The option numbers you use in the dialog must be the same as the ones used in the map for the menu.


Action Bar Menus and Pull-Down Menus

A pull-down menu is a list of options that drop down from an action bar:

To add pull down menus to your program:

  1. Add a procedure with a procedure body of type Action bar menu specified in the Procedure Overview

  2. Using the Screen design dialog:

    1. Use the Map Editor to create maps to display the action bar and the pull-down menus. There should be one map for the action bar and one for each pull-down menu. Each pull-down menu should have as its map number action_bar_mapnr+option_nr where option_nr is the field number assigned for the option that opens the pull-down. Another field, numbered 100+option_nr, is needed in the action bar menu to specify the position for the pull-down.

      In both 4.5xx and 4.6xx the map editor has special features to simpify this process, but the details will depend on which release you are using:

      • In 4.5xx:

        1. Add a map to define the text and action bar options, proceeding more or less as for creating a pop-up menu, but arranging all the options horizontally on a single line.

        2. (Optional). If you would like a line below the action bar positition the cursor on the start of the line below the action abr options. From this position draw a box one row deep that runs the length of the action bar (usually this should be the full screen width). Then, still in box mode, press <Alt+L> to raise the line to the correct height.

        3. For each of the action bar fields, select Pull down from the field options. A map will appear to allow the definition of the options for the pull down menu. Edit the maps as described as for creating a pop-up menu, except that for pull-downs you should normally not leave a blank line between the options - the terminal manager will arrange for the options to be spaced correctly automatically.

      • In 4.6xx:

        For creating a new menu, rather than creating the maps directly you can use the special menu editor. This is designed for creating action bar menus. This is opened by default if you are using 4.620 (5) or later.

        If you open an existing action bar, the Map Editor may or may not open it as a menu, depending on how the map was created. If the map is opened as an ordinary map, and you would like to edit it with the menu editor then close the map, and use the open menu dialog to open the map as a menu.

        In this case you will probably need to check the "All Maps" button in the Open Menu dialog. Beware that this may not preserve all properties of your menu.

        If you want to create help messages for your menu options you will need to open the map as an ordinary map and invoke the help editor on the controls as required.

      You should not normally have more than 15 options in the action bar menu itself (numbered 1 through 15). Similarly each pull down should have no more than 15 options. The reason for this is that internally the "real" option number is 16*option_nr of pulldown + option_nr on pull down. You can try using more options if you like, but will probably encounter problems.

    2. An Action bar menu is usually displayed using the Top of Screen output method, and the Clear before subsidiaries option is not selected.

  3. For each procedure that is selected using the pull down options, edit the Option number in the Link Edit dialog using the following format: action bar option_nr/pulldown option_nr. For example, to set up the 2nd option on the pull down for option 1 of the menu bar 1, in Link Edit you would specify the option number as 1/2.


Function Keys with Data Entry

You can set up a data entry procedure to act like a menu procedure, where options are selected using function keys. For example, on a sales entry screen, function keys could be used to repeat an item sale or calculate a discount.

To set up a data entry screen for use with function keys:

  1. Use the Combined Menu/Entry procedure type in the Procedure Overview
  2. Set up the map and fields for the main data entry screen as for an interactive procedure. Frequently only one general purpose field is used, and the function key is used to select the meaning of the input - for example a user might terminate an input F2 to indicate a quantity, or F3 to indicate an item code. You may want to include field processing for the endkey or the synonymous return field to set a "default" function key for input terminated without a function key.
  3. Add the subsidiary procedures for the function key options. In the Link Edit dialog, number the menu options according to the function keys which select them (1 for <F1>, 12 for <F12> and so on)

To set up a function key display area:

  1. Select Function key area map
  2. Create a new map, and add a line (or lines) of text to define the function keys. The function key map will display in the default position at the bottom of the screen (see Screen Layout Map if you want to change the display position).

For a more detailed description of function key area maps see Function Key Area Maps.


Hot Key Selection

Hot keys work in the same way as function keys, except that when they have been set up, they can be used to execute the corresponding procedure from any point in your program. At the end of the procedure, control returns to the point at which the hot key was pressed.

Hot keys differ from function keys defined in combined menu/entry procedures in the following ways:

Hot Keys | Function Keys with Data Entry
No map | Has a map for data entry screen
Cannot be selected in the procedure in which they are set up | Can be selected in the Combined Menu/Entry procedure which sets them up
Can be selected in subsequent Interactive, Combined Menu/Entry and in procedures where the access mode on the table is Window | Can only be selected in the Combined Menu/Entry procedure in which they are set up
Can be switched off by defining a Hot key set up procedure with no subsidiaries | Function keys are not available outside the Combined Menu/Entry procedure
Selecting a function key causes the hot key procedure to be called, with a subsequent return to the point in the calling procedure where it was selected | Selection of the function key terminates the procedure in which it is selected, followed by the execution of the function key procedure

The following guidelines should be observed when using Hot keys:

  1. Hot keys are not available in called or chained QA Build programs
  2. You must save and restore variables that are used in your program where hot keys can be used
  3. You must also ensure that the appearance of the screen is restored. For this reason, the best policy is to use pop-up maps which are cleared for your hot key options
  4. Hot keys are available on entry fields and window access procedures, and not from menus, multiple choice lists or error messages
  5. You should not set the force_option variable before a Hot key set up procedure
  6. Make sure you do not introduce recursion to your program

To use hot keys in your program:

  1. Set up a procedure of type Hot key setup
  2. Add the subsidiary procedures corresponding to each of the options:
    Use pop-up maps for screen display for the subsidiary procedures to ensure the original state of the screen is restored

  3. In subsequent procedures in your program in which you want the hot keys to be available, set the Translate func keys flag to Yes for the Map Properties of the procedure's main map


Menu Options

For each item on the menu, you must define a link to the procedure which performs the required actions. Set these up by inserting subsidiary procedures of the menu using the Procedure Link Editing dialog.

The Option number must correspond to the field number of the option on the menu map and should correspond to the option number appearing on the menu, if one is used. The optional Next option facility allows you to select the option which is highlighted when the current action is completed.

If you specify a Condition to enter, this will be evaluated when the menu is displayed. If the condition is not satisfied, the user will be unable to select it. By default, options that are not available are displayed greyed out (disabled).

Greyed Out Options

If you do not want an option to appear greyed out when the condition to enter is not satisfied, you can adjust the standard variable no_grey. The condition to enter is evaluated in two places:

  • When the menu is first displayed
  • When an option is selected

The variable no_grey will be set to TRUE for the first evaluation of the condition, to prevent greyed out display if the condition is not met. For the second evaluation, no_grey will be set to FALSE, and so the condition to enter is evaluated in the normal way.

To prevent greying out:

  1. Select Condition to enter in the Link editor for the option
  2. Press E to edit the formula
  3. Amend the condition to enter as follows: no_grey OR old condition

Default Menu Option

Usually when a menu is first displayed, the first option on the menu is the default option and so is highlighted. The option that is highlighted on return from a menu option (assuming the menu procedure is repeated) is set up using Next option in the Link Edit screen. You may, however, set up a different default option when a menu is first displayed.

The system variable next_option can be set up to indicate the default option. Set this up before entering the menu procedure.

To select a default option for a menu:

  1. Add the variable next_option of type I to the list of fields to be processed in an earlier procedure
  2. Select Formula and for the formula enter the number of the required default option


Use of Menus for Program Control

Menus provide a powerful means for defining the logic of your program. Wherever you want the flow of your program to follow a choice of paths depending on the user's response, you may use a menu procedure.

If you make the menu procedure a repeat procedure, the user can continue making selections from the menu until the menu is exited, by pressing <Esc> or a menu option that exits from the menu procedure (because you have a condition to repeat that tests return).

Switching Between Subsidiaries

You may sometimes want to select one of several branches at a certain point in your program without using one of the interactive menu types described so far. For example, in a hierarchy of menus an experienced user might be able to bypass the menus by entering a transaction code at the top level of the menu.

The easiest way to do this is to set the force_option variable described below in the field processing of a procedure that precedes each menu. When force_option is set menu and action bar menu procedures will short cut (bypass asking the user to select an option) and automatically return the value of force_option as the selected option. force_option is then reset to 0.

You could use witch between different actions automatically betwee that is, depending only on the results of previous processing. To do this, add a set of subsidiary procedures below the previous procedure and give each a condition to enter to determine whether it is executed.

The standard variable return is especially useful for selecting one (or more) of several possible subsidiaries. In the previous procedure set up the field processing to apply a meaningful value to return. Then set the condition to enter on each subsidiary procedure so that only the appropriate procedure(s) is (are) executed.

Providing an Escape Option

You may create your own escape procedure to ensure that the user performs a particular action before leaving the menu. Set up a procedure having the same option number as the escape option on the menu procedure. This procedure will be executed when the user presses the <Esc> key.

Example: Ask for confirmation before leaving menu

The following procedure tree uses a procedure that allows the user to escape the menu following confirmation:

The procedure body of MENU is set up with the escape option and condition to repeat, by setting the Escape option to 8 and by setting a condition to repeat in the Procedure Overview.

The local field exit_menu is tested in the repeat condition:

not(exit_menu)

The procedure body of CONFIRM is used to check whether the user really wants to leave the menu – the response sets the local field exit_menu.

To set up an escape procedure:

  1. Set up an escape option in the menu procedure
  2. Set up and initialise a local field for use in the repeat condition
  3. Add a condition to repeat which tests the local field
  4. Add an escape procedure
    Set the Link Option number to the escape option number you set up for step 1
  5. Set the local field (see step 2) to the value tested for in the repeat condition of the menu procedure

Test for Option Number

Two standard variables are provided which allow you to test for the last selected menu option. These are:

To test the value of the option selected in a pull down menu, you need to use the following calculation:

(16 * menu number) + pull-down option

For example, where an option is set up on the Link edit as 3/2, for the second option on the third menu, the calculation would be:

(16 * 3) + 2

This would give the corresponding pull down option for the menu.

Variables used with Menus

The default menu option described under Menu Options can be used to highlight an option. Sometimes, however, you may force a menu option to be executed:

force_option | Set this variable to force an option to be executed in the next menu (or when the current menu is repeated)
end_key return | These are two different names for the same variable. In a Combined menu/entry procedure, set this variable to set the option to be selected as though the user had pressed a key. For example, in a POS system, a combined menu entry procedure you might add field processing for end_key that examines the input source, and automatically treats input from a bar code scanner as a request for an item sale, and input from an MSR as a request for payment using a charge card. You may also sometimes want to reset end_key to 0, particularly in interactive proecdures or as part of hot key processing, to prevent a short cut in the next menu.


Standard Message Menus

At various points in a data entry program, messages regarding database access and updates will be displayed. You may set these up either as simple text messages with Yes or No responses or as pop-up menus offering choices. For example, the question:

Add this Customer record ? Yes

is equivalent to the following default menu:

No matching Customer record

Select action:
1 Insert
2 Search
8 Rekey

To set the default message style to menus, answer Yes to the question Ask questions with menus in the Program Summary .

Confirm Updates

You may wish to offer the user a confirm menu before the user's changes are updated to the database:

1 Save changes
2 Rekey changes
3 Abandon changes

To set up a confirm menu answer Yes to the question Confirm Updates? In the Procedure Overview.

This confirmation is suppressed if you have SAA or Windows user preferences, because the user will only leave a data entry procedure by pressing an OK button or the Enter key. With "traditional" user preferences the user can leave a dialog by just by tabbing off the last field by pressing Enter.

Save/Exit Menu

This is a standard procedure type which causes the menu below to be displayed. This is used for controlling Private Database updates.

1 Save changes
2 Abandon changes
__________________
Esc Cancel


Running Programs and Special Commands

QA Build provides all the facilities needed to control access to a system, through the use of logons, passwords and a menu. A menu can be designed to provide options to call other procedures, as described in the section on Menus and Function Keys, and also to make use of the facilities to control access to the system using DP4 usernames and passwords, to call other QA Build programs and call non QA Build programs. If you wish you can completely bypass menus built using DP4 Menu Pages. QAB menus offer more flexibility than ones built for DP4 itself. The only disadvantage is that there is less memory for running other programs, though this is unlikely to be a consideration except on very old systems or perhaps hand-held devices.

The facility to call other QA Build programs is also useful in splitting up a QA Build application which would be too large or cumbersome to exist as one program. For further information on handling large programs see Overlays.

The Program and Special Command facilities are all accessed through the use of procedure bodies of type Run another program.

The Special Command facilities include user log-on and log-off, and facilities to change passwords and per-user preferences such as colour schemes or language. There are also some facilities that are not specifically related to users.

To invoke a command:

  1. From the Procedure Body Editing menu select Procedure overview
  2. Select a procedure type of Run another program
  3. Enter your command in the Command field. Commands divide into three types, the syntax accepted for each type of command is described in detail on the following pages:
    • Calling a QA Build program
    • Calling a non QA Build program
    • Calling a Special Command
  4. Complete the rest of the dialog as required: most of the other entry fields are disabled.
  5. Select Screen/Form Design from the Procedure Body Editing menu.
  6. If your command includes any %1 to %9 options create a map with local fields for each option.
    If your command (for example %LOGON) gives you the option of using your own map, you may wish to create one. See the instructions with the specific commands.
    Otherwise, select No map
  7. Complete the rest of the Screen/Form Dialog as required.


Calling QA Build Programs

The facilities for calling QA Build programs can be used for your menu options. However, they can also be useful when your program size exceeds the size limit and you need to split the program into smaller programs which call each other. The QA Build program you call must belong to the same database.

If you are considering how to split up a large program, you should be aware that calling a program takes longer than continuing to the next procedure. Hence, you should split your program where the program flow is minimum.

There are several options you can use that control the degree of interaction between the calling and the called programs. For example if a program is called as part of a menu system it will probably be fairly independent of the calling program, and it should not unduly affect the caller, while if for reasons of size you have had to convert a call to another procedure to a call to another QAB program with that procedure as its topmost procedure, you will want to be able to call the QAB program in such a way that the behaviour is unchanged.

This command calls a QA Build program on the system called CREDIT. It is called as an overlay: %CALL CREDIT %OVERLAY

Although QA Programs can read options specified on the command line, this applies only to programs called directly by the operating system. You cannot pass a command tail into a called QAB program using the %CALL facility. If it is imperative that you pass a command line option to another QAB program it must be called as though it were a non QAB program, for example:
progrun -db %db% -prog another -my_tail value

Commands

There are two methods of calling one QAB program from another, depending on whether you want to resume execution of the first program when the second has finished.

%CALL

Call the QA Build program which follows this command and return when it has terminated.

If the called program performs a %LOGON, the original user will be restored when the called program terminates.

%CHAIN

Call a QA Build program but do not return.

If program 1 calls program 2 and that chains to program 3 control will still return to program 1 when program 3 terminates.

Use this option with caution; repeated use may eventually cause your program to run out of memory. In particular avoid repeatedly chaining round a loop of programs.

Command Tails

The following command tails can be used with %CALL and %CHAIN to control the interaction between the caller and called programs.

%field_name%

Substitute into the command the contents of the named character field.

For example, your program may define a local character field called NEXT_PROGRAM. The command for the procedure is %CHAIN %NEXT_PROGRAM%. If the contents of NEXT_PROGRAM is "SALESREP", the command executed by the procedure will be %CHAIN SALESREP.

The substitution is made for %CALL and %CHAIN commands, and for passing parameters to non QA Build programs. As the substituted value is not parsed, you cannot substitute a %command or %command tail.

%NOLOG

Suppress a line being written to the system log.

When a program is called from a DP4 menu, an entry is made in the system log, SYSLOG.PRN (not for programs called using %OVERLAY).

%NOSAVE

Do not save data in calling program (for %CALL only).

If you do not use %RECEIVE, the data in the calling program will be recovered when the called program terminates. Avoiding this saving and recovering routine may give a minor performance improvement.

%OVERLAY

Force called program to behave as part of the calling program (for %CALL only).

The effect of the two programs behaving as one includes the following:

  • Data is completely shared.
  • A logon in the called program is not lost on return to the calling program.
  • A logoff in the called program restarts the calling program.
  • The called program inherits the caller's current print device, unless the calling program does not have a print device and the called program's program summary does initialise a print device.
  • The screen layout map, if there is one, is not displayed unless the %SAVESCRN option is specified. In general an overlay program should specify the same layout map as the calling program in the program summary.
  • There is normally an implied decommit whenever a QAB program terminates to through away any uncommited transactions. This is supressed for programs called with %overlay allowing a single transaction to be split over overlays.
  • A program termination (caused by %SYSTEM command, an error or through the user terminating the program with <Ctrl+S> <Ctrl+C>, <Alt+F4> etc) in the called program terminates the caller

This option includes the effect of %SEND, %RECEIVE and %NOLOG.

%RECEIVE

Receive data back from the called program (for %CALL only).

This data includes data in tables which have the same role names in both programs and local fields which have the same names in both programs.

%SAVESCRN

Saves the state of the screen and restores it when the called program has terminated. The maps already displayed by the calling program will not be accessible to the called program.

%SEND

Send data in the calling program to the called program.

This data includes data in tables which have the same role names in both programs and local fields which have the same names in both programs.


Calling Non-QA Build Programs

You can call any executable program, or other command known to the operating system. For example, to run a DP4 report on the current database you could enter this command:
repprint -db %DB -prog myrep

Commands

Enter a command as you would at the operating system command line, except that if a command requires a % enter %%. A single % introduces one of the special command tails. When running the command the current path will be searched as determined by the operating environment. On Windows CE, there is no path or current directory and commands without a directory specification will be searched for in the directory where DP4 is loaded from.

Command Tails

You can specify any command tails (program arguments) exactly as you would at a command prompt. The total length of the command line is limited to 128 characters.

%1 to %9

Where command tails are to be entered by the user at run time, specify each command tail using %1, %2, up to %9.

You will need to set up each of the command tails as fields on the main map of your procedure. Number the fields for each of the command tails, 1 9. This allows the command tails to be entered when the program is called. The evalues entered by the user will be substituted as appropriate.

Rather than relying on this facility you may prefer to allow the user to enter comamnd tails in earlier procedures and then use the %field_name% facility described below. This would be useful if the value is selected using a multiple choice map or some other menu.

%BATCH

DP4 first tries to execute the command as a normal command. If the program cannot be executed, DP4 will ask the operating system shell to run the command. As the name suggests this is typically used to launch batch files or shell scripts. However, it can be used to launch any type of command launchable from the shell, such as internal commands.

%CLOSEDB

Close the current database before executing the command. The database will be reopened on return. This is useful if you want to run a DP4 program that requires exclusive access to the database, or any program that accesses the database directly such as a backup utility.

%DB

Current database. This would normally be used with the DP4 command tail −DB. Thus, −DB %DB.

%DOCUMENT

This command tail is only used in Windows. It informs DP4 that the program should be run using the ShellExecute() command. The command line should consist of a single "document" name, at the operating system will open it using the users preferred program and default "open" command for opening that type of document. The "document" can be almost anything in fact. For example you could use the command mailto:%name% %document to open the users preferred mail composer to send an e-mail to the address given in the name variable.

%<field_name>%

This command tail is used to substitute the contents of the named character field. This allows you to pass the contents of a field as a parameter to a program. For example, if you define a local field dir, which contains the text "b:", you could call a program as follows:

dbrestor %closedb -db %db -disk %dir%

The contents of the field dir is substituted at run time.

The substituted value cannot itself include any other %options.

%NOLOG

Suppress a line being written to the system log.

When a program is called an entry is made in the system log, SYSLOG.PRN, unless this options is speicified.

%NOWAIT

(Currently Windows only). Normally when a program is run DP4 will wait for the called program o take and then restore focus to the calling program. If you want to run a program completely asynchronously then specify the %NOWAIT option. For this option to work well you may well also require the program to be executed to run minimised. For DP4 progrmams see information for detach=1 in the Guide to DP4 configuration.

%PAUSE

Pause after the program has executed. This prompts the user to press any key to continue. This option is useless on Windows like operating systems (unless you use the console version of the DP4 terminal manager), but may be useful elsewhere to allow the output of the executed program to remain visible.

%SERVER

This argument is only relevant when the DP4 Terminal Server is used. It forces the program to be run with its user interface on the server. Normally when a DP4 program is run through the DP4 Terminal Server the program will run on an application server with its user interface on the same work station as the calling program. This might be useful for "remote administration" type functions such as scheduling a backup.

%TERMINAL

This argument is only relevant when the DP4 Terminal Server is used. It forces the program to be run on the workstation. Normally when a DP4 program is run through the DP4 Terminal Server the program will run on an application server with its user interface on the same work station as the calling program. This might be useful for running programs that are installed locally (usually non DP4 programs).

%WAIT

(Currently Windows only). Normally when a program is run DP4 will wait for the called program o take and then restore focus to the calling program, but not for it to necessarily terminate. If you want to run another program completely synchronously then specify the %WAIT option.

This option will not work for running 16 bit programs from 32 bit programs on a Windows NT family operating system.


Special Functions

These functions mosly control a user's access within a QA Build program. They can be invoked by selecting a procedure type of Run another program.

For example, entering %LOGON in the Command field will cause the procedure to prompt the user for a name and a password.

There is currently one command that is not user-related:

%SYSTEM

Return to environment that called this program. The calling environment may be another QAB program or DP4 menu system or the operating system.

User Related Commands

%LOCKUP

Lock program until user enters password.

If you specify no map for the procedure, the logon function will use a standard map. However, you may specify a map of your own. It must include local fields 1 and 2, both of length 8.

%LOGOFF

Log user off and restart program from the beginning.

%LOGON

Prompt user for name and password.

After the user has logged on, the program will use the language, the access read and write levels and the display preferences of that user.

If you specify no map for the procedure, the logon function will use a standard map. However, you may specify a map of your own. It must include local fields 1 and 2, both of length 8.

%PASSWORD

Prompt user to change the password.

%PREFERENCES

Allow user to change colours and other display attributes. The changes will only affect the current user.

Command Tails

%TEMPORARY

Can be used with %LOGON and %LOGOFF, to allow a temporary user to log on and use a particular function of a program. This may be particularly useful, where a supervisor must log on to perform a function which requires a high access level.

%LOGON %TEMPORARY Logs on a temporary user
%LOGOFF %TEMPORARY Logs off a temporary user and restores the original user. This does not restart the program. The program flow continues in the standard manner.


Printing

When A QA Build program prints, by sending maps to an output device, it may be sending the output to a printer, a file, the screen, or any combination of the three. The output may in reality be controlling specialised hardware such as a cash drawer or other POS device. Maps for printing are created in exactly the same way as ordinary maps and the same map may be output to the screen in an interactive procedure, and output to a print device in a print procedure. In the context of printing, maps are usually called forms.

Before a program can print, it must initialise a printer or other output device. Initialising an Output device requires two or three main pieces of information:

  • A bitmask to specify which of the possible output destinations are required, together with the various other flags you can set up in the Output devices dialog.

  • The name of a print device that determines how information should be sent to and formatted for a particular device. Print devices are described using a Parameterised Output Device, or POD, which describes a particular configuration of a Component Output Device (COD) in a Device Group. PODs, CODs and Device Groups are stored on the system database and are created using DFSETUP. This process is described in the OSDI Developer's Guide here.

    The print device is relevant even if you do not select "printer" as one of your output destinations. If you are printing to a file, the print device will determine aspects such as the pagination of the file, and any escape sequences it contains, and can even result in a completely different file format or character set being used. Printing to screen using DFPRINT is radically different from printing to screen using one of the other print devices.

  • For printing to a file you need to specify the name of the file to open.

Initialising Printing

There are two places where you can initialise printing:

  • You can initialise printing and specify the output device in the program header.

  • You can initialise printing and partially specify the device in a procedure body.

In both places, your program can opt to allow the user to select his own print device. Typically you may well want to do this when printing reports to general purpose printers, as it makes the system more flexible. You are most unlikely to want to let the user choose print devices in a program controlling an OSDI - because your program may well want to select various sub devices (i.e. different component device set ups) at various points in the procedure tree. If the user has initially selected a completely different print device your program would fall over in a system error.

Once a program has intialised printing it the output devices remain open throughout that part of the procedure tree beneath it, and the program cannot intialise printing for different output devices, though it can use a Sub device output procedures to change the print characteristics in some way.

For example if a procedure has a procedure body that opens a print device, all the subsidiary procedures can print, or select different sub devices belonging to the same device group, but they cannot select a device belonging to a different device group. The print device will be closed on exit from the procedure, so the procedure on the right cannot print, unless it also opens a print device. You should also remember that if the device opened is a "real" printer other users will probably not be able to gain access to the printer all the time this part of your program is running. (This won't be the case if you print using the DFPRINT print spooler, the older WINPRIN, or the Unix/Linux print spooler)

The steps needed to initialise printing in the two cases are described on the following pages:

  • Initialising Printing at Startup
  • Initialising Printing in a Procedure Body

Printing to Multiple Print Devices

The behaviour described above can sometimes be a nuisance. For example a program using an OSDI might also want to be able to print a report to the DFPRINT program. There are two ways to achieve this:

  • Structure the program so that the various print devices you need are opened in completely separate parts of the procedure tree. This is a good solution providing it fits in well with your application architecture.
  • Call the procedures that need to print to another print device using a subprogram that initialises the appropriate print device. Use the %CALL facility, together with %SEND and %RECEIVE (if required), but do not use %OVERLAY. (See Calling QAB programs for more details of the %CALL facility). QAB will temporarily close the current output device while the called program runs. The called program will print as required. When control returns to the calling program it will reopen the original print device automatically and output will continue normally.

Both solutions work well for printing to ordinary printers. They may not be entirely appropriate if you are using an OSDI, as OSDIs may expect to be loaded throughout the lifetime of a program, once loaded. On systems where the OSDI is loaded as a DLL you may want to add a C exit that additionally loads the library directly from the application, preventing it from being unloaded if the device gruop used by the OSDI is closed temporarily.

This section covers the following topics:

  • Print Control Variables
  • Print Error Handling
  • Device Group Handling


Inititialising Printing at Startup

The output destination for printing in a QA Build program can be selected using the Output devices option in the main Program Editing Functions menu. If you select Initialise printer/file output in the program summary, the print device will be initialised when the program loads and released when the program terminates. You cannot select another print device in the same program, but you can control the device by selecting other subdevices, assuming the print device is configured appropriately.

To select a print device in the program summary:

  1. In the Program Editing Functions menu select Program Summary
  2. Enable the Initialise printer/output file option
  3. Complete the dialog to return to the Program Editing Functions menu
  4. Select Output devices to open the Default Output Devices Dialog
  5. Select the required combination of output devices by completing this dialog as described in Default Output Devices.


Inititialising Printing in a Procedure Body

For a Device select/print procedure body printing is initialised will be initialised on entry to the owning procedure. The print device will be closed again on exit from the procedure. This allows the procedure body itself and all the subsidiary procedures to print.

To initialise printing in a procedure body:

  1. From the Program Editing Functions menu select Program Summary
  2. Make sure the Initialise printer/file output option is not enabled - clear it if necessary.
  3. What you do next will depend on your exact requirements:

    • If this is the only part of the procedure tree that will do any printing you can use the Output Devices dialog on the Program Editing Functions menu to select the required devices. This dialog will also be useful if only the Print Device needs to be changed in the procedure trees that print, because is specified directly in the procedure body.

    • If you are printing to radically different output devices in different procedure trees (e.g. file in one place, printer in another), or you want to control the output device programmatically, you cannot rely on the Output Devices dialog. For example you may need to print information to various different files. There is no equivalent of the Output Devices dialog at a procedure level. One possibility is to consider using a sub program, so that you can still select the devices in the program header. In this case you need to follow the advice at the start of this section in Printing to Multiple Output Devices. The other method, which you can use if you do not want the overhead of calling another program at this point in your program, or need more flexibility, is to use the QAB standard variables output_device and filename to specify the destination for your printing. You have to set these up before you get to the procedure that initialises printing, because the initialisation takes place before your procedure body executes, so setting them there is ineffective. See Print Control Variables for further information and examples.

  4. In both cases, you can now include Device/Select Print procedures in your procedure tree. Such procedures can either continue to use the current print device (if they specify "default_printer") , or they can select a new print device. See Procedure Overview for details.

The Program Editor will not allow you to set up a new Device select/print procedure underneath another one. Although you can cheat the Program Editor, by moving procedures around, the Program Compiler specifically checks for this, and will disable device selection in any subsidiary procedures of a procedure that does initialise a device.


Subdevice Output

Once a program has initialised printing it can control printing by selecting a subdevice POD(assuming the Device Group has suitable PODS) to change the device characterstics in some way. By far the most common use of this is in programs that use an OSDI. A typical POS program may need to switch between journal and receipt printers, or send output to a customer or operator display. All these would probably be selected by using Sub device output procedures and selecting an appropriate POD. These may all use the same COD with different parameter choices, or they may use different CODs. This is completely invisible and irrelevant to the QAB programmer.

A notable difference of selecting a subdevice from initialising a printer, is that once selected the current subdevice remains selected indefinitely. There is no concept of closing a subdevice in QAB. This means that a print procedure may be affected by procedures to the left of it in the procedure tree, not just the procedures above it.

Although Subdevice output is generally used in conjunction with an OSDI it is possible to use it with regular printers as well, assuming you have set them up correctly. For example you might use it to select different trays to print from on a laser printer - perhaps switching between printing on pre-printed stationery and blank paper at some point in your program.

For more information on setting up devices and using refer to Device Definition Hierarchy and Accssing an OSDI from QA Build in the OSDI Developer's Guide.

To select a Subdevice POD:

  1. Your program must already have initialised printing using one of the techniques described in Printing.

  2. At the appropriate point in your programs procedure tree insert a procedure that will be used to select the appropriate subdevice.
  3. Create/Edit the Procedure as required. From the Procedure Body Editing Menu select Procedure Overview
  4. Specify the procedure type as Sub device output and select the POD for the desired sub device.
  5. Close the dialog to save your changes.


Print Control Variables

Device Selection Variables

output_device can be set to select the output destination bitmask. filename allows you to name the files created by a program. These two variables must be set before initialising printing.

filename

filename contains the name of the file created if you choose file output. The default filename extension .PRN will be added if you specify a filename without an extension. This variable behaves in rather a strange way:

  • If you want to assign to the variable you define it in your field processing as a type U field. You can specify any length up to U59 if your formula does not specify the extension, or U63 if it does. (In a future release it will be made acceptable to use type C or M so that mixed case filenames can be generated.)
  • If you want to read the variable in a formula it is always treated as a U8 field, although you can assign a longer filename, you cannot read it using the filename variable.

output_device

output_device determines whether to produce screen, printer or file output.

The initial value of output_device depends on the responses to the questions in the Default Output devices dialog. You can modify the value of output_device to change the devices that will be opened by subsequent Device Select procedures.

output_device must be assigned before initialising printing. Setting output_device afterwards has no effect. For this reason, if you wish to use output_device, initialise printing in a procedure body. If printing is intialised by the program header, you cannot change output_device beforehand.

The value of output_device is the sum of various flags as shown in the table below. For each value the name given to the value in the DP4 C API is also shown. If you use output_device values extensively in your QAB programs it is suggested you create QAB macros using these names to make your code easier to understand.

These are the only flags you should use. Other flags are either meaningless in QAB or will cause errors.

Value | DP4UIAPI.h name | Effect | Description
1 | SCREEN | Screen output | Print output is scrolled on to free lines of the screen
2 | PRINTER | Printer output | Print output is sent to the printer port specified by the device type
4 | DP4_FILE | File output | Print output is written to a file. The file name is contained in the variable filename
32 | OFFER_DELETE | Offer Delete | If output is sent to a file and the file already exists allow the user to choose whether to append or delete.
256 | NOCTL | No control characters | Omits the printer control characters, for file output
512 | NO_APPEND | Restart file | If the filename exists, the file will be deleted
8192 | NO_SELECT | No run time selection | Prevents the print options dialog being offered at run time unless there is a problem selecting the requested output devices
16384 | NONE_ALLOWED | Permit none | Permit no output (screen,printer,file all off)
32768 | NO_RETRY | Prevent retry | Prevent the print options dialog being offered even when there is a problem selecting the requested output devices

Some of these flags have additional/different effets on the output when you are using a DFPRINT based output device. Please refer to the documentation of the dvc_choose() function for further information.

Example:

To include output_device in field processing define it with data type I. You can use any length.

A print program is required which depending on a certain condition prints either to a file or to a printer.

The program sets the value of OUTPUT_DEVICE in formula processing in a procedure body before the one that initialises the printer The formula below shows how the value of OUTPUT_DEVICE can be set:

IF condition then
  2 {select the printer}
ELSE
 (4+512){select file output to new file}

You might also examine the value of output_device after printing has started. For example if printing was only goin to screen you might decide to print a narrow version of a report, missing out coluns that take up too much space.

Variables Used During Printing

There are several QAB standard variables related to printing which you may find useful. line_nr and page_depth, page_width control where a new page should be started. page_nr can be used to print page numbers.

line_nr

line_nr contains the number of the last line written to the current page. Within a print procedure, line_nr includes the form output in that procedure. If used in an entry condition formula for a print procedure line_nr represents the last line printed.

When a printer is first selected or when a new page is started, line_nr is set to 0.

For printing to DFPRINT an approximate line_nr is calculated based on the position on the page divided by the height of the default font.

Example:

To include line_nr in field processing define it with data type I. You can use any length.

A report has a header on each page followed by repeated lines. When linenr is within 6 lines of the page depth (see below) a new page should be started

The procedure for printing a new header includes the entry condition:

line_nr+6 >= page_depth

page_nr

page_nr contains the current page number. It is incremented before field processing if the form output method is Top of form. It is incremented after field processing if the form options specify a new page at end.

To include page_nr in field processing define it with data type I. You can use any length.

page_depth

The value of page_depth is taken from the selected printer settings (the device type). It is set when the printer is selected.

For printing to DFPRINT an approximate page_depth is calculated based on the actual paper size and margins selected by the Windows print dialog, or in the DP4 configuration file

Example:

To include page_depth in field processing define it with data type I. You can use any length.

See example for line_nr above.


Print Error Handling

For printing to printers not controlled via an OSDI or a high level print spooler such as DFPRINT, WINPRIN or the Unix/Linux lp), if a device goes off-line when an application is trying to print, the following message will probably appear, prompting the user for the next action:

DP4 System message 120. Origin: Operator error

Printer device port name not available.

               <Retry>

If the user hits the Esc key instead of pressing Retry, all printing to the printer will be aborted (printing to screen or file continues normally however).

For printing via a printer driver, the behaviour depends on the driver and will be handled by its user interface, not by QAB.

For printing via an OSDI error information is returned to the application in the print_error variable. The pr_error_mode variable can be set to determine how your program responds to a print error.

print_error

DP4 queries the OSDI for error an error code to application programs in the variable print_error.

To include this in field processing declare it as type I using any length. The value 0 is considered to mean no error. All other values indicate an error condition.

pr_error_mode

When a print error occurs the action of the program depends on the value of the variable pr_error_mode.

Value | Interpretation
0 (default) | Report error and terminate program
1 | Report error and continue program
2 | Ignore error
2 | Ignore error
3 | If a hot key handler is available invoke it as though the user had pressed the hotkey given by Qpr_hotkey

The value 3 is used to allow a QAB program to provide an error handler that will


Device Group Handling

In DP4 groups of devices can be defined and accessed from a program. Complex devices are defined using DFSETUP. In QA Build, a procedure body is defined to select the current device and subsequent print procedures print to that device. For a full description of the use of groups of devices, please refer to the Device Integration Developers Manual.

Input Source Variable

When input is returned, the variable ip_source is set to indicate which input subdevice was used. The value of ip_source corresponds to the number of the input subdevice.

Definition: | ip_source I4
Example: | A credit card number can be input from the magnetic stripe reader or the keyboard Input subdevices Nr/Value Name Description *1 1 KEYBOARD Keyboard 2 2 SCANNER Barcode Reader *3 3 MSR Magnetic Stripe Reader
 | If input is received from the keyboard the value of ip_source equals 1. If input is received from the magnetic stripe reader, the value of ip_source equals 3


Conditions and Formulas

This section covers the following topics:

  • Using Conditions and Formulas
  • Formula Editing Menu
  • Condition Entry Dialog
  • Formula Entry Dialog
  • Formula Syntax
    • Comments
    • Operands
      • Constants
      • Referencing Fields
      • Multi-Occurs Fields
        • The Array Subscript Operator
        • Expressions with Multi-Occurs Operands
        • Assigning to A Variable Occurrence
  • Operators and Functions by Category
    • Arithmetical Operators and Functions
    • Scientific Functions
    • Character Operators and Functions
    • Date Operators and Functions
    • Logical Operators and Functions
    • Multi-Occurrence Operators and Functions
    • Relational Operators
    • Time Operators
  • Operator Precedence
  • Operator/Function Reference
  • Function Definition


Using Conditions and Formulas

  • Conditions can be used in a QA Build program to determine which actions to perform. For example, you can set a condition to enter a procedure. If the condition is not met, the procedure is not executed and the program continues with the next procedure. Conditions are expressed using Boolean expressions such as customer.balance < customer.credit_limit

    Each condition can be associated with an error message that is displayed if the condition fails. It sometimes happens that you may want to display an error message for a condition without otherwise altering the path of the program. This is achieved through Warning Only conditions. Also a condition can be divided into multiple parts, either because it is very complex, or because you want to display different error messages depending on which part of the condition failed. For multi-part conditions you can decide whether or not to carry on testing the condition when one part fails.

    Conditions can be used before or after performing many actions, for example:

    • deciding whether or not to:

      • enter a procedure
      • repeat a procedure
      • access, update, or delete a record
      • input or display a field

      • total one field in another

    • checking validity:

      • of a value entered by the user
      • of a total calculated by totalling

      • of a record accessed

    • filtering the records to be processed in a batch process or offered to the user in a multiple choice pop-up window

  • Formulas can be used in field processing to assign values to fields. For example to determine the order line value by multiplying the price by the quantity you could use the following simple formula: item_price * quantity

The expressions for conditions and formulas use the same syntax and support the same range of operators and functions. The only difference is that the expression for a condition must always evaluate to TRUE or FALSE. That is, it must result in a Boolean (Yes/No) data type. The expression for a formula must result in the data type of the field to which it is assigned.

Both Formulas and Conditions are created using the Formula Editing Menu.


Formula editing menu

This menu is used to assign and edit conditions or formuals and appears at every point in the Program Editor where a condition or formula can be used. It contains the following options:

1 Formula/condition number

Select the number of the formula to be used here. By default an unused number is offered. Note that this allows you to re-use the same formula or condition several times in a procedure. Refer to the advice in Sharing Program Components on sharing formulas and conditions to decide how you want to make use of this facility.

2 Delete a formula

Select the number of the formula to be deleted. Caution! Do not delete a formula unless you are certain that it is no longer used by any program or procedure. No warning is given if you delete a formula that is used elsewhere. The Weed Programs facility in QABUTIL can be used to automatically delete formulas and conditions.

3 No condition/formula

Select this option if you do not want to use a condition or formula here.

6 Copy formula...

Select this option if you want a copy a formula used elsewhere. You may copy formulas from any program in the current database. You may assign the copy a new number. The copy is then independent of the original.

7 Edit formula/condition...

Select this option to edit or create the selected formula/condition using either the formula entry or condition entry dialogs.

8 Edit/Continue (X)

Select this option to return to your previous location when you have made the required changes to the formula.


Condition Entry

Enter the condition which must be satisfied here. You may choose to split conditions such as "status_cd = 'ENT' and ledger_cd = 'P'" into two or more parts so that, if any part fails, a separate message can be displayed informing the user of the nature of the failure: that is with separate parts you can display either or both of the messages "Invalid Status Code" or "Invalid Ledger Code", whereas with a single condition you can only display a message such as "Invalid Status or Ledger" code.

For a multi-part condition press <Del> to delete the currentyl displayed part of the condition. You have to do this before beginning to edit this part of the condition. If you want to delete characters, make sure you press some other key first (such as → followed by ←)

You may find it useful to remember the following points when editing formulas:

  • Formulas consist of 5 lines of at most 64 characters each. Internally they are treated as one long line of 320 characters. In theory this means you can split an identifier over two lines, but this is almost certainly a very bad idea.
  • You can use <Ctrl+Y> to delete the part of the formula from the current position to the end of the line. Pressing <Ctrl+Y> will move the remaining lines up, joining the next line onto the end of this line.
  • You can use <Ctrl+N> to split the current line and move the rest of the formula down.
  • You can use <Ctrl+U> to restore the original state of the formula.

Mapname

This field only applies to entry conditions.

If you associate a remark (message) with this condition, specify the mapset in which it is stored. If you leave the mapname blank, the remarks mapset of the calling procedure body will be used.

Remark on failure

A message can displayed if any part of the condition fails to be satisfied. Normally you will want a message if the condition is part of a validation (an input validation, for example) but not if it is designed to control program flow (a repeat condition, for example).

Warning only

Enable this option if this part of the condition is for warning purposes only.

More

Enable this option if there is another part to this condition. Disable this option if this is a single-part condition or if this is the last part of the condition.

Continue on failure

This option only applies if the More option is set.

If the first part of a multi-part condition fails, there is no need to evaluate the rest of it. However, you can choose to continue evaluating the condition even if one part of it fails, so that the user is given information about any other parts which fail.


Formula Entry

You may find it useful to remember the following points when editing formulas:

  • Formulas consist of 5 lines of at most 64 characters each. Internally they are treated as one long line of 320 characters. In theory this means you can split an identifier over two lines, but this is almost certainly a very bad idea.
  • You can use <Ctrl+Y> to delete the part of the formula from the current position to the end of the line. Pressing <Ctrl+Y> will move the remaining lines up, joining the next line onto the end of this line.
  • You can use <Ctrl+N> to split the current line and move the rest of the formula down.
  • You can use <Ctrl+U> to restore the original state of the formula.

Formulas or conditions that are unusually long or complex can often be handled more conveniently by defining additional fields to act as work fields to store intermediate values. For example, suppose we have a formula:

<condition_1> AND <condition_2>

We can define a non-database field X with a formula:

<condition_2>

and then use the formula:

<condition_1> AND X


Formula Syntax

DP4 uses essentially the same formula processor in several DP4 utilities: QAB; Report Writer; Browser; Auditor; RECASTDB. This formula processor uses a sytax that is generally similar to that used by most programming languages. The syntax is explained in detail on the following pages, but you may find a brief over-view helpful.

  • Operands must be separated by spaces or operators that cannot be part of the operand.

    For example, 'A MOD B' has to be written 'A MOD B' and not 'AMODB', but A+B is acceptable.

  • An expression may use operators (+ - * / and also operators like mod denoted by an identifier), function calls (e.g. round(rate*amount,2), references to fields (customer.name), standard variables (for example end_key in QAB, or CLIENTNAME in Report Writer), and use constant values and parentheses.

  • For the purpose of matching names neither case nor underscores are significant. Thus all the following are equivalent: CUST_NR, Cust_Nr, cust_nr, custnr.

  • When referencing fields you can refer to any field in a database table already used in your program. The table does not need to be explicitly declared in the table processing for the current procedure, and the field does not need to have been processed in field processing.

  • DP4 does not prohibit you from declaring fields with names that have a predefined meaning as an operator or built-in function. For example you might inadvertently use YEAR as a field name in a program or on the database. If you do this the operator or function will not be available under that name. To minimise the chances of this causing you a problem you should use the RESWORDS program to post the reserved words for QAB to your database. (This list also does duty for Report Writer)

  • If an identifier is not the name of a field, role, or table defined on the database or a local field defined on your program, or a macro or function defined using QAB Function Definition, The formula processor will look for a match with the names of the operators and functions built into the formula compiler. In this case a partial match is acceptable: as long as at least three characters match the interncal function or operator, and as many characters as are specified match, then the identifier is considered to match. Thus, SEC(time), SECOND(time), and SECONDS(time) all extract the seconds from a time, but SECD(time) would generate an error. You are recommended only to use abbreviated operator or function names where there is no danger of confusion, for example MIN(time) would not extract the minutes from a time, because MIN is a function in its own right. As it takes a numeric parameter you would get a compiler error.

  • Each operator and function must be used with operands of the correct data types. Consider the formula expression:  code_prefix CONCAT code_number
    In this formula CONCAT joins two character fields to give one character field. It is essential that code_prefix and code_number are both character fields. If code_number were, say, an integer type, an error would occur at compile time.

  • Conditions and formula expressions are not simply worked out from left to right. Operands have a precedence of operation, which is overruled only by parentheses. For example: ( 1 + vat_rate ) * price
    The multiply operator * has a higher precedence than the add operator +. Without the parentheses, the expression would mean multiply vat_rate by price and then add 1.

    It is sometimes worth using parentheses even when not actually required, if this makes your intention clearer. On the other hand using parentheses where the precedence should be obvious can be confusing.

  • You may include comments in your expressions, using the comment delimiters { }. For example:
    (1 + vat_rate) * price
    {vat_rate equals a decimal fraction such as 0.175}

  • The DP4 Formula Processor allows you to process multi-occurs fields. This is where DP4 differs most from other programming languages in the way it processes expressions. How the DP4 Formula Processor handles-multi-occurs fields in formulas is introduced in Multi-Occurs Fields and Processing


Comments

Comments can be included in formulas and conditions. The comment must be delimited by a { character at the start and a } character at the end. Comments may not be nested. A comment may be placed anywhere where spaces would be permitted, and is equivalent to a space. This means you can't put a comment in the middle of an identifier, or field reference.


The Operands

An operand is the value that is acted upon and can take a constant or variable value, for example 1.15 or PRICE. This section is divided into the following topics:

  • Constant Values

  • Field Referencing

  • Multi-Occurs Fields


Constants

Ordinary Constant values use the syntax shown below:

Field Type Syntax Examples
Boolean

The identifiers listed in the examples.
In simple comparisons of the form field_name=character string if field_name is a boolean field and character string matches the yes or no word for the field, the comparison will be accepted and translated appropriately. (If character string is not recognised there will be a compilation error.)

 TRUE
Yes
FALSE
No
Character

Use single ' or " as the delimiter for the start and end of the string. You cannot include the delimiter in the string.

If you want to initialise a multi-occurs character field such as an address with a constant value you will need to use a string that is carefully constructed. See below for details.

 'ABC 123'
"Hello World"
Date

Use the day.month.year format (not your preferred format), where day, month and year are numbers. The Year should be between 100 and 9999, if the year is below 100 1900 is added.
Dates must be valid according to the rules used for the Gregorian calendar.

 8.1.1999 (8th Jan 1999)
02.07.63 (2nd July 1963)
1.1.2000 (1st Jan 2000)
20.3.1 (20th Mar 1901)
Numeric

A numeric constant uses the following syntax Optional -, 1 or more digits including an optional decimal point.

 1
99999.99
-7.5
Time

Use the hours:minute:second:hundredth format, where all four parts are two digit constants.
The format hh:mm:ss.hh is also accepted.
Times must be between 00:00 and 23:59:59.99.
Seconds and/or hundredths may be omitted, if so they are set to 0.

12:14:56:99
12:14:56.99
17:30:45
17:00

For types other than character you can use a number of constants separated by the concatenation operator to assign to a multi-occurs field. For example a multi-occurs numeric field could be intialised like this: 1 |2 | 3

Null Constants

Null values are denoted using the following identifiers:

Field Type | Null Name
Character | null_char
Date | null_date
Numeric | null_number
Time | null_time

Testing for NULL

If you want to test for a NULL value, you usually only need expression = NULL or expression <> NULL irrespective of data type.

There is no null value for Boolean fields.

Take care to assign all the values of an occurs field. If you assign fewer occurs than the field has, then it is padded with the last value specified

Initialising Multi-occurs Character Fields

To intialise the contents of a multi-occurs character field using a constant, consider the field as one long character field including all the occurrences next to each other. For example, to assign a multi-occurs field ADDRESS of type C20*4, consider it as a field of length 80. Address each character according to its position within the character string(an X shows the start of each occurrence):

 X1234567890123456789
"123 Cherry Lane 		X1234567890123456789
Stokehampton 		X1234567890123456789
Cheshire 		X1234567890123456789
ST8 5LN
"

The trailing spaces can be omitted if you like. You may find it easier to initialise each occurrence separately, and this will be necessary if the field is bigger than the maximum size of a formula.


Referencing Fields

Local fields and QAB standard variables are referenced using the full field name.

The full form for referencing a database fields is as follows:
tablename.identifying role.fieldname.fieldrole

Blank identifying roles and field roles are denoted by _. When compiling QAB programs the compiler will accept non ambiguous abbreviated field names in the following formats:

  • fieldname
  • fieldname.fieldrole
  • tablename.fieldname
  • tablename.fieldname.fieldrole
  • tablename.identifying role.fieldname

If an abbreviated definition could refer to more than one field, a compiler error will be generated. Because modification to a program or a database could render a formerly unambiguous reference ambiguous, or a reference could be ambiguous when a procedure is used in one program but not another, it is a good idea always to specify field names in full. Using abbreviated names should be confined to small, or programs you are prototyping. They should not be used in programs that are part of a complex system.

Examples

Consider the following table:

   EMPLOYEE
 1 STAFF_NR   N3
 * SHORT_NAME U10
   NAME       C25
   ADDRESS    C25x4
 * STAFF_NR   N3
    (MANAGER)

An order entry program would probably only refer to one copy of the employee table, so the following would be an acceptable reference to the employee name:
employee.name

In a program that maintained the employee table you would need two copies of the employee table : employee.main and employee.manager, so that the staff_nr(manager) field could be validated. Therefore the program would have to specify the employee name fields as follows:

employee.main.name {employee name}
employee.manager.name {manager name}

Because the staff_nr field occurs in the table twice, the field role must always be specified. In the order entry program you could use:
employee.staff_nr._

In the employee maintenance program you could refer to the following four instances of the staff_nr field:

  • employee.main.staff_nr._ {employee number}
  • employee.main.staff_nr.manager {manager number}
  • employee.manager.staff_nr._ {manager number}
  • employee.manager.staff_nr.manager {manager's manager number}

The only way to avoid ambiguity is to use the full form of the field specification. Note that the database logic will ensure that the second and third instances of the staff_nr field will be the same. However the formula compiler does not take this into account.

$ field reference

$ may be used to refer to the current field in the following three places:

  • A formula.
  • A field validation condition
  • A field input/output condition

The $ equates to the complete field. For example if you have an item of field processing for and address field that specifies a low and high occurs of 2 to refer to the second line of the address. $ still refers to the whole address field. You must use $[2] to refer to the second line of the address.


Multi-Occurs Fields

Multi-Occurs Fields and Processing

Fields in DP4 can have up to 99 occurrences. Inside the formula expressions may have more occurrences in this - there is no predefined limit on the number of occurrences in an expression. Multi-occurs fields can be thought of as arrays or sequences of variables. You can reference or modify the contents of a multi-occurs field using the QA Build formula processor, and construct a multi-occurs value by combining single occurs field.

Throughout this section we shall assume the field in the table below is defined:

Field | Type | Value
SALES_CODE | I2*5 | 23 43 7 19 2

  • Referencing Occurrences within a Field

    In a formula you can reference individual occurrences from a field or expression using the subscript operator [] like this:  SALES_CODE[5]. It is also sometimes possible to refer to a subrange of occurences of a field using this operator as well using the following syntax: SALES_CODE[2..3]. The [] operator works differently for character fields. For full details, see Array Subscript Operator.

  • Using Multi-occurs Fields in Formulas

    Most operators and functions in the formula processor work with multi-occurs expressions. This allows calculations which might require a for or while loop in other languages to be expressed in a single expression. For example SALES_CODE+1 adds 1 to each occurrence of SALES_CODE, while SALES_CODE*SALES_CODE multiplies corresponding occurences of SALES_CODE together. See Expressions with Multi-ccurs Operands for details.

    There are also a number of functions are intended specifically for multi-occurs fields, for example the MINIMUM function returns the minimum of a set of values expressed as a multi-occurs field. Thus MIN(SALES_CODE) returns the value 2. The SUM function returns the sum of the occurrences. SUM(SALES_CODE > 7) returns 3.

  • Creating Multi-Occurs expressions

    You can construct a new multi-occurs expresion by combining expressions with the concatenation operator |. For example the SALES_CODE value could be assigned using this expression: 23 | 43 | 7 | 19 |2. The | operator can be used with any type of field except character fields. Character fields are concatenated as strings using the concat operator, but you can also initialise a multi-occurs field directly as explained in Constants.

  • Assigning to all or part of a Multi-occurs Field

    You can easily assign a formula to a fixed range of occurrences in a multi-occurs field, by selecting the low and high occurs values appropriately in the field details.

    One weakness of the DP4 formula processor is that is not possible to assign a value to the nth occurrence of a field, or a variable subrange of occurrences. This is because in field processing you always have to specify a fixed range of occurences to process. There is a work-around for this problem, which is explained in Assigning to a Variable Occurrence.


Array Subscript operator [ ]

This section describes how you reference multi-occurs fields and expressions in formula processing.

Single Occurrence

You can reference a single occurrence of a multi-occurs expression using the [ ] array subscript operator like this:
multi-occurs expression[subscript expression]

multi-occurs expression is typically a field reference, but it can be any expression.
subscript expression should be an expression that evaluates to an integer between 1 and the number of occurrences in the first expression. If the number is out of range either a compiler time or a run time error will be generated.

Except for character fields, there is nothing unusual about the [] operator compared with other languages. For character fields the [] operator is very unusual indeed. For a character field, if the multi-occurs expression is a field reference, and subscript expression is an integer constant, and the [ is immediately next to the last character of the field reference, with no intervening spaces, then the [] operator behaves as you might expect - it extracts the complete occurrence from the field. In any other situation the [] operator extracts a single character from the field - if subscript expression evaluates to n then the result is the nth character in multi-occurs expression.

Examples

Suppose our program defines the following fields with the given values:

Field | Type | Value
ADDRESS | C30*4 | 123 Cherry Lane Stokehampton Cheshire ST8 5LN
BUDGET | N5*12 | 1 2 3 4 5 6 7 8 9 10 11 12
NAME | C30 | James Parrot Seed
I | I5 | 1

The table below shows the result of using the [] operator in various ways:

Expression | Result
address[1] budget[1] name[1] address[2] | '123 Cherry Lane' 1 'James Parrot Seed' 'Stokehampton'
address[i] budget[i] name[i] address[2][i] | '1' 1 'J' 'S'
address [1] budget [1] name [1] address [2] | '1' 1 'J' '2'
address[1][10] name[1][10] | 'y' 'r'
(address)[10] (name)[10] | 'y' 'r'

The syntax address [1] to extract the first character of the address, is probably sufficiently confusing that you should avoid using it. Using the double subscript [1][1], or the parenthesised form is probably better.

To refer to a range of characters within a character expression you must use the head() or tail() functions or both in combination.

Range of Occurrences

You can reference a range of occurrences using the subscript operator and two full stops, [ .. ]. The range must be specified by constant values, and multi-occurs expression must just be a field reference. If you want a range defined by variables, you must use a combination of head()and tail(). Using the example fields from above we have:

Expression | Result
BUDGET[2..4] | 2 3 4
ADDRESS[2..3] | 'Stokehampton Cheshire'


Expressions with Multi-occurs Operands

On the whole this section does not apply to character operands. A character field is always treated as a single string: for instance if ADDRESS is C30*4, it is treated by the formula processor as a string 120 long. Most operators and functions that accept character operands treat the character operand as a single string. So while the > operator applied to two instances of the budget field returns an array of 12 Boolean values, applied to two address fields it returns a single Boolean.

Most operators and functions in the DP4 formula processor accept multi-occurrence fields and expresions as operands as well as single-occurrence fields; though there are a few exceptions to this it is extremely unlikely you would ever want to use a multi-occurs expression where one is not allowed. This page explains how multi-occurrence operands are treated by the formula processor.

  • Unary Operators and Functions

    Except for functions that are specifically designed to process a multi-occurs field and return a single value (SUM, EVERY, ANY, MINIMUM, MAX) or perform some other special action on a multi-occurs field, the result of unary arithmetic operators/functions is that produced by applying the operator to each occurrence of the operand in turn. Thus ABS(-1|-2|-3) returns 1 | 2 | 3.

  • Binary Arithmetic Operators

    The formula budget+1 has as its result an occurs 12 field where the i'th occurrence is budget[i]+1. If VAT is another occurs 12 field then budget + VAT has as its i'th occurrence budget[i] + VAT[i]. The occurs of the left operand determines the number of occurs in the result - the formula 1 + budget has as its result the single value budget[1]+1.

    The other binary arithmetic operators (such as *, - and /) all behave in a similar way. Usually the second operand of an arithmetic binary operator should have either the same number of occurrences as the first operand or one occurrence, but this is not enforced by the formula compiler. The actual result, R, of A op B where 'op' is a binary operator calculated as follows:

    R[i] = A[i] op B[min(i, N)] where N is the number of occurrences of B.

    Note that, as already noted for addition above this means that if A and B have different occurrences then A op B is not the same as B op A.

    Concatenating a 0 or a 1 onto the end of the second operand may sometimes be useful. For example, to apply VAT holiday for a variable number of months you might use a formula like this:
    BUDGET + (HEAD(VAT,i) | 0).

  • Relational and Logical Operators

    The relational operators =, <>, <, >, <=, >= are all treated in the same way as the arithmetic operators: thus budget.THIS < budget.LAST has as its result an occurs 12 Boolean field, the i'th occurrence of which is the result of budget.THIS[i] < budget.LAST[i].

    Again the logical operators behave in the same way. Consider the following formula:
    budget.this>0 or budget.last = 0

    First the two relational expressions are evaluated giving two occurs 12 Boolean fields as the result. The OR then OR's the corresponding occurrences together to give another occurs 12 Boolean result. The ANY or EVERY functions are often useful when you have created results of this nature.

  • Results

    A similar process is used when assigning the final result of a formula to its field - if the target field has more occurrences than the result, then remaining occurrences will all contain the last occurrence of the result, if it has fewer the excess occurrences are discarded.

    Concatenating results with 0 may sometimes be useful to stop the last occurrence being used repeatedly.


Assigning to a Variable Occurrence

It may happen that in a QAB program we need to be able to assign a value to the nth occurence of some field, where n is a variable. This type of prcessing is very awkward in QAB. It may sometimes be possible to use some kind of trick to change the field - the PROVIDED operator is sometimes useful for this. However the most general method is to use the | operator and the HEAD and TAIL functions.

To assign a single value to a multi-occurs numeric field:

  1. Add the multi-occurs field to the field processing list (see Field Editing) and in the Field Details, set low occurs to 1 and high occurs to the number of occurrences in the field.
  2. Add a formula which uses a combination of the head() and tail() operators.

    For example, to assign to the ith occurrence of the N10*12 budget field you could use something like this :
    head(budget,i-1)| new_value | tail(budget, 12-i)

To assign to n adjacent occurrences you can extend the same technique as follows:

head(budget,i-1)| new_value_1 | new_value_2 |...| new_value_n | tail(budget, 12-i-(n-1))


Operators and Functions by Category

Following are quick lookup tables for each category of operators and functions:

  • Arithmetical Operators and Functions
  • Scientific Functions
  • Character Operators and Functions
  • Date Operators and Functions
  • Logical Operators and Functions
  • Multi-Occurrence Operators and Functions
  • Relational Operators
  • Time Operators

In these lookup tables operands and parameters may have the following data types:

C Mixed case character (ASCII collate)
D Date
H Unsigned 16 bit integer (range 0 to 65535)
I 16 bit integer (range −32767 to 32767)
J Unsigned 8 bit integer (range 0 to 255)
K Signed 8 bit integer (range -128 to 127)
L 32 bit integer
M Mixed case character (Database collate)
N Number (for very large or non integer numbers)
P Pseudo numeric (Digit only character fields)
S System character (for DP4 Names)
T Time
U Upper case character with Database collate
Y Boolean (Yes/No)

To simplify the lookup tables note the following:

  • where N is indicated, H, I, J, K and L may also be used
  • where C is indicated, M, P, S and U may be used


Arithmetical Operators and Functions

Operator/Function | Description | Operand Types |  | Result Type
 |  | First | Second |
− | Subtract | N | N | N
/ | Divide | N | N | N
+ | Add | N | N | N
* | Multiply | N | N | N
ABS | Absolute value | N |  | N
MOD | Modulo | N | N | N
ROUND | Round off number | N | N | N
TRUNC | Truncate number | N | N | N


Scientific Functions

Function | Description | Operand Type | Result Type
SINE | Sine | N | N
COSINE | Cosine | N | N
TANGENT | Tangent | N | N
ASIN | Arc sine | N | N
ACOS | Arc cosine | N | N
ATAN | Arc tangent | N | N
SINH | Hyperbolic Sine | N | N
COSH | Hyperbolic Cosine | N | N
TANH | Hyperbolic Tangent | N | N
LOG | Natural log | N | N
EXP | e**n | N | N


Character Operators and Functions

Operator/Function | Description | Operand Types |  | Result Type
 |  | First | Second |
ASCII | Convert character to ASCII code | C |  | N
CHARTONR or CHARTONUM | Convert numeral character to a number | C |  | N
CHR or CHAR | Convert ASCII code to character | N |  | C
CONCAT | Join character fields | C | C | C
HEAD | Leading characters | C | N | C
IN | One string in another? | C | C | Y
LENGTH_OF | Count characters in a string | C |  | N
LIKE | One string like another? | C | C | Y
NEXTCHAR | Next input character | Y |  | C
NUMTOCHAR | Convert number to characters | N | - | C
POS | Position in character field | C | C | N
READ_TAIL | Read command tail | C |  | C
READ_TOGGLE | Command tail used? | C |  | Y
TAIL | Trailing characters | C | N | C
TRIMMED | Remove trailing spaces | C |  | C
UPPERCASE | Convert to upper case | C |  | C
VALIDNUMBER | Only numbers in string? | C |  | Y


Date Operators and Functions

Operator/Function | Description | Operand Types |  | Result Type
 |  | First | Second |
− | Subtract | D | DN | ND
+ | Add | D | N | D
DATETONR or DATETONUM | Convert date to a number | D |  | N
DAY | Day part of date | D |  | N
MONTH | Month part of date | D |  | N
NRTODATE or NUMTODATE | Convert number to date | N |  | D
SETDAYS | Set day part of date | D | N | D
SETMONTHS | Set month part of date | D | N | D
SETYEARS | Set year part of date | D | N | D
YEAR | Year part of date | D |  | N


Logical Operators and Functions

Operator/Function | Description | Operand Types |  | Result Type
 |  | First | Second |
AND | Logical AND | Y | Y | Y
IF THEN ELSE | If statement | Y | Any | Any
NOT | Not | Y |  | Y
OR | Logical OR | Y | Y | Y
PROVIDED | Provided | Any | Y | Any


Multi-Occurrence Operators and Functions

Operator | Description | Operand Types |  | Result Type
 |  | First | Second |
| | Concatenation | DNTY | DNTY | DNTY
[ ] | Array Subscript | Any | Any |
ANY | OR all occurrences | Y |  | Y
EVERY | AND all occurrences | Y |  | Y
HEAD | Leading occurrences | Any | N | Any
MAXIMUM | Maximum occurrence | N |  | N
MINIMUM | Minimum occurrence | N |  | N
POSITION | Position multi-occurs field | Any | Any | N
SUM | Sum occurrences | NY |  | N
TAIL | Trailing occurrences | Any | N | Any


Relational Operators

Operator | Description | Operand Types |  | Result Type
 |  | First | Second |
< | Less than | Any | Any | Y
<= | Less than or equal to | Any | Any | Y
<> or != | Not equal to | Any | Any | Y
= or == | Equal | Any | Any | Y
> | Greater than | Any | Any | Y
>= | Greater than or equal to | Any | Any | Y


Time Operators

Operator | Description | Operand Types |  | Result Type
 |  | First | Second |
HOURS | Hour part of time | T |  | N
HUNDREDTHS | Hundredths part of time | T |  | N
MINUTES | Minute part of time | T |  | N
SECONDS | Seconds part of time | T |  | N
SETHOURS | Set hour part of time | T | N | T
SETHUNDREDTHS | Set hundredths part of time | T | N | T
SETMINUTES | Set minute part of time | T | N | T
SETSECONDS | Set second part of time | T | N | T


Operator Precedence

The larger the number, the higher the precedence. Operators with equal precedence are grouped left to right unless parentheses are used.

Precedence | Operator | Description
14 | a[n] | Subscript
14 | f() | Function call
13 | PREV/TOT | Modifiers (not supported in QAB)
12 | CHARTONR DATETONR NRTODATE | Conversion operators
11 | YEAR MONTH DAY HOUR MINUTE SEC HUNDREDTH | Extraction
10 | SUM | Multi-occurs operator
10 | CONCAT |
10 | SETYEAR SETMONTH SETDAY SETHOUR SETMINUTE SETSECOND SETHUNDREDTH | Date/Time Operators
10 | UPPERCASE | Character operator
9 | - | Unary Minus
9 | ABS | Absolute Value
9 | ~ | Bitwise Complement
8 | * / | Multiplication/Division
8 | MOD | Remainder after division
8 | & | Bitwise and
7 | IN | Test occurrences
7 | + - | Addition/Subtraction
7 | XOR ^ | Bitwise XOR
7 | ! | Bitwise OR
6 | < > <= >= | Relational
5 | = == <> != | Equal/not equal
4 | NOT | Logical/bitwise operator
3 | AND | Logical/Bitwise operator
2 | OR | Logical/Bitwise operator
1 | PROVIDED SIGNEDON | Numeric/Logical Binary operators


Operator/Function Reference

The operator/function reference defines each operator and function in the following format:

Syntax: | NAME(D parameter)
Return Value: | D return_value
Precedence: | 1 15
Description: | Description of the operator or function
Example: | Example usage and results

The components of the syntax and return value are shown as:

NAME | Name of the function or operator
D | Data type of the parameter, as defined in the data type table on page ?
 | Where a C is specified, U, P, S and M data types may also be used. Where an N data type is specified, the I and L data types can also be used
 | Where more than one data type can be passed, these are listed (for example, NDT for Number, Date and Time). Multi-occurs data types are indicated by the following syntax: [..]
parameter | Descriptive name of the parameter
 | Where more than one parameter is passed, each parameter is separated by a comma
return value | Descriptive name for the return value

The following functions are available:

  • ABS
  • Add +
  • AND
  • ANY
  • Array Subscript [ ]
  • ASCII
  • Bitwise Operators
  • CHARTONR or CHARTONUM
  • CHR (or CHAR)
  • Concatenation Operator |
  • CONCAT
  • DATETONR or DATETONUM
  • DAY
  • Divide /
  • Equal = (or ==)
  • EVERY
  • Greater than >
  • Greater than or Equal to >=
  • GET_PASSWORD
  • HEAD
  • HOUR
  • HUNDREDTH
  • IF THEN ELSE
  • IN
  • Less than <
  • Less than or Equal to <=
  • MAX
  • MIN
  • MINUTES
  • MOD
  • MONTH
  • Multiply *
  • NEXTCHAR
  • NUMTOCHAR
  • NOT
  • Not Equal to <> or !=
  • NRTODATE
  • OR
  • PATTERN_MATCH
  • POS
  • READ_TAIL
  • READ_TOGGLE
  • ROUND
  • SECONDS
  • SETDAYS
  • SETHOURS
  • SETHUNDREDTHS
  • SETMINUTES
  • SETMONTHS
  • SETSECONDS
  • SETYEARS
  • - Subtract
  • SUM
  • TAIL
  • TRUNC
  • UPPERCASE
  • VALIDNUMBER
  • YEAR


ABS

Syntax: | ABS(N value)
Return Value: | N result
Precedence: | 9
Description: | ABS gives the absolute magnitude of a number. This has the effect of removing the negative sign from negative numbers and leaving positive numbers unchanged
Example: | ABS(−101) = 101


Add +

Syntax: | N value_1 + N value_2 D date_1 + N number_of_days
Return Value: | N result/D result
Precedence: | 7
Description: | This operand adds numerical or date fields
Example: | 21 + 34 = 55 25.12.1991 + 7 = 01.01.1992


AND

Syntax: | Y boolean_value_1 AND Y boolean_value_2 N integer_value_1 and N interger_value2
Return Value: | Y result/N result
Precedence: | 3
Description: | AND results in TRUE only if both operands are TRUE. The result is FALSE otherwise. When used with two numeric values a bitwise and is performed: you may prefer to use the & operator in this case.
Example: | TRUE AND TRUE = TRUE TRUE AND FALSE = FALSE 5 & 6 = 1


ANY

Syntax: | ANY(Y[..] multi_occurs_boolean_field)
Return Value: | Y result
Description: | ANY performs an OR operation on all the fields of a multi occurrence boolean field
Example: | ANY(FALSE|FALSE|TRUE) = TRUE


ASCII

Syntax: | ASCII(C string_of_characters)
Return Value: | N[..] ascii values
Description: | ASCII converts a character string to a multi occurrence number field giving the ascii values of each character
Example: | ASCII("ABC") = 65|66|67


Bitwise Operators

The bitwise operators are as follow:

& Bitwise AND

! Bitwise OR

^ Bitwise exclusive OR (XOR)

~ Bitwise complement

Syntax:

N value_1 & N value_2
value_1 ! N value_2
N value_1 ^ N value_2
~Y value

Return Value:

Y result
Precedence: 8 (&), 7(!, ^),9 (~)
Description:

Use these operators for operating on numbers which are treated as a binary number. Where devices are selected according to the bits of a number, the use of these operators allows one bit to be changed without affecting the other bits

You can also use the operators AND, OR, XOR and NOT in place of the symbols above, but AND, OR and NOT, (but not XOR) have a differentprecedences (3, 2, and 4 respectively)

Example:

13 & 3 = 1
131 ! 3 = 15
13 ^ 03 = 14
~13 = 4294967282


CHARTONR

Syntax:

CHARTONR(C character_field)

Return Value:

N extracted_number
Precedence: 12
Description:

CHARTONR extracts the numerals in a character string and creates a number

Example:

CHARTONR("KT3 6BT") = 36
CHARTONR("KTA CBT") = 0


CHR (or CHAR)

Syntax: | CHR(N[..] multi_occurs)
Return Value: | C ascii_character_string
Description: | CHR (or CHAR) converts a multi-occurs number field to a character field
Example: | CHR(78|79) = "NO"


CONCAT

Syntax: | C part_1 CONCAT C part_2
Return Value: | C result
Precedence: | 10
Description: | CONCAT joins two character fields CONCAT can be used in combination with HEAD and TAIL to concatenate parts of a field
Example: | "HE" CONCAT "LLO" = "HELLO" "HELLO " CONCAT (HEAD("JOE BROWN",3)) = "HELLO JOE"


Concatenation Operator |

Syntax: | DNTY value_1 | value_2
Return Value: | DNTY [..] multi_occurs_field
Description: | This operator combines two fields into one multi-occurs field
Example: | 2|3{multi occurs field 1st occurrence = 2, 2nd occurrence = 3}

This operand may be useful when initialising multi occurrence numbers. You can also use it to create arrays for MAX and MIN to work on. It has lower priority than any operator except IF THEN ELSE.

The | operator does not work with Character fields. Use CONCAT instead.

C programmers who were expecting | to be a bitwise or operator should note that QAB uses # for that operator instead.


DATETONR

Syntax:

DATETONR(D the_date)

Return Value:

N numeric_representation
Precedence: 12
Description:

DATETONR converts a date to the number of days since 1st March 1980
To work out the day of the week, use MOD 7. The date 1st March 1980 is a Saturday, and so the modulus will range from 0 for Saturday through to 6 for Friday

Example:

DATETONR(02.03.81) = 366
DATETONR(08.03.80) MOD 7 = 0 {Saturday gives 0}


DAY

Syntax: | DAY(D the_date)
Return Value: | N
Precedence: | 11
Description: | DAY extracts the day part of a date
Example: | DAY(25.12.91) = 25


Divide /

Syntax: | N value_1 / N value_2
Return Value: | N result_of_division
Precedence: | 8
Description: | This operand is used for dividing numbers. Dividing by zero produces an error
Example: | 21 / 7 = 3

When dividing by a power of ten you may be able to avoid rounding errors that might otherwise be introduced by replacing the division by a multiplication: for example prefer x*0.01 to x/100. In other situations, multiplying by 1.00 (with an appropriate number of zeros after the decimal point) may also be beneficial.


Equal = (or ==)

Syntax: | NCDTY value_1 = NCDTY value_2
Return Value: | Y result_of_comparison
Precedence: | 5
Description: | This relational operator compares two fields and gives TRUE if they are equal
Example: | "SMITH" = "JONES" = FALSE 3.142 = 3.142 = TRUE

This operator may not work as expected when used with floating point numbers. Make sure the numbers do not contain any rounding errors by using the round or trunc functions if necessary.


EVERY

Syntax: | EVERY(Y[..] multi_occurs_boolean_field)
Return Value: | Y result
Description: | EVERY performs an AND operation between all the fields of a multi occurrence boolean field
Example: | EVERY(FALSE|FALSE|TRUE) = FALSE


Greater than >

Syntax: | NCDTY value_1 NCDTY value_2
Return Value: | Y result
Precedence: | 6
Description: | This relational operator compares two fields and gives TRUE if the first is greater than the second
Example: | 100 > 100 = FALSE "SMITH" > "JONES" = TRUE


Greater than or Equal to >=

Syntax: | NCDTY value_1 >= NCDTY value_2
Return Value: | Y result
Precedence: | 6
Description: | This relational operator compares two fields and gives TRUE if the first is greater than or equal to the second
Example: | 100 >= 100 = TRUE "JONES" = "SMITH" = FALSE


GET_PASSWORD

Syntax:

GET_PASSWORD(N mapnr, N fieldnr)
Return Value:

C password
Description:

GET_PASSWORD prompts the user for a password in the standard DP4 password format. The password is typed into a hidden field and it is not possible to move past the field

There are two points to note when using get_password():

  1. Take care when including get_password() in an expression:
    if ask_password then get_password(100, 1) else " "
    The password will always be requested, even when ask_password is FALSE
  2. If you use get_password() in an interactive database procedure, you are recommended to use the following formula:
    if false then $ else get_password(100,1)
    This formula prevents the password function form being called more than once. You may need to do this where database access causes a pre display. The above formula references the field itself, and so is not evaluated in a pre display

The GET_PASSWORD function uses the specified map and field for data entry. The returned value is the entered password or NULL if the user pressed <Escape>
Example:

get_password(100,1)
{pops up map mapnr, and returns the password entered in field fieldnr}


HEAD

Syntax:

HEAD(NCDTY field, N length)

Return Value:

C leading_characters[..] leading_values

Description:

HEAD gives the leading characters of a character field up to a specified length. HEAD can also be used to obtain the occurrences from a multi occurrence field from the first field up to the specified number

Example:

HEAD("HELLO",2) = "HE"
HEAD(2|3|4|9|0,3) = 2|3|4


HOUR

Syntax: | HOUR(T the_time)
Return Value: | N hour
Precedence: | 11
Description: | HOUR extracts the hour part of a time
Example: | HOUR(12:34:01:88) = 12


HUNDREDTH

Syntax: | HUNDREDTH(T the_time)
Return Value: | N hundredth
Precedence: | 11
Description: | HUNDREDTH extracts the hundredth of a second part of a time
Example: | HUNDREDTH(12:34:01:88) = 88


IF THEN ELSE

IF THEN ELSE allows conditional assignments to be made, depending on the value of a boolean expression.

Both expression 1 and expression 2 are always evaluated. For this reason, you should take care when including functions such as read_tail() or next_char() in an expression, or any function which produces a side effect.

Syntax: | IF expression THEN (expression1) ELSE (expression 2)
Return Value: | NCDTY value_to_assign
Description: | IF THEN ELSE allows different assignments to be made, depending on the value of a boolean expression
Example: | IF 109 THEN 25 ELSE 5 = 25


IN

Syntax: | NCDTY occurrence_1 IN NCDTY occurrence_2
Return Value: | Y result
Precedence: | 7
Description: | IN tests whether occurrence exists in another
Example: | "SMITH" IN "JOHN M. SMITH" = TRUE 3 IN 1|2|4 = FALSE


Less than <

Syntax: | NCDTY value_1 NCDTY value_2
Return Value: | Y result
Precedence: | 6
Description: | This relational operator compares two fields and gives TRUE if the first is less than the second
Example: | 100 < 100 = FALSE "JONES" < "SMITH" = TRUE


Less than or Equal to <=

Syntax: | value_1 <= value_2
Return Value: | Y result
Precedence: | 6
Description: | This relational operator compares two fields and gives TRUE if the first is less than or equal to the second
Example: | 100 <= 100 = TRUE "SMITH" <= "JONES" = FALSE


LENGTH_OF

Syntax:

LENGTH_OF(C data)
Return Value:

N result
Description:

This function counts the number of characters in a charactere field, including trailing spaces. To count the characters without trailing spaces use in conjunction with TRIMMED

Example:

LENGTH_OF("Hello World ") = 12
LENGTH_OF(TRIMMED("Hello World ")) = 11


LIKE

Syntax:

C data LIKE C pattern
Return Value:

Y result
Description:

This function searches for a pattern in a character array. Wildcards can be included in the pattern, using the syntax:
* Match any group of characters
? Match any one character
Any other character must match exactly
LIKE is case sensitive
The following rules are used:

  1. If the length of the pattern is 0, LIKE returns TRUE
  2. If the pattern is longer than the data, and the pattern matches up to the end of the data, TRUE is returned if the remaining characters are ' ' (space) or '*'
  3. If the data is longer than the pattern and the pattern matches up to the end of the pattern, the match only succeeds if all the remaining characters in the data are ' ' (space)
Example:

"A. Smith" LIKE "*Sm*" = TRUE
"A. Smith" LIKE "???S*" = TRUE


MAX

Syntax: | MAX(DNT[..] multi_occurs_field)
Return Value: | D, N or T result
Description: | MAX gives the maximum value contained in a multi occurrence field
Example: | MAX(12|42|34) = 42


MIN

Syntax: | MIN(DNT [..] multi_occurs_field)
Return Value: | D, N or T result
Description: | MIN gives the minimum value in a multi occurrence field
Example: | MIN(12|42|34) = 12


MINUTE

Syntax: | MINUTE(T the_time)
Return Value: | N minutes
Precedence: | 11
Description: | MINUTE extracts the minute part of a time
Example: | MINUTE(12:34:01:88) = 34


MOD

Syntax: | N value MOD N divider
Return Value: | N result
Precedence: | 8
Description: | The modulus operator gives the remainder after dividing the first operand by the second. MOD always gives a positive number. MOD 0 produces an error
Example: | 22 MOD 5 = 2 −1 MOD 7 = 6


MONTH

Syntax: | MONTH(D the_date)
Return Value: | N month
Precedence: | 11
Description: | MONTH extracts the month part of a date
Example: | MONTH(25.12.91) = 12


Multiply *

Syntax: | N value_1 * N value_2
Return Value: | N result
Precedence: | 8
Description: | This operand is used for multiplying numbers
Example: | 21 * 7 = 147


NEXTCHAR

Syntax: | NEXTCHAR(Y remove_character)
Return Value: | C next_character_input
Description: | NEXTCHAR reads the next character input. It gives chr(0) if no character is input. It gives CHR(1) to CHR(31) for control characters, cursor keys and function keys/If the operand is TRUE the character is removed from the input buffer. If it is FALSE the character is left in the buffer
Example: | NEXTCHAR(FALSE) {next character character remains in the input buffer} NEXTCHAR(TRUE) {next character character is removed from the input buffer}


NUMTOCHAR

Syntax: | NUMTOCHAR(N number)
Description: | NUMTOCHAR converts a number to a left justified character array The number of decimal places is taken from the scale factor of the number
Example: | NUMTOCHAR(1.5) = "1.5"


NOT

Syntax: | NOT expression
Return Value: | Y result/N result
Precedence: | 4
Description: | NOT gives the opposite value of a boolean field, and the 32 bit bitwise complement of a number. For bitwise nots you may prefer to use the ~ operator.
Example: | NOT TRUE = FALSE NOT FALSE = TRUE NOT 7=4294967288


Not Equal to <> or !=

Syntax: | value_1 <> value_2
Return Value: | Y result
Precedence: | 5
Description: | This relational operator compares two fields and gives TRUE if the first is not equal to the second
Example: | 100 <> 100 = FALSE "JONES" != "SMITH" = TRUE


NRTODATE

Syntax: | NRTODATE(N number_of_days)
Return Value: | D date
Precedence: | 12
Description: | NRTODATE converts a number to a date. The number is interpreted as the number of days since 1st March 1980
Example: | NRTODATE(366) = 02.03.81


OR

Syntax: | Y boolean_1 OR Y boolean_2 N integer_1 or N interger_2
Return Value: | Y result/N result
Precedence: | 2
Description: | OR results in TRUE if either operand is TRUE. The result is only FALSE if they are both FALSE. For N operands the result is the bitwise OR of the operands. You may prefer to use the ! operator in this case.
Example: | TRUE OR FALSE = TRUE TRUE OR TRUE = TRUE/FALSE OR FALSE = FALSE 5 OR 6 = 7


PATTERN_MATCH

Syntax:

PATTERN_MATCH(C pattern, C data)
Return Value:

Y result
Description:

This function searches for a pattern in a character array. Wildcards can be included in the pattern, using the syntax:
* Match any group of characters
? Match any one character
Any other character must match exactly
pattern_match() is case sensitive
The following rules are used:

  1. If the length of the pattern is 0, pattern_match() returns TRUE
  2. If the pattern is longer than the data, and the pattern matches up to the end of the data, TRUE is returned if the remaining characters are ' ' (space) or '*'
  3. If the data is longer than the pattern and the pattern matches up to the end of the pattern, the match only succeeds if all the remaining characters in the data are ' ' (space)
Example:

pattern_match("*Sm*","A. Smith") = TRUE
pattern_match("???S*", "A. Smith") = TRUE


POS

Syntax:

(NCDTY value_1, NCDTY value_2)

Return Value:

N position

Description:

POS determines the position of one field in another larger field. POS works for character fields and multi-occurs fields of any type. If there is no match the value given is zero

Example:

POS("EL","HELLO") = 2
POS(7, 12|3|7|37) = 3


READ_TAIL

Syntax:

READ_TAIL(C command_tail)

Return Value:

U command_tail

Description:

READ_TAIL reads a command tail where a program is specified on the command line. This function does not work for command tails passed using %CALL. Once a command tail has been read using either READ_TAIL or READ_TOGGLE, it cannot be re read
There are two ways of using this function. If you specify a command tail name, READ_TAIL reads the parameter after the specified command tail. (Use READ_TOGGLE to check for a command tail that has no parameter.) If you do not specify a command tail, On Win32 and Unix platforms READ_TAIL will read the next command tail that does not begin with a −. On legacy platforms READ_TAIL will read the next command tail regardless of whther or not it begins with a −.

It is best to use READ_TAIL with a command tail name unless you want to specify a list of similar items. You should call READ_TAIL() and READ_TOGGLE() to check for all the possible named command tails before reading command tails without a name.

Example:

given command tails of GEORGE FRED −HENRY B
READ_TAIL("HENRY") = "B"
READ_TAIL("") = "GEORGE"
READ_TAIL("") = "FRED"


READ_TOGGLE

Syntax:

READ_TOGGLE(C command_tail)

Return Value:

Y result

Description:

READ_TOGGLE gives TRUE if the specified command tail exists in the list of command tails. As with READ_TAIL you can only call this function once per command tail - checking for the command tail causes it to be removed from the list of command tails supplied to he program.

Example:

{given command tails of −GEORGE −FRED −HENRY B}
READ_TOGGLE("CHARLES") = FALSE
READ_TOGGLE("FRED") = TRUE


ROUND

Syntax: | ROUND(N value, N decimal_places)
Return Value: | N result
Description: | ROUND rounds a number to the specified number of decimal places ROUND returns a number with the same scale factor as the precision
Example: | ROUND(21.66,1) = 21.7


SEC

Syntax: | SEC(T the_time)
Return Value: | N seconds
Precedence: | 11
Description: | SEC extracts the second part of a time
Example: | SEC(12:34:01:88) = 1


SETDAY

Syntax: | D the_date SETDAY N the_day
Return Value: | D date
Precedence: | 10
Description: | SETDAY sets the day part of date. There is no check made that the resulting date is valid
Example: | 25.12.1991 SETDAY 31 = 31.12.1991


SETHOUR

Syntax: | T the_time SETHOUR N the_hour
Return Value: | T time
Precedence: | 10
Description: | SETHOUR sets the hour part of a time
Example: | 12:34:01:88 SETHOUR 10 = 10:34:01:88


SETHUNDREDTH

Syntax: | T the_time SETHUNDREDTH N the_hundredth
Return Value: | T time
Precedence: | 10
Description: | SETHUNDREDTH sets the hundredth part of a time
Example: | 12:34:01:88 SETHUNDREDTH 21 = 12:34:01:21


SETMINUTE

Syntax: | T the_time SETMINUTE N the_minute
Return Value: | T time
Precedence: | 10
Description: | SETMINUTE sets the minute part of a time
Example: | 12:34:01:88 SETMINUTE 15 12:34:15:88


SETMONTH

Syntax: | D the_date SETMONTH N the_month
Return Value: | D date
Precedence: | 10
Description: | SETMONTH sets the month part of a date
Example: | 25.12.1991 SETMONTH 8 = 25.08.1991


SETSECOND

Syntax: | T the_time SETSECOND N the_second
Return Value: | T time
Precedence: | 10
Description: | SETSECOND sets the second part of a time
Example: | 12:34:01:88 SETSECOND 3 12:34:03:88


SETYEAR

Syntax: | D the_date SETYEAR N the_year
Return Value: | D date
Precedence: | 10
Description: | SETYEAR sets the year part of date
Example: | 25.12.1991 SETYEAR 2000 = 25.12.2000


Subtract −

Syntax: | N value_1 − N value_2 first_date − second_date date − number
Return Value: | N or D result
Precedence: | 7
Description: | This operand can be used for subtracting numerical or date fields If the second date is later than the first date, the result is a negative number of days
Example: | 43 − 32 = 11 25.12.1992 − 25.12.1991 = 366 25.12.1992 − 30 = 25.11.1992


SUM

Syntax: | SUM(NY[..] multi_occurs_field)
Return Value: | NY result
Precedence: | 10
Description: | SUM adds the occurrences of a multi-occurs field. If the multi-occurs field is of boolean type, SUM adds the number of TRUE occurrences
Example: | SUM(12|3|5) = 20 SUM(TRUE|FALSE|TRUE) = 2


TAIL

Syntax:

TAIL(NCDTY[..] field, N position)

Return Value:

C trailing_charactersNCDTY[..] trailing_values

Description:

TAIL gives the trailing characters of a character field of a specified length. TAIL can also be used to obtain the specified number of occurrences from the end of a multi occurrence field

Example:

TAIL("HELLO",1) = "O"
TAIL(12|23|2|6|70,2) = 6|70


TRIMMED

Syntax:

TRIMMED(C data)
Return Value:

C result
Description:

This function removes any trailing spaces from a field that you want to manipulate ina formula. Note that this only works within one formula - trailing spaces will always be added to a character result when it is stored in a longer character variable.

Example:
        " Hello World "  CONCAT " Hello World " = " Hello World  Hello World "
TRIMMED(" Hello World ") CONCAT " Hello World " = " Hello World Hello World "


TRUNC

Syntax: | TRUNC(N value, N decimal_places)
Return Value: | N truncated_number
Description: | TRUNC truncates a number to the specified number of decimal places TRUNC returns a number with the same scale factor as the precision
Example: | TRUNC(21.66,1) = 21.6


UPPERCASE

Syntax: | UPPERCASE(C field)
Return Value: | C upper_case_characters
Precedence: | 10
Description: | UPPERCASE converts a character field to uppercase
Example: | UPPERCASE("Hello") = "HELLO"


VALIDNUM

Syntax:

VALIDNUM(C character_field)

Return Value:

Y result

Description:

VALIDNUM checks whether a character field contains only numeral characters (0 9). The result is TRUE if the field contains only numeral characters

Example:

VALIDNUM("A123") = FALSE
VALIDNUM("0123") = TRUE


YEAR

Syntax: | YEAR(D the_date)
Return Value: | N year
Precedence: | 11
Description: | YEAR extracts the year part of a date
Example: | YEAR(25.12.1991) = 1991


Function Definition

The Function Definition part of the Program Editor allows you define four types of information:

Functions and Macros Set up a function for inclusion in formulas. This can help simplify formulas

Constants Define a number as a named constant. This can help to improve the readability of your formulas. The use of constants also means that if a value is changed that is used in many programs, you need only change one value

External C Functions If you write your own operators (C Exits), and wish to call them from within a QA Build formula, you will need to define the name of the function, and the parameter types expected by the function

Word Strings Word strings are useful for displaying messages in multi lingual applications


Functions and Macros

You can define functions and macros for use in QA Build formulas. These consist of a name and associated text. The text is evaluated as a macro wherever the name is specified in a formula. In the following example, a mathematical function POWER has been defined which takes one parameter:

Function/Macro name POWER

Function/Macro text

exp(log(%1)*(%2)) {returns first parameter raised to power of second parameter}

A function like the one above can be called in any QA Build formula:

POWER($, 2)

This will evaluate to the value of the current field raised to the power of 2, that is, squared.

The rules for defining functions are:

  • Use the same syntax as in a formula
  • If your function accepts parameters, refer to these as %n in the function definition, where n is 1 for the first parameter passed to the function, 2 for the second parameter and so on up to 10 parameters. To include the % symbol in a macro, use %%
  • Use braces { } for comments. Comments included within braces are not evaluated
  • All text in macros is converted to upper case, except for text between quotes
  • Macros may call other macros, but there is a "nesting" limit of 20
  • Brackets should be used to ensure results are not affected by operator precedence

To define a QA Build function or macro:

  1. Enter the name of the function
  2. Select Macro for the function type
  3. Enter the function, taking care to use the correct syntax. Use %n to refer to the parameters passed to the function

The formulas you define in this way are evaluated as your program is compiled. If there are any syntax errors, these will be shown as occurring in the formula in which the function is used.

To use a function:

In the position in the formula where you wish to use the function, enter the name of the function, followed by brackets, and pass the parameters you wish to pass to the function

Example Macros: | Text: | Comments:
TIME_TO_NUMBER | HOUR(%1)*3600 + MINS(%1)*60 + SECS(%1) | This function returns the number of seconds since midnight for any given time
NUMBER_TO_TIME | (((00:00 SETHOUR((%1)/3600)) SETMINUTE(((%1)MOD3600)/60)) SETSECOND((%1) MOD 60)) | This function converts a number to a time, provided that the number means the number of seconds since midnight
TIME_PLUS_NUMBER | NUMBER_TO_TIME(TIME_TO_NUMBER(%1)+%2) | This function uses the two functions defined above to add a number representing the number of seconds since midnight to a time

For the benefits of using macros see Macro Formulas .


Constants

You can use the same entry screen as for Functions and Macros to enter a name for a constant and its numeric value for use in your program.

Function/macro name VAT

Type Macro

Function/Macro text

17.5

The use of numeric constants has the following benefits:

  • You can refer to a constant by the same name in any QA Build program on your database

  • The use of a constant name can improve the readability of your formulas

  • If the numeric constant ever changes, you need make only 1 change

    Example Constants:

    DELETE

    3

    The value −3 is substituted in a formula such as OPTION_NR DELETE

    PLUS

    +

    The formula A PLUS B evaluates to A + B


External C Functions

To define an external C function:

  1. Enter the name of the external function
  2. Select C function as the function type
  3. Enter the data types of the parameters followed by the result type of the function and the occurs checking number. For example, if your function has a time and a number parameter, and returns a time, and all are occurs 1, enter TNT0

For further information on C functions see When to Link QA Build with C

For further information on the occurs checking number see Occurs Checking Number.


Word Strings

Word strings are maps used for placing text in a field, avoiding the necessity to include text directly in a QA Build program. Because they are stored as maps, word strings can be easily manipulated using the Map Editor. This feature is particularly useful for multi language applications, because it allows you to take advantage of language support. Word strings are stored on the @WDATA mapset of your database.

The text stored in the maps cannot be accessed from within QA Build, but you can use word strings for substitution in formulas. For example, if you wish to display a message depending on the result of a calculation, you might set up a character field with the following formula:

if x>0 then "Positive" else if x<0 then "Negative" else "Zero"

This formula assumes your application is in English. If your application runs in more than one language, you could set up three word strings POSITIVE, NEGATIVE and ZERO and set up a map with the appropriate text for each string. To display the text, the type of the @ field is changed to W and the formula is changed to:

if x>0 then POSITIVE else if x<0 then NEGATIVE else ZERO

For each language your application runs in, you would enter the words for positive, negative and zero for the three word strings.

You are recommended to use maps wherever possible in multi lingual applications, because they make translation easy. If you wish to manipulate text from different languages within formulas, you are recommended to set up a table with a language selector as one of the keys.

To define a word string:

  1. Enter a name for the string
  2. Select word string as the function type
  3. For the Function/macro text field, enter a description for the string
  4. For the Associated map, create a map which displays the message. You have the option to create, copy, edit, delete, or change the number of the word string maps

To display the string:

  1. Add a @ field to the field processing list of your procedure. Select W for the data type
  2. In the formula for the field, select the name of the word string your wish to display. As the name of the word string evaluates to the number of the associated map, you can also select word strings numerically by specifying the number of the map containing the text

When you are printing a word string over another field, you must ensure that the word string text contains more characters than the over printed field. If you are using word strings for printing numbers as words, you may wish to take advantage of the Over another and Beside another output methods for printing. See Form Output Methods.


Design Decisions

This section covers the following topics:

  • Structure of Procedure Trees

Introduction

A program written in QA Build can be designed in the same way as a C program, using a Top Downwards or Bottom Upwards method, whichever you find most convenient. In practice, you will probably use a combination of these methods.

This section deals mainly with design decisions that are specific to QA Build.


Structure of Procedure Trees

Some structures are forced upon the programmer by the logic of the program, while in other situations there is a choice to be made. In particular, you often have to choose whether to insert a new procedure below or to the right of an existing procedure.

A common arrangement is:

where * indicates a repeated procedure. Here PROC4 is not repeated, so it is most natural to insert it to the right of PROC3, not below. You could put it below and insert a suitable condition to enter so that it is executed only once, but the condition would have to be evaluated on every repetition, so you would need a good reason for doing it that way.

The variable first_item is useful if a repeating procedure includes processing which needs to be carried out only on the first pass. If in the diagram above PROC3 does a batch access for some table and PROC2 should be executed only if at least one record is found then, unless you do a preliminary search (and a database access takes longer than evaluating a condition), you have to move PROC2 to within the repeat. The procedure must be re written in the form:

Anything that was done in PROC3 apart from the database access is now deferred until PROC5, and there is a condition to enter PROC2. You could use first_item as the condition, but remember that this variable is reset each time any procedure is entered or repeated. So if there was another procedure to the left of PROC2, you would need to save the value of first_item in your own local variable in the field processing of PROC3, otherwise its value will be lost when you arrive at PROC2.

Another common structure is:

where the same condition to enter governs several procedures. The three lower procedures need not have identical conditions to enter because a further condition to enter can be applied at the lower level. PROC1 frequently has a "dummy" procedure body which does nothing (except hold the entry conditions for subsidiaries).

The root procedure for a menu option often needs a "dummy" procedure body, as in this example:

In other situations there could be more than one possible arrangement and it may not be obvious which is best.

The procedures

and

might be identical in execution, so how do you decide whether to insert PROC3 below or to the right of PROC2? Consider whether PROC2 may be used elsewhere in this or another program; remember that whatever you decide will be effective globally.

Move sideways if you want to do any of the following:

  • Sometimes use PROC2 without PROC3
  • Execute PROC3 even when PROC2 terminates because the user presses Escape or because no record was found
  • Limit the depth of your program on the screen to make it easier to edit without needing to Zoom. Change the output device – you must start a new procedure tree for the new device
  • Separate groups of associated procedures to make the program easier to understand. For example, PROC2 might be concerned with database access and updating records, while PROC3 contains procedures for displaying and printing the information
  • Secure changes before doing anything else because secure changes is performed AFTER all subsidiaries have been executed

Move downwards if you want to do any of the following:

  • Always use PROC3 in conjunction with PROC2
  • Omit PROC3 if PROC2 is terminated by an Escape or by no record being found
  • Omit PROC3 if PROC2 is terminated by deleting a record, by a checkpoint failure, or by the record not being acceptable (fails a condition on the record)
  • Output to a device already selected in the previous procedure


Libraries

Programs having the Library program type are not compiled when you select All or Changed programs in the Program Compiler. They are simply collections of procedures and are not executable. The arrangement of the procedures within a library program has no significance.

A Library program is useful if you want to do any of the following:

  • Start with the lowest-level procedures and construct your program using a bottom upwards method
  • Keep all similar procedures together for ease of editing or to help you to identify suitable procedures or bodies for use elsewhere
  • Overcome or avoid the error Too many procedures in program. This error is encountered long before the program is too big to compile. By putting some procedures into a library and tying them there, you reduce the number of procedures that are simultaneously available for editing. The tied procedures are edited from within their library
  • Merge an overlay into the main program. Change the overlay program to type Library leaving it otherwise unchanged, because it will now be unnecessary to compile it separately
  • Tie the procedures "owned" by a particular member of a development team to warn other members not to edit these procedures. You cannot prevent them from doing so, but tying will minimise the risk of accidental changes

It is helpful to decide on some naming conventions before writing any programs, especially if the application could eventually be a large one. It is very difficult to locate a particular procedure or body in the list unless you know approximately where it must appear.

A 2 or 3 letter prefix can act as a reference to a particular library, so that you can quickly find where to view or edit a procedure.


Overlays

You can call one QA Build program from another; for details see Calling QA Build Programs . When you do this, the called program is called an overlay. Overlays are useful in the following situations:

  • The two programs combined into one are too big to compile
  • The combined program is not too big to compile, but compilation takes a long time and you are still at a development stage where a lot of changes are being made. The run time penalty of switching overlays is trivial in comparison
  • The combined program is not too big to compile, but it is too big to be run with the debugging option

Only in the first case should the final version retain the overlays, so it is only there that you need consider how most efficiently to divide the procedures between the two programs.

Select a natural break wherever possible – that is, a point in the program that is not often traversed, for example, after completing some initialisation or before entering a repeat loop constituting the main part of the program.

After executing an overlay with %call, control returns to the calling program which has to be re loaded. So, if the program logic allows a choice:

SMALL1 LARGE2
| is better than |

LARGE1 SMALL2

and

If you use the command tail %overlay, the values of all variables and table.roles which are processed in both the calling program and in the overlay will be passed between them. However, you must not use the value in a formula before it is declared it. Consequently you must name the table in your Table processing or the local field in your Field processing.

Remember also that there must be no gap in the chain that passes values from one program to another.

It is good practice to put all the necessary declarations into the root procedure, especially if the overlay structure is only temporary, and to refrain from putting anything else in that procedure. This makes it a simple matter to re assemble the parts into a single program:


Maps and Mapsets

It is good practice to limit the number of mapsets in your system. You need not always accept the default which creates a new mapset for each program. The reasons for this are:

  • It reduces the creation of duplicate maps by QA Build's automatic message generation routines – if all message maps are in the same mapset, there should be no such duplication
  • Re using existing main maps wherever possible reduces the amount of editing required when map layouts are changed
  • If the components of a program originated in several different libraries, confusion may arise if the final program uses many different mapsets inherited from its components.

There are some circumstances where your maps must belong to the same mapset or your program will not work properly:

  • Totalling from a field in one map into a field in another map (on screen already)
  • Attaching a window access or multiple choice map to a field on a map already displayed


Macro Formulas

Using Macro formulas can make your program easier to understand and to maintain. Consider using Macros for any of the following:

  • Long or difficult formulas – the macro name concentrates attention on the purpose of the formula
  • Frequently used formulas – save typing a long name every time
  • Numeric constants whose value gives no hint of their meaning
  • Constants whose value should not be hard coded and buried in your program because the value may need to be changed from time to time – the value of CENTURY, for example
  • Pieces of text which are not part of a map but are instead displayed or printed using a local variable with a formula – this is particularly helpful if you are likely to want to translate your application into another language


When to Link QA Build with C

You can mix QA Build with C in several ways. Although the techniques can be mixed, it is helpful initially to consider them separately:

  • You can call a QA Build program from a C program. For example, you may choose to write a high level menu program in C, because it needs to behave in ways you cannot achieve with QA Build. This technique is easy to use, but a disadvantage is that the QA Build programs cannot access the data of the calling C program. See Calling QA Build Programs from C.

  • You can write functions in C and add them to the QA Build function library. They are called from formulas in QA Build in the same way as DP4-provided functions. They access data from the QA Build program using value parameters and return a single value. For example, you might write a function NPV (net present value) for use in financial calculations. When you have written it and combined it into your Program Interpreter (PROGRUN), you can call NPV in the same way as you call standard DP4 functions such as SUM. See Adding Custom Functions to PROGRUN.

  • You can write QA Build procedure bodies in C instead of QA Build. They are called in the same way as ordinary QA Build procedure bodies. Your C code can access all the data of the QA Build program. For example, you might want to read data from an ASCII file in a QA Build program –at present QA Build does not support access to ASCII files. You could write a procedure body to open and read the file in C and then use the read data as required in the QA Build program. See Combining Procedure Bodies and Functions.

  • (This one applies only to Legacy Operating systems, and to 4.624) You can rebuild PROGRUN to accept a different maximum size of QA Build program. You can increase or decrease the size of the array used for storing the executing QAB program to either allow for larger programs or to save memory. See Changing the Maximum Program Size

Functions and procedure bodies written in C by users and called by PROGRUN are known as C exits.

Many QAB users build large and complex applications using only the standard QA Build facilities. The reason for this reluctance to mix C and QA Build is that each specially generated PROGRUN carries its own individual maintenance obligation.

Skill Required

Calling a QA Build program from a C program is easy, within the competence of any C programmer.

Altering the maximum size of QA Build programs involves only changing a constant, recompiling and linking. It requires no programming skill.

The other uses of C mentioned above entail pointers to access data; you must be competent with pointer manipulation and the use of casts. In a typical application you require only a small number of C functions or procedure bodies in comparison with the amount of work to be done in QA Build. In some organisations one skilled C programmer does this work as a service to a team of QA Build developers.


Calling QA Build Programs from C

You can easily call a QA Build program from a C program. Below is an example of a call to a QA Build program from an existing DP4 application written in C, with the changes from the original program shown in bold type.

/* LIBMAKE keywords to create .H include file */
/*
#c
#db MYDB
#mapname CPROG
#end
*/
#include "dp4.h"
#include "cprog.h"
#include "cexits.h"
...
void process()
{
  /* C application code */
  ...
  /* calls to QA Build */
  Qab_init();
  Qaction_program("MYPROG");
}

This example assumes that the application database is the same for the C application and the QA Build program, MYPROG. Also in this case there are no calls to DP4 functions in process() after the call to Qaction_program(), but you can include code after the call to Qaction_program() if you like. In this case you should pay attention to the advice in Resuming After Qaction_program().

For an introduction to writing DP4 applications using C see Installing and Using DP4 C Libraries and tools in the C++ Programmers Reference. The following is a summary of the steps needed to convert such a C program to one that calls a QAB program:

  1. Setup and compile the QA Build program.

    The QA Build program is created in the standard way. No additional definitions are required.

  2. Modify the C code

    In many cases this all you do is add calls to the functions Qab_init() and Qaction_program()and include the header file cexits.h that declares them. These functions are described in the section DP4 C Functions for calling QAB programs.

  3. Building the Program

    To build your program you need to include the appropriate QAB library with the list of libraries your program links with. If you are building a legacy program you can use QABLINK to link your program instead of DP4LINK. If you are using Visual C++ then add ntqab.lib (on regular Windows), or ccqab.lib (on Windows CE) to the list of libraries used. On Unix and Linux link with ccqab.a.

    Example command lines to build your program are shown for Windows and Unix below:

    Windows: cl -DWIN32 -Zp1 -Oxs progname.c -link ntqab.lib ntown.lib -entry:mainCRTStartup

    Unix or Linux: $(CC) -o progname $(CFLAGS) progname.c ccqab.a ccown.a

The QA Build program called can perform any action available with QA Build processing, including overlays and C exits.

Unless you call C exits, no extra definitions are required and you do not need to create any C interfaces with PROGCOMP.

If you call C exits, you must modify the source files produced by PROGCOMP to produce a hybrid of the two techniques. You must not try to call a QA Build program from within a C exit.

Adapting Non DP4 Programs

If your program uses only standard C functions, you can call QA Build by making the following changes to your source code:

  1. Include dp4capi.h and cexits.h
  2. Include the file which is generated by LIBMAKE
  3. Rename your main() to process(). You should note that process() is a void function, and that the program exit code must be set by calling dp4_halt()
  4. Add the calls to Qab_init() and Qaction_program()

Once this is done follow the instructions as given in the previous section.


Resuming after Qaction_program()

Having called a QA Build program, if you wish to resume with calls to DP4 screen functions, you must reload the mapset. Additionally, it is advisable to call db_open() again.

To reload a mapset, use the DP4 function map_load() after calling Qaction_program(), for example: map_load("CPROG");

Although it is not always necessary to call db_open(), there may be times when the QA Build program opens the database in a different mode from the C code. To avoid hard coding the database generation number and the open mode you can to use the following scheme:

/* Headers and Definitions */
...
void process()
{
  int ret_code;
  int save_db_generation = db_generation;
  int save_db_open_flags = db_open_flags;
  /* C application code */
  ...
  Qab_init();
  ret_code = Qaction_program("QABPROG"):
  db_open("MYDB",save_db_generation,save_db_open_flags);
  map_load("CPROG");
  /* test the return value from Qaction_program */
  if (ret_code!=0)
  {
    /* Warning that call to QA Build was unsuccessful */
    map_pause(2);
  }
  /* Additional C application code */
  ...
}

An even simpler alternative is simply to call the LIBMAKE generated init() again.

Screen Handling in C Programs

You are recommended to use different mapsets for the QA Build and C parts of the program. If you use the same mapset for both, the QABUTIL Weed operation (see Deleting Programs ) would remove any maps used exclusively by the C exits because it recognises (quite correctly) that these maps are not used by any QA Build procedures.

If you want to preserve the screen before calling the QA Build program and recover the screen on return to the C code, you can use map_new_window() and map_old_window() as shown below.

  map_new_window();
  Qaction_program("QABPROG");
  map_old_window();


LIBMAKE

The LIBMAKE preprocessor creates a file of definitions and code. For a full explanation see the C Programmers Reference Manual.

If your QA Build program calls a non DP4 C program, you should add the following definitions to the beginning of your source file.

/*

#c

#db MYDB

#mapname CPROG

#end

*/

If your source file is CPROG.C, LIBMAKE will produce a file called CPROG.H. Use the following command:

LIBMAKE CPROG.C


Linking with QABLINK

The batch file QABLINK links your object code with DP4 object modules and libraries. There is an appropriate version of QABLINK for each compiler and memory model supported. Use the following command:

QABLINK CPROG

If you want to run the QA Build part of your program with the PROGRUN debugging option, include the command tail D with QABLINK:

QABLINK CPROG D

You do not need a command tail when running your program.

The batch file QABLINKS is similar to QABLINK, but it creates a smaller executable program by excluding standard scientific operators such as LOG and SIN.

If you are linking a non DP4 program, the object modules LCROOT2 (Lattice), MCROOT2 (Microsoft) and HCROOT2 (Metaware) provide operating system independence. However, with some of the standard C functions in your program, you may find link errors. If this happens, remove the "ROOT" module from the link line.


DP4 C Functions

Qab_init

Syntax:

void Qab_init()

Example:

Qab_init();

The function Qab_init() must be called before Qaction_program(). However, once Qab_init has been called, Qaction_program() can be used repeatedly without another call to Qab_init().

Qaction_program

Syntax:

int Qaction_program(char * qab_prog)

qab_prog;

Example:

if (Qaction_program("REGIONS"))

{ ..

Qaction_program calls a QA Build program. The name in quotes may be in upper or lower case.

The return value is zero if the QA Build program was executed and non zero otherwise.

You may call Qaction_program any number of times in your C program. You may not call it from a C Exit.


Adding Custom Functions to PROGRUN

Introduction

The tasks involved in adding custom functions to the Program Interpreter (PROGRUN) are as follows:

1 | Set up the C Function Definitions | Using the QA Build Program Editor, define the formula operators as C functions
2 | Calling C formula operators from QA Build programs | Formula operators created as C functions may be invoked in the same way as the standard QA Build formula operators
3 | Write C functions in a .C source file | Write a C function for each formula operator defined
4 | Compile QA Build Programs | Compile the QA Build programs which use the new operators
5 | Generate C Function Interface | Generate the C function interface using the QA Build Program Compiler. Accept the default settings. A file called QCFUNC.C will be generated
6 | Compile all the source files | Use your C compiler to compile QCFUNC.C and the source files containing your C functions
7 | Create .LNK file | Create a file called, for example FPROGRUN.LNK which consists of this line: progrun qcfunc <your C modules>
8 | Link the modules | Link using the batch file supplied with DP4: QABLINK FPROGRUN This will create an executable program called FPROGRUN, which contains the functionality of the original PROGRUN together with support for your functions
9 | Run the generic progrun | Use the following command and specify the database and any program FPROGRUN


C Function Definitions

When using a C formula operator, a QA Build program requires information about the operator's parameters and result. Consequently you must define the name, the datatypes and the number of occurs in the Program Editor.

To set up a C function

  1. In the Program Editor select the Function definition option from the PROGMAKE facilities menu
  2. In the QAB function/macro set up dialog specify a suitable Function name
  3. Specify the type as C function
  4. In the Macro text box enter the datatypes of the function's parameters. Start with the parameters in order, followed by the datatype of the result, followed by the occurs checking number. For further on these see Function Datatypes and Occurs Checking Number .


Function Datatypes

The datatypes allowed for parameters and results are C, D, N, T and Y. Fields of type M, P, S and U may be used in functions which accept datatype C. Fields of types I,J,K and L may be used in C formula operands which accept datatype N.

QA Build Type | Function Definition Type | C Type
C, M, P, S, U | C | char *
D | D | datetype *
I, J, K, L, N | N | double *
T | T | timetype *
Y | Y | boolean *

Character parameters are like normal DP4 C character variables. They do not have a terminating 0.


Occurs Checking Number

The number of occurs is described by the occurs checking number. You do not need to define the exact number of occurs for each parameter. Use the table below to select the occurs checking number which best describes the number of occurs of the parameters and result.

Number of Occurrences |  |  |
First Parameter | Second Parameter | Result | Occurs Checking Number
1 | 1 | 1 | 0
any | any | 1 | 1
any | any | sum of parameter occurs | 2
any | 1 | same as first parameter | 3
1 | any | same as second parameter | 4*
any | any | same as first parameter | 5
any | 1 | 1 | 6
any | any | up to 128 | 7†
1 | 1 | up to 128 | 8†

For C datatypes, the number of occurs must be interpreted differently:

Number of occurs = length x occurs in QA Build program

* Do not use checking number 4. This is reserved for functions built into QA Build

† Except for result type C, avoid occurs checking numbers 7 and 8 as they require a lot of QA Build's internal stack


Calling C Formula Operators

You can call a C function in the same way as any of the standard QA Build operators. For example, a function defined as MULTIPLY with the function text NNN0 has been devised to multiply two parameters together and return the result. The following syntax assigns the return value 36 to a field:

MULTIPLY(12,3)


Generate C Function Interface

The C function interface is a small C source file which allows any QA Build program to call any C formula operator. The source file is created from the C Function definitions.

To generate the C function interface

  1. Run the QA Build Program Compiler
  2. Select the option Generate C Function Interface
  3. Accept the default output selections to create a file called QCFUNC.C


Writing C Formula Operators

Overview

Below is a simple example of a source file containing C formula operators:

#include "cexits.h"

void f_multiply(result,first,second)
double *result, *first, *second;
{
  /* Function to multiply two numbers */
  *result = *first * *second;
  set_scale(para_scale(1) + para_scale(2));
  set_occurs(1);
}

/**/

void f_unittoword(word,number1,number2)
char *word;
double *number1, *number2;
/* cannot use integer parameters */
{
  /* Function to convert a numeral to a word */

  int i;
  static char *word_for_nr[] =
  {
    "Zero","One","Two","Three","Four",
    "Five","Six","Seven","Eight","Nine"
  };
  strcpy(word,word_for_nr[(int) *number1]);
  set_occurs(5);
}

The source file can contain any number of functions. It must include the file cexits.h, which is supplied with DP4.

The major points to consider are the function name, the parameters and the use of macros such as set_scale and para_scale. These topics are covered in detail in the following sections.

Function Name and Type

The function name is the same as the C function defined in the Program Editor, in lowercase and prefixed with f_.

The use of the f_ prefix avoids clashes with existing library functions. However, if you wish to change or delete the prefix, you can edit maps 40 to 43 of the mapset, PROG2C, on the SYSTEM database. These maps are used by the Program Compiler to generate the C Function interface.

The function should be of type void.

Example:

Consider a C formula operator defined as MULTIPLY from the C Function definition option in the Program Editor. The C function in the source code is defined as: void f_multiply().

Parameters

A QA Build program passes parameters as values when it calls a C formula operator, but the C function itself accesses parameters as pointers. The first parameter of the C function is used for the return value of the formula operator. The second and the third parameters of the C function correspond to the first and second parameters of the formula operator respectively.

The pointers that are received by a C function do not point to the original data item within QA Build but point to items on QA Build's internal stack.

The allowable parameter types are listed in the table of Function Datatypes .

Parameter Control

For all return fields you must set the number of occurs, even if it is only 1. For all numerical return fields, you must set the scale factor. These operations can be performed using special macros which are defined in CEXITS.H. In addition, there are macros to obtain the same properties from the parameters sent.

If you want to pass more than two parameters from a C formula operator you must use a macro to obtain an address pointer. You can then use other macros in the same way as for the first two parameters.

Macros for Parameter Control

Macro | When to Use | Explanation
set_occurs(occurs) | Returning a value | Sets the number of occurs of the return field. For example, to set the number of occurs to 5: set_occurs(5);
set_scale(scale_factor) | Returning a numerical field | Sets the scale factor of the return field. For example, to set the scale factor to 2: set_scale(2);
para_occurs(para_nr) | Passing multi-occurs parameters | Obtains the number of occurs of a specified parameter. For example, to obtain the number of occurs of the first parameter: value = para_occurs(1);
para_scale(para_nr) | Passing numerical parameters | Obtains the scale factor of a specified parameter. For example,to obtain the scale factor of the2nd parameter: value = para_scale(2)
para_address(para_nr) | Passing more than two parameters | Obtains the address of a specified parameter. For example, to obtain the pointer of the 3rd parameter: p_para3 = para_address(3);

The macros set_occurs() and set_scale() must be called after all other data operations in the function, as they can otherwise have undesirable side effects.


Compiling the C Code

You must compile both your C source file and the C procedure interface file. The batch files supplied with DP4 are useful because they include the necessary compile options. For the Microsoft C compiler use the command:

MC filename

Other compilers require similar commands.


Create .LNK file

You need a .LNK file to link the required object code modules. Create a file called, for example, FPROGRUN.LNK containing a single line:

progrun qcfunc <your C module>

Set up this file using an editor, making sure that there is no terminating <CR> (Carriage Return).


Link the Executable Program

Link the executable program with QABLINK, the batch file supplied with DP4. There is an appropriate version of QABLINK for each compiler and memory model supported. Use following command: QABLINK FPROGRUN where FPROGRUN is the name of the .LNK file you have created. This will produce the executable program FPROGRUN.EXE.

If you want to run the QA Build part of your program with the PROGRUN debugging option, include the command tail D with QABLINK: QABLINK FPROGRUN D

You do not need a command tail when running your program. To create a slightly smaller executable program you can use QABLINKS. This is similar to QABLINK, but excludes the QA Build scientific operators.


PROGRUN/Database Compatibility

To produce your new PROGRUN with support for your own functions, you must set up function definitions on your database. Your new PROGRUN will therefore only work correctly on databases that contain the same function definitions with the same internal reference numbers. You can resolve this problem in either of two ways:

  • Build your PROGRUN so that it is dedicated to one database and QA Build program (which can be a menu program capable of calling all the others)
  • Ensure that all databases are compatible

Instructions for building a dedicated PROGRUN are given in the section Customising the Program Interpreter . If your program does not need a real procedure body written in C, add a dummy procedure of that type to persuade PROGCOMP to generate the requisite files.

You can ensure that all databases are compatible either by putting your functions into the seed database, BD, and having everyone develop from this, or by using QABUTIL to export function definitions from a master database and import into all the others.


Customising the Program Interpreter

Introduction

A valuable feature of procedure bodies written in C is that they can directly manipulate any of the tables and variables used within the QA Build program. The essential links between the data addresses in the Program Interpreter (PROGRUN) and those in the C Exit can be established either at compile time, giving a PROGRUN capable of running just one program, or at run time, giving a PROGRUN capable of running many programs. The first type is called a dedicated PROGRUN, and the second a generic PROGRUN.

It is easier to write a dedicated PROGRUN, because you do not have to write any code to assign addresses. With a dedicated PROGRUN, however, there are overheads during development, as you have to recompile and link your C code every time you change your QA Build program. For this reason we recommend that you master the generic technique first and only to use the dedicated technique as a way of producing a final program that runs faster. If you wish to have C Exits in overlaid programs you must use a generic PROGRUN, because each overlay is an independent program.

Generic PROGRUN: Summary of Tasks

1 | Setup QA Build program | To call a C procedure body create a procedure body of type Link to C
2 | Compile QA Build Program | Select the program you want to compile. At the end of compilation press Esc. Do not generate a .C file
3 | Generate C Procedure Interface | Use the QA Build Program Compiler to generate the C procedure interface. Accept the default settings. A file called QCBODY.C will be generated
4 | Create table definitions | Create a file with #pointer and #table definitions and run LIBMAKE to produce a .H file
5 | Write .C file | For each C procedure body write a function to perform the required processing. Include the .H file created in step 4
6 | Compile all the source files | Use your C compiler to compile QCBODY.C and the source files containing your C functions
7 | Create .LNK file | Create a file called GPROGRUN.LNK which consists of this line: progrun qcbody <your C modules>
8 | Link the modules | Link using the DP4-supplied batch file: QABLINK GPROGRUN This will create an executable program called GPROGRUN which contains all the functionality of PROGRUN plus C exits
9 | Run the generic progrun | Use the following command: GPROGRUN Specify the database and any program

Dedicated PROGRUN: Summary of Tasks

1 | Setup QA Build program | To call a C procedure body create a procedure body of type Link to C
2 | Compile QA Build Program | Select the program you want to compile. Do not generate a C function interface or a C procedure interface
3 | Generate the .C file | At the end of the program compilation you will be prompted to generate a file. Accept the defaults. A file will be created with the same name as the program, and with include statements for the .H and .QAC files
4 | Run LIBMAKE | LIBMAKE reads the database and sets up table declarations and some initial processing in a file with extension .H. Use the following command: LIBMAKE DPROGRUN.C
5 | Write C functions in .QAC file | For each C procedure body write a function to perform the required processing. Give the .QAC file the same name as the QA Build program
6 | Compile the C program | Use your C compiler to compile the .C file
7 | Link executable program | Use the QABLINK.BAT batch file supplied with DP4: (Do not link PROGRUN.OBJ) QABLINK DPROGRUN This will create an executable program called DPROGRUN
8 | Run the dedicated PROGRUN | Use the following command: DPROGRUN

In the instructions, the name DPROGRUN is given to the QA Build program calling the C exit

If you modify the data dictionary of a QA Build program, you will have to repeat the whole process. This is one reason for using a generic PROGRUN during development.

Because many of the processes are the same for both types of PROGRUN, the following account combines the two, highlighting the steps that are specific to one type.


Setting up QA Build (Dedicated PROGRUN only)

To call a C procedure body in your QA Build program you must set up a procedure of type Link to C. The QA Build Program Editor will behave as if the procedure body was of any regular procedure type.

To set up the procedure

  1. In the Procedure Dialog specify the Procedure Type as Link to C
  2. Specify the main mapname as CBODY1
  3. Specify the remarks mapname as CBODY2
  4. Set the procedure as a repeated procedure with Condition to repeat as Condition 1

Answer these questions in the same way as for any other procedure type

Compile your QA Build program in the normal way. At the end of the program compilation create a .C file. There is no need to generate a procedure interface.

To compile your QA Build program

  • Select output devices as follows:

Screen ? No
Printer ? No
File ? Yes
Print device DEFAULT_PRINTER
Output file PROG1 ( .C )

This entry screen appears at the end of the program compilation. Accepting the defaults gives you a .C file of the same name as your QA Build program


Generating the C Procedure Interface (Generic PROGRUN only)

The C procedure interface is a small C source file which allows any QA Build program to call any C procedure body. The source file is created from every procedure body of type Link to C.

To generate the C procedure interface:

  1. Run the QA Build Program Compiler
  2. Select the option Generate C Procedure Interface
  3. Accept the default output selections to create a file called qcbody.c


Writing the .C File (Generic PROGRUN) or .QAC File (Dedicated PROGRUN)

Below is a simple example of a C function which is called from a QA Build program:

int b_open_file()
{
  char * impfile;
  /* obtain the filename from the local QAB field, IMPFILE */
  impfile = (char *) Qget_pointer(0, "IMPFILE");
  if (tf_check(impfile))
    file_nr = tf_open(impfile, VF_RO);
  return 0;
}

The major points to consider are function names, database and local fields used in the QA Build program and return values. These topics are covered in detail in the following sections.

Function Name

The function name is the same as that of the procedure body, in lowercase and prefixed with b_.

The use of the b_ prefix avoids any clashes with library functions. However, if you wish to change or delete the prefix, you can edit maps 9 and 10 of the mapset PROG2C on the SYSTEM database. These maps are used by the Program Compiler to generate the calls to your C functions in the .C file.

Example

Consider a procedure body in a QA Build program called OPEN_FILE. The procedure type is Link to C. Within the Program Editor the procedure body is named: OPEN_FILE

Within the C source code the function is named: b_open_file

Local Fields

The local fields used in the calling QA Build program may be accessed using pointers.

Generic PROGRUN

For a generic Program Interpreter you need to use the function Qget_pointer() to obtain the value of the pointer. Value of pointer = Qget_pointer(0, "field name");

The field name should be the name used in the QA Build field processing. The name can be in upper or lower case but should not contain underscores.

Example

Within a QA Build program IMP_FILE is defined as a character field of length 12 and single occurs.
Within the C source code IMP_FILE is accessed using a pointer defined: char * impfile;

The appropriate value of the pointer is set using Qget_pointer(): impfile = (char *) Qget_pointer(0, "IMPFILE");

Dedicated PROGRUN

For a dedicated Program Interpreter you do not need to set the value of the pointer.

If you wish to remove the v_ prefix you should edit the maps 25 to 38 in the PROG2C mapset on the SYSTEM database.

Example

Within a QA Build program IMP_FILE is defined as a character field of length 12 and single occurs.
Within the C source code IMP_FILE is accessed using a pointer defined:char * v_impfile;

Standard Fields

Standard fields, such as TODAY are not accessed using Qget_pointer(), but are accessed directly. Thus: this_day = today;

Database Fields

Database fields used in the calling QA Build program may be accessed using structure pointers. These pointers are defined in the .H file generated by LIBMAKE. Note that for a Generic PROGRUN, you must obtain the value of the pointer with Qget_pointer() before a record can be accessed: record pointer = Qget_pointer(Table Reference Number, "role name")

For either type of PROGRUN fields may be accessed, in lowercase, using the syntax: table_name.table_role->field_role.field_name

For blank field and table roles the syntax becomes:table_name.field_name

Example (Generic PROGRUN

Consider a table EMPLOYEE which is given a role of MAIN within the Program Editor. Within the C source code the record pointer is obtained using Qget_pointer():
employee.main = (struct EMPLOYEE*) Qget_pointer(_EMPLOYEE,"MAIN");

The field EMP_NR with role MANAGER can then be accessed by: employee.main->manager.emp_nr

Note that this is the reverse to the field role order within QA Build formula processing (employee.main.emp_nr.manager).

The function Qget_pointer() returns 0 if the named item is not one of the variables in the current QA Build program. You will need to test that the value returned is not 0 before using it. One way to do this is to write your own enclosing routine for calls to Qget_pointer():

void *my_Qget_pointer(int number, char * name)
{
  void *retcode = Qget_pointer(number,name);
  if (retcode == 0)
  {
     map_pause(HORRIBLE_ERROR);
     halt();
  }
}

If you want to add table variables of your own, not used in the QA Build program, you may have problems. LIBMAKE will generate a structure definition and an initialised pointer variable for the structure. You need a simple local variable to allocate space for the structure in the data segment. If your table is called TABLE, you can declare a record like this: struct TABLE my_table = {sizeof(struct TABLE),_TABLE};

Example (Dedicated PROGRUN)

Consider the field EMP_NR with a role of MANAGER, belonging to the table EMPLOYEE with a role of MAIN.

Within QA Build formula processing it is accessed by:employee.main.emp_nr.manager

Within the C code of a C exit it is accessed by:employee.main->manager.emp_nr

Function Type

The function should be of type int. It returns a value which the QA Build program interprets as follows:

Returned Value | Meaning
0 | Successful termination
ESCAPE | Exit the calling QA Build procedure
SPACE_BAR | Abort the current instance of the procedure (for repeated procedures only)

Example

int b_read_line()
{
...
  if (s != END_OF_FILE)
  {
...
    return 0;
  }
  else
  {
    tf_close(file_nr);
    return ESCAPE;
  }
}

Example Source Code (Generic PROGRUN)

#include "dp4.h"
#include "dex2.h"

/* file_nr is used by both C functions */
int file_nr;char *s;
/* pointers to handle local variables used in QAB program */
boolean * filefound;
char * impfile;
/* Offsets defined from input file */
#define NAME_OFFSET 4
#define DAY_OFFSET 24
#define MONTH_OFFSET 27
#define YEAR_OFFSET 30
#define SEX_OFFSET 35
void * test_Qget_pointer(int number, char * name);
/* Function to open file */
int b_open_file()
{
  filefound = (boolean *) test_Qget_pointer(0, "FILEFOUND");
  impfile = (char *) test_Qget_pointer(0, "IMPFILE");
  if (*filefound = tf_check(impfile))
    file_nr = tf_open(impfile, VF_RO);
  return 0;
}

int b_read_line()
{
  /* Function to read the lines in the file */
  employee.main = (struct EMPLOYEE *) test_Qget_pointer(_EMPLOYEE, "MAIN");
  if (s = tf_txt_read(file_nr))
  {
    employee.main emp_nr = atoi(s);
    memcpy(employee.main emp_name,s+NAME_OFFSET,20);
    employee.main->date_of_birth.day = atoi(s+DAY_OFFSET);
    employee.main->date_of_birth.month= atoi(s+MONTH_OFFSET);
    employee.main->date_of_birth.year= atoi(s+YEAR_OFFSET);
    memcpy(employee.main sex,s+SEX_OFFSET,1);
    return 0;
  }
  else
  {
    tf_close(file_nr);
    return ESCAPE;
  }
}

void * test_Qget_pointer(int number, char * name)
{
  void * retcode = Qget_pointer(number,name);
  if (retcode == 0)
  {
    map_pause(HORRIBLE_ERROR);
    halt();
  }
}

Example Source Code (Dedicated PROGRUN)

/* file_nr is used by both C functions */
int file_nr;
/* Offsets corresponding to input file */
#define NAME_OFFSET  4
#define DAY_OFFSET  24
#define MONTH_OFFSET 27
#define YEAR_OFFSET  30
#define SEX_OFFSET  35

int b_open_file()
{
  /* Function to open file */
  if (*v_filefound = tf_check(v_impfile))
    file_nr = tf_open(v_impfile, VF_RO);
  return 0;
}

int b_read_line()
{
  /* Function to read the lines in the file */
  char *s;
  if (s = tf_txt_read(file_nr))
  {
    employee.main->emp_nr = atoi(s);
    memcpy(employee.main emp_name,s+NAME_OFFSET,20);
    employee.main->date_of_birth.day = atoi(s+DAY_OFFSET);
    employee.main->date_of_birth.month = atoi(s+MONTH_OFFSET);
    employee.main->date_of_birth.year = atoi(s+YEAR_OFFSET);
    memcpy(employee.main->sex,s+SEX_OFFSET,1);
    return 0;
  }
  else
  {
    tf_close(file_nr);
    return ESCAPE;
  }
}


Creating Table Definitions (Generic PROGRUN only)

You can create the necessary table definitions using the DP4 preprocessor, LIBMAKE. The preprocessor creates a file of definitions and code. For a full explanation see the C Programmers Reference Manual.

To create the table definitions, include the following lines in your source code:

/*

#pointer

#db MYDB

#table employee.main, table.role;

#end

*/

Run LIBMAKE with the following command:

LIBMAKE SOURCE.C

From the .H file generated, delete the two functions, init() and finish(). These functions are not required for a generic Program Interpreter.


Compiling your C Code

You must compile your C source file and, for a generic PROGRUN, the C procedure interface file.

The batch files supplied with DP4 are useful because they include the necessary compile options. For the Microsoft C compiler (small model program) use the command:

MC my_prgrn

where my_prgrn.c is the name of your C source file. Other compilers require similar commands.


Creating the .LNK File (Generic PROGRUN only)

You need a .LNK file to link the required object code modules. Create a file called GPROGRUN.LNK containing a single line:

progrun qcbody <your C modules>

Set up this file using an editor, making sure that there is no terminating <CR> (Carriage Return).


Linking the Executable Program

Link the executable program with QABLINK, the batch file supplied with DP4. There are versions of QABLINK for each compiler and memory model supported. (For a dedicated PROGRUN do not link PROGRUN.OBJ.)

Use the following command:

QABLINK MY_PRGRN

where MY_PRGRN is the name of the .LNK file you have created. This will produce the executable program MYPRGRN.EXE.

If you want to run the QA Build part of your program with the PROGRUN debugging option, include the command tail D with QABLINK:

QABLINK MY_PRGRN D

You do not need to use a command tail when running your program.

To create a slightly smaller executable program you can use QABLINKS. This is similar to QABLINK, but excludes the standard scientific functions.


Improving the Efficiency of the C Exit (Generic PROGRUN only)

Qget_pointer() is quite an expensive call to make, as it entails either database or workfile access. This becomes noticeable if a program makes more than a few calls to the routine. For example, you might have set up a function which made calls to Qget_pointer() for all the variables whether or not they are needed in this C Exit.

If a procedure body C exit is only called from one program, you can assume the pointers returned by Qget_pointer() will always be the same. In particular, if you use the architecture for a generic PROGRUN simply to avoid the need for recompilation, then you can initialise the pointers just once. This achieves the flexibility of the generic PROGRUN, but the C Exit calling mechanism is almost as efficient as in a dedicated PROGRUN.

You must not assume that Qget_pointer() will always return the same value for a variable used in several programs.


Combining Procedure Bodies and Functions

It is possible to create a generic or dedicated Program Interpreter which combines both procedure bodies and formula operators. Follow the procedures for creating the appropriate type of PROGRUN and link it as follows.

Create .LNK File

You need a .LNK file to link the required object code modules.

Create a file called CPROGRUN.LNK containing a single line as follows. For a generic PROGRUN:

progrun qcfunc qcbody your C modules

For a dedicated PROGRUN:

<your main module> qcfunc <your C function modules>

Set up this file using an editor, making sure that there is no terminating <CR> (Carriage Return).

Link the Executable Program

Link the executable program with the batch file supplied with DP4. Use the following command:

QABLINK CPROGRUN

where CPROGRUN.LNK is the name of the .LNK file you have created. This will produce the executable program CPROGRUN.EXE.


Testing a QA Build Program with C Exits

You can test the QAB part of a mixed QAB/C program in isolation with the standard Program Interpreter or the Debugging Program Interpreter in the normal way. You must supply the command tail dummyc when running the program, otherwise you will get a system error when QA Build attempts to run the C Exit.


Changing the Maximum Program Size

The following table lists the maximum QAB program size in various releases of DP4.

Platform & Release | Maximum Size for standard PROGRUN/PROGRUND | Maximum size achievable in custom built PROGRUN
Legacy Platforms. All releases | 42Kb | 64Kb
32 bit platforms. 4.623 or earlier | 64Kb | 64Kb
32 bit platforms. 4.624 or later | 4Mb | 1Gb

Up to release 4.624 this page only applies to legacy platforms, because on 32 bit platforms QAB only supports programs up to 64K in size, and the standard progrun is built using this size. As of release 4.624 this information also applies to 32 bit platforms

Sequence of Tasks

  1. Edit cexits.h and change MAX_PROG_SIZE to the desired value
  2. Compile progprog.c
  3. Modify your project file, by adding PROGPROG.C to the build, which will then be used instead of the module in the library

Selecting the Value for MAX_PROG_SIZE

Legacy versions

Standard legacy versions of PROGRUN can execute programs up to 42,000 bytes in size.

If you are using the small or medium model you are unlikely to be able to increase the size of the program very much because the data area is already close to the 64 K limit. There are several Kbytes free in the xCQABS libraries but not in the xCQABSD (debug) versions.

Suggested maximum sizes are given in progprog.c, but you can use trial and error to see how far you can go before getting fix up errors from the linker.

32 bit versions

4.624 32 bit PROGRUN can execute programs up to 4MB in size, which is likely to be enough for even the largest QAB programs, but you can build a custom version with either a smaller maximum (to save memory) or a larger limit.


Compiling a QA Build Program

This section covers the following topics:

  • Compilation Options
  • Command Tails for the Program Compiler
  • Compilation Display
  • Program Analysis
  • Error Messages during Compilation

Introduction

The Program Compiler utility (PROGCOMP) compiles a QA Build program into a set of instructions that can be interpreted by the Program Interpreter (PROGRUN). When your program is compiled, a number of checks are made. If any of these fail, PROGCOMP produces an appropriate error message.

To compile a QA Build program from the DP4 menu

  1. Select QA Build facilities
  2. Select Program Compiler
  3. Enter the name of the database
  4. Select Changed programs
    or
    Select Selected programs and then enter the name of the program

To compile a QA Build program from the command line

PROGCOMP -DB database name -PROG program name

For details of the command tails see Command Tails for the Program Compiler


Compilation Options

The compilation options are as follow:

QAB PROGRAM COMPILATION

1 Changed programs
2 All programs
3 Selected programs
4 Generate C function interface (F)
5 Generate C procedure interface (P)
6 Program load mechanism (L)
8 Exit (X)

The three most commonly used options are:

Action | Use for | Result
Changed programs | Compilation following a modification to one or more programs or macros | All programs affected by changes since the last QA Build compilation are compiled
All programs | Compilation following structural changes made to the database | All programs are compiled, whether or not they have been edited
Selected program | Compiling when working on a specific program | The specified program is compiled. Other programs are not affected
Take care that you do not compile a program that is being run by another user or one that is called by another program currently being executed The other options are:
Generate C function interface | Generating C code for use with C Exits | A C function definition is produced
Generate C procedure interface | Generating C code for use with C Exits | A C procedure body definition is produced
Program load mechanism | Selecting programs to be loaded from a QXE file rather than the database | A list box of all the programs is displayed and you can select and de-select programs to be loaded from disk. If you are running pure client server over a slow network loading programs from disk my improve program load times. However this options is rarely required using modern hardware.

For further information on the use of C code in conjunction with QA Build see When to Link QA Build with C


PROGCOMP

The QA Build program compiler. Run after creating or amending QA Build programs.

Refer to Compiling a QA Build Program in the QA Build Developer's Manual for more information about how to use this program.

Command Tail | Description
−PROG name | Specify the program to compile
−ALIGN | Aligns data in accordance with the packing used on RISC type machines
−ALL | Compile all programs regardless of status
−CHANGED | Compile only changed (or new) programs
−COMPATIBLE | Ensures compatiblity of compiled code with versions of PROGRUN as old as 4.515
−VERSION nn | Put the version number nn into the compiled program
−NOCC | Suppress the generation of C files
−GENERIC | Produce C files for generic PROGRUN or C exits from subprograms
−HEAP nnnnn | Allows you to compile program larger than 64k. Limit of extra 56K
−NOISY | Display procedure names during compilation
−PAUSE | Pause after -ALL or -CHANGED
−PRIVATE | Increase the speed of overlay switching by not passing the value of local variables and tables beginning with @ between programs
−SHARE | Force the use of shared mode
−DEBUG | Put additional information into the program for PROGRUND (to allow the debugging of QA Build programs)
−SHRINK | Reduce the size of the compiled program
−SIZE nnnnn | Specify maximum program size in bytes. The theoretical maximum value is 64K -1 (65535) but values above 65500 may compile incorrectly. If you want to use values above 42000 on a 16 bit system, you will have to create a customised version of PROGRUN
−400 | Do not recognize "global" variables introduced since 4.400 (may be useful for compiling very old QAB programs)

Compilation errors are usually sent to the screen. You can redirect the output with the standard -FILE, -PRINTER options


Compilation Display

While a program is being compiled, three types of information are displayed:

Header information | Displays the name of the procedure that is being compiled
Messages | Pop-up warning and error messages that display when your program is analyzed
Program information | Reports on the code and data size of the program and enumerates the error and warnings given

A header like the following is displayed throughout the compilation process:

A summary like the following is displayed below the header when compilation is complete:


Program Analysis

The Program Compiler analyses the program to determine the order in which the various steps should be executed. The following parts of the program structure are defined:

Analysis | Process
When to access the database The record is accessed when all the key fields are set up. A key is set up by any of the following methods:

  • The key fields are defined in the field processing
  • The keys are set up in a parent that has already been defined
  • The user enters values into the key fields
Depth to use for searches | This is set to the number of keys that can change during the search. Fields that stay the same are those which are copied from the parent table, or are assigned in a formula that is "Applied always"
Point to go back to when a validation fails | If a validation fails, the cursor returns to the first key field


Error Messages during Compilation

This section lists the error messages that can appear when you compile a QA Build program. There are three levels of error message in QA Build:

  • Fatal Errors
  • Serious Error Messages
  • Warning Messages

For each error message, one or more possible causes is listed, with the appropriate remedial action. The causes and actions described for each message are not exhaustive, but are intended to help the debugging process.


FATAL ERRORs

If a FATAL ERROR occurs, the compilation process will halt and no code will be generated.

FATAL ERROR 1 : Program too big to compile

Cause: The program is larger then the maximum size (normally 65,535 bytes, or 42,000 bytes in 16 bit operating systems)

Action: Separate the program into overlays (see Overlays ) or produce a customised version of the Interpreter with a larger maximum size (see Changing the Maximum Program Size )

FATAL ERROR 2 : Too many procedures

Cause: There are too many procedures in the program (the limit varies, depending on resources)

Action: Separate the procedures into overlay programs (see Overlays ) or place the commonly used procedures in a library program (see Libraries )

FATAL ERROR 3 : Compiler tables full program too big to compile

Cause: There are too many variables or tables

Action: Separate the procedures into overlay programs (see Overlays )

FATAL ERROR 4 : Missing database table

Cause: The table has been deleted from the database

Action: Remove references to the table from the program

Cause: The program has been transferred from an incompatible database

Action: Modify the program to reflect the structure of the database

Cause: The database is corrupted

Action: Run DBCHECK to determine the type of corruption and take the necessary action to recover your database

FATAL ERROR 5 : Missing keyfield <table name> <keyfield name>

Cause: The specified keyfield has been deleted from the table, or the program has been transferred from an incompatible database

Action: as for FATAL ERROR 4

FATAL ERROR 6 : Missing field description

Cause: Field processing exists for a field that is not on the database

Action: Modify the field processing

Cause: The program has been transferred from another database or the database is corrupted

Action: as for FATAL ERROR 4

FATAL ERROR 7 : (not currently used)

FATAL ERROR 8 : Missing index record

Cause: A table set up as a related table is not related to the main table

Action: Modify the table processing

Cause: The main table processing has been modified, but the fields have not been re validated

Action: Validate the field processing

Cause: The database structure has been changed, the program has been transferred from an incompatible database or the database is corrupted

Action: as for FATAL ERROR 4

FATAL ERROR 9 : Missing table processing record

Cause: Processing exists for a field in a table which is not processed, perhaps because the table processing has been changed without updating the field processing

Action: Remove any redundant field processing

FATAL ERROR 10 : Related table processing found for unrelated table

Cause: The program has been transferred from an incompatible database, the database structure has been modified or the main table for the procedure has been changed

Action: Delete the related table processing for the unrelated table or process the table as an additional table


Serious Error Messages

Serious error messages are produced to highlight an error in the way the program has been set up. Code will still be produced, but the program may not behave as intended.

Error 1. Attempting to change prime key of record. Field ignored.

Cause: A key field is set up allowing the record to be modified after it is defined (either as an input field or by assigning a formula)
Action: If you take no action to remove the field, the field is ignored or is treated as a display only field

Cause: A non key field is defined before the key field
Action: Change the field numbering in the Map Editor to make the key field the first field, then regenerate the field processing

Error 2. Clashing non database fields named field name found

Cause: A local field has been defined twice with different types
Action: If the fields are to be treated as the same field, make sure they have the same type and size definition.

If the fields are intended to be different, rename one of the local fields

Error 3. Insufficient fields processed to validate table table name

Cause: Validation is specified for a table, but insufficient fields are specified for an access to take place
Action: Add all the necessary fields to the map and then regenerate the field processing

Cause: The Generate parent checks option was selected in a procedure where not all tables require validating
Action: Delete table processing for related tables that are not used

Error 4. Validation mode for table table name should be 'Check parent on db'

Cause: Join parent from db was selected for a table where this validation mode cannot be used
Action: Change the validation mode to Check parent on db

Error 5. Invalid table field combination in paged choice map. Field will not be displayed

Cause: A multiple choice item map processes fields that cannot be joined from the table concerned
Action: Define the fields correctly

Error 6. Syntax error in formula not corrected. Formula not compiled

Cause: Invalid syntax in formula
Action: Amend the syntax of the formula and recompile. If you take no action, the formula is ignored

Error 7. Missing map mapset name Nr

Cause: The mapset has been edited since the program was last compiled and the specified map is missing
Action: Add a map with the specified number or edit the program to specify a different map number

Cause: If the procedure has been moved or copied, and the mapset name was left blank (to indicate the current mapset), a map with the specified number may not exist
Action: Copy a map or create a new one with the missing map number

Error 8. Illegal menu option number. Option cannot be selected

Cause: A non menu procedure was changed to a menu procedure and the options are not numbered correctly
Action: Edit the link options to change the order of the menu procedures

Error 9. Formula/Condition Nr Nr not set up or missing

Cause: The formula or condition with the specified number has not been set up
Action: Add a formula or condition with the specified number to the procedure or select No formula/condition

Error 10. Attempting to open a second print device. Attempt ignored

Cause: Only one logical device can be selected for a program
Action: Where a second printer is to be used, set up the second device as a subdevice of the logical device. Set up the procedure body as type Subdevice output

Error 11. No table processing set up for database procedure. Procedure treated as non database procedure

Cause: The question Database procedure? was left as Yes, and so QA Build expects table processing
Action: If no action is taken, the procedure will be treated as if it does not access the database

Error 12. Can't find field field name to total into. Totalling ignored

Cause: The local field to total into has not been set up yet
Action: Add a local field for totalling into, and for the output of the result

Error 13. Offering scans in procedure without any map. User cannot know what he is selecting

Cause: If the search method Scan if no match is used, a map must be set up to allow the user to select a record
Action: If user interaction is required, add a map to display the fields. If user interaction is not required, specify the Exact match, Next match or Previous match search methods

Error 14. Missing field description number

Cause: A table has been deleted from the database or the program has been transferred from an incompatible database
Action: Remove references to the field

Cause: The database may have become corrupt
Action: Run a DBCHECK on the database

Error 14. Field number number too big (Maximum is )

Cause: Fields that are input or displayed must be processed in mapfields 0 99. Somehow you have bypassed this restriction during set up
Action: Process the field in a valid mapfield

Error 15. No or missing procedure body for procedure procedure name

Cause: A procedure body was not set up for a procedure or it has been deleted using QABUTIL
Action: Add a procedure body to the procedure

Error 16. Predefined variable variable name has datatype datatype and not datatype

Cause: Where field processing is set up for a standard variable, the data type is not selected correctly
Action: Modify the data type in field processing

Error 17. Field has illegal internal reference

Cause: This error is caused by a bug within earlier versions of the Program Editor
Action: You can usually solve the problem by going into the Program Editor and selecting the problematical procedure body and then Field Processing, then Exit. If the error remains, contact ITIM Solutions for help on removing the problem

Error 18. %option command not recognised in run another program command command

Cause: An invalid program command option has been specified
Action: Edit the program command option in the Program Editor and select a valid option

Error 19. Missing table field table name field name

Cause: as for FATAL ERROR 5
Action: as for FATAL ERROR 5

Error 20. %%option varname not character variable in program command command

Cause: %varname% option is being used and varname is not the name of a type C, S, U, M or P field
Action: Supply the name of a field of the right type


Warning Messages

Warning messages indicate possible problem areas in a QA Build program. In some cases, an effect may be intentional, and the message can be disregarded. In other cases, the message will highlight an oversight on the part of the developer.

Warning 1. You have already processed this field

Cause | A key field is processed twice before the record is defined
Action | None

Warning 2. Accessing non key fields before key explicitly set up. Assuming required key set up before this field

Cause | Non key fields are processed before the key of the record is set up
Action | If the table is to be accessed by a secondary index, make sure this is stated in the table processing
Cause | The table is being auto incremented, and the incremented key is not displayed
Action | No action is necessary, but the message will clear if the field is set up as a no display field in the field processing
Cause | The same map is used in different procedures which process the table using different indexes
Action | Add duplicate fields so that the fields can be processed in the correct order in both procedures

Warning 3. Input field found in non interactive procedure – switched off

Cause | Field processing exists for input fields (this may be because an interactive procedure has been modified for use as a non interactive procedure)
Action | No action is necessary. To suppress the message, change the Input flag in the field processing

Warning 4. Changing key field after record accessed

Cause | One of the fields used to access the record is input to or assigned a new value after the record is already accessed
Action | If this is intentional, no action is required
Cause | A non key field is processed before a key field
Action | Change the order of field processing

Warning 5. Too little memory for multiple choice access on table <table name> in procedure body <body name>. Exact match search used instead

Cause | The program is very large
Action | Reduce the size of the program. See the suggested action for Fatal Errors 1, 2 and 3

Warning 6. Table <table name> is totalled into without being updated

Cause | A database field is specified for totalling into, but the table processing does not specify that the table is to be updated
Action | Modify the table processing if necessary

Warning 7. Including update procedure in report/enquiry program. Updates switched off

Cause | A procedure has been set up to update the database in a Report or Enquiry program – perhaps the procedure has been copied from a Data Entry program
Action | To clear the message, modify the procedure body description and the table processing

Warning 8. Deleting without asking first in interactive procedure

Cause | You have not set up a map for the Record deletion dialog question in table processing
Action | If you do not set one up the record will be automatically deleted, without user confirmation

Warning 9. %option <command> should be < > in program command

Cause | You have misspelled a %option command. For example, %bath instead of %batch
Action | Correct the spelling


Running a QA Build Program

This section covers the following topics:

  • Command Tails for the Program Interpreter
  • Running a Program Against Another Database
  • Run-Time Messages

Introduction

To run a QA Build program, you first need to load the Program Interpreter (PROGRUN). You must specify a database and the name of a program on the database.

The program can be selected from a pop-up list of programs on the database.

You can load PROGRUN from the DP4 menu or from the command line. If you load PROGRUN from the command line, you may specify the database and program names as parameters. The specified program will be run immediately; the PROGRUN startup fields are not displayed.

To load a program from the DP4 menu:

  1. Select QA Build Utilities
  2. Select Run program
  3. Type in the database name
  4. Enter the program name
    or
    Press <Ctrl+P> and select the program from a pop-up list of programs on the database

To load a program from the command line type:

PROGRUN -DB dbname -PROG program name

If you specify a valid database and program name (see Command Tails for PROGRUN ), your program will run immediately. If you do not specify the names as command tails, the Program Interpreter will display the entry fields in which you must specify database and program name.


PROGRUN

Runs compiled QA Build programs

Refer to Running a QA Build Program in the QA Build Developer's Manual for more information about how to use this program.

Command Tail | Description
−DATADB dbname | Specify the database to run the program against
−DB dbname | Specify the database to load the program from. Unless the −datadb command tail is specified this is also the database the program is run against.
−DUMMYC | Allow the test running of a program which includes C exits, without those exits being linked
−PROG name | Specify the QA Build program to run
−QXE path | Specifies the location to use for QXE files and QA Builds own workfile
−VERSION | Display the version number and run the program

QA Build programs can also read command tails, and programs that print use the standard DP4 command tails for selecting the print device.


Running a Program Against Another Database

You can specify a database other than the one that holds your programs to hold your data. If you do this, you can update the programs on a remote site by simply copying a new database, leaving the application database undisturbed.

The second data database must have the same table structure as your program database, but need not contain the same QA Build programs. To specify the database, type the following at the command line:

PROGRUN −DB program db −DATADB data db

where program db contains the program you wish to run, and data db has the application data the program is to access and update.

Make sure the installation process does not overwrite the database containing the user's data

The programs on your program database can be run against the data database, provided that:

  • The generation numbers of the two databases are the same
  • The tables on the program and data databases have an identical structure


Run-Time Messages

Run time errors are not specific to QA Build programs are not listed here. General DP4 Error messages are listed in the DP4 Quick Reference Manual.

The following warning message is specific to QA Build:

Warning! Program name (or procedures used by this program) has been edited since it was last compiled.

Cause: | The program has been changed or a procedure shared with another program has been changed without re compilation
Action: | Compile the program


Execution of a QA Build Program

The following is an idealised description of the activity performed by the Program Interpreter when it executes a QA Build program. Steps are executed in the sequence in which they are described, except where indicated by an instruction to terminate or repeat.

  • Startup
  • Procedure Initialisation
  • Procedure Execution
  • Procedure Body Execution
  • Processing for Each Field
  • Database Accesses
  • Execution_of_Totalling


Startup

  • The program is loaded into memory. A check is made to ensure that its structure is compatible with that expected by the version of the Program Interpreter being used. If the program name was specified from the command line and the program cannot be found, the Program Interpreter terminates.

  • The database is opened in the appropriate mode and the database generation is checked against the generation when the program was compiled. If they are not the same, an error is issued and execution ceases.

  • The user's read and write authority are checked against those required to run the program.

  • Where appropriate, the print device is selected in accordance with information in the program header.

  • The root procedure is executed (see Procedure Initialisation ).

  • The database is closed and PROGRUN terminates. Any uncommitted updates to the database are thrown away.


Procedure Initialisation

  • The entry condition of the procedure is evaluated. If it is not satisfied, procedure execution ceases.

  • Where appropriate (for device select procedures), the print device is selected in accordance with information in the output_device and filename variables.

  • firstitem is set TRUE.

  • The procedure is executed once (see Procedure Execution ). If the procedure is a repeat procedure, the procedure is repeated until the repeat condition is not satisfied or the procedure terminates with ESCAPE.

  • Where appropriate (for device select procedures), the print device is deselected.


Procedure Execution

  1. The value of option_nr is stored.
  2. For menus and action bars the condition to enter of all the subsidiary procedures is evaluated with no_grey set to TRUE and option_nr set temporarily to the value required to select that subsidiary. This is used to build a list of options that are greyed out in the menu.
  3. option_nr is restored to the value stored. On entry to a procedure body it is always the return value of the calling procedure.

  4. The procedure body is executed (see on Procedure Body Execution ) and a result returned indicating what happened.

    Possible results (return values) are:

    SUCCESS | 0 | Successful completion
    ESCAPE | −1 | The procedure was terminated because the user pressed <Escape> or the procedure was a batch procedure and all possible processing was completed
    DELETE | −3 | The procedure was terminated because the user chose to delete the record being processed
    TERMINATED | −12 | Premature termination – the procedure terminated because the record selected for processing did not satisfy all the necessary conditions or because of a non recoverable error or the procedure was of type Run another program and the command line could not be executed
    OPTION | >0 | If the procedure body is a menu then OPTION is the selected option number. If the procedure body is not a menu then the return value is forced to 0. If Qeat_func_keys is TRUE then the original return value is ignored. If Qeat_func_keys is FALSE the return value is stored in an internal variable that works like force_option. The next menu procedure will short cut and select the saved option. If force_option is set it will over-ride the saved option value, which is then discarded.

    If the procedure returned ESCAPE, procedure execution is terminated, unless the procedure has a window and the ESCAPE was generated after a selection had been made from the window.

    The subsidiary procedures of the procedure are executed according to the value returned by the procedure body:

    SUCCESS | Execute the subsidiary procedures in turn
    OPTION | Execute corresponding subsidiary procedure
    ESCAPE | Do not execute any subsidiary procedures
    Otherwise | Execute any subsidiary procedures if Always execute subsidiaries is specified

  5. If the procedure body specifies that changes are to be secured (see Securing changes), the database is checkpointed unless abandon_changes (or decommit) is TRUE (in which case decommit() is called). Note that this operation takes place after all subsidiary procedures have been executed. A checkpoint() or decommit() is also done in the same way if the procedure was a Save/Exit menu.

  6. firstitem is set FALSE.


Procedure Body Execution

Initialisation

  • Fields which are preset because they can be copied from predefined records are copied.
  • If the access mode is batch, the next record for processing is fetched (in accordance with setting of firstitem).
  • If batch access fails, the procedure body is terminated, returning ESCAPE, unless the procedure was interactive and new records could be processed.
  • If the access mode is Window, Cleared window or Preview window, the window is displayed and the user is invited to make a selection (unless it is a preview window). If there are no records for the window and new records are not allowed or the user presses <Escape>, the procedure body is terminated, returning ESCAPE.
  • If a record was selected from a window or fetched with a batch access or the main record is already fetched and is not new, related tables are joined, the condition on the record is checked and locking checks are performed. If these checks fail, execution of the procedure terminates and returns TERMINATED. If no record was fetched or if New record was selected from a window, and auto increment was specified, auto increment is performed.

Display

  • If the procedure is of type subdevice output, the appropriate subdevice is selected.
  • The map or form for the procedure is displayed.
  • The automatically retrieved record or record selected from the window is pre displayed, if appropriate.

Run Another Program

If the procedure body is of type Run another program, the command line is executed. If the procedure body is type Link to C, the C exit is called.

Field Processing

If the procedure is a menu or an action bar, an option is selected unless force option is set or the user pressed a short cut function key in an earlier procedure. If the user presses <Escape>, the Escape option is selected if there is one, otherwise the procedure is terminated returning ESCAPE.

The fields are processed in turn, as described under Processing for Each Field . All database accesses and any other questions and remarks in the procedure are associated with one of the fields (for instance, if deletion is enabled the question offering to delete the record is associated with the field at which the main record became defined).

The processing sequence may be modified by user input, as described below.

User Input

The user may press one of the keys which move to another field. When this happens, the following takes place:

  • If the field is before the current field, control goes directly to it, otherwise the current field and all fields prior to the new field are executed, but without asking for input. This ensures that the user does not bypass any mandatory input or validation. If there is mandatory input or validation, the cursor will move only as far as the relevant field and execution will proceed from that point as though he had requested to move to it and not the selected field
  • If the user presses the <Escape> key, the procedure is terminated and its map is cleared from the screen (unless another procedure originally drew the map and this procedure would not normally clear it) returning ESCAPE. If the procedure has a window, however, pressing <Escape> will return to the window (unless the procedure does not repeat)
  • If a database access fails, processing is returned to the first input field involved in the particular validation (as though a cursor key had been pressed). If there is no input for this validation, procedure body execution ceases, returning TERMINATED
  • If the user chooses to delete the record being processed, field processing ceases

If confirm updates was selected, after the last field has been processed the user will be offered the Save/rekey/abandon menu. If rekey is chosen, processing is returned to the point after the main record is defined.

Posting/Deleting and Final Processing

If field processing is terminated normally:

  • All records specified for updating and which satisfy the condition to update are posted without any checks beyond those always imposed by the database manager (checks for locking violation and contention)

Otherwise if deletion is to take place:

  • The record to be deleted from the database (and any children) are removed, checking that no orphans are left
  • Other records that are to be updated are posted without any checks

If the program does not use the private database and the number of times an updating procedure body has been executed is greater than or equal to the commit_frequency, the updates are confirmed with commit(). If this fails and terminate on commit failure was specified in the program header, the program terminates. Otherwise, an error message indicating the reason for failure is produced unless the program header suppressed update fail messages.

If updates fail, execution of the procedure body returns TERMINATED.

Print output is flushed and the screen is cleared as appropriate.


Processing for Each Field

For each field:

  • The formula for the field is evaluated if appropriate. Formulas are evaluated if Apply always is specified, or if clearfirst and range indicate it should be (see below)
  • If the field is to be input, input is solicited repeatedly until the validation conditions are satisfied, unless a field to return to on validation failure was specified, or until the user presses a cursor key to select another field. Before each input the appropriate input device mask is selected
  • If the field is not an input field (or is normally input but input did not take place because the condition to input was not satisfied) then the field is displayed or printed as appropriate. Note that validation conditions on fields are not tested if the condition to input/display was not satisfied
  • Any possible database accesses are performed, as described under Database Accesses
  • If an access fails, execution passes to another field as described earlier
  • If the procedure is interactive, the field number is set to the number indicated by the terminal manager. When the terminal manager sets the field number to zero, however, the Program Interpreter will process any remaining fields which are outside the input matrix

The field is totalled, if specified, as described under Execution_of_Totalling .

Field processing is further controlled through two variables: clearfirst and range.

range is the number of the first field not yet input. clearfirst is TRUE when a new record is being processed in non database procedures and before the main table is accessed in database procedures. Fields are only given their default values before data entry routines. Non-Apply always formulas are only evaluated when clearfirst is TRUE and range is less than or equal to the current field number.

Initial Value

The initial value of any field is the value it had when the record was fetched from the database or zero if the record is new to the database. For non database fields, the initial value is zero for new records or the same as the final value otherwise.


Database Accesses

  • If a condition to access is specified for a table, the access is only attempted when the condition is satisfied. Otherwise processing continues as though the validation succeeded, but the value of table.there for the field is meaningless.
  • The record is fetched, scanning as appropriate. During scans, only those records which satisfy validation conditions are offered and all fields from the record in the map are displayed. If multiple choice is specified, the multiple choice is only offered if a matching record is not found initially.
  • If the record satisfies the relevant conditions and it is already on the database, it is pre displayed on screen (for interactive programs only).
  • When the main table is accessed, change prevention and clearfirst flags are set as appropriate.
  • Confirmation of updates is requested after all the fields have been processed. The user can either abort the procedure (which returns ESCAPE), resume editing the fields or confirm changes. If he resumes editing, he will be returned to the first non key field if there is one or the first field if not.


Execution of Totalling

If field processing is terminated normally:

  • Update totalling adds change in value of the field to the target
  • Downdate totalling subtracts the change in value from the field
  • Report totalling adds the final value of the field to the target

Otherwise, when field processing is terminated with request to delete:

  • Update totalling subtracts current value of field from target
  • Downdate totalling adds current value of field to target
  • Report totalling still adds the final value of the field to the target

If a condition on any total value is not met, execution of the procedure body returns TERMINATED and terminates in the same way as after a commit failure –all records which should have been updated are re read from the database.


The QA Build Utility Programs

The QA Build utility programs are as follows:

QABUTIL | A general purpose program providing a range of utilities from transferring QA Build programs to finding and renaming program components.
PROGRUND | This utility runs QA Build programs and provides debugging information
AUTOQAB | This program generates a QA Build program to access and update the specified tables on your database


QABUTIL

The options available in QABUTIL include the following:

Transferring QA Build Updates

Renaming

Identifying the Impact of Database Changes

Hiding Programs

Deleting Programs

Locating Formulas and Identifiers

The QA Build program debugging utility, PROGRUND, is described under: Debugging Programs

The QA Build automatic program generator, AUTOQAB, is described under:Automatic Program Generation


Transferring QA Build Updates

The Transfer menu pops down when you select the Transfer option under the QABUTIL menu bar:

You can transfer a number of QA Build elements including programs, procedure trees,procedures, procedure bodies, macros and C function definitions. Alternatively, you can transfer all the programs of one QA Build system.

The elements of a QA Build system exist as records in the base dictionary tables of the application database. Consequently, the transfer operation involves an export and an import. The QA Build data is extracted from the source database to a file and the file is then posted to the target database. Finally, the QA Build programs updated on the target database must be compiled.

In practice there are two situations where you must transfer QA Build programs. One, the most difficult, situation is where development has been pursued on both databases. If it has, you must take care to avoid updates to the target database that would overwrite the changes made during development on the target database. See Version Control for how QABUTIL can help you deal with this situartion. The other situation is where the target database has remained untouched since the last transfer from the source database. This is more straightforward, since nothing is lost by transferring too many programs or procedures.

The explanations and instructions in this section assume that the two databases have the same table structure. For information on updating database structure see the Transferring Database Structure in the DP4 Developer's Manual.

Extracting

In extracting QA Build programs or other components, you create an ASCII file which you can copy to the system to be updated.

  1. From the QA Build Utilities menu select Transfer
  2. Select one of the extract options
  3. Name the component
  4. Select File Output

Posting

Copy onto your system the ASCII file containing the QA Build updates

  1. From the QA Build Utilities menu select Transfer
  2. From the transfer facilities menu select Post program file
  3. Specify the ASCII file containing the QA Build updates
  4. When the posting is complete, recompile the changed programs


Version Contro

Version Control

QA Build is often used for large systems developed by multiple programmers. In theory, such systems could be developed by programmers accessing one database through a network. In practice, development may be spread over several remote sites. In this situation, QABUTIL is the only practical means of interchanging updates. Without version control, this provides only a partial solution because it is not possible to:

  • Create export files (.QAB) containing just QA Build program modifications
  • Prevent two programmers from updating the same data (so that posting a QA Build file at the remote site might cause updates to be lost)
  • Reconcile changes where the same parts of programs have been updated

Using the Version Control functionality built into QABUTIL you can eliminate these problems. This functionality is all contained in the Options Dialog on the Transfer menu.

Options Dialog facilities

The Options Dialog contains two sets of options - one relevant when exporting files, and one when importing.

  • On export, as well as full export files, you can now create partial ones containing:

    • Only the parts of the program that are different from another database
    • Only the parts of the program that have changed since the beginning of an era

  • On import, you can use the new version-control feature to provide warnings where one programmer's modifications overlap with another's.

Export Options

The export options within the Options screen are:

Export mode

This takes one of three values:

  • Full - this is the default and specifies that all the records will be exported

  • Changes from era - specifies that all the changes since the beginning of the chosen era will be exported
  • Differences from target - specifies that the differences from another database will be exported
Changes from era | If you chose 'Changes from era' as the export mode, enter the name of the era. The era must exist on the database
Target database | If you chose 'Differences from target' as the export mode, enter the name of the database containing the old version of the database. The database must be accessible on your system
Export maps | Enable this option if you want maps included in the export file
Compact export

Enable this option to use a compact export format. You may want to clear this option if you want to be able to examine or edit the export file, as the export may be marginally more human readable.
Export macros | Enable this option if you want macros included in the export file

Your preferences are applied when you next create an export file by selecting an extract option in the QABUTIL Transfer menu. They are not preserved when you leave QABUTIL.

QABUTIL Export Groups

QABUTIL groups records from the QAB dictionary together. All the records in a group are examined when making exporting modifications.

  • For 'Changes from era' exports, if any record has a timestamp more recent than that of the era used all the records in group are exported

  • For 'Differences from target' exports, if any record is different from the corresponding record on the target database all the records in the group are exported.

QABUTIL groups records as follows:

  • A single program header
  • A procedure and all its subsidiaries (but not their code)
  • A procedure body and all its tables, fields and totals
  • A single macro, formula or all the parts of a condition
  • A map and all its fields/controls
  • A single help message
  • A single documentation record

Any deletion of one item from a group of related records will mark the 'header' record for the group. This ensures that the group is included in export files, even if no other updates are made to the group. Otherwise, a 'Changes from era' export would omit parts of programs that had only changed because of deletion.

On export, each record from a group of records is exported with a flag that indicates the status of the record. (This flag is an integer held in the cf= field with: cf=0 for unchanged records, cf=1 for amendments, cf=2 for insertions.)

  • For 'Changes from era' exports, this is relative to the era selected
  • For 'Difference from target' exports, this is relative to the current era on the target database
  • For 'Full' exports, this is relative to the current era on the database

The name of the era used to supply this information is included in the export file.

Post Options

The post options within the Options screen are:

List file | Enable this option if you want to include a listing of the files being posted in the error output. This is recommended when doing a version-controlled post because it makes it easier to track down errors. Disable this option to speed up posting
Version control post | If several programmers are developing QA Build programs on multiple copies of the database, a potential problem can arise when using QABUTIL to exchange updates in that two programmers may have updated the same item. Enabling version control will minimize this problem. If you do, QABUTIL will warn you when any information contained in the export file would update records which have changed since a specified version (beginning of a specified era)
Era to check | For version control only, the name of the era to be used. A warning will be issued if information contained in an export file attempts to update a record modified since the beginning of the specified era. The era must exist on the database
Ignore update clashes | Applies only to version controlles posts. If warnings are issued, posting is normally cancelled for the problem area, and related records. Enable this option if you want posting to continue regardless. This may be useful for testing a program, but may not have the desired effect, so we strongly recommend that you have a backup before posting with this option enabled
Test only posting | If enabled posting files perform testonly posting, which prevents the database from being updated

Your preferences are applied when you next post an export file by selecting the Post option in the QABUTIL Transfer menu. They are not preserved when you leave QABUTIL.

Version-Controlled Posting

During posting, QABUTIL checks the records to be updated to see whether their timestamps are earlier than the version-control era. A warning is issued if they are not, which will prevent posting unless you chose to ignore update clashes.

QABUTIL also does a comparison check to see whether the update would change the value of the records being updated. No update is performed for those records with unchanged values.

Related records are grouped together in the same way as during the export. If one record fails a check, its related records fail as well. So, for example, posting a procedure body causes a warning if any part of the procedure body being updated has a more recent timestamp than the version-control era.

If an era name is present in the file being posted and predates the versioncontrol era, the entire file is automatically rejected. To post the file you would have to choose to use the era specified in the file for version control. We have deliberately not automated this; the file is probably out of date.

Checking is also performed for deletions as follows: when a QA Build file is posted, records not marked as insertions (cf=2) must already be present or an error is flagged.

Updated records on the target database that are missing from the file being posted cause an error. For example, if you add a procedure to a QA Build program in "era-1" and post modifications to the same QA Build program using "era-1" for version control an error is produced if the procedure is missing from the export file.

Once an error is detected a report is produced comparing the data in the .QAB file with the data on the database. The format of this file is presently that for .QAB files but a more readable format may be adopted in future.

Version-Control Scheme

Using the facilities discussed, you can implement the following version control system for a multi-programmer development environment where each programmer works with a private copy of the database:

  1. Initially, each programmer is given the same master database with the same era record on it, for example "era-1"

  2. The programmers independently make changes to the database.

  3. When a new master database is required each programmer produces a .QAB file containing his/her changes. Ideally, programmers should do this by comparing their databases with their own copy of the master, but they may also do it by exporting changes from "era-1".

  4. The .QAB files are collected together and posted in turn to the master database, using the option to check records being updated have timestamps prior to the ERA (Version control post 'Yes'). The administrator may choose to perform a test-only posting of each file first and only really post the file if it goes through without errors, or even without warnings.

    Problems are encountered where two programmers have independently updated the same records. The administrator can use the report produced by QABUTIL to decide how to resolve such problems. It may be decided that:

    • One programmer's changes supersede another's and to post the file regardless of errors.
    • Posting the non-clashing parts of the file only is sufficient
    • One programmer will have to redo his work against a new master

  5. Finally, a new ERA record, for example "era-2", is added to the master database and a new copy of the master database is distributed to the programmers.

Version Control Limitations

Limitations that need considering are:

It is not possible to detect the situation where a change made by one programmer requires changes to be made to other parts of a program, but the programmer does not make the changes.

This situation can only be catered for in one way - by dividing QA Build source code into two types:

  • Private code which can only be used by the owner of the code, and can be changed freely.

  • Public code which can be used by anyone, but can only be changed by agreement.

Because QAB does not support this notion some suitable naming convention must be adopted to make it clear into which category code falls.

The order of posting makes a difference. For instance, suppose one .QAB file is created with 'Changes from era' and contains 'marked' records, that is records that have been modified during the specified era but whose values are unchanged. This can happen, for example, where you delete a procedure and reinsert it. If this file is posted first and another file containing updates to the 'marked' records is posted no errors will be reported. If the order of posting is reversed errors will be reported. This is not particularly significant; it just means that the amount of effort in analysing clashes may depend on the order of posting. As a general rule of thumb, we recommend you post the largest .QAB files first of all so that if conflicts do emerge it will mean redoing smaller amounts of work.


Renaming

The rename menu pops down when you select the Rename option under the QABUTIL menu bar:

QAB Rename

1 Map and program
2 Procedure
3 Procedure body (B)
4 Program only (O)
8 Exit (X)
9 Help

To rename programs, procedures and procedure bodies use the QA Build utility program. In renaming programs, you can choose whether to rename the mapset as well. Note, however, that this takes longer because of the way maps are identified in DP4.

  1. From the QA Build Utilities menu select Rename
  2. Select one of the rename options
  3. Enter the current name of the QA Build program, procedure or procedure body
  4. Enter the new name


Analysis

The Analysis menu (or the Impact menu prior to release 4.620) contains options to report inconsistencies between maps, QA Build parameter sets and the data dictionary.

  • Impact report looks at maps, table and field processing,(but not at totalling or formula processing). All maps are looked at, not just QAB maps. It reports various inconsistencies with the data dictionary.

  • The Corrections option is similar to the Impact report but contains facilities to remove some inconsistencies from maps.

  • Missing maps looks for maps used by QAB programs but missing from the database.


Identifying the Impact of Database Changes

Changing the database structure or changing the format of individual fields will inevitably affect the running of QA Build applications. The impact analysis utility identifies the effect of database changes. It examines an entire system and reports on discrepancies between the data dictionary and the QA Build programs. The report includes table, screen and field processing but does not include formula processing or totalling.

The errors reported are:

  • Mapfields or field processing for fields deleted from the database
  • Field length changes
  • Different display and true length for validated map fields
  • Processing for tables deleted from the database
  • Processing for related tables no longer related to main table
  • Index record for table processing missing or inactive
  • Missing table processing record - indicates a corrupt QAB program
  • Processing for fields no longer in tables
  • Changes of data type for fields
  • Processing more occurrences of a field than exist
  • Changes of field scale factor

To run Impact Analysis

  1. From the QA Build Utilities menu select Analysis (or Impact prior to 4.620)
  2. Select Impact report (or Report prior to 4.620)
  3. Select file output
  4. View or print out the created file

Corrections

Instead of simply reporting on discrepancies you can use the Corrections option to attempt to make appropriate corrections to the maps used by your program (the utility will not make corrections such as deleting field processing for fields that have been deleted from the database). If you choose to run this option you should also print the report and use it to review the changes made to the map. In some cases further editing of the map may be required.


Identifying Missing Maps

The Missing maps report allows you to identify any maps that are missing from a database but which are used by a QAB program. When you select it (from the Analysis option on the QABUTIL main menu) a dialog is displayed which enables you to select various options:

  • You can select to have per-program reports (one for each program containing at least one missing map), or a report that does a global analysis of the entire database. The per-program report may help you to decide whether a particular missing map is important or not - for example your database may contain programs that are not used in your intended configuration. It may also be useful in helping you find out what program needs to be edited to fix the problem. The global analysis report is considerably quicker to produce, and will be more compact as each instance of the a missing map's being sued is reported only once.

  • If you select a per-program report you can choose to ignore library (and variant library) programs. This may be useful if your database contains "unbound" variant library programs: you may not be concerned about missing maps that are only used in such programs.

  • Finally you can choose to stop a particular missing map being listed more than once (or once per program for the per-program report). If you do this you need to remember that a missing map may be used in another procedure as well as the once shown.

After completing this dialog you are asked to choose the output device to print to in the normal way.

Operational Details

The missing map report checks for the existence of each map in the current language, and also language zero if the current language is non zero. Maps are only reported as missing if they exist in neither language.

Each missing map encountered is listed with its mapset name and map number followed by summary information detailing where it is used:

  • The name listed is the name of the program (if the map is shown as being a layout map), the name of the procedure to which the entry condition belongs (if the map is shown as being a remark on an entry condition failure), or the name of a procedure body otherwise.

  • Additional information is shown that identifies further how the map is used. The type of the information varies, but should be fairly self explanatory.

Limitations

  • The Missing maps option will report missing remarks for conditions that are not actually used. To avoid this problem use Weed All Redundant QAB Information before running the Missing map report.

  • In two instances a map may be erroneously reported as missing:

    • If a missing map is shown as a map to attach a popup window to, it may not really be missing: the program may be attaching the popup to a map from another mapset, displayed in an earlier procedure, or as a program layout map.
    • Similarly, if a missing map is shown as being a main map that is Already on Screen or a Form that is already output, the map number may refer to a map from another mapset.

    It is not possible for the Missing map report to identify the intended map in either of these two cases.

  • The Missing map report does not check the contents of the map in any way - it only checks for the existence of the map. For example an item of field processing might refer to a control that was missing from the main map. It would be useful to have an option to perform this type of checking, and this is a possible future enhancement. However it would probably be rather slow.


Hiding Programs

When a QA Build system is ready to be installed, you can prevent unauthorised program editing by hiding (concealing) programs. Hidden programs can be run and can be recompiled, but cannot be changed.

Hiding is an irreversible process. It applies to all programs in the application system. Ensure that you have a backup of the database before you perform this action.

To hide a program

  1. From the QA Build Utilities menu select Delete
  2. Select Conceal programs
  3. You are given one final warning before the programs are hidden. Ensure that you have a copy of the database before you enter Yes


Deleting Programs

The delete menu pops down when you select the Delete option under the QABUTIL menu bar:

PROGRAM DELETION

1 All programs
2 Selected programs
3 Conceal programs
4 Repair ties
8 Exit (X)
9 Help

You can delete any of the elements of a QA Build system. Specific procedures, procedure bodies, formulas and maps can be deleted using the Program Editor and the Map Editor. Programs can be deleted using the QA Build Utilities program.

When you delete an individual program, its procedures and procedure bodies are deleted only if they are not used in any other programs. Because you may use maps in 3GL applications, you must confirm the deletion of each map. Despite this facility, you are recommended to use separate mapsets for 3GL applications and C Exits.

When you develop a QA Build system, you may find that you accumulate unwanted maps or formulas. Use the weed facility to delete them. This useful facility identifies those maps and formulas that are not used by programs and deletes them. If you are unsure about the effect of performing this weed operation you can first specify a report of what will be deleted.

Making deletions creates new space inside the database, but does not immediately change the size of the database. If you want to reduce the size of the database files, you must reorganise the database and rebuild the data and index files.

Weeding

The weed menu pops down when you select the Weed option under the QABUTIL menu bar:

Select data for deletion or report

1 All redundant QAB information
2 Formulas for selected procedures
3 Maps in selected mapsets
8 Exit (X)
9 Help


Locating Formulas and Identifiers

The QABUTIL Find menu offers the following facilities:

FIND/CHANGE

1 Identifier in formulas
2 Anything in formulas
3 Tables in procedures
4 Rename identifier
5 Change local variable
6 Options
8 Exit (X)
9 Help

The first two options provide the additional facility to edit the formula. This makes it easy for you to make global changes to the formulas used across the programs and procedures on your database.

Find an Identifier

The first Find option prompts for the identifier name and then shows the first procedure body that uses the identifier:

Use the cursor keys to scan through the complete set of procedure bodies which use the identifier. You have the following options for each formula found:

Formula Usage | This option pops up a window showing where the formula appears in your procedure
Containing Programs | This option pops up a list of all the programs that use the current procedure body
Edit | This option allows you to edit the formula

Find Text in a Formula

The second Find option prompts you for a search string. If the string is terminated with a ^ character, QABUTIL will find and display each formula using the string. As in the previous option, use the cursor keys to scan through the formulas found.

Find Tables in Procedure Bodies

The Tables in Procedures option on the Find menu displays the usage of the specified table in each procedure body.

Rename Identifier and Change Local Variables

Options 5 and 6 on the Find menu allow you to make global changes to the variables used in your program

Options to Limit Search

Options provides the following controls which affect the scope of the search:

QAB Find/Change options
Search type Global
Search area -
Case sensitive find/change ?
Confirm each change ? Yes
Letter to terminate find string ^

The options for finding items within formulas are:

Search Type | The search types allow you to restrict any finds or changes to one program or procedure. You may wish to use this if you are working on a database with other developers. You can also restrict finds or changes to macros only
Search Area | This option allows you to restrict the search area to the program or procedure body on which you are working
Case Sensitive | This option is set to No by default, set to Yes if you wish to match on case
Confirm each Change | This option is set to Yes by default. This is useful where you are making global changes, and wish to check each occurrence of an identifier. Change this to No if you do not need this facility
Letter to Terminate | This option allows you to change the character used to indicate search string termination


Debugging Programs

QA Build provides a facility for debugging programs. When you run a program using the debugger, the program is interrupted at regular intervals and its current state is displayed. At these points, you can check the values of all local and database fields used by the program. You may choose the frequency of the interruptions.

QAB Debug Screen

The QA Build debugging screen is shown above. Notice that under the program information several options are offered for the key entry. These allow you to determine the frequency of interruptions or to view and edit the current values of local and database fields.

Running the QA Build Debugger

To run the QA Build debugger, you use the program PROGRUND, which is a modified version of the QA Build Program Interpreter, PROGRUN.

  1. Either compile your QA Build program with the DEBUG command tail:
    PROGCOMP −DEBUG
    or
    From the QAB Menu compile your QA Build program using the Debugging Compiler

  2. Run the program using the debugger PROGRUND exactly as you would run with PROGRUN (using the same command tails)

    You can also run the debug compiled program with the standard Program Interpreter. It behaves as if it were compiled normally, except for a slightly increased load time

  3. Each time the program execution is interrupted you can specify the debug mode option:

    Debug
    Option     		Description
    X

    Exit program immediately
    B

    Specify procedure to start debug mode
    0

    No debugging
    1

    Procedure flow only
    2

    mode 1 + auto incrementing and map clearing
    3

    mode 2 + table access
    4

    mode 3 + field execution
    5

    Full debugging mode
    D

    View and edit database fields
    L

    View and edit local fields
    G

    View global variables

    When you select D or L, the fields defined so far in the program will pop up. Press <Enter> to modify the contents of the fields.

    The global variables are shown in the following format:


Automatic Program Generation

This section covers the following topics:

AutoQAB Configuration

AutoQAB-Generated Programs

Automatic Generation of Maps in a New Language

Introduction

The Automatic Program Generation utility provides a quick way of creating a program to access and update your database. You can use the utility to create a QA Build program which behaves much like a menu driven version of the DP4 BROWSER. You can also generate a customised program to update a specific table on your database.

In addition to generating programs, AUTOQAB also generates a set of maps for data entry on the tables in your database. You may use AUTOQAB specifically for this purpose without generating a QA Build program by using the command tail MAPS.

To generate a QA Build program:

  1. At the command line type: AUTOQAB
    The command tail ALL can be used to specify all the tables on the database
  2. Select one of the two main options:

AutoQAB FACILITIES

1 All tables
2 Select table...
8 Exit (X)
9 Help

Option 1 will create a program that updates all the tables on your database. If there are too many tables to fit into one program, additional programs are generated called DBNAME1, DBNAME2 and so on
Option 2 will create a program that updates one selected table

  3. Select the defaults for the program you are generating:
    Before generating the program, you may wish to change the program generation options. For more details see AutoQAB Configuration .

To generate a set of maps without a QA Build program:

Start up AUTOQAB with the command tail -MAPS:

AUTOQAB -MAPS

Enter the program name. Although no program will be generated, a program name is needed for the mapset.


AutoQAB Configuration

The Program Generation options menu appears when you have selected option 1 or 2 of the AUTOQAB Facilities menu. It shows the options you can modify for the generated program:

Program Generation Options

1 Index
2 Related table processing
3 Field sequencing
4 Name of program
5 Clearfirst
6 Search methods
7 Generate program
8 Exit (X)

If you are generating all tables, you can change the program name, clearfirst and search method defaults.

If you are generating a specific table, you can change any of the defaults, as follows:

  1. Index for Main Table
    You can select any primary or updated index, the default is the main index.
  2. Indexes for Related Tables
    You can select any primary or updated index. The default is to validate tables for which the parent child record has parent check flag set to YES. Validation modes can either be Always offer scan or multiple choice, according to programmer preference.
    Where multiple choice is selected, the fields in the multiple choice map are displayed across the page, with as many fields as can be displayed without wrapping.
  3. Field Processing
    You can change the sequence in which fields are processed or insert or remove fields from the field processing list. Do not use this option until you have finished changing indexes, because changing indexes also reorders fields. At program generation time the fields may be reordered if you try to process them in an inconsistent order.

AUTOQAB does not currently generate fields with the prevent change flag set, even if this is indicated as being required from the index link settings

  4. Program Name
    This defaults to QABn where n is the internal number of the selected table. You are recommended to change this to something more meaningful. You should be aware that any original program or maps with the generated name will be overwritten. The program name is used as a basis for the procedure and procedure body names. Programs for which all the field processing can be done in one map will need just one procedure with the same name as the program, if field processing has to be split between two or more procedures the name used will be NAME, NAME1, NAME2 and so on.
  5. Clearfirst
    The default is that all non-key fields are set to clearfirst and all key fields are not clearfirst. The clearfirst default can be changed for the key fields.
  6. Search Method
    The default search methods are Always offer scan for the main table and Multiple Choice for the related tables.
    You are allowed to change these defaults by selecting one of the following:


AutoQAB-Generated Programs

The generated program has the following structure and naming conventions:

The top procedure has the same name as the program. It is a repeating procedure with mapset names the same as the program name.

RETAIL0 in the example above is a menu procedure because the ALL option was selected.

The next level down consists of a procedure tree for each of the tables.

Where the table record contains more fields than can be processed on a screen, additional subsidiary procedures are created as shown by the RETAIL002 procedure tree in the above example.

If the main table can be accessed by more than one index then a menu is created at the top of the procedure tree with options for each of the indexes. This is illustrated by the procedure tree RETAIL003 in the example above.


Automatic Generation of Maps in a New Language

The maps and messages generated by QA Build are themselves generated from maps 700 to 743 of the system database. If you want to create maps in a new language, you need only translate the system maps, and run AUTOQAB.


QA Build Program Extract Files

This section covers the following topics:

Syntax of Extract Files

Numeric Values

Introduction

QA Build program extract files are produced using the Transfer option of QABUTIL. Normally, all you will do with these files is to post them to a second database. However, it is technically possible to use this format for making changes to a QA Build program using a text editor or to create programs from scratch. Because QABUTIL does not validate the extract file in any way, this is not recommended.


Syntax of Extract Files

The general syntax of the file is similar to that of MAPPOST. Certain words, prefixed with a #, are recognised as "heading" words. Some heading words process one "line" of information, though this can be split over more than one physical line by using a comma as a line continuation symbol. Others introduce multiple items separated by commas and terminated with a semicolon. In this case each item may be split over as many as lines as desired.

Each line of information consists of a number of keywords followed by an equals sign and a value. Alternatively values may be given without a keyword, the first value belongs to the first keyword and so on.

Heading words tables, fields and totals introduce multiple items.

The syntax is as follows:

Heading Word | Keyword | Default | Comments
#null | char | '!' |
#program | name | Mandatory |
 | type | 1 |
 | flags | 8 |
 | top_proc | NONE |
 | description | Blank |
 | device | 3 |
 | filename | Blank |
 | printer | 0 |
 | read | 0 |
 | write | 0 |
 | mapname | Blank |
 | mapnr | 0 |
 | writer | Blank |
 | modifier | Blank |
#procedure | name | Mandatory | The procedure attributes are followed by a comma introducing a list of sub procedures or a semi colon if there are none.
 | body | Same as procedure name
 | owner | 0
 | descr | Blank
 | proc | Mandatory | Sub procedures are separated by commas. The list of sub procedures is terminated with a semi colon. Defines subsidiary procedures of preceding procedure
 | option | In sequence
 | entry_cond | 0
 | ret_option | 0
#body | name | Mandatory |
 | type | 1 |
 | flags | 0 |
 | access_mode | 3 |
 | direction | 0 |
 | descr | Blank |
 | mapname | Blank |
 | remarks | Blank |
 | map | 0 |
 | maptype | 2 |
 | attach | 0 |
 | attach_fld | 0 |
 | del_quest | 0 |
 | auto_remark | 0 |
 | rep_cond | 0 |
 | del_cond | 0 |
 | device | 0 |
 | ip_device | 0 |
 | esc_option | 0 |
 | owner | 0 |
 | command | Blank |
#tables #table | name | Mandatory | Define tables of preceding procedure body.
 | role | Blank | Define tables of preceding procedure body.
 | joined_via with_role id_role | None None Blank | As many of these as required to specify join path to table
 | flags | 0 |
 | val_mode | 0 |
 | search | 0 |
 | index | 0 |
 | index_role | 0 |
 | table_ref | In sequence |
 | table_cond | 0 |
 | no_rmk | 0 | Remark when record not found
 | exist_rmk | 0 | Remark when record found
 | scan_quest | 0 | Message for scanning records
 | list_map | 0 | Map to display list of choices
 | detail _map | 0 | Map to display item in list
 | lock_quest | 0 | Remark for dealing with locked records
 | add_quest | 0 | Question for adding new record (main table) or allowing check over ride (other tables)
 | match_rmk | 0 | Remark to display if parent and child records do not match
 | val_cond | 0 | Condition to validate
 | post_cond | 0 | Condition to post
#qabfields | name | Mandatory | Define fields of preceding procedure body
 | role | Blank | Define fields of preceding procedure body
 | ref | In sequence |
 | table_ref | 0 |
 | fieldnr | REF |
 | flags | 3 |
 | formula | 0 |
 | validation | 0 |
 | input_cond | 0 |
 | type | Blank |
 | length | 0 |
 | prec | 0 |
 | scale | 0 |
 | occurs_low | 1 |
 | occurs_high |  |
 | fail | 0 | Field to return to
#formula | number part | Mandatory 1 | Define formulas and Conditions of preceding procedure body The formula text follows delimited by rows of underscores.
 | remark | 0 |
 | flags | 0 |
#totals |  |  | Define totalling operations of preceding procedure body
 | ref | Mandatory | Field reference of field being totalled
 | seq | 1 | To cater for multiple totalling of same field
 | name | Mandatory | Name of Target Field
 | role | Blank | Role of Target Field
 | table_ref | 0 | Table reference of target field in this procedure
 | type | 1 |
 | validation | 0 | Condition on total value
 | tot_cond | 0 | Condition to total
 | occurs |  |
#end | (none) |  | QABUTIL will not read past this point
The above is followed by the macro definitions as follows:
#documentation | word_type | Mandatory |
 | name | Mandatory |
The text follows between underscores:
#macro | name | Mandatory |
 | number | Mandatory |
 | type | 1 |
 | map | 0 |

Maps are listed out in MAPPOST format:

#mapname | name | Same as program name |
#language | number | Sets language to update |
#plus #upright #underscore #dot | char char char char | '+' '|' '_' '.' | Define the special characters to be used for plus, dot and so on

#map #remark #window #datamap | } | mapnr curs_rows curs_cols beep map_flags | (Mandatory) 0 0 0 0 | Post map text The effect of using #remark, #window and #datamap rather than map is to select the correct default colours
#help |  | mapnr | (Mandatory) | Posts help message
 |  | fieldnr | (Mandatory) |
 |  | beep | 0 |
 |  | shares | (none) |

The map text follows.


Numeric Values

Most of the fields are numeric. The values of the flags given below are bit maps built up from answers to the Yes/No questions. Values of other numeric fields normally correspond with the option numbers in the menus that select them in PROGMAKE.

Program Types

1 | Data Entry
2 | Enquiry
3 | Batch
4 | Report
5 | Library

Program Flags

1 | For read only programs
2 | For exclusive programs
4 | Stop program if update fails
256 | Print device required
512 | Print device selected at run time
1024 | Use menus for questions
2048 | Private database
4096 | Suppress update fail
16384 | Use QXE files to load

Procedure Types

1 | Interactive
2 | Batch
3 | Menu
4 | Print
5 | Combined menu/entry
6 | Action bar menu
7 | Save/Exit menu
8 | Sub device output
9 | Device select/print
10 | Run another program
11 | Link to C
12 | Hot key set up

Procedure Flags

1 | Database procedure
8 | Allow deletions
16 | Auto inc
32 | Enforce auto inc restriction
64 | Update database
128 | Secure changes
256 | Repeated procedure
1024 | Confirm deletes
2048 | Delay print out/Clear after subsidiaries
4096 | Clear map/Top of form at end
8192 | Always execute subsidiary procedures
16384 | Confirm updates

Procedure Modes (Access Methods for Main Table)

1 | Already fetched
2 | Batch (automatic)
3 | Normal
4 | Line (Automatic + Normal)
5 | Window
6 | Cleared Window
7 | Set up mode
8 | Preview window

Batch Access Methods/Preview Types (from map)

1 | First and onwards
2 | Equal and onwards
3 | Equal and backwards
6 | Last and backwards

Map Output Method

 | Non print Procedures | Print Procedures
1 | Top of screen | Top of form
2 | Header (Retain) | Normal
3 | Repeated (Scroll) | Beside another
4 | Attach to another map | Over another
5 | On screen already | Already output
6 | Pop-up window |

Table Flags

1 | Check parent/child match
2 | Allow join fail
4 | Update this table
8 | Verify this table
16 | Lock records
32 | Reject locked records
64 | Warning only validation
128 | Apply condition to new records
1024 | Allow existing records
2048 | Allow new records

Validation Modes

1 | Already fetched
2 | Check parent on database
3 | Join parent from database
4 | Prohibit parent

On the main table (which must be table 1) you should set this field as follows:

Access Mode | Validation Mode
1 | 1
2 | 3
3 | 2
4 | 2
5 | 5
6 | 5
7 | 3
8 | 5

Values for Table Scan_mode

1 | Exact match
2 | Scan if no match
3 | Always offer scan
4 | Next match
5 | Previous match
6 | Multiple choice

This field should be set to 1 for prevent parent tables

Field Processing Flags

1 | Input field
2 | Display always
4 | Prevent change
8 | Apply formula
16 | Field is totalled

Formula Flags

1 | Warning only
2 | Continue on failure

Totalling Types

1 | Update (+ change)
2 | Downdate (− change)
3 | Report (+ value)

Function/Macro Types

1 | Macro
2 | C Function
3 | Word String

Documentation Wordtypes

P | Program
F | Procedure
T | Procedure body

Map Flags

1 | Mnemonic selection
2 | Boxed
4 | Column major
8 | Translate function keys
16 | Wrap cursor at end
32 | Semitic (Hebrew version only)
256 | Double height
512 | Double width
1024 | Shadow
2048 | Function key area


Copy another program

This operation copies the contents of another program into the program you are creating. You will be asked to enter the name of the program to be copied. You may press <Ctrl+P> for a menu of the programs currently in the database. Note that this facility can copy only programs in the currently open database. Double-click on one or highlight it with the cursor keys and press <Enter> to copy it.

Use this operation if you wish to create a program that is similar to an existing one, by copying the original and then modifying the copy as required. But note that if you change any procedures in the copied program, those changes will be reflected in the original program unless you separately copy the procedure and the procedure body, since only one copy of each procedure and procedure body is held.

Note that this copy operation overwrites any program contents that are already present. Normally it is used when the new program is first created, before any procedures have been added. If there are any procedures present when you request this operation, you will be warned about the consequences of copying another program.


Example Call to a QA Build Program from C

Below is an example of a call to a QA Build program from an existing DP4 application written in C.

/* LIBMAKE keywords to create .H include file */
/*
#c
#db MYDB
#mapname CPROG
#end
*/
#include "dp4.h"
#include "cprog.h"
...
void process()
{
  /* C application code */
  ...
  /* calls to QA Build */
  Qab_init();
  Qaction_program("MYPROG");
}

This example assumes that the application database is the same for the C application and the QA Build program, MYPROG. There are no calls to DP4 functions in process() after the call to Qaction_program().

To write DP4 applications see the C Programmers Reference Manual and the C Supplement Manual.

Sequence of Tasks

1 | Setup and compile QA Build program | The QA Build program is created in the standard way. No additional definitions are required
2 | Write C code | The C code includes the functions Qab_init() and Qaction_program()
3 | Run LIBMAKE | Include the LIBMAKE keywords to generate a .H include file with the appropriate definitions and initialisation code. (See C Programmers Reference Manual)
4 | Compile C code | Use your C compiler to compile the source file. Batch files may be supplied with DP4 to assist you
5 | Link executable program | Use the QABLINK.BAT batch file supplied with DP4 with the following command (Do not link PROGRUN.OBJ): QABLINK CPROG
6 | Run the C program | Use the following command: CPROG

The QA Build program called can perform any action available with QA Build processing, including overlays and C exits.

Unless you call C exits, no extra definitions are required and you do not need to create any C interfaces with PROGCOMP.

If you call C exits, you must modify the source files produced by PROGCOMP to produce a hybrid of the two techniques. You cannot call a QA Build program from within a C exit.


PlaceHolders Reports Menu

1 Group List

Select this option to create a list of groups on the application database.

2 All PlaceHolders

Select this option to create a list of PlaceHolders sorted on groups and variant.

3 Selected PlaceHolders

Select this option to select individual groups for the report.

8 Exit [X]

Select this option to return to the PROGBIND Utilities Menu .


PROGBIND System Build

Group Name

Enter a two-digit Group ID such as TN. Press <Ctrl+P> for a menu of current groups.

Description

This displays the description of the selected Group.

Bind option

Choose Attach if you wish to link the PlaceHolders in the chosen group to the replacement procedure trees in a selected variant library.

Choose Detach to break the links between PlaceHolders and the attached replacement procedure trees.

Variant

Enter the ID of the required variant library; click on the arrow icon for a menu of available variant libraries.


PROGBIND Utilities Menu

1 Group Maintenance

Select this option to create and amend the use of function groups.

2 System Build Menu...

Select this option to build and detach the variant libraries.

3 PlaceHolder Reports Menu...

Select this option to open the reports menu.

4 Validate System Configuration

Select this option to scan through the QAB source code and check the integrity of the PlaceHolders and the links to any attached variants.

5 Reset All PlaceHolders

Select this option to detach all the variant libraries from their PlaceHolders.

8 Exit [X]

Select this option to return to the Facilities menu .


PROVIDED

PROVIDED allows conditional assignments to be made, depending on the value of a Boolean expression. It is used for conditional formulas for calculated fields.

Syntax:

<expression> PROVIDED <boolean_expression>

Return Value:

NCDTY value_to_assign

Description:

The calculated field takes the value of the expression if the boolean_expression is TRUE, but is set to zero for numeric fields or the null value for other types of field if it is FALSE.

Example:

If field LARGE_ORDER has the formula:

ORDER_VALUE PROVIDED ORDER_VALUE > 400

and field SMALL_ORDER has the formula:

ORDER_VALUE PROVIDED ORDER_VALUE <= 400

Then, the output looks like this:

ORDER_VALUE    LARGE_ORDER    SMALL_ORDER

  500.00         500.00           0.00
  200.00           0.00         200.00
    7.00           0.00           7.00


Search facility

Identifier in formulas

Use this option to find formulas using the specified table, field, role, macro, built-in function or operator name. This option parses the formulas and so ignores comments and strings. Press <Ctrl+P> for a menu of local field names.

Anything in formulas

Use this option to find any text within formulas including strings and comments. The formula is not parsed, so underscores which are stripped out by the previous option and by the compiler are significant.

Tables in procedures

Use this option to find procedures using the specified table. Press <Ctrl+P> for a menu of tables on the current database.

Rename identifier

Use this option to rename local variables or macros used within QAB programs. You may enter any kind of identifier. Press <Ctrl+P> for a menu of local field names. The most frequent use is for renaming local variables or redirecting all calls to one function to another or where one procedure has been copied to another.

There are limitations: for example, replacing a database name here only changes the name in formulas; it does not change the references to it in table or field processing which use an internal format. This option cannot rename tables and fields; use MAKEDB if you wish to do that.

Change local variable

Use this option to change the properties of local variables. You should use Impact analysis before making changes necessitated by amendments to the data dictionary. Press <Ctrl+P> for a menu of local field names.

Data type

Possible data types are:

N number | C/M mixed-case character | U upper-case character
Y yes/no | D date | T time
I integer | L long integer | P pseudo numeric (digit-only character field)

Length

Enter the width of this field (only necessary for character fields).

Precision

Enter the number of decimal places you want stored.

Scale factor

How many places is the decimal point shifted to the left for internal storage? For example, with a scale factor of 2 the number 1.51 is stored internally as 151. This process eliminates rounding errors.

Fields in procedures

Use this option to find procedures using the specified field.

Type

Select the field type from the menu: 1 Database field, 2 Local field, 3 Button field, 4 Picture field

Field

Enter the name of the field sought. Press <Ctrl+P> for a menu of available fields.

Match all roles

Enable if you wish to find all fields of this name, irrespective of their role. Disable if you wish to find only fields having the specified name and a particular role to be specified next.

Role

If you disabled the above item, enter the role to be sought. Press <Ctrl+P> for a list of roles for the specified field name.

Maps in procedures

Use this option to find procedures using the specified map. Enter the mapset name and number of the map sought.

Program calls

Use this option to find procedures of type Run another program.

The main window lists the procedure bodies and the command lines they contain.

Containing program

Use this option to display the name of the program containing the highlighted call.

Find in program flow diagram

Use this to return to the program flow diagram where the procedure containing the highlighted call will now be highlighted.

Print

Use this to print a copy of the list of procedures and the command lines they contain.

Exit [X]

Use this to return to the search menu.

Options

This menu item allows you to select options for all the find/change functions in this search facility.

Search type

Find and change options can apply to the whole database or single programs or procedures or macros. Enter the search type from the menu: 1 Global, 2 Program, 3 Procedure body, 4 Macro, 5 Procedure.

When searching for fields, tables, maps and program calls, searches at (2) program level still search the whole database unless the
Quick search only option is selected.

Search area

Enter the name of the program or procedure to be searched.

Case sensitive find/change

Enable this option if you require an exact match during searches. Disable it if case is not significant.

Confirm each change

Enable this option to confirm each change as it is made. This is particularly useful when changes are global in scope and you wish to avoid changing other users' code.

Letter to terminate find string

Because of the mechanism used by DP4 for entering data from the screen, in which fields are a fixed length, for QABUTIL to know whether you wish to search for example for 'A' or for 'A ', you should terminate each search string with the agreed terminating character. By default this is ^, but for maximum flexibility you may change it. Normally the terminating character will be ready and waiting in the search string. If you delete it, remember to put it back, otherwise your searches will not work.

Search whole program

Enable this option if you would like to search the whole of the program you have selected, including those parts of it in attached to StoreView PlaceHolders or tied to other programs.

Quick search only

Enable this option to speed up some of the search operations by confining the search to the procedures used in the selected program.


Suitable C Compilers

Availability

Linking C and QA Build is now available for the following compilers and memory models:

  • Microsoft C small, medium and large model (5.1 and 6.0)
  • Metaware High C small, medium, big and large models (1.5 or 1.6) (but may work with 1.4)

If you require operating system portability or are very tight on memory, consider using the Metaware or Lattice versions rather than Microsoft, particularly if you do not need scientific functions.

Memory Usage

All the following figures relate to MS DOS version linked with QABLINK or QABLINKS. The compilers are denoted by LC=Lattice, MC=Microsoft, HC=Metaware, and the memory models by initial letter:

 | LCS | HCS | HCM | HCB | HCL | MCS | MCM | MCL
Normal version | 111 | 108 | 121 | 125 | 137 | 112 | 120 | 132
Small version | 100 | 102 | 116 | 118 | 129 | 108 | 118 | 130

Small model versions were all linked with croot2.obj file. For Metaware with memory models other than small, a considerable memory saving is possible by including FIX.OBJ in the link. The savings are: medium model 3K, big model 6K, large model 7K.

There is also a penalty for linking without the DP4 root module for small model, which you will need to do if you use some standard C library functions. (The rule is that if you get duplicate symbols you must remove the root module from the link). For instance, in Microsoft C without the root module, the Program Interpreter requires 118K instead of 112K.

Microsoft

Three memory models are supported – small, medium and large. QABLINK as supplied links with the alternate maths library mLIBCA by default.

Compile your code with c, FPa and Zlp toggles, or use the MC.BAT supplied with DP4.

The Microsoft maths library will produce error messages if invalid values are passed to scientific functions (for example asin(2)). Normally this error message will be produced at a random place on the screen and will disappear before you have a chance to read it.

You can get an improved message by linking with the MATHERR.OBJ file supplied with DP4. (Note that this is only for use in DP4 programs and is not suitable for other programs).

If your equipment does not have a maths co processor fitted, you are recommended to use the alternate maths library, which runs up to 50 per cent quicker.

Special Considerations for Medium Model

When linking with a library dated after 7/2/91, all pointers are NEAR. Previously they were all FAR. This change radically simplifies use of this model and reduces the code size. If you need more data than is a available with this model, you can now use Large model instead. But you will have to recompile all previously existing code.

QABLINK.BAT sets the stack size to 4K instead of default value of 2K. 2K is unlikely to be enough if you have nested QA Build programs with %call or %chain.

Special Considerations for Large Model

If you are using Large model and wish to increase the maximum size of QA Build programs or declare large amounts of initialised data, it is advantageous to use the Gt toggle, as otherwise any large initialised data you declare will be put in DGROUP. As DGROUP is almost filled by QA Build data, you are likely to get link errors, and will reduce the maximum possible size of QAB program. Gt16 is best, as this will ensure that tables and other large records are FAR while small variables are NEAR. Any data item can be declared NEAR by using DP4D. This will then be portable to other compilers and memory models supported by ITIM Solutions.

QABLINK.BAT sets the stack size to 6K instead of default value of 2K. 2K is unlikely to be enough if you have nested QA Build programs with %call or %chain.

Metaware

This compiler produces the smallest code and the largest EXE files of the compilers currently supported. Two versions of each library and QABLINK batch file are supplied: one for use with MS DOS and one for use with FLEXOS. OS independence is supported for the small model version.

MS DOS users can substantially reduce the size of the EXE file by modifying QABLINK.BAT to use the /EXEPACK switch.

If you are using medium, big or large model and do not require any standard C I/O, you can save memory by linking with FIX.OBJ. This was produced using the information on memory usage in the Metaware manual. For MS DOS only, you can also link in FIXDOS.OBJ which disables interrupt handlers.

Special Considerations for Small Model

If you intend to convert your QAB C Exit program to another operating system by using EXE2CMD, EXE2UNIX, CMD2EXE or EXE2OS2, then you must link explicitly with the DP4 version of FSIMAB.OBJ. This replaces part of the Metaware library which contains a relocation.

Special Considerations for Big and Large Models under MS DOS

By default these models use up all the memory, thus making it impossible to load interfaces such as SRV2 and TRM2 automatically (so you have to preload them). Using information from Section 12 of manual for 1.6 INIT.ASM has been modified and reassembled so that only the correct amount of memory is used. The modified INIT.OBJ file is distributed as a convenience for users, but it is safest if you change your own (in case you have different version). This will be quite easy if you have MASM and impossible otherwise. The DP4LINK and QABLINK for these models link this module and DOSALLOC.OBJ (supplied with your copy of the compiler).

Big model is more efficient than large model. The code is much smaller and has far fewer load time relocations.

For large model the DP4 supplied HC.PRO is required, or at least you must include #define NO_FUDGE in your HC.PRO. If you do not do this, dedicated C exits will not work.

For these memory models ccmain() is linked explicitly as the first module instead of being pulled out of library. This is necessary to order segments correctly. If you use your own main() (for instance if you are writing an ADC) you will have to link the program another way.

You are recommended to create an HC.PRO profile file, and include in it pragma On (Optimise_for_space). This is because version 1.5 of the compiler sometimes produces incorrect code if this toggle is not turned on.


Validating Tables


Standard Command Tails

The following command tails are common across all DP4 utilities:

Command Tail Description

−DB DBNAME

Override the default database name for utilities that use a database

−DB pathname/dbname

To open a database on a different location to that specified in DFSETUP. For example:
BROWSER -DB A:\MYDATA
which will access the mydata database on the disk in drive a:

Only use this form of the -DB command tail when absolutely necessary, and please note the following restrictions:

  • DP4 will not allow you to open two databases with the same name in different locations. If a second program opens a database with the same name as a database that is already open it will be opened in the location the first program is using.

  • On Windows platforms, if DP4 is running as a service in the local system account you will not be able to access databases on a network drive using this technique.

−BASEDICT

Provide access to the base dictionary tables (for expert users of DP4)

−SERVER_NAME machine_name

From version 4.523/4.619 this command tail can be used when running DP4 on client machines that use the DP4 TCP/IP network requester to select the DP4 server to run against. See -server_name Command Tail for Applications in Guide to DP4 Networking and Resilience for further information.

−HELP

Popup the help message for the utility - this will usually be similar to the Quick Reference information for the program

Command Tails for Printing

The following command tails used to control the destination of "printed" output are especially useful:

Command Tail | Description
−SCREEN | Send device output to the screen
−PRINTER | Send device output to the printer port
−FILE filename | Send device output to a file
−DEVICE device_name | Select the device to determine the characteristics for print or file output

To suppress interactive questions about which device to use for output, specify at least one of -SCREEN, -PRINTER or -FILE.

The -NOSPACE Command Tail

Several database utilities have a -NOSPACE command tail, which instructs the utility to suppress checks for sufficient disk space. Over recent years the use of this has frequently become necessary, because the DP4 function for reporting free disk space originally returned a signed 32 bit number, which meant it could never report more than 2GB free. As of release 4.622 DP4 database utilities use 64 bit file handling functions, meaning they can handle files larger than any PC operating system currently supports, and therefore there should be no need to use this command tail any more. One possible exception to this arises if you are running DP4 on a Windows 9x machine: on such machines the operating system itself may lie about the amount of space available.

Configuration File Settings as Command Tails

From version 4.620 almost any option that can be specified in the DP4 configuration file, can also be specified as a command line option. This can be very useful if you sometimes need to run a program one way, and sometimes another. For example, many batch type DP4 utilities complete they display a message box and wait for the user to press the OK button before exiting, so that the user can examine the output screen. However, if the utilitiy is run as part of a script this may not be what you want. You can use the pause=0 setting in the DP4 configuration file to suppress the pause, but then the user can no longer see the output screen. Instead you should use -pause=0 on the command line. The = is currently optional, but you are recommended always to use it.-pause on its own is equivalent to -pause=1 and the same holds for other numeric configuration file entries.