Progress 4gl PDF
Progress 4gl PDF
#progress-
4gl
Table of Contents
About 1
Remarks 2
Versions 2
Examples 2
Installation or Setup 3
Hello, World! 10
FizzBuzz 10
Commenting code 13
Program files 14
Chapter 2: Compiling 17
Introduction 17
Syntax 17
Examples 17
Application Compiler 17
COMPILE statement 21
Introduction 27
Examples 27
CASE 28
Introduction 31
Examples 31
Chapter 5: Functions 35
Introduction 35
Remarks 35
Examples 35
Simple function 35
Recursion 38
Chapter 6: Iterating 42
Introduction 42
Examples 42
DO WHILE 42
REPEAT 44
Chapter 7: OS-utilities 45
Introduction 45
Examples 45
OS-COMMAND 45
OPSYS 46
OS-ERROR 46
OS-GETENV function 47
OS-COPY 48
OS-DELETE 48
OS-CREATE-DIR 48
OS-APPEND 49
OS-RENAME 49
Introduction 51
Syntax 51
Examples 51
Scope 53
Chapter 9: Queries 55
Introduction 55
Syntax 55
Examples 55
Basic Query 55
Multi-Tables Query 56
Moving poisition withing a query using next, first, prev and last 57
Introduction 59
Remarks 59
Examples 59
Concatenating strings 59
String manipulation 59
CASE-SENSITIVE strings 63
Lists 66
Introduction 69
Examples 69
Introduction 75
Syntax 75
Examples 75
Introduction 79
Examples 79
Operators 79
Comparing numbers 80
Credits 82
About
You can share this PDF with anyone you feel could benefit from it, downloaded the latest version
from: progress-4gl
It is an unofficial and free progress-4gl ebook created for educational purposes. All the content is
extracted from Stack Overflow Documentation, which is written by many hardworking individuals at
Stack Overflow. It is neither affiliated with Stack Overflow nor official progress-4gl.
The content is released under Creative Commons BY-SA, and the list of contributors to each
chapter are provided in the credits section at the end of this book. Images may be copyright of
their respective owners unless otherwise specified. All trademarks and registered trademarks are
the property of their respective company owners.
Use the content presented in this book at your own risk; it is not guaranteed to be correct nor
accurate, please send your feedback and corrections to [email protected]
https://riptutorial.com/ 1
Chapter 1: Getting started with progress-4gl
Remarks
ABL (Advanced Business Language). Earlier known as Progress 4GL.
Progress ABL is a programming language tied to the Progress OpenEdge environment, its
database and surrounding utilities. This makes it a "fourth generation" programming language.
Progress ABL is a strongly typed, late-bound, English-like programming language with growing
support for object orientation. The compiled code is run by the "AVM" (ABL Virtual Machine).
The language is developed and maintained by the Progress Corporation (formerly Progress
Software).
Versions
Examples
https://riptutorial.com/ 2
Installation or Setup
Installing Progress
Download your distribution from Progress. If you want a demo license you need to contact them.
Make sure you download a 64-bit and not a 32-bit tar file (unless you happen to run a 32-bit
machine).
Windows
The download will be a zip archive. Unpack it and simply run setup.exe. The installation will be
graphical but otherwise exactly like the one described below.
Linux/Unix/HP-UX etc
Put the tar file on your Progress system. Let's say you have it in your home directory:
/home/user/PROGRESSFILENAME.tar
Extract it:
cd /home/user
tar xvf PROGRESSFILENAME.tar
proinst
Change directory to another destination and create a temporary directory there. For example:
cd /tmp
mkdir proinst116
cd proinst116
Once the installation is complete this directory will contain information about the installation as well
as files you can save and used for future automatic repetitions of the same installation.
/home/user/proinst/proinst
+-------------------------------------------------------------------+
| Welcome |
+-------------------------------------------------------------------+
| |
| WELCOME TO THE OPENEDGE INSTALLATION UTILITY |
| |
| Ensure that you have your completed "Preinstallation Checklist |
https://riptutorial.com/ 3
| for Unix" handy to consult. This information will facilitate your |
| installation and ensure your choices are accurately recorded. |
| |
| Copyright (c) 1984-2015 Progress Software Corporation |
| and/or one of its subsidiaries or affiliates. |
| All Rights Reserved. |
| |
| |
| [Enter=OK] |
+-------------------------------------------------------------------+
Now you will need to insert license keys, company name etc. It's recommended to download an
"addendum file" then you can simply press Ctrl+A and use it.
+----------------------------------------------------------------------+
| Product Configuration Data |
+----------------------------------------------------------------------+
| [Enter=Additional] |
| Company Name: ______________________________ [Ctrl-E=Done] |
| Serial Number: _________ [CTRL-T=Quit] |
| Control Number: _____ _____ _____ [CTRL-N=Release Notes]|
| [CTRL-V=View] |
| [TAB=Next Field] |
| [CTRL-P=Help] |
| [CTRL-A=Addendum File]|
| |
+----------------------------------------------------------------------+
Adding an addendum-file:
+---------------------------------------------------------------------------+
| License Addendum File |
+---------------------------------------------------------------------------+
| |
| Enter Path: /home/myuser/myfile.txt______________________________________ |
| |
| |
| |
| [Enter=OK] [CTRL-N=Cancel] |
+---------------------------------------------------------------------------+
| [TAB=Next Field] |
| [CTRL-P=Help] |
| [CTRL-A=Addendum File]|
| |
+----------------------------------------------------------------------+
After you've added licenses manually or loaded them via a file you can press Ctrl+V to view
products to be installed:
+------------------------+
|Entered Product List |
+------------------------+
| 4GL Development System |
| OE Application Svr Ent |
+-----| OE Enterprise RDBMS |---------------------------------------+
| | OpenEdge Replication |nfiguration Data |
+-----+------------------------+---------------------------------------+
https://riptutorial.com/ 4
| [Enter=Additional] |
| Company Name: ______________________________ [Ctrl-E=Done] |
| Serial Number: _________ [CTRL-T=Quit] |
| Control Number: _____ _____ _____ [CTRL-N=Release Notes]|
| [CTRL-V=View] |
| [TAB=Next Field] |
| [CTRL-P=Help] |
| [CTRL-A=Addendum File]|
| |
+----------------------------------------------------------------------+
Once you're satisfied, press Ctrl+E to continue the installation or Ctrl+Q to quit.
+-----------------------------------------------------------------------+
| Done Configuration Data Confirmation |
+-----------------------------------------------------------------------+
| |
|Are you sure that you are done entering all the control numbers for the|
|OpenEdge products that will be installed? |
| |
| [Y=YES] [N=NO] |
+-----------------------------------------------------------------------+
| [CTRL-P=Help] |
| [CTRL-A=Addendum File]|
| |
+----------------------------------------------------------------------+
Depending on what you're installing you might need to set up different products during the
installation.
Next step is to decide if you want to enable the "OpenEdge Explorer". Y or N. This can be changed
later on.
+-------------------------------+
| Install Type and Destination |
+-------------------------------+
| Select Type of Installation |
| Select Destination Pathname |
| Select Management Pathname |
| Continue with Installation |
| View Release Notes |
| Cancel |
| Quit Installation |
| Help |
+-------------------------------+
+-----------------------------------------------------------------------------+
|Type: Complete Install |
|Destination pathname: /usr/dlc |
|Working Dir pathname: /usr/wrk |
https://riptutorial.com/ 5
|Management pathname: /usr/oemgmt |
|Management Working Dir pathname: /usr/wrk_oemgmt |
| |
+-----------------------------------------------------------------------------+
Now you have to decide directories where you want to install Progress as well as primary working
directory (basically where you want to store your code). Change these or move on with the
defaults. Historically /usr/dlc has always been the default so you might want to change this to
something thats unique for this specific version of Progress - that might help when upgrading.
Choose a Complete Install (the default).
Once done: choose Continue with Installation using arrow keys and press enter to continue.
+---------------------------------------------------+
| Select Server Engine |
+---------------------------------------------------+
|*SQL -Provides SQL access to OpenEdge data files |
| Continue with Install |
| Cancel |
| Help |
+---------------------------------------------------+
If you're not planning any SQL access you can press enter once and remove the * before SQL,
otherwise just Continue with Install.
+---------------------------------------------------------------------------+
| ATTENTION |
+---------------------------------------------------------------------------+
| |
|The OpenEdge Adapter for Sonic ESB is a recommended component of this |
|installation and requires a Sonic ESB installation somewhere on your |
|network. |
| |
|Choose YES if you plan on using OpenEdge Adapter for Sonic ESB, else choose|
|NO. |
| |
| [Y=YES] [N=NO] [H=Help] |
+---------------------------------------------------------------------------+
Most likely you do not need the OpenEdge Adapter for Sonic ESB so press N - otherwise you know
what to do.
+-----------------------------------------------------------------+
| ATTENTION |
+-----------------------------------------------------------------+
| |
|WebSpeed is a recommended component of this installation and |
|requires a Web Server installed somewhere on your network. |
| |
|Choose YES if you plan on using WebSpeed and you are installing |
|on the system where your Web Server is installed, else choose NO.|
| |
| [Y=YES] [N=NO] [H=Help] |
+-----------------------------------------------------------------+
https://riptutorial.com/ 6
If you plan on using WebSpeed for producing dynamic HTML press Y, otherwise N.
+------------------------------------+
| Web Server Type |
+------------------------------------+
| Select Web Server Type |
| Select Web Server Script directory |
| Copy the static HTML to docroot |
| Continue with Installation |
| Cancel |
| Quit Installation |
| Help |
+------------------------------------+
Setting up WebSpeed: Choose Select Web Server Type and set it to cgi (most likely anyway). Web
server script directory can be set to your servers cgi-bin directory or something like /tmp. Don't
copy the static HTML - it's really outdated. Continue!
+----------------------------+
|Language Selection |
+----------------------------+
| Chinese (Simplified) |
| Czech |
| Dutch |
| English - American |
| English - International |
| French |
| German |
| Italian |
| Polish |
| Portuguese - Brazilian |
| Spanish |
| Portuguese |
| Swedish |
| Spanish - Latin |
| Make Default |
| Continue with Installation |
| Cancel |
| Help |
+----------------------------+
Choose English unless you really need something else, you can actually select more than one -
make one default in that case. Continue!
+------------------------------------+
| International Settings |
+------------------------------------+
| Select CharacterSet,Collation,Case |
| Select a Date Format |
| Select a Number Format |
| Continue with Installation |
| Cancel |
| Quit Installation |
| Help |
+------------------------------------+
| Polish |
| Portuguese - Brazilian |
https://riptutorial.com/ 7
| Spanish |
| Portuguese |
| Swedish |
| Spanish - Latin |
| Make Default |
+-----------------------------------------------------------------------------+
| |
| CharacterSet,Collation,Case: ISO8859-1, Swedish, Basic |
| Date Format: ymd |
| Number Format: 1.234,56 (period, comma) |
+-----------------------------------------------------------------------------+
For the Itnernational Settings you should try and match any previous installations to help yourself
in the future. Otherwise you can set it to something that fits your own needs. This can be changed
in the future. Use UTF-8 if you want.
+---------------------------------------------------------------------------+
| Web Services Adapter URL |
+---------------------------------------------------------------------------+
| Please enter the URL of where you will configure the sample |
| Web Services Adapter's Java Servlet. |
| |
| URL: http://fedora-1gb-ams3-01.localdomain:80/wsa/wsa1___________________ |
| |
| [Enter=OK] [CTRL-N=Cancel] [CTRL-P=Help] |
+---------------------------------------------------------------------------+
Leave the defaults for the Web Services adapter URL unless you have a good reason.
+-------------------------------------------------------------------------+
| WSA Authentication |
+-------------------------------------------------------------------------+
| |
|Would you like to Disable the Web Services Adapter's administration user |
|authentication? |
| |
| [Y=YES] [N=NO] [H=Help] |
+-------------------------------------------------------------------------+
+-------------------------------------------------------------------------+
| Complete Installation |
+-------------------------------------------------------------------------+
| |
|The following products will be installed: |
|'4GL Development System (x USERS)', 'OE Application Svr Ent (y USERS)', |
|'OE Enterprise RDBMS (z USERS)', 'OpenEdge Replication (u USERS)' |
| |
|Disk Space Required for Products: 1,138,163,712 bytes |
|Disk Space Required for Installation: 1,139,343,360 bytes |
|Disk Space Remaining After Installation: 26,534,129,664 bytes |
| |
|Selected Destination Path: /usr/dlc |
| |
|Do you want to install the above listed product(s)? |
| |
https://riptutorial.com/ 8
| [Y=YES] [N=NO] [H=Help] |
+-------------------------------------------------------------------------+
+-----------------------------+
| Copy Scripts? |
+-----------------------------+
| |
|Copy the scripts to /usr/bin?|
| |
| [Y=YES] [N=NO] [H=Help] |
+-----------------------------+
If you choose to do this you might want to make sure there isn't a previous install being
overwritten.
+----------------------------------------------------------------------------+
| Installing Files |
+----------------------------------------------------------------------------+
| |
| Installing subcomponent: Common Files (m) |
| Installing file: libjvm.so |
| 17% |
| +------------------------------------------------------------------------+ |
| | | |
| +------------------------------------------------------------------------+ |
| |
| [CTRL-T=Quit] |
+----------------------------------------------------------------------------+
+-------------------------------------------------------------------------+
| Configuring WebSpeed |
+-------------------------------------------------------------------------+
| |
| a. Set up and start your Web server |
| - If you did not select to "Copy static HTML files to |
| Document Root directory", then manually copy the files |
| or set a link. |
| - For NSAPI Messenger, edit the "obj.conf" and "start" files |
| on the Web server. |
| b. Set up the Broker machine. |
| - Set environment variables if necessary. |
| - Edit the properties file (ubroker.properties), then start Broker. |
| c. To validate your configuration through the Messenger |
| Administration Page, enter ?WSMAdmin after the Messenger name |
| in a URL. |
| (For example, for CGI, http://hostname/cgi-bin/wspd_cgi.sh?WSMAdmin) |
| (For example, for NSAPI, http://hostname/wsnsa.dll?WSMADmin) |
| |
|See the "OpenEdge Application Server: Administration" guide for details. |
| |
| [Enter=OK] [H=Help] |
+-------------------------------------------------------------------------+
https://riptutorial.com/ 9
Some information about WebSpeed.
+---------------------------------------------------------+
|Installation of selected OpenEdge products is complete. |
|Refer to the installation notes for more information. |
+---------------------------------------------------------+
| End the OpenEdge Installation |
| View Release Notes |
| Help |
+---------------------------------------------------------+
Silent installation
The installation has stored a file named /usr/dlc/install/response.ini (or your installation
directory). This file can be used to repeat the exact same installation again in a "silent" install that
can be scriptet and run without any interaction.
Hello, World!
Once you've started your Progress editor of choice (there are a couple of options) simply write:
On Windows in Developer Studio: alt+shift+X, followed by G (Run -> Run As Progress OpenEdge
Application)
FizzBuzz
DO i = 1 TO 100:
/* Dividable by 3: fizz */
https://riptutorial.com/ 10
IF i MODULO 3 = 0 THEN
cOut = "Fizz".
/* Dividable by 5: buzz */
ELSE IF i MODULO 5 = 0 THEN
cOut = "Buzz".
/* Otherwise just the number */
ELSE
cOut = STRING(i).
Linux/Unix
You only need a couple of environment variables. The directory where Progress was installed
(default /usr/dlc but can be something else) needs to be in the DLC-variable
DLC=/usr/dlc
And you might also want the "bin" subdirectory of DLC in your PATH:
PATH=$PATH:$DLC/bin
Theres also a script installed called proenv that will do this (and a little bit more) for you. It's default
location is /usr/dlc/bin/proenv.
Some utilites:
showcfg
pro
This will start the "Procedure Editor" where you can edit and run your programs.
pro program.p
Will open program.p for editing if it exists. Otherwise an error will be displayed.
pro -p program.p
https://riptutorial.com/ 11
This will run "program.p". If there's a compiled file (program.r) present it will be run, otherwise it will
be temporarily compiled and after that executed. The compiled file will not be saved.
This shows how to create the demo database used in big parts of Progress documentation:
sports2000.
This assumes you have installed the Progress products with at least one type of database license.
Run proenv script/bat-file that will give you a prompt with all environment variables set.
Create a directory.
This example is for Windows. Directory handling etc might be different in another OS.
proenv> cd \
proenv> mkdir db
proenv> cd db
proenv> mkdir sports2000
proenv> cd sports2000
Syntax of prodb:
This will create a database called "mySportsDb" in the current directory. That database is an exact
copy of the sports2000 database that's shipped with the Progress install. Since the source
sports2000 database is located in the Progress install directory you don't need to specify path.
If you look at the directory content you will see some files:
proenv> dir
2017-01-12 20:24 2 228 224 mySportsDb.b1
2017-01-12 20:24 1 703 936 mySportsDb.d1
2017-01-12 20:24 32 768 mySportsDb.db
2017-01-12 20:24 2 951 mySportsDb.lg
2017-01-12 20:07 368 mySportsDb.st
2017-01-12 20:24 327 680 mySportsDb_10.d1
2017-01-12 20:24 65 536 mySportsDb_10.d2
2017-01-12 20:24 1 310 720 mySportsDb_11.d1
2017-01-12 20:24 1 376 256 mySportsDb_11.d2
2017-01-12 20:24 327 680 mySportsDb_12.d1
2017-01-12 20:24 65 536 mySportsDb_12.d2
2017-01-12 20:24 327 680 mySportsDb_7.d1
2017-01-12 20:24 65 536 mySportsDb_7.d2
2017-01-12 20:24 655 360 mySportsDb_8.d1
2017-01-12 20:24 655 360 mySportsDb_8.d2
2017-01-12 20:24 327 680 mySportsDb_9.d1
https://riptutorial.com/ 12
2017-01-12 20:24 65 536 mySportsDb_9.d2
File
Contains
name
.lg The database log file. Contains logging information in text format
.st The database structure file. Describe the storage layout in a text format
The actual data. Different files store data of different formats. The .st file can tell
.d?
what format
Now you can access the database directly by simply typing pro mySportsDb. This will start a
Progress Editor that's connected to the database. This will be a single user connection so nobody
else will be able to access the database at the same time.
To access the database. Press Ctrl+X to execute. This will display all contents of the "bill" table. If
you want to cancel you can press Ctrl+C.
Commenting code
/*
In all versions of
Progress ABL you can write
multi line comments
*/
//Starting with version 11.6 you can also write single line comments
string = "HELLO". //A single line comment can be written after some code
https://riptutorial.com/ 13
/* but then all nested comments must be terminated */ or the compiler
will generate an error */
Formally the single line comment starts with the double slash // and ends with a newline, carriage
return or end-of-file.
Program files
Progress ABL code is normally stored in files with different ending depending on what they
contain. The endings are optional but rather a defacto standard:
Filename
Contains
extension
.r The compiled result of any file containing Progress 4GL. Called r-code.
RUN program. //Will run program.r if present otherwise internal procedure "program".
Once the sports2000 database has been installed it's time to run it as a standalone server (and
not connect to it as a file).
https://riptutorial.com/ 14
This example is from Windows. Linux is the same but you need to change paths etc to match your
install.
proenv> cd \db\sports2000
proenv> proserve mySportsDb -H localhost -S 9999
OpenEdge Release 11.6 as of Fri Oct 16 19:01:51 EDT 2015
20:09:54 BROKER This broker will terminate when session ends. (5405)
20:09:54 BROKER The startup of this database requires 17Mb of shared memory. Maximum
segment size is 128Mb.
20:09:54 BROKER 0: Multi-user session begin. (333)
20:09:55 BROKER 0: Begin Physical Redo Phase at 0 . (5326)
20:17:36 BROKER 0: Before Image Log Initialization at block 1 offset 5300. (15321)
20:09:55 BROKER 0: Login by xyz on CON:. (452)
20:09:55 BROKER 0: Started for 9999 using TCP IPV4 address 127.0.0.1, pid 2892. (5644)
proenv>
This will start the mySportsDb on localhost and use port 9999 as primary port for database
access. If you want to connect to this database from another client on the same network or
elsewhere localhost wont work. Use your IP-address or hostname instead:
Once your database is up and running you can connect to it in your Progress editor:
or
If you get an error message you have either gotten some information wrong in the command or the
database isn't up and running. You could also have a software firewall or similar interfering.
You can check the database logfile (mySportsDb.lg in this example) for any clues.
DISCONNECT mySportDb.
or
DISCONNECT "mySportsDb".
To shut the database down you can run the proshut command from proenv:
https://riptutorial.com/ 15
proenv> proshut mySportsDb
OpenEdge Release 11.6 as of Fri Oct 16 19:01:51 EDT 2015
usr pid time of login user id Type tty Limbo?
24 7044 Wed Feb 01 20:22:57 2017 xyz REMC XYZ-PC no
1 Disconnect a User
2 Unconditional Shutdown
3 Emergency Shutdown (Kill All)
x Exit
You can also shutdown the database directly from the command line:
Or disconnect a user from command line (assuming you know it's user number, usr in the list
above):
https://riptutorial.com/ 16
Chapter 2: Compiling
Introduction
Compile Progress code as called "r-code" and is normally saved in a file with the extension .r.
There are a couple of different ways of compiling: using the COMPILE statement or on Linux or
AppBuilder: the built in Application Compiler. Developer Studio (the Eclipse environment) has
compiling built into it's build process.
You must have 4GL Development or OpenEdge Studio installed to compile 4GL programs which
update the database.
Syntax
• COMPILE program.p SAVE. //Compile program.p and save it's r-code
• COMPILE VALUE(var) SAVE. //Compile the named saved in the variable "var" and save it's
r-code
• COMPILE prog.p XREF prog.xref LISTING prog.list. //Compile prog.p and create xref and
listing-files. Don't save the r-code.
• COMPILE program.p SAVE NO-ERROR. //Compile program.p, save r-code and supress
errors to stop the execution.
Examples
Application Compiler
Windows AppBuilder
In the Windows Appbuilder the Application Compiler is found in the Tools Menu.
https://riptutorial.com/ 17
Procedure Editor (Linux - pro or Windows pro.exe
In the Procedure Editor (both Linux and Windows) the Compiler if found in the Tools menu.
Application Compiler
https://riptutorial.com/ 18
Regardless of OS the functionality of the compiler is the same. You can add directories and/or files
and compile them.
• Save new .r File. If not checked the files will simply be compiled but not saved. Useful for
error tracking for instance.
• Look in Subdirectories. Otherwise subdirectories will have to be added.
• Remove old .r Files. Overwrite old .r file.
• Onlu Compile if No .r File. Only compiles uncompiled files.
https://riptutorial.com/ 19
Options:
• Propath - shows you the propath and let's you select directories to compile from it.
• Add - lets you input a directory or file.
• Modify - lets you modify an existing entry.
• Delete - Deletes an entry.
• Start Compile - Starts the compiler. Shortcut: F2
Settings
https://riptutorial.com/ 20
• Xref File: If you want the compiler to create a XREF. Useful for debugging, checking index
usage etc.
• XML Format: If the compiler xref should be an xml. Otherwise "plain" text.
• Append: add to the existing xref file. Otherwise overwrite.
• Debug File: Debugger listing file.
• Encryption Key: If the source file is encrypted using xcode insert the key here.
• Minimize R-code Size: Remove some debugging information to keep the r-code small.
• Generate MD-5: Mostly for WebClient compiling.
Basic usage
COMPILE statement
Basic usage:
With a variable:
prog = "hello.p".
SAVE states that the .r-code should be saved for future use.
SAVE INTO dir OR SAVE INTO VALUE(dir-variable) saves the r-code in the specified directory:
LISTING file. Creates a listing file containing debug information regarding blocks, includes etc.
Listing has a couple of options for appending files, page-size and page-width:
https://riptutorial.com/ 21
APPEND PAGE-SIZE num PAGE-WIDTH num
XREF xreffilewill save a compiler xref file containing information about string and index usage etc.
You can also APPEND this one.
XREF-XML xreffile-or-dir will do the same thing as XREF but save the file in an xml-format instead. If
you use a directory the xref-file will be named programname.xref.xml.
PREPROCESS file will first translate all preprocessors and then create a new .p-file with the code
prior to compiling.
will compile an encrypted source code with key as key. You cannot use XCODE with the
XCODE key
XREF, XREF-XML, STRING-XREF, or LISTING options together.
COMPILE prog.p SAVE INTO /usr/r-code XREF /usr/xrefs/xref.txt APPEND LISTING /usr/listings.txt
APPEND NO-ERROR.
The COMPILER system handle let's you look at information regarding a recent compile.
MESSAGE
"Errors: " COMPILER:ERROR SKIP
"Warnings: " COMPILER:WARNING SKIP
https://riptutorial.com/ 22
"Messages: " COMPILER:NUM-MESSAGES
VIEW-AS ALERT-BOX INFORMATION.
/* program-with-warning.p */
DEFINE VARIABLE c AS CHARACTER NO-UNDO.
DEFINE VARIABLE i AS INTEGER NO-UNDO.
c = "hello".
DISPLAY c.
//This RETURN makes the program exit here and the code below unreachable.
RETURN.
MESSAGE
"Errors: " COMPILER:ERROR SKIP
"Warnings: " COMPILER:WARNING SKIP
"Messages: " COMPILER:NUM-MESSAGES
VIEW-AS ALERT-BOX INFORMATION.
DO iError = 1 TO COMPILER:NUM-MESSAGES:
DISPLAY
COMPILER:GET-FILE-NAME(iError) LABEL "Filename" FORMAT "x(20)"
COMPILER:GET-MESSAGE(iError) LABEL "Message" FORMAT "x(50)"
COMPILER:GET-NUMBER(iError) LABEL "Msg#"
COMPILER:GET-ERROR-COLUMN(iError) LABEL "Column"
COMPILER:GET-ERROR-ROW(iError) LABEL "Row"
WITH FRAME fr1 SIDE-LABELS 1 COLUMNS.
END.
Result:
https://riptutorial.com/ 23
Compiling a program with an error
c = "hello".
DISPLAY c.
//Casting should be required below...
IF TRUE THEN DO:
i = c.
END.
https://riptutorial.com/ 24
//Use no-errors to supress any error messages from interrupting us.
COMPILE c:\temp\program-with-error.p SAVE NO-ERROR.
MESSAGE
"Errors: " COMPILER:ERROR SKIP
"Warnings: " COMPILER:WARNING SKIP
"Messages: " COMPILER:NUM-MESSAGES
VIEW-AS ALERT-BOX INFORMATION.
DO iError = 1 TO COMPILER:NUM-MESSAGES:
DISPLAY
COMPILER:GET-FILE-NAME(iError) LABEL "Filename" FORMAT "x(20)"
COMPILER:GET-MESSAGE(iError) LABEL "Message" FORMAT "x(50)"
COMPILER:GET-NUMBER(iError) LABEL "Msg#"
COMPILER:GET-ERROR-COLUMN(iError) LABEL "Column"
COMPILER:GET-ERROR-ROW(iError) LABEL "Row"
WITH FRAME fr1 SIDE-LABELS 1 COLUMNS 20 DOWN.
Result, there's almost always two errors per error. "Could not understand" is followed by the actual
error:
https://riptutorial.com/ 25
Read Compiling online: https://riptutorial.com/progress-4gl/topic/9029/compiling
https://riptutorial.com/ 26
Chapter 3: Conditional statements
Introduction
Progress ABL supports two contitional statements: IF/THEN/ELSE and CASE.
Examples
IF ... THEN ... ELSE-statement
In the IF THEN ELSE statement the result can be either a single statement:
IF i = 0 THEN
MESSAGE "Zero".
ELSE
MESSAGE "Something else".
IF i = 0 THEN DO:
RUN procedure1.
RUN procedure2.
END.
ELSE DO:
RUN procedure3.
RUN procedure4.
END.
IF i = 0 THEN DO:
RUN procedure1.
RUN procedure2.
END.
ELSE IF i = 1 THEN DO:
RUN procedure3.
RUN procedure4.
END.
ELSE DO:
RUN procedure5.
RUN procedure6.
END.
https://riptutorial.com/ 27
DEFINE VARIABLE l AS LOGICAL NO-UNDO.
l = TRUE.
The IF/ELSE IF can compare several conditionals, with or without internal connections. This leaves
you free to mess up your code in several ways:
END.
ELSE IF i > 30 AND l = FALSE OR TODAY = DATE("2017-08-20") THEN DO:
END.
ELSE DO:
MESSAGE "I dont really know what happened here".
END.
CASE
The CASE-statement is a lot more strict than the IF/ELSE-conditional. It can only compare a single
variable and only equality, not larget/smaller than etc.
CASE c:
WHEN "A" THEN DO:
RUN procedureA.
END.
WHEN "B" THEN DO:
RUN procedureB.
END.
OTHERWISE DO:
RUN procedureX.
END.
END CASE.
CASE c:
WHEN "A" THEN DO:
RUN procedureA.
END.
WHEN "B" OR WHEN "C" THEN DO:
RUN procedureB-C.
END.
OTHERWISE DO:
https://riptutorial.com/ 28
RUN procedureX.
END.
END CASE.
Just like with the IF-statement each branch can either be a single statement or a block. Just like
with the ELSE-statement, OTHERWISE is not mandatory.
CASE c:
WHEN "A" THEN
RUN procedureA.
WHEN "B" OR WHEN "C" THEN
RUN procedureB-C.
END CASE.
Unlike a c-style switch-clause there's no need to escape the CASE-statement - only one branch will
be executed. If several WHENs match only the first one will trigger. OTHERWISE must be last and will
only trigger if none of the branches above match.
c = "A".
CASE c:
WHEN "A" THEN
MESSAGE "A" VIEW-AS ALERT-BOX. //Only "A" will be messaged
WHEN "A" OR WHEN "C" THEN
MESSAGE "A or C" VIEW-AS ALERT-BOX.
END CASE.
IF THEN ELSE can also be used like a function to return a single value. This is a lot like the ternary ?-
operator of C.
The value of the IF-part and the value of the ELSE-part must be of the same datatype. It's not
possible to use ELSE IF in this case.
https://riptutorial.com/ 29
DEFINE VARIABLE dat AS DATE NO-UNDO.
DEFINE VARIABLE beforeTheFifth AS LOGICAL NO-UNDO.
dat = TODAY.
https://riptutorial.com/ 30
Chapter 4: FIND statement
Introduction
The FIND statement is used to retrieve a single record from a single table. It has some limitations
compared to FOR or QUERY, but it's a simple and handy statement for fast access to records.
Examples
FIND basic examples
NO-LOCK - don't lock the record - meaning we will only read and not change the
record.
findLoop:
REPEAT :
FIND NEXT Customer NO-LOCK WHERE NAME BEGINS "N" NO-ERROR.
The latest find is always the one the availability check will work against - a unsuccesful find will
make AVAILABLE return false:
https://riptutorial.com/ 31
DEFINE TEMP-TABLE tt NO-UNDO
FIELD nr AS INTEGER.
CREATE tt.
tt.nr = 1.
CREATE tt.
tt.nr = 2.
CREATE tt.
tt.nr = 3.
PROCEDURE av:
DISPLAY AVAILABLE tt.
CREATE tt.
tt.nr = 1.
CREATE tt.
tt.nr = 2.
Also, a record found in a procedure will still be available after that procedure has exited.
PROCEDURE av:
https://riptutorial.com/ 32
FIND FIRST tt WHERE tt.nr = 1.
END PROCEDURE.
CREATE tt.
tt.nr = 1.
CREATE tt.
tt.nr = 2.
PAUSE.
RUN av.
NO-LOCK: Used for read only operations. If you do a FIND <record> NO-LOCK you cannot in any way
modify the record.
EXCLUSIVE-LOCK: Used for updates and deletes. If you do this you will "own" the record and
nobody else can modify it or delete it until you're done. They can read it (with no-lock) as long as
you haven't deleted it.
You can easily move from a NO-LOCK to an EXCLUSIVE-LOCK if the need to modify a record has arisen:
https://riptutorial.com/ 33
You can go from EXCLUSIVE-LOCK to NO-LOCK as well.
LOCKED RECORDS
Whenever other users might aquire a lock of a record you better take this possibility into account.
Collisions will occur!
It's better to handle this programmatically using the NO-WAIT statement. This tells the AVM to just
pass the FIND if the record is locked by somebody else and let you handle this problem.
https://riptutorial.com/ 34
Chapter 5: Functions
Introduction
A user defined function in Progress ABL is a reusable program module.
Remarks
• A function must be declared in the "main" procedure. It cannot be declared inside a
procedure or inside another function.
• A function in Progress ABL isn't a "first class citizen" unlike in programming languages like
Haskell or Javascript. You cannot pass a function as an input or output parameter. You can
however invioke them dynamically using DYNAMIC-FUNCTION or the CALL object.
• Calling functions in your queries can lead to bad performance since index matching will hurt.
Try to assign the value of the function to a variable and use that variable in the WHERE-clause
instead.
Examples
Simple function
/* This function returns TRUE if input is the letter "b" and false otherwise */
FUNCTION isTheLetterB RETURNS LOGICAL (INPUT pcString AS CHARACTER):
IF pcString = "B" THEN
RETURN TRUE.
ELSE
RETURN FALSE.
END FUNCTION.
https://riptutorial.com/ 35
A function can be forward declared, this is similar to specifications in a C header file. That way the
compiler knows that a function will be made available later on.
Without forward declarations the function MUST be declared before it's called in the code. The
forward declaration consists of the FUNCTION specification (function name, return type and
parameter data types and order). If the forward declaration doesn't match the actual function the
compiler will produce errors and the code will fail to run.
DISPLAY dividableByThree(9).
END.
A function can have multiple return statements and they can be placed in different parts of the
actual function. They all need to return the same data type though.
A function actually don't have to return anything at all. Then it's return value will be ? (unknown).
The compiler will not catch this (but your colleagues will so avoid it).
https://riptutorial.com/ 36
/* This function will only return TRUE or ?, never FALSE, so it might lead to troubles */
FUNCTION inTheFuture LOGICAL ( dat AS DATE):
IF dat > TODAY THEN DO:
RETURN TRUE.
END.
END.
MESSAGE inTheFuture(TODAY + RANDOM(-50, 50)) VIEW-AS ALERT-BOX.
A function can only return a single value but there's one way around that: the parameters are not
limited to input parameters. You can declare INPUT, OUTPUT and INPUT-OUTPUT parameters.
Unlike INPUT parameters you must specify OUTPUT or INPUT-OUTPUT before the parameters.
Some coding conventions might not like this but it can be done.
procStatus = "processing".
n = n + i.
procStatus = "done".
RETURN n.
END.
https://riptutorial.com/ 37
Here's an example of an INPUT-OUTPUT parameter:
/* Function doubles a string and returns the length of the new string */
FUNCTION doubleString RETURN INTEGER (INPUT-OUTPUT str AS CHARACTER).
RETURN LENGTH(str).
END.
str = "HELLO".
MESSAGE
"New string: " str SKIP
"Length: " len VIEW-AS ALERT-BOX.
Recursion
See recursion
IF num = 1 THEN
RETURN 1.
ELSE
RETURN num * factorial(num - 1).
END FUNCTION.
DISPLAY factorial(5).
With standard settings (startup parameter) the Progress session wont be able to handle very large
numbers in this example. factorial(200) will fill the stack and raise an error.
https://riptutorial.com/ 38
/* Move up (y coordinates - steps ) */
FUNCTION moveU LOGICAL (INPUT steps AS INTEGER):
IF posY = 0 THEN
RETURN FALSE.
RETURN TRUE.
END FUNCTION.
IF posY = 20 THEN
RETURN FALSE.
END FUNCTION.
IF posX = 0 THEN
RETURN FALSE.
RETURN TRUE.
END FUNCTION.
IF posX = 20 THEN
RETURN FALSE.
END FUNCTION.
REPEAT:
https://riptutorial.com/ 39
IF CHR(LASTKEY) = "q" THEN LEAVE.
IF CAPS(CHR(LASTKEY)) = "s" THEN UPDATE step WITH FRAME x1.
ELSE DO:
moved = DYNAMIC-FUNCTION("move" + CAPS(CHR(LASTKEY)), INPUT step).
IF moved = FALSE THEN
MESSAGE "Out of bounds".
END.
END.
END.
The CALL object is not as lightweight as the DYNAMIC-FUNCTION. It can be used to call different
things: functions, procedures, external program, Windows DLL-functions. It can also invoke
methods on objects and access getters/setters.
DO i = LENGTH(txt) TO 1 BY -1:
txtBackwards = txtBackwards + SUBSTRING(txt, i, 1).
END.
END FUNCTION.
https://riptutorial.com/ 40
Read Functions online: https://riptutorial.com/progress-4gl/topic/8857/functions
https://riptutorial.com/ 41
Chapter 6: Iterating
Introduction
There are several ways of iterating (looping) in Progress ABL.
Examples
DO WHILE
A DO WHILE loop will continue to loop unless the WHILE-part is met. This makes it easy to run forever
and eat up all time from one CPU core.
DO WHILE expression:
END.
expression is any combination of boolean logic, comparisons, variables, fields etc that
evaluates to a true value.
/* This is a well defined DO WHILE loop that will run as long as i is lower than 10*/
DEFINE VARIABLE i AS INTEGER NO-UNDO.
DO WHILE i < 10:
i = i + 1.
END.
However, the compiler wont help you so check that the WHILE-part eventually is met:
This iteration changes a value from a starting point to an end, optionally by a specified value for
https://riptutorial.com/ 42
each step. The default change is 1.
DO i = 10 TO 15:
DISPLAY i WITH FRAME x1 6 DOWN .
DOWN WITH FRAME x1.
END.
Result:
---------------i
10
11
12
13
14
15
END.
And over decimals. But then you most likely want to use BY - otherwise an INTEGER would have
done just as fine...
Using BY a negative number you can also go from a higher to a lower value:
DO i = 5 TO 1 BY -1:
END.
The expression will be tested until it's no longer met. This makes the counter be higher (if moving
upwards) or lower (if moving downwards) once the loop is finished:
DO i = 5 TO 1 BY -1:
END.
https://riptutorial.com/ 43
MESSAGE i. // Will message 0
Another example:
END.
DISPLAY da. //17/02/17 (TODAY + 11)
REPEAT
REPEAT, will repeat itself forever unless you explicitly exit the loop:
//Runs forever
REPEAT:
// Do stuff
END.
To exit the loop you can use the LEAVE keyword. With or without a label. Without a label LEAVE will
always effect the current loop. With a name you can specify what loop to LEAVE.
/* Without a label */
REPEAT:
//Do stuff
IF TRUE THEN LEAVE.
END.
/* With a label */
loopLabel:
REPEAT:
//Do stuff
IF <somecondition> THEN LEAVE loopLabel.
END.
https://riptutorial.com/ 44
Chapter 7: OS-utilities
Introduction
There are several built in functions and statements for accessing the operating system.
Examples
OS-COMMAND
Executes a OS-command.
OS-COMMAND without any options will start a new shell and not exit it - thus you will on graphical
OS:es leave a window "hanging".
cmd = "dir".
OS-COMMAND VALUE(cmd).
SILENT
After processing an operating system command, the AVM shell pauses. To exit the
window in Windows GUI platforms, you must type exit. To exit the window in Windows
character platforms, you must type exit and press RETURN or SPACEBAR. You can
use the SILENT option to eliminate this pause. Use this option only if you are sure that
the program, command, or batch file does not generate any output to the screen.
Cannot be used with NO-WAIT.
NO-WAIT
On Linux/Unix you will have to achieve this by preceding the command with a &-sign instead:
https://riptutorial.com/ 45
NO-CONSOLE
While processing an operating system command, the AVM creates a console window.
The console window may not be cleaned up after the command is executed. You can
use the NO-CONSOLE option to prevent this window from being created in the first
place.
No errors are ever returned from OS-COMMAND to Progress ABL so you have to check for errors
another way, possibly writing them to a file in a shell-script or similar.
OPSYS
Result:
OS-ERROR
Returns an error from a previous OS-* call represented by an integer. The calls that can return an
OS-ERROR are:
• OS-APPEND
• OS-COPY
• OS-CREATE-DIR
• OS-DELETE
• OS-RENAME
• SAVE CACHE
Note that OS-COMMAND is missing. You need to handle errors in OS-COMMAND yourself.
https://riptutorial.com/ 46
Error number Description
0 No error
1 Not owner
4 I/O error
6 No more processes
8 Permission denied
9 Bad address
10 File exists
11 No such device
12 Not a directory
13 Is a directory
OS-GETENV function
On a Windows machine:
https://riptutorial.com/ 47
MESSAGE OS-GETENV ("SHELL") VIEW-AS ALERT-BOX.
OS-COPY
Copy a file
OS-DELETE
As with many other OS-* utilities, you have to check status in OS-ERROR.
OS-DELETE VALUE("c:\dir\file.txt").
OS-CREATE-DIR
https://riptutorial.com/ 48
Creates a directory, status is in OS-ERROR
OS-CREATE-DIR directory
OS-CREATE-DIR VALUE("/usr/local/appData").
OS-APPEND
OS-RENAME
Rename a file or directory. Status is in OS-ERROR. Can also be used to move files (or move and
rename).
https://riptutorial.com/ 49
On Linux the list will simply be empty as there by definitions are no "drives" connected. Listing
directories is done in another way (INPUT FROM OS-DIR)
https://riptutorial.com/ 50
Chapter 8: Procedures
Introduction
There are two types of procedures in Progress ABL: internal procedures and procedure prototypes
that are facades to Windows dlls or Unix/Linux shared library procedures.
Just like with functions, procedures cannot be nested. You cannot nest functions in procedures
and vice versa.
Syntax
• RUN procedurename. //Runs a procedure called procedurename.
• RUN proc2(INPUT var1, output var2). //Inputs var1 and outputs var2 to/from proc2
• RUN proc3(input "name = 'joe'", OUTPUT TABLE ttResult). //Inputs name=joe and outputs
records in a table
Examples
A basic internal procedure
Unlike functions, there's no need to forward declare a procedure. It can be placed anywhere in
your code, before or after you call it using RUN.
RUN proc.
The procedure name is folowed by a colon sign telling us that this is the start of a block. The block
ends with END PROCEDURE. (but this can be replaced with simply END.).
https://riptutorial.com/ 51
A procedure can have parameters of different kinds: input, output, input-output (bidirectional) and
also some special types like temp-tables and datasets).
In the run statement it's optional to declare INPUT (it's considered default) - all other directions must
be specifically declared.
PROCEDURE divideAbyB:
DEFINE INPUT PARAMETER piA AS INTEGER NO-UNDO.
DEFINE INPUT PARAMETER piB AS INTEGER NO-UNDO.
DEFINE OUTPUT PARAMETER pdeResult AS DECIMAL NO-UNDO.
END PROCEDURE.
Parameters are totally optional. You can mix and match any way you want. The order of the
parameters are up to you but it's handy to start with input and end with output - you have to put
them in the right order in the run statement and mixing directions can be annoying.
Recursion is easy - RUN the procedure itself from inside the procedure. However if you recurse too
far the stack will run out of space.
PROCEDURE factorial:
DEFINE INPUT PARAMETER piNum AS INTEGER NO-UNDO.
DEFINE OUTPUT PARAMETER piFac AS INTEGER NO-UNDO.
END PROCEDURE.
DISPLAY f.
https://riptutorial.com/ 52
Scope
The procedure has it's own scope. The outside scope will "bleed" into the procedure but not the
other way arround.
PROCEDURE p:
j = 2.
END PROCEDURE.
RUN p.
Declaring a variable inside a procedure that has the same name as a parameter on the outside will
only create a local variable.
PROCEDURE p:
j = 2.
END PROCEDURE.
RUN p.
Any variable created on the inside of a procedure is accessible to that procedure only.
PROCEDURE p:
END PROCEDURE.
RUN p.
https://riptutorial.com/ 53
MESSAGE i VIEW-AS ALERT-BOX. // Unknown Field or Variable name i - error 201
https://riptutorial.com/ 54
Chapter 9: Queries
Introduction
The examples will be based on a copy of the demo database Sports 2000 provided with the setup
of Progress.
DEFINE the query and set what buffers (tables) and fields it works against.
OPEN the query with a specific WHERE-clause that defines how to retrieve the records. Possibly also
sorting (BY/BREAK BY)
GET the actual data - that can be the FIRST, NEXT, PREV (for previous) or LAST matching record.
Syntax
• DEFINE QUERY query-name FOR buffer-name. //General query definition for one buffer
• DEFINE QUERY query-name FOR buffer-name1, buffer-name2. //Joining two buffers
• DEFINE QUERY query-name FOR buffer-name FIELDS (field1 field2). //Only retreive field1
and field2
• DEFINE QUERY query-name FOR buffer-name EXCEPT (field3). //Retreive all fields except
field3.
Examples
Basic Query
https://riptutorial.com/ 55
/* Close the query */
CLOSE QUERY q1.
Multi-Tables Query
This query will join three tables: Customer, Order and Orderline.
The use of the OF statement as in childtable OF parenttable assumes that indexes are constructed
in a specific way. That is the case in the sports2000-database.
https://riptutorial.com/ 56
DOWN WITH FRAME frameA.
GET NEXT q1.
END.
Moving poisition withing a query using next, first, prev and last
loop:
REPEAT:
IF AVAILABLE Customer THEN DO:
DISPLAY Customer.NAME CustNum WITH FRAME frClient TITLE "Client data".
DISPLAY
"(P)revious" SKIP
"(N)ext" SKIP
https://riptutorial.com/ 57
"(F)irst" SKIP
"(L)ast" SKIP
"(Q)uit" SKIP
WITH FRAME frInstr
TITLE "Instructions".
END.
READKEY.
END.
https://riptutorial.com/ 58
Chapter 10: Strings
Introduction
In Progress ABL there are two types of strings, those defined as CHARACTER and those defined as
LONGCHAR. A file larger than 32K in length is a LONGCHAR. Most strings are unless specified any other
way case insensitive.
Remarks
Remember - all positions start with the position 1!
Examples
Defining, assing and displaying a string
Generally you should always define all variable and parameters as NO-UNDO unless you really need
to.
cString = "HELLO".
DISPLAY cString.
Concatenating strings
Using the + operator you can easily concatenate two or more strings.
cString = "HELLO".
String manipulation
There are a couple of useful built in functions for working with string. All functions working with the
position of characters start with index 1 as the first character, not 0 as is common in many
languages.
https://riptutorial.com/ 59
DEFINE VARIABLE i AS INTEGER NO-UNDO.
DEFINE VARIABLE c AS CHARACTER NO-UNDO.
i = 2000.
c = STRING(i).
DISPLAY c.
CHR(integer)
ASC(character)
DO ix = 1 TO 26:
letter[ix] = CHR((ASC("A")) - 1 + ix).
END.
cString = "HELLO".
MESSAGE "The string " cString " is " LENGTH(cString) " characters long" VIEW-AS ALERT-BOX.
• SUBSTRING(string, starting-position).
cString = "ABCDEFGH".
https://riptutorial.com/ 60
DISPLAY SUBSTRING(cString, 4). //Displays "DEFGH"
Substring can also be used to overwrite a part of a string. Use the same syntax but assign that
substring instead:
cString = "ABCDEFGH".
DISPLAY cString.
There's also a similar function called OVERLAY this example from the Progress documentation
covers the differences between OVERLAYand SUBSTRING:
https://riptutorial.com/ 61
ASSIGN
cSubstring = cOriginal
SUBSTRING(cSubstring,2,3) = "***"
cOverlay = cOriginal
OVERLAY(cOverlay,2,3) = "***"
cResults = cResults + "~n~n"
+ "For a length equal to the replacement, SUBSTRING and OVERLAY behave as
follows: ~n~n" + "SUBSTRING(target,2,3) = ~"***~" yields: "
+ cSubstring + ". ~n" + "OVERLAY(target,2,3) = ~"***~" yields: "
+ cOverlay + ".".
INDEX(source, target)
Search target within source (left to right) and return it's position. If it's missing return 0.
str = "ABCDEFGH".
Replaces from-string with to-string in string. From-string and to-string don't need to be
of the same length, to-string can also be nothing ("") to remove a character.
c = "ELLO".
https://riptutorial.com/ 62
c = "ABABABA".
TRIM(string)
Removes all leading and trailing spaces, tabs, line feeds, carriage returns.
TRIM(string, character).
LEFT-TRIM and RIGHT-TRIM does the same thing but only leading or trailing.
c = "__HELLO_WORLD_____".
If you want to use an ampersand in the string (and not use it as a parameter) escape it with
another ampersand: &&.
str = "&1 made &2 goals in &3 games playing for &4".
MESSAGE SUBSTITUTE(str, "Zlatan Ibrahimovic", 113, 122, "Paris Saint-Germain") VIEW-AS ALERT-
BOX.
MESSAGE SUBSTITUTE(str, "Mats Sundin", 555, 1305, "Toronto Maple Leafs") VIEW-AS ALERT-BOX.
A parameter can appear more than once in a string, all will be replaced:
MESSAGE SUBSTITUTE("&1 &2 or not &1 &2", "To", "Be") VIEW-AS ALERT-BOX.
CASE-SENSITIVE strings
https://riptutorial.com/ 63
All strings in Progress ABL are case sensitive unless specified otherwise.
This example will display a message box saying that the strings are identical.
str1 = "abc".
str2 = "ABC".
To declare a string case sensitive you just add the attribute CASE-SENSITIVE
str1 = "abc".
str2 = "ABC".
If string1 BEGINS with (or is equal to) string2 this will return true. Otherwise it will
return false. If string two is empty ("") it will always return true.
BEGINS is very useful in queries where you want to search the beginning of something, for
instance a name. But it's basically a function working on strings.
str = "HELLO".
beg = "HELLO".
DISPLAY str BEGINS beg. // yes
str = "HELLO".
beg = "H".
DISPLAY str BEGINS beg. // yes
str = "HELLO".
beg = "".
DISPLAY str BEGINS beg. // yes
https://riptutorial.com/ 64
str = "HELLO".
beg = "HELLO WORLD".
DISPLAY str BEGINS beg. // no
str = "HELLO".
beg = "HELLO".
DISPLAY str MATCHES beg. // yes
str = "HELLO".
beg = "H*".
DISPLAY str MATCHES beg. // yes
str = "HELLO".
beg = "*O".
DISPLAY str MATCHES beg. // yes
As mentioned before strings are normally case insensitive but that only regards comparison of
strings. There's built in functions for changing case.
CAPS (string)
LC(string)
https://riptutorial.com/ 65
DEFINE VARIABLE c AS CHARACTER NO-UNDO.
DEFINE VARIABLE d AS CHARACTER NO-UNDO.
c = "Hello".
d = "World".
c = "hello".
d = "hello".
c = "hello".
d = "hello".
Lists
There are a number of functions and methods for working with comma (or other character)
separated lists in Progress 4GL.
NUM-ENTRIES Returns the number of entries in a list. You can optionally specify delimiter,
comma is default
NUM-ENTRIES(string [, delimiter])
cList = "Goodbye,cruel,world!".
cList = "Goodbye;cruel;world!".
https://riptutorial.com/ 66
ENTRY - function - returns a specified entry in a list
cList = "Goodbye,cruel,world!".
cList = "Goodbye,cruel,world!".
MESSAGE REPLACE(cList, ",", " ") VIEW-AS ALERT-BOX. //Hello nice world!
cList = "Hello,nice,world!".
In Progress 4GL the normal way to write a special character is to preceed it with a tilde character
(~).
~" " Used to write " inside strings defined using "string".
~' ' Used to write ' inside strings defined using 'string'.
~~ ~ For instance if you want to print the sequence and not how
https://riptutorial.com/ 67
Sequence Interpreted as Comment
its interpreted.
~\ \
~t tab
New line/line
~n
feed
~r Carriage return
~E Escape
~b Backspace
~f Form feed
https://riptutorial.com/ 68
Chapter 11: TEMP-TABLE
Introduction
The TEMP-TABLE is a very powerful feature of Progress ABL. It's a temporary in-memory (mostly at
least) table that can be used for writing complex logic. It can be used as input/output parameters
to procedures, functions and other programs. One or more temp-tables can make up the
foundation of a DATASET (often called ProDataset).
Almost anything that can be done with a native Progress database table can be done with a temp-
table.
Examples
Defining a simple temp-table
This is the definition of a TEMP-TABLE named ttTempTable with three fields. NO-UNDO indicates that no
undo handling is needed (this is usually what you want to do unless you really need the opposite).
Temp-tables can (and should) be created with indices if you plan to run queries against them.
This table has one index (index1) containing of one field (field1). This index is primary and unique
(meaning not two records can have the same contents of field1).
You can define multiple indices for each temp-table. If you need them - define them. Basically an
index matching your query and/or sort order will help performance!
https://riptutorial.com/ 69
DEFINE TEMP-TABLE ttWithoutIndex NO-UNDO
FIELD field1 AS INTEGER
FIELD field2 AS CHARACTER
FIELD field3 AS LOGICAL.
ETIME(TRUE).
DO i = 1 TO 1000:
CREATE ttWithIndex.
ttWithIndex.field1 = i.
END.
iWithCreate = ETIME.
ETIME(TRUE).
DO i = 1 TO 1000:
CREATE ttWithoutIndex.
ttWithoutIndex.field1 = i.
END.
iWithoutCreate = ETIME.
RELEASE ttWithIndex.
RELEASE ttWithoutIndex.
ETIME(TRUE).
DO i = 1 TO 1000:
FIND FIRST ttWithIndex WHERE ttWithIndex.field1 = i NO-ERROR.
END.
iWithFind = ETIME.
ETIME(TRUE).
DO i = 1 TO 1000:
FIND FIRST ttWithoutIndex WHERE ttWithoutIndex.field1 = i NO-ERROR.
END.
iWithoutFind = ETIME.
MESSAGE
"With index took" iWithFind "ms to find and" iWithCreate "ms to create" SKIP
"Without index took" iWithoutFind "ms to find and" iWithoutCreate "ms to create"
VIEW-AS ALERT-BOX.
Searching with index was roughly 70 times faster compared to no index! This is just one run of
course so not a scientific proof but your index setup will make impact.
https://riptutorial.com/ 70
Inputting and outputting temp-tables
It's very simple to pass temp-tables in and out of programs, procedures and functions.
This can be handy if you want a procedure to process a bigger number of data than you can easily
store in a string or similar. You can pass temp-tables as INPUT, OUTPUT and INPUT-OUTPUT data.
CREATE ttRequest.
ASSIGN ttRequest.fieldA = "A"
ttRequest.fieldB = "B".
CREATE ttRequest.
ASSIGN ttRequest.fieldA = "B"
ttRequest.fieldB = "C".
CREATE ttRequest.
ASSIGN ttRequest.fieldA = "C"
ttRequest.fieldB = "D".
Result:
fieldA--------fieldB--------
B A
C B
D C
https://riptutorial.com/ 71
Input-outputting a temp-table:
PROCEDURE pythagoras:
DEFINE INPUT-OUTPUT PARAMETER TABLE FOR ttCalculate.
END PROCEDURE.
CREATE ttCalculate.
ASSIGN ttCalculate.num1 = 3
ttCalculate.num2 = 4.
CREATE ttCalculate.
ASSIGN ttCalculate.num1 = 6
ttCalculate.num2 = 8.
CREATE ttCalculate.
ASSIGN ttCalculate.num1 = 12
ttCalculate.num2 = 16.
Result:
3 4 5.00
6 8 10.00
12 16 20.00
Passing to functions
DO iNum = 1 TO 100:
CREATE ttNumbers.
ASSIGN ttNumbers.num1 = RANDOM(1,100)
https://riptutorial.com/ 72
ttNumbers.num2 = RANDOM(1,100).
END.
/* Function to check if two records has the same value in num1 and num2 */
FUNCTION hasAPair RETURNS LOGICAL (INPUT TABLE ttNumbers):
END FUNCTION.
You pass temp-tables to and from other .p-programs the same way you pass them to other
procedures. The only difference is that both the calling and the called program must have the
same temp-table declaration. One easy way is to store the temp-table program in a third file - an
include that's used in both programs.
Include file containing temp-table definition: /* ttFile.i */ DEFINE TEMP-TABLE ttFile NO-UNDO
FIELD fName AS CHARACTER FORMAT "x(20)" FIELD isADirectory AS LOGICAL.
/* checkFiles.p */
{ttFile.i}
Main program:
{ttFile.i}
CREATE ttFile.
ASSIGN ttFile.fname = "c:\temp\".
CREATE ttFile.
ASSIGN ttFile.fname = "c:\Windows\".
CREATE ttFile.
ASSIGN ttFile.fname = "c:\Windoose\".
https://riptutorial.com/ 73
FOR EACH ttFile:
DISPLAY ttFile.
END.
Result:
fName----------------- isADirector
c:\temp\ yes
c:\Windows\ yes
c:\Windoose\ no
https://riptutorial.com/ 74
Chapter 12: Variables
Introduction
Progress ABL is statically typed. The variables need to be declared and the datatype cannot be
changed during run time.
Syntax
• DEFINE VARIABLE i AS INT64 INITIAL -200 NO-UNDO. //A 64-bit integer initialized to -200
• DEFINE VARIABLE dt AS DATE INTIAL TODAY NO-UNDO. //A date variable set to todays
date.
• DEFINE VARIABLE j AS INTEGER EXTENT NO-UNDO. //An extent without a set length
• DEFINE VARIABLE b AS DATETIME LABEL "Departure time". //A variable with a label
Examples
Basic variable declarations
/*
https://riptutorial.com/ 75
DEFINE VARIABLE d AS DATE NO-UNDO.
DEFINE VARIABLE dt AS DATETIME NO-UNDO.
DEFINE VARIABLE dtz AS DATETIME-TZ NO-UNDO.
Progress supports one dimensional arrays, but they are called EXTENTS.
/* Define a character array with the length 5, and display it's length */
DEFINE VARIABLE a AS CHARACTER EXTENT 5 NO-UNDO.
DISPLAY EXTENT(a).
Individual positions i the array is accessed using "standard" c-style brackets. But the index starts
at 1. The maximum size is 28000.
a[1] = "A".
a[2] = "B".
a[3] = "C".
a[4] = "D".
a[5] = "E".
DISPLAY a[5].
Result:
DISPLAY a[0].
Result:
https://riptutorial.com/ 76
You can also define a indeterminate array without a set length. The length (extent) can be set in
run-time. But only once!
You can use the INITIAL option on the DEFINE VARIABLE statement to set initial values.
Result:
If you don't set all extents the remaining will get the last set value:
Result:
https://riptutorial.com/ 77
Using the LIKE keyword
Using LIKE you can base the definition of you variable on another variable or a field in a database
or temp-table.
Defining a variable LIKE a database field requiers the database to always be connected. This might
not always be what you want.
https://riptutorial.com/ 78
Chapter 13: Working with numbers
Introduction
Progress ABL supports three number formats: 32 and 64 bit integers and floats.
Examples
Operators
There's no += or ++ operator. To increase or decrease a variable you have to assign it to itself plus
or minus something. So to add 1 to a variable you do: i = i + 1.
i = 3.
j = 2.
DISPLAY i + j. // 3 + 2 = 5
DISPLAY i - j. // 3 - 2 = 1
DISPLAY i / j. // 3 / 2 = 1.5
DISPLAY i * j. //3 x 2 = 6
SQRT( number)
MESSAGE "The square root of 256 is " SQRT(256) VIEW-AS ALERT-BOX. // Messages 16
https://riptutorial.com/ 79
expression MODULO base
ROUND - Rounds a decimal expression to a specified number of places after the decimal point.
i = 40.
j = 45.
k = 56.
Comparing numbers
https://riptutorial.com/ 80
There are standard functions built in for comparing equality, inequality etc.
Equal = EQ i=j
The symbol can be exchanged with the alternative and vice versa. So var1 <> var2 is the same
thing as var1 NE var2.
You can compare a float with an integer but you cannot compare for instance a date with an
integer.
RANDOM(low, high)
DO i = 1 TO 20.
DISPLAY i RANDOM(1, 20).
PAUSE.
END.
https://riptutorial.com/ 81
Credits
S.
Chapters Contributors
No
2 Compiling Jensd
Conditional
3 Jensd
statements
5 Functions Jensd
6 Iterating Jensd
7 OS-utilities Jensd
8 Procedures Jensd
10 Strings Jensd
11 TEMP-TABLE Jensd
12 Variables Jensd
Working with
13 Jensd
numbers
https://riptutorial.com/ 82