Sunteți pe pagina 1din 228

BaanERP Tools

Programming features
A publication of:
Baan Development B.V.
P.O.Box 143
3770 AC Barneveld
The Netherlands

Printed in the Netherlands


© Baan Development B.V. 1998.
All rights reserved.
The information in this document
is subject to change without
notice. No part of this document
may be reproduced, stored or
transmitted in any form or by any
means, electronic or mechanical,
for any purpose, without the
express written permission of
Baan Development B.V.

Baan Development B.V.


assumes no liability for any
damages incurred, directly or
indirectly, from any errors,
omissions or discrepancies
between the software and the
information contained in this
document.
Document Information
Code: U7168A US
Group: User Documentation
Edition: A
Date: December, 1998
Table of contents

1 Introduction 1-1
3GL scripts 1-1
4GL UI and DAL scripts 1-1
Report scripts 1-2
Baan SQL 1-2
Dynamic-link libraries 1-2
BaanERP debugger 1-2
Multitasking 1-3
3GL/4GL functions 1-3
2 3GL programming language features 2-1
Overview 2-1
Vocabulary 2-1
Character set 2-1
Continuation symbol 2-1
Comment 2-1
Separators 2-2
Reserved words 2-2
Constants 2-3
Numeric constants 2-3
String constants 2-4
Symbolic constants 2-4
Enumerate and set constants 2-4
Variables 2-5
Data types 2-6
Declarations 2-6
Tables 2-7
Domains 2-8
Arrays 2-9
Fixed and based variables 2-12
Multibyte strings 2-13
Bidirectional strings 2-14
Common variables 2-14
NULL characters in strings 2-15
Expressions and operators 2-16
Arithmetic operators 2-17
Relational operators 2-18

Programming features
i
Table of contents

Logical operators 2-18


Operator precedence 2-19
Assignment Operator 2-20
Transfer of control 2-21
The GOTO statement 2-21
The IF ... THEN ... ELSE statement 2-22
The ON CASE statement 2-23
Iterations 2-25
WHILE statement 2-25
FOR statement 2-26
REPEAT ... UNTIL statement 2-26
Using BREAK and CONTINUE (iterations) 2-26
Functions 2-27
Function type and return value 2-27
Local variables 2-28
Static variables 2-28
Global variables 2-30
External variables 2-31
Function arguments 2-32
Function prototypes 2-35
Function calls 2-36
Preprocessor 2-36
Include files 2-36
Macro definition 2-37
Token pasting 2-39
Object identifications 2-39
Pragma codes 2-40
Conditional compiling 2-41
Compiler 2-45
3 4GL programming features 3-1
Overview 3-1
4GL script types 3-1
Event-driven programming 3-2
Event sections 3-2
Program sections 3-3
Main sections 3-3
Form sections 3-5
Main sections 3-5
Subsections 3-6
Group sections 3-7
Main section 3-7
Subsections 3-7

Programming features
ii
Table of contents

Choice sections 3-8


Main section 3-8
Subsections 3-8
Standard commands 3-9
Field sections 3-13
Main sections 3-13
Subsections 3-13
Example 3-16
Zoom.from sections 3-17
Main sections 3-17
Subsections 3-17
Example 3-18
Main table sections 3-18
Main section 3-18
Subsections 3-18
Example 3-20
Flow of standard program 3-21
Main routine 3-21
Choice sections 3-22
Form sections 3-22
Field sections 3-23
End of program 3-25
Zoom sections 3-25
Update database 3-25
Main table i/o section 3-27
Automatic import of variables 3-27
Case 1 3-27
Case 2 3-27
Case 3 3-28
Case 4 3-28
Case 5 3-29
Case 6 3-29
4 Report scripts 4-1
Overview 4-1
Event sections 4-1
Program sections 4-1
Report sections 4-2
Main sections 4-3
Subsections 4-4
Text field sections 4-4
Main section 4-5
Subsection 4-5

Programming features
iii
Table of contents

Predefined variables 4-5


Report script functions 4-7
Expanding text variables 4-8
Syntax 4-8
Example 4-8
5 Database handling 5-1
Overview 5-1
Database tables 5-2
Primary keys 5-2
Relationships/references 5-2
Combined fields 5-3
Indexes 5-3
Structured Query Language (SQL) 5-3
Naming conventions 5-4
Using tables in program scripts 5-4
Data types 5-4
Transaction handling 5-5
Locking 5-5
Record/page locking 5-6
Table locks 5-7
Application locks 5-7
Retry points 5-8
Coding retry points 5-8
Testing retry points 5-9
Baan SQL 5-9
SQL syntax 5-10
SELECT <select list> 5-10
FROM <from list> 5-11
WHERE <where condition> 5-11
GROUP BY <group condition> 5-13
HAVING <having condition> 5-14
ORDER BY<order by condition> 5-14
Set specification 5-15
Using program variables 5-15
SELECT clause 5-16
FROM clause 5-16
Note 5-16
References 5-17
Retrieving references to a record 5-17
Retrieving references using REFERS TO 5-17
REFERS TO syntax 5-18
Using aliases with REFERS TO 5-19

Programming features
iv
Table of contents

Using program variables or constants with REFERS TO 5-19


Example 5-21
Company numbers 5-21
The <table._compnr field 5-21
Using <table>._compnr in the WHERE clause 5-22
SQL and combined fields 5-23
Specifying a combined field 5-23
Comparison operators for combined fields 5-24
Indexes as combined fields 5-24
Meanings of upper and lower limits 5-25
SQL subqueries 5-26
Example 1 5-26
Example 2 5-26
SQL programming 5-27
Embedded SQL 5-27
Dynamic SQL 5-29
SQL and delayed locks 5-31
SQL trace options 5-32
Client tracing 5-32
Server tracing 5-32
Query identification 5-33
Trace options 5-33
Error handling 5-39
Fatal and non-fatal errors 5-39
The eflag 5-40
Example 5-41
Hints for using SQL 5-41
Hints for using db.retry.point 5-43
Situation 1: Only update actions, commit per fetch 5-44
Situation 2: Only update actions, commits over fetches 5-44
Situation 3: only print / collect actions (no commit) 5-45
Situation 4: Update actions plus print/collect with commit per fetch 5-45
Situation 5: Update actions plus print/collect with commit
over fetches 5-45
6 Data Access Layer (DAL) 6-1
Overview 6-1
Database integrity checks 6-2
Business methods 6-2
DAL terminology 6-3
UI, DAL, and STP interaction 6-3
Overview 6-3
Example 6-4

Programming features
v
Table of contents

UI function calls 6-4


Function flow 6-4
One-way interaction 6-5
DAL hooks 6-5
Property hooks 6-6
fieldname.check() 6-6
Object hooks 6-7
Available object hooks 6-7
Return values and errors 6-8
before.open.object.set() 6-9
before.get.object() 6-9
after.get.object() 6-10
before.destroy.object() 6-12
after.destroy.object() 6-12
before.save.object() 6-13
after.save.object() 6-14
after.commit.transaction() 6-15
method.is.allowed() 6-15
Data Access Methods (DAM) 6-16
Property methods 6-17
Setting properties 6-17
Retrieving properties 6-17
Query extensions 6-17
Communication with STP and CDAS 6-18
Transition issues (BAAN IV to BaanERP) 6-18
4GL event sections 6-18
4GL functions 6-19
Session codes 6-20
Predefined variables 6-20
Questions 6-20
Messages 6-21
Non-table fields 6-21
Include files 6-21
7 Multitasking and the GUI 7-1
Overview 7-1
Bshell scheduler 7-2
Running queue 7-2
Blocking queue 7-2
Sleeping queue 7-3
Terminating queue 7-3
Context switches 7-3
Forcing a context switch 7-3

Programming features
vi
Table of contents

Allocating bonus ticks 7-3


Events 7-5
Event types 7-5
Event flow 7-5
Event masks 7-6
Event functions 7-6
Event arrays 7-6
Sample code 7-7
Requests, inquiries, and replies 7-7
Processes, process groups, and main windows 7-8
Processes 7-8
Process groups 7-9
Main windows 7-9
Process groups and main windows 7-9
Synchronizing processes with the wait() function 7-13
8 Dynamic-link libraries 8-1
Overview 8-1
Function declarations 8-2
Scope of variables across DLLs 8-2
Global variables 8-2
External variables 8-2
Database fields 8-2
Sharing DLL object code 8-3
Dynamic linking 8-3
Dynamic linking at compile time 8-3
Dynamic linking at runtime 8-4
Function overloading 8-4
Specifying which DLLs belong to an object 8-5
Function search algorithm 8-5
Function overloading – example 8-6
Object information tool 8-7
Syntax 8-7
Options 8-7
Object and function descriptions 8-8
DLL compilation and runtime errors 8-9
Compilation errors 8-9
Runtime errors 8-9
9 The BAAN debugger 9-1
Overview 9-1
Commands 9-1
Debugger mouse actions 9-7

Programming features
vii
Table of contents

10 Errors 10-1
UNIX errors 10-1
Database errors 10-6
11 Known limits 11-1
Reports 11-1
Bshell 11-1
Bic 11-1
SQL 11-1
Tables 11-1
Indexes 11-2
12 ASCII table (ISO 8859-1) 12-1
13 Index 13-1

Programming features
viii
About this document

This document describes the main features of the Baan 3GL/4GL programming
language. Programming functions are described in a separate document:
BaanERP Tools: Functions Reference (document number: U7167A US).
Summary
Chapter 1 provides a brief introduction to Baan 3GL/4GL programming features.
Chapter 2 describes in detail the features of the Baan 3GL programming
language.
Chapter 3 describes the 4GL language features for BaanERP Tools. In particular
it describes the 4GL event sections used in Baan 4GL program scripts. It also
describes the flow of the standard program, and it discusses the automatic import
of variables
Chapter 4 describes the functionality available for creating report scripts.
Chapter 5 provides information about database handling and describes Baan SQL
in detail.
Chapter 6 describes in detail the Data Access Layer (DAL) and the functionality
available for creating DAL scripts.
Chapter 7 describes how the bshell provides a multitasking execution
environment for BAAN applications. It provides information about the Bshell
scheduler, context switches, events, requests, inquiries and replies, process and
process groups, and process synchronization.
Chapter 8 discussion the use of Dynamic Link Libraries (DLLs) in Baan
applications.
Chapter 9 provides information on the Baan Debugger.
Chapter 10 lists and describes operating system and database errors.
Chapter 11 lists the known limits that apply to Baan applications.
Chapter 12 presents an ASCII table (ISO 8859-1).

Programming features
ix
Table of contents

Typographical conventions
The following conventions are used in this manual:
bold Bold type is used for the names of functions, 4GL event
sections, and predefined variables, when these are included in
descriptive text.
italics Italics are used for the names of function arguments, when
these are included in descriptive text. Italics are also used for
other non-literals, for emphasis in the text, and for the titles of
publications.
monospace Monospace type is used for the syntax of functions,
declarations, expressions, SQL statements, and so on, for
program startup commands, and for code examples.
Acronyms
CDAS Common Data Access Server
DAL Data Access Layer
DAM Data Access Methods
DLL Dynamic-link library
RDBMS Relational Database Management System
SQL Structured Query Language
STP Standard Program
UI User Interface
Related documents
BaanERP Tools: Functions Reference (document number: U7167A US)
This document describes in detail the functions available for developing
BaanERP applications with the Baan 3GL/4GL programming language.
BaanERP Tools: Programming Features online
This document is available online as a Microsoft Windows help file. The help
file includes the document BaanERP Tools: Functions Reference. Both the help
file and the printed manuals are similarly organized. However, the help file
includes numerous hypertext links to facilitate navigation, and a comprehensive
index and full text search facility.
You can access the help file by choosing the Manual Pages option on the
Specific menu of the session Program Scripts (ttadv2530m000). The name of the
help file is tt_mp2.hlp.

Programming features
x
1 Introduction

The development tools supplied by the BaanERP Tools package enable


developers to program additional functionality for existing BaanERP applications
or to build entirely new applications.
The features provided by the development tools include:
n The BaanERP 3GL programming language.
n 4GL language features that enable you to add to or modify the default
behaviour of sessions, reports, and the Data Access Layer.
n Baan SQL, which enables you to retrieve database data.
n Support for Dynamic Link Libraries (DLLs).
n A debugger that enables you to control and test the execution of your
programs.

3GL scripts
3GL scripts are program scripts that are either linked to sessions without forms
or not linked to sessions at all. They do not have any relationship with the 4GL
Engine (previously known as the Standard Program or STP). When creating such
scripts, you must specify the entire program flow, including the main function.
You cannot use 4GL event sections or functions. Chapter 2, 3GL programming
language features, describes the 3GL programming language in detail.

4GL UI and DAL scripts


In BaanERP applications, the 4GL engine provides much of the default
functionality for a session. You can add to or modify the default functionality of
a session by creating a 4GL script that is linked to the session. 4GL scripts are
event-oriented. They consist of one or more event sections in which you program
actions to be performed at particular states of execution of the 4GL engine.
In previous versions of the software, changes or additions to the default
functionality of a session were programmed in a single script that was associated
with the session. In BaanERP, user interface actions and database actions have
been separated.

Programming features
1-1
Introduction

The Data Access Layer (DAL) now handles database interaction. Programmers
create a user interface (UI) script to change the default behavior of a session.
They create a DAL script to program all the logical integrity rules for a particular
table.
Chapter 3, 4GL programming features, describes the 4GL event sections that you
can use in UI scripts. Chapter 6, Data Access Layer, describes the event sections
and DAL functions that you can use in DAL scripts.
The statements programmed in event sections can be a combination of 3GL/4GL
functions and 3GL language statements.

Report scripts
Report scripts are 4GL scripts that are linked to a report in order to add to or
modify its output. In a report script, you can program actions that you want to be
performed at particular stages of the report execution. Chapter 4, Report scripts,
describes the event sections and report functions that you can use in report
scripts.

Baan SQL
You can use Baan SQL (Structured Query Language) in 4GL scripts to retrieve
data from database tables. There are two ways to use Baan SQL in a 4GL
program. You can embed it in the language (embedded SQL), or you can use
BaanERP 4GL functions (dynamic SQL). Chapter 5, Database handling,
describes both options in detail.

Dynamic-link libraries
BaanERP Tools supports the use of dynamic-link libraries (DLLs). A DLL
consists of a library of functions that is compiled and stored separately from the
processes that use the library. Each program that uses a DLL links to it at
runtime. Chapter 8, Dynamic-link libraries, describes the use of DLLs in
BaanERP applications.

BaanERP debugger
When you compile your programs, you can use the BaanERP debugger to control
and test their execution. Chapter 9 provides an overview of the debugger and
describes the numerous options it supports.

Programming features
1-2
Introduction

Multitasking
The bshell (also referred to as the Baan Virtual Machine) provides a multitasking
execution environment for BaanERP applications. Each bshell can execute and
schedule multiple parallel processes. Chapter 7, Multitasking and the GUI,
provides an overview of how processes and user interface interaction are
managed by the bshell.

3GL/4GL functions
BaanERP 3GL and 4GL functions are described in a separate document:
BaanERP Tools: Functions Reference (document number U7167A US).

Programming features
1-3
Introduction

Programming features
1-4
2 3GL programming language features

Overview
This section describes the features of the Baan 3GL programming language. You
can use the 3GL language in both 3GL scripts and 4GL scripts.

Vocabulary
Character set
You can include the following characters in a 3GL language statement:
n The full alphabet – a-z and A-Z
n The digits 0-9
n The characters # $ ^ & * ( ) - _ + = { } [ ] | \ ; : " , . / < >
This document uses uppercase characters to indicate keywords. However, this is
not necessary in your programs.

Continuation symbol
If a statement does not fit on one line, you can split it across two or more lines.
There is no limit to the number of lines that you can use for one statement.
Wherever you can include a space, you can also make a new line.
There are two exceptions: a string constant and a macro definition. If these do
not fit on one line, you must place a caret symbol [^] at the beginning of the
second and subsequent lines. You can use the caret symbol in other statements
also, but this is not necessary.

Comment
The pipe symbol [|] indicates that the text from the symbol to the end of the line
must be handled as a comment. The compiler ignores this text. Good comments
help the reader of a program source to understand the program better.

Programming features
2-1
3GL programming language features

Separators
You can use tabs, newlines, and spaces as separators in your program.

Reserved words
The Baan 3GL programming language includes a number of words with defined
meanings. These are reserved words that you cannot use other than in the context
for which they were designed. Not all these words are described in this
document; some are for internal use or for compatibility with older releases.
The following is a list of these reserved words:
AND END IN SELECTBIND
AT ENDCASE INPUT SELECTDO
BASE ENDFOR LE SELECTEMPTY
BASED ENDIF LONG SELECTEOS
BREAK ENDWHILE LT SELECTERROR
BSET ENDSELECT MB STATIC
CALL ENDDLLUSAGE MULTIBYTE STEP
CASE EQ NE STOP
COMMON EXTERN NOT STRING
CONST FALSE ON TABLE
CONTINUE FIXED OR THEN
DEFAULT FOR PRINT TO
DIM FUNCTION PROMPT TRUE
DLLUSAGE GE REF UNTIL
DOMAIN GLOBAL REFERENCE VOID
DOUBLE GOTO REPEAT WHEREBIND
ELSE GT RETURN WHEREUSED
EMPTY IF SELECT WHILE

Programming features
2-2
3GL programming language features

Constants
A constant is a value of a special type that you can assign to a variable. The Baan
3GL programming language supports the following types of constants:
n Numeric constants
n String constants
n Symbolic constants
n Enumerate / set constants

Numeric constants
The Baan 3GL programming language supports long and floating point
constants. Both types can be preceded by a ’+’ or a ’-’ sign.

Long constants
A long constant consists of the digits 0-9 only. For example:
12345
-530
0xFFF (hexadecimal)

Floating point constants


Floating point constants consist of the digits 0-9 and a decimal point. You cannot
end a constant with a decimal point. An exponential part can be added to a
constant by including the letter ’e’ followed by a long constant (maximum 307).
You can have up to six digits after the decimal point in a constant. For example:
123.56
0.8743
.8743
123.5e-5
-2.34e10

Programming features
2-3
3GL programming language features

String constants
A string constant is a number of displayable single-byte or multibyte characters
enclosed by double quotation marks. A string constant with length zero (that is,
an empty string) is indicated by two adjacent quotation marks. To include a
double quotation mark within a string constant, you must include two adjacent
double quotes.
For example:
"This is a string constant"
""
"A quote "" within a string constant"
"This is a very long string constant whic
^h does not fit on one line"

Symbolic constants
A symbolic constant is used in your program as a symbolic name, but at runtime
it has a fixed value. The defined constants are:
FALSE | The actual value is 0
TRUE | The actual value is 1
PI | The actual value is 3.141592653589793
Note that if FALSE and TRUE are used as boolean values (for example, as a
result of expressions), they mean zero and not zero respectively.

Enumerate and set constants


In the data dictionary you can define database table fields and domains of type
enumerate or set.
An enumerate or set domain consists of a number of constants. In the program
script (and other places in the data dictionary), a symbolic name is used. The
symbolic name is the domain name, followed by a period [.] followed by the
name of the constant. Variables of type set can also consist of a combination of
set values.
Examples of enumerate constants
DOMAIN colorbox_color | color is an enumerate domain
| with two constants, green
| and red

box_color = color.green
box_color = color.red

Programming features
2-4
3GL programming language features

Examples of set constants


DOMAIN feature cf | feature is a domain of type set
| with the constants bold,
| reverse, underlined
cf = feature.bold
cf = cf + feature.reverse | add constant, cf contains
| bold and reverse
cf = cf - feature.bold | subtract constant
etol( cf ) | returns the combined number of the
| current value, here reverse.

For working with sets, see also the bit functions in U7167A US BaanERP Tools:
Functions reference.
A special constant of type enumerate or set is EMPTY. This indicates an empty
set or an enumerate which is not defined. Internally, it has the value 0.

Variables
The name of a variable is used to indicate the memory location where data
(characters, numbers, and so on) is stored. The name of the variable refers to the
associated data.
The value of a variable can change during execution of the program. A variable
belongs to a data type and must be declared before its use. This is usually done at
the start of the program. In some cases initialization of the declared variables is
necessary. The default initial value is always zero for longs and doubles, and an
empty string for strings.
The name of a variable can consist of any number of letters, digits, underscores
[_], and periods [.]. It must begin with an alphabetic character.
You should keep the following in mind when naming a variable:
n The name of a variable must not be the same as a reserved word or a
previously declared variable.
n The name of the variable must give an indication of its contents. It is good
practice to indicate the purpose of a variable beside its declaration, in the
form of comments.
n A variable must not be used for a purpose other than the one for which it was
originally intended.

Programming features
2-5
3GL programming language features

Data types
There are five types of variables: long, double, string, table, and domain
variables.
n Long variables can contain any whole number from -2147483648 to
2147483647. For numbers beyond this range, use double variables instead.
Physically, four bytes are reserved for each long variable.
n Double variables are used for any number containing a decimal point, with a
maximum of 15 significant digits (8 bytes).
n A string variable contains a string constant. The maximum length of a string
is 1024 characters.
n A table variable is used for database tables in your program. The table must
be defined in the data dictionary.
n A domain variable is a variable of a certain type that is defined in the data
dictionary. Each of the following types are possible: long, byte, integer, date,
enumerate, set, float, double, string, text. Each domain defined in the data
dictionary can be used in a declaration of your program. See also “Domains”
later in this chapter.

Declarations
You can declare variables either within a function block or at the start of the
program. Variables declared in a function block can be used within that function
block only. Variables declared at the start of the program can be used in each
function described below the declaration.

General syntax
You declare variables with the following syntax (parts between [ ] are optional):
[ special ] <type> <variable> [ option ] [, <variable>
[ option ], ...]

Programming features
2-6
3GL programming language features

The following are the possible values for the various parts of a variable
declaration:
special EXTERN | Not in function blocks
STATIC | Only in function block; see
| "Functions" later in this chapter.
type LONG
DOUBLE
STRING
TABLE
DOMAIN <domain name> | domain name is a domain
| defined in the data
| dictionary
variable The syntax for <variable> is:
<name>[(<expression>,...)]
option BASED | (only for arrays)
FIXED
MB | (multibyte)

You can declare several variables of the same type in the same statement. To do
so, simply include commas between the variable names.
If a variable is to be accessed by its symbolic name from outside the program, it
must be declared with the keyword EXTERN, so that the variable name is stored
in the symbol table of the object.
You must declare a variable with the keyword EXTERN if that variable is used
within other programs, within forms (field names and variables within
expressions), or within the function expr.compile(). If the functions get.var()
and put.var() are used within the program, the variables in the other program
must be declared as EXTERN.

Tables
If you use database tables and records in your program, you must declare the
tables as follows:
TABLE <table name> | table name must start with a ’t’.

This declaration creates a pointer to the table. Declaration of a table implies


declaration of all its fields and its record buffer. It is not necessary to declare
these separately.
For a further details see Chapter 5, Database handling.

Programming features
2-7
3GL programming language features

Domains
When declaring temporary variables for storing values of database fields, you
can use normal variables of type long, double, or string. But this can cause
problems if the length or type of the table field has been changed in the data
dictionary. It is preferable to use domain declarations for storing the value of a
database table field. When you use a domain declaration, the type and length for
the declared variable are read from the data dictionary.
Variables of type enumerate or set must always be declared with a domain
declaration.
It is also possible to declare an array by using a domain. In that case the number
of elements must be set between the brackets, the length of the elements is
embedded in the data dictionary.

Examples
The following examples illustrate various domain declarations, and the
declaration of the same variables without domains.
DOMAIN declarations
DOMAIN tst_str10 tmp_str, | String of length 10
tmp_str_arr(100) | 100 strings of
| length 10
DOMAIN tst_long tmp_lng1, | A long number
tmp_lng2, | Another long number
tmp_lng_matr(5,2) | A matrix 5 x 2
DOMAIN tst_double tmp_dbl, | A floating point
tmp_dbl_arr(100) | 100 floating points

STRING declarations
#define LENG 10
STRING tmp_str(LENG), | String of length 10
tmp_str_arr(LENG*10,10) | 10 strings of length
| 100

LONG declarations
LONG tmp_lng1, | A long number
tmp_lng2, | Another long number
tmp_lng_matr(5,2) | A matrix 5 x 2

DOUBLE declarations
#define MAXLEN=1000
DOUBLE tmp_dbl, | A floating point
tmp_dbl_array(MAXLEN/10) | MAXLEN/10 floating
| points

Programming features
2-8
3GL programming language features

Arrays
You use arrays to store multiple variables of the same type within one data item.
Each variable can be referred to with the construction array_name(index).
For example, the first element in a one-dimensional array is array_name(1).
An array can have up to four dimensions. The dimension (and, in case of a string,
also the length) of an array must be stated in the declaration.
There are three types of arrays: long, double, and string.

Long arrays
You declare one-dimensional long arrays as follows:
LONG lng_array(5) | 1-dimensional array for 5 longs

The number in parentheses after the array name specifies the number of longs
that can be stored in the array.
You declare two-dimensional long arrays as follows:
LONG lng_array(2,4)

The first number after the array name indicates the number of dimensions in the
array. The second number indicates the number of longs that can be stored in
each dimension. The following diagram illustrates a two-dimensional array as a
matrix:
lng_array(1,1) = 3 lng_array(2,1) = 100

lng_array(1,2) = 4 lng_array(2,2) = 98

lng_array(1,3) = 56 lng_array(2,3) = 55

lng_array(1,4) = 78 lng_array(2,4) = 77

Double arrays
A double array differs from a long array only in that it is prefixed by the keyword
DOUBLE and the array elements must be filled with double values. You declare
a double array as follows:
DOUBLE dbl_array(5)

Programming features
2-9
3GL programming language features

String arrays
The declaration of string arrays differs from that for other array type in that the
first dimension contains the length of the strings to be stored in the array. For
example:
STRING str_arr(10,5) | 5 strings of length 10
STRING str_arr(10,5,5,5) | A 5 x 5 x 5 matrix of
| strings of length 10

When referencing a string array, the first dimension indicates the start position in
the string. The particular string to be accessed is indicated by the other
dimensions. It is possible to specify a length for the string to be retrieved by
using a semicolon [;] followed by the required length. If you omit the length, the
total length of the string is taken.

Example 1 (two-dimensional string array)


STRING str_arr(6,2) Space reserved for 2 strings of 6 characters
each. For example, the first string contains
"abcdef" and the second "ABCDEF".
str_arr(1,1) = "abcdef" This references the first string starting at
position 1.
str_arr(2,1;1) = "b" This references the first string starting at
position 2 with length 1.
str_arr(3,2) = "CDEF" This references the second string beginning
at position 3.

Example 2 (filling string variables)


Consider a string declared as:
STRING strg(26)

The statements listed below will have the indicated results:


strg = "good morning" The words "good morning" are stored
from the first position of strg:
"good morning"
strg(1) = "good morning" The same result but filled with spaces:
"good morning "

Programming features
2-10
3GL programming language features

strg(9) = "good morning" The string is stored from position 9


onwards and filled with spaces:
" good morning "
strg(2;3) = "Hello" The letters "Hel" (3 characters) are stored
from position 2 onwards:
" Hel good morning "

Example 3 (accessing string variables)


Suppose the string declared in the previous example has the value
"ABCDEFGHIJKLMNOPQRSTUVWXYZ". The results of the statements
below will be as indicated:
print strg "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
print strg(13) "MNOPQRSTUVWXYZ"
print strg(2;3) "BCD"

In these examples, strg(n) refers to the entire array from position n onwards and
strg(n;x) refers to x positions starting at position n.

Example 4 (two-dimensional string array)


Consider a 2-dimensional string array declared as:
STRING twice(6,2)

The statements below will have the indicated results.


twice(1,1) = "ABCDEF" The first string is filled from position 1
onwards.
twice(1,2) = "123456" The second string is filled from position 1
onwards.
print twice(3,1) "CDEF" is printed.
print twice(4,1;2) Two characters are printed starting at position
4 in string 1 – that is, "DE" is printed.

Programming features
2-11
3GL programming language features

String lengths
There are two lengths associated with a string: the maximum and the current
length. The maximum length is defined at declaration of the string and the
current length can vary, depending on the contents of the string.
If a string is declared as STRING strg(100), the maximum length of strg is 100.
If strg is filled as strg = "hello", the current length is 5. If strg is filled as strg(1) =
"hello", the current length is also is 100, as the remaining positions are filled with
spaces. When you specify a start position, the string is always filled out with
spaces.
If a string is declared as FIXED, the current length is always equal to the
maximum length, because the string is always filled up with spaces. Also, in the
case of multi-dimensional strings, the current length is always equal to the
maximum length.

Fixed and based variables

Fixed variables
A string variable can be declared as fixed so that its current length always equals
the maximum length. For example:
STRING name(10) FIXED
name = "andrew" | the string is always filled up
| with spaces

The keyword FIXED is applicable to one-dimensional strings only. Multi-


dimensional string arrays are always fixed and do not need to be declared with
the keyword FIXED.

Based variables
The BASED mechanism is applicable to strings or arrays of all possible types. It
determines that a variable will be based on another variable. For based variables,
no memory space is reserved when they are declared. At runtime they use the
same memory space as the variable on which they are based. So by using this
mechanism, the same section of memory can be accessed via different names.
You use the following construction to base one variable on another:
BASE var_1 AT var_2

In the case of a string, the based variable is always fixed. You must ensure that
the based variable always fits in the variable on which it is based. So it will be
safe to declare the basic variable as FIXED.

Programming features
2-12
3GL programming language features

Example
STRING a(10) FIXED
STRING b(5) BASED
BASE b AT a(3) | This indicates that the space
| occupied by b is the same as the
| space for a(3;5)
b(2;3) = "yes" | a(4;3) now also contains "yes"
a(1;8) = "12345678" | b now contains "34567"

Variable and fixed length strings


The following points summarize the rules for variable and fixed length strings:
n Strings with variable length are 1-dimensional strings not declared as based
or fixed. All other strings are fixed– that is, database table fields, string
arrays, and variables declared as FIXED or BASED.
n Normally, variable length strings are not filled up with spaces except when a
start position is specified in the assignment.
n Fixed length strings are always filled up with spaces, except when a length is
specified. In the latter case, the remaining positions are not overwritten by
spaces.
n If a string (of variable or fixed length) is filled from a certain start position,
the part of that string preceding the start position is filled up with spaces if it
was not filled before.

Multibyte strings
A string variable can be declared as a multibyte string, in order to handle
multibyte or bidirectional characters. For example:
STRING mb_str(10,5) MB | 5 multibyte strings of 10
characters

In a single-byte string, each byte contains a single character. But in a multibyte


string, characters can occupy from one to four bytes.

Example

This multibyte string contains six characters. The fourth character is a TSS (that
is, multibyte) character containing 4 bytes. Note that all TSS characters are
prefixed with hex 9b.

Programming features
2-13
3GL programming language features

NOTES n In program scripts, it is not possible to assign a multibyte character (for


example, Chinese) to a multibyte string.
n The string declared as:
str(x) MB | x is the number of characters

requires a space of x * mb.factor (where mb.factor has been defined in


the user file). A Japanese character for example has a multiplication factor of
about 2.
n The string str(x;1) contains one character, and a size of one or four bytes.
If the character has a size of 4 bytes, it is a TSS character.
n It is not advisable to assign a multibyte string to a normal string, as this can
cause problems regarding storage space and special manipulations like
indexing.
n You cannot use multibyte strings in combination with the BASED
mechanism.

Bidirectional strings
Bidirectional functionality is necessary to support languages that read and write
from right to left– for example, Arabic and Hebrew. Bidirectional input is
supported only on fields of type multibyte string. A string is bidirectional when it
contains at least one bidirectional character. You can check whether a string
includes bidirectional characters by calling mb.hasbidi(). All strings, including
bidirectional, are stored in logic order – that is, the first typed character is the
leftmost one in the string. This means that all regular string handling functions
like pos(), and constructors like the semicolon [;], work correctly.
Special attention must be paid when concatenating or splitting strings. When
bidirectional and non-bidirectional strings are concatenated, one part must be
reversed.

Common variables
Unlike other variables, which can be used only within the program in which they
are declared, common variables can be used in more than one program. Only
string variables can be declared as COMMON.
Common variables are automatically based on a common memory part that can
be used by several programs. For this reason they must be declared as BASED.
The fact that they are common must be indicated with the reserved word
COMMON.

Programming features
2-14
3GL programming language features

Declaration of common variables must be located outside every function. So they


must be declared as global variables.

Example
In program 1 and program 2, the same memory space is used for variables com_1
and com_2.
Within program 1:
STRING com_1(10) BASED, com_2(15) BASED
COMMON com_1, com_2
<functions which use com_1 and com_2>

Within program 2:
STRING com_1(10) BASED, com_2(15) BASED
COMMON com_1, com_2
<functions which use com_1 and com_2>

As an alternative to using common variables, you can use import(), export(),


get.var() and put.var() to transport the values of variables between processes.

NULL characters in strings


Technically, a string is an array of characters. The internal representation of a
string has a NULL character [\0] at the end. The NULL character represents the
character with the value zero.
Null characters can occur within strings after use of the following functions:
seq.read()
copy.mem()
store.byte()
store.short()
store.long()
store.double()
store.float()

When a normal string is copied into another string, it is always copied up to the
first NULL character. If the target string is a fixed string, then it is filled up with
spaces from the NULL character to the end of the string. The copy action can
result from a normal assignment, from passing a string as a call by value
parameter to a function, or from the use of the import() and export() functions.
There is one exception to this rule. When the left or right side in an assignment is
a record buffer (rcd.<table name>), all bytes in the buffer will be copied.

Programming features
2-15
3GL programming language features

For example:
table tttadv999
strbuf = rcd.ttadv999 | NULL characters are copied
rcd.ttadv999 = strbuf | NULL characters are copied
strbuf2 = strbuf | NULL characters are NOT copied

In all other cases, if NULL characters are meaningful, use copy.mem(). This
copies NULL characters as well as ordinary characters.

Expressions and operators


An expression is a combination of variables, constants, and operators, built up in
accordance with certain rules. There are two main kinds of operators: unary and
binary. All operators, except the negation operators, are binary. Expressions with
unary operators have the general form:
operator operand

Expressions with binary operators have the following form, where each operand
can be another expression, variable, or constant:
operand_1 operator operand_2

The available operators can be divided into four categories: arithmetic, relational,
logical, and control operators.
Class Operator Description
Arithmetic - negation
*/+- multiply/divide/add/subtract
\ remainder after division
& string concatenation
Relational = r EQ is equal to
<>or NE is not equal to
> or GT is greater than
< or LT is less than
>= or GE is equal to or greater than
<= or LE is equal to or less than
Logical AND, OR, NOT logical ‘and’, ‘or’ and negation
Control ?: question mark expression

Programming features
2-16
3GL programming language features

The question mark expression is a special type of expression. The syntax is:
<condition> ? <expr_1> : <expr_2>

If the condition is TRUE, expr_1 is executed. If the condition is FALSE, expr_2


is executed. For example:
lng_1 = (a >= b) ? a : b
| if condition a >= b is TRUE lng_1 gets the value of a,
| else lng_1 gets the value of b.

For further information on the different operator types, see the following
sections.

Arithmetic operators
Arithmetic operators perform arithmetic operations on the operands. All
arithmetic operators, except string concatenation, must have operands of
numerical type. If one of the two operands is a double, the other is also converted
to double, and the result is also of double type. For example:
45/30 is equal to 1
| 45 and 30 are both LONG so the result is LONG
45/30.0 is equal to 1.5
| 30.0 is a DOUBLE so the result is a DOUBLE

The concatenation operator concatenates expressions, variables, or constants of


string type.

Examples
LONG lng_1, lng_2
DOUBLE doub
STRING strg_1(16), strg_2(50), strg_3(10), strg_4(20)

Expression Result
lng_2 = 8\3 The variable lng_2 contains 2, which is the
remainder after dividing 8 by 3.
lng_1 = lng_2 + 3 The variable lng_1 now contains 5.
doub = lng_1 * 2.0 The variable lng_1 is multiplied by 2.0;
the result (10.0) is stored in doub.

Programming features
2-17
3GL programming language features

doub = 45 / 30 The variable doub now equals 1.0.


Note that 45 and 30 are both longs, so the
result of the expression is 1. Converted to
double for storing in the result, this
becomes 1.0.
doub = 45 / 30.0 The variable doub now contains 1.5.
strg_1 = "hel" & "lo" The variable strg_1 now contains the value
"hello".
strg_2 = strg_3 & strg_4 The contents of strg_3 and strg_4 are
concatenated and placed in strg_2.

Relational operators
Relational operators compare two operands. If the condition (equal, greater than,
less than, and so on) is satisfied, the result becomes TRUE. If not, it becomes
FALSE.
Relational operators can be performed on values of every available type, but the
operands of one expression must be of the same type. One exception to that rule
is that longs can be compared to doubles.
In the case of strings, the ASCII values of the strings are compared on the basis
of the following rules:
n The comparison is made on the basis of the ASCII value of the strings,
starting at the leftmost character of each string.
n If two characters are not equal, the string with the lower character value is
considered to be smaller and the comparison is terminated.
n If both strings contain the same number of characters and the characters agree
in all cases, the strings are equal. If one string is shorter than the other, and
the corresponding characters are equal, the shorter string is considered to be
smaller than the other.

Logical operators
Logical operators perform logical operations on their operands. The operator
NOT is unary, so it has only one operand. The operands of logical operators can
be logical expressions, relational expressions, variables, or constants.

Programming features
2-18
3GL programming language features

The following table illustrates the results of logical expressions:


A B NOT A A AND B A OR B
FALSE FALSE TRUE FALSE FALSE
FALSE TRUE TRUE FALSE TRUE
TRUE FALSE FALSE FALSE TRUE
TRUE TRUE FALSE TRUE TRUE
Variables or expressions of type LONG can be used as boolean. If a long
expression results in the value zero, it is evaluated as FALSE. If the result is not
equal to zero, it is evaluated as TRUE

Operator precedence
An expression can include a number of operators, variables, and constants. The
overall result is calculated in accordance with the rules of precedence described
here. For example, because multiplication has precedence over addition, the
result of the expression (3 + 4 * 5) is 23 and not 35.
The following table lists all operators in order of decreasing precedence.
Operators with high precedence are evaluated before operators with lower
precedence. Operators on the same line in the table have the same precedence. If
they occur in one expression, they are evaluated from left to right.
Operator Associativity
- (negation) right to left
*/\ left to right
& left to right
+ - (minus) left to right
= > < <> <= >= none
NOT right to left
AND left to right
OR left to right
?: right to left

You can use parentheses to force a specific precedence. For example:


The result of the expression 3+4*5 is 23
The result of the expression (3+4)*5 is 35
The result of the expression 10/2*5 is 25
The result of the expression 10/(2*5) is 1

Programming features
2-19
3GL programming language features

In case of doubt, always use parentheses. This makes the program easier to read.
NOTES n The operators NOT and - (negation) are unary, the others are binary.
n The relational operators are non-associative. This means that A <= B <= C is
not available. This should be A <= B AND B <= C.

Assignment Operator
You use the assignment operator [=] to store variables, constants, and the results
of expressions in a variable.

Examples (general)
LONG I, J, K(100), L(100)
STRING S(50), A, B(10)
DOUBLE AMOUNT
J = 4
AMOUNT = 123.45 * J
L(20) = 1001
S = "This is an example"
I = J | The contents of J are stored in
| variable I
L(2) = L(20) | Now L(2) is equal to L(20)
A = S(3;1) | The character ’i’ is placed in A
B(1;2) = S(6;2) | The word ’is’ is placed in the first
| two positions of B
K = L | The entire content of array L is
| copied to K

Except in the case of doubles and longs, both operators must be of the same type.
For doubles and longs, the following rules apply:
Left operator Right operator and conversion
long Can be integer or double. The result is converted to integer. In
case of double, the digits after the decimal point are truncated.
After truncation the value must be between -2147483648 and
2147483647.
double Result converted to double.

Programming features
2-20
3GL programming language features

Examples (longs and doubles)


LONG A
DOUBLE C
A = 3.14 | A is set to 3
C = A | C now contains 3.0
C = 45 / 30 | C now equals 1.0, because this
| division results in a long

Transfer of control
The Baan 3GL programming language supports several statements that change
the flow of control in a program. These enable control to be transferred either
conditionally or unconditionally to a specified point in the program.
You use the GOTO statement to transfer control unconditionally to a specified
point. You use the IF...THEN...ELSE statement or the ON CASE statement for
conditional transfer. These latter statements evaluate a specified condition and
transfer control if the condition is TRUE.

The GOTO statement


The GOTO statement transfers control unconditionally to a specified point in the
program. You use a label to indicate a position to which the statement can
transfer control. The label name can consist of letters, digits, underscores [_], and
periods [.]. It must begin with an alphabetic character.
Examples of labels:
LABEL_1:
START_OF_PROCESS_A:

Examples of GOTO statements:


GOTO LABEL_1
GOTO START_OF_PROCESS_A

Both the GOTO statement and the label to which it refers must be within the
same function. You cannot transfer control to a label in another function.

Programming features
2-21
3GL programming language features

The IF ... THEN ... ELSE statement


The IF statement enables conditional transfer of control. The statement tests a
condition, and executes the succeeding statements only if the condition is true.
The condition can consist of an expression, or it can be a long variable or a
function that returns a long. When the condition is a long variable or a function
that returns a long, the value zero is evaluated as FALSE and all other values are
evaluated as TRUE.

IF...THEN
This tests a condition. If the condition evaluates to TRUE, the statements
following the IF...THEN statement are executed. For example:
IF condition THEN
statement(s)
ENDIF

IF...THEN...ELSE
This tests a condition. If the condition evaluates to TRUE, the statements
following the IF...THEN statement are executed. If the condition evaluates to
FALSE, the statements following the ELSE statement are executed. For example:
IF condition THEN
statement(s)_1
ELSE
statement(s)_2
ENDIF

Examples
IF a > b THEN
b = b + 1
ENDIF
IF present AND found THEN
count = count + 1
do_something()
ENDIF

IF isspace(str1) THEN
message("String is empty")
ENDIF

IF a > b THEN
max_val = a
ELSE
max_val = b
ENDIF

Programming features
2-22
3GL programming language features

The ON CASE statement


This statement has the following syntax:
ON CASE expression
CASE expr_1:
statements_1
break
CASE expr_2:
statements_2
break
CASE expr_3:
statements_3
break
DEFAULT: | optional
statements
ENDCASE

The ON CASE statement is a multiple-way decision table. It evaluates an


expression and compares the result with the expressions of each specified CASE.
When a match is found, control transfers to that branch and the code for that
particular CASE is executed. If no match is found, the DEFAULT code is
executed. If there is no DEFAULT label, the program continues with the first
statement after ENDCASE. CASES and the DEFAULT label can occur in any
order.
The CASE statements serve as labels. This means that after execution of the
statements related to a particular CASE, the remaining CASES continue to be
evaluated. However, if two CASE expressions give the same result, only the first
is executed.
You can use the BREAK command to skip evaluation of other CASES after the
code for the matching CASE has been executed. You include this command at
the end of the code for each CASE. When the command is executed, execution of
the ON CASE statement ends. In the case of nested CASES, the BREAK
command cancels execution of the CASE statements at the same level only.
The ON CASE expression must be either a long expression or a string
expression.

Long expressions
The expression after ON CASE is evaluated and compared with the various
CASE expressions that follow. All expressions in the case labels must be of type
LONG or STRING. When string expressions are used, the ASCII value of the
string is compared with the ON CASE expression.

Programming features
2-23
3GL programming language features

For example:
ON CASE weekday
CASE 1:
day = "Sunday"
BREAK
CASE 2:
day = "Monday"
BREAK
CASE 3:
day = "Tuesday"
BREAK
DEFAULT:
day = "other day"
ENDCASE

It is also possible to execute a statement for multiple labels. For example:


ON CASE weekday
CASE 1:
CASE 2:
CASE 3:
CASE 4:
beginweek()
BREAK
CASE 5:
CASE 6:
CASE 7:
endweek()
BREAK
ENDCASE

String expressions
The expression after ON CASE is evaluated and compared with the various
CASE expressions that follow. Normally, when the ON CASE expression is a
string expression, the CASE expressions are also strings. However, if the ON
CASE expression evaluates to a non-printable character – for example, ESC
(=27), this cannot be tested against a string constant. In this situation, the CASE
expressions can be integer constants that represent ASCII values. The ASCII
value of the ON CASE expression is then compared with the numeric values of
the CASE labels.

Programming features
2-24
3GL programming language features

For example:
ON CASE choice_char
CASE "A":
CASE "E":
abort()
CASE "N":
next_screen()
BREAK
CASE "P":
last_screen()
BREAK
CASE 27:
escape()
BREAK
DEFAULT:
message("unknown choice")
ENDCASE

Iterations
Iterative statements repeat the associated statements until a specified condition
becomes FALSE.

WHILE statement
This statement has the following form:
WHILE expression
statement(s)
ENDWHILE

The expression is evaluated. If the result is TRUE (that is, non-zero), the
statements between WHILE and ENDWHILE are executed. The process is then
repeated until the expression is FALSE (that is, zero). When the expression
evaluates to FALSE, control passes to the statement after ENDWHILE. For
example:
LONG counter
counter = 1
WHILE counter <= 10000
counter = counter + 1
ENDWHILE

Programming features
2-25
3GL programming language features

FOR statement
This statement has the following form:
FOR num_var = num_expr_1 TO num_expr_2 [STEP num_expr_3]
statement(s)
ENDFOR

The num_var variable is referred to as the loop variable. Num_expr_1 specifies


the initialization of the loop variable. Num_expr_2 specifies the end value of the
loop variable. Num_expr_3 specifies the step size for each iteration. The latter
variable is optional; the default step size is 1. It is possible to use a negative step
size.
After execution of the statements within the loop, num_expr_3 is added to the
loop variable. The num_expr_2 expression is then evaluated. If the loop variable
does not exceed num_expr_2, the statements are executed again. This process
continues until the loop variable exceeds its end value. For example:
LONG total, i
FOR i = 1 TO 100 STEP 2
total = total + i
ENDFOR

REPEAT ... UNTIL statement


The REPEAT ... UNTIL differs from the WHILE statement, in that the REPEAT
... UNTIL statement tests the condition after execution of the loop statements
instead of before execution of the loop statements. Consequently the statement(s)
within a REPEAT ... UNTIL statements are executed at least once.
The syntax of this statement is:
REPEAT
statement(s)
UNTIL expression

The loop iterates as long as the result of the expression is non-zero.

Using BREAK and CONTINUE (iterations)


You can use the BREAK command in an iterative statement in order to cancel
the loop, regardless of the value of the loop condition. After a BREAK, control
passes to the statement following the terminated loop. It is important to note that
with nested loops the BREAK statement cancels the innermost loop only.
The CONTINUE command also interrupts an iteration loop. But, in this case, the
expression is immediately evaluated again for the next iteration.

Programming features
2-26
3GL programming language features

For example:
LONG numbers(100), i
FOR i = 1 TO 100
IF numbers(i) <= 0 THEN
CONTINUE | Next iteration
ENDIF
IF NOT process(numbers(i)) THEN
BREAK | Exit FOR statement
ENDIF
ENDFOR

The BREAK and CONTINUE commands can be very useful in a program, but
you should be take great care when using them.

Functions
In a program, a function performs a particular task that can be executed many
times, with different values. The syntax of a function is as follows (parts between
brackets [] are optional):
FUNCTION [ function_type ] function_name( [ arglist ] )
{
declaration of local variables
statement(s)
RETURN[( return_value )]
}

where arglist consists of one or more argument declarations with the following
syntax:
[REFERENCE / CONST] argument_type argument_name

When including more than one argument declaration, use commas [,] to separate
the declarations. Note that the key word REFERENCE can be replaced by REF.

Function type and return value


The type declaration in a function declaration indicates the type of the function.
This must be the same as the type of the return value. Functions can be of the
following types:
n long
n double
n string
n domain
n void (this means that no value is returned)

Programming features
2-27
3GL programming language features

If you do not specify a type, the function is of type void by default. In functions
of type void, the return statement cannot have any argument. For example:
FUNCTION add_one() | this function is of type void
{
count = count + 1 | count is a global variable
RETURN
}

Local variables
Variables declared within a function block are local variables. They are
accessible only within the function in which they are declared.

Declaration, initialization, and scope


Point of declaration In the function between the brackets { }.
Syntax of declaration <type> name
Initialization The value of local variables is undefined with each
function call. Local variables must always be
initialized in the function.
Scope (validity and time) Only within and during function execution.

Example
function void test()
{
long I | Local variable
for i =1 to 100 | Always to be initialized by user
....
endfor
return
}

Static variables
You can declare local variables as static variables. The value of a static variable
is saved at the end of the function and is used again the next time the function is
called. A static variable is automatically initialized at the start of the program.
Normal local variables are undefined at each function call, and are not initialized
at first call.

Programming features
2-28
3GL programming language features

Declaration, initialization, and scope

Point of declaration In the function between the brackets { }.


Syntax of declaration static <type> name
Initialization At program start:
numeric set to 0
strings set to ""
Scope (validity and time) Can be used only within the function but they
maintain their values throughout program
execution.

Example 1
function test()
{
static long started
if (not started) then
started = true
| Actions to be performed only first time around.
endif
....
return
}

Example 2
function void spool( long id, ref string line() )
{
static long save_id
static long lfn
if ( save_id <> id ) then | Optimalization
lfn = read_new_lfn( id )
save_id = id
endif
seq.puts( line, lfn )
}

Programming features
2-29
3GL programming language features

Global variables
Variables declared outside any function block are global variables. You can use
them in all functions that occur after the variable declaration.

Declaration, initialization, and scope

Point of declaration Outside the functions.


Syntax of declaration <type> name
Initialization At program start:
numeric set to 0
strings set to ""
Scope (validity and time) Valid anywhere in the source, throughout the
execution. If any arguments or variables within a
function are declared with the same name as a
global variable, the global variable cannot then be
accessed within that function.

Example 1
function test()
{
return
}
long glob_var
function long dupl()
{
return( glob_var * 2 )
}

In this example, glob_var is a global variable. It is not accessible in the test()


function as it was declared after that function block. On the other hand, glob_var
is accessible in function dupl().

Example 2
long i
function void test()
{
long i
i = 10 | This is the local variable
| Nothing happens to the global variable ’i’
}

Programming features
2-30
3GL programming language features

External variables
An external variable is declared outside the function blocks. You can use them in
all functions that occur after the variable declaration, and also in other programs
(for example, forms, reports, and runtime expressions). It is possible to declare
external variables within a function block but this is not recommended.
If a local variable has the same name as an external variable, the local variable is
used within the function. The external variable is not affected.

Declaration, initialization, and scope

Point of declaration Outside the functions. (Or within a function, but this
is not recommended.)
Syntax of declaration extern <type> name
Initialization At program start:
numeric set to 0
strings set to ""
Scope (validity and time) Valid anywhere in the source, throughout the
execution. Also valid outside the program – for
example, on forms/reports, in runtime expressions,
and by way of import() and export().

Example
extern long i
function void test()
{
i = 10 | This is the external variable
}

Programming features
2-31
3GL programming language features

Function arguments
The arguments of a function are the variables declared in the function header.
Arguments can be accessed only within the function.

Declaration, initialization, and scope

Point of declaration In the function between the brackets { }.


Syntax of declaration [ref|reference] <type> name
Note that array subscripts must be empty for
reference arguments.
Initialization At function call.
For value arguments (see below), the values of the
function call are copied.
For reference arguments, the same variable (or the
same memory area) is used as occupied by the
variable submitted to the function call.
Scope (validity and time) Only during function execution.

Value arguments
A value argument gets its value at the time of the function call and has its own
memory space. For example:
FUNCTION long dupl(long val) | val is a value argument
{
RETURN( val * 2 )
}
FUNCTION MAIN()
{
message("%d", dupl(10))
| the argument val gets value 10
}

Reference arguments
Reference arguments must be declared in the function header with the keyword
REFERENCE or REF.
A reference argument has the same memory space as the variable used in the
function call. Consequently, all changes made to a reference argument within the
function block are also accessible after the function call in the variable used in
the function call. If a reference argument has array subscripts, these must be
empty in the declaration as the contents and size of the call variable is used.

Programming features
2-32
3GL programming language features

If strings or large arrays are used, it is preferable to use reference arguments


instead of returning them with the return statement. This avoids copy actions.
Example 1
Note that in this example, the reference argument is empty in the declaration.
FUNCTION LONG read_line( REF STRING buffer() )
{
IF ( . . . . ) THEN
buffer = . . . . . . . . . . .
RETURN( 1 )
ELSE
buffer = ""
RETURN( 0 )
ENDIF
}

FUNCTION MAIN()
{
STRING line(100)
IF read_line(line) THEN
. . . .
ELSE
message("No present line")
ENDIF
. . . .
}

Example 2
This example uses a multi-dimensional array:
FUNCTION VOID fill_array ( REF LONG array(,) )
{
array(1,1) = 10
array(1,2) = 20
....
}

FUNCTION MAIN()
{
STRING a(5,10)
fill_array(a)
}

Programming features
2-33
3GL programming language features

Constant arguments
Constant arguments must be declared with the keyword CONST in the function
header. These are similar to reference arguments in that the same memory space
is used for the constant argument and the variable used in the function call.
However, with constant arguments, it is possible to use a string constant within
the function call. Consequently, the value of the argument after the function call
is not returned in the calling function.
You cannot change a constant argument within the function. Nor can you use a
constant argument as reference in another function call. Only strings with one
dimension can be used as constant arguments. The array subscript of the constant
argument must be empty in the declaration because the size of the constant is
used. For example:
FUNCTION print_mess( CONST STRING mesg() )
{
IF NOT isspace(mesg) THEN
message("Message: " & mesg)
ENDIF
}

FUNCTION MAIN()
{
.....
print_mess("This is a message")
.....
}

Varying number of arguments


It is possible to pass an (as yet) unspecified number of arguments to a function
definition by replacing the actual arguments with ’...’. The function call must
contain the actual arguments required to execute the function. This enables you
to use different arguments (from 0 to approximately 250) each time you call the
function. This feature is useful, for example, if you wish to change some (not all)
of the flags of a process, and you do not know beforehand precisely which flags
you are going to change. For information on using this feature, see the arg()
functions in U7167A US BaanERP Tools: Functions reference.

Programming features
2-34
3GL programming language features

Function prototypes
A function consists of three parts:
n Function prototype
n Function definition (function block)
n Function call
The function prototype must be exactly the same as the function header in the
function definition. The prototype of a function is not always necessary. For
example, it is not necessary in the following situations:
n if the function is of type void and has no arguments
n if the definition of the function occurs before any call of that function
In all other situations the prototype of the function must occur before the
function call. For example:
prototype
FUNCTION LONG compnr_check( LONG new_compnr )

function call
IF compnr_check( 999 ) THEN
. . . .
ELSE
mess("pcgen00016", 999)
| Comp. number %d not present
ENDIF

definition
FUNCTION LONG compnr_check( LONG new_compnr )
{
TABLE tpctst999

SELECT pctst999.*
FROM pctst999
WHERE pctst999.compnr = :new_compnr
ORDER BY pctst999.compnr
SELECTDO
compnr = pctst999.compnr
SELECTEMPTY
RETURN(FALSE)
ENDSELECT
RETURN(TRUE)
}

From compiler version bic6.1 onwards, it is not necessary to use function


prototypes within the same source as the function definition.

Programming features
2-35
3GL programming language features

Function calls
The call of a function must have the same number of arguments as declared in
the function header. Value arguments can be called using a constant or a
variable; reference arguments can only be called using a variable. If the called
function is not of type void, the function call must be assigned to a variable or
used as value argument in another function call.
It is possible to use recursive function calls. This means that in a function block a
call to the same function is used. However, this is currently implemented only for
functions without local variables and arguments. In other situations, the
following runtime message is generated: "Recursion not yet implemented".

Preprocessor
The bic6.2 compiler for 3GL program scripts has a built in preprocessor with the
following functions:
n include files
n macro definition
n object identifications
n pragma codes
n conditional compiling
All preprocessor directives starts with a ’#’. A preprocessor directive must be the
first word on a source line, possibly with leading spaces or tabs.

Include files
To include files, use one of the following statements:
#include "filename"
#include <filename>

Files included between ’<’ and ’>’ are searched for in the directory
’$BSE/include<rel.number>’. This directory is reserved for system headers and
cannot be used for applications. Normally a file specified between quotes is
searched for using the standard file redirection method.
Note that the preprocessor works only during compilation of a 3GL source, as the
standard generator std_gen6.2 does not have a preprocessor pass. So it is not
possible to use 4GL events in an included file.

Programming features
2-36
3GL programming language features

Example
Suppose the following entry occurs in $BSE/lib/fd.6.2<package_comb>:
ippmmm:/usr/bse/standard6.2

and there is an include statement as follows:


#include "ippmmmheader"

The file "/usr/bse/standard6.21/ippmmm/immmheader0" is included. If a ’/’


occurs in the filename, the file is searched using the specified path name. When a
file is included twice, the second include is ignored.

Macro definition
Use the following statement to define a macro:
#define macroname[(arguments)]

Use the folowing statement to undefine a macro:


#undef macroname[(arguments)]

Defining macros
The macro names in the source are expanded to the macro definition. If a macro
definition does not fit on one line, it is possible to continue the definition on the
next line(s) by using the caret symbol ’^’ at the beginning of each line. It is
possible to use arguments in the macro. For example:
#define a(x, y, z) for i = x to y
^ z()
^ endfor | The definition

a(1, 100, func) | The invocation

Macro definitions with the same name but with different numbers of arguments
are regarded as different. For example:
#define x definition without arguments
#define x() definition with 0 arguments
#define x(arg1) definition with 1 argument
#define x(arg1,arg2) definition with 2 arguments
#define x(...) definition with undefined
number of arguments

Note that there is a difference between a macro without arguments and a macro
with zero arguments. In the first case, you must use ’x’ in the program script to
call the macro. In the second case, you must use ’x()’ to call the macro.

Programming features
2-37
3GL programming language features

Undefining macros
The ’#undef’ statement causes the macro definition to be ignored. The number of
arguments in the ’#undef’ call must match the number of arguments in the
definition.
For example:
#define MAXLENGTH 1000
#define INCR 1
#define INCR(i) i=i+INCR
.....
#undef MAXLENGTH
#define INCR 2 | Redefines INCR
.....
#undef INCR(i)

An error occurs if you apply #undef to an unknown macro. To be sure that the
macro was defined, use the following construction:
#ifdef INCR | without arguments
#undef INCR(i)
#endif

See “Conditional compiling” later in this chapter for a detailed description of


#ifdef and #endif calls.

Variable macro arguments


You can use the ellipsis notation ( , ... ) to define the macro with a varying
number of arguments. For example:
#define fillbuf(buf1, format, ...)
^ buf1 = sprintf$(format,...)

| macro call
string buffer(100)
long l_val
double d_val
fillbuf(buffer, "%d. %s = %d %5.2f", 1, "Value",
^ l_val, d_val)

The macro definition can contain a number of arguments, but the ellipsis notation
must be the last argument.

Programming features
2-38
3GL programming language features

Token pasting
In the macro, you can enter a part of a variable as a macro argument. The symbol
’##’ is used to distinguish macro arguments and the rest of the macro. This is
omitted in the macro call. You can paste more macro arguments together as one
identifier. This principle is called token pasting. It is used to reuse parts of the
source code, where using functions is impossible or difficult.

Example 1
#define p(x) message("%d", var##x)
#define q(x,y) message("%d", x##y)
#define r(x,y,z) message("%d", x##y##z)
long var1a, var1b
p(1a) | becomes: message("%d", var1a)
p(1b) | becomes: message("%d", var1b)
q(var, 1a) | becomes: message("%d", var1a)
q(var, 1b) | becomes: message("%d", var1b)
r(var, 1, a) | becomes: message("%d", var1a)
r(var, 1, b) | becomes: message("%d", var1b)

Example 2
#define VRC(table,v,r,c)
^ tt##table##.vers = v
^ tt##table##.rele = r
^ tt##table##.cust = c

VRC(adv100, "6.2", "a", "")


VRC(adv200, "6.2", "a", "")
VRC(adv300, "6.2", "a", "")

Object identifications
The compiler always places a default identification in an object, but you can
include you own if you wish. You can use the UNIX command what to write all
object lines that begin with ’@(#)’ on standard output.
The default identification has the following content:
#ident "@(#)<source name>, YY/MM/DD, [HH/MM], From
${logname}"

To set you own identification use the following statement:


#ident "@(#)Identification of object"

For example:
#ident "@(#)<progname>, YY/MM/DD [HH/MM], version, author"

Programming features
2-39
3GL programming language features

Pragma codes
Pragma codes represent compiler options. The following pragma codes are
available:

#pragma nodebug Do not show the source while debugging.


#pragma debug Show the source while debugging.
#pragma nowarnings Do not give warnings about the source.
#pragma warnings Give warnings about the source.
#pragma notransactions The source contains only read actions;
there are no transactions. So, it is
sufficient to start one database server.
#pragma warning <text> The programmer generates his own
warning. (Warning level 15). See example.
#pragma fatal <text> The programmer generates his own error.
#pragma sticky Do not remove the object out of memory
when the process ends.
#pragma used <component> See below.
<code>

Example
#pragma warning This is not a fine solution !

| After compilation the following warning appears:


| <Source(line)>: Warning(15): This is not a fine solution !

Where-used list
In some cases the where-used list is not updated automatically. For example,
when a session code is entered but not expected:
message("ttadv2130m000")
or
bms.send("command", event, "ttaad3100m000", pr.id)

To put this session code into the where-used list, enter the following command
line:
#pragma used session ttadv2130m000

The following example illustrates the pragma codes to update the where-used
list:
#pragma used include <file>
#pragma used table <table code>

Programming features
2-40
3GL programming language features

#pragma used field <field code>


#pragma used domain <domain code>
#pragma used message <message code>
#pragma used question <question code>
#pragma used session <session code>
#pragma used menu <menu code>
#pragma used dll <dll objectname>
#pragma used chart <chart code>

Usually the where-used list is updated. In the case of functions, the where-used
list is updated if the function call contains the string value. For example:
mess("ttadvs0000", 1) | Where-used list will be updated

str = "ttadvs0000"
mess(str, 1) | Where-used list will not be
| updated

#pragma used message ttadvs0000

Location of pragma codes


You can place pragma codes anywhere in the source. For example, if you set the
following pragma at the start of the source:
#pragma nowarnings

you can enter the following pragma after a number of lines:


#pragma warnings

Conditional compiling

Directives
The preprocessor provides the following directives to enable you to compile
parts of the source depending on certain conditions:
#ifdef <macro> The source after #ifdef up to
#else/#elif/#endif is compiled if <macro>
is defined. Otherwise this source is
ignored.

Programming features
2-41
3GL programming language features

#ifndef <macro> The source after #ifndef up to


#else/#elif/#endif is compiled if <macro>
is not defined. Otherwise this source is
ignored.
#if <constant expression> The source after #if up to
#else/#elif/#endif is compiled if <constant
expression> is TRUE. Otherwise this
source is ignored.
#else If the condition belonging to
#if/#ifdef/#ifndef/#elif is FALSE, the
source after #else up to #endif is
compiled. If the condition belonging to
#if/#ifdef/#ifndef/#elif is TRUE, the
source after #else up to #endif is ignored.
#elif <constant ’#elif’ is a combination of #else and #if.
expression> For example:
#if <constant expression>
...
#else
#if <constant expression>
...
#else
...
#endif
#endif

This is equivalent to the following:


#if <constant expression>
...
#elif <constant expression>
...
#else
...
#endif
#endif To finish a part of the source started with
#ifdef/#ifndef/#if.
#undef <macro> To delete a macro definition. The macro is
not known on the next #ifdef call.

Programming features
2-42
3GL programming language features

Notes on using the directives


n The <constant expression> in #if and #elif is a numeric expression in which
you can use only macros of type long and operators such as +, -, *, /, =, <, >,
<=, >=, <>, and, or, not, ?:, ().
n It is possible to use nested #if structures.
n It is possible to define a macro when starting the compiler. You use the D
option to do this. For example:
bic6.2 -D<macro> | no value means default 1
bic6.2 -D<macro>=<value>
bic6.2 -D<macro>=’any token string’

These macros can also be used in the #if conditions. For example:
bic6.2 -DDEBUG -DMYTEST <source>
|source
#if DEBUG and MYTEST
message("Some debug information")
...
#endif

bic6.2 -DCUSTOMER_X -DCUSTOMER_Y <source>


bic6.2 -DSTANDARD <source>

#if STANDARD
....
#elif ( CUSTOMER_X and (not CUSTOMER_Y) )
....
#endif
n You can use #ifdef to make a part of the file inactive. For example:
#ifdef OLD
....
....
....
#endif
n The preprocessor works only during compilation of a 3GL source, as the
standard generator std_gen6.2 does not have a preprocessor pass. So it is not
possible to use 4GL events in #if, #ifdef or #ifndef.

Programming features
2-43
3GL programming language features

n It is not possible to use a #ifdef statement within an embedded SQL query.


So, the following construction is not allowed:
select *
#ifdef STANDARD
...
from x
#else
...
from y
#endif
where ...
selectdo
...
endselect

But the following construction is possible:


#ifdef STANDARD
select * from x where ...
selectdo
...
endselect
#else
select * from y where ...
selectdo
...
endselect
#endif
n The following keywords provide debug information in 3GL sources:
__FILE__ : contains the name of the current source
__LINE__ : contains the line number of the current
source

For example:
message("This is at line %d in the source %s",
__LINE__, __FILE__)

Programming features
2-44
3GL programming language features

Compiler
You compile program scripts with the bic6.2 compiler. The compiler is
automatically activated after you have edited your program or report script and
after the use of std_gen6.2 and repgen6.2 at the option ’Compile’ in the data
dictionary. You can also use the compiler to compile 3GL programs
The syntax for the compiler is:
bic6.2 [-ilpsV] [-wW[level]] [-o <object>]
[-d dll[:dll...]] [-D <macro>] [-I <dir>] source

-I Assembler output is stored in <source>.i. Do not specify an


object file.
-l Object will run in debug mode.
-p Object will run in profiling mode for time analysis (see function
times.on()).
-s Generate symbol table to stdout; for more information use
’bic_info6.2 -s’.
-V Print release information.
-w Suppress all warnings.
-wn Suppress warnings of level n.
-Wn Give warnings of level n.
-o <obj> Specifies the object name; when not specified, only a syntax
check is performed.
-d Specifies the DLL(s), separated by a colon (’:’). These are the
objects of dynamic-link libraries necessary to compile the source
program.
-D<macro> Specifies a condition <macro>, see also “Preprocessor” earlier in
this chapter.
-I<dir> Specifies a directory where included files are located.

Programming features
2-45
3GL programming language features

The warnings associated with the various level numbers are as follows:
Level Warning
1 Declared but never used
For example, remove the declaration of the non-used variables.
Warnings related to an include file can be prevented by entering
"#pragma nowarnings" at the beginning of the include file. All
warnings caused by the include file will then be ignored.
2 Function never used
A function is found but never called. For instance remove the function.
This warning related to an include file can be prevented by entering
"#pragma nowarnings".
3 Return value of function ignored
When a function yields a value, that value must be checked. The reason
is that many functions return TRUE or FALSE, depending on whether
the action has been completed successfully or not. If the return value is
of no importance, a dummy value must be assigned to the function. For
example:
dummy = ittadv0037.search.label(....)

4 A long divided by a long gives also a long


A long divided by a long will result in a long. For example:
long a
long b
a = 1
b = 3
a / b = 0 and not 0.33

Programming features
2-46
3GL programming language features

5 Statement/Label not reached


Occurs if a statement cannot be reached. For example:
function test.a()
{
if a = 0 then
...
return()
else
...
return()
endif
a = 1
}

The statement a = 1 will never be reached, so this warning indicates a


programming error or logical error.
6 Comparison of two float types may yield unpredictable results
This warning is given when two double types are compared for
equivalence. Please use the function double.cmp() to compare double
variables, double domains, and double functions. Note that for double
functions the warning appears only if the function has been prototyped
before its actual use. For example:
function double d()

function main()
{
domain tttst.db62 dbldom
double a, b

if a = b then
...
endif
if d() > a then
...
endif
if a <= dbldom then
...
endif
}

All the above expressions may yield unpredictable results, as no


tolerances have been specified.

Programming features
2-47
3GL programming language features

9 External variable ’%s’ passed as reference to function ’%s’


When an external variable is given as a call by reference to a function,
the value of this variable within the function is not equal to the local
value. Just after the return, the value of the external variable will be
changed. For example:
extern long a

function main()
{
a = 5
make.ten(a)
message("a = %d", a) | results in a = 10
}

function make.ten( ref long x )


{
message("a = %d", a) | results in a = 5
x = 10
....
message("a = %d", a) | results in a = 5
}

When within the function make.ten() another function is called using


the external variable a, the variable a still has the value 5.
10 Passing fixed variable to non-fixed call by reference argument (or non-
fixed to fixed)
When a global variable is declared as fixed, the variable within a
function is still regarded as fixed, although the reference declaration is
not fixed. For example:
string a(20) fixed
function process.variable( ref string b )
{
}

If the function is invoked using the argument "a", "b" is considered


fixed.

Programming features
2-48
3GL programming language features

12 External variable ’%s’ added in external symbol table


An external variable is declared within a function. For example:
function test()
{
extern long a
....
}

13 System library <bic_stdlib> NOT included !


15 Warning given by #pragma <text>
This is a warning of your own format, see “Pragma Codes” earlier in
this chapter.
16 Missing selectdo; automatic break or Missing selectdo; loop until
selecteos
When a selectdo is missing in the select statement, an automatic break
is generated to prevent unnecessary use of cpu time. When a selectdo is
missing, while a selecteos section exists, the select statement should be
executed. Probably this is a non optimal select statement.
18 Macro '%s' redefined
A macro is defined, which was already defined with the same
arguments.
19 Function '%s' in DLL '%s' ignored taken from '%s'
A duplicate function occurs in a DLL or program script. A function is
searched for in the script first and next in the libraries that are linked to
the script in the specified order. If a function is found twice, this
warning appears.

You can specify the options for automatic compilation in the session Maintain
Development Parameters (tadv0110m000).
When an error is found in the program, all warnings after that error are
suppressed.

Programming features
2-49
3GL programming language features

Programming features
2-50
3 4GL programming features

Overview
This section describes the 4GL features of the BAAN programming language.
When you create a session, the session generator generates a standard source.
This standard source provides default session functionality. If the required
functionality of the session is not fully implemented by the standard source, you
can program the additional functionality in a 4GL program script.
In previous versions of the software, changes or additions to the default
functionality for a session were programmed in a single script that was associated
with the session. Both user interface actions and database actions were
programmed in this script.
In BaanERP, user interface actions and database actions have been separated.
The Data Access Layer (DAL) now handles database interaction. Programmers
create a user interface (UI) script to change the default behavior of a session.
They create a DAL script to program all the logical integrity rules for a particular
table. This section discusses UI scripts.

4GL script types


There are four types of 4GL program scripts:
n type 1 (with main table): single-occurrence (details) session
n type 2 (with main table): multi-occurrence (overview) session, with group
fields
n type 3 (with main table): multi-occurrence (overview) session, without group
fields
n type 4 (without main table): print/processing sessions

Programming features
3-1
4GL programming features

Event-driven programming
4GL scripts are event oriented. They consist of one or more event sections in
which you program actions to be performed at particular states of execution of
the standard program – for example, when a session is started, before a form is
activated, before input to a field, after input to a field, and so on. It is not
necessary to program all sections, only those for which the standard program
does not provide the required functionality.
The statements programmed in a section can be a combination of 3GL/4GL
functions and 3GL statements. For information about the 3GL programming
language, see Chapter 2, 3GL programming language features.

Event sections
A 4GL script can contain one or more of the following types of sections:
n Program sections
n Form sections
n Group sections
n Choice sections
n Field sections
n Zoom sections
n Main table sections
With the exception of program sections, each section consists of a main section
and one or more subsections. A main section indicates the object for which the
programmed actions must be executed. A subsection specifies when the actions
must be executed. Program sections consist of a main section only.
If you do not include a main section immediately before a subsection, the
subsection is assigned to the previous main section in the script. For example:
form.1: | main section
init.form: | subsection executed for form 1
...
...
before.form: | subsection executed for form 1
...
...
after.form: | subsection executed for form 1
...

Programming features
3-2
4GL programming features

The following rules apply to the ordering of sections within a script:


n The declaration section must be the first section in the script.
n The functions section must be the last section in the script.
n Other sections can occur in any order, but the preferred order is: program
sections, form sections, group sections, choice sections, field sections, zoom
sections, main table sections.
n Do not mix sections of different types. For example, do not define a choice
section, followed by a field section, and then a choice section again.
Exceptions are the declaration and functions sections, which must be the
first and last sections respectively in the script.
Variables declared in the declaration section are global variables that you can
use in other sections. Variables declared within any other section are local
variables that you can use in that section only.

Program sections
You use program sections to define functions, and to declare tables and global
variables, that you want to use in the other sections in the script. You also use
program sections to program actions that you want to be executed when the
session starts or ends.
The following program sections are available. They are all main sections. There
are no subsections associated with program sections.

Main sections

declaration:
Use this section to declare tables and global variables that you want to use in
other sections in the script. See, “Declarations” and “Tables” in Chapter 2 for
details of the declaration syntax. You also use this section to define function
prototypes.

before.program:
Use this section to program actions that must be executed when the session starts.
For example, you can use this section to initialize or import variables or to read a
special record. Note that the default values or imported values overwrite the
fields of the main table.

Programming features
3-3
4GL programming features

on.error:
Use this section to program actions that must be executed at the end of the
session, before the after.program section. If the session is canceled, the actions
in this section are not executed. If an error is detected, you can check the
predefined variable e for the error code.

after.program:
Use this section to program actions that must be executed at the end of the
session, after the on.error section. If the session is canceled (with the command
ABORT.PROGRAM), the actions in this section are not executed.

after.update.db.commit:
Use this section to program actions that must be executed immediately after a
commit.transaction() call, if the database is updated.

before.display.object:
Use this section to program actions that must be executed each time the entire
record is displayed.

functions:
Use this section to define the functions used in the other sections. This must be
the last section in the script. The function syntax is the same as for 3GL
functions.

Programming features
3-4
4GL programming features

Example
declaration:
table tpctst999
long var01, ret

before.program:
if read_parameter() then
mess("pctsts0001", 1) | Parameter not present
stop()
endif

on.error:
mess("........", 1)

functions:
function long read_parameter()
{
select pctst999.*
from pctst999
order by pctst999._index1
as set with 1 rows
selectdo
return(0)
selectempty
return(1)
endselect
}

Form sections
You use form sections to program actions that you want to be executed when
forms are activated or ended. Form sections consist of a main section and a
subsection. The main section specifies the particular form(s) for which the
actions are to be executed. The subsections specify when the actions must be
executed – for example, when the form is activated or when the form is ended.

Main sections

form.<form number>:
The subsections associated with this section are executed for the specified form.
The <form number> is the sequence number of the form, as defined in the
Maintain Sessions by Package VRC session (ttadv2100m000).

Programming features
3-5
4GL programming features

form.all:
The subsections associated with this section are executed for all forms of the
session.

form.other:
A subsection associated with this section is executed for all forms for which the
particular subsection has not been programmed in a form.<form number>
section.

Subsections

init.form:
The actions programmed in this subsection are executed the first time the
specified form becomes current, immediately before the subsection before.form.
You use this subsection to program the first action to be performed by the system
– a first.set or first.view, for example.

before.form:
The actions programmed in this subsection are executed each time the specified
form becomes current.

after.form:
The actions programmed in this subsection are executed each time the specified
form is ended.

Example:
form.1:
init.form:
execute(first.set)
form.other:
init.form:
display("disp3")
form.all:
before.form:
display("disp")

Note:
Only the form.all section is relevant to dynamic forms. For dynamic forms, use
the group sections instead.

Programming features
3-6
4GL programming features

Group sections
With dynamic forms, the mapping of groups to forms is not fixed. Consequently,
the only form section that is relevant to dynamic forms is the form.all section.
When dealing with dynamic forms, you use group sections instead of form
sections
You use group sections to program actions that you want to be executed when
particular groups are activated or ended. Group sections consist of a main section
and a subsection. The main section specifies the particular group(s) for which the
actions are to be executed. The subsections specify when the actions must be
executed – for example, when the group is activated or when the group is ended.
Note that a group becomes active when the form on which it occurs becomes
current.

Main section

group.<group number>:
This section defines the group for which the actions programmed in the group
subsections are executed. The group number is automatically generated when the
group is created.

Subsections

init.group:
The actions programmed in this subsection are executed only the first time the
group becomes active, immediately before the before.group subsection. You use
this subsection to program the first action to be performed by the system when
the group becomes active. When the group is split across two or more forms
(because not all the group fits on one form or because it is a repeating group),
this subsection is executed the first time that the first form on which the group
occurs becomes current.
Note: the init.group section for group.1 is the former init.form section for
form.1. Group number 1 is not one of the groups in the form-definition.

before.group:
The actions programmed in this subsection are executed each time the specified
group becomes active. When the group is split across more than one form, this
subsection is executed whenever any form on which it occurs becomes current.

Programming features
3-7
4GL programming features

after.group:
The actions programmed in this subsection are executed each time the form on
which the group occurs is ended. When the group is split across more than one
form, this section is executed whenever any form on which the group occurs is
ended.

Choice sections
You use choice sections to program actions that you want to be executed when
standard commands are activated or ended. Choice sections consist of a main
section and a subsection. The main section specifies the standard command for
which the actions must be executed. The subsections specify when the actions
must be executed.

Main section

choice.<standard command>:
The subsections associated with this main section are executed for the specified
standard command. You use the Maintain Forms by Package VRC session to
select the standard commands (ttadv3100m000) for each form. A list of standard
commands appears at the end of this help topic.

Subsections

before.choice:
The actions programmed in this subsection are executed immediately before the
specified command is executed. You can use this subsection, for example, to test
data before the command process continues. You can use the choice.again()
function to stop the command.

on.choice:
The actions programmed in this subsection are executed when the specified
command is activated. These actions are executed instead of the standard action
associated with the command.

Programming features
3-8
4GL programming features

This subsection is available for the following commands only:


n print.data
n run.job
n global.delete
n global.copy
n bms
n all commands in type 4 programs (except modify.set, restart.input,
end.program, abort.program)

after.choice:
The actions programmed in this subsection are executed immediately after the
specified command has been executed. This subsection is not available for the
end.program and abort.program commands. For example:
choice.first.view:
after.choice:
execute(first.set)

Standard commands
The following table lists the available standard commands and the types of
program in which the various choice subsections can be programmed for each
command.
No Choice_option Description Before On After
01 start.set Add/start a new group in 1234 ---4 1234
main table
02 first.view View first group of main 1234 ---4 1234
table
03 next.view View next group of main 1234 ---4 1234
table
04 prev.view View previous group of 1234 ---4 1234
main table
05 last.view View last group of main 1234 ---4 1234
table
06 def.find Find a specific record on 1234 ---4 1234
key

Programming features
3-9
4GL programming features

07 find.data Start the set from the 1234 ---4 1234


current data in the
program script. Use: after
import of data in a zoom
process. (refresh)
08 first.set View first set of main 1234 ---4 1234
table
09 next.set First execute an 1234 ---4 1234
update.db and then view
next set of main table
10 display.set Open read-only details 1234 ---4 1234
11 prev.set First execute an 1234 ---4 1234
update.db and then view
previous set of main table
12 rotate.curr Show amounts in other 1234 1234 1234
currency (on.choice
subsections are available
for this command only if
no currencies are set)
13 last.set View last set of main 1234 ---4 1234
table
14 add.set Insert a new record 1234 ---4 1234
15 update.db First test consistency of 1234 ---4 1234
data and then (re)write
record or set (save)
16 dupl.occur Copy the record to a new 1234 ---4 1234
one
17 recover.set Undo modifications 1234 ---4 1234
which are not yet written
to the database
18 mark.delete Delete selected records 1234 ---4 1234
19 mark.occur Select a record on the 1234 ---4 1234
screen
20 change.order Change the search key 1234 ---4 1234

Programming features
3-10
4GL programming features

No Choice_option Description Before On After


21 modify.set Change a record stored in 1234 ---- 1234
the database
23 print.data Make a report with the 1234 1234 1234
current data on the form
24 create.job Add session in job 1234 ---- 1234
25 form.tab.change Go to another form in 1234 ---- 1234
session
26 first.frm Go to first form 1234 ---- 1234
27 next.frm Go to next form 1234 ---- 1234
28 prev.frm Go to previous form 1234 ---- 1234
29 last.frm Go to last form 1234 ---- 1234
31 resize.frm Resize the current form 1234 1234 1234
33 cmd.options Show available form ---- ---- ----
options
34 zoom Zoom to another 1234 ---- 1234
session/menu
35 interrupt Do an action each time 1234 1234 1234
interval
36 end.program End execution of the 1234 ---- ----
session; the main table
will be updated
37 abort.program Cancel execution of the 1234 ---- ----
session. The main table is
not updated
39 text.manager Start the text manager 1234 ---- 1234
40 run.job Run job of session 1234 1234 1234
41 global.delete Delete range of records 1234 1234 1234
42 global.copy Copy range of records 1234 1234 1234
43 save.defaults Save values as defaults 1234 ---- 1234

Programming features
3-11
4GL programming features

No Choice_option Description Before On After


44 get.defaults Get previous stored 1234 ---- 1234
defaults
45 start.chart Chart Manager is started 1234 ---4 1234
46 start.query Start query by form 1234 ---4 1234
60 ask.helpinfo Asks for session help ---- ---- ----
62 calculator Calculator will be started ---- ---- ----
63 calendar Calendar will be started ---- ---- ----
64 bms A broadcast message is 1234 1234 1234
received
71 cmd.whats.this Asks for field help ---- ---- ----
72 help.index Asks for the help index ---- ---- ----

The following commands are supported for backward compatibility only.


Support is not guaranteed in future versions of the software.
22 restart.input Restart the input after giving 1234 ---- 1234
<Esc> during input mode
38 cont.process Start user defined process 1234 1234 1234
48 user.0 User option 0 1234 1234 1234
49 user.1 User option 1 1234 1234 1234
50 user.2 User option 2 1234 1234 1234
51 user.3 User option 3 1234 1234 1234
52 user.4 User option 4 1234 1234 1234
53 user.5 User option 5 1234 1234 1234
54 user.6 User option 6 1234 1234 1234
55 user.7 User option 7 1234 1234 1234
56 user.8 User option 8 1234 1234 1234
57 user.9 User option 9 1234 1234 1234

Programming features
3-12
4GL programming features

Field sections
You use field sections to program actions that you want to be executed for a
variety of field events. Field sections consist of a main section and a subsection.
The main section specifies the field(s) for which the actions must be executed.
The subsections specify when the actions must be executed.

Main sections

field.<field name>:
The subsections associated with this main section are executed for the specified
field. The field name you specify must correspond with the name on the form.

field.all:
The subsections associated with this main section are executed for all fields on
the form

field.other:
A subsection associated with this section is executed for all fields for which the
particular subsection has not been programmed in a field.<field name> section.

Subsections

init.field:
The actions programmed in this subsection are executed the first time that the
form on which the field occurs becomes current. The subsection is executed
immediately before the init.form section of the form. You can use this section to
change field attributes.

before.field:
The actions programmed in this subsection are executed each time the focus
moves to the specified field. The subsection is executed immediately before the
before.input or before.display subsections.

before.input:
The actions programmed in this subsection are executed immediately before
input to an input field commences. This section is supported for backward
compatibility only. In BaanERP programs, use enable.fields() and
disable.fields() to enable and disable fields.

Programming features
3-13
4GL programming features

before.display:
The actions programmed in this subsection are executed each time that the
specified field is displayed. You can use this subsection, for example, to set the
value of a display field or to change the output format of currencies and dates (by
using attr.oformat$).

before.zoom:
When a zoom process is started on the specified field, the actions programmed in
this subsection are executed immediately before the zoom process is executed.
You can use this subsection to change the zoom attributes. For example, in the
field definition in the form manager, a default zoom code can be specified. You
can then use this subsection to change the zoom code (by setting the predefined
variable attr.zoomcode).

before.checks:
The actions programmed in this subsection are executed after input to the field
has been completed and the TAB key has been pressed. The subsection is
executed immediately before the domain and references are checked. You can
use this section, for example, to change the value of the field before the domain
and reference checks.

domain.error:
The actions programmed in this subsection are executed if the domain check
causes an error after data has been entered in the field. You can use this section
to provide your own error message instead of the standard message.

ref.input:
The actions programmed in this subsection are executed if a reference error
occurs. If you program this section, the standard program does not display a
message.

ref.display:
The actions programmed in this subsection are executed if there is an error in the
reference display.

Programming features
3-14
4GL programming features

check.input:
The actions programmed in this subsection are executed immediately after the
domain and reference checks. You can use this section to test for errors that are
not detected automatically. If the standard program detects an error, input is
automatically restarted. In a script, you can use set.input.error() to display an
error message and start input again.
This section is replaced by DAL functionality (if a DAL exists for the table).

on.input:
The actions programmed in this subsection are executed immediately after the
check.input subsection. You can use this section, for example, to perform some
appropriate action when the user enters a special character or to display a
warning message in certain cases.

when.field.changes:
The actions programmed in this subsection are executed when the new value
entered in the specified field differs from the old value.

after.zoom:
The actions programmed in this subsection are executed when the zoom process
on a specified field ends. You can use this subsection, for example, to redisplay
any field that has changed as a result of the zoom process.

after.input:
The actions programmed in this subsection are executed after all checks have
been performed on input to the specified field and after the when.field.changes
subsection has been executed. In this subsection, you can use the to.field()
function if the sequence deviates from the default.

after.display:
The actions programmed in this subsection are executed just after display of the
specified field. You can use this subsection, for example, to read reference tables.

Programming features
3-15
4GL programming features

after.field:
This is the last subsection for a field. The actions programmed in this subsection
are executed after input to or display of the specified field. This section is always
executed, even if the user left the field by pressing ESC, one of the arrow keys,
or a mouse button. You can use this subsection to perform special actions before
leaving the field.

Example
field.pctst099.item:
check.input:
select pctst001.*
from pctst001
where pctst001.item = :pctst099.item
as set with 1 rows
selectdo
....
selectempty
pctst001.dsca = "*****"
set.input.error(".....")
endselect
before.input:
if ..... then
attr.input = false
endif
after.display:
select pctst001.*
from pctst001
where pctst001.item = :pctst099.item
as set with 1 rows
selectempty
pctst001.dsca = "*****"
endselect

field.pctst099.date:
before.input:
attr.oformat$ = "%D002,2"

Programming features
3-16
4GL programming features

Zoom.from sections
You use zoom.from sections to program actions that you want to be executed
when the current session is activated as a zoom process. A zoom process can be
activated by a form-specific command of type session, by zooming on an input
field, by pressing CTRL+B, or by calling zoom.to$() in the program script.
Zoom.from sections consist of a main section and a subsection. The main section
specifies the process for which the actions must be executed. The subsections
specify when the actions must be executed.

Main sections

zoom.from.<zoom name>:
Each field has a name that can be used as a zoom name. You can specify the
zoom name in this main section. The zoom name indicates which field the
current session was zoomed from (when the subprocess is started from a form
command, the zoom name is "choice"). The subsections associated with this
main section are executed when the current process was zoomed to from the
specified process.

zoom.from.all:
The subsections associated with this main section are executed for all processes
from which the current session can be zoomed to.

zoom.from.other:
A subsection associated with this section is executed for all processes for which
the particular subsection has not been programmed in a zoom.from.<zoom
name> section.

Subsections

on.entry:
The actions programmed in this subsection are executed each time the current
process is started from the specified process(es). You can use this subsection to
import variables.

Programming features
3-17
4GL programming features

on.exit:
The actions programmed in this subsection are executed at the end of the current
process, if the current process was started from the process(es) specified in the
main section. You can use this subsection to export variables.

Example
zoom.from.all:
on.entry:
import("globvar", localvar)
on.exit:
export("globvar", localvar)

Main table sections


You use main table i/o sections to program actions that you want to be executed
when read or write actions occur on the main table. These sections are not
relevant to type 4 programs, as such programs do not have a main table. Main
table i/o sections consist of a main section and a subsection. The main section is
always main.table.io. The subsection indicates when the actions must be
executed.
Note that main.table.io sections (except the read.view subsection) are ignored
when the corresponding DAL hooks are programmed in a DAL script.

Main section

main.table.io:
The subsections associated with this main section are executed either before or
after input to or output from the main table. The main table for the current
session is defined in the data dictionary.

Subsections

before.read:
The actions programmed in this subsection are executed immediately before each
read action on the main table.

Programming features
3-18
4GL programming features

after.read:
The actions programmed in this subsection are executed after each read action on
the main table. You can use this subsection to call skip.io() in order to skip the
record just read and to read the next record (if necessary).

before.write:
The actions programmed in this subsection are executed before each write action
on the main table. A write action occurs when a record is inserted. You can use
this subsection to call skip.io() in order to undo the write action.

after.write:
The actions programmed in this subsection are executed after each write action
on the main table. A write action occurs when a record is inserted. You can use
this subsection, for example, to write data to tables other than the main table.

after.skip.write:
The actions programmed in this subsection are executed if a write action on the
main table has been skipped as a result of actions programmed in the
before.write subsection. The actions are executed immediately after the skip
action.

before.rewrite:
The actions programmed in this subsection are executed immediately before each
rewrite of a record in the main table. A rewrite action occurs when a record is
changed. You can use this subsection to call skip.io() in order to skip the rewrite
action.

after.rewrite:
The actions programmed in this subsection are executed after each rewrite action
on the main table. A rewrite action occurs when a record is changed.

after.skip.rewrite:
The actions programmed in this subsection are executed if a rewrite action on the
main table has been skipped as a result of actions programmed in the
before.rewrite subsection. The actions are executed immediately after the skip
action.

Programming features
3-19
4GL programming features

before.delete:
The actions programmed in this subsection are executed immediately before each
delete action on the main table. You can use this section to call skip.io() in order
to skip the delete action.

after.delete:
The actions programmed in this subsection are executed immediately after each
delete action on the main table. You can use this subsection, for example, for
deleting references, if this is not done automatically.

after.skip.delete:
The actions programmed in this subsection are executed if a delete action on the
main table has been skipped as a result of actions programmed in the
before.delete subsection. The actions are executed immediately after the skip
action.

read.view:
The actions programmed in this subsection are executed immediately after a
view action on the main table (start.set, first.view, next.view, prev.view or
last.view). You can use this subsection to call skip.io() in order to skip an entire
set of records with the same value in the view fields.

Example
main.table.io:
before.delete:
if (not pctst099.del) then
skip.io("pctsts0001")
| cannot be deleted
endif
read.view:
if strip$(pctst099.cprj) = "" then
skip.io("")
endif

Programming features
3-20
4GL programming features

Flow of standard program


This section presents the general flow of the standard program. It is not
complete, but the most important actions are included. The flow is similar for
programs of types 1, 2, 3, and 4. Any differences are indicated by comments.

Main routine
The main routine displays the first form on the screen and waits for a command.
function main()
{
before.program
read.form()
init.references
create.sql.queries
if background then
get.ref.var(parent)
read record
endif
db.bind(tmain) | Not in type 4
if job.process then | Only type 4
before.choice.run.job
execute(cont.process) or on.choice.run.job
after.choice.run.job
execute(end.program)
endif
zoom.from.on.entry
for each field on form
init.field
put.attributes()
endfor
change to start index | Not in type 4
init.form
execute start event
before.form

while true
input.choice()
if update.status and choice <> ... then | Not in
| type 4
on.update.db()
| See "update database" section
endif
on case choice
case <choice.option>
before.<choice.option>
on.<choice.option>
after.<choice.option>
break

Programming features
3-21
4GL programming features

case ...
....
endcase
endwhile

Choice sections
The choice sections above are given as before.choice.option and
after.choice.option. In a 4GL program, a choice section consists of the main
section choice.choice.option and one of the subsections before.choice, on.choice,
or after.choice.

Form sections
When changing from form X to form Y, the form sections are executed in the
following sequence:
after.form for X
init.form for Y (only the first time)
before.form for Y
The above subsections can be defined, as follows:
for all forms : form.all
for a specific form : form.<form name>
for forms not specifically defined : form.other
At init.form the sequence is:
form.all: init.form
form.<form name>: init.form or form.other: init.form

Programming features
3-22
4GL programming features

At before.form the sequence is:


form.all: before.form
form.<form name>: before.form or form.other: before.form
At after.form the sequence is:
form.<form name>: after.form or form.other: after.form
form.all: after.form

Field sections
There are two groups of field sections:
n sections for which the field.all section is executed first:
init.field
before.field
before.input
before.display
before.checks
before.zoom
check.input | if not in DAL
on.input
n sections for which field.all is executed after field.<field name> or field.other:
after.input
after.display
after.zoom
ref.input
ref.display
domain.error
when.field.changes
after.field
The general flow of input fields is as follows:
Field.input:
before.field
if not input.field then
read.reference
if error.reference then
ref.display
else
before.display
display.field()
after.display
endif

Programming features
3-23
4GL programming features

return
endif
before.input
if input disabled then
read.reference
if error.reference then
ref.display
else
before.display
display.field()
after.display
endif
return
endif

zoom.ret:
do.input.field()
if in.ret = ZOOM then
before.zoom
zoom.to ...
after.zoom
goto zoom.ret
endif
if choice = f.to.choice or
in.ret = arrow.up/down/left/right/tab then
goto after.field
endif
before.checks
check.domain()

if domain.error then
domain.error
error.message
goto field.input
endif
check.reference

if reference.error then
ref.input
error.message
goto field.input
endif
check.input
if check.input.error then
error.message
goto field.input
endif
on.input
....
....

Programming features
3-24
4GL programming features

when.field.changes
after.input
after.field

End of program
The flow of the end of the program is as follows. The function end.of.program()
is called when executing the standard command END.PROGRAM.
function end.of.program()
{
before.end.program
if e then
on.error
endif
after.form
after.program
if zoomfield <> "" then
fill.zoomreturn
zoom.from.on.exit
if record.marked then
commit.transaction() | Not in type 4
exit(exit.val)
endif
endif
commit.transaction() | Not in type 4
exit()
}

Zoom sections
The zoom.from.on.entry and zoom.from.on.exit sections are used In the main
routine and in the end.of.program() function. In a 4GL program, a zoom section
consists of a main section (zoom.from.all, zoom.from.<zoomname>, or
zoom.from.other) and a subsection (on.entry or on.exit).
The sequences in which the sections are executed differ for on.entry and on.exit.
For on.entry, zoom.from.all is executed first and then zoom.from.<zoomname>
or zoom.from.other. For on exit, it is the other way around.

Update database
An update of the database is necessary if a record is added, changed or deleted.
The actual update is executed if one of the following actions is activated after a
record has been added, changed or deleted: start.set, def.find, find.data, first.set,
next.set, prev.set, last.set, next.help, prev.halfset, change.order, cont.process.

Programming features
3-25
4GL programming features

All these actions first call on.update.db() if a record is added, changed or


deleted. The general flow of the on.update.db() is as follows. Note that this
function is not relevant to programs of type 4.
on.update.db()
{
if update.status = add.set or modify.set then
for all occurrences
check.domains()
if domain.error then
domain.error
error.message
return
endif
check.references
if reference.error then
ref.input
error.message
return
endif
for each field of occurrence
check.input
if check.input.error then
error.message
return
endif
endfor
endfor
endif
before.update.db
db.retry.point()
for all occurrences
before.write / before.rewrite / before.delete
if not skip then
write / rewrite / delete record
after.write / after.rewrite / after.delete
else
after.skip.write / after.skip.rewrite /
after.skip.delete
endif
endfor
back.to.old.key
after.update.db
commit.transaction()
after.update.db.commit
}

Programming features
3-26
4GL programming features

Main table i/o section


Most of the main table i/o are mentioned above. They are not available in
programs of type 4.
The read.view section is executed immediately after any view action on the main
table (only in type 3 programs). The function skip.io("messcode") can be used
in this section. This implies that the next or previous view is read.

Automatic import of variables


When you zoom from a main process to a subprocess, in some cases variables
are imported automatically.

Case 1
From the main process with main table X you have zoomed to the subprocess
with main table Y. Table X has a foreign (cross-reference) key to table Y. For
example, X = customer table, Y = currency table. The currency field in the
customer table refers to the currency table.
The standard program imports the field(s) from table X and stores them in the
primary key fields of table Y.

Consequences for programming in script


In the script of the subsession, only the following needs to be programmed:
if background then
execute(find.data)
(or, sometimes, for type 3 screens:
execute(first.set))
else
....
endif

There is no need to adjust the main session in order to import the variables into
the subsession.

Case 2
From the main process with main table X, you have zoomed to the subprocess
with main table Y. A field in table Y refers to table X. (In most cases, there is an
identifying relationship between table X and table Y – for example, order header
(table X) and order lines (table Y).) The standard program imports the primary
key values from table X and stores them in the corresponding fields of table Y.

Programming features
3-27
4GL programming features

Example
Primary key of table X is Order number; primary key of table Y is Order number,
position number.
The standard program imports the order number from table X to the
corresponding field in table Y. The position number in table Y remains, of
course, empty.

Consequences for programming in script


See Case 1 above.

Case 3
From the main process with main table X, you have zoomed to the subprocess
with the same main table. (For example, a zoom from ’Maintain Item Data’ (type
1) to ’Display Item Data’ (type 2 or 3).)
The standard program imports the primary key value from table X for the main
process and stores it in the primary key fields of table X for the subprocess.

Consequences for programming in script


See Case 1 above.

Case 4
From the main process with main table X you have zoomed to a subprocess
without a main table (for example, a print program). The standard program
imports the primary key values of main table X for the main process and stores
them in the primary key fields of table X (if available) for the subprocess.

Example
A zoom from ’Maintain Item Data’ to ’Print Item Data’. The main table for the
main process is tstpi100. The subprocess imports values of tstpi100.cuno and
tstpi100.item from the main process and stores them in the fields of the
subprocess.

Consequences for programming in script


In many cases, you must reprogram the main process in such a way that the
primary key fields of the main table for the subprocess are filled.

Programming features
3-28
4GL programming features

Case 5
From the main process without a main table you have zoomed to a subprocess
with main table X. The standard program imports the primary key values of table
X (if available) for the main process and stores them in the primary key fields of
the subprocess’s main table.

Example
A zoom from ’Print Item Data’ to ’Display Item Data’. The main table for the
subprocess is ttstpi100. The standard program tries to import tstpi100.cuno and
tstpi100.item from the main process and stores them in the fields of the main
table for the subprocess.

Consequences for programming in script


See Case 1 above.
In many cases you must reprogram the main process in such a way that the
primary key fields of the main table for the subprocess are filled.

Example
field.item.t:
before.zoom:
tstpi100.cuno = cuno.f
tstpi100.item = item.f

Case 6
From the main process with table X you have zoomed to a subprocess with table
Y. There is no relationship between table X and table Y (none has been defined
in the data dictionary). The standard program imports the primary key values of
table Y for the main process (if available) and stores them in the primary key
fields of table Y for the subprocess.

Consequences for programming in script


See Case 1 above.
Often you will have to reprogram the main process in such a way that the
primary key fields of the main table for the subprocess are filled.

Programming features
3-29
4GL programming features

Example
field.tswoc150.item:
before.zoom:
tstpi100.cuno = tswoc100.cuno
tstpi100.item = tswoc150.item

Programming features
3-30
4 Report scripts

Overview
BaanERP reports are used to output data from the database to a variety of devices
(for example, printers, displays, and files).
The contents and layouts of reports are defined in the data dictionary. In addition,
you can link a report script to a report. In a report script, you can program actions
that you want to be performed at particular stages of the report execution. For
example, you can create a script to perform calculations on the report data or to
read records from related tables that are not automatically available to the report.
You program report scripts in the same way as you program 4GL program
scripts, except that report scripts use different event sections and some special
functions.

Event sections
A report script consists of one or more event sections in which you program
actions to be performed at particular states of execution of the report to which the
report script is linked. The statements programmed in a report script section
consist of a combination of 3GL language statements and report script functions.
Report scripts support the following event sections:
n program sections
n report sections
n text field sections

Program sections
You use program sections to define functions, and to declare tables and global
variables, that you want to use in the other sections in the script. You also use
program sections to program actions that you want executed at the start and end
of the report and after the report writer receives a record.
The following program sections are available. They are all main sections. There
are no subsections associated with program sections.

Programming features
4-1
Report scripts

declaration:
Use this section to declare tables and global variables that you want to use in
other sections in the report script. See “Declarations” and “Tables” in Chapter 2
for details of the declaration syntax. Note that there is no need to declare a table
if a field of that table is defined in the report writer as an input field. The report
writer declares such tables automatically.

before.program:
Use this section to program actions that must be executed when the report is
started. For example, you can use this section to initialize or import variables.

after.program:
Use this section to program actions that must be executed at the end of the report.
For example, you can use this section to close files that were opened during
execution of the report.

after.receive.data:
Use this section to program actions that must be executed whenever the report
writer receives a record. For example, you can use this section to read or modify
a variable.

functions:
Use this section to define functions that you want to use in other sections in the
report script. The syntax of functions used in a report script is the same as for
3GL functions.
When included, this section must be the last section in the report script.

Report sections
A report consists of a number of layouts, which define the content and
organization of the various parts of a report (for example, the header, footer, and
body of the report). There are a number of possible layout types, as follows:
n before.report – printed once at beginning of the report (a title page, for
example)
n after.report – printed once at the end of the report
n header – printed at the top of each page

Programming features
4-2
Report scripts

n footer – printed at the bottom of each page


n before.field – printed before each group of records in reports of type 3; if a
group of records spans more than one page, the before.field layout is repeated
at the top of the new page
n after.field – printed immediately after each group of records in reports of type
3
n detail – used for the individual records included in the body of the report
The content and arrangement of these layouts are defined in the data dictionary.
In the report sections in a report script, you can program actions to be performed
before and/or after execution of individual layouts of these types. Report sections
consist of a main section and a subsection. You use a main section to specify the
particular layout for which the programmed actions are to be executed. You must
follow a main section with either a before.layout or an after.layout subsection.
These latter specify whether the actions are to be executed before or after the
particular layout is printed.

Main sections

before.report.<layout_number>:
Actions programmed in this section are executed for a specified layout of type
before.report.

after.report.<layout_number>:
Actions programmed in this section are executed for a specified layout of type
after.report.

header.<layout_number>:
Actions programmed in this section are executed for a specified layout of type
header.

detail.<layout_number>:
Actions programmed in this section are executed for a specified layout of type
detail.

before.<field name>.<layout_number>:
Actions programmed in this section are executed for a specified layout of type
before.field.

Programming features
4-3
Report scripts

after.<field name>.<layout_number>:
Actions programmed in this section are executed for a specified layout of type
after.field.

footer.<layout_number>:
Actions programmed in this section are executed for a specified layout of type
footer.

Subsections

before.layout:
Actions programmed in this subsection are executed before the specified layout
is printed.
When lattr.print is set to false, the layout is not printed and the before.layout
and after.layout sections are not executed. If you set lattr.print to false in the
before.layout section itself, the layout is not printed and the after.layout section
is not executed.

after.layout:
Actions programmed in this subsection are executed after the specified layout is
printed.

Text field sections


You use a text field section to apply certain actions to all instances of a particular
text field that are included in the report. A text field section consists of a main
section and a subsection. The main section specifies the particular text field to
which the actions must be applied. The subsection specifies that the actions are to
be performed before the text field is printed.
For example, you could include the page(), need(), and skip() functions in a text
field section to enforce a page break or to skip a specified number of lines before
the text field is printed. Or you could set lattr.print in the section in order to
skip one text line before printing the text field.

Programming features
4-4
Report scripts

Main section

field.<text_field>:
Actions programmed in this section are executed for all instances of the specified
text field that are included in the report. The text field must have been defined as
a report input field in the data dictionary.

Subsection

before.print:
Actions programmed in this section are executed before the specified text field is
printed.

Predefined variables
You can use the following predefined variables in a report script.
R = read-only R/W = read and write

lattr.autobefores R/W A boolean indicating whether or not before.field


layouts are automatically repeated at the top of a
page if the page break does not correspond with a
sort break. The default value is true.
lattr.autoreset R/W A boolean indicating whether or not
reset.suppress() is automatically performed in the
case of breaks of layouts in sorting fields. The
default value is true.
lattr.break R A boolean that is set to true during printing of
before.field layouts when the layout is printed as
the result of a sort field change. It is set to false
when a before.field layout is printed because of a
page break.
lattr.enddata R A boolean indicating whether or not the end of the
data has been reached.
lattr.header R A boolean that is set to true during printing of
header layouts and during printing of before.field
layouts at the top of a page.
lattr.language$ R The language code of the report.

Programming features
4-5
Report scripts

lattr.lineno R The current line number.


lattr.multicol R/W Indicates whether or not multiple columns are used
when more than one layout fits on a line. Possible
values are:
MULTICOL_NONE
Normal output; no columns are used.
MULTICOL_ORDER_HORIZONTAL
Output is arranged in multiple columns, ordered
horizontally. For example:
rec_1 rec_2 rec_3
rec_4 rec_5 rec_6
MULTICOL_ORDER_VERTICAL
Output is arranged in multiple columns, ordered
vertically (per page). For example:
rec_1 rec_3 rec_5
rec_2 rec_4 rec_6
lattr.multicol.count R/W Specifies the number of columns to use when
multiple columns are specified (see
lattr.multicol). The default value is 0. In this case,
the report manager calculates the number of
columns based of the report width and the layout
width.
lattr.multicol.repeat R/W A boolean indicating whether headers and footers
are automatically repeated for each column. The
default value is true. The width of the header and
footer layouts width must be equal to or less than
the width of the detail layout.
lattr.pageno R/W Current page number.
lattr.print R/W Indicates whether or not a layout is to be printed.
lattr.prline R/W After expansion of a variable in a report, this is
filled with the text line that is printed.
lattr.recordtimes R/W Indicates the number of times the layout will be
printed.

Programming features
4-6
Report scripts

lattr.textexpand R/W A boolean indicating whether or not variables


included in text fields can be expanded. The
default is false.
lattr.textlang$ R/W The language code for printing text. The default is
the language code of the report.
lattr.textline R A string containing the source text line from the
text manager.
lattr.textlineno R The number of the line that must be printed.
lattr.textlines.max R/W The maximum number of lines in the layout.
lattr.textlines.min R/W The minimum number of lines in the layout.

Report script functions


The statements programmed in a report script section consist of a combination of
3GL language statements and the following 4GL report functions:
layout.again() Evaluate and print layout again.
need( expr ) Force a page break if the number of free lines is less
than expr.
page() Start a new page.
reset.suppress() It is possible to suppress printing a value when it is
the same as the previous value. You can use this
function to suppress the value once (that is, for one
value).
skip( expr ) Skip expr number of lines before printing the next
layout.
skip.to( expr ) Skip to the line number indicated by expr.
Intervening lines are left blank. If the specified line
number is less than the current page number, a new
page is started.
to.page( expr ) Start a new page with number expr.

You cannot use need(), page(), skip(), skip.to(), or to.page() in header and
footer layouts.

Programming features
4-7
Report scripts

Expanding text variables


Variables or expressions that are included in text lines can be expanded or
substituted for report purposes. You must set the variable
lattr.textexpand=TRUE if you want this to happen. Expressions are internally
evaluated by the function expr.compile().
Customers can use variables in texts. For example, "address", "city", "salary" and
so on. Customers must be informed about these variables so that they can use
them.

Syntax
The following is the syntax for variables and expressions included in text lines:
$variable or #variable
${expression} or #{expression}

When the $ symbol if used, the text moves over to accommodate the expanded
variable or expression. When the # symbol is used, the expanded variable or
expression overwrites existing text.

Example
The following is an example of text created by using the text manager. The text
includes both variables and expressions that can be expanded/substituted for
report purposes.
To: $surname $christian name #date , Ede
$address
$ZIP code $city
Dear $christian name:
We have the pleasure of informing you that, as of $date,
your salary has been raised by 5%. For you this implies
a gross amount of
${edit( salary + ( salary * 0.05 ), "ZZZZ9,ZZ" )} per
month.
Kind regards, $Mgr.

Programming features
4-8
5 Database handling

Overview
BaanERP data is stored in a relational database, which is managed by a
Relational Database Management System (RDBMS). The RDBMS acts as the
database server for BaanERP applications. BaanERP supports the following
RDBMS products:
n Informix
n Oracle
n Sybase
n DB2
n Microsoft SQL Server
The BaanERP architecture includes a database driver. This provides the
application server (that is, the bshell) with a common interface to the database
server, regardless of which RDBMS product is used. This enables the application
server to be database independent. The database driver is responsible for
translating database commands received from the application server into
RDBMS-specific commands. Although, the database driver’s interface with the
application server is the same for all RDBMS products, Its interface with the
RDBMS is RDBMS-specific. Therefore, there is a separate database driver for
each of the supported RDBMS products.

Programming features
5-1
Database handling

Database tables
A relational database presents information to the user in the form of tables. In a
table, data is organized in columns and rows. Each column (also referred to as a
field) represents a category of data. Each row (also referred to as a record)
represents a unique instance of data for the categories defined by the columns.

A field always refers to a domain, which defines a set of values from which one
or more fields can draw their actual values. For example, the ’tcweek’ domain is
the set of all integers greater than zero and less than or equal to 53.

Primary keys
Every database table has a field, or a combination of fields, which uniquely
identify each record in the table. This unique identifier is referred to as the
primary key. Primary keys are fundamental to database operations, as they
provide the only record-level addressing mechanism in the relational model.
Primary keys act as references to the records in a table.

Relationships/references
With a relational database, you can store data across multiple tables and you can
define relationships between the tables. This means that individual tables can be
kept small and data redundancy can be minimized. A relationship exists between
two tables when they have one or more fields in common. So, for example, a
Customer Details table can be linked to an Orders table by including a Customer
ID field in both tables. In the Customer Details table, the Customer ID field is the
primary key. In the Orders table, it is referred to as a foreign key. By linking the
two tables in this way, there is no need for the Orders table to include customer
details such as name and address. Note that references from one table to another
must always use the primary key.

Programming features
5-2
Database handling

Combined fields
A combined field is a field that consists of two or more child fields. You can use
combined fields as primary keys, and you can also use them for references and
indexes.

Indexes
Indexes facilitate speedy searching and sorting of database tables. An index is a
special kind of file (or part of a file) in which each entry consists of two values, a
data value and a pointer. The data value is a value for some field in the indexed
table. The pointer identifies the record that contains this value in the particular
field. This is analogous to a conventional book index, where the index consists of
entries with pointers (the page numbers) that facilitate the retrieval of
information from the body of the book.
Note that it is also possible to construct an index based on the values of a
combination of two or more fields.
Every table must have at least one index, which is an index on the primary key
field(s). This is referred to as the primary index. An index on any other field(s) is
referred to as a secondary index.

Structured Query Language (SQL)


Baan SQL is the database query language that you use to access data in the
database tables. Using Baan SQL, you can construct queries to retrieve specific
data from the database. The syntax and usage of Baan SQL is discussed fully in
“Baan SQL” in Chapter 5.

Programming features
5-3
Database handling

Naming conventions
The naming syntax for tables, record buffers, and table fields is as follows:
tppmmmxxx | table
rcd.tppmmmxxx | record buffer of table
ppmmmxxx.ffffffff | logical field of table

where t stands for table, pp is the package code, mmm is the module code, xxx is
the table number (range 000 to 999), and ffffffff is a field name (maximum 8
alphanumeric characters, starting with an alphabetic character).

Using tables in program scripts


If a table is used in a script it must be declared with the following statement:
table tppmmmxxx

Declaration of a table implies declaration of all its fields and its record buffer. It
is not necessary to declare these separately.
There are no special functions for opening and closing tables. A table is
automatically opened at the first database call on that table and it is automatically
closed at the end of the program.
You can use the record buffer of a table to save the contents of a record in a
temporary string. And you can subsequently restore the record from the buffer. If
a record contains numeric fields, you can only save and restore records. You
cannot perform any other actions on the temporary string, as the presence of
NULL characters will cause loss of data.

Data types
The data type is the internal representation of table field and domain data. The
following data types are available:
DB.BITSET DB.INTEGER
DB.BYTE DB.LONG
DB.COMBINED DB.MAIL
DB.DATE DB.MULTIBYTE
DB.DOUBLE DB.STRING
DB.ENUM DB.TEXT
DB.FLOAT DB.TIME

Programming features
5-4
Database handling

Transaction handling
With respect to database actions, a transaction is a sequence of related actions
that are treated as a unit. The actions that make up a transaction are processed in
their entirety, or not at all.
A transaction ends with the function commit.transaction() (all changes made
during the transaction are stored in the database) or with the function
abort.transaction() (no changes are stored in the database). A transaction starts
either at the beginning of a process, with the function
set.transaction.readonly(), with the function db.lock.table(), or after the
preceding transaction has ended.
A transaction is automatically rolled back (that is, it is undone) when a process is
canceled and if a program ends without a commit.transaction() or
abort.transaction() after the last database call. Undoing a transaction is only
possible if the underlying database system supports this.
Certain database actions cannot be placed within a transaction, because they
cannot be rolled back. These actions are: db.create.table(), db.drop.table(), and
set.transaction.readonly(). These functions can be called only at the start of a
program or after the end of the preceding transaction.
You can set a retry point immediately before a transaction. In case of an error,
the system returns to this point and re-executes the transaction from there.
A read-only transaction is a transaction in which you are permitted only to read
records (without lock) from the database. You retain read consistency during the
entire transaction. This means that during the transaction your view of the
database does not change, even if other users update the records. A read-only
transaction starts with the function set.transaction.readonly() (this must be
called after ending the preceding transaction or at the beginning of the program)
and ends with a commit.transaction() or abort.transaction(). A consistent view
consumes a large amount of memory, so a read-only transaction must be as short
as possible; user interaction during the transaction is not recommended.

Locking
Database inconsistencies can arise when two or more processes attempt to update
or delete the same record or table. Read inconsistencies can arise when changes
made during a transaction are visible to other processes before the transaction has
been completed – for example, the transaction might subsequently be abandoned.

Programming features
5-5
Database handling

To avoid such inconsistencies, BaanERP supports the following locking


mechanisms:
n Record/page locking
n Table locking
n Application locking

Record/page locking
To ensure that only one process at a time can modify a record, the database
driver locks the record when the first process attempts to modify it. Other
processes cannot then update or delete the record until the lock has been released.
However, they can still read the record.
While one process is updating a table, it is important that other processes retain
read consistency on the table. Read consistency means that a process does not see
uncommitted changes. Updates become visible to other processes only when the
transaction has been closed. Some database systems do not support read
consistency, and so a dirty read is possible. A dirty read occurs when one process
updates a record and another process views the record before the modifications
have been committed. If the modifications are rolled back, the information read
by the second process becomes invalid.
Some databases, such as SYBASE, use page locking instead of record locking.
That is, they lock an entire page in a table instead of an individual record. A page
is a predefined block size (that is, number of bytes). The number of records
locked partly depends on the record size.

Supported features
INFORMIX ORACLE SYBASE DB2 SQL Server
locking row row page row page
dirty read yes no *
consistent read no yes *
transactions yes yes yes yes yes
* SYBASE will block when there is a read action (locked or not) on a record in a
page that has been locked.

Programming features
5-6
Database handling

Delayed locks
Locking a record for longer than required can result in unnecessarily long
waiting times. The use of delayed locks solves this problem to a great extent.
A delayed lock is applied to a record immediately before changes are committed
to the database and not earlier. When the record is initially read, it is temporarily
stored. Immediately before updating the database, the system reads the value of
the record again, this time placing a lock on it. If the record is already locked, the
system goes back to the retry point and retries the transaction. If the record is not
locked, the system compares the content of the record from the first read with the
content from the second read. If changes have been made to the record by
another process since the first read, the error EROWCHANGED is returned and
the transaction is undone. If no changes have occurred, the update is committed
to the database.
You place a delayed lock by adding the keyword FOR UPDATE to the SELECT
statement (see “BAAN SQL” later in this chapter).
For example:
table tpctst999
db.retry.point()
SELECT pctst999.*
FROM pctst999 FOR UPDATE
SELECTDO
pctst999.dsca = "...."
....
db.update(tpctst999, DB.RETRY)
ENDSELECT

Table locks
BaanERP provides a table locking mechanism, which enables you to lock all the
records in a specified table. A table lock prevents other processes from
modifying or locking records in the table but not from reading them. This is
useful when a particular transaction would otherwise require a large number of
record locks. You use the db.lock.table() function to apply a table lock.

Application locks
An application lock prevents other applications and users from reading and/or
modifying an application’s data during critical operations. It is not part of a
transaction and so is not automatically removed when a transaction is committed.
Instead, an application lock is removed when the application ends or when
appl.detete() is called.

Programming features
5-7
Database handling

Retry points
A retry point is a position in a program script to which the program returns if an
error occurs within a transaction. The transaction is then retried. There are a
number of situations where retry points are useful:
n During the time that a delayed lock is applied to a record/page, an error can
occur that causes the system to execute an abort.transaction(). In such cases,
all that BaanERP can do is inform the program that the transaction has been
aborted. However, if retry points are used, the system can automatically retry
the transaction without the user being aware of this.
n Some database systems generate an abort.transaction() when a dirty record
is read (that is, a record that has been changed but not yet committed). An
abort.transaction() may also be generated when two or more processes
simultaneously attempt to change, delete, or add the same record. In all these
situations, BaanERP Tools can conceal the problem from the user by using
retry points. It simply retries the transaction. If there is no retry point, the
transaction is aborted and the session is terminated.
n In BaanERP, updates are buffered, so the success or failure of an update is
not known until commit.transaction() is called. If an update fails, the
commit of the transaction also fails, and the entire transaction must be
repeated. If retry points are used, the system automatically retries the
transaction.
n Retry points can also resolve potential deadlock problems. If, for example,
the system is unable to lock a record, it rolls the transaction back and tries
again.
NOTE It is vital that retry points are included in all update programs.

Coding retry points


The retry point for a transaction must be placed at the start of a transaction. The
following example illustrates how you program retry points:
db.retry.point() | set retry point
if db.retry.hit() then
...... | code to execute when the system
| goes back to retry point
else
...... | initialization of retry point
endif

The function db.retry.hit() returns 0 when the retry point is generated – that is,
the first time the code is executed. It returns a value unequal to 0 when the
system returns to the retry point through the database layer.

Programming features
5-8
Database handling

When the system goes back to a retry point, it clears the internal stack of
functions, local variables, and so on that were called during the transaction. The
program continues from where the retry point was generated. The value of global
variables is NOT reset.
When a commit fails, the database automatically returns to its state at the start of
the transaction; the program is set back to the last retry point. It is vital, therefore,
that the retry point is situated at the start of the transaction. The db.retry.hit()
call must follow the db.retry.point() call. Do not place it in the SQL loop itself
as this makes the code very untransparent. When a retry point is placed within a
transaction, the system produces a message and terminates the session.

Testing retry points


The following shell variables enable you to test the operation of retry points:
TEST_RETRY=X This variable indicates how often the system must go
back to a retry point at the moment of committing. This
cannot be used when testing different sessions with retry
points parallel.
MAX_RETRY=X This variable indicates how often the system may return
to a retry point as a result of an abort in an update action.
Default: 10

Baan SQL
You use Baan SQL (Structured Query Language) to retrieve information from
the database. With Baan SQL’s SELECT statement, you can retrieve any
selection of data from the database, regardless of the number of tables in which
the data is stored. The system does search through all records sequentially. It
bases its search on a number of conditions defined in the SELECT statement.
The SELECT statement does not modify the data in the database. It is used
simply to query the data. Whereas only one user at a time can modify data, any
number of users can query or select the data concurrently.
The SELECT statement works at the table level; the output is also a table
consisting of 0, 1, or more records. If the query is very specific, the output may
consist of only one record. The structure of a SELECT statement is directly
derived from the table structure. It takes the following general form:
SELECT columns ...
FROM table(s) ...
WHERE each row fulfills the condition(s) ...

Programming features
5-9
Database handling

The following sections provide information on SQL syntax and using SQL.

SQL syntax
The general structure of an SQL query is as follows:
SELECT <select list>
[ FROM <from list> ]
[ WHERE <where condition> ]
[ GROUP BY <group list>
[ HAVING <having condition> ] ]
[ ORDER BY <order by list> [WITH RETRY [REPEAT LAST ROW] ]]
[ SET SPECIFICATION ]

For full details of any of the above clauses of the SELECT statement, see the
sections below.

SELECT <select list>


The SELECT clause specifies the table fields that must be represented in the
output and the functions (if any) that must be executed on the selected fields.
You can use the wildcard character [*] to specify all fields of all tables. You can
specify all fields of a particular table by suffixing the table name by the wildcard
character [*].
You can use the following functions in the SELECT clause:
n min(<field name>) – for the minimum value of the field
n max(<field name>) – for the maximum value of the field
n sum(<field name>) – for the sum of the values of the field
n count(<field name>) – for the number of fields selected
n count(*) – for the number of records selected
n avg(<field name>) – for the average value of the field.
These functions consider all selected records as one group and are executed on
that group. So they produce a single result – for example, the average value of a
specified table field. You usually use these functions in combination with the
GROUP BY clause. With this clause, you can group the results into a number of
subsets. The function is then performed on each subset.

Programming features
5-10
Database handling

FROM <from list>


The FROM clause specifies the names of the tables from which fields must be
selected. The names of fields specified in the SELECT clause include the table
name. It is not necessary to include these tables in the FROM clause as well.
In the FROM clause, you can define aliases for table names. Aliases are
necessary, for example, for double references and for references to the main
table. You define an alias as follows:
<table name> <alias name>

Include the keyword FOR UPDATE after a table name to indicate that the table
must be read with delayed lock. If you include FOR UPDATE after the keyword
SELECT in the SELECT clause, this indicates that all tables must be read with
delayed lock.

WHERE <where condition>


The WHERE clause specifies one or more conditions which the selected records
must meet. It has the following format:
WHERE <operand1> <operator> <operand2> [<operand3>]

Operands
Operands 1 and 2 can consist of any of the following:
n numeric constant (for example, 10, 3.1415927)
n string constant (for example, "John", "mod")
n combined constant (for example, { 10 , "xyz" })
n table field, optionally followed by array indication
n pseudo field – for example, for indexes (<table>._index1, see “Combined
fields” earlier in this chapter)
n 4GL variable or pseudo variable (see “Using program variables” later in this
chapter)
n numeric expression (in which the operators +, -, *, /, \ are possible)
n string expression (in which the operator & and a substring are possible)
n special field – enum constants, date constants (using the function
date.to.num(Y,M,D) and date.num())

Programming features
5-11
Database handling

Operand 2 can also consist of the following:


n subqueries
n set definition (as subquery or a list of constants)

Operators
The following operators are possible:
n comparison operators
=, <, <=, >, >=, <>, !=
#>, #>=, #<, #<= (for combined fields)
n IN operator
Here, operand2 contains a set of values (or subquery). The system tests if the
value of operand1 is present in operand2. This operator replaces a number of
expressions separated by the OR operator. For example, both of the following
statements define the same condition:
WHERE ttadv200.cpac = "tc" OR ttadv200.cpac = "tt"
WHERE ttadv200.cpac IN ( "tc", "tt" )
n LIKE operator
The LIKE operator compares operand1 with operand2. Operand2 can contain
wild cards. For example:
WHERE name LIKE "[Tt]riton.*"

In this example, "Triton2.1" and "triton" are legal values. In this case,
operand2 is a regular expression. See the expr.compile() function for a list of
the possible features that the expression can contain.
n BETWEEN / INRANGE operator
These operators check whether operand1 falls within a specified range of
values. Operand2 and operand3 specify the lower and upper values of the
range. For example, both of the following statements define the same
condition – that is, they define a range from "tc" through "ts":
WHERE ttadv200.cpac >= "tc" AND ttadv200.cpac <= "ts"
WHERE ttadv200.cpac INRANGE "tc" AND "ts"

The operators BETWEEN and INRANGE are identical, except when you use
combined fields (see “SQL and combined fields” later in this chapter).

Programming features
5-12
Database handling

n EXISTS operator
This operator tests whether certain records do or do not occur. It is used in
conjunction with subqueries. The following example selects only those items
on the order list with an order quantity greater than 100:
select tiitm001.item, tiitm001.dsca
from tiitm001
where exists
( select timps052.*
from timps052
where timps052.item = tiitm001.item and
timps052.qana > 100 )
n REFERS TO operator
See “References” later in this chapter.

GROUP BY <group condition>


The GROUP BY clause groups the records of the output table, based on the
fields specified in the clause. It combines similar records, producing a single
result record for each group of records with the same values for each table field
listed in the SELECT clause. All elements of the <group condition> must be
specified in the <select list> of the SELECT clause.
The power of the GROUP BY clause is most apparent when it is used with the
functions min, max, count, avg, and sum. In these cases, it produces summary
values for each group. For example:
SELECT ttadv200.cpac, ttadv200.cmod, ttadv200.cses
FROM ttadv200
GROUP BY ttadv200.cpac, ttadv200.cmod, ttadv200.cses

| Sessions with the same session code and different VRCs


| are selected only once

SELECT ttadv200.cpac, ttadv200.cmod, ttadv200.cses, count(*)


FROM ttadv200
GROUP BY ttadv200.cpac, ttadv200.cmod, ttadv200.cses

| The program counts the sessions with the same


| names but different VRCs.

Programming features
5-13
Database handling

HAVING <having condition>


The HAVING clause sets conditions on groups defined by the GROUP BY
clause (that is, HAVING does for groups what WHERE does for records). If you
use a HAVING clause, you must specify a GROUP BY clause also. The
elements in the <having condition> can be either elements from the <select list>
in the SELECT clause or functions on fields from the <select list>. The <having
condition> can contain arithmetical and string expressions. For example:
SELECT ttadv200.cpac, ttadv200.cmod, ttadv200.cses, count(*)
FROM ttadv200
GROUP BY ttadv200.cpac, ttadv200.cmod, ttadv200.cses
HAVING count(*) > 1

ORDER BY<order by condition>


The ORDER BY clause has the following syntax:
ORDER BY <order by condition> [ WITH RETRY [REPEAT LAST
ROW] ]

This clause determines the order of the records in the output table. In the <order
by condition>, you list one or more fields from the <select list> in the SELECT
clause. The field listed first in the ORDER BY clause takes precedence. You can
list the fields by name or you can specify an index to their position in the
SELECT clause. For each field in the <order by condition>, you can indicate if
the order is to be ascending (default) or descending – for example, ORDER BY 1
asc, 2 desc.
If retry points are included, you must use ORDER BY to ensure that the
sequence after return to the retry point is identical to the one used the first time.
After a jump to a retry point, it is important that the query continues at its
position in the selected set at the time that the retry occurred. By using the WITH
RETRY addition this is easy to realize. By performing a commit.transaction(),
the values for the retry conditions are saved. In case of a jump to a retry point,
the query continues, starting from the values saved during the
commit.transaction(). In the case of WITH RETRY the new set starts after the
saved values. In the case of WITH RETRY REPEAT LAST ROW, the set starts
at the saved value; so the last record is read again. In the case of WITH RETRY
REPEAT LAST ROW, enter a commit.transaction() in the SELECTEOS part
of the select statement to prevent a new build-up of the query in some cases.

Programming features
5-14
Database handling

Example (embedded SQL)


SELECT tisfc001.*
FROM tisfc001 FOR UPDATE
WHERE tisfc001.pdno < 100
ORDER BY tisfc001.pdno WITH RETRY
SELECTDO
tisfc001.proc = tcyesno.yes
db.update(tisfc001, DB.RETRY)
commit.transaction()
ENDSELECT
| In this case we do not use REPEAT LAST ROW, because the
| moment retry takes place, the value present at the stage of
| commit.transaction() has certainly been saved.

Set specification
Baan SQL supports the following options in relation to sets:
n Maximum set size.
With this option you can indicate the maximum number of records the query
can produce. The syntax is:
SELECT .... [from][where] AS SET WITH <number> ROWS
n Prepared set.
With this option, the entire set is retrieved before the first record is returned.
The set is temporarily stored. This option is useful when a process
simultaneously selects and maintains (or deletes or adds) records. In this case,
changes must not be visible in the selected records. The prepared set option
forces a consistent read. The syntax is as follows:
SELECT ... [from][where] AS PREPARED SET
n Maximum set size and prepared set.
You can combine both options as follows:
SELECT ... [from][where] AS PREPARED SET WITH <number>
ROWS

Using program variables


As a 4GL query is handled via the bshell, you can directly relate program
variables to it. Linking program variables is required for both the SELECT
clause and the WHERE clause.

Programming features
5-15
Database handling

SELECT clause
In the <select list> of the SELECT clause, program variables are used to indicate
the elements in which the query results must be stored. You can do this by
explicitly specifying a program variable. The general syntax is:
<select part>:<program variable>

For example:
SELECT ppmod123.field1:my_val1

Or you can directly use a program variable (a table or field name) from the
calling program in the <select list>. For example:
SELECT ppmod123.field1

In the first example, the program places the result in the <program variable>. In
the second example, the name in the query and the name of the program variable
are identical.

FROM clause
You can also use program variables in the WHERE clause. When evaluating the
query, the program loads the values of the program variables into the query and
includes them in the evaluation. This link can be realized by incorporating a
program variable in the query, preceded by a colon [:]. For example:
WHERE ppmod123.field1 = :myval1

Note
A table field can be a program variable as well as a query variable. Note that the
following query:
select tccom010.*
where tccom010.cuno = tccom010.cuno

has a different result from:


select tccom010.*
where tccom010.cuno = :tccom010.cuno

The latter selects one record; the former selects all records from the table
tccom010, as tccom010.cuno by definition equals tccom010.cuno for each row.

Programming features
5-16
Database handling

References
Retrieving references to a record
In the standard SQL interface, you can retrieve references to a record as follows:
| Suppose that tiitm001 has a reference to tccom010
| (field ’cuno’), and to tccom011 (field ’suno’)
table ttiitm001
table ttccom010
table ttccom011
SELECT tiitm001.*, tccom010.*, tccom011.*
FROM tiitm001, tccom010, tccom011
WHERE tiitm001.cuno = tccom010.cuno AND
tiitm001.suno = tccom011.suno

Retrieving references using REFERS TO


To simplify the retrieval of references, you can specify references (using the
keyword REFERS TO) in the WHERE clause. Apart from simplifying the query,
this also optimizes query handling. As references always refer to a primary key,
they can be found immediately. Moreover, the program fills a field with
reference characters if it does not find a reference. The following implements the
previous example by using REFERS TO:
table ttiitm001
table ttccom010
table ttccom011
SELECT tiitm001.*, tccom010.*, tccom011.*
WHERE tiitm001.cuno REFERS TO tccom010 AND
tiitm001.suno REFERS TO tccom011
| OR
SELECT tiitm001.*, tccom010.*, tccom011.*
WHERE tiitm001 REFERS TO tccom010 AND
tiitm001 REFERS TO tccom011

Programming features
5-17
Database handling

REFERS TO syntax
A REFERS TO statement has the following form:
<from> REFERS TO <to> [PATH <path> [,<path>...]]
[UNREF<mode>]

The following table explains the various parts of the statement:


<from> The referring table field or table.
<to> The table referred to.
<path> The path via which reference is reached (always table fields). If
PATH is specified, specifying a table field for <from> is
mandatory. For example:
WHERE table1.field REFERS TO table4.field
PATH table2.field, table3.field
<mode> A mode indicating system action if reference does not exist;
possible values are:
SKIP
If a reference cannot be found, the record is skipped.
CLEAR
If a reference is empty or absent, the referring record is filled
with spaces or 0 (numeric).
SETUNREF
The value of an undefined reference is filled with an ’undefined
reference’ sign, defined in the data dictionary, or with 0
(numeric).
CLEARUNREF
The referred record is filled with spaces or 0 (zero) when
reference fields are empty. When the reference is undefined the
referred record is filled with an ’undefined reference’ sign.
Depending on the reference definition in the data dictionary, the
default reference mode is:
reference mode in DD UNREF mode
mandatory SETUNREF
mandatory unless empty CLEARUNREF
not mandatory CLEAR

Programming features
5-18
Database handling

Example
SELECT ttadv100.*
FROM ttadv100, ttadv101
WHERE ttadv101.cmod BETWEEN "aaa" and "azz"
AND ttadv101 REFERS TO ttadv100 UNREF <UNREF_mode>
AND ttadv100.cpac BETWEEN " " and "zz"

The evaluation order of this query is as follows:


1 Find all rows / records in ttadv101 that match the BETWEEN "aaa" and "azz"
condition.
2 Find all references from each row from step 1 in table ttadv100.
3 Find all rows selected in step 2 which match the BETWEEN " " and "zz"
condition
When some reference does not exist in ttadv100 (step 2 fails), because of the
condition on the referenced table, the whole row is rejected.

Using aliases with REFERS TO


You can use aliases to refer from one table to another with two references. In the
following example, there are two references from ttadv300 to ttaad110, via
ttadv300.lanl and via ttadv300.clan. Both are searched for in the following
construction:
string desc.clan(20), desc.lanl(20)
table tttadv300
SELECT ttadv300.desi, ttadv300.cfrm, ttadv300.clan,
tclan.dsca:desc.clan, ttadv300.lanl,
tlanl.dsca:desc.lanl
FROM ttadv300, ttaad110 tlanl, ttaad110 tclan
WHERE ttadv300.desi = :designer AND
ttadv300.clan REFERS TO tclan UNREF SETUNREF AND
ttadv300.lanl REFERS TO tlanl UNREF SETUNREF

Using program variables or constants with REFERS TO


You can also retrieve references by using a program variable or a constant. This
avoids the program first reading the <from> table. In the following example
tiitm001 has a reference to tccom010 (field ’cuno’), and a reference to tccom011
(field ’suno’) and the value of tiitm001 is known:
Table ttccom010
Table ttccom011

SELECT tccom010.*, tccom011.*


FROM tccom010, tccom011

Programming features
5-19
Database handling

WHERE :tiitm001.cuno = tccom010.cuno AND


:tiitm001.suno = tccom011.suno

In the following example, the REFERS TO clause is used to achieve the same
result. If the program does not find the reference, it fills the field with reference
characters.
Table ttccom010
Table ttccom011

SELECT tccom010.*, tccom011.*


WHERE :tiitm001.cuno REFERS TO tccom010 AND
:tiitm001.suno REFERS TO tccom011

In this case the REFERS TO statement has the following from:


<from> REFERS TO <to> [UNREF <mode>]

The following table explains the various parts of the statement:


<from> The referring program variable or constant. If a combined
constant is used, all fields must be filled.
<to> The table referred to (refers to primary key of the table). This can
also be a field provided that a unique index to this field exists.
<mode> A mode indicating system action if reference does not exist;
possible values are:
SKIP
If a reference cannot be found, the record is skipped.
CLEAR
If a reference is empty or absent, the referring record is filled
with spaces or 0 (numeric).
SETUNREF
The value of an undefined reference is filled with an ’undefined
reference’ sign, defined in the data dictionary, or with 0
(numeric).
CLEARUNREF
The referred record is filled with spaces or 0 (zero) when
reference fields are empty. When the reference is undefined the
referred record is filled with an ’undefined reference’ sign.
As no dictionary default is available, SETUNREF has been
defined as the default action.

Programming features
5-20
Database handling

Example
SELECT tccom010.*
FROM tccom010, tiitm001
WHERE tiitm001.cuno = :tiitm001.cuno AND
tiitm001.cuno REFERS TO tccom010

This is equal to the following:


SELECT tccom010.*
WHERE :tiitm001.cuno REFERS TO tccom010

Company numbers
The company number used during execution of a BAAN 4GL query is the
current company number. The current company number is:
n The default company number (defined in the session "Maintain User Data" )
n A company number specified by the compnr.check() function

The <table._compnr field


The <table>._compnr field enables the use of different company numbers. This
field specifies the actual company number of the table. When this field is
undefined, the current company number is taken as the default. ’Undefined’ is
denoted by the value -1.
When a record is selected with ’select <table>.*’, the field <table>._compnr in
the record contains the company number of the table. When inserting a record in
a table with a specific company number, you must fill <table>._compnr with the
corresponding company number. The function db.insert() takes care of this and
writes the record in the correct (physical) table. No special file pointers are
necessary (for example, by using db.bind()).

Example
The record where the field ttadv100._compnr = 000 belongs
to the (physical) table ttadv100000
The record where the field ttadv100._compnr = 200 belongs
to the (physical) table ttadv100200

Programming features
5-21
Database handling

Using <table>._compnr in the WHERE clause


You can also use <table>._compnr in the WHERE clause of a Baan SQL
statement. In this case it is used as a search condition. For example:
SELECT ttadv100.*
FROM ttadv100
WHERE ttadv100._compnr = 200 AND ttadv100.cpac = ’tt’

This query lists all records from table ttadv100200 where ttadv100.cpac has the
value ’tt’.
You can use <table>._compnr in the following ways:
n <table>._compnr = <number> or <number> = <table>._compnr where
<number> specifies an integer number. The value of <number> is taken as
the company number. It must be a 3-digit number.
n <table>._compnr = <string> where <string> specifies a list of company
numbers, separated by commas [,]. This construction can be used to check a
condition for a range of company numbers. The string can contain only digits
and commas. (So, you specify company numbers 0 and 23 by
<table>._compnr = "000,023"). For example:
SELECT ttadv100.*
WHERE ttadv100._compnr = "200,300" AND <condition>

This query results in a list of the records from the table ttadv100 (with
company numbers 200 and 300) that match <condition>.
n <table>._compnr IN <set specification> where <set specification> is a set of
constants or a subquery.
Sets must not contain duplicate values. See IN operator in “WHERE
<where condition>” section earlier in this chapter.

Combining <table>._compnr conditions


n A list of AND conditions can contain only one condition that includes
<table>._compnr. So, the following construction is possible:
WHERE <table>._compnr = 100 AND <condition>

but the following is not possible:


WHERE <table>._compnr = 100 AND <table>._compnr = 200

Programming features
5-22
Database handling

n In a list of OR conditions, each condition must have a condition on


<table>._compnr, or none of them. So, the following construction is possible:
WHERE (<table>._compnr = 100 OR <table>._compnr = 200)
AND <condition>

but the following is not possible:


WHERE (<table>._compnr = 100 OR <other_field> = <value>)
AND <condition>

SQL and combined fields


Defining a query that can be handled efficiently by the query handler is a
complex task. This is especially true if there are combined fields (which consist
of a number of child fields), as each field must be specified separately.

Specifying a combined field


As the designer can usually judge best which index should be used for an
optimum result, the following construction enables the designer to specify a
combined field:
WHERE ppmod001.comb1 = {"adv", "099", "123"}

A child field can be an expression, a BAAN 4GL variable, or a pseudo variable.


A comparison operator (=, >, >=, etc.) for a combined field applies to the
combination of all child fields. For example:
| suppose the combined field ppmod001.comb1 consists of the
| fields ppmod001.modu, ppmod001.tblno and ppmod001.compno:
WHERE ppmod001.comb1 >= {"adv", "000", "100"}
| The above statement is equal to the following statement
WHERE ( ppmod001.modu > "adv" ) OR
( ppmod001.modu = "adv" AND ppmod001.tblno >
"000" ) OR
( ppmod001.modu = "adv" AND ppmod001.tblno =
"000" AND ppmod001.compno >= "100" )

Programming features
5-23
Database handling

Comparison operators for combined fields


The comparison operators #>, #>=, #<, #<= for a combined field apply to each
child field separately. For example:
WHERE ( ppmod001.comb1 #>= {"adv", "000", "100"} AND
ppmod001.comb1 #<= {"zzz", "999", "200"} )

This represents the following:


WHERE ppmod001.modu >= "adv" AND
ppmod001.modu <= "zzz" AND
ppmod001.tblno >= "000" AND
ppmod001.tblno <= "999" AND
ppmod001.compno >= "100" AND
ppmod001.compno <= "200"

If a child field of a combined field is not specified, the value of this field is free
and is not included in the condition. For example:
WHERE ppmod001.comb1 #>= {"adv", "000", "100"} AND
ppmod001.comb1 #<= {"zzz", "999"}

This represents the following:


WHERE ppmod001.modu >= "adv" AND
ppmod001.modu <= "zzz" AND
ppmod001.tblno >= "000" AND
ppmod001.tblno <= "999" AND
ppmod001.compno >= "100"

As the field ppmod001.compno has no upper limit, all values greater than or
equal to 100 are fetched.

Indexes as combined fields


You can specify an index as a combined field even if a combined field is not
present in the data dictionary. The index name is table._indexY where:
table is the name of the table or alias of a table
_index is a prefix to indicate that an index field is involved (a condition for
this syntax is that there are no field names having this format)
Y is the sequence number of the index defined in the data dictionary
As a combined field is being used, the value must always be enclosed by ’{’ and ’}’.
For example:
WHERE tiitm001._index1 = { :item }

Programming features
5-24
Database handling

As with other combined fields, children of index fields for which no value is
specified are not included in the condition. However, you can leave fields
unspecified only at the end of the index, not in the middle.
An index’s pseudo field cannot be used in the query preceded by ’:’.

Meanings of upper and lower limits


In connection with combined fields, the combination of upper/lower limit can
have two meanings (compare ’>’ and ’#>’).
n Firstly, it can mean that each field of a record that meets the conditions lies
between the boundaries specified. For example:
WHERE ( ppmod001.comb1 #>= {"adv", "000", "100"} AND
ppmod001.comb1 #<= {"zzz", "999", "200"} )

Here, {"adv", "050" "123"} meet the conditions, but the combination {"uvw",
"123", "300"} does not, because of the last child field.
For this construction we define the INRANGE statement. With INRANGE,
the preceding example becomes:
WHERE ppmod001.comb1
INRANGE {"adv", "000", "100"} AND
{"zzz", "999", "200"}

With INRANGE, the boundaries indicated apply to each separate field (usual
in print sessions).
n Secondly, it can mean that all records are selected for which the combined
field (regarded as one single field) lies between the boundaries indicated. For
example:
WHERE ( ppmod001.comb1 >= {"adv", "000", "100"} AND
ppmod001.comb1 <= {"zzz", "999", "200"} )

Here, {"uvw", "123", "300"} meets the condition as "uvw"&"123"&"300"


lies between "adv"&"000"&"100" and "zzz"&"999"&"200" (’&’ means
concatenation).
For this purpose we use a BETWEEN statement. With BETWEEN, the
preceding example becomes:
WHERE ppmod001.comb1
BETWEEN {"adv", "000", "100"} AND
{"zzz", "999", "200"}

With BETWEEN, the fields in the combined are regarded as one field; the
boundaries apply to the combined field as a whole.

Programming features
5-25
Database handling

If the field consists of one single element, INRANGE and BETWEEN are
equivalent.

SQL subqueries
Baan SQL permits the use of subqueries. These are SELECT statements in the
WHERE clause of another SELECT statement.
Defining nested queries can be very difficult. It is best to define the subquery of
the lowest level first and the main question last.

Example 1
Select those prices from the item file that are above average. When calculating
the average, the system should not take prices less than or equal to zero into
account.
SELECT tiitm001.copr | cost price
FROM tiitm001
WHERE tiitm001.copr >
( SELECT avg(tiitm001.copr)
WHERE tiitm001.copr > 0 )
ORDER BY tiitm001.copr

In this case, the subquery should only produce one result (here the average cost
price). If the subquery produces more than one result, use the operators IN and
EXISTS.

Example 2
Select the numbers and names of all suppliers who have yet to deliver.
SELECT tccom020.suno, tccom020.nama
FROM tccom020 | Suppliers
WHERE EXISTS
( SELECT *
FROM timps053 | Purchase orders
WHERE timps053.suno = tccom020.suno )
SELECT tccom020.suno, tccom020.nama
FROM tccom020
WHERE tccom020.suno IN
( SELECT timps053.suno
FROM timps053
GROUP BY timps053.suno )

Programming features
5-26
Database handling

SQL programming
There are two ways to use Baan SQL in a Baan 4GL program. You can embed it
in the language (embedded SQL), or you can use Baan 4GL functions (dynamic
SQL).

Embedded SQL

Syntax
The following loop structure enables the use of SQL as part of the language:
SELECT < set definition (= actual query, see "SQL Syntax"
earlier in this chapter) >
[ SELECTBIND ( number, var) ] ...
[ WHEREBIND (number, expression) ] ...
[ SELECTDO
< operation(s) on all selected records > ]
[ SELECTEOS
< operation(s) after last selected record > ]
[ SELECTEMPTY
< operation(s) if nothing has been selected > ]
[ SELECTERROR
< operation(s) in case of an error condition > ]
ENDSELECT

Description
Data is selected on the basis of the condition specified in the SELECT statement.
The operations between SELECTDO and ENDSELECT are performed on each
record from the selected set. The loop ends either when the program has handled
the entire set or the program encounters a BREAK command.
In the case of an error, the program executes the SELECTERROR part. A
CONTINUE statement in the SELECTERROR part causes the next record to be
processed. If there is no SELECTERROR section, the program generates a
BREAK as the default action. Note that errors are returned only if the shell
variable ERROR.BYPASS = 1.
The SELECTEOS part is executed after the SELECTDO of the last selected
record. The intention of the SELECTEOS part is that before the program
encounters a ’break’ from the loop you can give a commit.transaction(), while a
commit.transaction() may cause a retry. If a commit.transaction(), given after
the ENDSELECT part, causes a retry, then the whole query must be regenerated.

Programming features
5-27
Database handling

If the SELECTDO part is suppressed, the program encounters a ’break’ from the
loop after the first selected record. If the SELECTDO part is suppressed but the
SELECTEOS part occurs, the program will select the whole set of records.
In order to bind pseudo variables, the functions SELECTBIND and
WHEREBIND have been added. Pseudo variables have the form ’:<number>’. A
special bind function is then used to link a program variable to the pseudo
variable. For example:
SELECT ppmod123.field1:5, ppmod123.field2:6
SELECTBIND(5, my_val1)
SELECTBIND(6, my_val2)
SELECTDO
....
ENDSELECT

This is equal to ’SELECT ppmod123.field1:my_val1’, but using a bind function.


It is also possible to use a pseudo variable in the WHERE clause. The
WHEREBIND function is then used to link a value to the pseudo variable. For
example:
SELECT ....
WHERE ppmod123.field1 = :1
WHEREBIND(1, 10 + sqrt(a+b))
SELECTDO
....
ENDSELECT

Used BAAN 4GL variables (with ’:’) are ’bound’ automatically.

Example
table tppmod090
long total, m_val
m_val = 25
SELECT ppmod090.fld1, count(*):1
FROM ppmod090, ppmod091 | this line is optional
WHERE ppmod090.fld2 = ppmod091.fld5 AND
ppmod091.fld3 > :m_val
GROUP BY ppmod090.fld1
ORDER BY 2
SELECTBIND(1,total)
SELECTDO
print_info(ppmod090.fld1, total)
SELECTERROR
message("Error %d occurred", db.error)
break
ENDSELECT

Programming features
5-28
Database handling

See “Dynamic SQL” below for an example of the same query executed using
dynamic SQL.

Dynamic SQL
Dynamic SQL enables a program to form an SQL statement during execution, so
that the contents of the statement can be determined, for example, by user input.

Functions
To use Dynamic SQL, the following functions are available:
Functions Description
sql.parse() Query definition.
sql.set.rds.full() This sets the size of the RDBMS buffer.
sql.select.bind() These functions link program variables to the
sql.where.bind() query's pseudo variable. In Dynamic SQL, all
non-external program variables used in the
query must be linked to a pseudo variable with
these functions.
sql.exec() This function initializes the query. It gives the
variables their proper values.
sql.fetch() This function executes the query. It reads one
result on which operations can be performed.
This function must be invoked for each separate
record from the selected set.
sql.break() Stops execution of the query. Any interim
results are cleared.
sql.close() Deletes (all) internal information belonging to
this query.

See the Dynamic SQL functions, in U7167A US BaanERP Tools: Functions


reference, for more information and a description of the syntax of these
functions.

Programming features
5-29
Database handling

Sequence of actions
By using these functions a query, once defined, can be reused optimally. The
sequence of the actions is then:
sql.parse, sql.bind, sql.exec, sql.fetch, sql.fetch, ...,
sql.break
sql.bind, sql.exec, sql.fetch, sql.fetch, ...,
sql.break
sql.close

In this way you can prevent expensive sql.parse() calls, while still working with
new arguments.
This flow is automatically generated from an embedded SQL program by bic6.2.
Usually, the programmer will therefore opt for embedded SQL, the more so,
since it automatically binds 4GL variables.

Example
Table tppmod090
Long sql
Long total, m_val
m_val = 25
if ( not sql ) then
sql = sql.parse(
"select ppmod090.fld1, count(*):1 " &
"from ppmod090, ppmod091 " &
"where ppmod090.fld2 = ppmod091.fld5 AND " &
"ppmod091.fld3 > :2 " &
"group by ppmod090.fld1 " &
"order by 2")
endif
sql.select.bind(sql, 1, total)
sql.where.bind(sql, 2, m_val)
sql.exec(sql)
while ( true )
on case ( sql.fetch(sql) )
case eendfile:
break
case 0:
print_info(ppmod090.fld1, total)
continue
default: | error
message("Error %d occurred", db.error)
endcase
break
endwhile
sql.break(sql)

Programming features
5-30
Database handling

sql.close(sql) | After sql.close, a new sql.parse must be


sql = 0 | executed to give sql a correct value. If
| the last two lines are skipped, sql
| retains a correct value.

See “Embedded SQL” earlier in this chapter for an example of the same query
executed using embedded SQL.

SQL and delayed locks


The purpose of delayed locking is to postpone the physical lock of a record as
long as possible. The program places the physical lock immediately before
updating. During updating, the value of the record at the moment of selection
must be known, as this is compared to the value at the moment of updating.
During comparison, the relationships between fields are taken into account.
For a record to be delayed-locked when selected, the keyword FOR UPDATE
must be included in the SELECT statement. In SQL, 'normal' record locking is
not possible.
When placing a delayed lock on a record, the program fills the field
<table>._dlock with an identification number linked to the record. This field
identifies the original record. This guarantees a fast search procedure. This field
will be overwritten when changing the record buffer.
Updates in combination with SELECT FOR UPDATE are executed with the
following functions:
db.insert(table, DB.RETRY [, eflag])
db.update(table, DB.RETRY [, eflag])
db.delete(table, DB.RETRY [, eflag])

The DB.RETRY flag indicates that we are dealing here with updates with
SELECT FOR UPDATE and retry points. This flag ensures that the actual
update action is postponed until the commit. The db.update() and db.delete()
functions with the DB.RETRY flag can be invoked only in combination with
SELECT FOR UPDATE. The use of db.insert() with the DB.RETRY flag, on
the other hand, is not linked to the use of SELECT FOR UPDATE.

Programming features
5-31
Database handling

SQL trace options


It is possible to monitor whether the SQL compiler and optimizer are taking the
right decisions. If the results indicate that the design is incorrect, the query must
be changed. The optimizer can never emulate the programmer. The Baan
programming language therefore enables the programmer to instruct the
computer what it has to do to a considerable degree of detail. You can define
which index to use; also, a number of special (non-standard SQL) functions have
been built in.
You can use the environment variable TT_SQL_TRACE to trace what happens
inside the SQL processor. TT_SQL_TRACE is used to trace SQL processing in
the client (that is, bshell6.2), as well as in the server process (that is,
bisam_srv6.2). The methods involved are different for client and server.

Client tracing
When TT_SQL_TRACE is set, the output from the client process is written to
stderr. To keep this information, you must call the bshell with the options "-
keeplog" and "-logfile <log file name>". For example:
bx6.1 -- -keeplog -logfile mylogfile -set
TT_SQL_TRACE=02000

This enables TT_SQL_TRACE with value 02000. The output is written to the
file "mylogfile" in the current working directory.

Server tracing
When TT_SQL_TRACE is set, the output is written to a file named "dbs.log".
This file is located in the current working directory when the Database Server
runs locally (on same host as Display Server). When the database is remote, it is
written in the login directory of the corresponding user on that remote system. To
enable logging of TT_SQL_TRACE in the file "dbs.log", you must also set the
environment variable DBSLOG, as follows:
DBSLOG=02000
The "02000" is an octal value, and can be OR-ed with other variables. The
meaning of the other values is described in a separate document (Logging
Database Information (DBSLOG)).

Programming features
5-32
Database handling

Query identification
A QueryIdentifier (QID) identifies each query. This makes it easy to find out
which output in the Client log file is related to the output of the Server log file.

Trace options
The following values for TT_SQL_TRACE are available (C indicates that an
option can be used for the client; S indicates that it can be used for the server):
1. TT_SQL_TRACE=040 (C) Show queries with their QID
2. TT_SQL_TRACE=02000 (C) Show calls of internal SQL
functions
3. TT_SQL_TRACE=0200 (C) Show query execution times
4. TT_SQL_TRACE=04000 (C+S) Show query evaluation plan
5. TT_SQL_TRACE=020000 (S) Show FullTableScan
6. TT_SQL_TRACE=010000 (S) Show Query Evaluation Plan

You can combine trace options by assigning several different flags to


TT_SQL_TRACE at the same time. For example:
TT_SQL_TRACE=010200 export TT_SQL_TRACE
bshell6.2 2>sql.dbg

In sql.dbg, this produces the results of both options 010000 and 0200.

TT_SQL_TRACE=040
Shows all queries (query text) which are evaluated. For example:
Evaluated Query (QID : 4)
select ttadv450.vers, ttadv450.rele, ttadv450.cust,
ttadv450.expi, ttadv450.mess
where ttadv450._index1 = {:clan, :1, :2}

TT_SQL_TRACE=02000
Shows calls of internal SQL functions, including their arguments. For example:
[SqlParse: before parse] SqlId : 200db008 - QID : 4 - Status : 0
Trans 1 Mode 0 Query:
select ttadv450.vers, ttadv450.rele, ttadv450.cust, ttadv450.expi,
ttadv450.mess
where ttadv450._index1 = {:clan, :1, :2}
[SqlParse: after parse (e = 0)]
SqlId : 200db008 - QID : 4 - Status : 2
[SqlWhereBind for clan] SqlId : 200db008 - QID : 4 - Status : 2
Value(str): ’2’
[SqlWhereBind for 1] SqlId : 200db008 - QID : 4 - Status : 2
Value(str): ’tt’
[SqlWhereBind for 2] SqlId : 200db008 - QID : 4 - Status : 2
Value(str): ’gfdmnucal ’

Programming features
5-33
Database handling

[SqlExec (CompNr 000)] SqlId : 200db008 - QID : 4 - Status : 2


[SqlFetch] SqlId : 200db008 - QID : 4 - Status : 3
[SqlFetch returns 0 errno 0 msg ]
SqlId : 200db008 - QID : 4 - Status : 4
’6.1 ’ ’b ’ ’ ’ 2 ’Calendar

[SqlBreak] SqlId : 200db008 - QID : 4 - Status : 4

WARNING: The line following the message "SqlFetch returns .." shows the result of the
fetch. The order of the fields is not always the order as mentioned in the
SELECT clause.

TT_SQL_TRACE=0200
Shows query execution times. For example:
================================================
Fetch times of Query (QID : 12) in SqlBreak :
select ttdsk360.*
where
(ttdsk360.user = :logname$ or ttdsk360.user = :1)
order by ttdsk360.user desc, ttdsk360.cpac desc,
ttdsk360.rsst desc, ttdsk360.rsid desc
------------------------------------------------
Nr Rows Fetched : 39
Fetch Time for 1st Row : 0.060 sec
Max Fetch Time : 0.060 sec
Average Fetch Time : 0.000 sec
Average Fetch Time (except Max) : 0.000 sec
================================================

A value 0.000 means that the time to fetch was too small to be measured. This
can happen when the result is already available in the client. In that case, fetching
is (globally seen) only copying from one memory location to another.

TT_SQL_TRACE=04000
Show the query evaluation tree. There is different output for client and server.
The client shows how the query is distributed over different databases (normally
there is one database). The server shows how the query is evaluated. This can be
different for different databases. The example shows the evaluation in a non-SQL
database driver. In the examples, the same query is used as in the example of
TT_SQL_TRACE=040.
Client Side:
Expression Tree (QID : 4):
[11] flag 011 cost 0 next_eval 12 next 12
cost 0 0 nr 0
Operand1:
Database View Id 0 - Drivers:

Programming features
5-34
Database handling

Database View - Tables:


ttadv450 (dd: ttadv450) flag 1000 cost 0
Database View - Expression:
[10] flag 031 cost 0 next_eval -1 next -1
cost 0 0 nr 0
Operand1: CombColumn: ttadv450._index1
Combined {
(1) ttadv450.clan
(2) ttadv450.cpac
(3) ttadv450.cmes
(4) ttadv450.vers
(5) ttadv450.rele
(6) ttadv450.cust
}
Operator: =
Operand2: Combined {
(1) Host var: ’clan’ = ’2’
(2) Host var: ’1’ = ’tt’
(3) Host var: ’2’ = ’gfdmnucal ’
}
Database View - Host Variables:
Host var: ’clan’ = ’2’
Host var: ’1’ = ’tt’
Host var: ’2’ = ’gfdmnucal ’
Database View - Output Columns:
ttadv450.vers
ttadv450.rele
ttadv450.cust
ttadv450.expi
ttadv450.mess
Database View - RDS Out:
Dump Info on RDS: MalId 15 - Size 143 164 - Flag 2
[ 1] type 6 dept 1 size 4 offset 0
[ 2] type 6 dept 1 size 2 offset 4
[ 3] type 6 dept 1 size 4 offset 6
[ 4] type 1 dept 1 size 1 offset 10
[ 5] type 7 dept 1 size 132 offset 11
Operator: DBView
Operand2: --
AND (011)
[12] flag 011 cost 0 next_eval -1 next -1
[1] End of Query
-End of Tree --

Server:
Generated Query Execution Tree:
Sql id 6 company 000 sid 1 flag 1 (QID : 4)
Output Columns (total 5)
[ 0] vers idx 0 type 6 size 4 depth 1

Programming features
5-35
Database handling

[ 1] rele idx 1 type 6 size 2 depth 1


[ 2] cust idx 2 type 6 size 4 depth 1
[ 3] expi idx 3 type 1 size 1 depth 1
[ 4] mess idx 4 type 7 size 132 depth 1
Rds Out:
Dump Info on RDS: MalId 6 - Size 143 164 - Flag 2
[ 1] type 6 dept 1 size 4 offset 0
[ 2] type 6 dept 1 size 2 offset 4
[ 3] type 6 dept 1 size 4 offset 6
[ 4] type 1 dept 1 size 1 offset 10
[ 5] type 7 dept 1 size 132 offset 11
All tables
ttadv450 flag 12
Query 200a207c flag 00
Tables
ttadv450 (a) dep 1 flag 012
ttadv450(a).cmes
ttadv450(a).cpac
ttadv450(a).clan
ttadv450(a).mess
ttadv450(a).expi
ttadv450(a).cust
ttadv450(a).rele
ttadv450(a).vers
Output Columns (total 5)
ttadv450.vers idx 0 type 6 size 4 depth 1
ttadv450.rele idx 1 type 6 size 2 depth 1
ttadv450.cust idx 2 type 6 size 4 depth 1
ttadv450.expi idx 3 type 1 size 1 depth 1
ttadv450.mess idx 4 type 7 size 132 depth 1
View bind
vers : type 6 size 4 flag 9
rele : type 6 size 2 flag 9
cust : type 6 size 4 flag 9
expi : type 1 size 1 flag 9
mess : type 6 size 132 flag 9
View expression
[15] flag 011 cost 0 next_eval 17 next 17
Table View on ’ttadv450’
Expression:
[14] flag 011 cost 3 next_eval 16 next 16
SrchOper for ttadv450 compnr 000 index 1 (mode 0)
Field clan:
Equal: [=] Bind (Col clan) Value: ’2’
Field cpac:
Equal: [=] Bind (Col cpac) Value: ’tt’
Field cmes:
Equal: [=] Bind (Col cmes) Value: ’gfdmnucal ’
Field vers:
Field rele:

Programming features
5-36
Database handling

Field cust:
AND (011)
[16] flag 011 cost 0 next_eval -1 next -1
cost 0 0 nr 0
Operand1:
Operator: QpsPermOper
Operand2: ’-1’
AND (011)
[17] flag 011 cost 0 next_eval -1 next -1
[1] End of Query

There are a lot of numbers/state values in this tree. Most of them are for internal
use only.

TT_SQL_TRACE=020000
Shows all tables for which FullTableScan is performed. This can depend on
which database is used. For example:
FullTableScan for table ttdsk355 (QID : 2)

The QID refers to the query. This can be found in the client trace file.

TT_SQL_TRACE=010000
Shows Query Evaluation Plan (QEP). This also depends on which database is
used. For example:
Query Evaluation Plan (QID : 30)
------------------------------------------------
Table tccom000 (alias: a): IndexSearch with condition(s):
SrchOper for tccom000 compnr 000 index 1 (mode 0)
Field ncmp:
Equal: [=] Bind (Col ncmp) Value: ’812’
Forced Order: Ascending (index 1)
Table tcmcs046 (alias: b): IndexSearch with condition(s):
SrchOper for tcmcs046 compnr 000 index 1 (mode 6)
Field clan:
Equal: [REF (clear)] Index Col key 1 mode 5 equal 0
tccom000(a).clan
Table tcmcs002 (alias: c): IndexSearch with condition(s):
SrchOper for tcmcs002 compnr 000 index 1 (mode 6)
Field ccur:
Equal: [REF (clear)] Index Col key 1 mode 5 equal 0
tccom000(a).ccur
================================================

Programming features
5-37
Database handling

The query which belongs to this QEP is shown in the client log file
(TT_SQL_TRACE=040) as follows:
Evaluated Query (QID : 30)
Select tccom000.*, tcmcs002.*, tcmcs046.*
From tccom000, tcmcs002, tcmcs046
Where tccom000.ncmp = :company.nr
And tccom000.ccur refers to tcmcs002 unref clear
And tccom000.clan refers to tcmcs046 unref clear
order by tccom000.ncmp

From the QEP, the following information can be retrieved:


1 There are three tables: tccom000 (alias: a), tcmcs046 (alias: b), tcmcs002
(alias: c). The alias name is used in case of SQL databases. This alias name is
only one or two characters in order to keep the SQL statement for the SQL
database as short as possible.
2 The order of evaluation of the tables is from top-to-bottom in the QEP, so
first tccom000, then tcmcs046, then tcmcs002.
3 Searching in tccom000 is done on the primary index (index 1). The field
’ncmp’ has value ’812’ and has been specified by some ’=’ operation. In the
query above, this comes from the line containing
"where tccom000.ncmp = :company.nr".
4 The query has an ORDER BY part. The ORDER BY can be eliminated by
reading tccom000 on index 1, in ascending order. This is denoted by: "Forced
Order: Ascending (index 1)".
5 Next table to be read is tcmcs046. Here also index 1 is used for reading. The
key is specified by some "unref clear" operation. The field ’clan’ is filled to
search in the table. This field specifies the whole primary key (index 1).
6 Last evaluated table is tcmcs002, in the same way as tcmcs046. Here, the
field ’ccur’ is used.

Programming features
5-38
Database handling

Error handling
There are two broad categories of errors. Errors generated by the operating
system (numbers 1 - 99) and errors generated by the database system (numbers
100 - 900). See Chapter 10, Errors.
To keep the messages to be sent over the network to a minimum, database
actions and messages must be buffered as much as possible. Buffering updates
means that as few updates as possible return error codes. These, after all, cause a
rollback and a return to a retry point. It is therefore possible to allocate flags to
updates indicating the action to be taken by the system when a database action
fails. The database server will then no longer execute a rollback, but a follow-up
action.

Fatal and non-fatal errors


There are fatal and non-fatal errors. Fatal errors terminate the session. Non-fatal
errors are those that are temporary (for example, elocked), or that are caused by a
multi-user situation (for example, a record is deleted by two processes, one
process receiving enorec). Non-fatal errors cause the program to go back to the
last retry point. If this is missing, the session is terminated. Fatal errors are errors
caused by:
n Errors in the (logical) database (for example, enotable, eddcorrupt, enoserver)
n Errors in the underlying (R)DBMS (for example, ebadkey)
n Program errors (for example, enotinrange)
The following error codes result in a return to the retry point:
elocked (107) record is locked
eflocked (113) file (table) is locked
emlocked (210) record in mirroring is locked
ereflocked (601) reference record is locked
edupl (100) duplicate record
enorec (111) record not present
erowchanged (201) record modified since last read
eabort (850) rollback in (R)DBMS

Programming features
5-39
Database handling

The eflag
For a number of errors it is possible to indicate which action the system must
carry out when the error occurs. You can add these to a database action with the
<eflag>. For example:
db.insert(table, DB.RETRY [, eflag])
db.update(table, DB.RETRY [, eflag])
db.delete(table, DB.RETRY [, eflag])

The following flags are available for eflag:


db.exit.on.norec stop session if no record was found
db.skip.norec skip update/delete action if no record
was found
db.exit.on.dupl stop session if duplicates occur
db.skip.dupl skip update/insert action if duplicates
occur
db.exit.on.rowchanged stop session if record has been changed
after delayed lock
db.skip.rowchanged skip action if record has been changed
after delayed lock
db.return.error * return error code
db.return.norec * return error code if no record was found
db.return.dupl * return error code if duplicates occur
db.return.ref.exists * return error code if reference exists
db.return.ref.not.exists * return error code if reference does not
exist
db.return.rowchanged * return error code if record has been
changed after delayed lock
* These flags store the contents of the buffer to the database if an error occurs.
The program does not return to the retry point.
Note that you can combine flags by using the ’+’ sign.

Programming features
5-40
Database handling

Example
table ttiitm001
db.retry.point()
SELECT tiitm001.*
FROM tiitm001 FOR UPDATE
WHERE tiitm001.item between :item.f and :item.t
SELECTDO
......
db.insert(ttiitm001, DB.RETRY, db.skip.dupl)
ENDSELECT

In this case, the flag ’db.skip.dupl’ causes the system neither to go back to the
retry point nor return an error code when adding an already existing record.

Hints for using SQL


1 In the WHERE clause:
− use as few conditions as possible:
− use as many conditions with indexes as possible
− use as many conditions with combined fields as possible
− use BETWEEN/INRANGE when an upper and lower boundary are
specified for a field.
2 Use REFERS TO if references have been defined in the data dictionary.
3 Use as few overlapping OR conditions as possible. For the following:
cpac&cmod >= ’ttadv’ (only if there is no index to cpac&cmod)

do not use the following construction:


WHERE ttadv200.cpac > ’tt’ OR
ttadv200.cpac >= ’tt’ AND ttadv200.cmod >= ’adv’

instead use the following construction:


WHERE ttadv200.cpac > ’tt’ OR
ttadv200.cpac = ’tt’ AND ttadv200.cmod >= ’adv’

Use BETWEEN or INRANGE if there is an index to cpac&cmod. For


example:
WHERE ttadv200._index1 BETWEEN {’tt’, ’adv’} and { }

4 Only select fields of tables necessary in the flow of the program.

Programming features
5-41
Database handling

5 It is not necessary to bind external variables and database fields used in the
query as substitution variables. In embedded SQL local variables are bound
automatically.
6 The program executes the query the first time the function sql.fetch() is
called. If reading in the entire set is unnecessary (so no order by, group by,
and so on), the program only physically retrieves records at each sql.fetch().
This avoids an entire set being retrieved when only part of it is used. When
the set is no longer necessary call sql.break() to clear the remaining records.
7 Use ORDER BY to ensure that the records are retrieved in the correct
sequence. If an index can be used for the ORDER BY, no sort action will
take place beforehand.
8 The program will execute a Full Table Scan (FTS) if the operators NOT
INRANGE, IN, LIKE are used or if no index (or part of an index) is used.
This means that the system checks beforehand whether all records in the table
meet the conditions of the query. This precludes optimization. If only the first
part of an index (combined or normal field) is used, the system uses that
index to search the table.
When GROUP BY is used, the system first determines the entire set
(prepared set), before producing the first record. The same thing happens if
the operator is preceded by an expression –for example, WHERE
<table.field1> & <table.field2> = "...........". Note that the various
subexpressions separated by AND or OR cannot be combined for
optimization.
9 It is possible to have the program carry out commits at certain points in the
select loop. If retry points are included, use of ORDER BY is required to
ensure that the sequence after return to the retry point is identical to the one
used the first time. See also the practical examples.
10 If a transaction adds or processes records, it depends on the database if the
same transaction can also process new records without a select being carried
out again. To ensure that new records are processed in any case, a commit
and a new select are required. To ensure that new records are not processed,
define the set at the point of the select (prepared set).
11 When using transactions, the first priority should be that the actions within
the transaction constitute a logical unit. For reasons of performance, update,
insert, and delete actions can be combined up to a number of 256 (this is an
average, not a limit) per transaction. For example, programs processing
orders should execute one commit.transaction() for each order.

Programming features
5-42
Database handling

12 It may be necessary to know how far the program had advanced before it
moved back to the retry point. If the key consists of more than one field, it is
probably possible to have the program execute a commit.transaction() when
a particular key field changes. If a key consists of order number and order
line, for instance, a commit by order number would be preferable to a commit
by order line. Care should be taken in programs that print and process data in
one run. An abort transaction restores the database but not the output to the
printer. In such cases a commit must be executed for each print line to
prevent lines being printed twice.
For example:
save.orno = start
select for update order line.orno order line.pono
from order line
where order line.orno >= :save.orno
selectdo
if ( save.orno <> order line.orno ) then
commit.transaction()
save.orno = order line.orno
endif
update actions()
endselect
commit.transaction()

13 A db.retry.point() must be included in each update program.

Hints for using db.retry.point


For programs of type 1, 2 or 3, this is generally the retry.point of the standard
program which has a retry just after ’before choice’ of update.db and in the read
section where also the references are read.
To have clearly structured sources, never use more than one level
db.retry.point(). Only one above the first level of select. Use retry in the
following situations:
Situation for Select Commit per Commit over
fetch multiple fetch
db.update/db.insert/db.delete 1 2
Collecting 3 3
Combination 4 5
A skip back to a retry point can be indicated by a problem in:
n db.update(), db.delete() or db.insert() with argument db.retry
n commit.transaction()

Programming features
5-43
Database handling

Situation 1: Only update actions, commit per fetch


The function ’with retry’ can be used here. For example:
db.retry.point()
select tisfc001.*
from tisfc001 for update
where tisfc001.pdno inrange :pdno.f and :pdno.t
order by tisfc001._index1 with retry
selectdo
tisfc001.proc = tcyesno.yes
db.update(ttisfc001,db.retry)
commit.transaction()
endselect

In this case REPEAT LAST ROW is not used, because when retry is done, the
value which was present at the stage of commit.transaction() has certainly been
saved.

Situation 2: Only update actions, commits over fetches


In this case we can also use the ’with retry’ option. For example:(commit per
order and not per fetch !):
save.pdno = 0
db.retry.point()
select ticst001.*
from ticst001 for update
where ticst001.pdno inrange :pdno.f and :pdno.t
order by ticst001._index1 with retry repeat last row
selectdo
if ticst001.pdno <> save.pdno then
if save.pdno <> 0 then
commit.transaction()
endif
save.pdno = ticst001.pdno
endif
ticst001.proc = tcyesno.yes
db.update(ticst001,db.retry)
selecteos
commit.transaction()
endselect

Both db.update() and commit.transaction() can result in a retry. So selecteos is


used. Notice that REPEAT LAST ROW is used because the present value at the
time of doing the save is already the value of the next order.

Programming features
5-44
Database handling

Situation 3: only print / collect actions (no commit)


There is no transaction or dependency between records / fetches. For example:
total.quant = 0
select ticst001.*
from ticst001
where ticst001.pdno inrange :pdno.f and :pdno.t
order by ticst001._index1
selectdo
total.quant = total.quant + ticst001.quan
print.row(1)
endselect

Situation 4: Update actions plus print/collect with


commit per fetch
For example, update of print status plus print and collect:
total.quant = 0
db.retry.point()
select ticst001.*
from ticst001 for update
where ticst001.pdno inrange :pdno.f and :pdno.t
order by ticst001._index1 with retry
selectdo
ticst001.proc = tcyesno.yes
db.update(ticst001,db.retry)
commit.transaction()
total.quant = total.quant + ticst001.quan
print.row()
endselect

Notice that this works only when the db.update() and commit.transaction() are
done before the print and the collect statement. An alternative to this solution is
described in situation 5.

Situation 5: Update actions plus print/collect with


commit over fetches
For example, update of print status plus print and collect per order:
save.pdno = 0
save.pono = 0
total.select.quan = 0
db.retry.point()
total.order.quan = 0
select ticst001.*
from ticst001 for update
where ticst001.pdno inrange :pdno.f and :pdno.t

Programming features
5-45
Database handling

order by ticst001._index1 with retry repeat last row


selectdo
if ticst001.pdno <> save.pdno then
if save.pdno <> 0 then
total.order.quan = 0
commit.transaction()
endif
save.pdno = ticst001.pdno
endif
if ticst001.pdno > lsp.pdno or
(ticst001.pdno = lsp.pdno and
ticst001.pono > lsp.pono ) then
print.row()
total.select.quan = total.select.quan + ticst001.quan
lsp.pdno = ticst001.pdno
lsp.pono = ticst001.pono
endif
total.order.quan = total.order.quan + ticst001.quan
ticst001.proc = tcyesno.yes
db.update(ticst001,db.retry)
selecteos
commit.transaction()
endselect

lsp stands for last processed. pdno and pono are together the index of cst001. The
’lsp.’ variables should always be used for this kind of saving within selects. These
variables save which record has already been printed/collected. The
total.order.quan can always be added because setting to zero is done for each
retry again.

Programming features
5-46
6 Data Access Layer (DAL)

Overview
In BAAN applications, the standard program (STP) provides much of the default
functionality for a session. In previous versions of the software, changes or
additions to the default functionality for a session were programmed in a single
script that was associated with the session. Both user interface actions and
database actions were programmed in this script.
In BaanERP, user interface actions and database actions have been separated.
The Data Access Layer (DAL) now handles database interaction. Programmers
create a user interface (UI) script to change the default behavior of a session.
They create a DAL script to program all the logical integrity rules for a particular
table. So the DAL ensures the logical integrity of the database. As in previous
versions of the software, the database server ensures the referential integrity of
the database.
The DAL script for a particular table has the same name as that table. It is
implemented as a DLL that can be accessed by user interface scripts (via the
standard program), by other DALs, and by external programs (via the Common
Data Access Server (CDAS). The following diagram illustrates the overall
relationship of these components.

Programming features
6-1
Data Access Layer (DAL)

Database integrity checks


The following are examples of some logical integrity rules that could be
programmed in a DAL script:
n When customers have reached their credit limit, they cannot order further
items.
n If the invoice for an order has been printed, the order cannot be changed.
n If the current VRC of a user is not equal to the package VRC of the program
script, the script cannot be compiled.
Programming database integrity checks in a separate DAL script has two main
advantages:
n Code reuse: The integrity rules do not have to be replicated in each session
that uses a particular table.
n External access: External applications can access the database via the CDAS
and the DAL.
For an overview of the interaction between the user interface, the standard
program, and the DAL, see “DAL, UI, and STP interaction” later in this chapter.

Business methods
In addition to performing data integrity checks, the DAL provides business
methods for handling non-interactive database modifications such as printing
sales orders or posting all orders to history.
A business method is a function that performs a task that involves manipulating
and/or checking one or more tables in the database. The function is programmed
in a DAL script and can be called directly from a UI script. It must be
programmed in the DAL script of the most relevant table.
Users can activate a business method with a single command. There is then no
further user interaction. The UI script calls the relevant function in the DAL
script. The function performs all operations to complete the required task. The
user must wait until the business method finishes before continuing with other
tasks. However, if a progress window is provided, the user can cancel the
business method by clicking on the Cancel button.
The UI script starts a business method by calling the
dal.start.business.method() function.

Programming features
6-2
Data Access Layer (DAL)

DAL terminology
Class A description of a group of objects with similar properties,
common behavior, common relationships, and common
semantics. A DAL class corresponds to a BAAN table
definition.
Object An instance of a class. It consists of data and the methods
(code) to manipulate the data. The data of a DAL object
corresponds to a record in a BAAN table.
Object set All objects of a class, as stored in the database. A DAL
object set corresponds to a BAAN table.
Property An attribute of an object. A DAL property corresponds to a
field in a record in a BAAN table.
Method The implementation of an operation for a particular class. It
provides a means of retrieving and manipulating data
(properties) in objects. Method is another name for an
operation or function.
Hook A logic integrity rule. Hooks are implemented in the DAL as
functions.

UI, DAL, and STP interaction


Overview
A DAL script contains all the logic integrity rules for a particular object set.
These rules are referred to as hooks and they can be programmed for every
possible manipulation of an object in the object set. For each session with a main
table, the standard program ensures that the integrity rules for the table are
checked each time an update, delete, insert, or read operation is performed on an
object of the table.
If there is no DAL script for the particular object set being accessed, no logic
integrity checks are performed (unless they are programmed in the UI script
itself).
A DAL script can contain hooks that prevent access to the database and hooks
that prevent data being passed back to the user interface. The former are executed
before the database action. The latter are performed after the database action.

Programming features
6-3
Data Access Layer (DAL)

Example
If a user changes the address of a customer on a form, the standard program
changes the address for that customer in the database via the DAL. If you want
certain restrictions to apply to the update action, you can program a property
hook in the DAL of the session’s main table to impose these restrictions. For
example:
when the address changes:
if changed into something valid then
accept
else
reject
endif

UI function calls
The UI script can use either the db.* functions or the DAL Data Access Methods
(DAM) to manipulate the database. The db.* functions are direct database calls.
They do not use the DAL. In this case, any logic integrity checks required must
be programmed in the UI script itself. This means that they cannot be reused by
other sessions. In preference, use the DAL Data Access Methods. These access
the database via the DAL, so all necessary checks are automatically performed.

Function flow
When the DAL is used to manipulate the database, the main steps involved are as
follows:
1 The user issues a command through the user interface to access a record in
the database.
2 The standard program loads the appropriate DAL and calls the hooks in the
DAL to check the integrity rules for the object.
3 If the particular database action is permitted, the standard program issues the
appropriate database call.
4 After the database has been updated, the DAL can perform further checks to
determine whether or not data is passed back to the user interface.
5 The standard program passes data back to the user interface (provided that the
integrity rules permit this).

Programming features
6-4
Data Access Layer (DAL)

The following diagram illustrates this flow:

One-way interaction
Note that because the DAL can be used in situations where there is no user
interface, the DAL cannot call functions in the UI.

DAL hooks
A hook is a function, with a predefined name, that the DAL programmer
programs in a DAL script. The function is used to program logic integrity rules
for database access. The DAL script is compiled into a DLL.
When a user issues a command to access the database, the standard program
loads the DAL DLL for the object being accessed and calls the hooks to perform
the integrity checks. Provided that a DAL script exists for an object set being
accessed, the standard program always ensures that the hooks in the DAL are
called at the appropriate times.
A DAL script can contain two types of hooks:
n Property hooks
n Object hooks

Programming features
6-5
Data Access Layer (DAL)

Property hooks
Property hooks are hooks that relate to a specific property (that is, a table field).
Property hooks replace the check.input event sections for fields of the main
table. If there is a DAL for an object set, the property hooks are called to perform
the necessary field checks. Any check.input sections in the UI script are ignored.
So, if a UI script contains check.input sections for fields of the main table, you
must replace these by property hooks in the DAL. In the UI script, check.input
sections are retained for non-database form fields. Such sections are executed
even if a DAL exists.
Currently there is only one property hook: fieldname.check(). The standard
program calls this hook when inserting or updating an object.

fieldname.check()

Syntax
function extern long ppmmmvss.bbbb.check( long has_changed
[, long element] )

Description
Use this hook to program logical integrity rules for a specified field. The function
name is ppmmmvss.bbbb.check(), where pp is the package code, mmm is the
module code, vss is the table number, and bbbb is the field name.

Arguments
has_changed This indicates whether the value of the field has changed. It is
set by the standard program and can be tested in the hook.
Possible values are:
0 not changed
DAL_NEW change caused by inserting a new object
DAL_UPDATE change caused by updating an existing object
element This is set for array fields only. It indicates the index of the
array element that must be checked.

Return values
This hook returns 0 if the value of the field is accepted. It returns a negative
value (DALHOOKERROR) if the value is not accepted.

Programming features
6-6
Data Access Layer (DAL)

See also
“Property methods” later in this chapter

Example
A property hook programmed in DLL tdsls040:
function extern long tdsls040.oqua.check(long has_changed)
{
if tdsls040.stat = tdsls.stat.invoiced and
has_changed then
dal.set.error.message("tdsls44041")
| Order is already invoiced, cannot change
| quantity
return(DALHOOKERROR)
endif
...
return(0)
}

Object hooks
Object hooks are used for checking the logic integrity of objects (that is, records
in a table).
Object hooks replace the before.read, after.read, before.write, after.write,
before.rewrite, after.rewrite, before.delete, and after.delete subsections in the
main.table.io section of the UI script.
If there is a DAL for an object set, the standard program calls the object hooks in
the DAL every time that a DAM is executed for that object set. The object hooks
perform the necessary checks to ensure the logical integrity of the objects being
accessed. Any of the above mentioned sections in the UI script are ignored. So, if
a UI script contains any of these sections, you must replace these by object hooks
in the DAL.

Available object hooks


Open object set hook
before.open.object.set()
Get object hooks
before.get.object()
after.get.object()
Destroy object hooks

Programming features
6-7
Data Access Layer (DAL)

before.destroy.object()
after.destroy.object
Save object hooks
before.save.object()
after.save.object()
After commit transaction hook
after.commit.transaction()
Method is allowed hook
method.is.allowed()

Return values and errors


Only the before hooks can prevent a database action. The after.get.object() hook
can prevent a record from being sent to the UI. Both these types of hook block
continuation of the calling method if the return value is set to
DALHOOKERROR (a negative constant). If the return value is zero, the method
continues.
The following table indicates how return values and error messages are handled.
The STP column indicates how the standard program reacts if a hook returns a
DALHOOKERROR. The Messages column indicates what happens to messages
when an error occurs. The Warnings column indicates what happens to messages
when no error is returned. The DAM column indicates how Data Access
Methods react when an error occurs.
Hook STP Messages Warnings DAM
before.open.object.set() exit display display return
before.get.object() ignore ignore ignore return
after.get.object() skip ignore ignore return
before.destroy.object() return display display return
after.destroy.object() ignore ignore ignore return
before.save.object() return display display return
after.save.object() ignore ignore ignore return

Programming features
6-8
Data Access Layer (DAL)

before.open.object.set()

Syntax
function extern long before.open.object.set()

Description
Use this to program checks that determine whether opening of the object set is
permitted.
If this hook exists, it is executed after the before.program event section for the
main table. It is also called for every first access to a DAL by business methods
or Data Access Methods.
You can also use this section to program query extensions.

Return values
The hook returns 0 if opening of the object set is permitted. If the hook returns a
negative value (DALHOOKERROR), the session is aborted (if the hook is called
by the STP) or the method returns an error (if the hook is called by a DAM or a
business method).

before.get.object()

Syntax
function extern long before.get.object( long dir )

Description
Use this to program checks that determine whether reading a record is permitted.
The dir argument is set by the STP or CDAS and specifies the object being read.
The possible values are:
DAL_GET_FIRST DAL_GET_NEXT DAL_GET_PREV
DAL_GET_LAST DAL_GET_FIND DAL_GET_CURR
This object hook replaces the before.read subsection of the main.table.io event
section in a UI script. If there is a DAL for an object set, this hook is called to
perform the necessary checks. Any before.read sections in the UI script are
ignored. So, if a UI script contains before.read sections for the main table, you
must replace these by before.get.object() hooks in the DAL.

Programming features
6-9
Data Access Layer (DAL)

Return value
The hook returns 0 if reading of the record is permitted. Negative return values
(DALHOOKERROR) are ignored by the calling program.

Example
function extern long before.get.object( long dir )
{
return(0)
}

after.get.object()

Syntax
function extern long after.get.object( long dir )

Description
Use this to program checks that determine whether reading a record is permitted.
The checks are performed after the record has been retrieved from the database.
The dir argument is set by the STP or CDAS and specifies the object being read.
The possible values are:
DAL_GET_FIRST DAL_GET_NEXT DAL_GET_PREV
DAL_GET_LAST DAL_GET_FIND DAL_GET_CURR
This object hook replaces the after.read subsection of the main.table.io event
section in a UI script. If there is a DAL for an object set, this hook is called to
perform the necessary checks. Any after.read sections in the UI script are
ignored. So, if a UI script contains after.read sections for the main table, you
must replace these by after.get.object() hooks in the DAL.

Notes
It is preferable to use a query extension in the before.program section of the UI
script instead of this hook. So, only use this hook if a query extension is not
possible.
The standard program ignores messages set in this hook.

Return value
The hook returns 0 if reading of the record is permitted. If the hook returns a
negative value (DALHOOKERROR), the object is skipped.

Programming features
6-10
Data Access Layer (DAL)

Example
| after.get.object hook

function extern long after.get.object(long dir)


{
on case dir
case DAL_GET_FIRST:

break
case DAL_GET_NEXT:

break
case DAL_GET_PREV:

break
case DAL_GET_LAST:

break
case DAL_GET_FIND:

break
case DAL_GET_CURR:

break
default:

break
endcase
if ttadv100.cpac = "tt"
return(DALHOOKERROR)
| Error messages from after.get.object are
| ignored by the standard program
endif
return(0)
}
| Query extension with the same result
| Use this in preference
before.program:
query.extend.where("ttadv100.cpac <> ""tt"" ")

Programming features
6-11
Data Access Layer (DAL)

before.destroy.object()

Syntax
function extern long before.destroy.object()

Description
Use this to program checks that determine whether deleting a record is permitted.
This object hook replaces the before.delete subsection of the main.table.io
event section in a UI script. If there is a DAL for an object set, this hook is called
to perform the necessary checks. Any before.delete sections in the UI script are
ignored. So, if a UI script contains before.delete sections for the main table, you
must replace these by before.destroy.object() hooks in the DAL.

Return value
The hook returns 0 if deleting the record is permitted. If the hook returns a
negative value (DALHOOKERROR), the transaction is aborted.

Example
function extern long before.destroy.object()
{
if tdsls040.stat = tdsls.stat.invoiced then
dal.set.error.message("tdsls44041")
| You cannot delete an invoiced transaction
return(DALHOOKERROR)
endif
...
return(0)
}

after.destroy.object()

Syntax
function extern long after.destroy.object()

Description
Use this to update referenced tables after the current object has been deleted.
This object hook replaces the after.delete subsection of the main.table.io event
section in a UI script. If there is a DAL for an object set, this hook is called to
perform the necessary actions. Any after.delete sections in the UI script are

Programming features
6-12
Data Access Layer (DAL)

ignored. So, if a UI script contains after.delete sections for the main table, you
must replace these by after.destroy.object() hooks in the DAL.

Return value
The hook returns 0 if successful. Negative return values (DALHOOKERROR)
are ignored by the calling program.

Example
function extern long after.destroy.object
{
return(0)
}

before.save.object()

Syntax
function extern long before.save.object( mode )

Description
Use this to program checks that determine whether saving a record is permitted.
The mode argument is set by the standard program. The possible values are:
DAL_NEW indicates a new record
DAL_UPDATE indicates a record being updated
This object hook replaces the before.write and before.rewrite subsections of the
main.table.io event section in a UI script. If there is a DAL for an object set, this
hook is called to perform the necessary checks. Any before.write and
before.rewrite sections in the UI script are ignored. So, if a UI script contains
before.write and before.rewrite sections for the main table, you must replace
these by before.save.object() hooks in the DAL.

Return value
The hook returns 0 if saving the record is permitted. If the hook returns a
negative value (DALHOOKERROR), the transaction should be canceled.
If the hook was called directly by the standard program, the transaction is
canceled automatically. If the hook was not called by the standard program, the
transaction must be canceled by calling abort.transaction() or abort.io(),
typically in the UI script.

Programming features
6-13
Data Access Layer (DAL)

Example
function extern long before.save.object(long mode)
{
if mode = DAL_NEW then
| this is code from before.write subsection
else
| this is code from before.rewrite subsection
endif
return(0)
}

after.save.object()

Syntax
function extern long after.save.object( mode )

Description
Use this to update referenced tables after a record has been added or updated.
The mode argument is set by the standard program. The possible values are:
DAL_NEW indicates a new record
DAL_UPDATE indicates a record being updated
This object hook replaces the after.write and after.rewrite subsections of the
main.table.io event section in a UI script. If there is a DAL for an object set, this
hook is called to perform the necessary actions. Any after.write and
before.rewrite sections in the UI script are ignored. So, if a UI script contains
after.write and after.rewrite sections for the main table, you must replace these
by after.save.object() hooks in the DAL.

Return values
The hook returns 0 if successful. Negative return values (DALHOOKERROR)
are ignored by the calling program.

Example
function extern long after.save.object( long mode )
{
if mode = DAL_NEW then
| this is code from after.write subsection
else
| this is code from after.rewrite subsection
endif
}

Programming features
6-14
Data Access Layer (DAL)

after.commit.transaction()

Syntax
function extern void after.commit.transaction()

Description
Use this to update other database tables after an update of the current table has
been committed to the database.
This object hook replaces the after.update.db.commit event section in a UI
script. If there is a DAL for an object set, this hook is called to perform the
required actions. Any after.update.db.commit sections in the UI script are
ignored. So, if a UI script contains a after.update.db.commit section for the
main table, you must replace it by an after.commit.transaction() hooks in the
DAL.
This hook is called only by the STP and CDAS. It is not called by Data Access
Methods or commit.transaction().

method.is.allowed()

Syntax
function extern long method.is.allowed( long method )

Description
This is a special hook for handling centralized authorizations. Use it to perform
checks that determine whether certain Data Access Methods are permitted for an
object. The method argument specifies the method to check.
The hook can be called from the UI script, but it is also called from the standard
program to check if certain operations are allowed.

Return values
TRUE the method is permitted
FALSE the method is not permitted

Programming features
6-15
Data Access Layer (DAL)

Example
function extern long method.is.allowed( long meth )
{
on case meth
case DAL_NEW:
if .... then
dal.set.error.message("error message")
return(false)
endif
break
case DAL_UPDATE:
...
break
case DAL_DESTROY:
...
break
endcase
return(true)
}

Data Access Methods (DAM)


You use Data Access Methods (DAM) to update the database via the DAL. In a
UI script, you can use the methods to update any object set. In a DAL script, you
can use the methods to update objects in an object set other than the one with
which the script itself is associated.
There are three Data Access Methods:
n dal.update()
n dal.new()
n dal.destroy()
These functions encapsulate the db.update(), db.insert() and db.delete()
functions respectively, together with the DAL hooks of the object set.

Programming features
6-16
Data Access Layer (DAL)

Property methods
Setting properties
In order to notify the DAL about a change in the value of a property, you must
use the dal.set.property() function to change the value. Using this function
ensures that the relevant property hooks are executed when the value is changed.
If you use an assignment or the db.* functions to change a property value, the
DAL is not notified of the change. Consequently, the property checks in the DAL
are not executed.
You can call this function from both UI and DAL scripts.

Retrieving properties
You can retrieve the value of the has_changed flag (see “Property hooks” earlier
in this chapter) for any property by calling dal.get.property.flag(). The property
must have been changed by the dal.set.property() function.
You can call this function from both UI and DAL scripts.

Query extensions
Query extensions define conditions that the standard program adds to the
SELECT, FROM, and/or WHERE clauses of a database query in order to
minimize the number of fields read from the main table and in order to retrieve
all reference table fields required by the UI and/or DAL scripts.
BaanERP provides the following functions for constructing query extensions:
n query.extend.select()
n query.extend.select.in.zoom()
n query.extend.from()
n query.extend.from.in.zoom()
n query.extend.where()
n query.extend.where.in.zoom()
You can program query extensions in the UI script and/or the DAL script. In the
UI script, you program the extensions in the before.program or before.zoom
sections. In the DAL script, you program the extensions in the
before.open.object.set() hook.
For a full discussion of query extensions, see “SQL Query extensions” in
U7167A US BaanERP Tools: Functions reference.

Programming features
6-17
Data Access Layer (DAL)

Communication with STP and CDAS


BaanERP provides the following functions to enable the standard program and
external applications to retrieve and display DAL error messages:
n dal.count.error.messages()
n dal.get.error.message()
n dal.get.first.error.message()
n dal.reset.error.messages()
n dal.set.error.message()
You use dal.set.error.message() to specify a message to be displayed when the
DAL returns an error (that is, a negative value). This function is called in the
DAL script and not in the UI script.
UI scripts can call dal.get.first.error.message() to retrieve DAL messages.
External applications use dal.get.error.message() to retrieve DAL messages.
The following example illustrates the use of these functions in a UI script:
db.retry.point()

nr.of.errors = dal.count.error.messages()
if db.retry.hit() then
| Remove the error messages since last commit
dal.reset.error.messages( nr.of.errors )
endif


commit.transaction()
nr.of.errors = dal.count.error.messages()

commit.transaction()
while dal.get.error.message( message ) >= 0
report.message( message )

Transition issues (BAAN IV to BaanERP)


4GL event sections
This table lists the 4GL event sections that have been moved to the DAL in
BaanERP. It also lists the DAL methods that replace these sections.
If a DAL script exists for a particular table, the standard program calls the
methods in the DAL to perform the relevant integrity checks. Any corresponding
4GL event sections in the UI script are ignored. If there is no DAL for the table,
the 4GL event sections are executed.

Programming features
6-18
Data Access Layer (DAL)

4GL event sections DAL methods


field.<x>: function extern long <x>.check()
check.input:
main.table.io: function extern long before.get.object()
before.read:
main.table.io: function extern long after.get.object()
after.read:
main.table.io: function extern long before.save.object()
before.write:
main.table.io: function extern long after.save.object()
after.write:
main.table.io: function extern long before.save.object()
before.rewrite:
main.table.io: function extern long after.save.object()
after.rewrite:
main.table.io: function extern long before.destroy.object()
before.delete:
main.table.io: function extern long after.destroy.object()
after.delete:

4GL functions
This table lists those 4GL UI functions that have DAL equivalents. If a DAL
exists for a particular table, it is preferable to use the DAL functions instead of
the 4GL UI functions.

4GL function DAL function


on.main.table() with.object.set.do()
on.old.occ() with.old.object.values.do()
set.input.error() dal.set.error.message()
return(DALHOOKERROR)
skip.io() dal.set.error.message()
return(DALHOOKERROR)

Programming features
6-19
Data Access Layer (DAL)

abort.io() dal.set.error.message()
return(DALHOOKERROR)
db.update() dal.update()
db.delete() dal.destroy()
db.insert() dal.new()

If a function is used only in the DAL, you should move it to the DAL. If a
function is used in both the DAL and the UI, you can move it to the DAL (the UI
can call functions in the DAL but not vice versa). Alternately, you can store the
function in a separate DLL that is linked to both the UI and the DAL.

Session codes
The DAL does not recognize session codes (prog.name$) and so cannot evaluate
them. However, in the case where integrity checks must be session dependent, a
solution is to evaluate properties instead of session codes. For example, you
could identify a field or a combination of fields whose value(s) uniquely identify
the session. Or you could add a property to the object set and give this property a
value that is unique to the session.

Predefined variables
The predefined variable previous.choice is not supported in the DAL, as the
DAL does not recognize standard commands. Instead, the first argument in the
before.save.object() and after.save.object() hooks indicates if an insert or
update operation caused the save operation. Note that for the DAL, the copy
command is an insert operation.
The predefined variable before.update.check is not supported in the DAL. In
BaanERP, field checks are executed both after field input and before the database
operation. The programmer cannot influence when these checks are performed.
Note that in the CDAS, checking is programmed in the dal.set.property() hook
and not in the dal.after.save.object() hook.

Questions
It is not possible to ask for user input during the execution of DAL hooks. If
hooks contain conditions that depend on user input, set the condition before the
hook is called. Alternately, handle the user input and check the conditions after
completion of the database operations.

Programming features
6-20
Data Access Layer (DAL)

Messages
The DAL cannot display error messages directly. However, you can set an error
message in the DAL with the dal.set.error.message() method. The DAL can
notify the calling process of an error by returning the value DALHOOKERROR.
The process can then display the error message. Messages can be stacked before
they are retrieved by the UI.

Non-table fields
Only table fields are checked in the DAL. Checks for non-database fields must
still be programmed in the UI script.

Include files
When writing a Data Access Layer, you must include bic_dal. To use progress
indicators and Data Access Methods in your UI, you must include bic_dam.

Programming features
6-21
Data Access Layer (DAL)

Programming features
6-22
7 Multitasking and the GUI

Overview
The BAAN bshell provides the execution environment for BAAN applications.
Whenever a user logs on to the BAAN system, a separate bshell process is
activated for that user. So, when multiple users log on to the same BAAN
system, or when the same user logs on multiple times, multiple bshell processes
are activated on the execution platform.
The BAAN bshell provides a multitasking execution environment for BAAN
applications. Each bshell can execute and schedule multiple parallel processes.
So, it blocks individual processes only while they are waiting for user input, and
not while other processes are waiting for user input.
This section provides an overview of how processes and user interface
interaction are managed by the bshell.
The following diagram provides a schematic overview of the relationships
between the scheduler, process queues, and the display server.

Programming features
7-1
Multitasking and the GUI

NOTE The bshell is a single-threaded process. The complete bshell blocks when a
database action is performed by the database driver.

Bshell scheduler
The bshell scheduler is responsible for managing and scheduling processes. It
maintains four process queues, as follows:
n running queue
n blocking queue
n sleeping queue
n terminating queue
The scheduler keeps all processes in one or other of the above process queues,
depending on their current state.

Running queue
The scheduler schedules processor time only for those processes in the running
queue. It schedules each process in the running queue in turn, in order of priority.
Each scheduled process receives a number of ticks, and the execution of each
instruction costs the process a certain number of ticks. When the process has
used up all its ticks, the bshell schedules another process.
The scheduler always schedules the process with the highest priority.
When a process is started, it gets a default priority (this depends on its nice
value). While it is in the running queue, the process’s priority is incremented each
time that another process is scheduled. In this way, the process’s priority
increases until it has the highest priority in the running queue. It is then
scheduled. When a process has been scheduled, its priority returns to its default
value.

Blocking queue
When a process is waiting for input, the scheduler moves it from the running
queue to the blocking queue. So, the running queue contains only processes that
can be run. When a blocked process receives an event, it is moved back from the
blocking queue to the running queue.

Programming features
7-2
Multitasking and the GUI

Sleeping queue
Processes in the sleeping queue are suspended until some external action wakes
them again. Processes can be put in a sleeping state (and so in the sleeping
queue) only by the functions sleep(), suspend(), receive.bucket$(), and related
functions.

Terminating queue
When a process ends, it is stripped of almost all allocated data and moved to the
terminating queue. When the scheduler has scheduled another process to run, it
then ’cleans up’ the terminating queue. That is, it removes all processes except
zombie processes. A zombie process is a process that has ended but whose parent
process has not yet caught its exit signal.

Context switches
Each running process receives a number of ticks when it is scheduled. This is the
equivalent of a UNIX time slice. The execution of each instruction costs the
process a number of ticks. When the process has used up all its ticks, the bshell
schedules another process. The switch to another process is referred to as a
context switch.

Forcing a context switch


Normally, the bshell executes context switches automatically. Functions such as
bms.send(), appl.set(), and start.session() also cause a context switch. And
programmers can force a context switch by calling suspend(0).This is useful
when the immediate response of another process is required.

Allocating bonus ticks


The database locking mechanism prevents two or more processes from updating
or deleting the same record or table simultaneously. When a process is modifying
the database, the relevant record or table is locked. Other processes cannot
modify that record or table until the lock is released.
Some database management systems use a page locking mechanism instead of a
record locking mechanism. That is, they lock a predefined block size that can
include several records. This can result in a deadlock when two processes within
the same bshell attempt to lock records that are positioned very close to each
other in the database. To prevent this happening, you can boost the number of
ticks assigned to a process by using the bonus tick mechanism.

Programming features
7-3
Multitasking and the GUI

When this mechanism is enabled, the bshell assigns bonus ticks to a process
when it locks a database record (note that a lock is applied when the first update,
insert, or delete is performed by a database transaction) . This means that the
process is not scheduled out before the lock is released (the lock is released when
the transaction is committed or aborted).
You can configure the bonus tick mechanism by means of environment variables
or user resources.
User resource Environment variable Value
bonus_enable BSE_BONUS_ENABLE 0 bonus ticks off
(default)
1 bonus ticks on
bonus_timeslice BSE_BONUS_ The number of bonus ticks to
TIMESLICE assign to a process. The default
is 1073741824.
bonus_timeout_ BSE_BONUS_ 0 Warning mechanism off.
warning TIMEOUT_WARNING
1 Warning mechanism on. A
warning message is
displayed when a process
is about to loose its time
slice. The message is also
displayed when a context
switch is forced.
2 This is similar to 1, except
that when the object is in
debug mode, the process
stops and the debugger
points to the next
instruction.
3 This is similar to 2, except
that the process is aborted.

Programming features
7-4
Multitasking and the GUI

Events
BaanERP programs are designed to respond to events. Usually, they consist of a
main loop that waits for events to occur. When an event occurs, the program
performs the appropriate actions to process the event. It then returns to its
waiting state.

Event types
There are four broad categories of events, as listed below. For a full list of
event types, see Chapter 17, Events, in U7167A US BaanERP Tools:
Functions reference.
n User action events.
Most of the events that occur in a BAAN application relate to user
interaction. When a user performs an action in the user interface (for
example, clicking the mouse or pressing a keyboard key), this generates an
event.
n Timer events.
A process can start a timer that sends an event to the calling process at
specified intervals. See Chapter 46, Timers, in U7167A US BaanERP Tools:
Functions reference.
n Client events.
A process can send client events to another process by using the send.event()
function. The sending and receiving processes can determine their own
protocol.
n Bucket message events.
A process can broadcast bucket message events to other processes by using
the bms.send() function.
Note that functions such as keyin$() and data.input() are based internally on
events.

Event flow
When a UI object generates an event, the display server sends that event to the
bshell. The bshell distributes received events to the event queues of the
appropriate process groups; note that events are sent to process groups and not
individual processes. The bshell never requests events; the display server always
acts independently.

Programming features
7-5
Multitasking and the GUI

When one of the processes in the bshell requires input, the bshell checks whether
any event is present in the event queue of the process group to which the process
belongs. If there are no events in the event queue, it checks the connection
between the bshell and the display server. If it finds events there, it distributes
them to the appropriate process groups.

Event masks
A process can set the event mask of a UI object in order to specify the types of
events in which it is interested; the object then generates only events of these
types (see the select.event.input() function). Normally, event masks are
automatically set for keyboard and mouse events. However, programmers should
not assume any default settings for an event mask. Note that the following event
types are always selected and cannot be masked: client events, timer events,
bucket message events.

Event functions
BAAN Tools provides the following functions for handling events:
n next.event( event )
n peek.event( event )
n pending.events( )
n send.event( event )
n select.event.input( object_id, mask )

Event arrays
The event argument included in the next.event(), peek.event(), and send.event()
functions consists of an array of longs (of size EVTMAXSIZE) that contains
details of the incoming or outgoing event. You retrieve the contents of an event
array by using the set of parameters defined for the particular event type. See
“Event array parameters” in Chapter 17 in U7167A US BaanERP Tools:
Functions refence.

Programming features
7-6
Multitasking and the GUI

Sample code
The following example illustrates the basic principle of event handling that is
used by almost every event driven program.
#include <bic_event>

function main_event_loop()
{
long event( EVTMAXSIZE )

while next.event( event )


on case evt.type( event )
case EVTBUTTONPRESS:
case EVTBUTTONRELEASE:
x = evt.button.x( event )
y = evt.button.y( event )
button = evt.button.button( event )
if button = EVTBUTTON1 then
print "left button pressed"
refresh()
endif
break
default:
print "not a button press/release"
refresh()
return
endcase
endwhile
}

Requests, inquiries, and replies


A request is a message sent by the bshell to the display server requesting some
action – for example, the creation of a window or some other UI object. It
consists of a package of information that tells the display server what action to
perform and the parameters to use for that action. Requests are asynchronous, so
the bshell does not wait for a reply from the display server. Consequently, if an
error occurs, the bshell does not detect it. The display server, however, may
generate an error message on screen and/or in a log file.
An inquiry is a request for which a result is returned in the form of a reply.
Normally, inquiries are used to retrieve information about existing UI objects.
When a process sends an inquiry to the display server, it waits for the server to
process the inquiry and send a reply. Consequently, inquiries are more time-
consuming than asynchronous requests. When response time is important, avoid
using the inquiry/reply mechanism.

Programming features
7-7
Multitasking and the GUI

For example, when retrieving information that does not change, you can store the
data in variables and retrieve it from those variables instead of sending queries to
the display server.
The following example illustrates the use of requests, inquiries, and replies:
#include <bic_gpart>

gpart_id = first.gpart( window_id ) | Inquiry-


| reply
while gpart_id
get.gpart( window_id, part_id, type, x, y ) | Inquiry-
| reply
if type = GPLINE then
destroy.gpart( window_id, gpart_id ) | Request
endif
gpart_id = next.gpart( window_id, gpart_id ) | Inquiry-
| reply
endwhile

Processes, process groups, and main


windows
Processes
A process consists of a program object, a date, and a state. A process that has
been started can be in any one of the following states:
n Running
The process is in the running queue. It has been scheduled to run, or is ready
to be scheduled.
n Blocking
The process is in the blocking queue. It is waiting for input.
n Sleeping
The process is in the sleeping queue. It has been suspended and requires an
external action to wake it up again.
n Terminating
The process is in the terminating queue. It has ended but not all its resources
have yet been removed.
Each process stores information about the current main window, the current
menu, and current character window. Other information about display objects is
not directly available to it.

Programming features
7-8
Multitasking and the GUI

Process groups
A process group is a group of related, interdependent processes. One process is
the group leader. Child processes are started by the leader or by one of its
children. All processes belong to a process group.
The following are some of the characteristics of process groups:
n Each process group has its own event queue. Events are sent to the process
group, not to individual processes.
n When one process within a group starts a child process, the child process is
automatically placed in the same process group as its parent. However, you
can use the function set.pgrp() to place the process in a different process
group.
n When a process is killed or ended, its parent is automatically awakened,
unless the parent and child are in different groups.
n You use the grab.mwindow() function to set the process group to which a
main window sends its events. After calling this function, all events that
occur in the main window are sent to the specified process group.

Main windows
A main window acts as the frame window for both sessions and 3GL
applications. It is used for starting processes and for creating graphical and
character windows. It is not used directly for handling user input or displaying
program output. Usually, a main window consists of a border, title bar, menu bar,
status area, control menu box, sizing controls, and a work area. The objects used
by an application for user interaction are created within the work area of a main
window and are managed by that main window.
Each process can have zero, one, or more main windows.

Process groups and main windows


Process groups and main windows are used:
n To ensure unambiguity of input focus
n To keep related processes dependent
n To enable parent and child processes to be independent of each other

Programming features
7-9
Multitasking and the GUI

Unambiguity of input focus


To ensure that there is never any ambiguity as to where the events generated by a
particular window are sent, when you create a main window, you define a single
process group to which that main window sends its events. You do this with the
grab.mwindow() function. After calling this function for a main window, all
events that occur in that window are sent to the specified process group. In
principle, there is only one running process in a process group at any time; so the
events are sent to that process.
In principle, each process could have its own event queue. However, there are a
number of situations where this would cause problems. For example:
n When the input focus is on an input field and the user zooms to another
process to select a value. In this case, the parent process is moved to the
sleeping queue and will be awakened when the child process ends.
However, if the user types in the input field after the zoom process has started
but before the zoom process has created its own windows, the characters
typed are sent to the sleeping process and not to the zoom process for which
they were intended. When the windows of the zoom process appear, the user
must then restart the selection. However, when the zoom process exits, the
characters sent to the parent process are displayed in the input field and not
those entered in the zoom process. This is because, when awakened, the
parent process finds those characters in its process queue.
n A similar problem can arise when a uses ends a number of nested processes
by repeatedly pressing the EXIT key and also when processes are
synchronized by using bucket functions.
These problems are avoided by enabling related, interdependent processes to
share the same process group (and so the same event queue).

Programming features
7-10
Multitasking and the GUI

Keeping related processes dependent


When a process is started by the functions activate(), act.and.sleep(), or
wait.and.activate(), by default the new process inherits the process group and
main window of its parent. The parent process is moved to the sleeping queue
will be awakened automatically when the child process ends.
The following diagram illustrates this.

A process with the same ID as its process group is referred to as the group leader.
For the group leader, the predefined variable background is set to FALSE, For
child processes, this variable is set to TRUE.
A process group keeps track of the number of processes that belong to it. When
no processes remain in the group, the group is automatically destroyed. You can
destroy all processes in a particular group simultaneously by calling kill.pgrp().

Programming features
7-11
Multitasking and the GUI

Making related processes independent


When a process starts a child process, by default the child process inherits the
process group and main window of its parent, and the parent is suspended until
the child exits. However, it is possible to create a new process group and main
window for the child process and so keep the parent and child processes
independent. The following diagram illustrates this.

The code for disconnecting a child process from its parent can be included either
in the parent process or in the child process. In the following example, it is
included in the parent process.
old.mwindow = current.mwindow()
new.mwindow = create.mwindow( title, mode, flags)
change.mwindow( new.mwindow )
child.pid = act.and.sleep( program.name, arglist )
if child.pid then
set.pgrp( child.pid, child.pid )
grab.mwidow( new.mwindow, child.pid )
reactivate( child.pid )
endif
change.mwindow ( old.mwindow )
destroy.mwindow( new.mwindow )

In this example, the parent process first creates a new main window for the child
process. It then starts the child process and calls set.pgrp() to place the child
process in a different process group. By specifying the same ID for the process
and the process group in the set.pgrp() call, a new process group is automatically
created with the same ID as the process. The process becomes the leader of the
new process group. The parent process then calls grab.mwindow() to link the
new process group to the new main window.

Programming features
7-12
Multitasking and the GUI

In this example, it is important to start the child process with act.and.sleep() and
to reactivate the process only after it has been placed in the new process group. If
the process is started with activate(), and a context switch occurs immediately
after the child process is started, then the process would be scheduled with the
incorrect process group.
Note that if a process is moved to another process group, it will be impossible to
switch it back to the original group if that group is destroyed in the meantime.

Synchronizing processes with the wait()


function
The wait() function enables synchronization of parent and child processes. It
causes the parent process to wait for one or all of its children to exit before
continuing. Each child process sends an exit signal to the parent process when it
ends.
The parent can choose to ignore child signals or not. If it wants to ignore child
signals, the parent process calls:
signal(SIGCHLD, SIGIGN)

If it does not want to ignore child signals, it calls:


signal(SIGCHLD, SIGNOIGN)

When a parent process issues a wait() call but is ignoring child signals, child
processes exit without any action by the parent. They exit, send an exit signal to
the parent, and are removed. The parent process blocks until it receives an exit
signal from the last process. The exit value and process ID of the last child are
returned in the return value and prodess_id argument respectively of the wait()
function.

Programming features
7-13
Multitasking and the GUI

The following diagram illustrates this situation.

When a parent process issues a wait() call and is not ignoring child signals, the
exit signal of every child process is caught by the wait() function (including exit
signals from children in other process groups). So, each child waits after exiting
until its parent process calls the wait() function. While it is waiting for the parent
process to catch its exit value, the child process becomes a zombie process. It is
placed in the terminating process queue, but is not removed until the parent
catches its exit signal.

Programming features
7-14
Multitasking and the GUI

The following diagram illustrates this situation.

Note that if a parent ends while some of its children are zombie process, those
children are removed automatically.

Programming features
7-15
Multitasking and the GUI

Programming features
7-16
8 Dynamic-link libraries

Overview
BaanERP Tools supports the use of dynamic-link libraries (DLLs). A DLL
consists of a library of functions that are compiled, linked, and stored separately
from the processes that use them. The functions are resources that can be shared
by multiple programs that are running concurrently. Only one copy of the DLL
needs to be present in memory. Each program that uses the DLL links to it at
runtime, at the time it calls one of the DLL’s functions.
For example, consider the sprintf$() function. This is a very common function,
used by many programs. Instead of including a copy of the function in each
program that uses it, you can program the function in a separate DLL. Multiple
programs can then share this one copy of the function, by linking to the DLL at
runtime.
The following are some of the benefits of using DLLs:
n Instead of programming common functions in each program that uses them,
you can program the functions in one or more DLLs. The code can then be
shared by all the programs that use it. By implementing DLLs in BaanERP
applications, you can reduce the size of objects to a minimum, as the standard
program is not merged with each 4GL program script.
n You can upgrade a function within a DLL without recompiling all the
applications that use it.
n You can upgrade an application without recompiling all the DLLs it uses.
n A DLL is loaded only when required. It is also loaded only once, though
multiple programs can share its code.
The following sections describe how you handle DLLs.

Programming features
8-1
Dynamic-link libraries

Function declarations
For functions programmed within a DLL to be accessible to other programs, they
must be declared as external functions – that is, they must be declared with the
keyword EXTERN.
For example:
function extern long funct_a( long arg )
{
.....
}

Functions declared without the EXTERN keyword are local functions. They are
accessible only within the program in which they are declared. A DLL usually
contains both local and external functions.

Scope of variables across DLLs


Global variables
Global variables are declared outside the functions in a script, without the
EXTERN keyword. They are only known within the DLL in which they are
declared. For example:
long var_1

External variables
External variables are declared outside the functions in a script, with the
EXTERN keyword. They are known within all DLLs in the process in which
they are declared. For example:
extern long var_2

Database fields
Database tables are declared as follows:
table t<table_name>

Declaration of a table implies declaration of all its fields also. Though a table
declaration does not include the EXTERN keyword, tables fields are known
within all DLLs in the process in which they are declared. When one DLL in a
process reads a record, the values of the record fields are shared by all DLLs in
the same process.

Programming features
8-2
Dynamic-link libraries

Sharing DLL object code


DLL objects contain both a program code part and a data part. The program code
of a DLL object is loaded into memory when one of the DLL’s functions is
called. The data part defines the memory part available for variables. Memory is
allocated for a variable the first time that variable is used.
The program code part of a DLL can be loaded into a common virtual memory
address space. This enables two or more sessions, within one or more bshells on
the same system, to share the program code. The data part is not loaded into
shared memory and so cannot be shared.
By default, the program code of BaanERP DLLs can be shared only within the
same bshell. Once the DLL has been loaded, it is shared by all process within the
bshell.
For process within all bshells on the same system to be able to share the program
code, you must specify the object name in the file $BSE/lib/srdd_tabx.x (where
x.x is the bshell version). The object will be loaded automatically and its code
will be shared by sessions within all bshells on the same system. It is advisable to
use this method for all DLLs that are frequently used by several users. For more
details, consult the Shared memory manager section in the BaanERP Tools
Technical Manual.

Dynamic linking
DLLs can be linked to a program script either at compile time or at runtime.

Dynamic linking at compile time


For dynamic linking at compile time, you must specify the libraries that belong
to a program script when you create or update that script. Which libraries belong
to a program script depends on which external functions the program uses. It is
necessary to specify the libraries at compile time because the compiler locates
the functions and checks the types of arguments and return values with the
function prototypes. Note that a DLL object is loaded into memory and linked to
the executable program at runtime, at the time a DLL function is called.
You must compile the libraries before you compile the program script that uses
those libraries. However, since loading and linking take place at runtime, when a
library is changed, you need only compile the library and restart the program.

Programming features
8-3
Dynamic-link libraries

The are two ways to compile the libraries and program script:
n Use the BaanERP Tools. Choose the Compile option in the sessions
"Maintain Libraries" and "Maintain Program Scripts".
n Use the bicx.x compiler (where x.x is the bhsell version). For example:
$ bic6.2 dll1 -o odll1
$ bic6.2 dll2 -o odll2
$ bic6.2 dll3 -o odll3
$ bic6.2 <program> -o<object> -d odll1:odll2:odll3

Note that, at runtime, external functions are searched for in the order in which the
DLLs were supplied during compilation. In above example, dll1 is first.

Dynamic linking at runtime


This is used in cases where the DLL(s) that must be linked to a program are
known only at runtime.
For example, consider a programmer’s interface that receives a string containing
the library or external function to be called. The return value of the function call
(if any) is sent back to the calling process. In this case dynamic linking at
runtime must be used, because only at runtime is it known which external
function must be called.
A number of functions are available for handling dynamic linking at runtime.
Using these functions, you can load a specific DLL and execute one or more of
its functions. See Chapter 14, DLL functions (executing), in U7167A US
BaanERP Tools: Functions reference.

Function overloading
When DLLs are dynamically linked at runtime, instead of at compile time,
function overloading is possible. This means that an external function contained
in a loaded DLL can be replaced by overloading another DLL that contains a
function with the same name and arguments.
You use the load_dll() function to load DLLs dynamically. When you call this
function, you can specify whether the DLL is to be overloaded or not. When
searching for an external function called by a program, the bshell always
searches in overloaded DLLs first.

Programming features
8-4
Dynamic-link libraries

Specifying which DLLs belong to an object


There are three methods for specifying which DLLs belong to an object.
n Specify the DLL dynamically, with overload, in the source code. For
example:
load_dll( <dll_object>, DLL_OVERLOAD )
n Specify the DLL statically at compile time. There are three ways to do this:
− use the option "Define Libraries" in the session "Maintain Program
Scripts"
− use the following syntax when starting the bicx.x compiler (where x.x is
the bshell version)
bicx.x script -o object -d dll_object

− use the following pragma code in the script


#pragma used dll <dll_object>
n Specify the DLL dynamically, without overload, in the source code. For
example:
load_dll( <dll_object>, 0 ) or
load_dll( <dll_object> ) | 0 is default

Function search algorithm


When a DLL function is called for the first time, the function address is
unresolved. The bshell attempts to resolve the function address by searching for
the function using the search method described below. After a function address is
resolved, that address is used for all subsequent function calls with the same
function name and arguments. So once a function address is resolved, the
function cannot be replaced by overloading a different DLL containing a
function of the same name and arguments.
The bshell searches DLLs in the following order:
1 All dynamically overloaded DLLs – that is, DLLs loaded by calling
load_dll() with the overload flag set to DLL_OVERLOAD. For example:
load_dll(dll_object, DLL_OVERLOAD)

The bshell searches the overloaded DLLs in reverse order. So, the DLL that
was overloaded last is searched first.
2 The first DLL where the function was found during compilation.

Programming features
8-5
Dynamic-link libraries

3 All statically specified DLLs.


First the bshell searches in the DLLs specified in the compilation command
with the –d option. The DLLs are searched in the order they are specified in
the command. For example:
bic6.2 script -o object -d odll1:odll2:odll3

Next the bshell searches in DLLs specified using the following pragma
statement:
#pragma used dll <dll_object>

4 All dynamically loaded DLLs – that is, DLLs loaded by calling load_dll()
without the overload flag or with the overload flag set to 0. For example:
load_dll(dll_object, 0)

The bshell searches the loaded DLLs in the order in which they were loaded.
So, the DLL that was loaded first is searched first.
Note that you can change the search order of dynamically loaded DLLs by
calling load.dll() for a loaded DLL and changing the value of the overload
flag.

Function overloading – example


Compile
bic6.2 dll -o odll
bic6.2 dll_new -o odll_new
bic6.2 script -o object -d odll –DDEBUG

Script code
function main()
{
#ifdef DEBUG
if ( debug flag ) then
| Load dynamically DLL 'dll_new' to
| overload the DLL 'dll'

load_dll("odll_new", 1)
| Argument '1' means overload
endif
#endif

dll_function()
}

Programming features
8-6
Dynamic-link libraries

dll code
function extern dll_function()
{
...
}

dll_new code
function extern dll_function()
{
...
}

Object information tool


To display information about an object, use the object information tool
bic_infox.x (where x.x is the bshell version). Note that maximum information is
displayed when the object has been compiled with the debug option.

Syntax
bic_infox.x [-aidshwcV] [-eu] [-f[flags]] object

Options
-a Show all available object information (default).
-I Show instructions in object (Disassembler). This is only possible
when the object is compiled with the debug option.
-d Show used DLLs and where the external functions used are
defined.
-s Show list of used variables, (global) functions, string constants, and
double constants.
-h Show Object header.
-w Show #ident <strings>.
-c Check checksum.
exit(0) ok
exit(1) corrupted

Programming features
8-7
Dynamic-link libraries

-V Show version/release and portings information of bic_infox.x.


-e Show prototypes of all external functions defined in the object.
-u Show the general usage of the library (see below).
-f Show flags.
-f<flag> Check flags. When this options is used, all other options are
ignored. <flag> can be a combination of the following:
1 NOTRANSACTION
2 DEBUG
4 PROFILE
8 DLL
exit(0) flag(combination) is in object
exit(1) flag(combination) is not in object

Combining -e and -u displays the description of the object and the descriptions
and prototypes of all external functions within the object (see below).

Object and function descriptions


You specify an object’s description between the keywords DLLUSAGE and
ENDDLLUSAGE in the program script. You place the information outside all
functions so that it is global for the entire program. The description should
explain the general usage of the object. The description is added to the object and
you can retrieve it with the command bic_infox.x -u <object>.
You also specify a function’s description between the keywords DLLUSAGE
and ENDDLLUSAGE in the program script. However, you place the information
within the body of the function, between the { and } characters. The description
should explain the general usage of the object, its arguments, and its return value,
for example. You can retrieve function descriptions and prototypes with the
command bic_infox.x –eu <object>.

Programming features
8-8
Dynamic-link libraries

Example
dllusage
This is the object description.
enddllusage

function extern long abs( long arg )


{
dllusage
This function returns the absolute value of the
specified argument.
Input:
ARG The value for which the absolute value is to be
returned.
Output:
Return the positive value of ARG.
enddllusage

return ( arg >= 0 ? arg : -arg )


}

DLL compilation and runtime errors


Compilation errors
A compilation error occurs when a function is not found in the specified DLLs or
if there is an argument mismatch.

Runtime errors
The following runtime error message is displayed when a script calls a function
that has been removed from the DLL and only the DLL has been recompiled:
Dynamic Link Failure function %s

The following runtime error messages are displayed when a function’s arguments
are changed within a DLL without recompilation of programs that call the
changed function:
Arg error <function name>(<argument name>) type ...
expected ...

or
Illegal number of args (%d) for function %s expected %d

To avoid runtime errors, it is advisable to recompile programs whenever you


change the programmer’s interface of a DLL that is linked to those programs.

Programming features
8-9
Dynamic-link libraries

Programming features
8-10
9 The BAAN debugger

Overview
The BAAN debugger is an interactive statement interpreter that enables you to
control and test the execution of a program. Its principal features include:
n Simultaneous program execution and display of debug information
n Source instructions and debug information are displayed in their own
windows, separate from the application
n Source instructions are displayed in a window that you can move and resize
n On non-graphical displays, debug information is displayed in screen columns
81 to 132
n The debugger supports variable tracing during program execution (that is, all
changes to the values of specified variables are displayed during program
execution)
n The debugger is fully symbolic – machine addresses are not used
n The debugger is fully integrated with the bshell.
The debugger works only with source programs that have been compiled with the
debug option (-l in the case of the BAAN Compiler). When you start such a
program, two additional windows are displayed. One displays the source code
being executed (the line currently being processed is highlighted). The other is a
command window where you input debugger commands and where command
results and error messages are displayed.
For information about other debug facilities, the “Logic Server” section in the
BaanERP Tools Technical Manual.

Commands
Debugger commands consist of from one to three words; the words can be
separated from each other by one or more spaces. The assignment command is an
exception. Here the variable and the value to be assigned are separated by a ':='
sign. Most commands require a line number and/or a source name. If no source
name is specified, the default is the current source.

Programming features
9-1
The BAAN debugger

When you start the debugger for an object, the source used by the debugger is
first searched for by using the directions in the file fdx.x.<pack.comb.> (where
x.x is the bshell version), then it is searched for in the $BSE/tmp directory, and
finally in the current directory.
Command Action
b [[source] line_number] Set a break point on the current or specified
line of the current or specified source. The
line is highlighted.
Note that you cannot set break points in
included parts in a source. Instead the break
point must be set in the included file.
B [source] Display all set break points in the current or
specified source.
c [number] Continue (execute the source) up to the next
break point. If you specify a number, the
debugger skips that number of break points.
The executed source lines are not displayed.
C [number] Same as ’c’, except that the executed source
lines are displayed.
cc Change the size of the command window.
This is valid only when application and
debugger are in the same window.
cs Change the size of the source window. This is
valid only when the application and debugger
are in the same window.
d [source line_number] Delete the break point on the specified line
number in the source. If you do not specify a
line number, the debugger provides the
opportunity to interactively delete any break
point in the current source file.
D [source] Delete all set break points in the specified or
current source.
delete all Delete all traces and stop instructions.
delete number Delete a specified trace or stop instruction.

Programming features
9-2
The BAAN debugger

Command Action
ds Delete the source window.
dd Generate a dump of the data dictionary.
<expression> Execute the specified expression.
f [noarg|noname] Display stack trace (present nesting of
[number] functions), with the arguments of the
functions. If you specify a number, only that
number of nestings is displayed.
noarg only the names of functions are
displayed
noname arguments are displayed but not
their names
fdebug Show the open tables on the command
window.
Fdebug Print the open tables to the spooler.
g [source] line_number Continue program execution at the specified
line number (goto).
help (or ?) Start the Help Viewer for debugger options.
instr Generate a symbolic dump of the object.
l Display the full list of program variables.
L Display the list of program variables that
have a value, and display their current values.
To stop displaying the contents of an array,
enter ’n’ at the question ’More ?’.
lp Print the output of the ’l’ command.
Lp Print the output of the ’L’ command.
mem Generate a dump including information about
the memory allocated.
p Go to the source line currently being
processed (this is useful after the view or seek
command). The line is indicated by a greater
than (>) sign before the line number.

Programming features
9-3
The BAAN debugger

Command Action
q Exit the debugger (quit).
return Continue execution until the end of the
current function.
s [number] Execute the program step by step, the
specified number of instructions at a time
(default is 1). During execution, the
intermediate source lines are not displayed.
S [number] Similar to ’s’, except that a function call is
executed as a single statement.
slow [number] Reduce the rate of running source code. The
maximum number is 100; the minimum (and
default) number is 0.
split number Split the command and source window. The
specified number is the last line of the
command window. This is often used after
resizing the Debugger window.
status Display traced variables and/or stop
[trace|stop|number] instructions. If you specify a number, only
that traced variable or stop instruction is
displayed.
stop if <expression> Stop execution when expression is True.
stop in <function> Stop execution when function is entered.
sym Generate a dump of the symbol table.
trap off Disables ‘trap on’. This command is available
only when the bshell was started with the -
dbgcpu flag.
trap on The debugger stops after execution of every
function call. This command is available only
when the bshell was started with the -
dbgcpu flag.

Programming features
9-4
The BAAN debugger

Command Action
t variable Trace the specified variable during program
execution. The execution of the program
stops every time the value of the variable is
changed. You cannot trace array variables or
common variables.
T variable [value] Same as ’t’, except that program execution is
not stopped when the variable changes. If you
specify a value, execution stops when the
variable gets that value.
u [variable] Stop tracing the specified variable. If no
variable is specified, you can stop tracing
each variable interactively.
U Stop tracing all variables currently being
traced.
v [source] line_number Display a specified source line in the source
window (view). If line_number is greater than
the total number of lines, the last line is
displayed.
v {+-} number Shift the display to the source line that is
number lines before or after the current
source line.
/pattern Seek a matching text pattern in the current
source and display that line.
[function.]variable Display the value of a specified variable. If
[/option] you specify a function, the value of the local
variable of that function is displayed.
Otherwise, the value of the variable of the
current function or the global variable is
displayed. If a variable has no value (empty
string or zero number), the value is not
displayed.
/option can have the following values:

Programming features
9-5
The BAAN debugger

Command Action
/
Use to display the value of a variable that has
the same name as a Debugger command. For
example, ‘b/’ displays the value of variable b.
/d
Print the flags of the variable and the
dimension for arrays.
/D
Display the number of bytes allocated in a
long or double variable. Display the number
of bytes for one element in a string array.
The following options are useful for record
buffers and binary data. They display the
value of a string variable with its full declared
length, regardless of NUL characters.
/x
NUL characters are displayed as ‘^@’.
/X
Characters with values 0 to 31 are displayed
as ‘\x<value in hex>’.
/b
All characters are displayed as ‘\x<value in
hex>’.
variable := value Assign a value to a variable. The value can
be:
n A string constant or expression
n A numeric constant or expression
n A variable name
vi Activate the vi editor for the current source at
the current source line. Changing the source
has no influence on the source displayed by
the debugger in the source window.
width value Change the width of command and source
window. You can specify any value in the
range 80 to 132.

Programming features
9-6
The BAAN debugger

Command Action
<Ctrl>D/<Ctrl>U Page down or up in source.
Command Action
<Esc> Toggle between normal and history mode.
<Arrow up>/ <Arrow In history mode, these display previously
down> executed commands on the command line. In
normal mode, these move the cursor through
the source.

Debugger mouse actions


The Debugger supports the following mouse actions in the source window:
n Click on a line number to set a break point on that line.
n Click on a source line to move the cursor to that line.
n Click on a variable to display its value.

Programming features
9-7
The BAAN debugger

Programming features
9-8
10 Errors

There are three categories of errors:


n Error numbers 1-99 are operating system errors
n Error numbers 100-899 are database errors
n Error numbers 900-999 are network errors

Notes
To retrieve errors with error codes greater than 1000, subtract 1000 from the
error number. The result is the database error code. For example, the error code
1510 represents the database error code 510 (that is, 1510 – 1000).
When a fatal error occurs, information about the error is stored in the log files in
the directory $BSE/log. For example, if bdbpost6.2 causes an error, this is
reported in the file log.bdbpost6.2.

UNIX errors
1 EPERM Not owner
This indicates an attempt to modify a file that cannot be
modified, except by its owner or by a super user. This is
also generated when ordinary users attempt an action that
only a super user is permitted to carry out.
2 ENOENT No such file or directory
This occurs when a specified file name should exist but
does not, or when one of the directories in a path name
does not exist.
3 ESRCH No such process
This occurs when a specified process cannot be found.

Programming features
10-1
Errors

4 EINTR Interrupted system call


This indicates that an asynchronous signal (such as
interrupt or quit), which the user has elected to catch,
occurred during a system call. If the system resumes
execution after processing the signal, it will appear as if
the interrupted system call returned this error code.
5 EIO I/O error
This indicates some physical I/O error. In some cases, this
error can point to the call following the one to which it
actually applies.
6 ENXIO No such device or address
This occurs when I/O on a special file refers to a
subdevice that either does not exist, or is beyond the limits
of the device. It may also occur when, for example, a tape
drive is not online or no disk pack is loaded on a drive.
7 E2BIG Arg list too long
This occurs when an argument list longer than 5120 bytes
is presented to a member of the exec family system calls.
8 ENOEXEC Exec format error
This indicates that a request has been made to execute a
file which, although it has the appropriate permissions,
does not start with a valid magic number. See a.out(5).
9 EBADF Bad file number
This indicates either that a file descriptor does not refer to
an open file, or that a write request has been made to a file
that is opened only for reading, or that a read request has
been made to a file that is opened only for writing.
10 ECHILD No child processes
This occurs when a wait is executed by a process that has
no existing or unwaited-for child processes.
11 EAGAIN No more processes
This indicates that a fork has failed, either because the
system’s process table is full or because the user is not
allowed to create any more processes.

Programming features
10-2
Errors

12 ENOMEM Not enough space


This occurs during an exec or sbrk, when a program asks
for more space than the system is able to supply. This is
not a temporary condition. The maximum space size is a
system parameter.
This error can also occur when the arrangement of text,
data, and stack segments requires too many segmentation
registers, or if there is not enough swap space during a
fork.
13 EACCES Permission denied
This occurs when an attempt is made to access a file in a
manner forbidden by the protection system.
14 EFAULT Bad address
This occurs if the system encounters a hardware fault
when it attempts to use an argument of a system call.
15 ENOTBLK Block device required
This occurs when a nonblock file is specified where a
block device is required – for example, in mount.
16 EBUSY Device busy
This indicates an attempt to mount a device that was
already mounted, or an attempt to dismount a device on
which there is an active file (open file, current directory,
mounted-on file, active text segment). This error also
occurs when an attempt is made to enable accounting
when it is already enabled.
17 EEXIST File exists
This occurs when an existing file is specified in an
inappropriate context (for example, as a new link name in
the link function).
18 EXDEV Cross-device link
This occurs when an attempt is made to link to a file on
another device.

Programming features
10-3
Errors

19 ENODEV No such device


This occurs when an attempt is made to apply an
inappropriate system call to a device – for example, when
an attempt is made to read from a write-only device.
20 ENOTDIR Not a directory
This indicates that a nondirectory was specified where a
directory is required – for example, in a path prefix or as
an argument to chdir(S).
21 EISDIR Is a directory
This indicates an attempt to write to a directory.
22 EINVAL Invalid argument
This indicates an invalid argument. For example,
dismounting a nonmounted device, specifying an
undefined signal in signal or kill, reading or writing a file
for which lseek has generated a negative pointer. This
error is also used by the math functions described in the
(S) entries of the UNIX manual.
23 ENFILE File table overflow
This indicates that the system's table of open files is full,
and that temporarily no more open commands can be
accepted.
24 EMFILE Too many open files
This occurs when an attempt is made to open more than
the maximum number of files. No process can have more
than 20 file descriptors open at a time.
25 ENOTTY Not a typewriter
This indicates that the specified device does not have the
properties of a terminal.
26 ETXTBSY Text file bus
This occurs when an attempt is made either to execute a
pure-procedure program that is currently open for writing
or reading, or to open for writing a pure-procedure
program that is being executed.

Programming features
10-4
Errors

27 EFBIG File too large


This indicates that the size of a file exceeded the
maximum file size (1,082,201,088 bytes) or ULIMIT. See
ulimit(S).
28 ENOSPC No space left on device
This occurs during a write to an ordinary file when there is
no free space left on the device.
29 ESPIPE Illegal seek
This occurs when an lseek is issued to a pipe.
30 EROFS Read-only file system
This occurs when an attempt is made to modify a file or
directory on a read-only device.
31 EMLINK Too many links
This indicates an attempt to make more than the maximum
number of links (1000) to a file.
32 EPIPE Broken pipe
This indicates an attempt to write on a pipe for which
there is no process to read the data. This condition
normally generates a signal; the error is returned if the
signal is ignored.
33 EDOM Math arg out of domain of func
This indicates that an argument of a math function is
outside the defined domain of the function.
34 ERANGE Math result not representable
This indicates that the value of a function in the math
package cannot be represented within machine precision.

Programming features
10-5
Errors

Database errors
100 EDUPL This indicates that a duplicate value exists.
101 ENOTOPEN This indicates that the specified table is not
open.
102 EBADARG This indicates that an illegal argument has
been specified.
103 EBADKEY This indicates that an Illegal key description
has been specified. Use the bdbpre and
bdbpost tools to resolve.
107 ELOCKED This indicates that the specified record is
locked. You can either wait until the lock is
released or you can remove the lock yourself.
108 EKEXISTS This indicates that the specified key already
exists.
110 EENDFILE This indicates that the end of file has been
reached.
111 ENOREC This indicates that no record was found that
matches the query criteria.
112 ENOCURR This indicates that there is no current record.
113 EFLOCKED This indicates that the table is locked. You
can either wait until the lock is released or
you can remove the lock yourself.
201 EROWCHANGED This indicates that the record was changed
after a delayed lock.
202 EDBLOCKED This indicates that the database is locked.
You can either wait until the lock is released
or you can remove the lock yourself.
203 ETRANSACTIONON This occurs when you attempt an action that
is not allowed within the transaction.
204 EISREADONLY This indicates that the transaction is read
only.

Programming features
10-6
Errors

205 ENOTINRANGE This indicates that the field value is out of


range and does not agree with the domain
definition.
206 ENOTLOCKED This indicates that the record is not locked.
207 EAUDIT This indicates an audit trailer error.
208 EPERMISSION This occurs when you attempt an action that
is not allowed at this time.
209 EMIRROR This indicates an error in the mirroring of the
database. The tables are inconsistent. You
can use the bdbpre and bdbpost tools to copy
the tables correctly.
210 EMLOCKED This indicates either that the record is locked
in the mirrored database, or that the tables
are inconsistent, or that the mirroring
definition in tabledef6.2 is not compatible.
213 ETRANSACTIONOPEN This indicates that the transaction was
started, but not updated. This is an internal
bshell error.
214 EUNALLOWEDCOMPNR This indicates that the operation for mapping
company numbers is not allowed. If the
logical company is not equal to the physical
number, then you are not allowed to perform
a drop or clear table operation.
251 EAUDSETUP This occurs when the audit server setup is not
correct. See the log.audit file for more
information.
252 EAUDCORRUPT This indicates that an audit file is corrupt.
See the log.audit file for more information.
253 EAUDLOCKED This indicates that the audit file is locked by
another user. See the log.audit file for more
information.
254 EAUDABORT This indicates that a commit transaction has
failed in the audit server. See the log.audit
file for more information.

Programming features
10-7
Errors

301 ESQLQUERY This indicates a general SQL error. It occurs


when the SQL query is syntactically
incorrect.
302 ESQLSYNTAX This indicates that the SQL syntax is not
correct.
303 ESQLREFER This indicates that a reference in the query
cannot be found.
305 ESQLWRONGROW This occurs when a wrong record was
returned. It probably means either that the
table index is corrupt or that the RDBMS has
a different sorting order than the BaanERP
software.
501 EMEMORY This indicates an internal memory error.
502 EBDBON This indicates that the user is already logged
on.
503 EBADADRS This occurs when an illegal address is used.
504 EBADFLD This indicates an undefined column.
505 ENOSERVER This indicates either that there is no server
specified in tabledef6.2 or that the server
cannot be started. See the log file for more
information.
506 ENOTABLE This indicates that the table does not exist.
507 ETABLEEXIST This indicates that the table you are trying to
create already exists.
508 EBDBNOTON This indicates that you are not logged on to a
database.
509 EBADCURSOR This indicates that you have a bad memory
cursor or that a bad table pointer has been
specified.
510 EDBNOTON This indicates that the database has not been
started. Start the database to correct the
problem.
511 EWRONGVERSION This indicates that client and server versions
are incompatible.

Programming features
10-8
Errors

512 EDDCORRUPT This indicates a corrupt data dictionary. You


can use the bdbpre and bdbpost tools to
repair it.
513 ENODD This indicates that the data dictionary was
not found.
514 ESECURITY (ORACLE) This indicates a security error. It
probably means that you do not have the
correct user or group permission.
515 ELICENSEERROR This indicates a license error; probably an
unpatched binary.
516 EUPDSEGM This occurs during the making or filling of
rollback segments. It probably means that the
disk is full.
517 EDELAYED This indicates a general delayed locking
error.
518 ENOSESSION This occurs when an invalid session code is
specified.
519 ENOCOMPNR This occurs when no company number is
specified or when an illegal company number
is specified. A valid company number is a
number between 0 and 999.
520 EBUFUPD This occurs when flushing of buffered
updates fails. The flushing can fails due to a
lock or to a referential integrity constraint.
521 ENOSHM This indicates that shared memory has not
been loaded. Load shared memory to correct
the problem (see BaanERP Tools Technical
Manual for more information).
600 EREFERENCE This indicates a general reference error. See
the log file for more information.
601 EREFLOCKED This indicates that the reference table is
locked. You can either wait until the lock is
released or remove the lock yourself.

Programming features
10-9
Errors

602 EUNDEFREF This indicates an undefined reference. This


probably means that there is a problem in the
runtime data dictionary. See the log file for
more information.
604 EREFUPDATE This indicates that a reference could not be
updated.
605 EREFEXISTS This indicates that the record cannot be
deleted while a reference exists. See the log
file for more information.
606 EREFNOTEXISTS This indicates that the reference does not
exist.
607 ENOREFTBL This indicates that the reference table could
not be found. This may mean that the data
dictionary is not correct. See the log file for
more information.
608 ENOREFCNT This indicates that no reference counter fields
are present.
609 EUPDREFCNT This indicates an error during updating of the
reference counter.
700 ESETLOCALE This indicates an error during setting of the
locale. See the log file for more information.
850 EABORTONLOCK Automatic rollback on lock error. The
database driver aborts the transaction.

Programming features
10-10
11 Known limits

Reports
n Sorting doubles correct to 6 decimal places
n Maximum 253 fields on a report (including array elements)

Bshell
n Maximum string buffer size is 4K – be careful when concatenating strings
with '&' sign
n Maximum function stack depth is 75

Bic
n Maximum 255 arguments per function
n Maximum 32K variables per compilation
n Maximum BRANCH of 32K (BRANCH refers to the amount of generated
code per function, including macros etc.)

SQL
n Maximum of 50 AND conditions within WHERE clause – otherwise the
following error message appears:
"Stack overflow [QueryId = XX]"

Tables
n Maximum record length is 3072 bytes
n Maximum number of fields is 1024
n Maximum field length is 3072 bytes

Programming features
11-1
Known limits

Indexes
n Maximum index length is 120 bytes
n Maximum number of fields is 32 (no combined fields)
n Maximum number of index parts is 8

Programming features
11-2
12 ASCII table (ISO 8859-1)

Character Decimal Hexadecimal Character Decimal Hexadecimal

0 00 128 80
^A 1 01 129 81
^B 2 02 130 82
^C 3 03 131 83
^D 4 04 132 84
^E 5 05 133 85
^F 6 06 134 86
^G 7 07 135 87
^H 8 08 136 88
^I 9 09 137 89
^J 10 0A 138 8A
^K 11 0B cf$(0) 139 8B
^L 12 0C cf$(1) 140 8C
^M 13 0D cf$(2) 141 8D
^N 14 0E cf$(3) 142 8E
^O 15 0F cf$(4) 143 8F
^P 16 10 cf$(5) 144 90
^Q 17 11 cf$(6) 145 91
^R 18 12 cf$(7) 146 92
^S 19 13 cf$(8) 147 93
^T 20 14 cf$(9) 148 94
^U 21 15 cf$(10) 149 95

Programming features
12-1
ASCII table (ISO 8859-1)

Character Decimal Hexadecimal Character Decimal Hexadecimal

^V 22 16 cf$(11) 150 96
^W 23 17 cf$(12) 151 97
^X 24 18 cf$(13) 152 98
^Y 25 19 cf$(14) 153 99
^Z 26 1A cf$(15) 154 9A
ESC 27 1B 155 9B
^\ 28 1C 156 9C
^] 29 1D 157 9D
^^ 30 1E 158 9E
^_ 31 1F 159 9F
SP 32 20 NBSP 160 A0
! 33 21 ¡ 161 A1
" 34 22 ¢ 162 A2
# 35 23 £ 163 A3
$ 36 24 ¤ 164 A4
% 37 25 ¥ 165 A5
& 38 26 ¦ 166 A6
' 39 27 § 167 A7
( 40 28 ¨ 168 A8
) 41 29 © 169 A9
* 42 2A ª 170 AA
+ 43 2B « 171 AB
, 44 2C ¬ 172 AC
- 45 2D SHY 173 AD
. 46 2E ® 174 AE
/ 47 2F ¯ 175 AF

Programming features
12-2
ASCII table (ISO 8859-1)

Character Decimal Hexadecimal Character Decimal Hexadecimal

0 48 30 ° 176 B0
1 49 31 ± 177 B1
2 50 32 ² 178 B2
3 51 33 ³ 179 B3
4 52 34 ´ 180 B4
5 53 35 µ 181 B5
6 54 36 ¶ 182 B6
7 55 37 · 183 B7
8 56 38 ¸ 184 B8
9 57 39 ¹ 185 B9
: 58 3A º 186 BA
; 59 3B » 187 BB
< 60 3C ¼ 188 BC
= 61 3D ½ 189 BD
> 62 3E ¾ 190 BE
? 63 3F ¿ 191 BF
@ 64 40 À 192 C0
A 65 41 Á 193 C1
B 66 42 Â 194 C2
C 67 43 Ã 195 C3
D 68 44 Ä 196 C4
E 69 45 Å 197 C5
F 70 46 Æ 198 C6
G 71 47 Ç 199 C7
H 72 48 È 200 C8
I 73 49 É 201 C9

Programming features
12-3
ASCII table (ISO 8859-1)

Character Decimal Hexadecimal Character Decimal Hexadecimal

J 74 4A Ê 202 CA
K 75 4B Ë 203 CB
L 76 4C Ì 204 CC
M 77 4D Í 205 CD
N 78 4E Î 206 CE
O 79 4F Ï 207 CF
P 80 50 Ð 208 D0
Q 81 51 Ñ 209 D1
R 82 52 Ò 210 D2
S 83 53 Ó 211 D3
T 84 54 Ô 212 D4
U 85 55 Õ 213 D5
V 86 56 Ö 214 D6
W 87 57 × 215 D7
X 88 58 Ø 216 D8
Y 89 59 Ù 217 D9
Z 90 5A Ú 218 DA
[ 91 5B Û 219 DB
\ 92 5C Ü 220 DC
] 93 5D Ý 221 DD
^ 94 5E Þ 222 DE
_ 95 5F ß 223 DF
` 96 60 à 224 E0
a 97 61 á 225 E1
b 98 62 â 226 E2
c 99 63 ã 227 E3

Programming features
12-4
ASCII table (ISO 8859-1)

Character Decimal Hexadecimal Character Decimal Hexadecimal

d 100 64 ä 228 E4
e 101 65 å 229 E5
f 102 66 æ 230 E6
g 103 67 ç 231 E7
h 104 68 è 232 E8
i 105 69 é 233 E9
j 106 6A ê 234 EA
k 107 6B ë 235 EB
l 108 6C ì 236 EC
m 109 6D í 237 ED
n 110 6E î 238 EE
o 111 6F ï 239 EF
p 112 70 ð 240 F0
q 113 71 ñ 241 F1
r 114 72 ò 242 F2
s 115 73 ó 243 F3
t 116 74 ô 244 F4
u 117 75 õ 245 F5
v 118 76 ö 246 F6
w 119 77 ÷ 247 F7
x 120 78 ø 248 F8
y 121 79 ù 249 F9
z 122 7A ú 250 FA
{ 123 7B û 251 FB
| 124 7C ü 252 FC
} 125 7D ý 253 FD

Programming features
12-5
ASCII table (ISO 8859-1)

Character Decimal Hexadecimal Character Decimal Hexadecimal

~ 126 7E þ 254 FE
DEL 127 7F ÿ 255 FF

Programming features
12-6
13 Index

3 C
3GL programming language..... 1-1, CASE statement....................... 2-23
............................................... 2-1 combined fields......5-23, 5-24, 5-25
constants ................................ 2-3 commands..................3-8, 3-9, 3-12
expressions and operators.... 2-16 common variables........... 2-14, 2-15
iterations .............................. 2-25 compiler ................................... 2-45
overview ................................ 2-1 conditional compiling .............. 2-41
transfer of control ................ 2-21 constants ......................2-3, 2-4, 2-5
variables................................. 2-5 context switches......................... 7-3
vocabulary ............................. 2-1
D
4
DAL overview .................... 6-1, 6-2
4GL programming features Data Access Layer
automatic import of variables ...... communication with STP and
......................................... 3-27 CDAS .............................. 6-18
event sections......................... 3-2 data access methods............. 6-16
flow of standard program..... 3-21 hooks...................................... 6-5
overview ................................ 3-1 object hooks.................... 6-7, 6-8
property hooks ....................... 6-6
A
property methods ................. 6-17
after.commit.transaction() ....... 6-15 query extensions .................. 6-17
after.destroy.object()................ 6-12 terminology............................ 6-3
after.get.object() ...................... 6-10 transition issues (BAAN IV to
after.save.object() .................... 6-14 BaanERP) ........................ 6-18
arithmetic operators ................. 2-17 UI DAL and STP interaction . 6-3
ASCII table.............................. 12-1 database handling
assignment operator................. 2-20 error handling ...................... 5-39
hints for using db.retry.point 5-43
B
hints for using SQL.............. 5-41
based variables......................... 2-12 overview ................................ 5-1
before.destroy.object()............. 6-12 transaction handling............... 5-5
before.get.object()...................... 6-9 using program variables....... 5-15
before.open.object.set() ............. 6-9 debugger .................................... 9-1
before.save.object() ................. 6-13 debugger commands .................. 9-1
debugger mouse actions............. 9-7
delayed locks .................... 5-7, 5-31

Programming features
13-1
Index

dynamic-link libraries................ 8-1 N


E NULL characters ............ 2-15, 2-16
embedded SQL ........................ 5-27 O
error codes ............................... 10-1
ORDER BY clause .................. 5-14
errors............................... 10-1, 10-6
events.................................. 7-5, 7-6 P
external variables..................... 2-31
page()......................................... 4-7
F pragma codes .................. 2-40, 2-41
predefined variables................... 4-5
fixed variables ......................... 2-12
preprocessor............................. 2-36
FROM clause........................... 5-11
R
G
REFERS TO clause ................. 5-20
global variables........................ 2-30
relational operators .................. 2-18
GOTO statement...................... 2-21
report scripts
GROUP BY clause .................. 5-13
event sections......................... 4-1
H expanding text variables ........ 4-8
functions ................................ 4-7
HAVING clause ...................... 5-14
overview ................................ 4-1
I program sections.................... 4-1
report sections................. 4-2, 4-3
include files ............................. 2-36
text field sections ................... 4-4
L reports........................................ 4-1
report scripts .......................... 4-1
layout.again()............................. 4-7
reset.suppress() .......................... 4-7
limits........................................ 11-1
retry points.......................... 5-8, 5-9
local variables.......................... 2-28
locking .........................5-5, 5-6, 5-7 S
logical operators ...................... 2-18
scheduler............................. 7-2, 7-3
M SELECT clause........................ 5-10
SELECT statement .................. 5-10
macro definition.............. 2-37, 2-38
SQL................................... 5-9, 5-10
main windows..................... 7-8, 7-9
SQL programming................... 5-27
method.is.allowed() ................. 6-15
SQL syntax .............................. 5-10
multitasking and the GUI .......... 7-1
SQL trace options .................... 5-32
overview ......................... 1-3, 7-1
static variables ......................... 2-28
requests
subqueries ................................ 5-26
inquiries
replies ............................ 7-7 W
synchronizing processes with the
wait()........................................ 7-13
wait() function ................. 7-13
WHERE clause ........................ 5-11

Programming features
13-2
Index

WHILE statement........... 2-25, 2-26

Programming features
13-3
Index

Programming features
13-2

S-ar putea să vă placă și