Sie sind auf Seite 1von 428

UniData

Developing UniBasic Applications

Version 5.2 June 2000 Part No. 000-6837

Published by Informix Press

Informix Corporation 4100 Bohannon Drive Menlo Park, CA 94025-1032

2000 Informix Corporation. All rights reserved. The following are trademarks of Informix Corporation or its affiliates, one or more of which may be registered in the United States or other jurisdictions: Answers OnLineTM; ArdentTM; AxielleTM; C-ISAM; Client SDKTM; CloudconnectorTM; CloudscapeTM; CloudsyncTM; CloudviewTM; DataBlade; Data DirectorTM; Data MineTM; Data Mine BuilderTM; DataStage; Decision FastStartTM; Decision for Telecommunications Campaign ManagementTM; Decision FrontierTM; Decision Solution SuiteTM; DecisionscapeTM; DialogueTM; Dynamic Scalable ArchitectureTM; Dynamic ServerTM; Dynamic Server.2000TM; Dynamic ServerTM, Developer EditionTM; Dynamic ServerTM with Advanced Decision Support OptionTM; Dynamic ServerTM with Extended Parallel OptionTM; Dynamic ServerTM with MetaCube ROLAP Option; Dynamic ServerTM with Universal Data OptionTM; Dynamic ServerTM with Web Integration OptionTM; Dynamic ServerTM, Workgroup EditionTM; Dynamic Virtual MachineTM; Encrypt.CSMTM; Enterprise Decision ServerTM; E-stageTM; FormationTM; Formation ArchitectTM; Formation Flow EngineTM; Foundation.2000TM; Frameworks for Business IntelligenceTM; Frameworks TechnologyTM; Gold Mine Data Access; i.DecideTM; i.Financial ServicesTM; i.FoundationTM; i.IntelligenceTM; i.ReachTM; i.SellTM; Illustra; Informix; Informix 4GL; Informix COM AdapterTM; Informix Enterprise Command CenterTM; Informix Extended Parallel ServerTM; Informix Informed DecisionsTM; Informix InquireSM; Informix Internet Foundation.2000TM; InformixLink; InformiXMLTM; Informix Red Brick Decision ServerTM; Informix Session ProxyTM; Informix VistaTM; InfoShelfTM; Installation AssistantTM; InterforumTM; I-SpyTM; IterationsTM; J/FoundationTM; LUCIDTM; MaxConnectTM; Media360TM; MediazationTM; MetaArchitectTM; MetaBrokerTM; MetaCube; MetaHubTM; MetaStageTM; NewEraTM; O2 & DesignTM; O2 Technology & DesignTM; Object TranslatorTM; Office ConnectTM; ON-BarTM; OnLine Dynamic ServerTM; OnLine/Secure Dynamic ServerTM; OpenCase; OrcaTM; PaVERTM; Prism; Prism & DesignTM; RedBack; RedBeanTM; RedBeans & DesignTM; Red Brick and Design; Red Brick Data MineTM; Red Brick Decision ServerTM; Red Brick Mine BuilderTM; Red Brick DecisionscapeTM; Red Brick ReadyTM; Red Brick Systems; Regency Support; Rely on Red BrickSM; RISQL; Server AdministratorTM; Solution DesignSM; STARindexTM; STARjoinTM; SuperTerm; SuperView; SureStartTM; SystemBuilderTM; TARGETindexTM; TARGETjoinTM; The Data Warehouse Company; UniData; UniData & Design; UniVerse; Universal Data Warehouse BlueprintTM; Universal Database ComponentsTM; Universal Web ConnectTM; ViewPoint; Virtual Table InterfaceTM; VisionaryTM; Web Integration SuiteTM; XML DataPortTM; Zero Defect Data. The Informix logo is registered with the United States Patent and Trademark Office. The DataBlade logo is registered with the United States Patent and Trademark Office. Documentation Team: Claire Gustafson GOVERNMENT LICENSE RIGHTS Software and documentation acquired by or for the US Government are provided with rights as follows: (1) if for civilian agency use, with rights as restricted by vendors standard license, as prescribed in FAR 12.212; (2) if for Dept. of Defense use, with rights as restricted by vendors standard license, unless superseded by a negotiated vendor license, as prescribed in DFARS 227.7202. Any whole or partial reproduction of software or documentation marked with this legend must reproduce this legend.

ii Developing UniBasic Applications

Contents

Chapter 1 - Introduction to UniBasic............................................13


In This Chapter .........................................................................................14 UniBasic Capabilities ...............................................................................15 UniBasic Statements .................................................................................16 Types of Statements ............................................................................16 Building Blocks for Writing Statements .............................................16 Statement Syntax and Layout .............................................................19 UniData File Types ...................................................................................23 Data Representation in UniBasic Programs ..............................................24 Delimiters and the Null Value ............................................................24 Constants .............................................................................................25 Variables .............................................................................................25 Arrays ..................................................................................................26 Getting System Information ......................................................................27 @Variables .........................................................................................27 STATUS Function ..............................................................................28

Developing UniBasic Applications

Contents

Chapter 2 - Program Control.........................................................29


In This Chapter .........................................................................................30 Subroutines ...............................................................................................31 Internal Subroutines ............................................................................31 External Subroutines ...........................................................................33 Looping .....................................................................................................36 Conditional Tests ......................................................................................38 IF/THEN/ELSE Statements ................................................................38 CASE Statements ................................................................................41 Reversing Conditional Evaluations: The NOT Function ....................43 Comparison Operators Used in Conditional Statements ....................44 Branching ............................................................................................48 Summary of Program Control Commands ...............................................49

Chapter 3 - Creating and Running a Program .............................51


In This Chapter .........................................................................................52 Creating a Program with AE .....................................................................53 Creating a Program Record .................................................................53 More AE Commands ..........................................................................56 Compiling a UniBasic Program ................................................................59 Compile Commands ...........................................................................59 Directing the Compiler .......................................................................59 Compiler Messages .............................................................................65 Creating Cross-Reference Reports ......................................................68 Cataloging a UniBasic Program ...............................................................73 Points to Remember about CATALOG ..............................................73

Developing UniBasic Applications

Contents

Direct Cataloging ................................................................................74 Local Cataloging .................................................................................75 Global Cataloging ...............................................................................75 Using the ECL CATALOG Command ...............................................77 Removing a Catalog Entry ..................................................................78 CATALOG Examples .........................................................................79 Running a UniBasic Program ...................................................................82 Running a Program from AE ..............................................................82 Running a Program from ECL ............................................................82

Chapter 4 - Maintaining Data in Files ...........................................93


In This Chapter .........................................................................................94 UniData Locks ..........................................................................................95 Database Triggers .....................................................................................96 Trigger Rules ......................................................................................96 The Nature of Triggers .......................................................................97 ECL Commands and Triggers ............................................................97 UniBasic Commands Affected by Triggers ........................................98 Writing an UPDATE Trigger Subroutine ...........................................99 Writing a DELETE Trigger Subroutine ............................................102 UniBasic STATUS Function Return Values ....................................105 Troubleshooting ................................................................................106 Maintaining Files ....................................................................................107 UniData Hashed Data Files ...............................................................107 Alternate Key Indexes ......................................................................108 Non-UniData Sequential Files ..........................................................112

Developing UniBasic Applications

Contents

Opening Files ..........................................................................................115 Example ............................................................................................115 Selecting Records ...................................................................................116 Creating a Select List of Record IDs ................................................116 Clearing a Select List ........................................................................118 Reading, Writing, and Deleting Data from Files ....................................119 Getting Ready to Read ......................................................................119 Reading Record IDs from a Select List ............................................119 Reading Data from Files ...................................................................119 Example ............................................................................................122 Writing Data to Files .........................................................................123 Example ............................................................................................125 Deleting Data from Files ...................................................................126 Closing Files ...........................................................................................128 Accessing Data in Unopened Files .........................................................129

Chapter 5 - Using UniData Locks ...............................................131


In This Chapter .......................................................................................132 Understanding the UniData Locking System .........................................133 How UniData Locks Work ...............................................................133 Types of UniData Locks ...................................................................133 When UniBasic Finds a Lock ...........................................................135 Points to Remember about Locks .....................................................135 Locking Commands ................................................................................136 Checking Lock Status .......................................................................137 What Commands Do with Locks ............................................................138

Developing UniBasic Applications

Contents

When to Use Locking Commands ..........................................................140 Programming Problems ..........................................................................142 Causes ...............................................................................................142 Minimizing Problems .......................................................................142 Locking Example ....................................................................................144

Chapter 6 - Working with Data in Programs ..............................147


In This Chapter .......................................................................................148 UniData Arrays .......................................................................................149 Dynamic Arrays ................................................................................149 Example ............................................................................................153 Dimensioned Arrays .........................................................................153 Inquiring about Data .........................................................................155 Type ..................................................................................................155 Location ............................................................................................157 Extraction ..........................................................................................157 Performing Numeric Operations .............................................................159 Arithmetic Operators ........................................................................159 Mathematic Functions .......................................................................164 Formatting and Converting Data ............................................................166 ICONV and OCONV: The All-Purpose Functions ..........................166 Character Format Conversion ...........................................................168 Strings and Dynamic Arrays .............................................................168 Numbers ............................................................................................170 Dates and Times ................................................................................175 UniBasic Multibyte Support ...................................................................181

Developing UniBasic Applications

Contents

Modified Functions and Commands .................................................181 Single-Byte Functions .......................................................................183 Multibyte Functions ..........................................................................184

Chapter 7 - External Interaction..................................................187


In This Chapter .......................................................................................188 Interacting with Other UniBasic Programs .............................................189 Sharing Data .....................................................................................189 Including Code at Compilation .........................................................193 Interacting with UniData ........................................................................195 Executing Virtual Attributes .............................................................195 Executing ECL Statements ...............................................................196 Executing UniData SQL Statements .................................................197 Defining and Using Programs and Functions ...................................198 Writing User Exits ..................................................................................199 What Are User Exits? .......................................................................199 Calling a User Exit from UniBasic ...................................................200 Calling a User Exit from a Virtual Attribute ....................................200 Calling a User Exit from a Proc ........................................................201 Parameters in User Exits ...................................................................202 Interacting with Hardware I/O Devices ..................................................203 Display Terminals .............................................................................203 Printers ..............................................................................................209 Tape Drives .......................................................................................212 Interacting with the Operating System ...................................................213

Developing UniBasic Applications

Contents

Chapter 8 - Linking Programs with UniData ..............................215


In This Chapter .......................................................................................216 Linking C Programs (UNIX Only) .........................................................217 Before You Begin .............................................................................217 Calling a C Function from UniBasic with CALLC ..........................219 Calling a UniBasic Subroutine from a C Program with CallBasic ...235 If This Is Not the First Time .............................................................246 File Examples ...................................................................................249 More on make, makeudt, and makeudapi .........................................254 Troubleshooting CALLC ..................................................................257 Linking C Programs (Windows NT or Windows 2000 Only) ................259 Dynamic Link Libraries (DLLs) and UniData .................................259 CALLC Features and Components ...................................................260 Using CALLC ...................................................................................264 CallBasic Features and Components ................................................267 Using CallBasic ................................................................................272

Chapter 9 - UniBasic Transaction Processing ..........................279


In This Chapter .......................................................................................281 Transaction Processing Commands ........................................................282 Executing TP and Non-TP Transactions ..........................................282 Starting a Transaction .......................................................................283 Committing a Transaction ................................................................285 Aborting a Transaction .....................................................................287 Testing for an Active Transaction ....................................................288 Transaction Processing Programming Example ...............................289

Developing UniBasic Applications

Contents

Creating or Converting to a Recoverable File ........................................290 Creating a Recoverable File ..............................................................290 Converting to a Recoverable File .....................................................291 Transaction Processing Programming Problems ....................................293 Transaction Abort .............................................................................293 Degraded Performance .....................................................................297

Chapter 10 - Null Value Handling................................................299


Representing Unknown Values ..............................................................300 Turning Null Value Handling Off ....................................................300 Null Value Handling in UniBasic ...........................................................301 Printing and Displaying ....................................................................301 Sorting and Indexing .........................................................................301 Summary of Effects on Commands and Functions ..........................302 The Null Value in Numeric Functions ..............................................303 The Null Value in Conditional Tests ................................................305 The Null Value in Conversion Functions .........................................313 The Null Value in String Functions ..................................................315

Chapter 11 - Managing Named Pipes .........................................323


Points to Remember ..........................................................................325 OSOPEN .................................................................................................326 Opening Named Pipes ......................................................................327 OSBREAD ..............................................................................................330 Reading from a Named Pipe .............................................................330 OSBWRITE ............................................................................................334 Writing to Named Pipes ....................................................................334

10

Developing UniBasic Applications

Contents

OSCLOSE ...............................................................................................337 STATUS Function Return Values ..........................................................339 INMAT ...................................................................................................340 Syntax ...............................................................................................340 Description ........................................................................................340 Troubleshooting ......................................................................................341 Example ..................................................................................................342

Chapter 12 - Using CallHTTP ......................................................345


Configuring the Default HTTP Settings .................................................346 Getting the Current HTTP Default Settings ...........................................349 Creating an HTTP Request .....................................................................350 Setting Additional Headers for a Req .............................................. uest 353 Adding a Parameter to the Request .........................................................355 Submitting a Request ..............................................................................357 Getting a Response Header .....................................................................360 Protocol Logging ....................................................................................361

Chapter 13 - Using the Socket Interface ....................................363


Socket Function Error Return Codes ......................................................364 Opening a Socket ....................................................................................368 Closing a Socket .....................................................................................370 Getting Information From a Socket ........................................................371 Reading From a Socket ...........................................................................373 Writing to a Socket .................................................................................375 Setting the Value for a Socket Option ....................................................377 Parameters .........................................................................................377

Developing UniBasic Applications

11

Contents

Getting the Value of a Socket Option .....................................................379 Initializing a Server Side Socket Connection .........................................382 Accepting an Incoming Connection Attempt on the Server Side ...........383 Protocol Logging ....................................................................................385

Appendix A - Sample Program ...................................................387


UPDATE_ORDER .................................................................................388 DISPLAY_MESSAGE ...........................................................................395

Appendix B - UniBasic Transaction Processing Concepts......397


In This Appendix ....................................................................................398 Transaction Processing Rules: The ACID Properties .............................399 Atomicity ..........................................................................................399 Consistency .......................................................................................399 Isolation ............................................................................................399 Durability ..........................................................................................400 Transaction Isolation ...............................................................................401 Transaction Processing Errors ..........................................................401 What Are Isolation Levels? ..............................................................404 Programming to Isolation Levels ......................................................405 Example: Programming to Isolation Level 2 ....................................406 Example: Programming to Isolation Level 3 ....................................407 Updating Nonrecoverable Files in Transactions .....................................408

Index

12

Developing UniBasic Applications

Chapter 1 - Introduction to UniBasic

This manual is for programmers who write applications that use the UniData relational database management system (RDBMS). It explains how to use UniBasic commands and functions to write application programs.

Devloping UniBasic Applications

13

Chapter 1 - Introduction to UniBasic

In This Chapter
This chapter introduces the concepts and terms used throughout this manual and in the UniBasic Commands Reference. It includes the following sections: UniBasic Capabilities UniBasic Statements UniData File Types Data Representation in UniBasic Programs Getting System Information

14

Devloping UniBasic Applications

UniBasic Capabilities

UniBasic Capabilities
UniBasic is UniDatas structured programming language intended for a variety of applications. UniBasic provides you with the following capabilities: The modular language style enables you to create general UniBasic subroutines that other processes can call. From within a UniBasic program, you can perform the following tasks: Access other UniData products and operating system tools. Call external C functions. Pass data to and from external C programs. Manage display terminal input and output.

Dynamic arrays and a full set of array commands provide an easy interface to UniDatas extended relational database structure. An interface with the query languages UniData SQL and UniQuery enable you to pass record IDs or entire records to the query language.

For the syntax of UniBasic commands and functions, see the UniBasic Commands Reference

Devloping UniBasic Applications

15

Chapter 1 - Introduction to UniBasic

UniBasic Statements
This section describes the types of statements you can use in UniBasic. Statements are the building blocks you use to write UniBasic programs. This section also describes how you can lay out your statements in a program.

Types of Statements
You can write four types of UniBasic statements: Assignments Load numeric or string values into variables. You also can assign the results of a function to a variable: variable = OCONV(1286, MD/2). Control statements Direct the order in which commands are executed, such as in a subroutine call: GOSUB update_client. Conditional statements Control the execution of a set of operations based on the value of a variable: IF...THEN or DO...UNTIL. Compiler directives Instruct the UniBasic compiler to evaluate and conditionally include statements: $INCLUDE.

Building Blocks for Writing Statements


Statements consist of five types of building blocks, which are described in the following subsections: Commands Functions Operators Special Characters Comments

16

Devloping UniBasic Applications

UniBasic Statements

Commands
Commands are the primary building blocks of the UniBasic instruction set. Generally, each statement begins with a command word. In structured coding, only one command is included on each line of code. The UniBasic Commands Referenceexplains the syntax and use of each command. In the following UniBasic statement, SELECT is the command word, and ORDER and 1 are arguments used by the command: SELECT ORDER TO 1.

Functions
Functions reformat, extract, and perform mathematical calculations. The result of a function can be stored in a variable or used in a command. In the following statement, the function EXTRACT is an argument in the PRINT command: PRINT EXTRACT(A,2,2). You can nest UniBasic functions. Therefore, a function can be an argument in a function as in: PRINT OCONV(EXTRACT(A,2,2),"MD2,$"). This statement extracts data from the array A and prints out the extracted data with two decimal places, preceded by $. The UniBasic Commands Referenceexplains the syntax and use of each function.

Operators
UniBasic supports the following operator types: Arithmetic For computing and assigning values. Conditional For testing data and directing program execution. You can use the following operator types: Relational Boolean

Note
For information about arithmetic operators, see Chapter 6 - Working with Data in Programs. For information about conditional operators, including how they are used in UniBasic programs, see Chapter 2 - Program Control.

Devloping UniBasic Applications

17

Chapter 1 - Introduction to UniBasic

Special Characters
You can use the following special characters in UniBasic statements: Continuation character (|) Placed at the end of a line. It indicates that a command continues on the next line. Semicolon (;) Separates commands on the same line. Be aware, though, that including more than one command on a line makes your code more difficult to read and debug. Quotation marks ( or ) Must enclose string variables and literal values. They can be used together to differentiate a literal that is embedded in a string. Backslash (\) Can also be used to replace quotation marks when a string variable is embedded in a literal.

In the following example, HISTORY4 is assigned to a string that includes the HEADING command. Single quotation marks are embedded in double quotation marks, and backslashes are used to enclose the entire string:

Program Example
MON = @MONTH DA = @DAY HISTORY1 = "LIST ALMANAC WITH MONTH = ":MON:" AND DAY = ":DA HISTORY2 = "BY TYPE BY YEAR" HISTORY3 = "YEAR TYPE EVENT ID.SUP COL.SUP NI.SUP " HISTORY4 = \HEADING "'C'Today is 'DLC' On This Day in History 'LL'" \ HISTORY = HISTORY1:HISTORY2:HISTORY3:HISTORY4 PERFORM HISTORY

Comments
Comments narrate the actions of a program for clarity, but the compiler ignores them. You can enter a comment on a line by itself, or you can enter it on the same line as a program statement. The comment commands are *, !, and REM. To enter a comment on a line by itself, begin the line with a comment command, and then enter the comment text.

18

Devloping UniBasic Applications

UniBasic Statements

In the following example, two asterisks (**) are used for commented text:

Program Example
** ** ** ** ** ** ** ** ** ** Program : Programmer : Created : Description: : : : : : : UPDATE_ORDERS Todd Roemmich 04/02/1996 Check and/or alter Order records Display Screen and ask for Order # Read record (if it exists) and display fields Prompt for a command (Alter, Delete, or Quit) A) Allow the user to change price or address D) Delete the record Q) Exit the program

To enter a comment on the same line as a program statement, precede the comment command with a semicolon. In the following example, a comment is entered on the same line as the GOSUB command:

Program Example
GOSUB OPEN_FILES ; ** go open files based on user input

Statement Syntax and Layout


The syntax for each command and function is included in the UniBasic Commands Reference. Multiple statements can be grouped on one line, single statements can extend over several lines, and statements can be nested.

Stacking Multiple Statements on a Line


Structured programming conventions suggest that you place no more than one statement on a single line. For backward compatibility, UniBasic enables you to include multiple statements on a single line separated by a semicolon (;).

Devloping UniBasic Applications

19

Chapter 1 - Introduction to UniBasic

Reminder
Programs are easier to follow if you place a single command on each line.

Extending Statements Across Multiple Lines


Individual statements can extend across several lines if you follow one of these rules: Break the statement as shown in the syntax of the command or function in the UniBasic Commands Reference. Use a continuation character (|) at the end of the line. The following example prints This line is CONTINUED:

Program Example
001: PRINT "This line is ": OCONV(| 002: "continued","MCU")

While Informix recommends that you use a single-line construction, in two cases you might need to use a multiline construction: Statements with arguments Statements or functions that have arguments separated by commas, such as CALL, COMMON, and DATA, often require several lines. A multiline COMMON statement follows:

Program Example
COMMON NAME,DOB,SS.NUM,ADDRESS,CITY,STATE, ZIP,HOME.PHONE,BUS.PHONE,ACC.NUM

Long statements Lengthy constructions, such as FOR/NEXT and IF/THEN/ELSE, often require several lines.

20

Devloping UniBasic Applications

UniBasic Statements

The following example, which is taken from the sample program in Appendix A - Sample Program, shows a lengthy IF/THEN/ELSE construction:

Program Example
IF PRODUCT_LINE = '' THEN PRODUCT_LINE = PRODUCT_NUMBER "R#10" COLOR_LINE = COLOR "R#10" QUANTITY_LINE = QUANTITY "R#10" PRICE_LINE = PRICE END ELSE PRODUCT_LINE := " ":PRODUCT_NUMBER "R#10" COLOR_LINE := " ":COLOR "R#10" QUANTITY_LINE := " ":QUANTITY "R#10" PRICE_LINE := " ":PRICE END

Nesting Statements
You can nest a statement within another statement. For example, you can test for one condition, and if it is true, test for an additional condition. In the following example, the second IF/THEN is nested:

Program Example
IF expression THEN statements IF expression THEN statements END ELSE statements END END

Devloping UniBasic Applications

21

Chapter 1 - Introduction to UniBasic

The following program segment, which is taken from the sample program in Appendix A Sample Program, is an example of an IF/THEN/ELSE nested inside a conditional LOOP. The LOOP test is in the UNTIL statement.

Program Example
LOOP DISPLAY @(15,6): INPUT ORDER_NUMBER ORDER_NUMBER = OCONV(ORDER_NUMBER,"MCU") IF NUM(ORDER_NUMBER) OR ORDER_NUMBER[1,1] = "Q" THEN VALID_ORDER_NUMBER = 1 END ELSE VALID_ORDER_NUMBER = 0 MESSAGE = "Order # must be a Number, or the letter 'Q'" CALL DISPLAY_MESSAGE(MESSAGE) END UNTIL VALID_ORDER_NUMBER REPEAT

22

Devloping UniBasic Applications

UniData File Types

UniData File Types


File types that make up the UniData database include those listed in the following table. For more information about UniData files, see Using UniData. UniData File Type Static hashed file Dynamic hashed file Dictionary (DICT) file Alternate key index (static file) Alternate key index (dynamic file) DIR file MULTIFILE (multilevel file) MULTIDIR (multilevel DIR file) Data file. Directory file that contains a data and an overflow portion. UniData treats these two portions as one file. UniData automatically resizes the file to avoid level 2 overflow. Data file that contains attribute definitions for a UniData file. Data file located in the same directory at the same level as the indexed data file. This index is based on an attribute other than the primary key. Data file located in the dynamic file directory along with the data file and the overflow file. An operating system directory directly accessible by UniData. A directory that contains one or more UniData hashed files. There is one dictionary for the MULTIFILE. All hashed files in the MULTIFILE share the same dictionary. A directory that contains one or more subdirectories. There is one dictionary for the MULTIDIR. All subdirectories in the MULTIDIR share the same dictionary. If you copy records from a file into one of the subdirectories, each record is stored as a text file. UniData File Types Description

Devloping UniBasic Applications

23

Chapter 1 - Introduction to UniBasic

Data Representation in UniBasic Programs


Before you can work with data in a program, you must bring it into the program and store it in a usable form. In UniBasic programs, data is represented in the following ways: Delimiters and the null value Constants Variables Arrays @variables

Delimiters and the Null Value


Informix recommends that you use @variables to represent UniData delimiters and the null value in UniBasic programs instead of entering the ASCII code directly. The ASCII code used for these values is determined by the language (such as English, Chinese, or Spanish) being represented. The @values to use are: Null value @NULL Record mark @RM Attribute mark @AM, @FM Value mark @VM Subvalue mark @SM Text mark @TM

Note
You turn on or off null value handling with the UniData configuration parameter NULL_FLAG. For a full explanation, see Chapter 10 - Null Value Handling.

24

Devloping UniBasic Applications

Data Representation in UniBasic Programs

Constants
A constant can be a numeric value, such as 25, or a string value, such as inventory. You must use quotation marks to enclose string values. You can use up to 32,765 constants at one time.

Variables
A variable can be as simple as a character string or as complex as a dimensioned array. UniBasic does not require explicit declaration of variable data type (unless it is a dimensioned array). Type is inferred from the data initially assigned to it.

Setting Variable Values


Variables receive their values from one of the following: Data input through the keyboard. An assignment (variable_name = 1234). A computation statement. Data read from a file. Data stored in virtual memory or on hard disk, tape, or another storage medium. For more information, see Chapter 3 - Creating and Running a Program. Arguments in a program call. For more information, see Chapter 3 - Creating and Running a Program.

Variable Names
Variable names must follow the conventions described below: Must be unique. Must begin with a letter. Can contain any combination of letters, numbers, underscores, periods, and dollar signs.

Devloping UniBasic Applications

25

Chapter 1 - Introduction to UniBasic

Are limited to a length of 64 characters. Cannot be a reserved word. For a list of reserved words, see the UniBasic Commands Reference.

Arrays
An array is simply a form of variable, which is a place reserved in memory for storing data in a UniBasic program. Each element of an array can contain a constant, a simple variable, or a dynamic array. UniBasic allows for two types of arrays: Dynamic arrays Separate each attribute, value, and subvalue of a record by delimiters. Dimensioned arrays Store data in designated cells of a fixed-sized matrix. Access to data in a dimensioned array is rapid, but less flexible.

Tip
Use dynamic arrays when the number of elements is small, unknown, varied, or when the data to examine is near the beginning of the array. Use dimensioned arrays when the number of elements is large or the program will process the elements nonsequentially. For more information about using arrays to process data in a UniBasic program, see Chapter 6 Working with Data in Programs.

26

Devloping UniBasic Applications

Getting System Information

Getting System Information


A wealth of information is available to your UniBasic program through system variables (called @variables) and through the UniBasic STATUS function.

@Variables
UniData @variables store and make available information about the system, the user process, and the UniBasic program being executed. Some @variables are available through any UniData or 4GL tool. Others are available through UniBasic only. UniData Available through any tool. For a complete list of UniData @variables, see Using UniData. For example: @SYSTEM.RETURN.CODE returns a value indicating a condition or error after the execution of an ECL command. It also returns additional information. For example, after SELECT, it reports the number of records selected. UniBasic Available through UniBasic only. For a complete list of UniBasic @variables, see the UniBasic Commands Reference. For example: @SENTENCE stores the statement that invoked the current UniBasic program. @TRANSACTION returns 1 if a transaction is active, and returns 0 if no transaction is active.

Reminder
Use @variables to represent UniData delimiters and the null value. Do not use the UniBasic CHAR function because the ASCII code varies with language group.

Devloping UniBasic Applications

27

Chapter 1 - Introduction to UniBasic

STATUS Function
The UniBasic STATUS function provides information about the last UniBasic statement executed. Generally, a return value of 0 indicates successful execution, while 1 or any other number indicates an error condition. For cases in which return values are set by a command, those values are documented with the command in the UniBasic Commands Reference. UniData triggers also set STATUS values. For more information about triggers, the return values set for the STATUS function, and an additional return value set by triggers, see Chapter 4 Maintaining Data in Files.

28

Devloping UniBasic Applications

Chapter 2 - Program Control

A structured program consists of: A main routine that controls the order of program execution, and Subroutines that perform functions such as screen, printer, and data manipulation.

This chapter introduces the UniBasic program control mechanisms that enable you to write structured code. If you are new to programming or new to UniBasic, read Chapter 1 Introduction to UniBasic first. For more information about the syntax of UniBasic commands and functions, see UniBasic Commands Reference.

Developing UniBasic Applicatons

29

Chapter 2 - Program Control

In This Chapter
The UniBasic commands control the order of execution of statements in the program. These commands create subroutines, loops, and conditional tests, and otherwise direct program flow. These structures and the commands that build them are introduced in the following sections: Subroutines Looping Conditional Tests Summary of Program Control Commands

30

Developing UniBasic Applicatons

Subroutines

Subroutines
In UniBasic, you can code subroutines that are internal to the calling program or separately compiled and cataloged. You can nest subroutines up to 1024 levels deep.

Internal Subroutines
Internal subroutines are self-contained units within a program. They begin with a label and end with a RETURN statement.

Commands That Call Internal Subroutines


Within the constraints of structured programming, GOSUB and ON GOSUB are the only acceptable statements to use for calling an internal subroutine: GOSUB label1 RETURN [TO] ON expr GOSUB label1[:][,label2[:]...] RETURN [TO]

Tip
GOTO and ON GOTO are provided in UniBasic for backward compatibility. These commands do not return control to the calling statement and are generally not used in structured programs because they make the code difficult to read and maintain.

Naming Internal Subroutines


Each internal subroutine must begin with a label. Labels can be numbers or short descriptive strings. The following labels are used in the sample program in Appendix A - Sample Program:
INITIALIZE ALTER_RECORD WRITE_RECORD GET_RECORD_COMMAND

Developing UniBasic Applicatons

31

Chapter 2 - Program Control

You must observe the following conventions when creating statement labels: If a label begins with a number, it must contain only numbers. If the label begins with a letter, it can contain any combination of letters, numbers, underscores, periods, and dollar signs. Spaces are not allowed in subroutine names. If the label is alpha or alphanumeric, it must end with a colon to differentiate it from a variable. The label cannot be a reserved keyword.

When you select names for subroutine labels, choose meaningful, descriptive names. Avoid abbreviations. Add comments after the statement label, and when you use numeric statement labels, arrange the subroutines in ascending order. If you follow these guidelines, you will be able to locate internal subroutines easily.

Example of an Internal Subroutine


The following example, taken from the sample program in Appendix A - Sample Program, illustrates calling internal subroutines:

Program Example
*-------------- Main Logic ----------------------------GOSUB INITIALIZE LOOP GOSUB DISPLAY_SCREEN GOSUB GET_ORDER_NUMBER UNTIL ORDER_NUMBER[1,1] = 'Q' GOSUB DISPLAY_DATA IF RECORD_FOUND THEN GOSUB GET_RECORD_COMMAND RELEASE REPEAT GOSUB EXIT

32

Developing UniBasic Applicatons

Subroutines

In the previous example, the first line calls the subroutine INITIALIZE. Processing proceeds from the INITIALIZE: label, as the following example shows, until it reaches RETURN:

Program Example
INITIALIZE: DISPLAY @(-1) PROMPT '' RETURN

Processing picks up again at the next line that follows the subroutine call in Main Logic (see the preceding program example). This line contains the command LOOP.

External Subroutines
External subroutines are separately compiled programs that are called from a UniBasic program using the CALL statement. You can pass variables and constants to an external subroutine through arguments. The structural requirements of external subroutines include the following: The SUBROUTINE statement must be the first noncomment line in the subroutine. The SUBROUTINE statement must contain the same number of arguments as the calling program. The subroutine must finish with a RETURN command. The compiled subroutine must be cataloged so that the main program can locate it. You can nest subroutines up to 1024 levels deep.

Calling an External Subroutine


Use the CALL command to initiate an external subroutine. The syntax of the CALL command follows: CALL sub.name [(argument1[,argument2]...)] CHAIN and ENTER also execute subroutines, but they do not return control to the calling program and are not recommended in structured programming.

Developing UniBasic Applicatons

33

Chapter 2 - Program Control

Naming an External Subroutine


The SUBROUTINE command names the external routine. This command must be the first line of code in the called routine, and the last line must be RETURN. The syntax follows: SUBROUTINE sub.name [(argument1[,argument2]...) statement(s) RETURN

Examples
The following sample program calls the external subroutine CALLED.PGM. It passes on the operation code selected by the user.

Program Example
PRINT "Enter operation to perform: (1)add, (2)delete, (3)update : ";INPUT operation CALL CALLED.PGM(operation,ret.val) PRINT "Operation completed: ":ret.val END

The following subroutine accepts the code, passes in the parameter operation, and returns a value in ret.value:

Program Example
SUBROUTINE CALLED.PGM(operation,ret.val) BEGIN CASE CASE operation = 1 ret.val = "Record added." CASE operation = 2 ret.val = "Record deleted." CASE operation = 3 ret.val = "Record updated." END CASE RETURN

34

Developing UniBasic Applicatons

Subroutines

The following program is the globally-cataloged subroutine DISPLAY_MESSAGE that is included in Appendix A - Sample Program. It displays the message passed in the variable MESSAGE.

Program Example
SUBROUTINE DISPLAY_MESSAGE(MESSAGE) DISPLAY @(5,20):MESSAGE DISPLAY @(5,21):"Press the (Return) key.": INPUT PAUSE,1_ RETURN

The following program segment is an example of a call to the preceding subroutine from UPDATE_ORDER:

Program Example
MESSAGE ="**(Record Does Not Exist)**" GO_BACK = "Y" CALL DISPLAY_MESSAGE(MESSAGE)

Developing UniBasic Applicatons

35

Chapter 2 - Program Control

Looping
UniBasic provides the following commands that support looping to repeatedly execute a set of commands. Command LOOP/REPEAT Action Statements can precede and/or follow the evaluation. You can include multiple UNTIL and/or WHILE clauses. The loop continues until the UNTIL condition is satisfied or until WHILE is false. Executes statements repeatedly while a counter increments or decrements (default is +1). The loop continues until the counter reaches a specified number or the UNTIL condition is satisfied or until WHILE is false. Transfers control to the next iteration of a FOR/NEXT or LOOP/REPEAT statement. Transfers control to the line after REPEAT or NEXT. Commands for Looping

FOR/NEXT

CONTINUE EXIT

Note
To test for the null value, you must test the return value of the function ISNV or ISNVS, as in LOOP UNTIL ISNV(X) = 1. You cannot test for the null value directly, as in LOOP UNTIL X = @NULL, because the null value is not comparable to any value. For information about how null values affect UniBasic, see Chapter 10 - Null Value Handling.

36

Developing UniBasic Applicatons

Looping

Example
The following program segment is taken from the sample program in Appendix A - Sample Program. This segment updates the price if the user has entered a new one.

Program Example
*------------ Subroutines ---------------------------ALTER_RECORD: * Create a new screen, and allow PRICE and ADDRESS to be changed. * Initialize variables and draw the screen NEED.TO.WRITE = 0 DISPLAY @(-1):@(15,5):"Alter ORDER": DISPLAY @(10,8):"(Press RETURN to leave un-changed)" DISPLAY @(8,9):"Old Price":@(42,9):"New Price (Enter 2 decimal places)"

* Change the PRICE field (if desired) FOR ENTRY = 1 TO NUM_ENTRIES NEW.PRICE = "" DISPLAY @(10,9+ENTRY):OCONV(ORDER.REC<7,ENTRY>,"MR2$,"): INPUT @(45,9+ENTRY):NEW.PRICE NEW.PRICE = OCONV(NEW.PRICE,"MCN") IF NEW.PRICE # '' AND NUM(NEW.PRICE) THEN ORDER.REC<7,ENTRY> = NEW.PRICE NEED.TO.WRITE = 1 END NEXT ENTRY

Developing UniBasic Applicatons

37

Chapter 2 - Program Control

Conditional Tests
Two primary functions of a program are evaluating and processing data appropriately based on that evaluation. To evaluate data, you need to build conditional statements. UniBasic conditional statements include the following: IF/THEN/ELSE CASE

Within these statements, you can nest additional conditional tests with the following keywords: WHILE UNTIL

IF/THEN/ELSE Statements
An IF/THEN/ELSE statement evaluates a piece of data and then processes it in a manner appropriate for that particular data.

Note
For information about how null values affect UniBasic, see Chapter 10 - Null Value Handling. You can use the syntax in any of the following IF/THEN/ELSE statements. Type 1 2 Syntax IF ...THEN ... IF ... THEN ... ELSE ... Example IF confirmed = "Y" THEN ... IF confirmed = "Y" THEN GOSUB DISPLAY ELSE ...

IF/THEN/ELSE Statement Syntax

38

Developing UniBasic Applicatons

Conditional Tests

Type 3

Syntax IF ... THEN ... ... END ELSE ...

Example IF confirmed = "Y" THEN INFOMESSAGE = ... GOSUB DISPLAY_INFO END ELSE GOSUB DISPLAY_ERR END

IF expr THEN ... END ELSE ... END

IF confirmed = "Y" THEN INFOMESSAGE = ... GOSUB DISPLAY_INFO END ELSE INFOMESSAGE = ... GOSUB DISPLAY_INFO END IF/THEN/ELSE Statement Syntax

You can test for a true condition in a variable with a statement similar to the following:
IF var THEN statements1 ELSE statements2

When var is true, (specifically, when it is anything other than 0), statements1 are executed. When var is false (when it is 0), statements2 are executed.

Developing UniBasic Applicatons

39

Chapter 2 - Program Control

Example
The following program segment is taken from the sample program in Appendix A - Sample Program. This statement is an example of Type 1, which is described in the previous table.

Program Example
* Accept INPUT to change values of address INPUT @(40,13):STREET1 IF STREET1 = '' THEN STREET1 = CLIENT.REC<4> INPUT @(40,14):STREET2 IF STREET2 = '' THEN STREET2 = CLIENT.REC<5>

The following program segment is taken from the sample program in Appendix A - Sample Program. This statement is an example of Type 4, which is described in the preceding IF/THEN/ELSE Statement Syntax table.

Program Example
IF STATUS() = 1 THEN DISPLAY "A Transaction had already been started, NESTED Transactions" DISPLAY "are NOT Allowed. (Contact System Administrator)" INPUT PAUSE,1_ END ELSE DISPLAY "The Recoverable File System is not enabled." END

40

Developing UniBasic Applicatons

Conditional Tests

CASE Statements
The CASE statement accommodates multiple conditions. When testing for more than two conditions, the CASE statement is more efficient than IF/THEN/ELSE because when a true condition is encountered, UniBasic skips all subsequent CASE evaluations. Syntax: BEGIN CASE CASE expr1 statements CASE expr2 statements ... {CASE 1 statements} END CASE If a condition is true, UniBasic performs all statements associated with that CASE statement and then transfers control to the statement that follows END CASE. If a condition is false, UniBasic goes on to test the next CASE condition. You can enter an unlimited number of CASE conditions. The CASE 1 statement always evaluates to true. Placed as the last test, it executes if all previous CASE conditions are false.

Note
The null value is always evaluated to false. For information about how the null value affects UniBasic, see Chapter 10 - Null Value Handling.

Developing UniBasic Applicatons

41

Chapter 2 - Program Control

Example
The following program segment is taken from UPDATE_ORDER in Appendix A - Sample Program. In this subroutine, the user selects an operation to perform (alter, delete, or quit), and then the program calls the appropriate subroutine to perform that operation. Note that CASE 1 handles the user entering an invalid response.

Program Example
GET_RECORD_COMMAND: * Enter a valid command to act upon (the Desired record is already shown). DISPLAY @(7,22):"Enter A)lter, D)elete, or Q)uit: ": INPUT COMMAND,1 COMMAND = OCONV(COMMAND[1,1],"MCU") BEGIN CASE CASE COMMAND = "A" GOSUB ALTER_RECORD CASE COMMAND = "D" GOSUB DELETE_RECORD CASE COMMAND = "Q" FINISHED = 1 CASE 1 MESSAGE = "Valid options are A, D, or Q. Please try again" CALL DISPLAY_MESSAGE(MESSAGE) END CASE RETURN

42

Developing UniBasic Applicatons

Conditional Tests

Reversing Conditional Evaluations: The NOT Function


The NOT function inverts the result of conditional statements, making a true result false or a false result true. If the expression is true, the function returns 0 (false). If the expression is false, the function returns 1 (true). You must enclose the expression being reversed in parentheses. The STATUS function in the following program tests for a value other than 0, which indicates that the record was not found. Note that the comparison of the STATUS return value with 0 is enclosed in parentheses.

Note
You cannot use NOT to test for the absence of the null value because NOT @NULL is evaluated to @NULL. For more information about how the null value is handled in conditional statements, see Chapter 10 - Null Value Handling.

Program Example
OPEN "INVENTORY" TO open.file ELSE PRINT "Open error." ;STOP PRINT "Enter product to display: ";INPUT search_product search_product = OCONV(search_product, "MCT") new_product = search_product SETINDEX "PROD_NAME", search_product ON open.file IF NOT(STATUS()=0) THEN PRINT "Record not found.";STOP LOOP UNTIL new_product <> search_product READFWD dyn.array.var FROM open.file ELSE PRINT "Readfwd error." new_product = EXTRACT(dyn.array.var,3) IF new_product = search_product THEN PRINT "Record is: ":dyn.array.var REPEAT END

Developing UniBasic Applicatons

43

Chapter 2 - Program Control

Comparison Operators Used in Conditional Statements


Comparison operators can be used to make compound conditional statements. Comparison operators include Boolean and relational operators.

Boolean Operators
Boolean operators combine or compare sets of relational data. A common use for Boolean operators is to check if a numeric variable falls within a range. Boolean operators take one of the following forms. Expression expr1 AND expr2 expr1 OR expr2 Description When both expressions are true, the result is true. When either expression is true, the result is true. Boolean Operators

Note
With null value handling turned on, the logical operators AND and OR follow the ANSI SQL 3-way logic and null value evaluation comparison rules. For more information, see Chapter 10 Null Value Handling. In the following example, either expression (NEW_CLIENT # OLD_CLIENT) or NEED.TO.WRITE must be true for the CLIENT.REC values to be updated. The values in NEW_CLIENT and OLD_CLIENT must not be equal, or NEED.TO.WRITE must be 1.

Program Example
IF (NEW_CLIENT # OLD_CLIENT) OR NEED.TO.WRITE THEN * Re-assign values to CLIENT.REC CLIENT.REC<4> = STREET1 CLIENT.REC<5> = STREET2 CLIENT.REC<6> = CITY

44

Developing UniBasic Applicatons

Conditional Tests

CLIENT.REC<7> = STATE CLIENT.REC<8> = ZIP GOSUB WRITE_RECORD END

Relational Operators
UniBasic supports the following relational operators for making comparisons. Operator EQ = NE # <> >< GT > LT < GE >= => LE <= =< #> #< MATCH MATCHES Equal to. Not equal to. Greater than. Less than. Greater than or equal to. Less than or equal to. Not greater than. Not less than. Value matches a pattern (such as 0N or 0A) or a literal. Relational Operators Description

Note
Some symbols serve as both relational and arithmetic operators.

Developing UniBasic Applicatons

45

Chapter 2 - Program Control

UDT.OPTIONS
UDT.OPTIONS provide settings that modify the operation of UniData and UniBasic. For value comparison, if you set UDT.OPTION 1 ON, UniData evaluates zero and empty string equally. For more information, see the UDT.OPTIONS Commands Reference.

Operator Precedence
Within nested operations, you can control the order of execution by using parentheses with arithmetic or comparison operators. UniBasic first resolves the innermost parenthetical expression and then works outward. If multiple operators appear within a single command statement or within a single parenthetical expression, and the operators have equal precedence, UniBasic performs the operations from left to right. In the absence of parentheses, UniBasic uses the following order of precedence. Order of Precedence First Second Third Operator ^ ** * / + : CAT Description Exponentiation Multiplication Division Unary minus Addition Subtraction Concatenation

Fourth

Operator Precedence

46

Developing UniBasic Applicatons

Conditional Tests

Order of Precedence Fifth

Operator > GT < r = EQ >= => GE #> <= =< LE #< <> >< # NE MATCH MATCHES

Description Greater than Less than Equal to Greater than or equal to Not greater than Less than or equal to Not less than Not equal to Matching And Or

Sixth

& AND ! OR

Operator Precedence (continued)

Example
You can use the following sample program to test the logical operator OR. To test the logical operator AND, substitute AND for OR in the program.

Program Example
PROMPT'' PRINT "First value: Enter true or false: ";INPUT answer answer = UPCASE(answer) GOSUB SET.VAL val.one=value PRINT "Second value: Enter true or false: ";INPUT answer answer = UPCASE(answer) GOSUB SET.VAL val.two=value If (val.one OR val.two ) = 1

Developing UniBasic Applicatons

47

Chapter 2 - Program Control

PRINT "Condition True." ELSE PRINT "Condition is false."

Branching
For backward compatibility, UniBasic supports branching, although this practice makes code difficult to follow and maintain. The GOTO and ON GOTO commands direct the program to jump to another place in the code. For the syntax of these commands, see the UniBasic Commands Reference.

48

Developing UniBasic Applicatons

Summary of Program Control Commands

Summary of Program Control Commands


The following table summarizes the UniBasic commands that facilitate structure and control in programs. Command ABORT CALL CALLC CASE CHAIN CONTINUE END EXECUTE Action Stops program execution. Calls an external subroutine. Calls an external C subroutine. Executes statements based on a condition. Branches to a separate program. Transfers control to the next iteration of the FOR/NEXT or LOOP/REPEAT statement. Indicates the end of a conditional statement, program, or subroutine. Executes an external UniData statement, such as a UniQuery, UniData SQL, or operating system command. (Identical to PERFORM.) Executes a UniData SQL statement from within a UniBasic program. Ends a FOR/NEXT or LOOP/REPEAT statement and transfers control to the first statement after the loop statement. Repeats statements based on a condition. Executes an internal subroutine, and then returns to the next statement after GOSUB. Executes statements one time based on a condition. Program Control Commands

EXECUTESQL EXIT

FOR/NEXT GOSUB IF/THEN/ELSE

Developing UniBasic Applicatons

49

Chapter 2 - Program Control

Command LOOP/REPEAT ON expr GOSUB name PCPERFORM PERFORM

Action Repeats a set of statements based on a condition. Branches to a subroutine based on the value of an expression. Executes an operating system command from within a UniBasic program. Executes an external UniData statement, such as a UniQuery, UniData SQL, or operating system command from within a UniBasic program. (Identical to EXECUTE.) Returns control from an external subroutine back to the original program, or returns control from an internal subroutine to the next statement that follows GOSUB. Stops program execution. Program Control Commands (continued)

RETURN

STOP

50

Developing UniBasic Applicatons

Chapter 3 - Creating and Running a Program

This chapter describes how to create and run a UniBasic program. If you are not familiar with the UniData RDBMS, the UniBasic statement types, and how data is represented in UniBasic programs, Informix recommends that you review Chapter 1 - Introduction to UniBasic before you begin this chapter.

Developing UniBasic Applications

51

Chapter 3 - Creating and Running a Program

In This Chapter
This chapter introduces the tools that you need to create and edit UniBasic programs. It includes compiling, cataloging, and running the program. These topics are presented in the following sections: Creating a Program with AE Compiling a UniBasic Program Cataloging a UniBasic Program Running a UniBasic Program

52

Developing UniBasic Applications

Creating a Program with AE

Creating a Program with AE


You can use any ASCII text editor or word processor to create a UniBasic program, as long as you save it in ASCII format as a record in a directory file. UniData creates a basic program (BP) file for this purpose when you initiate a new account. UniData provides the Alternate Editor (AE) line editor, or you can use the operating system default editor (with the ECL command ED). This section provides instructions for using AE.

Tip
AE is designed for writing UniBasic programs. It recognizes UniData hashed files, it enables you to execute UniData commands from within a file you are editing, and it comes with online help. For information about the building blocks used to create UniBasic programs, see Chapter 1 Introduction to UniBasic.

Creating a Program Record


Perform the following steps to create a program record:

1. Start AE
To create a program using AE, enter the AE command at the ECL prompt. You will use this same command later to edit the program. Syntax: AE directory.file prog.name directory.file is the directory in which the source code is located (usually BP); prog.name is the name you choose for the program.

Tip
UniData creates a BP directory file when you create your account.

Developing UniBasic Applications

53

Chapter 3 - Creating and Running a Program

If You Are Successful After you start the AE editor, UniBasic opens the program at the top and displays the command mode prompt:

Screen Example
:AE BP NEWFIL Top of New "NEWFIL" in "BP". *--:

When you first start AE, you are in command mode. This is where you enter commands that affect the entire file. If You Are Not Successful If you fail to indicate a directory file where the program is to reside (such as the BP file), AE does not recognize the file name, and it displays a message like this:

Screen Example
:AE NEWFIL Unable to find file "NEWFIL". File >

At this point, your best option is to press ENTER and start over.

2. Change to dInput Mode


For now, you want to key in the program, so you want to be in input mode. To change to input mode, enter i. AE changes the prompt to reflect your current line number.

Screen Example
*--: i 001=

54

Developing UniBasic Applications

Creating a Program with AE

Enter as many lines of code as you want. Press ENTER at the end of each line. You remain in input mode as long as you enter text on a line before pressing ENTER, as shown in the following example:

Screen Example
001= ! Program: NEWFIL 002= ! Created: 1/1/96 003= !

Tip
To display and enter the ASCII code for control characters (including UniData delimiters and the null value) in AE, press SHIFT+6.

3. Return to Command Mode


When you want to get help, save the file, compile the program, or execute some other command directive, press ENTER at the line number. You are returned to command mode, as demonstrated by the return of the command mode prompt. In the following example, the line number prompt 004= is replaced by the command mode prompt *--:

Screen Example
004= *--: <-- line number prompt <-- is replaced by command mode prompt

Developing UniBasic Applications

55

Chapter 3 - Creating and Running a Program

4. Access AE Help
To get help on AE, enter HELP or H at the command-line prompt, as shown in the following example:

Screen Example
*--: HELP

You can also enter a help topic on the same line. To get a list of all topics, enter ALL or A.

Screen Example
*--: H ALL

5. File, Save, or Quit


You file, save, or quit in command mode. To save and quit the program, enter FI. To quit the program without saving it, enter EX or Q. After you quit the program, the ECL prompt reappears. Use the FIBCFN or ECL NEWPCODE command to dynamically activate a cataloged subroutine. You can use a UniBasic shell to modify, recompile, recatalog, and retest a UniBasic program without returning to the ECL prompt.

More AE Commands
You can enter any of the following basic AE editor commands when you are in command mode. Command C/old.string/new.string Description Changes the current character string to a new character string on the current line. More AE Commands

56

Developing UniBasic Applications

Creating a Program with AE

Command R/old.string/new.string P HELP I EX or Q FI FIB FIBR

Description Replaces the current line with new text entered. Displays one page of the record. Displays online help for AE. You can also enter HELP followed by a topic or AE command. Changes to input mode so you can enter text. Quits the record without saving changes made during this editing session. Files the program record and saves changes. Files the program record and compiles it. Files the record, compiles it, and runs it. Note - If the compile is unsuccessful, the program you execute is an earlier version that does not include changes in the most recent version.

FIR

Files the record and runs the compiled version. Note - Be aware that the version run can differ from the one you are editing.

FIBCFN

The N option of the FI command equates to the ECL NEWPCODE command. FIBCFN compiles a program and catalogs it (locally) with NEWPCODE. You need to use F (force) in conjunction with the N option. For more information, see the online help for the AE editor. Tip - Use FIBCFN or ECL NEWPCODE to dynamically activate a cataloged subroutine. You can use a UniBasic shell to modify, recompile, recatalog, and retest a UniBasic program without exiting to ECL.

LNn

Lists the number of lines specified, with no line numbers. More AE Commands (continued)

Developing UniBasic Applications

57

Chapter 3 - Creating and Running a Program

Command n T SPOOLHELP SPOOLHELP -FULL ENTER

Description Goes to line number n. Goes to the top of the record. Prints brief help. Prints extensive help. Returns to command mode. More AE Commands (continued)

Tip
Do you need to cut and paste lines in a program without the line numbers? AE offers the command LNn, which lists no line numbers. n is the number of lines to list.

58

Developing UniBasic Applications

Compiling a UniBasic Program

Compiling a UniBasic Program


The UniBasic compiler processes UniBasic code into the interpretive code (also called object code) that is used by the UniBasic interpreter at runtime.

Compile Commands
You can compile a program using the following commands: Compiling within AE You can compile a program you are currently editing in AE by entering FIB at the command prompt. When you enter FIB, UniData saves the program, compiles it, files the compiled code, and returns you to the ECL prompt. For more information about compiling from AE, see Creating a Program with AE earlier in this chapter. Compiling from ECL The BASIC command compiles the program from the ECL prompt. For the syntax of the BASIC command, see ECL BASIC Command Options later in this chapter.

Directing the Compiler


You can direct the compiler to take some action during compilation with the following: ECL BASIC command options ECL and UniBasic commands

ECL BASIC Command Options


One way to direct the compiler is with ECL BASIC command options. Syntax: BASIC filename [TO filename] progname1 [progname2...] [option]

Developing UniBasic Applications

59

Chapter 3 - Creating and Running a Program

The following table describes each parameter of the syntax. Parameter filename progname Description Specifies the file from which the source program code is compiled. Note - The compiled version is saved in _filename. Specifies the source code program to be compiled and used with the UniData Basic interpreter. If you do not enter progname, and if you do not have a select list active, UniData prompts for a program name. Tip - You can create a select list before executing BASIC; this compiles all programs in the select list. For example, to select all UniBasic source files in the BP directory, enter
SELECT BP WITH @ID UNLIKE "_..."

Then, enter
BASIC BP

UniData compiles all programs in BP. option Specifies additional function(s) to be performed. You must choose and option and include the hyphen. For valid options, see the BASIC Command Options table. BASIC Command Parameters The following table lists compile options available with the ECL BASIC command. Option -D -G Description Creates a symbol table for use with the UniBasic debugger. For more information about the debugger, see Using the UniBasic Debugger. Reports on execution time for the program, as described in Reporting Execution Time later in this chapter. BASIC Command Options

60

Developing UniBasic Applications

Compiling a UniBasic Program

Option -I

Description Compiles UniBasic reserved words regardless of the case in which they are entered (uppercase, lowercase, or mixed case). Without this option, the compiler recognizes uppercase reserved words. For a list of UniBasic reserved words, see theUniBasic Commands Reference. Generates a list of the program. Generates a cross-reference table of statement labels and variable names used in the program. For more information, see Creating Cross-Reference Reports later in this chapter. Creates a symbol table for use with the UniBasic debugger. For more information about the debugger, see Using the UniBasic Debugger. If you compile a program with the -I option, all reserved words in UniBasic are case insensitive. BASIC Command Options (continued)

-L -LIST -X -L -XREF -L -Z1 -Z2 -I

ECL and UniBasic Commands


Some additional ECL and UniBasic commands are provided to instruct the compiler to take action of the following types: Select a compiler type. Define variables and include or exclude blocks of code.

Selecting a Compiler Type You can tune your UniBasic program to be more compatible with other software that does not follow UniData syntax. You can direct the compiler to do this from the ECL prompt or from within a UniBasic program, as follows: ECL BASICTYPE command, entered from the ECL prompt, sets the current BASICTYPE to be used by the UniBasic compiler. You can also use this command to learn which BASICTYPE is currently active or which BASICTYPE was used to compile a program.

Developing UniBasic Applications

61

Chapter 3 - Creating and Running a Program

UniBasic $BASICTYPE command, entered as the first line in a UniBasic program, determines the BASICTYPE to be used in the compilation of the program that follows. $BASICTYPE in a UniBasic program overrides the BASICTYPE established at the ECL prompt.

The following options are available for use with both BASICTYPE and $BASICTYPE. Parameter U P R M UniBasic Pick BASIC Advanced Revelation BASIC McDonnell Douglas BASIC/Reality BASIC $BASICTYPE Parameters For more information about the ECL BASICTYPE command, see the UniData Commands Reference. For more information about the compiler directive $BASICTYPE, see the UniBasic Commands Reference. More Compiler Directives Along with $BASICTYPE, UniData offers other compiler directives that you can use within UniBasic programs to define variables and establish blocks of code based on the variables. These directives are shown in the following table. Compiler Command $DEFINE var $UNDEFINE var Description Defines and clears a control variable var. Description

More Compiler Directives

62

Developing UniBasic Applications

Compiling a UniBasic Program

Compiler Command $IFDEF var statements1 [$ELSE statements2] $ENDIF $IFNDEF var statements1 [$ELSE statements2] $ENDIF $INCLUDE $INSERT

Description If var is defined, include statements1 in compiled code. Otherwise, include statements2.

If var is not defined, include statements1 in compiled code. Otherwise, include statements2.

Inserts the indicated UniBasic program or program segment into your program during compilation. A common use is to insert frequently used blocks of code or data into appropriate programs. More Compiler Directives (continued)

The constructions of these compiler directives are very similar to Boolean IF... THEN... ELSE logic. However, these compiler directives instruct the compiler to include or exclude blocks of code based on the variables provided. Compiler Directives Example When you compile the following program, UniBasic compiles either the first section (line 4) to open files for factory 1, or it compiles the second section (line 17) to open files for factory 2:

Program Example
comp_dir 001: 002: 003: 004: 005: 006: 007:

CANT.OPEN = '' $DEFINE FACT1 $IFDEF FACT1 OPEN 'PARTS1' TO PARTS ELSE CANT.OPEN<-1> = 'PARTS1' END

Developing UniBasic Applications

63

Chapter 3 - Creating and Running a Program

008: 009: 010: 011: 012: 013: 014: 015: 016: 017: 018: 019: 020: 021: 022: 023: 024: 025: 026: 027: 028: 029: 030: 031: 032: 033: 034: 035: 036: 037: 038: 039: 040: 041: 042:

OPEN 'PURCHASE.ORDERS1' TO PURCHASE.ORDERS ELSE CANT.OPEN<-1> = 'PURCHASE.ORDERS1' END OPEN 'SALES.ORDERS1' TO SALES.ORDERS ELSE CANT.OPEN<-1> = 'SALES.ORDERS1' END $ELSE OPEN 'PARTS2' TO PARTS ELSE CANT.OPEN<-1> = 'PARTS2' END OPEN 'PURCHASE.ORDERS2' TO PURCHASE.ORDERS ELSE CANT.OPEN<-1> = 'PURCHASE.ORDERS2' END OPEN 'SALES.ORDERS2' TO SALES.ORDERS ELSE CANT.OPEN<-1> = 'SALES.ORDERS2' END $ENDIF IF CANT.OPEN THEN PRINT 'Cannot open file(s): ' LOOP REMOVE FILE FROM CANT.OPEN SETTING MORE.FILES PRINT SPACE(5):FILE WHILE MORE.FILES REPEAT PRINT 'Stopping...': INPUT ANYTHING STOP END END

64

Developing UniBasic Applications

Compiling a UniBasic Program

Compiler Messages
The UniBasic compiler displays both warning and error messages. If the compiler generates warning messages only, it compiles the program and produces object code. However, if the compiler generates any error messages, it does not produce object code. The last line of compiler messages indicates whether the program compiled successfully. Object code is stored in the same DIR-type file as the source code. The record name for the object code is the same as that for the source code, but is prefixed with an underscore (_). For example, the source code record TEST1 generates object code record _TEST1.

Successful Compilation
After UniBasic successfully compiles a program, it displays a completion message and returns you to the UniData colon (ECL) prompt. The following example demonstrates the successful compilation of program TEST1:

Screen Example
... Compiling Unibasic: BP/TEST1 in mode 'u'. compilation finished :

The compiler might return warning messages when compilation is successful. These messages are preceded by Warning: .

Note
If you run batch jobs to compile groups of programs, you need to code these jobs to terminate only when error messages are returned by the compiler. Warning messages do not terminate processing.

Developing UniBasic Applications

65

Chapter 3 - Creating and Running a Program

The following program prints a variable that is never assigned a value:

Screen Example
PROMPT '' PRINT @(-1) PRINT var PRINT "Enter record to update: "; INPUT answer OPEN 'STUDENT' TO STU ELSE PRINT "Can't open STUDENT file." READU RECORD FROM STU,answer LOCKED PRINT "Record locked by another user, waiting..." THEN PRINT "I am locking the record now and going to sleep." FOR X = 1 TO 10 SLEEP 2 NEXT X END PRINT "Waking up now." RELEASE END

The UniBasic compiler returns a warning message when it compiles this program. Notice that the program is compiled, and object code is produced.

Screen Example
Compiling Unibasic: BP/LOCKUP in mode 'u'. Warning: Variable 'var' never assigned a value compilation finished :

66

Developing UniBasic Applications

Compiling a UniBasic Program

Unsuccessful Compilation
If UniBasic fails to compile a program, it returns the cursor to the colon (ECL) prompt. The example that follows is a UniBasic program that contains the following errors: The variable answer is spelled two different ways. See lines 3 and 5. The FOR/NEXT loop beginning on line 8 contains a misspelling: T should be TO.

Screen Example
001: 002: 003: 004: 005: 006: 007: 008: 009: 010: 011: 012: 013: 014: PROMPT '' PRINT @(-1) PRINT "Enter record to update: "; INPUT answer OPEN 'STUDENT' TO STU ELSE PRINT "Can't open STUDENT file." READU RECORD FROM STU,answr LOCKED PRINT "Record locked by another user, waiting..." THEN PRINT "I am locking the record now and going to sleep." FOR X = 1 T 10 SLEEP 2 NEXT X END PRINT "Waking up now." RELEASE END

The UniBasic compiler produces the following warning and error messages when it attempts to compile this program:

Screen Example
Compiling Unibasic: BP/LOCKUP in mode 'u'. main program: syntax error at or before <line 8> FOR X = 1 T 10 ----------------^ Expecting: array,string,number,function,variable,TO,OR,AND,!,>,>=,<,<=,= ,<>,MATCH,CAT,:,+,main program: syntax error at or before <line 10> NEXT X

Developing UniBasic Applications

67

Chapter 3 - Creating and Running a Program

-----^ Expecting: end-of-line,END,; Warning: Variable 'X' never assigned a value Warning: Variable 'T' never assigned a value Warning: Variable 'answr' never assigned a value compilation failed :

The missing O in TO causes the compiler to misinterpret line 8, the NEXT statement on line10 is not matched, and two unassigned variable warning messages result. Finally, the misspelling of the answer variable produces an unassigned variable message. If the only error in this program had been the misspelled variable, only warning messages would have been displayed, and object code would have been produced. However, a runtime error would occur if you tried to execute the program.

Note
Syntax errors are sometimes reported on a line following the actual error. This happens when the statement is incomplete, and the compiler looks for the remainder of the statement on a subsequent line. Also, combining multiple statements on the same line, or including an inordinate number of comment lines could cause the line counter to be incorrect.

Creating Cross-Reference Reports


With the UniBasic cross-reference capability, you can generate a table that describes statement labels and variables. This capability is especially useful when you use it in conjunction with the UniBasic debugger. You can use the cross-referencing feature by compiling programs with the BASIC command and the -L (or -LIST) and -X -L (or -XREF -L) options, using the syntax introduced in ECL BASIC Command Options earlier in this chapter.

68

Developing UniBasic Applications

Compiling a UniBasic Program

Listing Included Code


When you use the -L (or -LIST) option, UniBasic generates a source code listing of the program, including all lines brought in with an $INSERT or $INCLUDE statement. UniBasic saves the listing in the source directory with an underscore (_) prefix, followed by the program name and a LIST suffix.

Listing Statements and Variables


When you use the -X -L (or -XREF -L) option, UniBasic produces a cross-reference report listing statement label and variable names used in the program. The report states where and how the program uses its labels and variables by listing the name, type, reference, and function of each variable. Name Name of the label or variable used in the program, such as 100, PARTNO, or X. Type Type of label or variable. UniData sorts the report by type starting with label because its type is 0, and ending with equate because it has the highest type number, which is 14. The cross-reference types are described in the following table. Type 0 1 2 3 4 5 6 7 8 9 Label Common area Compiler @variable Read-only @variable @variable Variable Variable function Common variable One-dimensioned array Two-dimensioned array Cross-Reference Types Description

Developing UniBasic Applications

69

Chapter 3 - Creating and Running a Program

Type 10 11 12 13 14

Description One-dimensioned array argument Two-dimensioned array argument One-dimensioned array in COMMON Two-dimensioned array in COMMON Equate

Cross-Reference Types (continued) Reference Line number on which the program refers to the label or variable. Function UniBasic uses the function symbol to reference the variable. Symbols and their functions are listed in the following table. Symbol * = ! @ $ no symbol Function Definition of variable or label. Assignment of variable. Dimension of array. Argument to called subroutine. Variable or label is inside an INCLUDE or INSERT statement. Simple reference such as IF X = 7 THEN. Function Symbol References

70

Developing UniBasic Applications

Compiling a UniBasic Program

Cross-Reference Report Examples


The following example shows the compile command with cross-referencing options and the listing UniData generates using the -L option:

Screen Example
:BASIC BP -X -L Please enter BASIC program file name: index.test :SPOOL BP _index.test.LIST -T BP: _index.test.LIST BP/index.test Source Listing 0001 OPEN "INVENTORY" TO open.file ELSE PRINT "Open error." ;STOP 0002 PRINT "Enter product to display: ";INPUT search_product 0003 search_product = OCONV(search_product, "MCT") 0004 new_product = search_product 0005 SETINDEX "PROD_NAME", search_product ON open.file 0006 IF NOT(STATUS()=0) THEN PRINT "Record not found.";STOP 0007 LOOP UNTIL new_product <> search_product 0008 READFWD dyn.array.var FROM open.file ELSE PRINT "Readfwd error." 0009 new_product = EXTRACT(dyn.array.var,3) 0010 IF new_product = search_product THEN PRINT "Record is: ":dyn.array.var 0011 REPEAT 0012 END

Developing UniBasic Applications

71

Chapter 3 - Creating and Running a Program

The following example shows the output from the -X -L option:

Screen Example
:SPOOL BP _index.test.XREF -T BP: _index.test.XREF BP/index.test Cross Reference Listing Name....................... dyn.array.var new_product open.file search_product Type.. 5 5 5 5 References................................... 0008= 0009 0010 0004= 0007 0009= 0010 0001= 0005 0008 0002= 0003= 0003 0004 0005 0007 0010

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 * = ! @ $

Label Common Area Compiler @ Variable Readonly @ Variable @ Variable Variable Variable Argument Common Variable One Dimension Array Two Dimension Array One Dimension Array Argument Two Dimension Array Argument Common One Dimension Array Common Two Dimension Array Equate Definition of Symbol Assignment of Variable Dimension of Array Argument to CALL Include or Insert statement

72

Developing UniBasic Applications

Cataloging a UniBasic Program

Cataloging a UniBasic Program


Cataloging UniBasic programs simplifies program execution and can improve efficiency of system resource use by letting multiple users access a single copy of a compiled program from memory. Use the ECL CATALOG command to catalog one or more UniBasic programs.

Note
For more information about the CATALOG command, see the UniData Commands Reference. For more information about managing catalogs, see Administering UniData on UNIX or Administering UniData on Windows NT or Windows 2000. Compiled UniBasic programs can be cataloged directly, locally, or globally. The process and options for cataloging a UniBasic program are introduced in the following sections: Points to Remember about CATALOG Direct Cataloging Local Cataloging Global Cataloging Using the ECL CATALOG Command Removing a Catalog Entry CATALOG Examples

Points to Remember about CATALOG


The ECL CATALOG command copies the compiled code of a UniBasic program or list of UniBasic programs into the system catalog. Some points to remember about cataloged programs are listed below: When you catalog a program globally, more than one user can run the program at the same time, even though only one copy resides in system memory.

Developing UniBasic Applications

73

Chapter 3 - Creating and Running a Program

You can use the ECL VCATALOG command to find out if your source code is the same version as the cataloged program. A recataloged program is not available until the user who is recataloging it returns to the ECL prompt. You can use the ECL NEWPCODE command to ensure that you are running the latest version.

Note
For more information about NEWPCODE and VCATALOG, see Administering UniData on UNIX or Administering UniData on Windows NT or Windows 2000, and the UniData Commands Reference.

Direct Cataloging
Keep in mind the following points about direct cataloging: Compiled code is located in the program file in the UniData account where the program was compiled and cataloged. The VOC file in the account contains a pointer to the compiled code in the program file. Users in the same account can execute the program by entering the program name at the ECL prompt. Because users access the compiled code in the program file, developers do not need to recatalog the code if they recompile. When a user executes a directly cataloged program, UniData loads a copy of the program into the address space of the user. Because users execute the programs from their own address space, one user can recompile a directly cataloged program while another user is running the program.

74

Developing UniBasic Applications

Cataloging a UniBasic Program

Local Cataloging
Keep in mind the following points about local cataloging: Compiled code is located in the CTLG directory in the UniData account where the program was cataloged, as well as in the program file. CTLG is a directory, and each record is a compiled UniBasic program. The accounts VOC file contains a pointer to the compiled program in the CTLG. Users in the same account can execute the program by entering the program name at the ECL prompt. Developers must recatalog a program after recompiling, to place a new copy of the compiled code into the CTLG. When a user executes a locally cataloged program, UniData loads a copy of the program into the users address space. Because users execute the programs from their own address space, one user can recompile a directly cataloged program while another user is running the program.

Global Cataloging
Keep in mind the following points about global cataloging: When a user tries to globally catalog a UniBasic program, UniData checks to see if a copy of the program resides in memory. If a copy exists in memory, a process is currently executing either the program in question or a program that called the program in question. The program in question cannot be recataloged until all processes executing it, or all programs that called it, have completed. UniData displays an error message, and the CATALOG command fails. If a copy does not exist in memory, no one is currently executing it and no program that is still running has called it. UniData copies the new compiled code into the global catalog.

Global cataloging is the default. If you execute the CATALOG command without specifying local or direct cataloging, your program is globally cataloged.

Developing UniBasic Applications

75

Chapter 3 - Creating and Running a Program

Compiled code is located in a system wide global catalog. The default global catalog is /udthome/sys/CTLG (for UNIX) or \udthome\sys\CTLG (for Windows NT or Windows 2000). Even though Windows NT or Windows 2000 do not require file and path names that are case sensitive, UniData uses case-sensitive strings for comparing and searching for names in cataloged programs. Developers must recatalog a program after recompiling, to place a new copy of the compiled code into the global catalog. To run a globally cataloged program if you have a program with the same name cataloged locally or directly, precede the program name with an asterisk, as in *prog.name.

Note
A UniData installation can have more than one global catalog space. udthome determines which global catalog space a particular UniData session accesses. For more information about multiple global catalog spaces, see Administering UniData on UNIX or Administering UniData on Windows NT or Windows 2000. A system-wide global catalog space is a directory with 26 subdirectories named A through Z. Compiled code is stored in the subdirectory corresponding to the first letter of the program name. Cataloged programs that begin with nonalpha characters are stored in the subdirectory named X. The program name can be the same as the source and object, or you can specify a different name when you execute the CATALOG command.

Tip
Consider your program naming conventions if you are using global cataloging. Because the compiled code is placed in subdirectories according to name, you could have an unbalanced situation if a large number of your program names begin with the same letter (for instance, a general ledger application in which all the files begin with gl). A globally cataloged program is available to users in all UniData accounts. When a user executes a globally cataloged program, UniData checks to see if a copy already exists in shared memory.

76

Developing UniBasic Applications

Cataloging a UniBasic Program

If so, UniData notifies the udt process where to locate the copy in shared memory. If not, UniData loads a copy into shared memory for the user to execute.

Using the ECL CATALOG Command


To catalog a program, enter the following command at the ECL prompt: Syntax: CATALOG file.name cat.name prog.name [LOCAL | DIRECT] [FORCE] [NEWVERSION | newversion] By default, cataloged code is placed in the program directory with the source code and in the system global catalog. This makes the program available to all accounts. Users share one copy of the cataloged code. The following table describes each parameter of the syntax. Option file.name cat.name prog.name GLOBAL Features Specifies the file containing the program to be cataloged. Usually a basic program (BP) file. Specifies the name of the system catalog into which the object code is to be copied. Specifies the UniBasic program containing object code to be cataloged. This is the default option (do not specify it in the CATALOG command); the compiled code is placed in the program file and in the system global catalog. This makes the program available to all accounts. Users share one copy of the compiled code. Compiled code is located in the program file and in local catalog space. The LOCAL option is available on the local account. To use it from another account, catalog it in the other account also. Catalog Command Options

LOCAL

Developing UniBasic Applications

77

Chapter 3 - Creating and Running a Program

Option DIRECT

Features The compiled code is placed in the program file only and is available only on the local account. To use it on another account, catalog it in that account also; recataloging is not required when you recompile the program. Replace a cataloged program without prompting. Directs the shared basic code server (sbcs) to replace the current program version in shared memory with the new version just cataloged. To use NEWVERSION, you must have root privileges on UNIX or Administrator privileges on Windows NT or Windows 2000. Tip - The N option of the Alternate Editor (AE) FI command equates to NEWPCODE. For example, FIBCFN compiles a program and catalogs it (locally) with NEWPCODE. You need to use F (force) in conjunction with the N option. For more information, see the online help for AE. Note - For more information about the NEWVERSION option, see Administering UniData on UNIX or Administering UniData on Windows NT or Windows 2000. Catalog Command Options (continued)

FORCE NEWVERSION newversion

Reminder
You can execute a cataloged program from the ECL prompt or from any UniBasic program.

Removing a Catalog Entry


Use the ECL DELETE.CATALOG command to remove a cataloged program. For more information about this command, see the UniData Commands Reference.

78

Developing UniBasic Applications

Cataloging a UniBasic Program

CATALOG Examples
The following example shows the FORCE option to globally catalog the object code of the program CUST.XREF, whose source code is located in the BP.UTIL directory file:

Screen Example
:CATALOG BP.UTIL CUST.XREF FORCE

The next example shows the CATALOG command without the FORCE option. Because the program GPA was already cataloged, UniData requests verification before proceeding to recatalog the item.

Screen Example
:CATALOG BP GPA /usr/ud/sys/CTLG/GPA exists, do you want to overwrite?(Y/N)Y

The following example lists the contents of the CTLG file in the demo database:

Screen Example
:LIST CTLG LIST CTLG 17:39:37 Aug 04 1999 1 CTLG...... AddRecord DelRecord DUMMY EXAMPLE FndRecord UpdRecord 6 records listed :

Developing UniBasic Applications

79

Chapter 3 - Creating and Running a Program

Reminder
You must compile your programs successfully before you can catalog them. In the next example, UniData catalogs the compiled object code of the PSTLCODE_FMT program locally. Afterwards, notice the following: The local CTLG directory shows an entry for PSTLCODE_FMT. A VOC pointer exists that shows a path to a copy of the program (in this case, \UniData\demo\CTLG) and shows where the program source is kept (BP_SOURCE).

Screen Example
:CATALOG BP_SOURCE PSTLCODE_FMT LOCAL :LIST CTLG LIST CTLG 17:39:37 Aug 04 1999 1 CTLG...... AddRecord DelRecord DUMMY EXAMPLE FndRecord PSTLCODE_F MT UpdRecord 7 records listed :CT VOC PSTLCODE_FMT VOC: PSTLCODE_FMT: C D:\UNIDATA\DEMO\CTLG\PSTLCODE_FMT BP_SOURCE PSTLCODE_FMT :

80

Developing UniBasic Applications

Cataloging a UniBasic Program

In the next example, UniData directly catalogs the PSTLCODE_FMT program. Compare the VOC record to the previous example, and notice how the path to the program has changed. The DIRECT keyword causes UniData to create a VOC pointer that points to the file in which the program resides. UniData does not place a copy of the program in either CTLG directory.

Screen Example
:CATALOG BP_SOURCE PSTLCODE_FMT DIRECT PSTLCODE_FMT has been cataloged, do you want to overwrite(Y/N)? Y :CT VOC PSTLCODE_FMT VOC: PSTLCODE_FMT: C BP_SOURCE\_PSTLCODE_FMT :

Tip
To remove a copy of a program from the local or system CTLG directory, use the ECL DELETE command or DELETE.CATALOG. In ECLTYPE P, you also can use the DECATALOG command.

Developing UniBasic Applications

81

Chapter 3 - Creating and Running a Program

Running a UniBasic Program


You can run a program from AE or from ECL.

Running a Program from AE


To compile and run a program you are currently editing, enter the AE command FIBR at the AE command prompt. UniData compiles and files the program, runs the compiled version, and displays the ECL prompt. If compilation is unsuccessful, the last successfully compiled version runs.

Reminder
The FIR command files the source code you are editing and runs the compiled version. The two versions will be different if you have modified the program since the last compilation. Be sure to use the AE command FIBR to compile, file, and run the program.

Running a Program from ECL


The RUN command tells UniData to run a compiled UniBasic program. If the program is not globally cataloged, you must specify the directory file that contains the program (for example, BP). Syntax: RUN directory.file program.name [-option] If the program is globally cataloged, you must have a pointer to the directory file that contains the program in your VOC file to execute the globally cataloged version. To run the program, enter its cataloged name at the ECL prompt. Syntax: catalog.name [-option]

82

Developing UniBasic Applications

Running a UniBasic Program

In addition, you can run a program that is locally cataloged by entering the its name at the ECL prompt. UniBasic programs are usually stored in the BP directory. Syntax: program.name [-option]

RUN Options
The following table describes the options [-option] in the syntax for running a program.

Reminder
Several of the following RUN options invoke the UniBasic Debugger. To make full use of the debugger, you must prepare your program as explained in the Using the UniBasic Debugger.

Option -D -E -F -D -E

Description Invokes the UniBasic debugger immediately before the program executes. Invokes the UniBasic debugger when a warning or runtime error occurs. Invokes the UniBasic debugger when a fatal error occurs. Invokes the UniBasic debugger immediately. After you execute the program from the debugger, if UniData encounters a warning or runtime error, UniData returns to the debugger. Invokes the UniBasic debugger immediately. After you execute the program from the debugger, if UniData encounters a fatal error, UniData returns to the debugger. Creates a cross-reference report (program profile). For further information, see Reporting Execution Time later in this chapter. Program Run Options

-D -F

-G

Developing UniBasic Applications

83

Chapter 3 - Creating and Running a Program

Option -N

Description Displays output without pausing at the bottom of the screen. Without this option, scrolling stops at the bottom of each page, prompting the user to press ENTER to continue. Tip - Use UDT.OPTIONS 32 to direct UniData to retain or suppress the HEADING statement when the no page option is used.

-P -S

Routes all program output to the system printer. Suppresses warning messages. Program Run Options (continued)

UDT.OPTIONS
You can determine how UniBasic sets an uninitialized variable with UDT.OPTIONS 15 on for empty string or off for zero. For more information about UDT.OPTIONS, see the UDT.OPTIONS Commands Reference.

Reporting Execution Time


UniData generates a program profile report when you run or execute a program with the -G option. If you previously compiled the program with the -G option, the profile also reports on internal subroutines. UniData creates two reports that it stores in the current directory. Both reports are of the same format, and are named as follows:
"profile."@USERNO (for UNIX) or "profile."pid

(for Windows NT or Windows 2000) Calculates CPU execution time. For example, the file profile.3 would be a report of CPU time generated for the user whose user number is 3.

"profile.elapse."@USERNO (for UNIX) or "profile.elapse."pid (for Windows NT or

Windows 2000) Calculates real execution time (CPU and I/O). For example, profile.elapse.3 would be the corresponding report of real execution time for the same user.

84

Developing UniBasic Applications

Running a UniBasic Program

Note
For Windows NT or Windows 2000, run the LISTUSER command to determine the process ID (pid). It is listed as USRNBR. For more information about program profiling, see Administering UniData on UNIX or Administering UniData on Windows NT or Windows 2000. Layout of Profile Reports Summary Section Summary statistics regarding execution time are listed for each program, subroutine, and called program; entries are sorted by decreasing execution time. Body Section Each program or subroutine is assigned an identifying index number, in descending order of execution time. Each index is analyzed in a subsection delineated by dashed lines. Within the subdivided sections, the first column lists the index number for the program or subroutine analyzed in that section. Parent programs and subroutines are analyzed on the lines preceding the index number; the indexed item is analyzed on the line containing the index number; and child programs or subroutines are analyzed on subsequent lines. The following UNIX example is the profile report for the UPDATE_ORDER program in Appendix A - Sample Program:

Screen Example
%time cumsecs seconds %time cumsecs seconds 25.0 25.0 25.0 25.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.02 0.04 0.06 0.08 0.08 0.08 0.08 0.08 0.08 0.08 0.08 0.02 0.02 0.02 0.02 0.00 0.00 0.00 0.00 0.00 0.00 0.00 calls name calls name 1 3 2 1 1 1 3 1 1 1 1 BP/_UPDATE_ORDER BP/_UPDATE_ORDER:DISPLAY_SCREEN BP/_UPDATE_ORDER:DISPLAY_DATA BP/_UPDATE_ORDER:ALTER_RECORD BP/_UPDATE_ORDER:OPEN_FILES BP/_UPDATE_ORDER:INITIALIZE BP/_UPDATE_ORDER:GET_ORDER_NUMBER /users/ud_51/sys/CTLG/d/DISPLAY_MESSAGE BP/_UPDATE_ORDER:GET_RECORD_COMMAND BP/_UPDATE_ORDER:WRITE_RECORD BP/_UPDATE_ORDER:EXIT

Developing UniBasic Applications

85

Chapter 3 - Creating and Running a Program

index

%time

self descendents

called/total called+self called/total 1 3/3 2/2

[1] [2]

100.0

0.02 0.02 0.02

0.06 0.00 0.00

parents name index children <spontaneous> BP/_UPDATE_ORDER [1] BP/_UPDATE_ORDER:DISPLAY_SCREEN BP/_UPDATE_ORDER:DISPLAY_DATA

[3] 0.00 0.02 1/1 BP/_UPDATE_ORDER:GET_RECORD_COMMAND [5] 0.00 0.00 1/1 BP/_UPDATE_ORDER:OPEN_FILES [6] 0.00 0.00 1/1 BP/_UPDATE_ORDER:INITIALIZE [7] 0.00 0.00 3/3 BP/_UPDATE_ORDER:GET_ORDER_NUMBER [8] 0.00 0.00 1/1 BP/_UPDATE_ORDER:EXIT [11] ---------------------------------------------0.02 0.00 3/3 BP/_UPDATE_ORDER [1] [2] 25.0 0.02 0.00 3 BP/_UPDATE_ORDER:DISPLAY_SCREEN [2] ---------------------------------------------0.02 0.00 2/2 BP/_UPDATE_ORDER [1] [3] 25.0 0.02 0.00 2 BP/_UPDATE_ORDER:DISPLAY_DATA [3] 0.00 0.00 1/1 /users/ud_51/sys/CTLG/d/DISPLAY_MESSAGE [9] ---------------------------------------------0.02 0.00 1/1 BP/_UPDATE_ORDER:GET_RECORD_COMMAND [5] [4] 25.0 0.02 0.00 1 BP/_UPDATE_ORDER:ALTER_RECORD [4] 0.00 0.00 1/1 BP/_UPDATE_ORDER:WRITE_RECORD [10] ---------------------------------------------0.00 0.02 1/1 BP/_UPDATE_ORDER [1] [5] 25.0 0.00 0.02 1 BP/_UPDATE_ORDER:GET_RECORD_COMMAND [5] 0.02 0.00 1/1 BP/_UPDATE_ORDER:ALTER_RECORD [4] ---------------------------------------------0.00 0.00 1/1 BP/_UPDATE_ORDER [1] [6] 0.0 0.00 0.00 1 BP/_UPDATE_ORDER:OPEN_FILES [6] ----------------------------------------------

86

Developing UniBasic Applications

Running a UniBasic Program

0.00 0.00 1/1 BP/_UPDATE_ORDER [1] [7] 0.0 0.00 0.00 1 BP/_UPDATE_ORDER:INITIALIZE [7] ---------------------------------------------0.00 0.00 3/3 BP/_UPDATE_ORDER [1] [8] 0.0 0.00 0.00 3 BP/_UPDATE_ORDER:GET_ORDER_NUMBER [8] ---------------------------------------------0.00 0.00 1/1 BP/_UPDATE_ORDER:DISPLAY_DATA [3] [9] 0.0 0.00 0.00 1 /users/ud_51/sys/CTLG/d/DISPLAY_MESSAGE [9] ---------------------------------------------0.00 0.00 1/1 BP/_UPDATE_ORDER:ALTER_RECORD [4] [10] 0.0 0.00 0.00 1 BP/_UPDATE_ORDER:WRITE_RECORD [10] ---------------------------------------------0.00 0.00 1/1 BP/_UPDATE_ORDER [1] [11] 0.0 0.00 0.00 1 BP/_UPDATE_ORDER:EXIT [11] ----------------------------------------------

----------------------------------------------

The summary section of the report provides information in the following columns. Parameter %time cumsecs seconds calls name Description Percentage of the total runtime used by this program or subroutine. Execution time, in number of seconds, for this program or subroutine and all called programs and subroutines. Execution time, in number of seconds, for this program or subroutine alone. Number of times this program or subroutine is called. Name of the program or subroutine. Program Profile Summary

Developing UniBasic Applications

87

Chapter 3 - Creating and Running a Program

The body of the report provides information in the following columns. Parameter index %time self descendents called Description An identifying number assigned to this program or subroutine. Index numbers are assigned in descending order of execution time. Percentage of the total program runtime used by this program or subroutine and its descendents. Execution time for this program or subroutine. Execution time for descendents of this program or subroutine. Line contents differ according to the line of the subsection you are reading: called/total Lines preceding the index analyze parents; lists number of times this index is called by the parent listed in the name field. called+self Line containing the index; lists number of times the routine is called and the number of times it calls itself. called/total Lines following the index number analyze children and descendents; lists number of times this index calls the child listed in the name field. name index Name of the program or subroutine analyzed in this row of the report subsection. Index identifying the program or subroutine listed in the name field. Program Profile Body The tables that follow the next example explain the detail report lines for item [3] in the report. The subject lines are repeated here.

88

Developing UniBasic Applications

Running a UniBasic Program

Screen Example

called/total parents called+self name index called/total children <spontaneous> ---------------------------------------------0.02 0.00 2/2 BP/_UPDATE_ORDER [1] [3] 25.0 0.02 0.00 2 BP/_UPDATE_ORDER:DISPLAY_DATA [3] 0.00 0.00 1/1 /users/ud_51/sys/CTLG/d/DISPLAY_MESSAGE [9] ---------------------------------------------index %time self descendents

The following table describes the first line of the preceding report segment. Data
0.02

Column self descendents called/total parents index

Description The execution time for parent (calling program); in this case, BP/_UPDATE_ORDER. The execution time for parent (calling program); in this case, BP/_UPDATE_ORDER. The number of times parent BP/_UPDATE_ORDER called BP/_UPDATE_ORDER:DISPLAY_DATA. The parent (calling subroutine). The index number for BP/_UPDATE_ORDER.

0.00

2/2

BP/_UPDATE_ORDER [1]

Program Profile Detail: First Line

Developing UniBasic Applications

89

Chapter 3 - Creating and Running a Program

The next table describes the second line of the preceding report segment. Data
[3]

Column index %time

Description The index number for


BP/_UPDATE_ORDER:DISPLAY_DATA .

25.0

The percentage of the total program runtime used by BP/_UPDATE_ORDER:DISPLAY_DATA. The execution time for
BP/_UPDATE_ORDER:DISPLAY_DATA.

0.00

self descendents name index number

0.02

The execution time for


/users/ud_52/sys/CTLG/d/DISPLAY_MESSAGE.

BP/_UPDATE_ORDER: DISPLAY_DATA [3]

The name of this subroutine. The index number for BP/_UPDATE_ORDER:


DISPLAY_DATA .

Program Profile Detail: Second Line The final table describes the third line of the preceding report segment. Data
0.00

Column self

Description The execution time for child (called program); in this case, /users/ud_52/sys/CTLG/d/ DISPLAY_MESSAGE. The execution time for child (called program); in this case, /users/ud_52/sys/CTLG/d/ DISPLAY_MESSAGE.

0.00

descendents

Program Profile Detail: Third Line

90

Developing UniBasic Applications

Running a UniBasic Program

Data
1/1

Column called/total

Description The number of times parent


BP/_UPDATE_ORDER:DISPLAY_DATA

called child

/users/ud_52/sys/CTLG/d/DISPLAY_MESSAGE.

/users/ud_52/sys/CTLG/d/ DISPLAY_MESSAGE [9]

child index

The name of this child (called program). The index number for
/users/ud_52/sys/CTLG/d/ DISPLAY_MESSAGE.

Program Profile Detail: Third Line (continued)

Developing UniBasic Applications

91

Chapter 3 - Creating and Running a Program

92

Developing UniBasic Applications

Chapter 4 - Maintaining Data in Files

This chapter provides the information you need to select the correct command or function for maintaining data in files through UniBasic programs. For the syntax of these commands, see the UniBasic Commands Reference.

Developing UniBasic Applications

93

Chapter 4 - Maintaining Data in Files

In This Chapter
This chapter introduces the concepts and commands you use to maintain files. It consists of the following sections: UniData Locks Database Triggers Maintaining Files UniData Hashed Data Files Alternate Key Indexes Non-UniData Sequential Files

Opening Files Selecting Records Reading, Writing, and Deleting Data from Files Closing Files Accessing Data in Unopened Files

94

Developing UniBasic Applications

UniData Locks

UniData Locks
In a multiuser system, record and file locks, when used consistently, prevent more than one user from accessing the same record at the same time. UniBasic locks are advisory only. You must observe locking conventions consistently in all of your applications to prevent one user from overwriting another users updates. You can use resource locks to reserve computer resources for your exclusive use. To set, check, and release UniBasic locks, you must use the commands specifically created for the purpose. For information about using UniBasic locks, see Chapter 5 - Using UniData Locks.

Tip
Another way to ensure consistency among files is through UniData transaction processing. For more information, see Chapter 9 - UniBasic Transaction Processing. or Appendix B - UniBasic Transaction Processing Concepts.

Developing UniBasic Applications

95

Chapter 4 - Maintaining Data in Files

Database Triggers
A database trigger specifies a UniBasic subroutine to call when a user attempts to access a record. The UniBasic subroutine can then allow or prevent access. Triggers are tied to record updates (modifying or inserting) and deletions. A UniData trigger can be used to validate an attempt to make a change to the database against user-defined constraints, or business rules, and allow the change only if the constraint is satisfied. Triggers are associated with the data file, so the business rules are applied to any access to the file or record, not just access through a particular application. For instance, a trigger subroutine might allow only certain users to update records in a UniData file, or prevent some records from being updated at all, or prevent entry of the null value.

Trigger Rules
UniData triggers are governed by the following rules: A UniData trigger must be a globally cataloged UniBasic subroutine. When running Network File Access (NFA), the trigger subroutine and the trigger itself must reside on the server where the hashed file resides, not on the host. UniData triggers are activated before the update or delete. A trigger can invoke another trigger. UniData does not limit the number of levels you can nest triggers. Be careful to use conditional statements to avoid infinite loops that can be caused by nested triggers. You can call an external C routine from a trigger. File types: Hashed files You can use triggers only with static or dynamic hashed files. The files can be recoverable or nonrecoverable. No DIR or multilevel directories You cannot set a trigger on a DIR file or a multilevel directory. You can associate triggers with the static or dynamic subfiles within a multilevel file, but not with the multilevel file itself.

Writing from a trigger subroutine:

96

Developing UniBasic Applications

Database Triggers

Writes allowed to DIR files You can write to a directory file from within a trigger subroutine. You might want to do so to create a log of trigger operations. Writes allowed to hashed files You can include write commands within a trigger subroutine.

The Nature of Triggers


Triggers are composed of the trigger definition, which is stored in the file header, and a globally cataloged UniBasic subroutine that is called when you attempt to update or delete a record. Use the ECL CREATE.TRIGGER command to define a trigger. If the file header already contains a trigger by the same name, UniData does not overwrite the existing trigger. Instead, you must delete the existing trigger before you can create the new one. Three ECL commands let you create, delete, and list triggers. Note that these changes to the trigger in the file header do not affect the UniBasic subroutine. Command syntax and details about how to code these commands follow this section. The UniBasic trigger subroutine can contain any operation on a file, including writing to it. For example, the subroutine could stipulate that certain kinds of record modifications are not allowed for a file, or it might place restrictions on whether users can delete records from a particular file. You can change the underlying trigger subroutine without having to delete and re-create the trigger.

Note
To create or delete a trigger, you must be the owner of the file at the operating system level, or you must have root privileges on UNIX or Administrator privileges on Windows NT or Windows 2000.

ECL Commands and Triggers


This section discusses ECL command that create or maintain triggers, and lists ECL commands that are affected by triggers.

Developing UniBasic Applications

97

Chapter 4 - Maintaining Data in Files

ECL Commands That Create or Maintain Triggers


The following ECL commands create or maintain triggers. For the syntax of these commands, see the UniData Commands Reference. CREATE.TRIGGER Creates a trigger in a file header that calls a UniBasic subroutine. DELETE.TRIGGER Deletes the trigger definition from a file header. It does not affect the UniBasic trigger subroutine. LIST.TRIGGER Displays a list of triggers.

ECL Commands Affected by Triggers


The UniData triggers feature has an impact on how some ECL commands behave. The following commands have changed behavior. For the syntax of these commands, see the UniData Commands Reference. CLEAR.FILE COPY DELETE ED MODIFY UNIENTRY

UniBasic Commands Affected by Triggers


The following UniBasic commands invoke triggers: DELETE DELETEU WRITE WRITEU WRITEV

98

Developing UniBasic Applications

Database Triggers

WRITEVU

If a trigger returns a constraint violation, control is passed to the ON ERROR clause of the command, if one exists. If no ON ERROR clause is included in the command, the UniBasic program aborts.

Developing UniBasic Applications

99

Chapter 4 - Maintaining Data in Files

Tip
The UniBasic STATUS function returns the status of the preceding command. You can place it within the trigger subroutine to learn about the status of individual commands executed within the trigger. If you place it immediately after the statement that calls the trigger, it returns the status of the UniBasic command as determined by the trigger. These values are listed in UniBasic STATUS Function Return Values later in this chapter. The trigger subroutine also returns a value indicating its status in the parameter execstat; the values returned in execstat are listed in the Parameters sections for the UPDATE and DELETE trigger subroutines.

Writing an UPDATE Trigger Subroutine


A UniBasic subroutine or function serves as the UPDATE trigger that is executed when a user attempts to update a record in the subject file.

Tip
You can call an external C routine from the UniBasic subroutine or function that is called from a trigger.

Subroutine
The following SUBROUTINE definition must be the first noncomment line in the UniBasic trigger subroutine. Syntax: SUBROUTINE trigname(execstat, dictflag, filename, record.ID.expr, recordval) The parameters in this statement are defined later in this section.

Function
The following FUNCTION definition must be the first line in the UniBasic trigger function. Syntax: FUNCTION trigname(dictflag, filename, record.ID.expr, recordval)

100

Developing UniBasic Applications

Database Triggers

The function must include the following statement: RETURN excstat

Parameters
The following table describes each parameter of the syntax. Parameter trigname execstat Description The name of the globally cataloged subroutine. The execution status returned by the trigger subroutine: 0 No updates are allowed. 1 Updates are allowed. 2 Updates are allowed, using the return recordval. dictflag DICT Indicates that the trigger is operating on the dictionary file. Indicates that the trigger is operating on the data file. Note - The quotation marks are required. filename record.ID.expr The name of the file on which the trigger is operating. The record to be updated. Trigger Parameters

Developing UniBasic Applications

101

Chapter 4 - Maintaining Data in Files

Parameter recordval

Description The input record value submitted to the UPDATE trigger. recordval is both an input and output parameter. The trigger can change this value (for example, by performing a conversion). Then, if the trigger sets execstat to 2, this value is passed back in recordval and updates the data record. Only strings and numbers are valid. Note - If the value returned in recordval is invalid, the record is not updated, even if the trigger subroutine sets execstat to 2. In this case, the UniBasic STATUS function returns 3 when executed immediately after the command that calls the trigger. Only strings and numbers are valid. Trigger Parameters (continued)

UPDATE Trigger Example


The following example begins with an UPDATE trigger subroutine called TRIG1. Because the return status is always 5, no record in the file can be updated.

Program Example
SUBROUTINE TRIG1(exec.stat,dict.flag,trig.name,rec.id.expr,rec.val) exec.stat=5 RETURN

102

Developing UniBasic Applications

Database Triggers

Next, we create the trigger, associate it with the ORDERS file, and list the triggers associated with the ORDERS file:

Screen Example
:CREATE.TRIGGER ORDERS TRIG1 UPDATE :LIST.TRIGGER ORDERS BEFORE UPDATE TRIGGER: TRIG1 BEFORE DELETE TRIGGER: not defined

Finally, we attempt to copy record 969 into record 970 in the ORDERS file, and the trigger prevents the copy:

Screen Example
:COPY FROM ORDERS TO ORDERS 969,970 Cannot update 970, due to trigger constraint. 0 records copied

Writing a DELETE Trigger Subroutine


A UniBasic subroutine or function serves as the DELETE trigger that is executed when a user attempts to delete a record in the subject file.

Tip
You can call an external C routine from the UniBasic subroutine or function that is called from a trigger.

Developing UniBasic Applications

103

Chapter 4 - Maintaining Data in Files

Subroutine
The following SUBROUTINE definition must be the first line in the UniBasic trigger subroutine. Syntax: SUBROUTINE trigname(execstat, dictflag, filename, record.ID.expr) The parameters in this statement are listed later in this section.

Function
The following FUNCTION definition must be the first line in the UniBasic trigger function. Syntax: FUNCTION trigname(dictflag, filename, record.ID.expr) The function must include the following statement: RETURN execstat

Parameters
The following table describes each parameter of the syntax. Parameter trigname execstat Description The name of the globally cataloged subroutine. The execution status returned by the trigger subroutine. Valid values for this include: 0 Delete is not allowed 1 Delete is allowed Trigger Parameters

104

Developing UniBasic Applications

Database Triggers

Parameter dictflag

Description DICT Indicates that the trigger is operating on the dictionary file. Indicates that the trigger is operating on the data file. Note - The quotation marks are required.

filename record.ID.expr

The name of the file the trigger is operating on. The record to be deleted. Trigger Parameters (continued)

DELETE Trigger Example


The following example begins with a DELETE trigger subroutine called DEL_TRIG that always returns 1 and always allows records to be deleted:

Program Example
SUBROUTINE DEL_TRIG(exec.stat,dict.flag,trig.name,rec.id.expr,rec.val) exec.stat=1 RETURN

Reminder
After creating and compiling the subroutine, you must catalog it globally. Next, we create the trigger and associate it with the ORDERS file:

Screen Example
:CREATE.TRIGGER ORDERS DEL_TRIG DELETE

Developing UniBasic Applications

105

Chapter 4 - Maintaining Data in Files

Finally, we delete records in the ORDERS file. The trigger always allows the deletion because the subroutine sets the execution status to 1.

Screen Example
:DELETE ORDERS 912 '912' deleted. :

UniBasic STATUS Function Return Values


The UniBasic STATUS function returns values that give information about trigger operations. Return Value 0 1 2 No error. System error, such as a damaged file. Constraint violation. In this case, the UniBasic trigger subroutine returns a value of 0 in the parameter excstat, indicating that the update or delete is not allowed. Trigger execution error or unexpected return from trigger routine (for example, trigger subroutine is not cataloged). STATUS Function Values Description

Note
Most UniBasic commands and some ECL commands, within a trigger or outside a trigger, set the value of STATUS.

106

Developing UniBasic Applications

Database Triggers

Warning
When you write UniBasic programs or subroutines, always include the ON ERROR clause for statements that update hashed files. This is especially important when using triggers, because any failure caused by a trigger aborts the UniBasic program unless the ON ERROR clause is present.

Troubleshooting
If a trigger does not work correctly, check the following: Debug and test trigger subroutines. Check the return values. Are they what UniData expects? Is the trigger subroutine cataloged?

Developing UniBasic Applications

107

Chapter 4 - Maintaining Data in Files

Maintaining Files
You can write UniBasic programs to maintain hashed data files, alternate key indexes, and nonUniData sequential files. The following sections describe how to maintain them: UniData Hashed Data Files Alternate Key Indexes Non-UniData Sequential Files

UniData Hashed Data Files


The types of UniData files that you can maintain with UniBasic programs are introduced in Chapter 1 - Introduction to UniBasic. This section suggests the steps you might follow to maintain files.

Maintaining UniData Hashed Files


The following procedure is one that you might follow when updating a UniData hashed file. (For a sample program, see Appendix A - Sample Program.) 1. Use the OPEN command to open the data file. 2. Use one of the following methods to select records: Create a select list using SELECT, SELECTINDEX, UniQuery SELECT, UniData SQL SELECT, or a paragraph select. Set a pointer in an alternate index with SETINDEX.

3. Use one of the following commands to read data from selected records: READU (use this command to set an exclusive lock), READ, READV, READVL, READVU, READNEXTTUPLE, READFWD, READBCK, READFWDL, READBCKL, READFWDU, READBCKU 4. Manipulate data in arrays. See Chapter 6 - Working with Data in Programs.

108

Developing UniBasic Applications

Maintaining Files

5. Use one of the following commands to write data to selected records: WRITE, WRITEU, WRITEV, WRITEVU, WRITET 6. Use the CLOSE command to close the data file.

Alternate Key Indexes


Alternate key indexing speeds searching for records within a database. Alternate keys allow access to records without the need to use the record ID or read through the entire file. An alternate key index consists of values for the alternate key and the associated @IDs of each record.

The Primary Key


UniData requires that each record in the database have a unique identifier, which is called the record ID, @ID, or primary key. The UniData hashing algorithm uses the @ID to locate the record in the database. When UniData looks for a record, it uses the @ID as the key to finding the record quickly.

Tip
Create an index for @ID to speed access to data records.

The Alternate Key


UniData allows only one primary key for a record. If you are searching for a record based on the primary key, searches are fast. Most of the time, however, searches are based on other information contained in a record, such as a name, city, phone number, or the result of a virtual attribute calculation. These searches are slower than a search based on the primary key. You can create alternate keys any time after you create the data file. For more information, see Using UniData or the CREATE.INDEX command in the UniData Commands Reference.

Developing UniBasic Applications

109

Chapter 4 - Maintaining Data in Files

Duplicate Alternate Keys


Although primary keys must be unique, duplicate alternate keys can be entered. To block the entry of duplicate keys in indexes of non-RFS files, specify the NO.DUPS keyword when you create the index (with CREATE.INDEX). However, if you do so, you will not be able to build the index by using BUILD.INDEX if duplicate values are already present in the key attribute in your data file. Also, any WRITE command that attempts to create a duplicate alternate key will generate a fatal error if no ON ERROR clause is coded.

Note
The Recoverable File System (RFS) does not support NO.DUPS. You can detect duplicate keys by executing the ECL command DUP.STATUS ON, then checking for a STATUS function return value of 10 immediately after the following UniBasic commands: WRITE, WRITEU, WRITEV, WRITEVU READFWD, READFWDL, READFWDU READBCK, READBCKL, READBCKL

Creating and Maintaining Alternate Indexes


You might perform the following steps to create and manage alternate indexes. For the syntax of the ECL commands, see the UniData Commands Reference. 1. Use the ECL CREATE.INDEX command to create the index. 2. Use the ECL BUILD.INDEX command to load alternate keys into the index.

Note
You cannot execute BUILD.INDEX while users are accessing the file. 3. Use the ECL ENABLE.INDEX or DISABLE.INDEX command to enable or disable (respectively) the automatic updating of the alternate key index. You also can use the ECL UPDATE.INDEX command to update the index manually.

110

Developing UniBasic Applications

Maintaining Files

4. Use the SELECTINDEX command to build a select list based on the alternate index file, or use the SETINDEX command to set a pointer to an alternate key value in the index. 5. Read records from the UniData file by using the alternate index and any of the following UniBasic commands: READFWD, READFWDL, REAFWDU, READBK, READBKL, READBKU

Using Alternate Index Files


Use the following alternate index commands and functions to speed access to UniData files. For syntax and usage, see the UniBasic Commands Reference. Command/ Function FILEINFO Action Returns the name of the index to which the last SETINDEX statement was applied. Also requests the key value of the index for the record that was read by the last browsing statement, such as READFWD or READBCK. Retrieves one record ID from an index, assigns the contents of the record to a dynamic array, and assigns the record ID to the @ID variable. Same as READBCK, but places a shared lock on the record. Same as READBCK, but places an exclusive lock on the record. Retrieves one record ID from an index, assigns the contents of the record to a dynamic array, and assigns the record ID to the @ID variable. Same as READFWD, but places a shared lock on the record. Alternate Index Commands

READBCK

READBCKL READBCKU READFWD

READFWDL

Developing UniBasic Applications

111

Chapter 4 - Maintaining Data in Files

Command/ Function READFWDU SELECTINDEX

Action Same as READFWD, but places an exclusive lock on the record. Creates a select list based on an alternate key. The list can contain all records for this alternate index or a subset based on a particular key value. Sets a pointer to the initial alternate key value in the alternate index. Use READFWD or READBACK to traverse the index. Alternate Index Commands (continued)

SETINDEX

The following program demonstrates use of an alternate index file for the demonstration database file INVENTORY. The index is on attribute 3, PROD_NAME. The program prompts the user for a product, and then it prints all records for that product.

Program Example
OPEN "INVENTORY" TO open.file ELSE PRINT "Open error." ;STOP PRINT "Enter product to display: ";INPUT search_product search_product = OCONV(search_product, "MCT") new_product = search_product SETINDEX "PROD_NAME", search_product ON open.file IF STATUS()>0 THEN PRINT "Record not found.";STOP LOOP UNTIL new_product <> search_product READFWD dyn.array.var FROM open.file ELSE PRINT "Record not found." new_product = EXTRACT(dyn.array.var,3) IF new_product = search_product THEN PRINT "Record is: ":dyn.array.var REPEAT END

112

Developing UniBasic Applications

Maintaining Files

The following example shows user input and output from the preceding program:

Screen Example
Enter product to display: ?trackball Record is: 01/17/1996p09:00AMpTrackballpDeluxe ModelpGrayp2373p5799p30 Record is: 01/18/1996p10:00AMpTrackballpEconomy ModelpWhitep2299p4499p30 Record is: 01/11/1996p12:00PMpTrackballpSuper Deluxe ModelpGrayp494p9899p70

Non-UniData Sequential Files


You can use the following types of commands to maintain data in non-UniData sequential files: Sequential File Commands Specifically for manipulating non-UniData sequential files. UniData Hashed File Commands You can use these after you make an entry for the sequential file in the UniData VOC file. For instructions about creating a VOC entry, see Using UniData.

Sequential File Commands


UniBasic provides commands to manipulate non-UniData sequential files, including files that contain data stored in binary format. These commands access the file via the operating system file name or place name and therefore do not use a VOC entry to locate the file. Access to different types of sequential files lets you modify print jobs, log files, and source code using a UniBasic program.

Developing UniBasic Applications

113

Chapter 4 - Maintaining Data in Files

UniBasic provides several sets of commands to handle sequential file operations. To open a sequential file, use either the OSOPEN or OPENSEQ command. After opening a file, you can read and write the entire file, read and write one record at a time, or read and write the file beginning at a specified byte location. The following table lists the commands that pertain to sequential read and write operations. Read/Write Starting at a Specified Byte Location OSBREAD OSBWRITE n/a

Read/Write Entire File OSREAD OSWRITE n/a

Read/Write One Record READSEQ WRITESEQ WRITESEQF WEOFSEQ

Commands for Reading/Writing Non-UniData Sequential Files To close a sequential file, use either the OSCLOSE or CLOSESEQ command. To delete a sequential file, use the OSDELETE command.

Note
Even though you can use any combination of sequential read and write commands, you should be careful to avoid writing data in unintended areas of the file. Informix recommends you become thoroughly familiar with them before you mix their use. For the syntax and use of these commands, see the UniBasic Commands Reference. In addition, if you have opened a named pipe, you cannot use the READSEQ command to read a record from it, and you cannot use WRITESEQ or WRITESEQF to write a record to it. You must use the OSBREAD and OSBWRITE commands.

114

Developing UniBasic Applications

Maintaining Files

Warning
The UniBasic OSDELETE command is intended for use on OS-type files (not UniData hashed files). If you execute the command against a recoverable hashed file, UniData could crash immediately or at the next RFS checkpoint. If the command is executed against a nonrecoverable hashed file, UniData will not crash, but other unpredictable problems could occur.

UniData Hashed File Commands


You can use hashed file commands (OPEN, READ, WRITE, CLOSE, and DELETE) on nonUniData sequential files that have VOC entries. However, we discourage this practice because UniData could, in some cases, insert characters in the file, even upon opening it. Use the sequential file commands listed earlier, or operating system commands.

Developing UniBasic Applications

115

Chapter 4 - Maintaining Data in Files

Opening Files
UniBasic provides the following commands for opening files. You must open a file in a UniBasic program before you can access the data in it. Command OPEN Action Open a file. Generally, you store a pointer to the open file in a variable. However, you can open a default file by omitting the variable. Then you can execute file-level commands against this default file by again omitting the file variable. Open a sequential file, starting at the beginning of the file. Open a sequential file that does not use CHAR(10) as the line delimiter. Commands That Open Files For the syntax and use of these commands, see the UniBasic Commands Reference.

OPENSEQ OSOPEN

Example
The following program segment is taken from the sample program in Appendix A - Sample Program. It demonstrates opening a UniData hashed file.

Program Example
OPEN_FILES: OPEN "CLIENTS" TO CLIENT_FILE ELSE MESSAGE = "The CLIENT file could not be opened." CALL DISPLAY_MESSAGE(MESSAGE) STOP END

116

Developing UniBasic Applications

Selecting Records

Selecting Records
Some ways you might determine which record(s) to retrieve include: Prompting the user or a data stack for record ID(s) or other selection criteria. Performing calculations. Executing conditional tests.

Creating a Select List of Record IDs


The record IDs of selected records are placed in a select list. UniData provides ten storage areas identified by their number: 0 through 9. If you do not specify a list number when you select records IDs, UniData stores them in list 0. You can use multiple select lists to process data from several files simultaneously. UniBasic supports record selection by internal and external statements and commands, or externally from ECL, a paragraph, or a paragraph that performs an ECL (UniQuery or UniData SQL) SELECT statement. After you create a select list, the prompt changes to a greater than symbol (>), any subsequent select statement acts against that list. You must delete or deactivate the select list before retrieving IDs based on completely new criteria. For information about deleting and deactivating select lists, see the ECL commands CLEARSELECT and SAVE.LIST in the UniData Commands Reference or the UniBasic CLEARSELECT command in the UniBasic Commands Reference.

Saving a Select List


Select lists are available only during the current work session. You can save a select list to a file by using the UniBasic WRITELIST command. Then, when you want to use the list again, execute the SELECT command followed by the name of the file.

Developing UniBasic Applications

117

Chapter 4 - Maintaining Data in Files

Internal Select
You can select records within a UniBasic program using the following commands. Command SELECT SELECTINDEX Action Collect a list of all @IDs from a specified file. Create a select list based on an alternate key index. The list can be made up of an entire index or can be limited to a particular alternate key value or values. Note - You must use the SETINDEX command to set the pointer before you can read records. FORMLIST EXECUTE a UniQuery SELECT EXECUTE a UniData SQL SELECT Create a select list from a dynamic array. Execute a UniData SQL or UniQuery SELECT command using the UniBasic EXECUTE or EXECUTESQL command to create a list of record IDs. Note - These SELECT statements offer the advantage of letting you use selection criteria. SELECTINFO Determine if a select list is active. Selecting Records within a UniBasic Program For the syntax and use of UniBasic commands, see the UniBasic Commands Reference. For the syntax and use of UniQuery commands, see Using UniQuery. For the syntax and use of UniData SQL SELECT, see the UniData SQL Commands Reference.

External Select
You can select records from the ECL prompt or from a UniData paragraph. Then run the program or paragraph using the select list.

118

Developing UniBasic Applications

Selecting Records

Examples of External Select In the following example, a UniQuery SELECT is executed from the ECL prompt. The > prompt indicates that a select list is active. The UniBasic program executed from this prompt uses the active select list.

Screen Example
:SELECT INVENTORY WITH QUANTITY > '999' >RUN BP OVERSTOCK_REPORT

The following example UniData paragraph selects a stored select list, and then it runs a UniBasic program using the selected records:

Program Example
PA SELECT INVENTORY3 RUN BP NEWITEMS_REPORT

Clearing a Select List


Once you create a select list, any subsequent select statement acts against that list. You must delete or deactivate the active select list before retrieving IDs based on completely new criteria. CLEARSELECT empties a record ID list created by a SELECT statement. You can clear one or all select lists. The ECL commands CLEARSELECT and SAVE.LIST also clear or deactivate select lists. For more information about these commands, see the UniData Commands Reference.

Developing UniBasic Applications

119

Chapter 4 - Maintaining Data in Files

Reading, Writing, and Deleting Data from Files


This section describes how to read, write, and delete data from files.

Getting Ready to Read


Before you start reading records, you might want to take one of the following steps: Make a record ID available to the program with the UniBasic READNEXT or READNEXTTUPLE command. Set a pointer to an initial alternate key value in an alternate index with the SETINDEX command.

Reminder
After you open a file and before you read data, you need to select records for processing. For information about selecting records and creating select lists of record IDs, see the previous section.

Reading Record IDs from a Select List


After you have selected record IDs for processing, you can use the UniBasic READNEXT command to assign the next record ID from an active select list to a variable.

Reading Data from Files


Reminder
You must understand the UniBasic record locking system to select the appropriate combination of commands to prevent data inconsistency, which can result from multiple users updating files at the same time. For more information, see Chapter 5 - Using UniData Locks.

120

Developing UniBasic Applications

Reading, Writing, and Deleting Data from Files

UniBasic supplies a number of commands for reading records so that the data in them can be manipulated in a UniBasic program. You will want to use the proper command for the type of file from which you are reading, the type of variable you are reading into, and the type of lock (if any) you want to check for and set. The following table introduces the UniBasic commands that read records from files and data from records. READ Command MATREAD Action Assigns values in attributes of a record to corresponding elements of a dimensioned array regardless of lock status. Retains existing locks. If the record is available (unlocked or shared lock), sets a shared lock on the record. Assigns the contents of the record to a dimensioned array. If the record is available (no locks), sets an exclusive lock. Assigns the attributes of the record to a dimensioned array. Reads a record regardless of lock status and assigns its contents to a dynamic array. Retains existing locks. If the record is available (unlocked or shared lock), sets a shared lock on the record. Assigns the records contents to a dynamic array. If the record is available (no locks), set an exclusive lock. Assigns the contents of the record to a dynamic array. Reads data from an attribute and assigns it to a variable regardless of lock status. Retains existing locks. If the record is available (unlocked or shared lock), sets a shared lock. Reads data from an attribute and assigns it to a variable. If the record is available (no locks), sets an exclusive lock on the record. Reads data from an attribute and assigns it to a variable. Reads the next record from a non-UniData sequential file and assigns the data to a variable regardless of lock status. READ Commands

MATREADL

MATREADU READ READL

READU READV READVL READVU READSEQ

Developing UniBasic Applications

121

Chapter 4 - Maintaining Data in Files

READ Command OSREAD OSBREAD

Action Reads an entire sequential file and assigns the contents to a variable. Reads data from a sequential file starting at a specified byte location for a certain length of bytes, and assigns the data to a variable. Regardless of lock status, reads the next available record from a tape and assigns it to a variable. Using a list of record IDs, reads the next record to a variable regardless of lock status. Retains existing locks. Retrieves one record ID from an index, assigns the contents of the record to a dynamic array, and assigns the record ID to the @ID variable regardless of lock status. Retains existing locks. If the record is available (unlocked or shared lock), sets a shared lock. Retrieves one record ID from an index, assigns the contents of the record to a dynamic array, and assigns the record ID to the @ID variable. If the record is available (no locks), sets an exclusive lock. Retrieves one record ID from an index, assigns the contents of the record to a dynamic array, and assigns the record ID to the @ID variable. READ Commands (continued)

READT READNEXTTUPLE READFWD READBCK READFWDL READBCKL

READFWDU READBCKU

For syntax and instructions for coding read statements, see the UniBasic Commands Reference.

Tip
Using UniBasic commands to modify files and records containing binary data (for example, directory files such as BP) could have unpredictable results. Use operating system commands instead, such as cp (for UNIX) or copy (for Windows NT or Windows 2000).

122

Developing UniBasic Applications

Reading, Writing, and Deleting Data from Files

Example
The following program segment is taken from the sample program UPDATE_ORDER in Appendix A - Sample Program. The READU command reads a record in the ORDERS file (in the demo database), converting attributes in preparation for displaying them on the screen.

Program Example
DISPLAY_DATA: * Display the current information in the desired record. This is * determined by the number the user entered (ORDER_NUMBER). READU ORDER.REC FROM ORDERS_FILE,ORDER_NUMBER THEN * Read with a lock so that no one else can modify it at the same time. RECORD_FOUND = 1 ORDER_DATE = OCONV(ORDER.REC<1>,"D4/") ORDER_TIME = OCONV(ORDER.REC<2>,"MT") CLIENT_NUMBER = ORDER.REC<3> ADDRESS = ''

Developing UniBasic Applications

123

Chapter 4 - Maintaining Data in Files

Writing Data to Files


Reminder
Write statements execute regardless of lock status. For this reason, you must precede them with the READU command to prevent data inconsistency that can result from multiple users updating files at the same time. For more information, see Chapter 5 - Using UniData Locks. UniBasic supplies a number of commands for writing records into UniData hashed files from UniBasic variables, including dynamic and dimensioned arrays. You will want to use the proper command for the type of file you are writing to and the type of variable you are writing from. Use the following table to decide which UniBasic command to use to write to a file. WRITE Command MATWRITE MATWRITEL Action Writes elements of a dimensioned array to corresponding attributes of a record regardless of lock status. If the record is available (unlocked or shared lock), sets a shared lock on the record. Writes elements of a dimensioned array to corresponding attributes of a record. If the record is available (no locks), sets an exclusive lock. Writes elements of a dimensioned array to corresponding attributes of a record. Writes an expression to a record regardless of lock status. Releases any record or file lock this process set. Writes an expression to a record regardless of lock status, but retains locks if present. Writes an expression to an attribute of a data record regardless of lock status. Writes an expression to an attribute of a data record. WRITE Commands

MATWRITEU

WRITE WRITEU WRITEV WRITEVU

124

Developing UniBasic Applications

Reading, Writing, and Deleting Data from Files

WRITE Command OSWRITE OSBWRITE WRITESEQ WRITESEQF

Action Writes the contents of a variable to a sequential file. Writes a variable starting at a specified byte location to a sequential file. Writes a variable as a record on a sequential file from a current record pointer position. Writes a variable as a record on a sequential file from a current record pointer position. Forces UniData to immediately write the data to the disk. Writes the value of an expression onto a tape. WRITE Commands (continued)

WRITET

For syntax and instructions for coding write statements, see the UniBasic Commands Reference.

Tip
Using UniBasic commands to modify files and records containing binary data (for example, directory files, such as BP) could have unpredictable results. Use operating system commands instead, such as cp (for UNIX) or copy (for Windows NT or Windows 2000).

Developing UniBasic Applications

125

Chapter 4 - Maintaining Data in Files

Example
The following program segment is taken from the sample program, UPDATE_ORDER in Appendix A - Sample Program. The WRITEV command writes an attribute to the ORDERS file (in the demo database) after an order number (one value in a multivalued attribute) has been deleted from it.

Program Example
DELETE_RECORD: * (Assuming the order #'s are on line 12) READVU ORDER_LINE FROM CLIENT_FILE,CLIENT_NUMBER,12 THEN LOCATE ORDER_NUMBER IN ORDER_LINE<1> SETTING POSITION THEN DEL ORDER_LINE<1,POSITION> END WRITEV ORDER_LINE ON CLIENT_FILE, CLIENT_NUMBER, 12 END * DELETE ORDERS_FILE, ORDER_NUMBER RELEASE CLIENT_FILE,CLIENT_NUMBER RETURN

126

Developing UniBasic Applications

Reading, Writing, and Deleting Data from Files

Deleting Data from Files


Reminder
DELETE executes regardless of lock status. The only way to prevent data inconsistency that can result from multiple users updating files at the same time is to precede DELETE with READU. For more information, see Chapter 5 - Using UniData Locks. The following table provides a brief introduction to the UniBasic commands that delete data from files and/or records. Command DELETE DELETEU OSDELETE Action Deletes a record from a UniData file regardless of lock status. Releases all locks set by this process. Checks for locks. If the record is available, deletes it. Deletes a sequential file. Commands that Close Files

Warning
The UniBasic OSDELETE command is intended for use on OS-type files (not on UniData hashed files). If the command is executed against a recoverable hashed file, UniData could crash immediately or at the next RFS checkpoint. If the command is executed against a nonrecoverable hashed file, UniData will not crash, but other unpredictable problems could occur. For the syntax and use of these commands, see the UniBasic Commands Reference.

Developing UniBasic Applications

127

Chapter 4 - Maintaining Data in Files

Program Example
DELETE_RECORD: * (Assuming the order #'s are on line 12) READVU ORDER_LINE FROM CLIENT_FILE,CLIENT_NUMBER,12 THEN LOCATE ORDER_NUMBER IN ORDER_LINE<1> SETTING POSITION THEN DEL ORDER_LINE<1,POSITION> END WRITEV ORDER_LINE ON CLIENT_FILE, CLIENT_NUMBER, 12 END * DELETE ORDERS_FILE, ORDER_NUMBER RELEASE CLIENT_FILE,CLIENT_NUMBER RETURN

128

Developing UniBasic Applications

Closing Files

Closing Files
The following table introduces the commands that close files. Command CLOSE CLOSESEQ OSCLOSE Action Releases locks and closes a data or dictionary file. Releases locks and closes a sequential file. Closes a sequential file. Commands that Close Files For the syntax of these commands and instructions for coding close statements, see the UniBasic Commands Reference.

Developing UniBasic Applications

129

Chapter 4 - Maintaining Data in Files

Accessing Data in Unopened Files


With the ICONV T and OCONV T functions you can access singlevalued, multivalued, and multisubvalued attributes in a file that you have not opened. You can also use these functions to determine if a particular record or attribute exists or contains an empty string. The first line of the following program segment reads the demonstration database file ORDERS, accessing the record with @ID 912, and retrieving the second attribute. OCONV T also converts the internal date to external display format. The second line prints the value in that attribute.

Program Example
record.ret = OCONV("912","TORDERS;V;;2") PRINT "Record retrieved: ":record.ret END

The following output is obtained by running the preceding program:

Screen Example
Record retrieved: 12:30PM

130

Developing UniBasic Applications

Chapter 5 - Using UniData Locks

In a multiuser environment, you must be able to prevent more that one user from updating records simultaneously. By observing the UniBasic locking conventions consistently in all of your applications, you can provide this protection. You can also use locks to reserve computer resources. This chapter introduces UniBasic locks and the commands that comprise the UniBasic locking system. For more information about ensuring database consistency, see Chapter 9 - UniBasic Transaction Processing.

Developing UniBasic Applications

131

Chapter 5 - Using UniData Locks

In This Chapter
This chapter contains the following sections: Understanding the UniData Locking System Locking Commands What Commands Do with Locks When to Use Locking Commands Programming Problems Locking Example

132

Developing UniBasic Applications

Understanding the UniData Locking System

Understanding the UniData Locking System


In a multiuser system, record and file locks prevent more than one user from accessing the same record at the same time.

How UniData Locks Work


Unlike physical locks that prevent access, UniBasic locks are advisory only. Imagine a physical lock as the deadbolt on a hotel room door and an advisory lock as a Do Not Disturb sign. UniBasic locks merely inform others attempting access that the locked entity is in use, while not explicitly preventing access.

Types of UniData Locks


The UniData lock types are: Exclusive lock Checks for and sets locks before updating data. Shared lock Used for read-only operations. Prevents other users (who also use locking commands) from writing to the record while it is being read.

Exclusive Locks (U)


Records locked with exclusive (U) locks: Cannot be accessed by any READ U or L command. Are accessed by commands that do not check for locks and that are issued from other processes.

Note
The original lock, although ignored, is retained. Are accessed and modified by any WRITE or DELETE command. Are released by the commands WRITE and DELETE issued from the same process.

Developing UniBasic Applications

133

Chapter 5 - Using UniData Locks

Cannot be locked by RECORDLOCKU or RECORDLOCKL issued from another process.

Shared Locks (L)


Records locked with shared (L) locks: Cannot be accessed or locked by READ U commands. Are accessed by READ L commands.

Note
The original lock is retained. Are accessed by READ commands that do not check for locks. Are accessed and changed by any WRITE or DELETE command.

Note
Even though these commands update regardless of lock status, they retain the original lock. Are released by the commands WRITE or DELETE issued from the same process. Cannot be locked by RECORDLOCKU from another process.

Tip
WRITE and DELETE commands execute regardless of lock status. For this reason, to prevent data inconsistency, these commands must be preceded by READU or another command that issues locks.

134

Developing UniBasic Applications

Understanding the UniData Locking System

When UniBasic Finds a Lock


When a locking command encounters a locked file or record, UniBasic does one of the following (in order of preference): 1. Executes the LOCKED clause of the UniBasic command, if present. 2. Beeps periodically while waiting if the ECL command DEFAULT.LOCKED.ACTION BELL [interval] is set. 3. Waits for the record to be unlocked, but does not send a signal to the terminal.

Points to Remember about Locks


Keep in mind the following points about locks: Anyone can update or delete locked files and records with any WRITE or DELETE command. If you set a lock, only you, or someone signed on with root privileges on UniData for UNIX or Administrator privileges on UniData for Windows NT or Windows 2000, can release it. TRANSACTION COMMIT and TRANSACTION ABORT releases all locks your process has set within the transaction.

Note
For more information about using transactions to ensure database consistency, see Chapter 9 UniBasic Transaction Processing.

Developing UniBasic Applications

135

Chapter 5 - Using UniData Locks

Locking Commands
Reminder
UniBasic locks are advisory only. To maintain data consistency, all programs that update the database must check for locks. Keep the following points in mind when writing UniBasic programs: Locking Records UniBasic provides distinct read, write, and delete commands that check and set locks. These commands all have an appended U or L in the command name, indicating the type of locks they set. All other commands ignore locks, operating as if no lock is set. A chart of locking commands is provided in What Commands Do with Locks later in this chapter. Locking Files You can set an advisory lock on an entire file by issuing a FILELOCK against it. Use the FILEUNLOCK command to unlock the file. Locking Resources Use the LOCK command to set an advisory lock that reserves a computer resource (such as a printer or tape drive) for use by a particular program. Unlock the resource with the UNLOCK command. Unlocking You can use the ECL command SUPERRELEASE to release record and file locks. To unlock resources, use the CLEAR.LOCKS and SUPERCLEAR.LOCKS commands.

For more information about ECL commands, see the UniData Commands Reference.

UDT.OPTIONS
Setting UDT.OPTIONS 35 on prevents locking a record in a called subroutine that was locked in the calling program. UDT.OPTIONS 78 addresses two situations in which UniBasic locking is incompatible with Pick-style locking. For more information about UDT.OPTIONS, see the UDT.OPTIONS Commands Reference.

136

Developing UniBasic Applications

Locking Commands

Checking Lock Status


To learn about a the lock status of a record, use the UniBasic RECORDLOCKED function. After calling RECORDLOCKED, you can use the STATUS function to obtain the user number of the user who set the lock. The GETREADU function returns a dynamic array containing detailed information about all records that have been locked by any UniBasic or ECL command. The ECL command LIST.READU also provides this same information (for more information, see GETREADU in the UniBasic Commands Reference). The GETQUEUE function returns a dynamic array that contains information about all records currently locked and waiting to be released. The ECL command LIST.QUEUE also provides this information (for more information, see GETQUEUE in the UniBasic Commands Reference).

Developing UniBasic Applications

137

Chapter 5 - Using UniData Locks

What Commands Do with Locks


The following table identifies how the previously discussed commands react to UniBasic locks. Note that some commands execute regardless of lock status, but leave locks in place (Ignores column). Others, regardless of lock status, release locks and execute (Releases column). Still others delay file activity until locks are released (Sets Exclusive and Sets Shared columns).

Reminder
Include the LOCKED clause in command statements to tell UniData what to do when it encounters a lock.

Command READ READU READV READVU MATREAD MATREADU READBCKU READFWDU RECORDLOCKU READL READVL MATREADL READBCKL

Ignores X

Sets Exclusive

Sets Shared

Releases

X X X X X X X X X X X X What Commands Do with Locks

138

Developing UniBasic Applications

What Commands Do with Locks

Command READFWDL RECORDLOCKL WRITE WRITEU WRITEVU MATWRITE MATWRITEU DELETE DELETEU

Ignores

Sets Exclusive

Sets Shared X X

Releases

X X X X X X X What Commands Do with Locks (continued)

Developing UniBasic Applications

139

Chapter 5 - Using UniData Locks

When to Use Locking Commands


Follow the guidelines in this table when selecting record- and file-locking commands. Attributes of Dynamic Arrays READV

When to Use

Action

Files

Dynamic Arrays

Dimensioned Arrays MATREAD

Retrieving information; allowing update while reading. Updating records (must be preceded by READU to ensure data consistency). Reading and securing information for update (other programs must check for locks). Updating; keeping ownership of lock (other programs must check for locks).

Read without checking locks. Write; release locks.

READ

WRITE

WRITEV

MATWRITE

Read and set exclusive lock.

FILELOCK

READU RECORDLOCKU

READVU

MATREADU

Write; do not release locks.

WRITEU

WRITEVU

MATWRITEU

Record Locking Guidelines

140

Developing UniBasic Applications

When to Use Locking Commands

When to Use

Action

Files

Dynamic Arrays

Attributes of Dynamic Arrays READVL

Dimensioned Arrays MATREADL

Retrieving information, but keeping others from updating (using READU or WRITEU) while you are reading (other programs must check for locks). Release locks you placed.

Read and lock for read-only.

READL

Release locks.

FILEUNLOCK

RELEASE

RELEASE

RELEASE

Record Locking Guidelines (continued)

Developing UniBasic Applications

141

Chapter 5 - Using UniData Locks

Programming Problems
This section discusses how UniBasic programming problems may arise, and provides suggestions on how to minimize the problems.

Causes
A multiuser environment creates opportunities for two programs to access the same record at the same time, resulting in the following types of problems: Inconsistent data caused by the following: Lost updates The updates from one program overlay those of another. Dirty reads A program reads a record that is in the process of being modified by another. Unrepeatable reads A program reads the same record twice, obtaining two different values. This results when the first read executes while the record is in the process of being updated, and the second read executes after the record has been updated.

Bottlenecks Result when one program retains a lock on a file or record for an inordinate amount of time, causing other programs to wait. Deadlocks Occur when two or more programs are waiting for each other to release records with no chance of either being released.

Minimizing Problems
Using record locking consistently, you can eliminate the programming problems listed earlier while reducing bottlenecks and deadlocks. To do so, use the following guidelines: Always check for locks and always lock records before writing. Minimize the amount of time records are locked. Request user input before locking records, not while the records are locked. Impose a protocol on the order data items can be updated. For example, the protocol might let programs lock record B only after locking record A (to avoid deadlocks).

142

Developing UniBasic Applications

Programming Problems

Note
Deadlocks cause transaction processing to abort one of the deadlocked transactions. For more information about UniData transactions, see Chapter 9 - UniBasic Transaction Processing.

Developing UniBasic Applications

143

Chapter 5 - Using UniData Locks

Locking Example
The following program segments are portions of the sample program in Appendix A - Sample Program. The main routine (Main Logic), drives the program by calling subroutines to perform the main tasks. Notice that RELEASE is executed as the last step in the LOOP. In almost all cases, records are released as soon as they are written to the file. However, some error conditions could cause records to remain locked when control is returned to the LOOP.

Program Example
*-------------- Main Logic ----------------------------GOSUB INITIALIZE LOOP GOSUB DISPLAY_SCREEN GOSUB GET_ORDER_NUMBER UNTIL ORDER_NUMBER[1,1] = 'Q' GOSUB DISPLAY_DATA IF RECORD_FOUND THEN GOSUB GET_RECORD_COMMAND RELEASE REPEAT GOSUB EXIT

The following program segment shows the READU command that sets an exclusive lock on the record that the user has selected for update:

Program Example
DISPLAY_DATA: * Display the current information in the desired record. This is * determined by the number the user entered (ORDER_NUMBER). READU ORDER.REC FROM ORDERS_FILE,ORDER_NUMBER THEN * Read with a lock so that no one else can modify it at the same time.

144

Developing UniBasic Applications

Locking Example

ORDER_DATE = OCONV(ORDER.REC<1>,"D4/") ORDER_TIME = OCONV(ORDER.REC<2>,"MT") CLIENT_NUMBER = ORDER.REC<3> "R#5"

In the next program segment, the WRITE command updates the previous record read and releases the exclusive lock (WRITE always releases locks that were set by the same user process):

Program Example
WRITE_RECORD: . . . WRITE CLIENT.REC ON CLIENT_FILE,CLIENT_NUMBER WRITE ORDER.REC ON ORDERS_FILE,ORDER_NUMBER

Developing UniBasic Applications

145

Chapter 5 - Using UniData Locks

146

Developing UniBasic Applications

Chapter 6 - Working with Data in Programs

In most cases, you select from a number of similar commands to perform a specific task. This chapter provides the information you need to select the correct command or function to manipulate data in UniBasic programs.

Developing UniBasic Applications

147

Chapter 6 - Working with Data in Programs

In This Chapter
You have read data into the program from files or accepted input from the user. You are ready to manipulate the data before writing it to a file or report. This chapter introduces in the following sections the concepts and commands you will use to do this manipulation: UniData Arrays Inquiring about Data Performing Numeric Operations Formatting and Converting Data UniBasic Multibyte Support

148

Developing UniBasic Applications

UniData Arrays

UniData Arrays
You generally use an array to store data that you read into a UniBasic program from UniData hashed files. Multivalued and multi-subvalued attributes can be loaded into an array in the same way that you load an individual value into a variable. The following two types of UniBasic arrays are introduced in Chapter 1 - Introduction to UniBasic. Dynamic arrays Separate each attribute, value, and subvalue of a record by delimiters; they are flexible, allowing easy modification. Dimensioned arrays Store data in designated cells of a fixed-sized matrix; access to data in a dimensioned array is rapid but less flexible.

Dynamic Arrays
The UniData file structure uses reserved delimiters to partition data items that are stored sequentially. The same delimiters are used in dynamic arrays (see Dynamic Array Delimiters). As the name implies, the size of a dynamic array is determined by the data contained in it.

Advantages of Using Dynamic Arrays


Dynamic arrays have the following advantages over dimensioned arrays: Need not be defined before you use them. Can be assigned quickly. Require less memory than dimensioned arrays. Can be three-dimensional.

Developing UniBasic Applications

149

Chapter 6 - Working with Data in Programs

Dynamic Array Delimiters


UniData uses the same delimiters to separate elements in a dynamic array as those used in a UniData hashed file. UniBasic evaluates the delimiter symbols and writes the correct ASCII values when you write a record. The following table illustrates common symbols used to represent delimiters. The character used to display the delimiter symbols depends on your terminal emulation. Delimiter Symbol (unprintable) ~ } | { Meaning Record mark Attribute mark Value mark Subvalue mark Text mark @variable @RM @AM, @FM @VM @SM @TM

Dynamic Array Delimiters

Note
You must use the @variables to refer to delimiters within UniBasic programs, not the delimiter symbols. The symbols that display for these delimiters could differ on your system.

Maintaining Dynamic Arrays


The commands you use to load data into an array and write to a file from an array are introduced in Chapter 4 - Maintaining Data in Files. After data is loaded into the sorted array, you might use the following procedure to add, replace, insert, and delete data in the array: 1. Make sure that the data in the array is sorted. Performing a LOCATE or FIND on unsorted data produces unpredictable results. 2. Execute the LOCATE or FIND command to determine the location of one of the following: Where new data should be inserted in a multivalued attribute.

150

Developing UniBasic Applications

UniData Arrays

Of a string that you intend to update or delete within a multivalued attribute.

3. Use the EXTRACT command (or < >) to extract an attribute (value, multivalue, or multi-subvalue) from the array. 4. Use one of the following commands as appropriate to insert, delete, update, or replace an array element: INSERT, INS DEL, DELETE (the function) REPLACE (or < >)

Summary of Dynamic Array Commands


Use the following commands and functions to create and maintain data in a dynamic array. Command/ Function FIND, LOCATE Action Determines the location of an existing array element. Sets a pointer to the location where an array element should be inserted. Copies an array element (attribute, value, multivalue, or multisubvalue) into a variable. Inserts or replaces an array element (attribute, value, multivalue, or multi-subvalue) from a variable. Deletes an array element. Dynamic Array Commands

EXTRACT, < > REPLACE, < > DEL, DELETE

Developing UniBasic Applications

151

Chapter 6 - Working with Data in Programs

FIND vs. LOCATE


UniBasic provides two commands that return the location of a string within a dynamic array: FIND and LOCATE. The following table contrasts the two commands. FIND Returns attribute position. Expression searched for could be a variable, matrix element, function, or a literal string. Can limit the search to a specific attribute, value, or subvalue. Can begin the search at a specific attribute, value, or subvalue. Assumes the records are sorted and justified. SETTING returns the position after the last value if the string is not found. Must match the entire element in an array at the level specified. LOCATE Returns attribute, value, and subvalue position. Expression searched for must be numeric or string.

Searches the entire array. Can find the nth occurrence of a string. Assumes the records are sorted and justified.

SETTING returns 0 if the string is not located.

Locates an element or part of an element in an array.

FIND vs. LOCATE For the syntax and use of these commands, see the UniBasic Commands Reference manual.

152

Developing UniBasic Applications

UniData Arrays

Example
The following example is taken from the sample program UPDATE_ORDER in Appendix A Sample Program. In this example, the user has entered a record to be deleted. The desired attribute (containing the order line) is read from the CLIENT file and placed in a dynamic array (ORDER_LINE). Then, the LOCATE command is used to determine the position of the order in the array. That order is deleted and the attribute is written back to the file.

Program Example
DELETE_RECORD: ... (Assuming the order #'s are on line 12) READVU ORDER_LINE FROM CLIENT_FILE,CLIENT_NUMBER,12 THEN LOCATE ORDER_NUMBER IN ORDER_LINE<1> SETTING POSITION THEN DEL ORDER_LINE<1,POSITION> END WRITEV ORDER_LINE ON CLIENT_FILE, CLIENT_NUMBER, 12 END *

Dimensioned Arrays
A dimensioned array stores data in the elements of a fixed-size matrix. The array must be declared in your UniBasic program before you can use it. When you read data in from a UniData hashed file, the delimited data is placed in the matrix elements in the order they appear in the file. If your array is not large enough to contain all the data read in, all leftover data is placed in the zero element, even if more than one attribute is left over.

Advantages of Using Dimensioned Arrays


Dimensioned arrays have the following advantages over dynamic arrays: Access is faster. Access time is not affected by the size of the array. Can contain dynamic arrays.

Developing UniBasic Applications

153

Chapter 6 - Working with Data in Programs

Maintaining Dimensioned Arrays


Perform the following steps to create and use a dimensioned array: 1. Use the DIM or DIMENSION command to create the array and assign a static dimension. 2. Manipulate data in the array: Use the MATREAD, MATREADL, or MATREADU command to place data in the array from a record. Use the MATPARSE command to place data in the array from a dynamic array or string. Use the MAT command to assign a single value to all elements of the array or to copy one dimensioned array to another. Use the MATBUILD command to convert a dimensioned array to a dynamic array.

3. Use a statement (for example, X1 = var) to update elements in the dimensioned array. 4. Use the MATWRITE, MATWRITEL, or MATWRITEU command to write array elements to a record.

Summary of Dimensioned Array Commands


Use the following commands and functions to create and maintain data in a dimensioned array. Command/ Function DIM DIMENSION INMAT MAT Action Creates a dimensioned array. Required before data can be loaded into the array. Returns the number of elements or the dimension of the array. Also returns the status of some commands. Assigns a value to all elements of a dimensioned array, or copies one array to another. Dimensioned Array Commands

154

Developing UniBasic Applications

UniData Arrays

Command/ Function MATBUILD MATPARSE dim.array(x,y,z)=var

Action Generates a dynamic array from a dimensioned array. Distributes elements in a delimited string or dynamic array to consecutive elements of a dimensioned array. Extracts a value from an element of a dimensioned array.

Dimensioned Array Commands (continued) For the syntax and use of these commands and functions, see the UniBasic Commands Reference.

Inquiring about Data


A number of UniBasic functions provide information about a string or array without modifying the original data. They fall into three categories: Type Functions that qualify the data (such as alpha or numeric and length). Location Functions that return the positions of strings or attributes in the data. Extraction Functions that return values found within the string or array.

Type
This section describes functions that qualify the data.

Testing for the Null Value


The null value is defined within UniData as an unknown value, as opposed to an empty string. In UniBasic programs, this unknown value is represented by @NULL, which is translated into a specific language-dependent (such as English, German, or Chinese) ASCII code. The empty string, represented by is no longer considered to be null in UniData. Two functions test for the null value: ISNV and ISNVS.

Developing UniBasic Applications

155

Chapter 6 - Working with Data in Programs

Testing for Other Data Types


The following functions provide information about type of data. Function ALPHA NUM, NUMS LEN MAXIMUM MINIMUM COUNTS COUNT DCOUNT ISNV/ISNVS Action Determines whether a string is made up exclusively of alphabetic characters. Determines whether a value is numeric. Determines the length of a string. Determines the largest numeric element found in an array. Determines the smallest numeric element in an array. Determines the number of times a substring appears within one or more elements of an array. Determines the number of times a substring appears within a string or all elements within an array. Determines the number of delimited substrings in a string. Tests for the null value (the unknown value, not empty string). Functions That Inquire about Data Type For the syntax and use of these functions, see theUniBasic Commands Reference.

156

Developing UniBasic Applications

UniData Arrays

Location
The following functions return the location of substrings in a string or array. Function COL1 Action Returns the column position before the string located by the FIELD function. Note - For an introduction to FIELD, see Extraction in this chapter. COL2 Determines the column position after the string located by the FIELD function. Note - For an introduction to FIELD, see Extraction following this table. FIND LOCATE FINDSTR INDEX Determines the position of a string in a dynamic array element. Determines the starting position of a substring within a string; user can specify occurrence. Functions That Inquire about Location For the syntax and use of these functions, see the UniBasic Commands Reference. Determines the position of a string in a dynamic array.

Extraction
The following functions return values found in a string or array without modifying the original data. Function [] Action Returns a specified number of characters beginning at a specified location. Functions That Extract Without Modifying

Developing UniBasic Applications

157

Chapter 6 - Working with Data in Programs

Function OCONV/S T REMOVE

Action Returns a contiguous string of a specified length starting at a specified location. Successively copies each element of a dynamic array to a variable. Maintains a pointer to the location in the array of the last element copied. Returns data from an attribute, value, or subvalue in a dynamic array. Returns a substring or group of substrings. Treats a string as an array with fields delimited by any specified ASCII character. Returns one or more strings separated by a specified delimiter. Returns a string if it is of a specified length or falls within the specified range of lengths. Returns data values that fall within specified ranges. Returns data values that match a specified pattern. Extracts all alphabetic characters. Extracts all nonalphabetic characters. Extracts all numeric characters (0-9). Extracts nonnumeric characters. Extracts all alphabetic and numeric values. Extracts characters that are neither alphabetic nor numeric.

EXTRACT <> FIELD

OCONV/S G OCONV/S L OCONV/S R OCONV/S P OCONV/S MCA OCONV/S MC/A OCONV/S MCN OCONV/S MC/N OCONV/S MCB OCONV/S MC/B

Functions That Extract Without Modifying (continued) For the syntax and use of these functions, see theUniBasic Commands Reference.

158

Developing UniBasic Applications

Performing Numeric Operations

Performing Numeric Operations


You can perform numeric operations in UniBasic with the two tools, which are described in the following sections: Arithmetic Operators Mathematic Functions

Arithmetic Operators
Arithmetic operators compute values. For example, the following statement multiplies the value of COST by the value of QUANTITY and stores the result in the variable PRICE:
PRICE = COST * QUANTITY

The following table lists valid arithmetic and concatenation operators. Operator + + * / ** or ^ : Action Unary plus (same as multiplying by +1). Unary minus (use to change a value to negative: var = -var). Addition. Subtraction. Multiplication. Division. Exponentiation. Concatenation. Arithmetic Operators

Developing UniBasic Applications

159

Chapter 6 - Working with Data in Programs

You can combine arithmetic and concatenation operators to perform special functions as the following table shows. Operator += Action Increments the value of a variable. LINES += 1 is more efficient than LINES=LINES+1. Decrements the value of a variable. LINES = 1 is more efficient than LINES=LINES-1. Multiplies the value to the left of the operator by the value to the right of the operator, as in var *= var. Divides the value to the left of the operator by the value to the right of the operator, as in var /= var. Concatenates the value to the left of the operator by the value to the right of the operator, as in var := var.

-=

*=

/=

:=

Combined Arithmetic and Concatenation Operators

Points to Remember
Keep in mind the following points when you write UniBasic commands that perform calculations: Floating point When you execute an arithmetic operation, UniData invokes the appropriate host operating system command, which performs the operation in floating point. When the results are converted to string format for print or display, the rounding that is automatically applied can produce unexpected results. Use the ECL command FLOAT.PRECISION to control when and how rounding is applied. Significance UniData supports up to 14 digits of significance (although this significance can vary across hardware lines). If you use more than 14 digits, accuracy is not

160

Developing UniBasic Applications

Performing Numeric Operations

guaranteed. Use the UniBasic PRECISION command to set significance for a work session. The default is 4. Data type Be aware of data type when you execute arithmetic calculations. Results of the same calculations executed on string and numeric data occasionally differ. Null value The result of calculations on data containing the null value is the null value. Generally, null is converted to 0 for arithmetic calculations. However, when UniData encounters the null value as a divisor, 1 is used to avoid a system error.

Reminder
The null value is defined within UniData as an unknown value, as opposed to an empty string.

UDT.OPTIONS
Before you execute an arithmetic operation, you can set UDT.OPTIONS 10 on to trim blank spaces around a numeric value. The trim prevents a runtime error. For more information about UDT.OPTIONS, see the UDT.OPTIONS Commands Reference.

Arithmetic Operations on Arrays


When you execute arithmetic operations on dynamic arrays, you can apply them in one of the three following ways: Apply the operation to just the first element in the array. Apply the operation to all elements in the array. Apply an operation to two arrays (for example, multiply one array by another).

Developing UniBasic Applications

161

Chapter 6 - Working with Data in Programs

Applying the Operation to the First Element in the Array Performing an arithmetic operation on an array applies the operation to just the first element of the array. For example, the program segment:
X = 1:@VM:2:@VM:3 Y = X/100 PRINT Y 0.01y2y3 (where y represents

prints

the value mark).

Applying the Operation to All Elements in the Array To apply the operation to all elements, include the REUSE command, as in the following sample program:

Program Example
VIEWERS = 100:@VM:200:@VM:300 COST = 40 VCOST = VIEWERS*COST PRINT "1. ":VCOST VCOST = VIEWERS*REUSE(COST) PRINT "2. ":VCOST

In the following execution of this program, notice that the first result reflects application of the arithmetic operation (multiplication) is applied to only the first element of the array. The REUSE command is used to produce the second result, causing the arithmetic operation to be applied to each element in the array (y represents the value mark in this display).

Screen Example
:RUN BP arith 1. 4000y0y0 2. 4000y8000y12000

162

Developing UniBasic Applications

Performing Numeric Operations

Applying Operations to Two Dynamic Arrays When an arithmetic operation is applied to dynamic arrays of different length, the operation is applied to only the number of element in the shortest array. The remaining elements are filled with 1 or 0, depending on the operation performed: for division, elements are filled with 1; for addition, subtraction, and multiplication, elements are filled with 0. In the following example program, the first two elements are multiplied, and then the second two elements are multiplied. The third element is filled with 1, which results in VCOST = 4000:@VM:200:@VM.

Program Example
VIEWERS = 100:@VM:200:@VM:300 COST = 40:@VM:1 VCOST = VIEWERS*COST

However, if you apply the REUSE function, the last element in the shorter array is used to complete the operation on the remaining elements of the longer array. The following version of the program produces a result of 4000:@VM:200:@VM:300:

Program Example
VIEWERS = 100:@VM:200:@VM:3 COST = 40:@VM:1 VCOST = VIEWERS*REUSE(COST)

Developing UniBasic Applications

163

Chapter 6 - Working with Data in Programs

Mathematic Functions
Numeric functions process variables, constants, tables, or data in a variety of ways. The following table lists UniBasic mathematical functions. Function ABS ACOS ASIN ATAN COS EXP INT LN MOD PWR RND SIN SQRT SUM Action Returns the positive value of a number. Returns the arc cosine (inverse cosine) in degrees. Returns the arc sine (inverse sine) in degrees. Returns the arc tangent (inverse tangent) in degrees. Returns the cosine of a number. Returns the base number raised to the power specified. EXP is the inverse of LN. Returns the integer value of a number. INT truncates; it does not round the value. Returns the natural base logarithm. LN is the inverse of EXP. Returns the remainder of the division operation using specified numbers. Raises a number to the power specified. Returns a random integer from 0 to a specified number 1. Returns the sine of a number. Return the square root of a positive numeric expression. Totals the numeric values in an attribute according to dynamic array delimiters. You can enter a range, starting position, and level for which to calculate the total. Mathematic Functions

164

Developing UniBasic Applications

Performing Numeric Operations

Function TAN

Action Returns the tangent of numeric expression. Mathematic Functions (continued)

For the syntax and use of these functions, see the UniBasic Commands Reference.

Reminder
Numeric functions on data containing the null value result in the null value.

Developing UniBasic Applications

165

Chapter 6 - Working with Data in Programs

Formatting and Converting Data


UniData provides functions to format, modify, and convert the following types of data: Characters Convert between EBCDIC and ASCII for compatibility with other computer systems. You can also convert from ASCII character to ASCII code (decimal value). Strings and dynamic arrays Justify and format using special characters. Numbers Justify and format using special characters for dollars and other numeric display formats. Convert among ASCII character, decimal, binary, octal, and hexadecimal. Modify, justify, scale, and round numbers. Dates and times Convert between external display format and UniData internal format. Format dates and time for display using special characters; spell out month or day of the week.

Before getting into the types of data, you should understand the ICONV and OCONV functions.

ICONV and OCONV: The All-Purpose Functions


The UniBasic functions ICONV and OCONV perform many conversions between internal and display format, among numbering systems, and others. ICONV primarily converts to internal format for date and number storage, and OCONV primarily converts to external display format. Yet both perform many other conversions, and sometimes both execute the same conversion. You will see the different options for these commands listed throughout the following sections. OCONVS performs on an array the same function that OCONV performs on a variable. When OCONVS is available, the functions are represented in the tables that follow as OCONV/S.

Note
When invalid data is submitted to ICONV or OCONV, or an invalid conversion code is used, these functions return the original value. In BASICTYPE P, with UDT.OPTIONS 56 on, OCONV returns an empty string if the input value or conversion code is invalid.

166

Developing UniBasic Applications

Formatting and Converting Data

You can use the following program example to test the many conversion code options available for ICONV and OCONV:

Program Example
PROMPT "" LOOP PRINT "Input or output [I/O]?" : INPUT i_or_o IF i_or_o = "" THEN STOP PRINT "Conversion code? " : INPUT y_conv_code PRINT "Argument? " : INPUT z_argument IF OCONV(i_or_o, "MCU")= "O" THEN PRINT "Executing OCONV" answer = OCONV(z_argument,y_conv_code) END ELSE answer = ICONV(z_argument,y_conv_code) END PRINT \"\ : answer : \"\ WHILE 1 DO REPEAT

The following screen example shows how you can test the masked extended (MD) option for OCONV by running the previous program:

Screen Example
Input or output [I/O]?O Conversion code? ME2;1 Argument? 123.123 Executing OCONV "12.31" Input or output [I/O]? :

Developing UniBasic Applications

167

Chapter 6 - Working with Data in Programs

Character Format Conversion


Use the character conversion functions described in the following table to convert data to and from formats for other computer systems. Function ASCII Action Converts the string expression str.expr from EBCDIC to the corresponding ASCII values. This is the inverse of the EBCDIC function. Converts the string expression str.expr from ASCII to the corresponding EBCDIC values. This is the inverse of the ASCII function. Converts from ASCII code (decimal value) to ASCII character. Converts from ASCII character to ASCII code (decimal). Converting Character Formats For the syntax and use of these functions, see the UniBasic Commands Reference.

EBCDIC

CHAR SEQ

Strings and Dynamic Arrays


Several formatting functions are available for manipulating data in strings or dynamic arrays. Function FMTS [] : Action Formats a multivalued attribute for output, adding special characters for dollars, dates, or other number formats. Replaces a string, starting at the position indicated. Concatenates strings. Formatting and Modifying Strings

168

Developing UniBasic Applications

Formatting and Converting Data

Function DOWNCASE ICONV MCL OCONV/S MCL UPCASE ICONV MCU OCONV/S MCU OCONV/S MCT ICONV MCT OCONV/S ML ICONV ML CONVERT OCONV/S MCC ICONV MCC OCONV/S MCP ICONV MCP TRIM TRIMB TRIMF OCONV/S S

Action Converts characters to lowercase.

Converts characters to uppercase.

Converts to initial caps style. The first character in each word is uppercase, and the remaining characters are lowercase. Left-justifies a string or dynamic array. Replaces selected characters in a string. Converts all occurrences of substring x to substring y. Converts nonprinting characters to tildes (~). Removes all extraneous spaces from a string. Removes all trailing spaces from a string. Removes all leading spaces from a string. Converts to SOUNDEX phonetic code.

Formatting and Modifying Strings (continued) For the syntax and use of these functions, see the UniBasic Commands Reference.

Developing UniBasic Applications

169

Chapter 6 - Working with Data in Programs

In the following example, OCONV MCU converts all characters in the variable i_or_o to uppercase:

Program Example
PROMPT "" LOOP PRINT "Input or output [I/O]?" : INPUT i_or_o IF i_or_o = "" THEN STOP PRINT "Conversion code? " : INPUT y_conv_code PRINT "Argument? " : INPUT z_argument IF OCONV(i_or_o, "MCU")= "O" THEN PRINT "Executing OCONV" answer = OCONV(z_argument,y_conv_code) END ELSE answer = ICONV(z_argument,y_conv_code) END PRINT \"\ : answer : \"\ WHILE 1 DO REPEAT DISPLAY @(10,9+ENTRY):OCONV(ORDER.REC<7,ENTRY>,"MR2$,"):

Numbers
You can round and scale numbers, and you can use special characters to format numbers for display as dollars or other styles. You can also convert numbers among decimal, binary, octal, hexadecimal, and ASCII. The following two sections introduce the conversions available and the functions for performing these conversions: Formatting and modifying numbers. Converting among numbering systems and ASCII.

170

Developing UniBasic Applications

Formatting and Converting Data

Formatting and Modifying Numbers


You can use the functions described in the following table to format, convert, and modify numbers. Function FMT OCONV/S ML Action Right or left justifies, suppresses zeros, and formats using special characters. Left justifies, suppresses zeros, scales a specified number of decimal places, rounds to specified number of decimal places, and formats using special characters. Left justifies, scales a specified number of decimal places, and rounds to specified number of decimal places. Right justifies, scales a specified number of decimal places, rounds to the number of decimal places indicated, and suppresses or adds zeros to the left or right. Formats for dollars using special characters. For OCONV only, adds special characters for credit, debit, and other monetary indicators.

ICONV ML ICONV MR OCONV/S MR ICONV MRn OCONV/S MRn ICONV MD OCONV/S MD FMT

Specifies length of converted string, fills string with a specified character if data is shorter than the length indicated, breaks up the string if it is longer than the length indicated, centers text, scales a specified number of decimal places, and adds other special characters. Converts decimal to integer, scales a specified number of decimal places, and rounds to specified number of decimal places. Converts integer to packed decimal. Converts packed decimal to integer. Functions for Formatting and Modifying Numbers

ICONV MD OCONV/S MP OCONV/S MP1 ICONV MP

For the syntax and use of these commands, see theUniBasic Commands Reference.

Developing UniBasic Applications

171

Chapter 6 - Working with Data in Programs

The following program segment is taken from the sample program in Appendix A - Sample Program. In this example, OCONV MR2$ is used to display the price of an item.

Program Example
ALTER_RECORD: * Create a new screen, and allow PRICE and ADDRESS to be changed. * Initialize variables and draw the screen NEED.TO.WRITE = 0 DISPLAY @(-1):@(15,5):"Alter ORDER": DISPLAY @(10,8):"(Press RETURN to leave un-changed)" DISPLAY @(8,9):"Old Price":@(42,9):"New Price (Enter 2 decimal places)"

* Change the PRICE field (if desired) FOR ENTRY = 1 TO NUM_ENTRIES NEW.PRICE = "" DISPLAY @(10,9+ENTRY):OCONV(ORDER.REC<7,ENTRY>,"MR2$,"): INPUT @(45,9+ENTRY):NEW.PRICE NEW.PRICE = OCONV(NEW.PRICE,"MCN") IF NEW.PRICE # '' AND NUM(NEW.PRICE) THEN ORDER.REC<7,ENTRY> = NEW.PRICE NEED.TO.WRITE = 1 END NEXT ENTRY

Alternate Numbering Systems and ASCII


You can convert among decimal, ASCII character, binary, octal, and hexadecimal by using various options of the ICONV and OCONV functions. Function ICONV MB ICONV MB0C OCONV/S MB Binary Binary Decimal From Decimal ASCII characters Binary To

Converting Among Numbering Systems and ASCII

172

Developing UniBasic Applications

Formatting and Converting Data

Function OCONV/S MO OCONV/S MX OCONV/S MCD[x] ICONV MCX[D] SEQ ICONV MO ICONV MO0C OCONV/S MCX[D] OCONV/S MCD ICONV MX ICONV MCD[X] ICONV MX0C ICONV HEX OCONV/S MB0C OCONV/S MO0C

From Decimal Decimal Octal

To

Hexadecimal

Decimal Octal Octal Hexadecimal

ASCII character Decimal ASCII characters Decimal

Hexadecimal ASCII character ASCII character

ASCII characters Binary Octal

Converting Among Numbering Systems and ASCII (continued) OCONV conversions from ASCII characters produce multiple-digit numbers in the target numbering system: Hexadecimal One ASCII character produces two hexadecimal characters. For example, ASCII character 0 is equal to hexadecimal 30. Octal One ASCII character produces three octal characters. For example, ASCII character 0 is equal to octal 060. BinaryOne ASCII character produces eight binary characters. For example, ASCII character 0 is equal to octal 00110000.

For the syntax and use of these commands, see the UniBasic Commands Reference.

Developing UniBasic Applications

173

Chapter 6 - Working with Data in Programs

Numbering System Examples


The following table provides examples using the ICONV and OCONV functions to convert among numbering systems and ASCII characters. Function ICONV MB ICONV MB0C OCONV/S MB OCONV/S MO OCONV/S MX SE ICONV MO ICONV MO0C ICONV MX ICONV MX0C ICONV HEX OCONV/S MB0C OCONV MO0C OCONV/S MX0C OCONV/S HEX CHAR From 01100100 01100100 100 100 100 100 144 144 64 64 d d d d 100 d 01100100 144 64 d 100 d 100 d 01100100 64 64 100 To

Examples of Numbering System Conversions For the syntax and use of these functions, see the UniBasic Commands Reference. The following example demonstrates the use of ICONV to convert from hexadecimal to ASCII character:

174

Developing UniBasic Applications

Formatting and Converting Data

Program Example
PRINT PRINT INPUT o_num PRINT END "Convert hex to ASCII character." "Enter hex number to convert: " : i_num = ICONV(i_num,'MX0C') "hex ":i_num:" = ASCII character ": o_num

Here is the output from the previous program:

Screen Example
Convert hex to ASCII character. Enter hex number to convert: ?64 hex 64 = ASCII character d

Dates and Times


The OCONV and ICONV functions convert dates and times from the internal format stored in UniData files to a more readable external format suitable for display or inclusion in a report.

Developing UniBasic Applications

175

Chapter 6 - Working with Data in Programs

Date Formats
Valid formats for internal storage and external display of dates include the following: Internal format The number of days before or since December 31, 1967. External format You can direct UniData to use spaces, forward or backward slashes, periods, or commas as delimiters when converting to or from the following output formats: Standard U.S.: MM/DD/YY International: DD/MM/YY Month, Day, Year (month can be a number, three-digit abbreviation, or can be spelled out; day of the week can be added)

Tip
Store dates in internal format so that you can then perform arithmetic operations on them.

Functions for Converting Dates


Use the ICONV and OCONV functions to convert between internal and external date formats. Function ICONV D OCONV/S D ICONV MT OCONV/S MT Action Converts from standard U.S. external date format to system date. Converts from internal system date to a specified external date format. Converts from external time format to system time. Converts from internal system time to external time format. Functions for Converting Dates For the syntax and use of these functions, see the UniBasic Commands Reference.

176

Developing UniBasic Applications

Formatting and Converting Data

Points to Remember When Entering Dates


If you are not aware of the following defaults, conversions into internal date format (using ICONV) might not produce the effects you expect: If you do not enter a year, UniData defaults to the current year. For example, if you enter 12/1 and the year is 1996, the system stores 10563 (12/1/96). If you enter a number between 1 and 12, UniData defaults to the first day of the month and the current year. For example, if this year is 1996 and you enter 12 only as the date, UniData stores 10563 (12/1/96).

UDT.OPTIONS
You can invalidate all date input of less than six digits by setting UDT.OPTIONS 82 on.

Century Pivot Date


Prior to UniData 5.2, any 2-digit year entered from 01 through 29 defaulted to the next century. For example, UniData interpreted 12/31/29 as December 31, 2029. 1930 was the century pivot date. You can now set your own century pivot date. The century pivot date only applies to the ICONV function when using the D2 format, not D3 or D4. The udtconfig file, located in /usr/ud52/include on UniData for UNIX or \udthome\include on UniData for Windows NT or Windows 2000, now contains the CENTURY_PIVOT parameter, with a default value of 1930. You can change this value in one of the following ways: Enter a 4-digit year. UniData interprets this first 2 digits as the century, and the last 2 digits as the year. The last 2 digits of the year you enter, through 99, are considered to be in the century you specify. 0, through the year you entered -1, are considered to be in the next century. For example, if the century pivot date is 1950, years 50 through 99 are in the 1900s, and years 0 through 49 are in the 2000s. If the century pivot date is 2000, 0 through 99 are in the 2000s. Enter a code in the form of nn, indicating that the next nn years are in the next century. UniData calculates the century pivot date as: current_year - (100 - nn)

Developing UniBasic Applications

177

Chapter 6 - Working with Data in Programs

For example, if the current year is 2000 and the century pivot code is 50, the century pivot date is 1950 (2000 - (100 - 50)). The CENTURY.PIVOT ECL command overrides the systemwide century pivot date defined in the udtconfig file. Syntax: CENTURY.PIVOT [4-digit year | nn] If you enter CENTURY.PIVOT with no options, UniData returns the current setting for the century pivot date. You can also use the UniBasic CENTURY.PIVOT function to set the century pivot date. Syntax: CENTURY.PIVOT(4-digit year | nn)

Time Formats
Valid formats for internal and external storage and display of time include the following: Internal format The number of seconds since midnight. External format You can direct UniData to use spaces or special characters such as slash, period, comma, or asterisk as delimiters when converting to the following output format: HH MM [SS].

178

Developing UniBasic Applications

Formatting and Converting Data

Date and Time Examples


The following program demonstrates conversion into internal date format. First, the user is prompted for date, and that date is converted to internal format. On the first print line, the date is displayed both as entered and in internal format. The next print line displays the internal date converted back to external format with a slash between the two-character month, day, and year.

Program Example
PRINT "Enter date to be converted to internal format:" INPUT i_date o_date = ICONV(i_date,"D") PRINT "Date ":i_date:" was converted to ":o_date PRINT o_date:" converts back to ":OCONV(o_date,"D2/") END

The following results if the year is 1995 and you enter 3 in response to the prompt:

Screen Example
Enter date to be converted to internal format: ?3 Date 3 was converted to 9922 9922 converts back to 03/01/95

The following table shows several date and time conversions. Function OCONV/S D2/ OCONV/S D4* ICONV MT Input Value 1 10300 12:00 Output Format 1/1/68 03*13*1996 43260

Date and Time Conversion Examples

Developing UniBasic Applications

179

Chapter 6 - Working with Data in Programs

Function ICONV D OCONV/S DDMY,A,Z4 OCONV/S MT

Input Value 12/1/28 22251 82800

Output Format 22251 01 December 2028 23:00

Date and Time Conversion Examples (continued)

Note
Dates default to U.S. format. For European format, use the ECL command DATE.FORMAT, which is described in the UniData Commands Reference.

180

Developing UniBasic Applications

UniBasic Multibyte Support

UniBasic Multibyte Support


This section summarizes the support of languages that require multiple bytes to represent a single character. Modified Functions and Commands Support multibyte languages. Single-Byte Functions Support only single-byte languages. Multibyte Functions Designed specifically for multibyte language support.

Multibyte languages require that strings be recognized by character rather than byte. These changes do not affect functionality for single-byte languages, because, for these languages, one byte represents each character.

Modified Functions and Commands


The following table lists the UniBasic functions that have been modified to analyze strings by character instead of byte. Function CHANGE CHAR CHARS COL1 COL2 CONVERT COUNT Comment Replaces all occurrences of a substring with a string. Changes a numeric expression to its ASCII (American Standard Code for Information Interchange) character string equivalent. Changes a numeric value in an array to its ASCII character equivalent. Returns the column position preceding a substring found by the FIELD function. Returns the column position following a substring found by the FIELD function. Converts a single- or multibyte character string to another character string. Returns the number of times a substring appears within a string. UniBasic Character Recognition Functions

Developing UniBasic Applications

181

Chapter 6 - Working with Data in Programs

Function COUNTS DCOUNT FIELD

Comment Returns the number of times a substring appears within each element of an array. Counts delimited substrings. Locates and returns a substring or group of substrings; treats a string as an array, with fields delimited by any ASCII character. See also COL1 and COL2 in this table. Determines the position of a substring in a dynamic array. Receives unprompted input from an attached line. Converts string or numeric data to internal representation format based on conversion codes. Returns the starting position of a specified occurrence of a substring within a string. Requests data from an input queue or the terminal screen. Places the cursor at a specific location on the terminal screen and requests input from the user. See the note on mask parameters later in this chapter. Returns the length of an expression. Returns the length of the values within each element of a dynamic array. Determines if a variable matches a specific pattern of characters or numbers. Only single-byte characters are considered to be alphabetic or numeric. Returns a substring that matches a pattern or literal. Only single-byte characters are considered to be alphabetic or numeric. Converts string or numeric data from internal format to display format based on conversion codes. UniBasic Character Recognition Functions (continued)

FINDSTR GET ICONV INDEX INPUT INPUT @

LEN LENS MATCH

MATCHFIELD OCONV

182

Developing UniBasic Applications

UniBasic Multibyte Support

Function REMOVE

Comment Searches a dynamic array for system delimiters, then assigns the delimiter and following array element to a variable. Recognizes col.pos as a character position. Converts a single character to its ASCII code value. Converts the first character in each element of a dynamic array to its ASCII code value. Extracts strings from elements within a dynamic array. Replaces all occurrences of one substring with a second substring. UniBasic Character Recognition Functions (continued)

SEQ SEQS SUBSTRINGS SWAP

Note
Mask Multibyte characters could be masked for display purposes. However, each double-byte character uses two display characters. So, four double-byte characters displayed in the mask "######" prints as "XX -XX " (two characters, a space, a hyphen, two characters, and a space).

Single-Byte Functions
The following table lists the functions that might not work properly when used with a multibyte character set. Function ALPHA DOWNCASE Comment Because UniBasic does not recognize multibyte characters as alphabetic, returns 0 instead of converting them. Converts only single-byte characters to lowercase. Single-Byte Functions

Developing UniBasic Applications

183

Chapter 6 - Working with Data in Programs

Function FMT

Comment Formats an expression for display. The length parameter defines the number of display characters. The fill character must be single-byte, and the justification option does not work for multibyte languages. See the note on mask following this table. Determines if an expression is numeric. Returns 0 for multibyte characters. Determines, for each element of an array, if that element is numeric. Returns 0 if the dynamic array element contains multibyte characters. Sets the prompt displayed by the INPUT command to a specified singlebyte character. You cannot prompt with a multibyte character. Converts an expression into a phonetic code. Can return unpredictable results with multibyte characters. Converts only single-byte characters to uppercase. Single-Byte Functions (continued)

NUM NUMS PROMPT SOUNDEX UPCASE

Note
Mask Multibyte characters can be masked for display purposes. However, each double-byte character uses two display characters. So, four double-byte characters displayed in the mask ###### prints as XX -XX (two characters, space, hyphen, two characters, and a space).

Multibyte Functions
The following UniBasic functions are designed for use with multibyte character sets. Function BYTELEN CHARLEN Description Returns the number of bytes in a string. Returns the number of characters in a string. UniBasic Functions for Multibyte Languages

184

Developing UniBasic Applications

UniBasic Multibyte Support

Function DISPLAYWIDTH ISMB LEN MBLEN

Description Returns the number of bytes required to display a string expression. Returns a 0 for single-byte language setting, 1 for multibyte language setting. Returns the number of bytes in a string. Returns the number of bytes required to display a character. UniBasic Functions for Multibyte Languages

Note
For examples of these commands in multibyte languages, see UniData International.

Developing UniBasic Applications

185

Chapter 6 - Working with Data in Programs

186

Developing UniBasic Applications

Chapter 7 - External Interaction

This chapter introduces the concepts and commands that enable you to write a UniBasic program that communicates with external software and hardware.

Developing UniBasic Applications

187

Chapter 7 - External Interaction

In This Chapter
UniBasic program interaction with software and hardware is presented in the following sections: Interacting with Other UniBasic Programs Interacting with UniData Writing User Exits Interacting with Hardware I/O Devices Interacting with the Operating System

For more information about running UniBasic programs from ECL, see the UniData Commands Reference. For more information about using C programs (for UNIX) or external programs (for Windows NT or Windows 2000) with UniData, see Chapter 8 - Linking Programs with UniData. For more information about the interaction of UniBasic with paragraphs, menus, and virtual attributes, see the Using UniData.

188

Developing UniBasic Applications

Interacting with Other UniBasic Programs

Interacting with Other UniBasic Programs


You can interact with other UniBasic programs by sharing data, by incorporating code at compilation, and by calling programs. These commands and concepts are presented in the following parts of this section: Sharing Data Including Code at Compilation

Sharing Data
When you design an application, you can divide processing into several programs that use the same variables. You can use one of the following vehicles for passing variables among UniBasic programs: Program Arguments (using @SENTENCE) Subroutine Call Arguments COMMON Data Stacks

Program Arguments
You can execute a UniBasic program from ECL, from a paragraph, or from another UniBasic program. The statement that executes the program is stored in @SENTENCE. You can retrieve arguments that were included in this statement by parsing the contents of @SENTENCE.

Tip
Remember that if you include EXECUTE or CHAIN in the program, the value of @SENTENCE will be updated.

Developing UniBasic Applications

189

Chapter 7 - External Interaction

Subroutine Call Arguments


When only few variables are being passed, or only two programs share them, passing variables through arguments is the most expedient method of sharing data. The variables must be defined the same in the calling and called routines, and the called program or subroutine must provide for the arguments in the SUBROUTINE statement. The CALL command transfers processing to the called program and lists the arguments to be passed. The SUBROUTINE command begins a cataloged subroutine and lists the arguments to be received. Regardless of the BASICTYPE you use to compile, if you pass the same variable twice, UniBasic updates the variable values for both occurrences of the variable whenever you change either one. For this reason, both xx and yy always contain the same value in the following program: test program: tv1 = "howdy" CALL test.sub(tv1,tv1) test.sub program:
SUBROUTINE test.sub(xx,yy) xx = xx : "!" yy = yy : "?" PRINT "xx = ":xx PRINT "yy = ":yy RETURN

In the preceding subroutine, both variables, xx and yy refer to the variable tv1. So when xx is updated by being concatenated with !, yy is also updated, and when yy, which now contains howdy!, is updated by being concatenated with ?, xx is also updated. The output for this program follows:
xx = howdy!? yy = howdy!?

190

Developing UniBasic Applications

Interacting with Other UniBasic Programs

COMMON
When you are sharing groups of variables among multiple programs, you can use the single unnamed common or multiple named common areas to pass a stack or stacks of variables. Each program that needs to access the variables must define the named or unnamed common area(s), by executing the COMMON command, before accessing them. You can most efficiently accomplish this by defining common in a file and including that file in programs at compile time (with $INCLUDE).

Tip
When possible, pass parameters in SUBROUTINE arguments rather than through common areas. This makes it easier to tell which subroutine uses which arguments.

Warning
Trying to access variables in common areas before defining them with the COMMON command could result in a core dump.

Data Stacks
You can use a data stack to pass data to INPUT statements in called processes or UniBasic programs. The stack is available until the user process ends, returning the user to the ECL prompt.

Tip
The DATA statement, which loads the data stack, can also be placed in a UniData paragraph. The data stack is managed as follows: The stack is stored in the @DATA system variable. The stack is loaded by the DATA command. Items in the stack are delimited by CR (carriage return). The stack is built and depleted on a first in first out basis. After UniData exhausts all the responses in the data stack, it reverts to the terminal for user input.

Developing UniBasic Applications

191

Chapter 7 - External Interaction

Tip
To suppress output to the terminal, execute the UniBasic HUSH command immediately before the INPUT command that reads the stack; otherwise, the INPUT prompt and data stack response are displayed. Examples of Passing Data Through a Data Stack In this example, a program prompts for user input and displays information requested by the user on a display terminal; another program prints the same information in hardcopy. Both programs require input to be loaded into the variables ACTIVITY.END.DATE and REGION. The first program CHAINs to the second program, passing the values in a data stack rather than prompting the user for the same data twice. The first program contains the following code:

Program Example
PRINT "Enter the last date for activity" INPUT ACT.END.DATE * check for valid dates and convert to internal form PRINT "Enter Region" INPUT REGION * check for valid regions * display data PRINT "Do you want a hardcopy?" INPUT ANS IF ANS = "Y" THEN DATA ACT.END.DATE DATA REGION CHAIN "REGION.PRINT"; * execute REGION.PRINT program END . . .

When the INPUT statements in REGION.PRINT are executed, UniBasic fills them from the data stack.

192

Developing UniBasic Applications

Interacting with Other UniBasic Programs

The following program segment, which is taken from REGION.PRINT, prompts for the passed data. The presence of the data stack does not inhibit the prompt; it displays on the screen followed by the response from the data stack.

Program Example
PRINT "Enter the last date for activity" INPUT ACT.END.DATE * check for valid dates and convert to internal form PRINT "Enter Region" INPUT REGION * check for valid regions

Note
You can clear the stack created by DATA statements using either the UniBasic or ECL CLEARDATA commands.

Including Code at Compilation


You can insert a UniBasic program or program segment during compilation with the UniBasic commands $INCLUDE and $INSERT. You might use these commands to include stack routines that perform frequently used operations, such as EQUATE and DEFFUN.

Executing UniBasic Programs and Subroutines


The following table lists commands that execute other UniBasic programs and subroutines. Command CALL Acton Transfers control to an external subroutine. When UniBasic encounters a RETURN statement in the subroutine, it returns control to the calling program. Executing UniBasic Programs and Subroutines

Developing UniBasic Applications

193

Chapter 7 - External Interaction

Command CHAIN

Acton Terminates the current UniBasic program and executes another program. A common use of CHAIN is to execute a cataloged program. Transfers control to a cataloged program. Executes an ECL or UniData SQL command. (You can EXECUTE RUN program.)

ENTER EXECUTE/ PERFORM

Executing UniBasic Programs and Subroutines (continued)

UDT.OPTIONS
UDT.OPTIONS 6 and 40 determine where control is passed on RETURN; UDT.OPTIONS 11 and 27 clear the data stack. For more information, see the UDT.OPTIONS Commands Reference.

194

Developing UniBasic Applications

Interacting with UniData

Interacting with UniData


Interaction with other UniBasic programs and UniData products is presented in the following sections: Executing Virtual Attributes Executing ECL Statements Executing UniData SQL Statements Defining and Using Programs and Functions

Reminder
UniData stores and makes available information about the system, the user process, and the UniBasic program being executed in @variables. Some are available through any UniData or 4GL tool; others are available only through UniBasic. For a complete list of UniData @variables, see Using UniData. The UniBasic @variables are listed in the UniBasic Commands Reference.

Executing Virtual Attributes


Reminder
Virtual attributes are dictionary records that contain executable code. They are explained in Using UniData. You can execute a virtual attribute from within a UniBasic program with the UniBasic CALCULATE or {} command, or the ITYPE function. You must first open the dictionary containing the virtual attribute to @DICT and read a record from the corresponding data file into @RECORD.

Developing UniBasic Applications

195

Chapter 7 - External Interaction

Executing ECL Statements


You can execute ECL commands within a UniBasic program. Any entry valid at the ECL prompt can follow an execute command. The following table lists the UniBasic commands for executing UniData commands. Command EXECUTE PERFORM Action Executes an ECL command. Returns output and error messages in variables if specified. Note - Use the CAPTURING clause to save output of the executed command in an dynamic array. Use the RETURNING clause to save error messages. MDPERFORM Executes a UniData command, and transfers a select list if specified. Also returns output and error messages in variables if specified. Executes an ECL command in BASICTYPE U regardless of the BASICTYPE the program was compiled in. Also returns output and error messages in variables if specified. Execute Commands The following UniBasic statement executes the ECL command LIST.READU:

UDTEXECUTE

Program Example
EXECUTE "LIST.READU"

196

Developing UniBasic Applications

Interacting with UniData

The next UniBasic statement executes a UniQuery statement:

Program Example
EXECUTE "SSELECT CUSTOMER WITH STATE='CO' BY ZIP"

For more information about ECL, see Using UniData.

Executing UniData SQL Statements


You can use the EXECUTESQL, EXECUTE, and PERFORM commands to execute UniData SQL statements. If you need to return selected items for subsequent processing by the program, include a TO clause and a variable in which UniData SQL can return selected items. In the following example, the variable SQL_INPUT is provided for the return of NAME, ADDR, and CITY:

Screen Example
EXECUTESQL "SELECT NAME,ADDRESS,CITY FROM CLIENTS TO SQL_INPUT;" DONE=0 LOOP PRINT "DONE = ":DONE READNEXTTUPLE CLIENT.REC FROM 'SQL_INPUT' ELSE DONE = 1 UNTIL DONE CONVERT @AM TO " " IN CLIENT.REC CONVERT @VM TO "," IN CLIENT.REC PRINT "CLIENT.REC = ":CLIENT.REC REPEAT END

Note
You must use the UniBasic command CLEARSQL to clear active UniData SQL variables created with EXECUTESQL...TO statements.

Developing UniBasic Applications

197

Chapter 7 - External Interaction

Defining and Using Programs and Functions


Within a UniBasic program, you can use the commands described in the following table to write and call other programs and functions. Command/ Function PROGRAM FUNCTION DEFFUN Action Provided for backward compatibility. Defines a program. Creates a user-written function that accepts passed arguments and can return values. Declares a user-written function that was defined by the UniBasic FUNCTION command.

Commands for Defining and Using Programs and Functions

198

Developing UniBasic Applications

Writing User Exits

Writing User Exits


An explanation of user exits and the procedures for writing and calling them are explained in the following subsections: What Are User Exits? Calling a User Exit from UniBasic Calling a User Exit from a Virtual Attribute Calling a User Exit from a Proc Parameters in User Exits

What Are User Exits?


Some RDBMS systems employ user exits to convert and format data. UniBasic provides many of these conversions in the UniBasic ICONV and OCONV functions. However, for backward compatibility, UniData supports user exits, which consist of a calling statement and a UniDataprovided or user-defined UniBasic subroutine. You can call user exits from UniBasic, UniQuery, and UniData paragraphs. For backward compatibility, UniData supports user exits from Procs. This section focuses on calling user exits from UniBasic programs and calling UniBasic subroutines that serve as user exit functions.

Tip
You might find that the operations provided by user exits can be performed more efficiently by a UniData @variable, a UniBasic command or function, or a UniData paragraph.

Developing UniBasic Applications

199

Chapter 7 - External Interaction

Calling a User Exit from UniBasic


Perform the following steps to call a user exit from a UniBasic program. The called user exit can be UniData-defined or user-defined. 1. Select a UniData user exit that performs the conversion you want. For a list of user exit conversion routines, see Using UniData. or Write a UniBasic subroutine to perform the conversion. The first command in the subroutine must be the following: Syntax: SUBROUTINE Uuser.exit(ret.val, status, input.val, type) 2. Write the user exit call in your UniBasic program. Regardless of the type of user exit you are calling, use the following syntax: Syntax: ret.val = {ICONV | OCONV}(input.val,"Uuser.exit") The parameters used in these commands are explained in Parameters in User Exits later in this section.

Calling a User Exit from a Virtual Attribute


Perform the following steps to write a virtual attribute that calls a UniBasic user exit or UniDatadefined user exit: 1. Select a UniData user exit that performs the conversion you want. For a list of user exit conversion routines, see Using UniData. or Write a UniBasic subroutine to perform the conversion. The first command in the subroutine must be the following: SUBROUTINE Uuser.exit(ret.val, status, input.val, type)

200

Developing UniBasic Applications

Writing User Exits

2. Write a virtual attribute that calls the user exit. The syntax that you use depends on whether you are converting single or multivalued/multi-subvalued attributes: Singlevalued: {ICONV | OCONV}(input.val,"Uuser.exit") Multivalued/Multi-subvalued: SUBR(-{ICONVS | OCONVS},input.val,"Uuser.exit")

The parameters used in these commands are explained in Parameters in User Exits later in this section.

Calling a User Exit from a Proc


Perform the following steps to write a Proc that calls a UniBasic- or UniData-defined user exit: 1. Select a UniData user exit that performs the conversion you want. For a list of user exit conversion routines, see Using UniData. or Write a UniBasic subroutine to perform the conversion using the following syntax: Syntax: SUBROUTINE Uuser.exit(proc,cib,pib,sib,ibp,cob,pob,sob) 2. Write a statement in a Proc that calls the user exit. Syntax: Uuser.exit param1 [,param2]...[,]...

Developing UniBasic Applications

201

Chapter 7 - External Interaction

Parameters in User Exits


The following table describes each parameter of the syntax. Parameter Uuser.exit ret.val status input.val type proc cib pib sib ibp cob pob sob Description The name of the UniData user exit or user-defined UniBasic subroutine that performs the conversion. The string or value UniData returns to the calling program as a result of the conversion function. The status of the conversion; 0 indicates a successful conversion. The input string or value to be converted. The type of conversion: 0 for external format to internal format and 1 for internal format to external format. The source code of the Proc itself. UniData reads the Proc user exit statement, then passes it and all subsequent lines as proc. The current input buffer: 0 for primary and 1 for secondary. The primary input buffer. The secondary input buffer. The input buffer pointer. The current output buffer: 0 for primary and 1 for secondary. The primary output buffer. The secondary output buffer. Parameters in User Exits

202

Developing UniBasic Applications

Interacting with Hardware I/O Devices

Interacting with Hardware I/O Devices


When programming in UniBasic, you can direct input and output (I/O) devices such as display terminals, printers, and tape drives. These commands are introduced in the following subsections: Display Terminals Printers Tape Drives

Display Terminals
Input Commands
Input commands request data from a users keyboard or a data stack and store the result in a variable for use in a UniBasic program. For more information about data stacks, see Using UniData. UniBasic provides the following commands for accepting and acting on user input from the terminal. Command INPUT INPUT @ Action Requests data from a user terminal or a data stack. Requests data from a user terminal or a data stack, determines the cursor position and display a mask on the terminal, and determines the characteristics of the data received. Allows a single character to be defined as the null character in an INPUT @ statement. Prints a user-defined error message on the bottom line of the display terminal and clears the error message when UniData receives the next input. Input Commands

INPUTNULL INPUTERR

Developing UniBasic Applications

203

Chapter 7 - External Interaction

Command INPUTIF CLEARINPUT INPUTCLEAR INPUTTRAP

Action Assigns data from the type-ahead buffer to a variable. Clears the type-ahead buffer. Conditionally branches to a specific statement label when specific characters are entered. Input Commands (continued)

Tip
You can fill input statements from the data stack rather than from user input. For example, the following program segment loads the string FINISHED in the data stack and then loads it in the variable CODE.

Program Example
DATA "FINISHED" INPUT CODE

UniBasic allows up to 500 elements in the data stack. For more information about the data stack, see Sharing Data earlier in this chapter, or see the DATA command in the UniBasic Commands Reference. The UDT.OPTIONS described in the following table affect the input commands. UDT. OPTION 12 Description When ON If you use INPUT var, expr with a data stack and an element in the data stack is shorter than expr, UniData retains unused characters and they are available for subsequent input statements. UDT.OPTIONS for Input

204

Developing UniBasic Applications

Interacting with Hardware I/O Devices

UDT. OPTION 18 65

Description When ON When UniData passes data to a UniBasic program to fill an input statement, UniData suppresses the echo of the prompt character and the data. If you exceed the field length during an INPUT command, the terminal beeps. UDT.OPTIONS for Input (continued)

UDT.OPTIONS
For more information about UDT.OPTIONS, see the UDT.OPTIONS Commands Reference.

More Display Terminal Commands and Functions


You can use the following UniBasic commands to manage display terminal and keyboard I/O. Terminal Control Use the commands in the following table to control display on the terminal. Command CRT, DISPLAY HUSH PAGE PRINT PRINT @ Action Directs output to the display terminal (CRT) regardless of the setting of the PRINTER command. Enables or disables terminal output. Prints the current output page. Prints to the display terminal or printer. Prints to the display terminal at a specific column and row position. For more about the @ function, see Enhanced Terminal Capabilities later in this chapter. Terminal Control Commands

Developing UniBasic Applications

205

Chapter 7 - External Interaction

Note
Print lines can be up to 256 characters. For more information about LIMITS, see the UniData Commands Reference. Keyboard Control Use the ECHO and BREAK commands to control keyboard input. Command ECHO BREAK Action Enables or disables display of keyboard input on the terminal. Enables or disables the break key. When enabled, the break key interrupts program execution and places the user at the debugger prompt. Keyboard Control Commands Prompt, Headings, and Footings The commands in the following table control the display of the prompt character and headings and footings. Command PROMPT HEADING FOOTING Action Sets the input prompt to a user-supplied one-character string or empty string. Displays a heading at the top of each page, which can be up to 536 characters. Displays a footing at the bottom of each page, which can be up to 536 characters. Prompt, Heading, and Footing Commands

206

Developing UniBasic Applications

Interacting with Hardware I/O Devices

Enhanced Terminal Capabilities


The UniBasic @ function has two forms, which enable you to: Syntax: @(col[,row]) @(-num.expr) For a table of all @ options, see the @ function in the UniBasic Commands Reference. Some commonly used options are provided in the following table. Option -1 -2 -3 -4 -19 -10 -17 Action Clears the screen and places the cursor at position 0,0. Places the cursor at position 0,0. Clears from cursor to end of screen. Clears from cursor to end of line. Sends audible signal. (Terminal beeps.) Moves cursor up one line. Move cursor down one line. @ Function Options for Terminal Control Specify a column and row to position the cursor. Perform one of 84 terminal actions.

Reminder
Because @ is a function, it must be used within a statement that contains a command, such as PRINT @ or DISPLAY @.

Developing UniBasic Applications

207

Chapter 7 - External Interaction

Examples
The PRINT @ statement prints data at a specific column and row. For example, the following statement prints three asterisks on row 1 beginning in column 38:

Screen Example
PRINT @(38,1):'***'

The following command clears the display terminal and positions the cursor at row 0, column 0:

Screen Example
PRINT @(-1)

In the next example the terminal bell beeps after an error message is printed:

Screen Example
PRINT @(0,23):"Invalid choice. Please re-enter.":@(-19)

Note
UniBasic searches the /usr/lib/terminfo file (for UNIX) or the udthome\include\udtermcap file (for Windows NT or Windows 2000) to obtain terminal configuration and interpretation of the @ code. For UNIX, if terminfo does not exist, UniBasic queries the /etc/termcap file. Obtain terminal emulation settings from your hardware vendor. For Windows NT or Windows 2000, if udtermcap does not exist, UniBasic returns a status message stating that it cannot find the file and will default to vt100 terminal emulation.

208

Developing UniBasic Applications

Interacting with Hardware I/O Devices

Printers
The following table lists the commands that direct the printer. Command PRINT ON PRINTER CLOSE PRINTER OFF PRINTER ON Action Direct output to a specific printer. Direct output stored in a print file or a print buffer to the print queue. Direct output to the display terminal. Direct output to the printer. Printer Commands

Note
Print lines can be up to 256 characters. For more information about LIMITS, see the UniData Commands Reference.

Reminder
The following commands manage both display terminal and printer output: FOOTING, HEADING, PAGE, and PRINT.

Developing UniBasic Applications

209

Chapter 7 - External Interaction

UDT. OPTIONS and Printing


The UDT.OPTIONS in the following table affect the way in which data is printed. UDT.OPTION 4 5 7 Command Affected OCONV in the absence of HEADING n/a Effect When On When printing dates, formats month in uppercase. Pauses at the bottom of each screen page. Page feeds or returns to the colon prompt after the last line of data is printed rather than printing blank lines to end of page. Closes the print job after printing completes. Prints the break line and suppresses the preceding blank line, essentially inhibiting double spacing. When printing dates, converts Sunday to 7 (rather than 0). Retains the heading when the @ function is used. Displays the system date in a specific format. This option also is affected by the ECL command DATE.FORMAT.

9 25

EXECUTE, PERFORM UniQuery BREAK.ON keyword OCONV HEADING, @ function HEADING, FOOTING

29 32 34

UDT.OPTIONS for Printing

210

Developing UniBasic Applications

Interacting with Hardware I/O Devices

UDT.OPTION 46

Command Affected PRINT, CRT DISPLAY, EXECUTE PERFORM, INPUT, SLEEP, STOP

Effect When On Waits for PRINT, INPUT, EXECUTE, or FLUSH before flushing data to the system buffer.

48

PRINT, DISPLAY, CRT

When printing right-justified data, does not break as specified in format. (Will overwrite another column of data.) When generating a BSELECT list, does not create a blank line for a key when the selected attribute is an empty string. Forces the footing to print on the final page of a report.

59

PRINT, DISPLAY, CRT

64

FOOTING

UDT.OPTIONS for Printing (continued)

UDT.OPTIONS
For detailed information about UDT.OPTIONS, see the UDT.OPTIONS Commands Reference.

Developing UniBasic Applications

211

Chapter 7 - External Interaction

Tape Drives
Before you execute the UniBasic tape drive commands, you must: Initialize the tape drive with the ECL SETTAPE command. Attach the tape drive with the ECL T.ATT command.

Other ECL commands also manage the tape drive. For more information, see Using UniData. UniBasic manages tape drive I/O with the following commands. Command READT RESIZET Action Reads the next available record from a tape. Changes the block size used by the WRITET command when the block size in one file is not the same as the block size in T.ATT. Rewinds the tape. Writes an EOF (end-of-file) mark to tape. Writes a record onto tape. Tape Drive Commands

REWIND WEOF WRITET

212

Developing UniBasic Applications

Interacting with the Operating System

Interacting with the Operating System


Use the commands in the following table to interact with the operating system within a UniBasic program. Command/ Function PCPERFORM SYSTEM Action Executes an operating system command within a UniBasic program. Retrieve system-level information set by previous UniBasic or ECL commands such as SETPTR and TERM. UniBasic Operating System Commands For more information about these functions, see the UniBasic Commands Reference. The following UniBasic statement executes the UNIX command pwd (print working directory) and stores the results in the variable MY.LOCATION:

Screen Example
PCPERFORM 'pwd' CAPTURING MY.LOCATION

Developing UniBasic Applications

213

Chapter 7 - External Interaction

214

Developing UniBasic Applications

Chapter 8 - Linking Programs with UniData

This chapter introduces the concepts and procedures for writing a UniBasic program that calls or is called by an external C program. For more information about CALLC, see Administering UniData on UNIX or the Administering UniData on Windows NT or Windows 2000.

Developing UniBasic Applications

215

Chapter 8 - Linking Programs with UniData

In This Chapter
This chapter consists of the following sections: Linking C Programs (UNIX Only) Linking C Programs (Windows NT or Windows 2000 Only)

216

Developing UniBasic Applications

Linking C Programs (UNIX Only)

Linking C Programs (UNIX Only)


The information in this section applies to the UNIX operating system only. If you use Windows NT or Windows 2000, see Linking C Programs (Windows NT or Windows 2000 Only) later in this chapter.

Before You Begin


Before you try to set up links between UniData and C programs, review the following tips. Before you link a C program for CallBasic Determine whether someone has previously modified the executable files in udthome/work in an effort to link C programs to your UniData executable. If these files have been changed, you must restore them to their original state. For more information, see If This Is Not the First Time later in this chapter. Before you link a C program for CALLC When you upgrade from an earlier release, UniData asks whether you want to overlay the cfuncdef file. If someone installing or upgrading UniData chose not to overlay this file, the wrong version could reside in udthome/work. The cfuncdef file printed in the section File Examples later in this chapter shows the correct version of this file to use with UniData. Set up a test environment You can set up an alternate test environment with a separate udt to use to test linked programs so you do not disrupt the work of others. For instructions, see Administering UniData on UNIX. Sign on as root UniData supplies required files and templates needed to link the C program or function with UniData. These files and templates reside in the udthome/work directory, to which only root has read or write access. You can copy the files to another directory temporarily to work on them. However, unless you set up a test environment, they must reside in udthome/work before you run the make, makeudt, or makeudapi command to set up a CallBasic or CALLC link with UniData.

Developing UniBasic Applications

217

Chapter 8 - Linking Programs with UniData

Provide absolute path Any time you specify a path in a file, you must specify the absolute path, such as /usr/ud52/work, rather than using an environment variable, such as udthome/work.

Regarding triggers You can call a C function from a UniBasic trigger subroutine. For information about UniData triggers, see Chapter 4 - Maintaining Data in Files.

Define environment variables The following environment variables must be defined: udthome, udtlib, and udtbin. For information about setting up environment variables, see Administering UniData on UNIX.

You must have the following components to use the CallBasic API: Development environment Your system must have a full software development kit. (A base compiler is not sufficient.) You also will need network libraries if you are using NFA.

Tip
Consult your host operating system documentation and your hardware vendor if you have questions about your C development environment. C program You need to code and compile the C application that calls UniBasic. Function definitions and makefiles When you install UniData, the file callbas.mk is installed into the directory udthome/work. You will use this makefile as a template to build your application with UniData linked into it.

218

Developing UniBasic Applications

Linking C Programs (UNIX Only)

Calling a C Function from UniBasic with CALLC


Follow the procedure described in this section to link a C function with UniData so that it can be called by a UniBasic program.

Tip
To save time and frustration, read Before You Begin earlier in this chapter.

Procedure Summary
Here is a summary of the steps you must follow to link a C program with UniData so that it can be called by a UniBasic program: 1. Write the C program. 2. Compile the C program. 3. Tell UniData about the C program (in cfuncdef_user). 4. Rebuild the UniData executable (using makeudt or makeudapi). 5. Write and compile the UniBasic subroutine. 6. Execute the UniBasic subroutine.

1. Write the C Program


The first step in creating a C function that you will call from a UniBasic program is to write the C program with the standard compiler for your platform and operating system. Guidelines for Writing C Programs You might find the following guidelines helpful when writing the C program. Naming Variables Avoid naming variables or functions with the prefix U and an underscore (U_), such as U_unit and U_errout. UniData uses U_ as an identifier for variable names and functions.

Developing UniBasic Applications

219

Chapter 8 - Linking Programs with UniData

Passing Arguments You cannot pass more than 22 arguments. Each argument is limited to 255 characters. Include the bstring header to pass binary data. For more information, see the Data Types for UNIX C Definition table later in this chapter.

Returning Arguments The maximum number of bytes allowed in a function return is 256. If you increase the size of a variable of data type bstring, you must free the original memory assignment and reallocate it and reassign the length to avoid a memory leak. For more information, see the Data Types for UNIX C Definition table later in this chapter.

Passing Binary Data Include the bstring header file to pass a binary string to or from a C program, especially when that data could contain imbedded ASCII character 0 (which C interprets as a line terminator). To do this, the C function must include the header file callc_bstr.h, which contains the definition of bstring, and set the returning string length (for example, retbuf->len and out->len). A sample C program is provided in Passing bstring-Type Data later in this chapter.

Displaying Error Messages To display error messages, use the UniBasic C function U_errout. U_errout has the same syntax as U_preprint, except the variable U_unit is replaced by 0. U_erroutoutput goes to errout whereas U_preprint output goes to stdout.

Syntax: U_errout(0,"error message from the routine, value is %d",value); Printing To maintain screen integrity and I/O redirection, use the UniBasic C function U_preprint instead of the C function printf. The U_preprint function refreshes the screen, enabling the C subroutine to properly manage screen I/O. This function follows syntax similar to printf.

Syntax: U_preprint pattern,arg1,arg2...

220

Developing UniBasic Applications

Linking C Programs (UNIX Only)

For example:

Program Example
extern int U_unit; . . . U_preprint(U_unit,"message from the routine, value is %d",value);

Ending the C Program Do not use exit to end the C program. Instead, use U_done, which performs various cleanup tasks, and then terminates the C program.

C Program Example The following C function (c_example) emulates the UniBasic GETENV function. Both retrieve the value of the specified UNIX environment variable using the UNIX getenv system call.

Program Example
char *c_example(envar) char *envar; { char *getenv(); static char buf[100]; char *pathlist; sprintf (buf,"%s",getenv(envar)); return(buf); /* return string to UniData }

*/

Developing UniBasic Applications

221

Chapter 8 - Linking Programs with UniData

2. Compile the C Program


A. Use the UNIX cc command with the -c option to produce the object code. Verify the following before you proceed to the next step: The C program does not contain the main() function. The C program compiles cleanly.

For our example, you enter the following commands at the UNIX shell prompt:

Screen Example
$ cc -c c_example.c

B. Copy the object code to the udthome/work directory:

Program Example
$ cp c_example.o $UDTHOME/work

Permissions
To place the program in udthome/work, you must have write permission to that directory. For assistance, see your system administrator.

222

Developing UniBasic Applications

Linking C Programs (UNIX Only)

3. Tell UniData about the C Program


A. Check for required files in udthome/work. To proceed, the C program must reside in the udthome/work directory, along with the makefile (c_example.mk in this example) and the function definition file (cfuncdef), and the C functions that the makeudt or makeudapi utility uses (funchead.o interfunc.o callcf.o, and efs_init.o). B. Look at cfuncdef. This file, placed in udthome/work at installation, must not refer to any UniData or sitespecific C programs. It could contain reference to site-specific libraries. Some earlier releases of UniData included some UniData functions in cfuncdef. If the person installing UniData Release 5.2 or upgrading from an earlier version chose not to overlay this file, you could have the wrong version. Also, if someone has previously linked C programs to UniData for CALLC, cfuncdef could contain reference to these programs. For an example of an empty cfuncdef file, see the template in File Examples later in this chapter. C. Create cfuncdef_user. After making sure that cfuncdef is free of references to other C programs, make a copy of it, and call it cfuncdef_user. The file should contain the following three lines: $$FUN (you put the C function definition here) $$OBJ (you put the object file here) $$LIB (you put the library path here if the C function is not stored in udtlib)

Developing UniBasic Applications

223

Chapter 8 - Linking Programs with UniData

cfuncdef_user File Example The following example shows a version of the cfuncdef_user file that has been modified to set up the function c_example on a Hewlett Packard computer. The next few steps refer to this example.

Program Example
/* this is a test for adding a C function to the*/ /* Runtime version of UniData. */ /* C function declaration format: function-name:return-type:number-of-argument:arg1,arg2,...argn */ $$FUN /* beginning of C function */ c_example:string:1:string $$OBJ /* object files or .o file comes here */ /usr/ud51/work/c_example.o $$LIB /* library comes here */

D. Add the function definition to cfuncdef_user. The function definition line follows $$FUN. Syntax: fun_name:rtn_data_type:num_arg:arg_data_type A colon separates each element, and a comma separates multiple arguments. You can enter more than one function definition, each on a separate line. The following table lists the function definition arguments. Argument fun_name Description The name of the C program as it will be called from UniBasic programs. The data type of the return value. cfuncdef Function Definition Value in Example c_example

rtn_data_type

string

224

Developing UniBasic Applications

Linking C Programs (UNIX Only)

Argument num_arg arg_data_type

Description The number of arguments. The data type of the arguments. 1

Value in Example

string

cfuncdef Function Definition (continued) The following table lists valid data types for arguments and return values. Data Type int short long double float char string Description An integer. A 32-bit signed long word. A 16-bit signed long word. A 32-bit signed long word (same as int). A 64-bit floating point value. A 32-bit floating point value. A signed byte. A pointer to a character string terminated by NULL (0) in a 34-K buffer. The buffer size cannot be changed. Although memory reallocation is not supported, you can allocate memory for the variable in a UniBasic program before passing it in the parameter to the C program. For example:
... var = SPACE(64000) CALLC(var...) The variable now has been allocated a size of 64 KB.

Data Types for UNIX C Definition

Developing UniBasic Applications

225

Chapter 8 - Linking Programs with UniData

Data Type bstring

Description A pointer to a structure (struc) in a buffer that is a minimum of 34 KB in size. You can reallocate this buffer. Also, you can pass data contain the NULL (0) character by using bstring. Defined as:
typedef struct ( char *str; int len } bstring;

Warning - To prevent a memory leak, you must free the original buffer assignment before reallocating it. If the string is changed, the length must also be reassigned. pointer A 32-bit signed long word. Data Types for UNIX C Definition (continued) E. Add the object code path and file name. Enter the object code path and file name on the line that follows $$OBJ. You can put it in any directory as long as you specify the absolute path here. F. Add the library path to cfuncdef_user. If your function does not reside in udtlib, enter the library path on the line that follows $$LIB, preceded by -L. Do not use the environment variable (udtlib) in the path name, but instead list the absolute path. The function in the example does not link any alternate libraries.

226

Developing UniBasic Applications

Linking C Programs (UNIX Only)

4. Rebuild the UniData Executable


The system-level makeudt command builds a new UniData executable (udt). The makeudapi command also builds a new UniData executable (udapi_slave) with links to C programs so that they are accessible through InterCall, UniObjects, or UniObjects for Java. Use the command that is appropriate for your situation to build the executable.

Warning
If you are upgrading from a version of UniData before Release 3.3, you will need to install the makefile for Release 5.2 because the format for this file has changed more than once in subsequent releases. If you use a pre-3.3 version of the file, makeudt or makeudapi will fail. It is best to be logged in as root to execute makeudt or makeudapi because you must have write access to udthome/work. Also, you must be in the udthome/work directory, but execute the utility from udtbin, as in the following examples: udtbin/makeudt or udtbin/makeudapi. These utilities use cfuncdef_user and base.mk to create the file new.mk. For more information about these utilities, see More on make, makeudt, and makeudapi in this chapter. For information about error messages and common problems you could encounter during this process, see Troubleshooting CALLC later in this chapter.

Tip
If users are logged in to UniData, the makeudt or makeudapi utility might not finish because it might not be able to overwrite the production udt or udapi_slave. If an error message displays indicating an unsuccessful completion, you need to locate the new udt or udapi_slave executable in the udthome/work directory. Later on, when UniData is no longer busy, move the new executable to udtbin. makeudt Syntax: makeudt [-n nfa]

Developing UniBasic Applications

227

Chapter 8 - Linking Programs with UniData

The following table describes each parameter of the syntax. Parameter -n nfa Description Use this option only if you are not using UniData OFS/NFA. This option uses dummy libraries rather than network libraries required by NFA. Software development environments may or may not include the network libraries. If your environment does not include these, and you do not use the -n nfa option, makeudt fails. makeudt Parameters For more information about using the makeudt utility, see Administering UniData on UNIX. makeudapi Syntax: makeudapi

For more information about using the makeudapi utility, see Administering UniData on UNIX.

5. Write and Compile the UniBasic Program


A. Write the UniBasic program. Next, you will write the UniBasic program that uses the UniBasic CALLC function to call the C function. UniBasic Program Example In the following example, the UniBasic program CDEMO performs the following tasks: 1. Prompts for the name of the environment variable for which to display the value. 2. Calls the C program c_example you created. 3. Returns the value of the environment variable requested in the prompt.

228

Developing UniBasic Applications

Linking C Programs (UNIX Only)

Program Example
* CDEMO * PROMPT '' PRINT 'Enter an environment variable (such as PATH or TERM)': INPUT VARNAME EVAL = CALLC c_example(VARNAME) PRINT PRINT 'Value of ":VARNAME:" is ':EVAL PRINT STOP END

B. Compile the UniBasic program. Enter the BASIC command at the ECL prompt, as in the following example. For instructions about compiling and cataloging UniBasic programs, see Chapter 3 - Creating and Running a Program.

Screen Example
:BASIC BP CDEMO

6. Execute the UniBasic Program


Execute the UniBasic program using the UniData ECL RUN command. The following example demonstrates running the UniBasic program CDEMO:

Screen Example
:RUN BP CDEMO Enter an environment variable name (such as PATH or TERM)

Developing UniBasic Applications

229

Chapter 8 - Linking Programs with UniData

To continue the example, the user enters TERM. The program calls the C function that determines the value of the environmental variable and returns it to the UniBasic program, which displays the value.

Screen Example
?TERM Value of TERM is vt100

Passing bstring-Type Data


This example demonstrates passing bstring-type data. Notice that the original bstring passed by the UniBasic program is shorter than the minimum buffer size of 34 KB. Therefore, UniData assigns a 34-KB buffer to it. Within the C program, however, the size of the variable passed by the UniBasic program is changed. To avoid a memory leak resulting from assigning a larger buffer, the C program checks the size of the string to be passed back, frees up the original 34-KB allocation, and reassigns a buffer of adequate size. The following UniBasic program prompts the user for the length of buffer to assign (see length in the INPUT statement), then passes this length to the C program (see length in the CALLC statement):

Program Example
PROGRAM trycallc * * Input a length to indicate that the length of the 2nd IO string * and bstring will be changed in C routine. * PRINT "Input length of new IO strings ": INPUT length < INPUT statement instr = "THE FIRST INPUT STRING" iostr1 = "THE FIRST IO STRING" iostr2 = "THE SECOND IO STRING" bstring = "THE BSTRING PRINT "Before calling C function str_arg()"

230

Developing UniBasic Applications

Linking C Programs (UNIX Only)

PRINT CALLC str_arg(length, instr, iostr1, iostr2, bstring) PRINT PRINT PRINT PRINT PRINT PRINT PRINT PRINT PRINT PRINT < CALLC statement

"After called C function str_arg()" "The "The "The "The "The "The 1st input string 1st IO string length of the 2nd IO string length of the bstring is 1st 20 char of the 2nd string 1st 20 char of the bstring is >> >> >> >> >> >> ":instr ":iostr1 ":LEN(iostr2) ":LEN(bstring) ":iostr2[1,20] ":bstring[1,20]

The following C program is called by the CALLC statement in the preceding UniBasic program. The buffer size assigned to the arguments IOstr2 and bstring are tested. If either is greater than 34KB, the memory allocation for that variable must be freed and reallocated. Because IOstr2 is 1KB in size, the memory allocation is freed and reallocated before passing it to the UniBasic program.

Program Example
#include <stdio.h> #include <string.h> #include "/usr/ud51/include/callc_bstr.h" #define ONEK 1024 extern int U_unit; int str_arg(len, InStr, IOstr1, IOstr2, Bstr) int len; *length of the 2nd IO string and Bstring after updating */ char *InStr; /* an input string, read only */ char *IOstr1; /* an IO string, length of which is less than 34K */ char *IOstr2; * an IO string whose length will be greater * than 34K. */ bstring *Bstr; /* an IO bstring struct pointer */ {

Developing UniBasic Applications

231

Chapter 8 - Linking Programs with UniData

char int

buf1[30], buf2[30]; memsize;

/* * What the input strings are */ U_preprint(U_unit, "The 1st input string U_preprint(U_unit, "The 1st IO string U_preprint(U_unit, "The 2nd IO string U_preprint(U_unit, "The binary string U_preprint(U_unit, "Length of the bstring

(%s)\n", (%s)\n", (%s)\n", (%s)\n", (%d)\n",

InStr); IOstr1); IOstr2); Bstr->str); Bstr->len);

/* * For an IO string, the memory size is either 34 KB * or the length of the original string in the basic * program. Memory reallocation * is not supported for string-type parameters. * Allocate memory in the UniBasic program. (Assign * a long string to the variable as * a work around if the size is known.) */ strcpy(IOstr1, "The 1st IO string is changed"); len *= ONEK; memsize = strlen(IOstr2); memsize = (memsize > 34*ONEK ? memsize : 34*ONEK); if ( len < memsize ) { memset(IOstr2, 'a', len); memcpy(IOstr2, "len < 34K ", 10); IOstr2[len] = '\0'; } else { /* no memory reallocation is allowed here */ memset(IOstr2, 'b', 100); IOstr2[30*ONEK] = '\0'; memcpy(IOstr2, "len > 34K ", 10); } /* * For bstring data, the initial memory size is the * same as for string data, and memory reallocation * is supported. To avoid a memory leak, the buffer

232

Developing UniBasic Applications

Linking C Programs (UNIX Only)

* size for the bstring needs to be freed * before allocating a larger buffer. */ memsize = Bstr->len; memsize = (memsize > 34*ONEK ? memsize : 34*ONEK); if ( len < memsize ) { Bstr->len = len; memset(Bstr->str, 'c', len); memcpy(Bstr->str, "len < 34K ", 10); } else { /* memory re-allocation is allowed here */ free(Bstr->str); Bstr->str = malloc(len); Bstr->len = len; memset(Bstr->str, 'd', len); memcpy(Bstr->str, "len > 34K ", 10); } /* * print out changed strings */ memcpy(buf1, IOstr2, 20); memcpy(buf2, Bstr->str, 20); buf1[20] = buf2[20] = '\0'; U_preprint(U_unit, U_preprint(U_unit, U_preprint(U_unit, U_preprint(U_unit, U_preprint(U_unit, U_preprint(U_unit, return 0; } "\nAfter modification:\n"); "The 1st IO string "Length of the 2nd IO string "Length of the bstring "The 1st 20 char of IO str2 "The 1st 20 char of Bstring

(%s)\n", IOstr1); (%d)\n",strlen(IOstr2)); (%d)\n", Bstr->len); (%s)\n", buf1); (%s)\n", buf2);

Developing UniBasic Applications

233

Chapter 8 - Linking Programs with UniData

The following cfuncdef_user file contains the function and argument definitions needed to link the preceding C program with UniData:

Screen Example
/* comment lines come here. */ /* C function declaration format: * function-name:return-type:number-of-argument:arg1,arg2,...,argn */ $$FUN /* beginning of C function */ str_arg:int:5:int,string,string,string,bstring < argument definitions $$OBJ /* *.o come here */ str_arg.o < function definition $$LIB /* library comes here

The following shows the output produced by executing the UniBasic program trycallc after linking the C program str_arg with UniData. Notice that the user responded to the prompt of length for the output string with a number less than 34; this will require that the memory allocation for IOstr2 be freed and reallocated to pass back a variable of 1 KB in size.

Screen Example
:run BP trycallc Input length of new IO strings ?30 Before calling C function str_arg() The 1st input string The 1st IO string The 2nd IO string The binary string Length of the bstring (THE (THE (THE (THE (11) FIRST INPUT STRING) FIRST IO STRING) SECOND IO STRING) BSTRING)

After modification: The 1st IO string Length of the 2nd IO string Length of the bstring The 1st 20 char of IO str2 The 1st 20 char of Bstring

(The 1st IO string is changed) (30720) (30720) (len < 34K aaaaaaaaaa) (len < 34K cccccccccc)

234

Developing UniBasic Applications

Linking C Programs (UNIX Only)

After called C function str_arg() The 1st input string The 1st IO string The length of the 2nd IO string The length of the bstring is The 1st 20 char of the 2nd string The 1st 20 char of the bstring is Enter <New line> to continue... >> >> >> >> >> >> THE FIRST INPUT STRING The 1st IO string is changed 30720 30720 len < 34K aaaaaaaaaa len < 34K cccccccccc

Calling a UniBasic Subroutine from a C Program with CallBasic


You can link a C program with UniData so that it executes a UniBasic subroutine by using the UniData CallBasic application programming interface (API). When you use CallBasic, your UniBasic routine is called from a C program that is executed from the operating system prompt. Both UniBasic and UniData are invisible to the user. CallBasic lets you combine the power of C with the advantages offered by UniBasic subroutines. For example, you can write an application in C to access the UniData database, or you can retrofit existing C applications to incorporate UniBasic subroutines to access the UniData database.

Tip
To save time and frustration, read Before You Begin earlier in this chapter.

Procedure Summary
Here is a summary of the steps you must follow to link a C program with UniData so that it will call a UniBasic subroutine: 1. Write, compile, and catalog the UniBasic subroutine. 2. Write the C program. 3. Create a makefile. 4. Compile and link the C program.

Developing UniBasic Applications

235

Chapter 8 - Linking Programs with UniData

5. Execute the C program.

Requirements
You must have the following components to use the CallBasic API: Development environment Your system must have a full software development kit. (A base compiler is not sufficient). You also will need network libraries (for example, TCP/IP) if you are using NFA.

Tip
Consult your host operating system documentation and your hardware vendor if you have questions about your C development environment. C programs You will need to code and compile the C application that calls UniBasic. Function definitions and makefiles When you install UniData, the file callbas.mk is installed in the udthome/work directory. You will use this makefile as a template to build your application with UniData linked into it.

The Procedure
You are now ready to link a C program that will call a UniBasic subroutine.

1. Write, Compile, and Catalog the UniBasic Subroutine


Perform the following steps to create a UniBasic subroutine that will be called from a C program: A. Write the UniBasic subroutine. Write a UniBasic subroutine that includes a RETURN statement. You cannot use a UniBasic mainline program, because the subroutine must return control to the C program.

236

Developing UniBasic Applications

Linking C Programs (UNIX Only)

For example, the following subroutine (EXAMPLE) returns a value in RTNVAL. The remaining arguments, ARG1 and ARG2, pass data from the C program to the EXAMPLE subroutine.

Program Example
SUBROUTINE EXAMPLE(RTNVAL, ARG1, ARG2) PRINT ARG1 PRINT ARG2 RTNVAL = "val1" RETURN END

B. Compile the UniBasic subroutine. Use the ECL command BASIC to compile the UniBasic subroutine. For more information about this command, see Chapter 3 - Creating and Running a Program, or the UniData Commands Reference. In this example, we compile the EXAMPLE subroutine:

Screen Example
:BASIC BP EXAMPLE

C. Catalog the UniBasic subroutine. Use the ECL command CATALOG to catalog the UniBasic subroutine. Here we catalog the EXAMPLE subroutine:

Screen Example
:CATALOG BP EXAMPLE

Developing UniBasic Applications

237

Chapter 8 - Linking Programs with UniData

2. Write the C Program


Write the C program that calls your UniBasic subroutine.

Note
A transaction started in a UniBasic subroutine invoked by a C program can continue into another subroutine called by the same C program:
udtcallbasic_init( ) U_callbas(subroutineA) * this subroutine starts a transaction U_callbas(subroutineB) * this subroutine commits the transaction udtcallbasic_done( )

However, a transaction started in a UniBasic program executed from ECL must complete before the process returns to ECL. Sample Program The following example (mypgm.c) illustrates a C program that calls a UniBasic subroutine:

Program Example
/* call a UniBasic subroutine EXAMPLE from mypgm.c */ #include <stdio.h> #include <setjmp.h> #include "/usr/ud51/include/share.h" #endif main() { /* declare variables */ char arg0[BUFSIZ]; char arg1[BUFSIZ]; char *args[2]; char *rtn; int sts; /* initialize UniData environment */

238

Developing UniBasic Applications

Linking C Programs (UNIX Only)

/* first, set up an error handler */ int jmpret, sat; U_SET_JMP(jmpret, sat); if (!jmpret) { udtcallbasic_done(1); exit(-1); } udtcallbasic_init(0,0); /* assign arguments to be passed to the subroutine */ strcpy(arg0, "1ST ARGUMENT"); strcpy(arg1, "2ND ARGUMENT"); args[0] = arg0; args[1] = arg1; /* call the subroutine */ sts = U_callbas(&rtn, "EXAMPLE", 2, args); if (sts = = 0){ printf("Return: %s\n", rtn); free(rtn); } printf("Status: %d\n", sts); /* shut down UniData environment */ udtcallbasic_done(sts); }

Include Required Functions Three functions must be included in the C program: udtcallbasic_init, which initializes UniData, U_callbas, which calls the UniBasic subroutine, and udtcallbasic_done, which closes files and the UniData session. Required Functions: Start UniData Your C program must execute this function once and only once. The C function udtcallbasic_init initializes the UniData environment, starting a udt process to execute the CallBasic subroutine call.

Developing UniBasic Applications

239

Chapter 8 - Linking Programs with UniData

Syntax: udtcallbasic_init(value1, value2) int value1; int value2;

Note
You must initialize the UniData environment with udtcallbasic_init before calling a UniBasic subroutine.

Warning
You can call udtcallbasic_init only once in your program. If you attempt to call it more than once, your program could cause a core dump.

240

Developing UniBasic Applications

Linking C Programs (UNIX Only)

The following table describes each parameter of the syntax. Parameters value1 Description Names the type of UniData process that will be started. The default is 0. Values for each parameter are in the share.h file in the /usr/ud52/include directory. Valid parameters include the following: 01 U_PHANTOM This parameter behaves like the ECL PHANTOM command. 0100 U_REDIRECT Use this parameter to override the terminal attributes that UniData sets by default; it sends UniData output to a file instead of the terminal screen. 0400 U_UDT_SERVER Sets a flag that, when a fatal error occurs, returns control to the calling UniBasic program rather than aborting (same as UDT.OPTIONS 41). Note - When UDT.OPTION 41 is used, errors are returned in EXECUTE instead of longjmp. value2 Enter one of the following values to clear or not clear the screen when CALLBASIC is initialized: 0 Do not clear the screen. 1 Clear the screen. udtcallbasic_init Arguments Required Functions: Call the UniBasic Subroutine This function calls a UniBasic subroutine, passes arguments, and sets a pointer to the return value. You can execute this function numerous times in your C application, each time you want to call a UniBasic subroutine. Syntax: int U_callbas (rtn, progname, argc, argv)

Developing UniBasic Applications

241

Chapter 8 - Linking Programs with UniData

The following table describes each parameter of the syntax. Parameter rtn Data Type char Description The address of the pointer to the value returned from the UniBasic subroutine; you cannot use this argument to pass anything to the subroutine. The return values include the following: 0 The function executed successfully. -1 A UniBasic program error occurred in the RUN process, you are out of memory, or the system is performing a CHAIN. -2 A fatal error occurred or a STOP was encountered in a UniBasic program. progname argc argv char int char The pointer to the name of the subroutine. The number of arguments passed to the subroutine. The address of an array of pointers to arguments. You cannot use these arguments to pass anything to the subroutine. U_callbas Function Arguments

Note
UniData allocates memory every time you execute U_callbas. In the C program, you must free the storage space pointed to by the first argument or the memory will be unavailable to the system. However, you must free memory only if the function completes successfully.

242

Developing UniBasic Applications

Linking C Programs (UNIX Only)

Required Functions: Close Files and UniData This function clears all UniData-related temporary files and other resources and ends the interface between the C application and UniData. You must execute this function once in your C application. You must also include this function in any error exits in your application that could be taken after udtcallbasic_init. Syntax: udtcallbasic_done(status) int status; status is returned, but it is currently not interpreted.

3. Create a makefile
Perform the following steps to create or modify a makefile to specify to UniData the linked program name and location. A. Copy callbas.mk. Copy the callbas.mk file from udthome/work to your work directory. You will need to make a backup copy of this file in case you need to start this process over. You might want to give the copy a name similar to that of your C function to make it easier to remember later which makefile was used to link which C function. A sample of this file is displayed in File Examples later in this chapter. B. Enter the library path. Change the libpath in your makefile file to your udthome/lib directory, using the absolute path instead of udthome. Here is an example of a libpath line:
libpath = -L/usr/ud52/lib

Also, if you find a line like the following in the file, remove it:
libpath = -L$$UDTLIB

This ensures that the make process will execute regardless of the definition of the environment variable udtlib.

Developing UniBasic Applications

243

Chapter 8 - Linking Programs with UniData

C. Find NEWOBJS. Look for the statement that defines NEWOBJS. It looks like this:
NEWOBJS = callbas.o

Tip
Does your NEWOBJS line differ from this? If it refers to a previously linked C function, you need to reinitialize the files UniData uses to create the link. For more instructions, see If This Is Not the First Time later in this chapter. D. Change callbas.o. NEWOBJS must refer to your object file. Because our sample C program is c_example.c, the object name is c_example.o:
NEWOBJS = c_example.o

E. Enter object name again. Modify the executable name again in a line that looks like this before modification:
callbas: $(NEWOBJS) $OBJS)

To link our sample C program, this line must be:


c_example: $(NEWOBJS) $(OBJS)

4. Compile and Link the C Program


Note
Before you execute make, you need to know if anyone has executed the UniData system-level command makeudt or makeudapi since UniData was installed or upgraded. If they have, you MUST restore the files modified by this utility. For more information, see If This Is Not the First Time later in this chapter.

244

Developing UniBasic Applications

Linking C Programs (UNIX Only)

From the UNIX prompt, enter the make command to compile the C program and link it to UniData. The -f option tells UNIX to use the file specified in makefile_name rather than the default, callbas.mk. make Syntax: make -f makefile_name For example, the following command compiles the C program named c_example.c into an executable named c_example and links the executable to UniData:

Screen Example
%make -f c_example.mk

5. Execute the C Program


Run the C program from the operating system prompt. Syntax: [path]executable_name [arg1] [args2...] If the UniBasic subroutine is globally cataloged, you can execute the C program from any UniData account or directory. If the UniBasic subroutine is locally or directly cataloged, you must execute the C program in a UniData account that has an entry for the program in the VOC file. This is the account from which the make utility was executed or one in which you manually create a VOC record for the C program. You must set udthome, udtbin, and your path. For information about setting these environment variables, see Administering UniData on UNIX.

Developing UniBasic Applications

245

Chapter 8 - Linking Programs with UniData

The following command invokes the program named c_example from the /usr/ud52/work directory:

Screen Example
%/usr/ud52/work/c_example

The sample C program, c_example, which calls the UniBasic program EXAMPLE, produces the following output:

Screen Example
1ST ARGUMENT 2ND ARGUMENT Status: 0 Return: val1

If This Is Not the First Time


Perform the following steps to reinitialize the files in udthome/work if someone has already linked C functions to UniData. You need to perform this procedure before you try to execute the make, makeudt, or makeudapi utility.

1. Find cfuncdef_user
Look in the udthome/work directory for a file named cfuncdef_user. If you do not find it, look for cfuncdef. Here is why: When UniData is installed, the files required for the UNIX make utility to link C programs with UniData are provided in udthome/work. The instructions in the preceding section (for CallBasic) direct the user to copy cfuncdef to cfuncdef_user, then modify this template with information required by the UNIX make utility.

246

Developing UniBasic Applications

Linking C Programs (UNIX Only)

2. Take a Look Inside


Use the UNIX more command to look at the contents. Does the file look like this template?

Program Example
/* this is a test for adding C function to the RUN Machine */ /* comment lines come here. */ /* C function declaration format: function-name:return-type:number-of-argument:arg1,arg2,...,argn */ $$FUN /* beginning of C function */ $$OBJ /* *.o come here */ $$LIB /* library comes here */

3. Look for Modifications


If you have linked one or more C functions to UniData for the purpose of CALLC, cfuncdef or cfuncdef_user will contain references to C functions on the line that follows $$FUN, and object names in the line following $$OBJ. It could also list site-specific libraries under $$LIB. If the file has been modified, you need to reinitialize the files the UNIX make utility uses to create the link between C programs and UniData. (These are the other files placed in udthome/work when UniData is installed: funchead.c, interfunc.c, and so forth.)

4. Log In as root
Before you proceed to the next step, you must log in as root.

5. Reinitialize
Execute the UniData system-level commands genefs, gencdef, and genfunc to update funchead, interfunc, and other C functions used by the makeudt or makeudapi utility.

Developing UniBasic Applications

247

Chapter 8 - Linking Programs with UniData

Enter the following commands, in the indicated order, from the UNIX prompt:

Screen Example
# $UDTBIN/genefs # $UDTBIN/gencdef # $UDTBIN/genfunc # ls -tl |pg total 26 -r--r--r-1 root -r--r--r-1 root -r--r--r-1 root -rw-rw-rw1 root -rw-rw-rw1 root -r--r--r-1 root -r--r--r-1 root -rw-rw-rw1 root -r--r--r-1 root -rw-rw-rw1 root

sys sys sys sys sys sys sys sys sys sys

2735 738 760 97 422 1006 155 985 292 1424

Jun Jun Jun Jun Jun Jun Jun Jun Jun Jun

10 10 10 10 10 10 10 10 10 10

16:08 16:08 16:08 16:08 16:08 16:08 16:04 16:04 16:04 16:04

interfunc.c funchead.c callcf.c ndef cdef efs_init.c efsdef callbas.mk cfuncdef base.mk

Permissions
You must be logged in as root to execute these commands, and you must execute them in the order shown in the example. After you reinitialize, return to the procedure for linking C programs with UniData.

248

Developing UniBasic Applications

Linking C Programs (UNIX Only)

File Examples
The base.mk file and the cfuncdef file are platform-specific.

base.mk Example
Warning
Do not copy the sample makefile onto your system, and do not copy a makefile from another platform. If you do, makeudt or makeudapi will probably fail. Always start with the base.mk file released with UniData. The following example shows a base.mk file for UniData on an HP system:

Screen Example
# pg base.mk # # The Porting Date : Jun. 11, 99 # The System to Be Ported : HPUX10 # CC CFLAGS LDFLAGS OPTFLAGS DBFLAGS libpath addlib addlibpath odslib udlib licnlib = dclcnlib nfalib = dfslib = = cc = -q -z +ESsfc = -Wl,-a,archive = -O +Ovolatile = = -L/disk1/srcman/alpha/ud_51_990611_2025/bin/lib = -lm -Wl,-a,shared -lcurses -lsec = = -lud = -lndbm -lcl -lBSD -llicn = -lnfaclnt

libs_clt = -lshare -ludsql -ludmach -lbasic -lret1 -lperf -lides -lpipe

Developing UniBasic Applications

249

Chapter 8 - Linking Programs with UniData

-lfunc -lndx $(dfslib) -lshm -lmglm -lglm -lulc -lcmn -llicn -ludus -lunix $(nfalib) $(odslib) libs_svr = -lnfasvr -lshare -ludsql -ludmach -lbasic -lret1 -lperf -lides -lpipe -lfunc -lndx $(dfslib) -lshm -lmglm -lglm -lulc -lcmn -llicn \ -ludus -lunix $(odslib) libs_srv = -lushare -lusql -lumach -lbasic -lret1 -lperf -lides -lpipe -lfunc -lndx -lshm -lmglm -lglm -lulc -lcmn -llicn -ludus -lunix -lunirpc $(nfalib) $(odslib) OBJS = funchead.o interfunc.o callcf.o efs_init.o UDOBJS = funchead.o interfunc.o callcf.o efs_init.o udt: $(OBJS) $(CC) $(LDFLAGS) $(OBJS) $(NEWOBJS) $(NEWLIBS) $(libpath) -lapidummy $(libs_clt) $(addlibpath) $(addlib) -o $@

udapi_slave: $(OBJS) $(CC) $(LDFLAGS) $(OBJS) $(NEWOBJS) $(NEWLIBS) $(libpath) -lapidummy -licapi $(libs_clt) -lunirpc $(addlibpath) $(addlib) -o $@ udtsvr: $(OBJS) $(CC) $(LDFLAGS) $(OBJS) $(NEWOBJS) $(NEWLIBS) $(libpath) -lapidummy $(libs_svr) $(addlibpath) $(addlib) -o $@ udsrvd: unirpc_srv.o $(OBJS) $(CC) unirpc_srv.o $(OBJS) $(libpath) -lapidummy $(libs_srv) $(addlibpath) $(addlib) -o $@ uniapisvr: $(OBJS) $(CC) $(LDFLAGS) $(OBJS) $(NEWOBJS) $(NEWLIBS) $(libpath) -lapisvr $(libs_clt) -lmsg $(udlib) $(addlibpath) $(addlib) \

250

Developing UniBasic Applications

Linking C Programs (UNIX Only)

-o $@ .c.o: $(CC) $(CFLAGS) $(IDIR) $(OPTFLAGS) $(DBFLAGS) -c $<

cfuncdef Example
Some earlier releases of UniData included some UniData functions in cfuncdef. If someone upgraded UniData and chose not to overlay this file, the wrong version could reside in udthome/work. The cfuncdef file must look similar to the following example, which contains no reference to C functions:

Screen Example
# pg cfuncdef /* this is a test for adding C function to the RUN Machine */ /* comment lines come here. */ /* C function declaration format: function-name:return-type:number-of-argument:arg1,arg2,...,argn */ $$FUN /* beginning of C function */ $$OBJ /* *.o come here */ $$LIB /* library comes here */

Developing UniBasic Applications

251

Chapter 8 - Linking Programs with UniData

callbas.mk Example
The following is an example of what the callbas.mk template might look like.

Note
Do not copy this example. Use the template provided in your udthome/work directory for the platform on which you are linking C programs with UniData. This example shows link options that might not be valid on some platforms.

Program Example
# # # #

The Porting Date : Dec. 11, 96 The System to Be Ported : HPUX10.debug

CC CFLAGS LDFLAGS OPTFLAGS DBFLAGS libpath addlib addlibpath odslib udlib licnlib dclcnlib nfalib dfslib

= = = = = = = = = = = = = =

cc -q -z +ESsfc -DNULL_OK -DSQLTP -DUDMS -DNEW_INTER -Wl,-a,archive -g -L/disk2/srcman/alpha/ud_pkqa4/bin/lib -lm -Wl,-a,shared -lcurses -lud -lndbm -lcl -lBSD -llicn -lnfaclnt

OBJS NEWOBJS

= funchead.o interfunc.o callcf.o efs_init.o = callbas.o

newlibpath = newlibs = libpath = -L$$UDTLIB

252

Developing UniBasic Applications

Linking C Programs (UNIX Only)

libs

= -lshare -ludsql -ludmach -lbasic -lperf -lret1 -lides -lpipe \ -lfunc -lndx $(dfslib) -lshm -lcmn $(licnlib) -ludus $(nfalib) \ $(odslib) $(udlib)

callbas:$(NEWOBJS) $(OBJS) $(CC) $(LDFLAGS) $(NEWOBJS) $(OBJS) \ $(libpath) -lapidummy $(libs) $(newlibpath) $(newlibs) \ $(addlibpath) $(addlib) -o $@ .c.o: $(CC) $(CFLAGS) $(IDIR) $(OPTFLAGS) $(DBFLAGS) -c $<

bstring Definition File


callc_bstr.h, which is located in /usr/ud52/include, contains the definition of bstring. Include this header in a C function when you need to pass binary data, especially when that data contains /0 (called NULL in C), which is interpreted by C as a line terminator.

Screen Example
typedef struct { char *str; int len; } bstring;

Developing UniBasic Applications

253

Chapter 8 - Linking Programs with UniData

More on make, makeudt, and makeudapi


The utilities make, makeudt, and makeudapi create new UniData executables. However, the makeudt and makeudapi commands perform more operations than the make command performs.

makeudt and makeudapi


Use the makeudt utility to create a new executable udt, or use makeudapi to create a new executable udapi_slave, in udthome. Using one of these utilities forces udt or udapi_slave to be created in udtbin. 1. First, makeudt or makeudapi reads cfuncef and cfuncdef_user to obtain the following information:
...$$FUN /* beginning of C function */ str_arg:int:5:int,string,string,string,bstring $$OBJ /* *.o come here */ str_arg.o $$LIB /* library comes here */ /disk2/ud52/our_lib

< function and argument definitions < C program object file name < site-specific library path, if different from udtlib

2. Using information gained from cfuncdef and cfuncdef_user, makeudt or makeudapi executes the following three functions: genefs Needed for NFA (Network File Access) only. gencdef Creates a definition file (called cdef) for the following UniData functions: callcf.c funchead.c interfunc.c genfunc Uses the definitions in cdef to create the following UniData functions: callcf.c funchead.c interfunc.c

254

Developing UniBasic Applications

Linking C Programs (UNIX Only)

3. Next, makeudt or makeudapi copies the base.mk file to new.mk and writes the C program and library path, which it obtained from cfuncdef_user:
NEWOBJS = str_arg.o NEWLIBS =/disk2/ud52/our_lib

4. UniData overlays new.mk if it already exists.

Note
makeudt or makeudapi always writes the udt or udapi_slave executable to udtbin. If UniData is executing when it tries to overwrite the file, it notifies you. You must then copy the executable file, udt or udapi_slave, into udtbin when all users are signed out of UniData. 5. Finally, makeudt or makeudapi deletes the cdef file.

make
By default, the makeudt or makeudapi utility makes a new UniData executable and replaces udtbin/udt (or, in the case of makeudapi, udtbin/udapi_slave) with the new one. The make utility performs functions similar to makeudt and makeudapi with the following exceptions: The make utility does not automatically execute the three functions genefs, gencdef, and genfunc. The make utility lets you place the udt or udapi_slave executable in any directory. To do so, change the output path for the targets from -o $@ to -o path. For more information, see the UNIX man page for make.

Developing UniBasic Applications

255

Chapter 8 - Linking Programs with UniData

The following example shows a sample base.mk with the output destinations indicated:

Screen Example
# pg base.mk ... udt: $(OBJS) < udt target $(CC) $(LDFLAGS) $(OBJS) $(NEWOBJS) $(NEWLIBS) $(libpath) -lapidummy $(libs_clt) $(addlibpath) $(addlib) -o $@ < change to -o path udapi_slave: $(OBJS) < udapi_slave target $(CC) $(LDFLAGS) $(OBJS) $(NEWOBJS) $(NEWLIBS) $(libpath) -lapidummy -licapi $(libs_clt) -lunirpc $(addlibpath) $(addlib) -o $@ <change to -o path udtsvr: $(OBJS) < udtsvr target $(CC) $(LDFLAGS) $(OBJS) $(NEWOBJS) $(NEWLIBS) $(libpath) -lapidummy $(libs_svr) $(addlibpath) $(addlib) -o $@ <change to -o path udsrvd: unirpc_srv.o $(OBJS) $(CC) unirpc_srv.o $(OBJS) $(libpath) -lapidummy $(libs_srv) $(addlibpath) $(addlib) -o $@ < udsrvd target

<change to -o path

uniapisvr: $(OBJS) < uniapi.svr target $(CC) $(LDFLAGS) $(OBJS) $(NEWOBJS) $(NEWLIBS) $(libpath) -lapisvr $(libs_clt) -lmsg $(udlib) $(addlibpath) $(addlib) \ -o $@ <change to -o path

256

Developing UniBasic Applications

Linking C Programs (UNIX Only)

Troubleshooting CALLC
The following table contains solutions for some common problems encountered when you attempt to link C programs with UniData. Problem makeudt or makeudapi aborts with a message indicating that UniData cannot find a library. For example: /usr/ccs/bin/ld:
Can't find library for -lapidummy *** Error exit code 1

Solution Add the full path to the C program in the cfuncdef_user file that follows $$OBJ. Check the libpath line in your makefile (the one you created by copying base.mk). It should list the absolute path to the UniData libraries. This is stored in the environment variable udtlib. To find out the path for udtlib, at the UNIX prompt, enter env. This is not an error message. The new udt or udapi_slave has been built successfully.

After completing, makeudt displays the message: 'udt' is up to date or makeudapi displays the message:
'udapi_slave' is up to date

makeudt or makeudapi aborts with an error message such as: unsatisfied symbol: str_arg

where str_arg is the object file for your C program.

References to libraries are incorrect. Check the cfuncdef_user file. Enter the full path to the object file for the C program after the line that contains $$OBJ. Tip - To find out where the makeudt or makeudapi utility thinks the libraries are, look in your makefile at the line beginning with libpath = L. If it is correct, it lists the absolute path to udtlib. To find out the path for udtlib, at the UNIX prompt, enter env.

Troubleshooting Tips

Developing UniBasic Applications

257

Chapter 8 - Linking Programs with UniData

Problem When you try to copy your C program to udthome, UNIX returns an error message like the following:
cp: cannot create /disk1/ud52/str_arg.o: Permission denied

Solution Unless the permissions have been changed for the directory udthome since UniData was installed, you must be logged in as root to copy files into this directory.

When you execute the UniBasic program that calls the C program you linked to UniData, a memory leak results.

To prevent a memory leak, you must free the original buffer assignment in the C program before reallocating it. If the string is changed, the length must also be reassigned. For more information, see Passing bstring-Type Data earlier in this chapter. Users are currently executing UniData sessions. (Execute the command LISTUSER from the ECL prompt to determine who those users are.) The new executable has been created, but UniData was unable to overlay the current executable. With all users signed off, copy the udt or udapi_slave executable from udthome/work to udtbin.

makeudt or makeudapi aborts with an error message similar to the following (for makeudapi, replace udt with udapi_slave in this example):
mv: /disk1/ud52/bin/udt: cannot write: Text file busy Execute "mv udt /disk1/ud52/bin/udt" error, errno = 0 The udt make failed.

When you log in as root, UniData cannot find udthome or other files or directories.

The setting for the UNIX environment variable PATH is lost. Reset your path as it is in your .login or .profile file (if you have one), or see your system administrator or the UNIX man pages for instructions on setting PATH. Reset the environment variables udthome and udtbin. For instructions, see Administering UniData on UNIX or the UNIX man pages.

Troubleshooting Tips (continued)

258

Developing UniBasic Applications

Linking C Programs (Windows NT or Windows 2000 Only)

Linking C Programs (Windows NT or Windows 2000 Only)


The information in this section applies to the Windows NT or Windows 2000 operating system only.

Note
For more information about CALLC, see Administering UniData on Windows NT or Windows 2000.

Dynamic Link Libraries (DLLs) and UniData


Both the CALLC implementation and the CallBasic implementation in UniData for Windows NT or Windows 2000 use the Microsoft Windows Dynamic Link Library (DLL) facility. This facility lets separate pieces of code call one another without being permanently bound together. Linking between the separate pieces is accomplished at runtime (rather than compile time) through a Dynamic Link Library (DLL) interface. Both the CALLC interface (for calling external functions from UniData) and the CallBasic interface (calling UniData functions from external code) are implemented as DLLs. For CALLC, developers create a DLL and then call that DLL from UniData. Special VOC entries for each function that is called from a DLL communicate interface information to UniData. For CallBasic, developers link their code with UniData.LIB (located in the UniData bin directory) and then make calls into the UniData DLL. The .LIB file supplies interface information.

Because linking between caller and DLL is accomplished at runtime, either the caller or the DLL can be modified independently. For UniData, this means that you can upgrade your UniData version without the need to relink with external routines, and you can update your external DLL without the need to relink UniData. A DLL is language-independent. Many software development environments for Windows can produce a DLL.

Developing UniBasic Applications

259

Chapter 8 - Linking Programs with UniData

Note
For information about linking code into a DLL, see the documentation for your software development environment.

CALLC Features and Components


CALLC enables users to execute external functions from within a UniBasic application. The external functions can be written in C, C++, or Borland Delphi. This section describes the features of CALLC.

Note
UniData includes a series of CALLC examples, both external functions and UniBasic programs. The examples are in the CALLC_DEMO folder located in the UniData demo account. For more information, see Administering UniData on Windows NT or Windows 2000.

CALLC Syntax and Data Types


The UniBasic CALLC command has the following syntax: rtn = CALLC function(arg1,arg2,...argn) The following table lists the parameters of the syntax. Parameter rtn function arg1,....argn Description Return value from CALLC. Must be a valid data type. The name of the external function being called. Arguments to the external function. Each must be a valid data type. CALLC Parameters

260

Developing UniBasic Applications

Linking C Programs (Windows NT or Windows 2000 Only)

Valid data types for return values and arguments are listed in the following table. Data Type CHAR INT POINTER SHORT LONG STRING CHAR_PTR INT_PTR SHORT_PTR LONG_PTR DESCRIPTOR NONE A signed byte. An integer (32-bit signed). A 32-bit signed long word. A short (16-bit) integer. A long integer. A pointer to a null-terminated character string in a 34K buffer. A pointer to a null-terminated character string. A pointer to a 32-bit signed long word. A pointer to a 16-bit integer. A pointer to a 32-bit integer. A pointer to a string descriptor. Use for functions that do not return anything (for instance, VOID). Data Types for CALLC Description

E Type VOC Entries


An E (executable) type VOC entry identifies the DLL for an external function being called using CALLC, and identifies the data types for its arguments and return value.

Developing UniBasic Applications

261

Chapter 8 - Linking Programs with UniData

The following table defines the attributes required for an E type VOC entry. Attribute @ID Attribute 1 Attribute 2 Description The function name. The VOC entry type. You must specify E. The location of the DLL. It must be a fully qualified path, a path relative to the current working directory, or a name that can be located by way of the users path environment variable. The function name in the DLL. The data type for the return value. The data type for the first argument. The data types for the second through nth arguments. Attributes of E Type VOC Entry The following screen shows the VOC entry for a function named callcpp_subr1:

Attribute 3 Attribute 4 Attribute 5 Attributes 6 - n

Screen Example
:CT VOC callcpp_subr1 VOC: callcpp_subr1: E CALLC_DEMO\CALLC_CPP\callcpp_test.dll callcpp_subr1 INT INT SHORT LONG CHAR STRING

262

Developing UniBasic Applications

Linking C Programs (Windows NT or Windows 2000 Only)

POINTER :

Notice that this function expects six arguments, and returns an INT. The function is accessed from a dynamic linked library called callcpp_test.dll.

Warning
Informix recommends that you keep your development environment clearly separate from your production environment when developing a CALLC application. Separating environments is useful in any case, but can be critical because difficulties in the external functions can terminate udt sessions and potentially damage data.

CALLC and UDT.OPTIONS 88


One function can call another in a stack-based architecture by using one of the following conventions: Pascal calling convention. _cdecl calling convention.

The Pascal calling convention is the default for UniData.

Note
For C and C++, the default calling convention is _cdecl. For Delphi, the default calling convention is Pascal. You can use the Pascal convention in C or C++, and you can use the _cdecl convention in Delphi. For information about choosing a calling convention, see the documentation for your development environment.

Developing UniBasic Applications

263

Chapter 8 - Linking Programs with UniData

A new UDT option has been introduced into UniData for Windows NT or Windows 2000 to let CALLC function correctly with both _cdecl and Pascal calling conventions. The following table describes the behavior of CALLC commands with this option turned on or off. UDT.OPTIONS 88 OFF (default) ON _cdecl Convention CALLC fails, terminating the UDT. CALLC executes. Pascal Convention CALLC executes. CALLC fails, terminating the udt.

UDT.OPTIONS 88

Warning
As the preceding table indicates, calling a function with the wrong UDT.OPTIONS 88 setting almost certainly terminates a udt session and could produce other undesirable results.

Using CALLC
To call an external function from UniBasic, perform the following procedure.

1. Write and Compile the External Function


You can code the external function in C, C++, or Delphi. Guidelines for Writing C Functions You might find the following guidelines helpful when writing external functions in C: Naming Variables Avoid naming variables or functions with the prefix U and an underscore (U_), such as U_unit and U_errout; UniData uses U_ as an identifier for variable names and functions. Passing Arguments You cannot pass more than 22 arguments. Each argument is limited to 255 characters.

264

Developing UniBasic Applications

Linking C Programs (Windows NT or Windows 2000 Only)

Displaying Error Messages To display error messages, use the UniData C function U_errout. U_errout() has the same syntax as U_preprint(), except the variable U_unit is replaced by 0. U_errout() output goes to errout whereas U_preprint() output goes to stdout.

Syntax: U_errout(0,"error message from the routine, value is %d",value); Printing To maintain screen integrity and I/O redirection, use the UniData C function U_preprint instead of the C function printf. The U_preprint function refreshes the screen, enabling the C subroutine to properly manage screen I/O. This function follows syntax similar to printf().

Syntax: U_preprint pattern,arg1,arg2... Ending the C Program Do not use exit(). Instead, use U_done, which performs various cleanup tasks, and then causes the C program to terminate.

Steps for Developing the Function A. Code the function using C, C++, or Borland Delphi. Make certain that all the functions to be called from outside the program are exported in one of the following ways: A _declspec( dllexport ) statement. An EXPORTS statement. The EXPORTS statement lists the names and optionally the ordinal values of the functions exported by the DLL. When ordinal values are specified, they must be in the range 1 through n where n is the number of functions exported by the DLL.

Note
The maximum number of bytes allowed in a function return is 256. B. Compile the function or functions and link the code into a DLL.

Developing UniBasic Applications

265

Chapter 8 - Linking Programs with UniData

Warning
UniData for Windows NT or Windows 2000 takes full advantage of the Win32 environment. The UniData DLL is a 32-bit DLL, and any DLLs you call by way of CALLC must also be 32-bit DLLs. You cannot call a 16-bit DLL from UniData. C. Create the VOC entry for every function that you can call from the DLL. You will need to create an E type record in the VOC file in every UniData account where you will be calling the functions. The VOC entry contains information that enables UniData to locate and execute the called function. After the DLL and the E type VOC entry are created, the function can be accessed from UniBasic via CALLC.

2. Write and Compile the UniBasic Program


The following example shows a portion of the sample UniBasic program that calls an external function named ps:

Program Example
. . . PRINT "TURNING ON UDT.OPTIONS 88; REQUIRED FOR C" PERFORM "UDT.OPTIONS 88 ON" PRINT "THE ID OF MY CURRENT UNIDATA PROCESS IS: ":@USERNO PRINT "PASSING THE ID TO THE C ROUTINE." pid = @USERNO pname = '' cname = '' ptime = '' virt_mem = 0 RESULT = CALLC ps(pid, pname, cname, ptime, virt_mem) PRINT "THE C ROUTINE RETURNED: ":RESULT IF RESULT >= 0 THEN PRINT

266

Developing UniBasic Applications

Linking C Programs (Windows NT or Windows 2000 Only)

. . . END ELSE PRINT "AN ERROR HAS OCCURRED IN THE C ROUTINE." END PRINT "TURNING OFF UDT.OPTIONS 88 BEFORE CLOSING" PERFORM "UDT.OPTIONS 88 OFF" STOP END

Notice the following points: The function name in the CALLC statement matches the name in the E type VOC entry. By default, the calling convention for a C program is the _cdecl convention. Therefore, UDT.OPTIONS 88 must be turned on. Error handling is based on the RESULT from the C function rather than the STATUS of the CALLC statement. The statement can complete successfully (STATUS of 0) even if the C function has encountered an error.

Reminder
The external function examples and UniBasic examples in this section are taken from the sample programs installed with the current release of UniData on Windows NT.

CallBasic Features and Components


You can write programs that execute UniBasic subroutines by using the UniData CallBasic application programming interface (API). When you use CallBasic, your UniBasic routines are called from an external program, and UniBasic and UniData are invisible to the user. CallBasic enables you to combine the power of programming languages such as C, C++, and Delphi with the advantages offered by UniBasic subroutines. For example, you can write an application in C to access the UniData database, or you can retrofit existing applications to incorporate UniBasic subroutines that let you access the database.

Developing UniBasic Applications

267

Chapter 8 - Linking Programs with UniData

Note
UniData includes two CallBasic examples: both the external programs and the UniBasic subroutines. The examples are in the CALLBASIC_DEMO folder located in the UniData demo account. The UniBasic subroutines are in the BP file in the UniData demo account. For more information, see Administering UniData on Windows NT or Windows 2000.

Functions of the CallBasic API


udtcallbasic_init The C function udtcallbasic_init initializes the UniData environment, starting a udt process to execute the CallBasic subroutine call. Syntax: udtcallbasic_init(value1, value2) int value1; int value2;

Note
You must initialize the UniData environment with udtcallbasic_init before calling a UniBasic subroutine.

Warning
You can call udtcallbasic_init only once in your program. If you attempt to call it more than once, your program could cause a core dump.

268

Developing UniBasic Applications

Linking C Programs (Windows NT or Windows 2000 Only)

The following table describes each parameter of the syntax.

Parameters value1

Description Names the type of UniData process that will be started. The default is 0. Valid parameters include the following: 0 or 01 U_PHANTOM This parameter behaves like the ECL PHANTOM command. 010 U_PHANTOMHUSH This parameter behaves like U_PHANTOM, except that it suppresses the Phantom process started and Phantom process completed messages. 0100 U_REDIRECT Use this parameter to override the terminal attributes that UniData sets by default; it sends UniData output to a file instead of the terminal screen. 0400 U_UDT_SERVER Sets a flag that, when a fatal error occurs, returns control to the calling UniBasic program rather than aborting (same as UDT.OPTIONS 41). Note - When UDT.OPTION 41 is used, an error will be returned in EXECUTE instead of longjmp.

value2

Enter one of the following values to clear or not clear the screen when CALLBASIC is initialized: 0 Do not clear the screen 1 Clear the screen udtcallbasic_init Arguments

U_callbas The C function U_callbas calls a UniBasic subroutine, passes arguments, and sets a pointer to the return value. Syntax: int U_callbas (rtn, progname, argc, argv)

Developing UniBasic Applications

269

Chapter 8 - Linking Programs with UniData

The following table describes each parameter of the syntax. Parameter rtn Data Type char Description The address of the buffer containing results from the UniBasic subroutine. You cannot use this argument to pass anything to the subroutine. The return values include the following: 0 The function executed successfully. -1 A UniBasic program error occurred in the RUN process, you are out of memory, or the system is performing a CHAIN. -2 A fatal error occurred or a STOP was encountered in a UniBasic program. progname argc argv char int char The pointer to the name of the subroutine. The number of arguments passed to the subroutine. The address of an array of pointers to arguments. You cannot use these arguments to pass anything to the subroutine. U_callbas Function Arguments

Note
UniData allocates memory from the process heap every time you execute U_callbas. In the C program, you must free the memory pointed to by the first argument or the memory will be unavailable to the system. You must use the HeapFree API call to free the memory. However, you must free memory only if the function completes successfully.

270

Developing UniBasic Applications

Linking C Programs (Windows NT or Windows 2000 Only)

udtcallbasic This function calls a UniBasic subroutine, passing arguments, and returns a pointer to the results. Syntax: int udtcallbasic(rtn, progname, argc, arg[0], ..., arg[n]) The syntax of this function is required if the calling language is not C because the definition of the return buffer is consistent between the external program and the call with udtcallbasic. The user is responsible for allocating memory for the buffer to store results. You can execute this function numerous times in your application when you want to call a UniBasic subroutine. The following table describes each parameter of the syntax. Parameter rtn Data Type char Description The address of the pointer to the value returned from the UniBasic subroutine. You cannot use this argument to pass anything to the subroutine. The return values include the following: 0 The function executed successfully. -1 A UniBasic program error occurred in the RUN process, you are out of memory, or the system is performing a CHAIN. -2 A fatal error occurred or a STOP was encountered in a UniBasic program. progname argc arg[0] ... arg[n] char int char The pointer to the name of the subroutine. The number of arguments passed to the subroutine. The pointers to arguments passed to the subroutine.

U_callbas Function Arguments

Developing UniBasic Applications

271

Chapter 8 - Linking Programs with UniData

udtcallbasic_done The UniData udtcallbasic_done C function clears all UniData-related temporary files and space, and shuts down the UniData environment. Syntax: udtcallbasic_done(status) int status; status is returned, but it currently is not interpreted.

Using CallBasic
You must have the following components to use the CallBasic API: Development environment Your system should have a full software development kit. (A base compiler is not sufficient.) UniData.LIB file You must link your external program with the UniData.LIB file when you build the executable. Interface information is contained in UniData.LIB.

Tip
Consult your host operating system documentation and your hardware vendor if you have questions about your development environment.

272

Developing UniBasic Applications

Linking C Programs (Windows NT or Windows 2000 Only)

1. Write, Compile, and Catalog the UniBasic Subroutine


Perform the following steps to create a UniBasic subroutine that will be called from an external program: A. Write the UniBasic subroutine. Write a UniBasic subroutine that includes a RETURN statement. The subroutine must return control to the external program. For example, the following subroutine, EXAMPLE, returns a value in RTNVAL. The remaining arguments, ARG1 and ARG2, pass data from the C program to the EXAMPLE subroutine.

Screen Example
SUBROUTINE EXAMPLE(RETNVAL,ARG1,ARG2) PRINT "THE FIRST ARG IS ":ARG1 PRINT "THE SECOND ARG IS ":ARG2 RETNVAL="RETURN" RETURN END

Note
This subroutine is included in the BP file of the UniData demo account. B. Compile the UniBasic subroutine. To compile the UniBasic subroutine, use the ECL command BASIC. For more information about this command, see Chapter 3 - Creating and Running a Program, or the UniData Commands Reference.

Developing UniBasic Applications

273

Chapter 8 - Linking Programs with UniData

In the following example, we compile the EXAMPLE subroutine:

Screen Example
:WHERE D:\UniData\demo :BASIC BP EXAMPLE Compiling Unibasic: BP\EXAMPLE in mode 'u'. compilation finished :

C. Catalog the UniBasic subroutine. To catalog the UniBasic subroutine, use the ECL command CATALOG. Depending if you want to access the subroutine from one account or many, you can catalog it directly, locally, or globally. In the following example, we demonstrate globally cataloging the EXAMPLE subroutine:

Screen Example
:CATALOG BP EXAMPLE D:\UniData\sys\CTLG\e\EXAMPLE has been cataloged, do you want to overwrite?(y/n)Y :

2. Code, Compile, and Link the External Program


For information about compiling the external program, see the documentation for your application development environment. Make certain that: Your declarations for the UniBasic functions use the correct syntax for your programming language. You link your code with the UniData.LIB file.

274

Developing UniBasic Applications

Linking C Programs (Windows NT or Windows 2000 Only)

Write the external program that calls your UniBasic subroutine. Use the CallBasic functions in the program as follows: udtcallbasic_init() Your program must execute this function once and only once. U_callbas() You can execute this function numerous times in your program, each time you want to call a UniBasic subroutine. The syntax for U_callbas is supported if the calling language is C. udtcallbasic() Remember to allocate memory for the buffer to store results. You can execute this function numerous times in your program, each time you want to call a UniBasic subroutine. udtcallbasic_done() Your program must execute this function once. You must also include this function in any error exits in your application that could be taken after udtcallbasic_init().

The following example (callbasic_example1.c) shows a program that calls a UniBasic subroutine:

Program Example
#include <stdio.h> #define NT #ifdef NT #include <windows.h> #endif /* NT */ /* Declare UniData callbasic functions */ #ifdef CPP /* for c++ */ extern "C" int udtcallbasic_init(int i, int j); extern "C" int udtcallbasic(char *xbuf, char *ybuf, int i, char *arg, ...); extern "C" int udtcallbasic_done(int k); extern "C" int U_callbas(char **xbuf, char *ybuf, int i, char **zbuf); #else /* for c */ extern int udtcallbasic_init(); extern int udtcallbasic_done(); extern int U_callbas(); #endif /* CPP */

Developing UniBasic Applications

275

Chapter 8 - Linking Programs with UniData

#ifdef NT extern int udtcallbasic(); #endif /* NT */ void main() { /* Declare variables */ char *rtn; char arg0[BUFSIZ]; char arg1[BUFSIZ]; char *args[2]; int sts; /* Initialize the UniData environment */ udtcallbasic_init(0,0); /* Assign arguments for the UniBasic routine */ strcpy(arg0, "Plants"); strcpy(arg1, "Animals"); args[0] = arg0; args[1] = arg1; printf("Executing UniBasic subroutine using U_callbas()...\n"); /* Call the UniBasic routine */ sts = U_callbas(&rtn, "EXAMPLE", 2, args); if (sts == 0){ printf("Return value from UniBasic subroutine is %s\n", rtn); #ifdef NT /* Variable rtn returned by UniData come from the process heap, not the C-Runtime They must be free'd with HeapFree(). */ HeapFree(GetProcessHeap(), 0, rtn); #else free(rtn); #endif /* NT */ } #ifdef NT /* Allocate memory for return variable */ rtn = (char *)malloc(256); printf("\nExecuting UniBasic subroutine using udtcallbasic()...\n");

276

Developing UniBasic Applications

Linking C Programs (Windows NT or Windows 2000 Only)

/* Call the UniBasic subroutine using udtcallbasic. */ sts = udtcallbasic(rtn, "EXAMPLE", 2, args[0], args[1]); if (sts == 0){ printf("Return value from UniBasic subroutine is %s\n", rtn); } free(rtn); #endif /* NT */ /* Close everything properly */ udtcallbasic_done(sts); }

The following segment from the makefile for callbasic_example1.c shows linking with the UniData.LIB file:

Program Example
LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib a\ dvapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib\ /nologo /subsystem:console /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi\ 32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib c:\u\ nidata\bin\unidata.lib /nologo /subsystem:console /machine:I386 LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\ odbccp32.lib c:\unidata\bin\unidata.lib /nologo /subsystem:console\ /incremental:no /pdb:"$(OUTDIR)/callbasic_example1.pdb" /machine:I386\ /out:"$(OUTDIR)/callbasic_example1.exe" :

Reminder
The sample program and makefile are included in the CALLBASIC_DEMO folder in your UniData demo account.

Developing UniBasic Applications

277

Chapter 8 - Linking Programs with UniData

3. Use the New Executable


To run the new executable and call the UniBasic subroutine, your working directory must be a UniData account where the subroutine is cataloged. You can execute your routine from the MS-DOS Command prompt, and you will need to be sure to specify the full path name of the executable, or include its location in your PATH. The following screen shows the results of executing the callbasic_example1 executable from the demo directory:

Screen Example
:!callbasic_example1 UniData is running under a temporary license! This license will expire in 6 days. Executing UniBasic subroutine using U_callbas()... THE FIRST ARG IS Plants THE SECOND ARG IS Animals Return value from UniBasic subroutine is RETURN Executing UniBasic subroutine using udtcallbasic()... THE FIRST ARG IS Plants THE SECOND ARG IS Animals Return value from UniBasic subroutine is RETURN :

Reminder
If your UniBasic subroutine is globally cataloged, you can use CallBasic from any UniData account. You do not need to be in the UniData account where the subroutine was written.

278

Developing UniBasic Applications

Chapter 9 - UniBasic Transaction Processing

Note
This chapter applies to UniData for UNIX systems only. UniData for Windows NT or Windows 2000 does not support transaction processing. Transaction processing (TP) combines a set of operations in a single logical function so that the database is maintained in a consistent state throughout an update, even in the event of a system failure. Two UniData products support transaction processing: the Recoverable File System (RFS) and Journaling. One of these products must be in use for transaction processing to work. In the absence of RFS or Journaling, UniData ignores TP commands.

Note
RFS and Journaling are purchased separately from the bundled UniData RDBMS. TP works only with recoverable files or files associated with Journaling. For more information about these products, see Using Journaling and Administering the Recoverable File System. To learn more about creating new recoverable files or converting existing files, see Administering the Recoverable File System. To create a UniBasic transaction, you bind the operations by TRANSACTION START and TRANSACTION COMMIT statements. If the transaction is unsuccessful, none of the operations within the transaction take place. After the transaction is initiated, UniData can recover the updated files.

Developing UniBasic Applications

279

Chapter 9 - UniBasic Transaction Processing

This chapter introduces the UniBasic transaction processing commands. Appendix B - UniBasic Transaction Processing Concepts explains the basic concepts on which UniData transaction processing is based. For information about using UniData locks to ensure database consistency, see Chapter 5 - Using UniData Locks.

280

Developing UniBasic Applications

In This Chapter

In This Chapter
This chapter consists of the following sections: Transaction Processing Commands Creating or Converting to a Recoverable File Transaction Processing Programming Problems

Developing UniBasic Applications

281

Chapter 9 - UniBasic Transaction Processing

Transaction Processing Commands


The following UniBasic commands and the variable @TRANSACTION let you create UniBasic programs with transaction processing safeguards: TRANSACTION START TRANSACTION COMMIT TRANSACTION ABORT @TRANSACTION

Note
Transaction processing works only with recoverable files or the UniData Journaling product. To learn more about creating new recoverable files or converting existing files, see the Administering the Recoverable File System.

Warning
When including WRITE statements within a transaction, you must code an ON ERROR clause that takes appropriate action to notify the user and stop the transaction. If the transaction is not aborted by the ON ERROR clause, processing continues, and the transaction commits inappropriately.

Executing TP and Non-TP Transactions


Informix recommends that you NOT execute at the same time applications that perform transactions and those that do not. If both types of applications make updates to the same UniData file at the same time, indexes based on virtual fields might not be updated, and the only notification you will receive will be a warning recorded in the sm.log file.

282

Developing UniBasic Applications

Transaction Processing Commands

The following demonstrates a scenario that results in indexes based on virtual fields not being updated because TP and non-TP applications are running simultaneously: 1. The TP application initiates a transaction by executing the UniBasic TRANSACTION START command or by executing any UniData SQL transaction-initiating command. 2. The TP transaction writes a new record to a recoverable file. (The write is held pending the transaction commit.) 3. The non-TP application executes a write, creating a record with the same @ID as the one created in Step 2. 4. When the TP transaction commits, UniData discovers that the record now exists, so it changes its insert operation to an update. However, virtual fields are resolved and their index entries are created when a write is executed within a transaction, not when the transaction is committed. Because the operation has become an update rather than insert, no new alternate key index entry is made for the new record.

Starting a Transaction
Use the UniBasic TRANSACTION START command to initiate a transaction. Syntax: TRANSACTION START {THEN statement(s) | ELSE statement(s)} The UniBasic TRANSACTION START command initiates a new transaction and temporarily stores all updates until a the transaction is committed or aborted. You must specify a THEN or an ELSE clause, but can specify both.

Note
UniBasic does not support nested transactions. When UniData encounters a nested transaction, the ELSE clause executes.

Developing UniBasic Applications

283

Chapter 9 - UniBasic Transaction Processing

STATUS Function Values


After you execute TRANSACTION START, the STATUS function returns one of the values described in the following table. Value 0 1 errno Description Successful transaction start. Indicates a nested start. Indicates an error message. STATUS Values For information about the UniBasic STATUS function, see the UniBasic Commands Reference.

Example
In the following example, which is taken from the sample program in Appendix B - UniBasic Transaction Processing Concepts, the TRANSACTION START command starts the transaction process:

Program Example
TRANSACTION START ELSE IF STATUS() = 1 THEN DISPLAY "A Transaction had already been started, NESTED Transactions" DISPLAY "are NOT Allowed. (Contact System Administrator)" INPUT PAUSE,1_ END ELSE DISPLAY "The Recoverable File System is not enabled." END END

284

Developing UniBasic Applications

Transaction Processing Commands

Committing a Transaction
Use the UniBasic TRANSACTION COMMIT command to commit a transaction. Syntax: TRANSACTION COMMIT {THEN statement(s) | ELSE statement(s)} The UniBasic TRANSACTION COMMIT command concludes the active transaction. UniData writes all pending updates to the appropriate files. You must specify a THEN or an ELSE clause, or both. If a TRANSACTION COMMIT statement executes in the absence of an active transaction, UniData executes the ELSE clause.

Successful Commit
UniData performs the following steps during a transaction commit: 1. Disables the break key. 2. Writes all updates. 3. Releases all records locked by this process. 4. Executes the THEN clause, if included. 5. Enables the break key.

Unsuccessful Commit
If the transaction cannot commit and the ELSE clause executes, UniData performs the following steps: 1. Aborts the transaction without writing records. 2. Releases all records locked inside the transaction.

Developing UniBasic Applications

285

Chapter 9 - UniBasic Transaction Processing

STATUS Function Values


After you execute TRANSACTION COMMIT, the STATUS function returns one of the values described in the following table. Value 0 1 3 Description Transaction committed. Transaction not started. Transaction cannot commit. STATUS Values

Example
In the following example, taken from the sample program in Appendix B - UniBasic Transaction Processing Concepts, the TRANSACTION COMMIT command ends the transaction process and writes the new record to the database:

Program Example
TRANSACTION COMMIT IF STATUS() = 1 DISPLAY "The END ELSE DISPLAY "The END ELSE THEN TRANSACTION was not started" TRANSACTION could not be committed."

286

Developing UniBasic Applications

Transaction Processing Commands

Aborting a Transaction
Use the UniBasic TRANSACTION ABORT command to abort active transactions. Syntax: TRANSACTION ABORT The UniBasic TRANSACTION ABORT command cancels the active transaction, discarding the pending writes. As a result, none of the updates associated with the transaction occur. In addition to encountering a TRANSACTION ABORT statement, a transaction could abort due to any of several conditions, including: A program finishes before a transaction commit is issued (STOP or END). The program CHAINs to another program. An error terminates the program before a transaction commit is issued. A user breaks out of the program before a transaction commit is issued. This can be prevented programmatically by disabling the interrupt key during the transaction by executing the ECL command PTERM -BREAK OFF. The user running the program is logged out, or the process is killed before a transaction commit is issued.

The system handles these abort conditions in the same way it does TRANSACTION ABORT.

Note
You should be aware of these various abort conditions and control the resulting action from within the program where possible and appropriate. For example, if no ON ERROR clause is included in a write statement and the write fails, the program aborts, terminating the transaction.

Developing UniBasic Applications

287

Chapter 9 - UniBasic Transaction Processing

Example
In the following example, the transaction process aborts if var is 10:

Program Example
TRANSACTION START THEN PRINT "Transaction started." ELSE PRINT "Transaction start failed, STATUS = ":STATUS(); STOP READU var FROM file.var, record1 var += 2 IF var = 10 THEN TRANSACTION ABORT; GOTO ERR: WRITE var TO file.var, record1 TRANSACTION COMMIT THEN PRINT "Transaction committed." ELSE PRINT "Transaction Aborted, STATUS = ":STATUS(); STOP

Testing for an Active Transaction


The variable @TRANSACTION lets you test for an active transaction. The following table describes the possible values for @TRANSACTION. Value 1 0 Description A transaction is active. A transaction is not active. @TRANSACTION Values

288

Developing UniBasic Applications

Transaction Processing Commands

Transaction Processing Programming Example


The following program segment is taken from the sample program in Appendix B - UniBasic Transaction Processing Concepts. Note that the WRITE commands are enclosed in a transaction, and that both or neither are committed.

Program Example
WRITE_RECORD: * The record(s) have been updated. Make sure that if the RECOVERABLE * FILE System is operational that either BOTH records are updated, or that * None are (using Transaction processing commands). TRANSACTION START ELSE IF STATUS() = 1 THEN DISPLAY "A Transaction had already been started, NESTED Transactions" DISPLAY "are NOT Allowed. (Contact System Administrator)" INPUT PAUSE,1_ END ELSE DISPLAY "The Recoverable File System is not enabled." END END WRITE CLIENT.REC ON CLIENT_FILE,CLIENT_NUMBER WRITE ORDER.REC ON ORDERS_FILE,ORDER_NUMBER TRANSACTION COMMIT IF STATUS() = 1 DISPLAY "The END ELSE DISPLAY "The END END RETURN ELSE THEN TRANSACTION was not started" TRANSACTION could not be committed."

Developing UniBasic Applications

289

Chapter 9 - UniBasic Transaction Processing

Creating or Converting to a Recoverable File


You can make a file recoverable when you create it, or you can convert a nonrecoverable file to be recoverable. UniData supports recovery of the following file types: DATA DICT MULTIFILE DYNAMIC

Note
The recoverable file system does not protect DIR and MULTIDIR type files. You cannot specify the DIR or MULTIDIR options with the recoverable options.

Warning
The operating systemlevel acctrestore command, which restores accounts in CPIO format, does not work with the UniData Recoverable File System (RFS).

Creating a Recoverable File


Use the ECL CREATE.FILE command with the RECOVERABLE option to create a file for use with RFS. For more information about RFS, see Administering the Recoverable File System. Syntax: CREATE.FILE [DICT | DATA] [DIR | MULTIFILE | MULTIDIR] filename [,subfile] [modulo [,block.size.multiplier]] [TYPE hashtype] [DYNAMIC [KEYONLY | KEYDATA] [PARTTBL part_tbl]] [RECOVERABLE] [OVERFLOW] For more information about the ECL CREATE.FILE command, see the UniData Commands Reference.

290

Developing UniBasic Applications

Creating or Converting to a Recoverable File

Example
In the following example, the CREATE.FILE command creates the recoverable file MASTER, with a modulo of 4. Because 4 is not a prime number, UniData changes the modulo to the next highest prime number, which is 5:

Screen Example
:CREATE.FILE MASTER 4 RECOVERABLE 4 is not a prime number, modulo changed to 5. Create file D_MASTER, modulo/1,blocksize/1024 Hash type = 0 Create file MASTER, modulo/5,blocksize/1024 Hash type = 0 Added "@ID", the default record for UniData to DICT MASTER.

Converting to a Recoverable File


To convert an existing file to a recoverable file, use the UniData udfile command. To learn more about creating new recoverable files or converting existing files, see Administering UniData on UNIX. Syntax: udfile [-r | -s] file.name

Permissions
The UniData operating systemlevel udfile command converts a nonrecoverable UniData file to a recoverable file or a recoverable file to a nonrecoverable UniData file. You must have root privileges to convert a file using this command. If you do not specify an option,UniData returns the type of file (that is, recoverable or nonrecoverable). You cannot convert a file using udfile while UniData is running.

Developing UniBasic Applications

291

Chapter 9 - UniBasic Transaction Processing

Parameters
The following table describes the valid udfile options. Option -r -s Description Converts a nonrecoverable file to a recoverable file. Converts a recoverable file to a nonrecoverable file. udfile Options

Note
The udfile command does not convert files that were created in one-half kilobyte blocks. If you try to do so, UniData issues an error message indicating that the file cannot be converted. You must resize the file to at least a 1K block size, using the ECL RESIZE or system-level memresize command. For information about ECL commands, see the UniData Commands Reference.

292

Developing UniBasic Applications

Transaction Processing Programming Problems

Transaction Processing Programming Problems


Programming problems could, in some cases, degrade performance and, in other cases, cause UniData to abort transactions. This section points out some of these problems and suggests ways of overcoming them. Many problems are caused or compounded by improper use of UniData locks. For more information, see Chapter 5 - Using UniData Locks.

Transaction Abort
The following problems cause UniData to abort transactions: Deadlocks When two or more programs are waiting for each other to release records, UniData aborts one of the transactions to unlock its records and remove the deadlock. You can reduce the likelihood of deadlocks occurring by imposing a protocol on the order data items can be updated. For example, the protocol might let programs lock record B only after locking record A. If all programs on the system follow this protocol, deadlocks will be avoided. Upgrading Locks If two users share a lock on a record, and one or both tries to upgrade to a U lock, one user receives the upgraded lock, and UniData aborts the other transaction. Execute the LOCKED clause in the locking statement to abort the transaction, but not the program. Nested Transactions UniData does not support nested transactions. A TRANSACTION START within another active transaction executes the ELSE clause, if specified, and aborts the transaction if none is specified. Use the @TRANSACTION variable to find out if a transaction is active before calling an external subroutine. For more information about @TRANSACTION, see Testing for an Active Transaction earlier in this chapter.

Developing UniBasic Applications

293

Chapter 9 - UniBasic Transaction Processing

Called Programs and Subroutines If a you start a transaction within a called program or subroutine, and the subroutine stops abnormally, UniData could return control to the calling program with the transaction still active. To avoid this, include the ON.ABORT clause in the subroutine to stop program execution in the event of a problem affecting a subroutine.

Transaction Processing Limitations UniData does not support transaction processing outside of UniBasic and UniData SQL. Although you can include EXECUTE statements within a transaction, you cannot execute any of the following types of operations with the EXECUTE: UniData SQL (use EXECUTESQL) UEntry/UReport File-level operations on recoverable files (for example, the UniBasic CLEARFILE commands and the following ECL commands: CLEAR.FILE, DELETE.FILE, RESIZE, CREATE.FILE, CREATE.INDEX, BUILD.INDEX, DELETE.INDEX, CNAME).

UDT.OPTIONS
If UDT.OPTION 35 is on when you issue an EXECUTE statement, UniData creates a new user and processes it independently from the original UniBasic program, causing unpredictable results in TP. Make sure UDT.OPTION 35 is off when using TP.

294

Developing UniBasic Applications

Transaction Processing Programming Problems

Examples
Recovering from a deadlock In this example, a deadlock results because two programs are waiting for each other to release the same record (steps 3 and 4). UniBasic aborts Transaction 2 to break the deadlock when this transaction tries to upgrade to an exclusive lock. However, the LOCKED clause (LOCKED TRANSACTION ABORT) in the RECORDLOCKU statement tells UniBasic to abort the transaction only, thus preventing the program from aborting. Step 1 Transaction 1
TRAN1: TRANSACTION START THEN PRINT "Transaction started." READL var1 FROM file.var, record1 ELSE PRINT "File not found."

Transaction 2
TRAN2: TRANSACTION START THEN PRINT "Transaction started."

2 3
RECORDLOCKU var1 LOCKED TRANSACTION ABORT; GOSUB TRAN1 var += 2 WRITE var TO file.var, record1 TRANSACTION COMMIT THEN PRINT "Transaction committed."

READL var1 FROM file.var, record1 ELSE PRINT "File not found."

RECORDLOCKU var1 LOCKED TRANSACTION ABORT; GOTO TRAN2 var += 2 WRITE var TO file.var, record1 TRANSACTION COMMIT THEN PRINT "Transaction committed."

Avoiding Program Abort on Deadlock

Developing UniBasic Applications

295

Chapter 9 - UniBasic Transaction Processing

Nested transactions The next three examples demonstrate creating and avoiding nested transactions. In this first program segment, the main routine starts a transaction, and then calls a subroutine that contains a TRANSACTION START. This always results in an aborted transaction because transactions cannot be nested.

Program Example
TRANSACTION START ELSE PRINT "Transaction error.";STOP READU var FROM file.var, record1 ELSE PRINT "File not found." IF var = "" THEN CALL SUBRZ WRITE var TO file.var, record1 TRANSACTION COMMIT ELSE PRINT "Transaction aborted." SUBROUTINE SUBRZ TRANSACTION START ELSE PRINT "Transaction error.";STOP READU var1 FROM file.var, record2 ELSE PRINT "File not found." var1 += 2 WRITE var1 TO file.var, record2 TRANSACTION COMMIT ELSE PRINT "Transaction aborted."

The next program segment avoids the problem demonstrated in the previous example. The subroutine tests @TRANSACTION for an active transaction before starting its own transaction.

Program Example
TRANSACTION START ELSE PRINT "Transaction error.";STOP READU var FROM file.var, record1 IF var = "" THEN CALL SUBRZ WRITE var TO file.var, record1 TRANSACTION COMMIT ELSE PRINT "Transaction aborted." SUBROUTINE SUBRZ IF @TRANSACTION = 1 THEN RETURN TRANSACTION START ELSE PRINT "Transaction error.";STOP READU var1 FROM file.var, record2 var1 += 2 WRITE var1 TO file.var, record2 TRANSACTION COMMIT ELSE PRINT "Transaction aborted."

296

Developing UniBasic Applications

Transaction Processing Programming Problems

In the following program, the subroutine tests for an active transaction with @TRANSACTION before an EXECUTE statement:

Program Example
TRANSACTION START ELSE PRINT "Transaction not started."; STOP READU var FROM file.var, record1 IF var = "" THEN CALL SUBRZ WRITE var TO file.var, record1 TRANSACTION COMMIT ELSE PRINT "Transaction aborted." SUBROUTINE SUBRZ IF @TRANSACTION = 0 THEN EXECUTE "CLEAR.FILE file.var" ELSE RETURN RETURN

Degraded Performance
This section discussed causes and solutions of degraded performance. Locked Records If you lock records within a transaction, those records remain locked until you commit or abort the transaction. If your program performs a number of operations, especially requesting user input, you increase the waiting period for other users trying to access these records. To avoid this problem, put input statements before the TRANSACTION START. If you must include requests for user interaction within a transaction, include the WAITING clause in the INPUT statement to time out if the user does not respond in a reasonable amount of time. Long Transactions Performing a number of operations inside a transaction also contributes to performance degradation. By minimizing the number of operations in the transaction, you can improve performance.

Developing UniBasic Applications

297

Chapter 9 - UniBasic Transaction Processing

Note
Protecting data by locking records and using transaction processing can contribute to reduced efficiency and system performance.

Example
In the following example, the INPUT times out after five minutes if the user does not respond:

Program Example
READU var FROM file.var, record1 ELSE PRINT "File not found." INPUT var WAITING 300 TRANSACTION START ELSE PRINT "Transaction not started." STOP WRITE var TO file.var, record1 TRANSACTION COMMIT ELSE PRINT "Transaction aborted."

298

Developing UniBasic Applications

Chapter 10 - Null Value Handling

This chapter introduces the null value handling in UniBasic. UniData supports null value handling on both UNIX and Windows NT or Windows 2000 systems. You can turn on null value handling, which makes the UniData RDBMS more compliant with the standards defined by ANSI SQL 92. Compliance improves compatibility with client and desktop tools.

Developing UniBasic Applications

299

Chapter 10 - Null Value Handling

Representing Unknown Values


If you turn null value handling on, and you have accepted the default language group when installing UniData, the null value is represented by the ASCII character 129. If you change language groups, a different character could be assigned to represent the null value. For this reason, Informix recommends that you use the @NULL variable to represent the null value in UniBasic.

Tip
To determine which ASCII value represents the null value on your system, use the UniBasic command SEQ(@NULL).

Turning Null Value Handling Off


When you install UniData, null value handling is turned off by default (the udtconfig parameter NULL_FLAG = 0). With null value handling turned off, an empty string represents unknown or missing values, and UniBasic programs that contain the variable @NULL will not compile. If you try to execute object code containing @NULL, the program terminates with a fatal error. After turning null value handling on, Informix recommends that you not turn it off, as the null character could have been introduced to your data. If you must turn null value handling off, be sure to check your data and convert the null value to another string (using a UniBasic program or virtual attribute) before trying to execute queries, virtual attributes, or UniBasic programs. You will also need to regenerate any indexes that could contain null values; if you do not, records with null alternate key values cannot be retrieved.

300

Developing UniBasic Applications

Null Value Handling in UniBasic

Null Value Handling in UniBasic


The presence of the null value in data or function/command arguments profoundly affects the results of UniBasic commands, functions, and arithmetic operators. These effects differ according to whether null value handling is turned on or off, and range from UniBasic ignoring the character to generating a runtime error.

Printing and Displaying


The ASCII character that represents the null value is nonprinting, and the environment variable NVLMARK, which specifies an alternative character to represent the null value for display or printing in UniData and UniQuery, has no effect in UniBasic. Although no comparable function exists in UniBasic, you can use the UniBasic conversion commands, such as SWAP and CONVERT, or the UniData SQL command NVL to convert the null value to another string before printing.

Sorting and Indexing


Null Value Handling On The null character is always sorted as the lowest value, lower than all negative numbers.

Tip
Use the UniBasic command SETINDEX with the rop operator NULL_VAL_ALT_KEY to set the alternate key index pointer to the null value (the beginning of the index). The rop operator FIRST_ALT_KEY sets the pointer to the first non-null key. Null Value Handling off The character that would represent the null value is sorted normally, by ASCII value.

Developing UniBasic Applications

301

Chapter 10 - Null Value Handling

Summary of Effects on Commands and Functions


The following table summarizes the effects of the null value in data and function or command arguments. Details are provided in the sections that follow. Type of Operation Numeric calculations Null Value Handling On Result is the null value. Null Value Handling Off Original value is returned. UniBasic returns the message Non-numeric found when numeric required. No special handling of the null value. Follow the ANSI SQL 2way logic rules. No special handling of the null value; invalid for format code. No special handling of the null value. In a parameter, if numeric is required, generates runtime error. No special handling of the null value. Sorting is based on ASCII values.

Relational operations Conversions String functions

Follow the ANSI SQL 3-way logic rules. Result is the null value; invalid for format code. No special handling of the null value. In a parameter, if numeric is required, displays a warning message and uses 0. Null value is lowest (smaller than all negative numbers).

Sorting and indexing

Null Value Handling Summary

302

Developing UniBasic Applications

Null Value Handling in UniBasic

The Null Value in Numeric Functions


The presence of the null character in data affects numeric functions whether nulls are turned on or off. The following table lists the UniBasic numeric operators and functions. Numeric Operators and Functions + ^ ATAN BITXOR INT POWER SCMP SQRT ABS BITAND COS LN PWR SDIV SSUB * ACOS BITNOT DROUND MOD RND SIN TAN / ASIN BITOR EXP NEG SADD SMUL

Numeric Operators and Functions Affected by the Null Value

Null Value Handling On


In Data The result of any numeric function on data containing the null value is the null value. In Arguments Any time UniBasic encounters a null value as a command or function argument when a number is required, UniBasic prints a warning message and uses 0 as the function argument.

Reminder
When a UniBasic numeric function encounters a non-null, nonnumeric character, UniBasic issues a warning message: Non-numeric found when numeric required; the command or function processes, using 0 in place of the invalid character.

Developing UniBasic Applications

303

Chapter 10 - Null Value Handling

Examples The following program demonstrates the principle that any arithmetic operation on data that contains the null value results in the null value:

Program Example
A = 100 + @NULL GOSUB PRINTNULL PRINT "100 + @NULL = ":A A = 0 A = SIN(@NULL) GOSUB PRINTNULL PRINT "SIN(@NULL) = ":A A = 0 A = 99/@NULL GOSUB PRINTNULL PRINT "99/@NULL = ":A A = 0 A = 33/99 GOSUB PRINTNULL PRINT "33/99 = ":A STOP PRINTNULL: IF ISNV(A) THEN SWAP @NULL WITH "null value" IN A RETURN

304

Developing UniBasic Applications

Null Value Handling in UniBasic

The preceding program prints the following:

Screen Example
100 + @NULL = null value SIN(@NULL) = null value 99/@NULL = null value 33/99 = 0.3333

Null Value Handling Off


The null value is invalid. When a numeric function encounters the null value as data or an argument, UniBasic displays a warning message and uses 0.

Note
UniBasic programs containing @NULL will not compile when null value handling is turned off.

The Null Value in Conditional Tests


The presence of the null value in data affects conditional tests only if null value handling is turned on; then it is evaluated to false (0). You perform conditional tests in UniBasic with the following statements: IF/THEN/ELSE CASE

Within these statements, you can nest additional conditions using the following keywords: WHILE UNTIL

Developing UniBasic Applications

305

Chapter 10 - Null Value Handling

Examples
The following examples demonstrate the effect of the null value in IF/THEN/ELSE and CASE statements. In this first example, COUNT.TO.TEN does not execute because X is the null value. Instead, the program prints @NULL evaluates to 'false'.

Program Example
X=@NULL IF X THEN GOSUB COUNT.TO.TEN ELSE GOSUB NO.COUNT STOP COUNT.TO.TEN: FOR X = 1 TO 10 PRINT X NEXT X RETURN NO.COUNT: PRINT "@NULL evaluates to 'false'" RETURN

In the following program segment, CASE 1 executes because X is the null value. The program segment prints Deleting corrupt record; @ID is null.

Program Example
X = @NULL BEGIN CASE CASE X = 1 GOSUB ADD_RECORD CASE X = 2 GOSUB UPDATE_RECORD CASE X = 3 FINISHED = 1 CASE 1 GOSUB DELETE_RECORD

306

Developing UniBasic Applications

Null Value Handling in UniBasic

END CASE STOP ADD_RECORD: PRINT "Adding a record." RETURN UPDATE_RECORD: PRINT "Updating a record." RETURN DELETE_RECORD: PRINT "Deleting corrupt record; @ID is null." RETURN

Comparison Operators Used in Conditional Tests


The following table lists the UniBasic comparison operators. Comparison Operators NOT LTS GE, >=, => OR NOTS EQ, = GES #> LE, <=, =< EQS GT, > #< Comparison Operators LES NE, #, <>, >< GTS LT, < NES AND

Note
The logical operators NULL and NOT @NULL are both evaluated to the null value, which produces a result of false in a conditional test.

Developing UniBasic Applications

307

Chapter 10 - Null Value Handling

Null Value Handling On


The results of comparisons using the logical operator AND are provided in the following truth table (T, F, and N represent TRUE, FALSE, and @NULL, respectively). T T F N T F N F F F F N F N N

Null in Conditional Statements with AND The results of comparisons using the logical operator OR are provided in the following table (T, F, and N represent TRUE, FALSE, and @NULL, respectively). T T F N T T T T F N F T N N N

Null in Conditional Statements with OR You can use the following sample program to test comparisons of the logical operator OR. To use the program to test the logical operator AND, substitute AND for OR in this program.

Program Example
PROMPT @(-1) PROMPT'' PRINT "First value: Enter true, false, or null: ": INPUT answer answer = UPCASE(answer) GOSUB SET.VAL val.one=value

308

Developing UniBasic Applications

Null Value Handling in UniBasic

PRINT "Second value: Enter true, false, or null: ": INPUT answer answer = UPCASE(answer) GOSUB SET.VAL val.two=value BEGIN CASE CASE (val.one OR val.two ) = 1 PRINT "Condition is true." CASE ISNV(val.one OR val.two) = 1 PRINT "Condition is null" CASE 1 PRINT "Condition is false." END CASE SET.VAL: IF answer = "NULL" THEN value = @NULL IF answer = "TRUE" THEN value = 1 IF answer = "FALSE" THEN value = 0 RETURN

The following example shows a sample run of this program. It demonstrates that the comparison true OR null produces a result of true.

Screen Example
:RUN BP null.compare First value: Enter true, false, or null: true Second value: Enter true, false, or null: null Condition is true.

Developing UniBasic Applications

309

Chapter 10 - Null Value Handling

Examples The following program segments demonstrate the effect of the null value on comparisons.

Reminder
In a comparison test, the null value is interpreted by UniBasic as false. A comparison with the null value produces a result of null:

Program Example
X = @NULL IF NOT(X) THEN PRINT "1a. NOT(NULL) is TRUE" END ELSE Y = NOT(X) PRINT "1b. NOT(NULL) IS FALSE" END

This program segment prints the following:

Screen Example
1b. NOT(NULL) IS FALSE

Any number is greater than the null value:

Program Example
X = @NULL IF (100 LE X) THEN PRINT "2a. 100 LE NULL is TRUE" END ELSE PRINT "2b. 100 LE NULL IS FALSE"

310

Developing UniBasic Applications

Null Value Handling in UniBasic

END PRINT "

100 LE NULL evaluates to ":(100 LE @NULL)

This program segment prints:

Screen Example
2b. 100 LE NULL IS FALSE 100 LE NULL evaluates to 0

The null value compared to the null value yields a result of false:

Program Example
X = @NULL IF (X EQ X) THEN PRINT "3a. NULL EQ NULL IS TRUE" END ELSE PRINT "3b. NULL EQ NULL IS FALSE" END PRINT " NULL EQ NULL evaluates to ":(@NULL EQ @NULL)

This program segment prints:

Screen Example
3b. NULL EQ NULL IS FALSE NULL EQ NULL evaluates to 0

Anything AND the null value yields a result of false:

Program Example
X = @NULL IF (1 AND X) THEN

Developing UniBasic Applications

311

Chapter 10 - Null Value Handling

PRINT "4a. 1 AND NULL IS TRUE" END ELSE PRINT "4b. 1 AND NULL IS FALSE" END PRINT " 1 AND NULL evaluates to ":(1 AND @NULL)

This program segment prints:

Screen Example
4b. 1 AND NULL IS FALSE 1 AND NULL evaluates to

Anything OR the null value yields a result of true:

Program Example
X = @NULL IF (1 OR X) THEN PRINT "5a. 1 OR NULL IS TRUE" END ELSE PRINT "5b. 1 OR NULL IS FALSE" END PRINT ' 1 OR NULL evaluates to ":(1 OR @NULL)

This program segment prints:

Screen Example
5a. 1 OR NULL IS TRUE 1 OR NULL evaluates to 1

312

Developing UniBasic Applications

Null Value Handling in UniBasic

Null Value Handling Off


The UniBasic comparison operations follow ANSI 2-way logic, with the null value being compared as its ASCII value (ASCII character 129 in the English language group).

The Null Value in Conversion Functions


The following table lists the UniBasic conversion functions. Conversion Functions ICONV ICONVS OCONV OCONVS FMT FMTS

Conversion Functions Affected by the Null Value

Null Value Handling On


In Data For conversion functions ICONV/S, OCONV/S, and FMT/S, the presence of the null value in data produces a result of the null value, and the STATUS function return value is set to 5. In Parameters If a UniBasic program tries to use the null value as a conversion specification, UniBasic generates a runtime error and returns the input string. The STATUS function then returns 2, indicating an invalid conversion specification.

Tip
Some string functions also perform conversions of various kinds while ignoring the null value. Use the conversion functions in the preceding table when you want to produce a null value when a null value is input; use the string functions to convert between the null value and some other string.

Developing UniBasic Applications

313

Chapter 10 - Null Value Handling

Examples The following example demonstrates that an ICONV conversion on data containing the null value produces a result of the null value:

Program Example
X = ICONV(@NULL,"D2/") PRINT "STATUS FOR ICONV IS ":STATUS() IF ISNV(X) THEN PRINT "ICONV on the null value produces the null value."

This program produces the following results:

Screen Example
STATUS FOR ICONV IS 5 ICONV on the null value produces the null value.

Note
If the null value is found in a string to be converted, the UniBasic STATUS function return value is set to 5. This example demonstrates that @NULL in an argument produces no result, but sets the STATUS return code to 2:

Program Example
PRINT = ICONV(1234,"@NULL") PRINT "STATUS FOR ICONV IS ":STATUS()

314

Developing UniBasic Applications

Null Value Handling in UniBasic

This program produces the following results:

Screen Example
STATUS FOR ICONV IS 2

Null Value Handling Off


In Data The ASCII character that would represent the null value in data has no effect on conversion functions. In Parameters The result is the null value; invalid format code.

The Null Value in String Functions


The following general guidelines apply to all string functions: Null Value Handling On In Data The UniBasic string commands and functions are not affected by the null value in data. Therefore, you can use them to manipulate the null character just as you would any other character. Exceptions to this rule are listed in the following sections. The following list highlights some points to remember about the null value in string functions: ISNV and ISNVS test for the null value as a string or dynamic array element. SEQ(@NULL) returns the ASCII code representing @NULL on your system. You cannot use an expression like IF CUSTOMER = @NULL to search for the null value. You must instead test for a return value of 1 from ISNV or ISNVS to determine if a variable or element is the null value. You cannot use NOT(@NULL) to test for the absence of the null value because NOT(@NULL) evaluates to the null value. You must instead test for a return value of 0 from ISNV or ISNVS. You can assign the null value to a variable in the following way: X=@NULL

Developing UniBasic Applications

315

Chapter 10 - Null Value Handling

In Parameters Whenever a numeric parameter is required, and the null value is encountered, UniBasic displays the warning message Null value found when numeric required; UniBasic then continues processing using 0 as the parameter. Null Value Handling Off In Data The ASCII character that would represent the null value has no effect on conversion functions when encountered in data. You can use these functions to locate and change any ASCII value, including nulls. In Parameters Whenever a numeric parameter is required, and the null value is encountered, UniBasic displays the warning message Non-numeric found when numeric required; UniBasic continues processing using 0 as the function parameter.

Summary
The null value receives no special handling in string functions. It is processed as any other character. If UniBasic encounters the null value in a command parameter when a number is expected, it displays a warning message and uses 0. Type String conversion Commands/Functions ASCII, CHANGE, CHAR, CHARS, CONVERT, EBCDIC, REPLACE, SEQ, SEQS DOWNCASE, UPCASE, SOUNDEX Note - Special characters, including the null value, are ignored. They remain unchanged in function output. Testing for data type ALPHA (alphabetic) NUM, NUMS (numeric) ISNV, ISNVS (null value) Note - Special characters, including the null value, are not alphabetic or numeric. UniBasic String Function Types

316

Developing UniBasic Applications

Null Value Handling in UniBasic

Type Conditional tests

Commands/Functions IF/THEN/ELSE, CASE, WHILE, UNTIL Note - For more information see The Null Value in Conditional Tests earlier in this chapter.

Concatenation Insertion Deletion Location

: (colon), CAT, CATS, SPLICE [], FIELDSTORE, GROUPSTORE, INS, INSERT DEL, DELETE GROUP, MATCHFIELD, XLATE FIND, FINDSTR, LOCATE, MATCH, MATCHES

Extraction Counting Returning System, File, Printer, and User Information Measuring Length Repeating

[ ], EXTRACT, FIELD, FIELDS, REMOVE, SUBSTRINGS, TRIM, TRIMB, TRIMF, TRIMS COUNT, COUNTS, DCOUNT DIR, SYSTEM, GETPTR, GETPU, GETUSERGROUP, GETUSERNAME LEN, LENS Note - The null value has a length of 1 byte. STR, STRS SPACE, SPACES Note - These functions produce a string or array elements made up of spaces. UniBasic String Function Types (continued)

Developing UniBasic Applications

317

Chapter 10 - Null Value Handling

Examples
The following subroutine converts UniData delimiters and the null value to printable characters:

Program Example
* externally cataloged subroutine to convert the null value * and UniData delimiters for printing SUBROUTINE null.swap(A) SWAP @NULL WITH "null value" IN A SWAP @AM WITH "AM" IN A SWAP @VM WITH "VM" IN A SWAP @SM WITH "SM" IN A RETURN

Testing for Data Type The following program uses the function ISNV to test a string to see if it consists of the null value:

Program Example
A = 0 PRINT "1.a ISNV(0) = ":ISNV(A) PRINT "2.a NOT(ISNV(0)) = ":NOT(ISNV(0)) A = "abc":@NULL:"def" PRINT "1.b ISNV('abc':@NULL:'def') = ":ISNV(A) PRINT "2.b NOT(ISNV('abc':@NULL:'def')) = ":NOT(ISNV(A)) A = @NULL PRINT "1.c ISNV(@NULL) = ":ISNV(A) PRINT "2.c NOT(ISNV(@NULL)) = ":NOT(ISNV(A))

318

Developing UniBasic Applications

Null Value Handling in UniBasic

This program produces the following results. Notice that ISNV executed on a string that contains the null value and other characters produces a negative result (0).

Screen Example
1.a 2.a 1.b 2.b 1.c 2.c ISNV(0) = 0 NOT(ISNV(0)) = 1 ISNV('abc':@NULL:'def') = 0 NOT(ISNV('abc':@NULL:'def')) = 1 ISNV(@NULL) = 1 NOT(ISNV(@NULL)) = 0

The following program segment inserts the null value into or deletes the null value from a multivalue in the QUANTITY attribute for a selected record in the INVENTORY demo database file:

Program Example
OPEN 'INVENTORY' TO INV_FILE ELSE PRINT "OPEN error" STOP END PRINT "Enter record to be modified: ": INPUT rec READU REC_ARRAY FROM INV_FILE,rec LOCKED PRINT "Record locked." ELSE PRINT "Record not found." LOCATE @NULL IN REC_ARRAY<6,1,0> SETTING POINT THEN PRINT "The null value already exists in the array at position ": PRINT POINT PRINT "Deleting the null value." DEL REC_ARRAY<6,POINT> PRINT "@NULL deleted from the array at position ":POINT PRT_ARRAY = CHANGE(REC_ARRAY,@NULL,"@NULL") FOR X = 1 TO POINT PRINT PRT_ARRAY<6,X> NEXT X WRITE REC_ARRAY TO INV_FILE,rec ON ERROR PRINT "WRITEerror." END ELSE

Developing UniBasic Applications

319

Chapter 10 - Null Value Handling

REC_ARRAY<6,POINT> = @NULL PRINT "@NULL placed in the array at position ":POINT PRT_ARRAY = CHANGE(REC_ARRAY,@NULL,"@NULL") FOR X = 1 TO POINT PRINT PRT_ARRAY<6,X> NEXT X WRITE REC_ARRAY TO INV_FILE,rec ON ERROR PRINT "WRITEerror." END STOP

The following example shows a a sample run of the preceding program:

Screen Example
:RUN BP null.del.ins Enter record to be modified: ?56070 @NULL placed in the array at position 5 400 500 394 399 @NULL

A second execution of this program produces the following results:

Screen Example
:RUN BP null.del.ins Enter record to be modified: ?56070 The null value already exists in the array at position 5 Deleting the null value. @NULL deleted from the array at position 5 400 500 394 399

320

Developing UniBasic Applications

Null Value Handling in UniBasic

Reminder
When a numeric parameter is required, and the null value is encountered, UniBasic displays the warning message Null value found when numeric required; UniBasic continues processing using 0 as the function parameter. Sorting and Indexing The following series of examples demonstrates the use of SETINDEX to set the record pointer to the first null key in the PROD_NAME alternate key index:

Program Example
OPEN 'INVENTORY' TO inventory ELSE PRINT "Open error" SETINDEX 'PROD_NAME', NULLVAL_ALT_KEY inventory FOR X = 1 TO 5 READFWD rec FROM inventory THEN PRINT rec<0>:", ":rec<3>:", ":rec<4> END ELSE NULL NEXT X STOP

This program produces the following result when no null values exist in the PROD_NAME index:

Screen Example
:RUN BP set.idx 10020, Adapter, A/C Adapter for notebook computers 10086, Adapter, Ethernet LC Card 10092, Adapter, Workgroup Hub 10082, CD Player, Portable Model 10104, CD Player, Personal Model, Bass Boost

Developing UniBasic Applications

321

Chapter 10 - Null Value Handling

After the null value is inserted into the PROD_NAME attribute for records 10008 and 56060, the same program produces the following results:

Screen Example
:RUN BP set.idx 10015, , Portable, B/W, 6 ppm 10238, , Super Deluxe Model 10020, Adapter, A/C Adapter for notebook computers 10086, Adapter, Ethernet LC Card 10092, Adapter, Workgroup Hub

322

Developing UniBasic Applications

Chapter 11 - Managing Named Pipes

The UniBasic OSOPEN, OSBREAD, and OSBWRITE commands enable UniBasic programs to interface, through named pipes, with RedBack (UniDatas web application development software), other UniBasic programs, or other non-UniData processes.

Note
You cannot use the READSEQ command to read a named pipe, nor can you use the WRITESEQ or WRITESEQF command to write to a named pipe. You must use OSBREAD and OSBWRITE to perform these tasks. Three factors affect the operation of these commands: Is the target of the command a named pipe or a file? For a named pipe Is it open for a complementary operation on the other end? (When read access is requested, is the pipe open for writing on the other end? Conversely, when write access is requested, is the pipe open for reading on the other end?) For a named pipe Is data stored in the pipe, and is it of the length specified in the command?

Processing can be modified to accommodate different circumstances. For example, a single process can open the pipe for read-write access, and then coordinate access programmatically. Also, a process can force the requested access by executing the NODELAY option.

Developing UniBasic Applications

323

Chapter 11 - Managing Named Pipes

For UNIX, use the UNIX mkfifo command to create a named pipe, as in the following example:

Reminder
To execute a UNIX command from the ECL prompt, precede it with the UniData bang (!) command.

Screen Example
:!mkfifo pipe_file :ls -l pipe_file prw-rw-rw1 carolw

unisrc

0 Jul 28 09:55 pipe_file

For Windows NT or Windows 2000, use the following Win32 APIs to create a named pipe: CreateNamedPipe() Creates an instance of a named pipe on the local machine. For open mode, specify PIPE_ACCESS_DUPLEX. You also can specify a timeout value, which the UniBasic OSOPEN command uses to wait for an available instance. ConnectNamedPipe() Establishes a connection between the server pipe instance and the client side. DisconnectNamedPipe() Returns the server pipe instance to a listening state. It does not destroy the pipe. Use this API if your server uses multiple pipe instances. CloseHandle() Destroys the pipe when the last pipe instance closes. Use this API when you no longer need the pipe.

324

Developing UniBasic Applications

Points to Remember
Keep in mind the following points when accessing named pipes: When a process reads from a pipe, the data is removed from the pipe. This differs from a process reading from a file, in which case the data is copied into a variable, but also remains in the file. If the NODELAY keyword is not specified, and a process tries to access (open, read, or write) a pipe that is not already open in the opposite mode, the process waits for the pipe to be accessed in the opposite mode before proceeding. This can give the appearance of a hung process. (Opposite mode means that if you try to open a pipe for reading, it also must be open for writing on the other end. If you try to open a pipe for writing, it also must be open for reading on the other end.) UniData cannot temporarily close named pipes to manage the operating systems limitation on the maximum number of files allowed to be opened at a time. Therefore, opening a large number of named pipes at the same time can cause a process to try to open more than the maximum number of files allowed by the operating system. For UniData for UNIX only, the PIPE_BUF system variable determines the length of the named pipe. For a write (OSBWRITE) to be atomic, the length of data to be written must be less than or equal to the value of the PIPE_BUF. Maximum length for PIPE_BUF is determined by the operating system. For information about system variables, see Administering UniData on UNIX.

The sections that follow describe the syntax and processing for OSOPEN, OSBREAD, and OSBWRITE for use with named pipes.

Developing UniBasic Applications

325

Chapter 11 - Managing Named Pipes

OSOPEN
Syntax: OSOPEN filename [READONLY | WRITEONLY] TO file.var [NODELAY] [ON ERROR statements] {THEN statements [END] | ELSE statements [END]}

Note
If filename is not a named pipe, the NODELAY keyword has no effect. On UniData for UNIX, filename in the UniBasic OSOPEN command must include the entire path unless the pipe or file resides in the current directory. On UniData for Windows NT or Windows 2000, use the filename format \\computername\PIPE\pipename where computername is a valid computer name or a period (.), which specifies the local machine, and pipename is the name of a pipe. The keywords READONLY, WRITEONLY, and NODELAY are used with named pipes: READONLY opens the pipe or file for read access only. WRITEONLY opens the pipe or file for write access only.

Note
To specify that a pipe be open for both read and write access, omit the READONLY and WRITEONLY keywords from the OSOPEN statement. NODELAY forces a pipe to be opened immediately. This lets a process continue even when the pipe is not open in the opposite access mode. The application then must manage access to the pipe to ensure that it is opened for the opposite process before reading from or writing to it.

326

Developing UniBasic Applications

OSOPEN

Reminder
If OSOPEN against a named pipe is successful, the file pointer is set to the beginning of the file.

Opening Named Pipes


The following table, which applies to UniData for UNIX only, summarizes the actions UniBasic takes when OSOPEN is executed against a named pipe. Mode read-write Open1 no effect NODELAY no effect Action Depends on whether the operating system supports read-write access mode: OS supports read-write mode opens. OS does not support read-write mode executes ON ERROR or aborts. read-only yes no no effect no Opens. The process waits until the pipe is opened in write-only mode or read-write mode on the other end. Opens. Opens. The process waits until the pipe is opened in read-only mode or read-write mode on the other end. ELSE clause executes. 0 2 0 0 after open 0 0 0 after open 3 STATUS2

no write-only yes no

yes no effect no

no

yes

Summary of OSOPEN Functionality with Named Pipes (UniData for UNIX Only)
1Is

the pipe open in the opposite mode? is the UniBasic STATUS function setting after this operation?

2What

Developing UniBasic Applications

327

Chapter 11 - Managing Named Pipes

Opening a Pipe in Read-Write Access Mode


Processing depends on whether the operating system supports this access mode. OS supports read-write access mode UniBasic forces the pipe to open immediately. OS does not support read-write access mode The ON ERROR clause executes if specified, or the program aborts; the UniBasic STATUS function is set to 2.

Opening a Pipe in Read-Only Access Mode


Processing depends on whether the pipe is open for writing on the other end: If the pipe is open for writing on the other end, UniBasic opens the pipe for read-only access. If the pipe is NOT open for writing on the other end: NODELAY specified UniBasic forces the pipe open immediately in the requested mode. NODELAY not specified The process waits until the pipe is opened in write-only mode on the other end.

Opening a Pipe in Write-Only Access Mode


Processing depends on whether the pipe is open for reading on the other end: If the pipe is open for reading on the other end, UniBasic opens the pipe for write-only access. If the pipe is NOT open for reading on the other end: NODELAY specified The ELSE clause executes; STATUS is set to 3. NODELAY not specified The process waits until the pipe is opened in read-only mode on the other end.

328

Developing UniBasic Applications

OSOPEN

More about Opening Named Pipes (Windows NT or Windows 2000)


On UniData for Windows NT or Windows 2000, the following behavior occurs when you attempt to open a named pipe: If the named pipe exists, and if an instance is available, the OSOPEN command opens the pipe immediately. If NODELAY is specified, and if the named pipe does not exist or no instance is available, the ELSE clause executes, and STATUS is set to 4. If NODELAY is not specified: If all instances are busy, the process waits until an instance is available, and then the pipe opens. If the timeout period expires, the ELSE statement executes and the UniBasic STATUS function is set to 4. If the named pipe does not exist, the process waits for five minutes before the ELSE statement executes.

Developing UniBasic Applications

329

Chapter 11 - Managing Named Pipes

OSBREAD
OSBREAD var FROM file.var [AT byte.expr] LENGTH length.expr [NODELAY] [ON ERROR statements] The UniBasic OSBREAD command accommodates reading from named pipes: The AT clause is not allowed. AT is not appropriate for use with named pipes because they are always read with no offset. The NODELAY keyword forces UniData to read the pipe immediately, which lets a reading process continue even when the pipe is not open for writing or no data exists in the pipe.

Note
If you do not specify NODELAY on an OSBREAD against a named pipe, the process trying to read waits until the pipe is opened for writing, and data is written to it, before reading from the pipe.

Reading from a Named Pipe


Keep these points in mind when writing programs that read from named pipes: Unlike a typical read, reading a named pipe removes the data. Your application should check the length of var after reading a pipe. The value returned could be shorter than length.expr. Data is truncated if it is longer than length.expr. On UniData for Windows NT or Windows 2000, you can set a named pipe to messageread mode by indicating -1 for length.expr. To set a named pipe to byte-read mode, set length.expr to a value greater than -1. Data in message mode always has a boundary, and it can be obtained in its entirety. Data in byte mode has no boundary. The reader must specify a length and have some kind of protocol with the server to interpret it correctly.

330

Developing UniBasic Applications

OSBREAD

Summary for Reading Named Pipes


The combination of the following conditions and command options determine the action taken by UniBasic when OSBREAD is executed against a named pipe: Presence or absence of data in the pipe. Open/closed status of the pipe. Presence or absence of the AT and NODELAY command options.

The following table, which applies to UniData for UNIX only, summarizes actions taken by UniBasic for each possible combination of these factors. AT yes Data1 no effect Open2 no effect NODELAY no effect Action ON ERROR executes; if no ON ERROR clause, the program aborts. Note - ON ERROR executes regardless of other conditions (presence of data; open/closed status; NODELAY). no no yes no no effect no no effect no effect UniBasic returns data, of the length requested, from the pipe. UniBasic returns actual content of the pipe regardless of its length. Can be an empty string. The process waits for data to be written to the pipe, or for the pipe to be closed; if closed with no data written, returns an empty string. UniBasic returns actual content of the pipe regardless of its length. Can be an empty string. 0 0 STATUS3 2

no

no

yes

no

0 after read

no

no

yes

yes

Summary of OSBREAD Functionality with Named Pipes (UniData for UNIX Only)
1Does

the pipe contain data of the length requested?

Developing UniBasic Applications

331

Chapter 11 - Managing Named Pipes

2 3

Is the pipe open in the opposite mode? What is the UniBasic STATUS function setting after this operation?

Reading from a Named Pipe


When a process executes a read against a named pipe, the actions UniBasic takes depend on whether the pipe contains data of the length requested in the read statement. Data of the Required Length When the named pipe contains data of at least the length requested, UniBasic returns the actual contents of the pipe, even if the pipe contains more data than that requested. NODELAY has no effect in this case. Data of Insufficient Length When the named pipe contains insufficient data (the pipe is empty or the length of the data is less than specified in the OSBREAD statement), processing depends on whether the pipe is open for writing on the other end. If the pipe is open for writing on the other end: NODELAY specified UniBasic returns actual content of the pipe regardless of its length. Can be an empty string. The STATUS function return value is set to 0. NODELAY not specified The process waits for data of the length requested to be written to the pipe or for the pipe to be closed. If the pipe is closed with no data or insufficient data written to it, UniBasic returns the actual contents of the pipe. The STATUS function return value is set to 0 in either case.

If the pipe is not open for writing, UniBasic returns the actual content of the pipe regardless of its length. It can be an empty string. NODELAY has no effect.

332

Developing UniBasic Applications

OSBREAD

More about Reading from a Named Pipe (Windows NT or Windows 2000)


For UniData for Windows NT or Windows 2000, the following behavior occurs when you attempt to read message-type or byte-type named pipes. Message-Type Pipes If NODELAY is specified, and if a message exists in the pipe, UniBasic returns the actual contents of the pipe regardless of its length. If a message does not exist in the pipe, UniData returns an empty string and the STATUS function return value is set to 5. If NODELAY is not specified, and if a message exists in the pipe, UniBasic returns the actual contents of the pipe regardless of its length. If a message does not exist in the pipe, the process waits until a complete message is sent to the pipe.

Byte-Type Pipes If NODELAY is specified, and if the data has a length greater than or equal to length.expr, UniBasic returns the contents of the pipe according to the length requested. If the data has a length less than length.expr, UniBasic returns the actual contents of the pipe (even with zero length) and the STATUS function return value is set to 5. If NODELAY is not specified, and if the data has a length greater than or equal to length.expr, UniBasic returns the contents of the pipe according to the length requested. If the data has a length less than length.expr, either the process waits until it receives data with length length.expr, or a server end closes the pipe. In the case of a server end close, UniBasic returns the contents of the pipe perhaps with length less than length.expr, and the STATUS function return value is set to 5.

Developing UniBasic Applications

333

Chapter 11 - Managing Named Pipes

OSBWRITE
Syntax: OSBWRITE expr [ON | TO] file.var [AT byte.expr] [NODELAY] [ON ERROR statements] The UniBasic OSBWRITE command accommodates writing to a named pipe: The AT clause is not allowed because named pipes are always read with no offset. The keyword NODELAY forces UniData to write when a pipe is full.

Note
If you do not specify NODELAY, the writing process waits until the pipe is opened for reading.

Writing to Named Pipes


The combination of the following conditions and command options determine the action taken by UniBasic when OSBWRITE is executed against a named pipe: Presence and length of data in the pipe Open/closed status of the pipe Presence or absence of the AT and NODELAY command options

The following table summarizes the actions taken by UniBasic when OSBWRITE is executed against a named pipe. AT yes Empty1 no effect Open2 no effect NODELAY no effect Action ON ERROR executes; if no ON ERROR clause, the program aborts. Note - ON ERROR executes regardless of other conditions (presence of data; open/closed status; NODELAY). Summary of OSBWRITE Functionality with Named Pipes STATUS3 2

334

Developing UniBasic Applications

OSBWRITE

AT no no no

Empty1 no effect yes no

Open2 no yes yes

NODELAY no effect no effect no

Action ON ERROR executes; if no ON ERROR clause, the program aborts. UniBasic writes to the pipe. The process waits for sufficient space to become available in the pipe to contain the data being written. UniBasic first tries to write to the pipe; when it runs out of space, the ON ERROR clause executes. The program fails if ON ERROR is not specified. Tip - The INMAT function returns the length of data written to the pipe.

STATUS3 6 0 0 after writing 3

no

no

yes

yes

Summary of OSBWRITE Functionality with Named Pipes


1Is

enough space available in the pipe to receive the data? is the UniBasic STATUS function return value after this operation?

2What

Writing to a Closed Named Pipe


If the pipe is not open for reading, the ON ERROR clause executes. The program fails if ON ERROR is not specified.

Writing to an Open Named Pipe


Processing depends on whether the pipe contains sufficient space to accommodate the data being written: Sufficient space available UniBasic writes to the pipe regardless of whether NODELAY is specified.

Developing UniBasic Applications

335

Chapter 11 - Managing Named Pipes

Sufficient space NOT available Processing depends on whether NODELAY is specified: NODELAY specified UniBasic first tries to write to the pipe; when it runs out of space, the ON ERROR clause executes. If the ON ERROR clause is not coded, the program aborts. The UniBasic STATUS function is set to 3, and the INMAT function returns the length of data written to the pipe. NODELAY not specified The process waits for sufficient space to become available in the pipe to receive the data. When all the data is written successfully, the INMAT function returns the length of data written to the pipe.

Warning
On UniData for UNIX only, the length of data in expr must be less than or equal to the value of the PIPE_BUF system variable for the write to be atomic. PIPE_BUF determines the length of the named pipe. Maximum length is determined by the operating system.

336

Developing UniBasic Applications

OSCLOSE

OSCLOSE
Syntax
OSCLOSE file.var [ON ERROR statements]

Description
The UniBasic OSCLOSE command closes a sequential file that was opened with the OSOPEN statement.

Parameters
The following table describes each parameter of the syntax. Parameter file.var ON ERROR statements Description Specifies the file to close. Specifies statements to execute if the OSCLOSE statement fails with a fatal error because the file is not open, an I/O error occurs, or UniData cannot find the file. If you do not specify the ON ERROR clause and a fatal error occurs, the program terminates. OSCLOSE Parameters

Developing UniBasic Applications

337

Chapter 11 - Managing Named Pipes

STATUS Function Return Values


After you execute OSCLOSE, the STATUS function returns one of the values described in the following table. Return Value 0 5 Meaning The file is closed successfully. The file did not close. STATUS Function Values

Examples
In the following example, the program statement closes the file UNDEF:

Program Example
OSCLOSE UNDEF

Related Topics
OSOPEN
The UniBasic OSOPEN command opens an operating systemlevel sequential file.

338

Developing UniBasic Applications

STATUS Function Return Values

STATUS Function Return Values


After you execute OSOPEN, OSCLOSE, OSBREAD, or OSBWRITE, the STATUS function returns one of the values described in the following table. Return Value 0 1 2 3 Execution was successful. Invalid file name or file variable. User lacks permission to access the file or pipe (at operating system level). Execution against a named pipe: OSBWRITE was executed against a pipe that is open for reading, but is full; NODELAY was specified. OSOPEN with the WRITEONLY option was executed against a pipe that was not open for reading. 4 5 6 The file does not exist. Undefined error. OSBWRITE was executed against a named pipe that is not open for reading. UniBasic STATUS Function Return Values Description

Developing UniBasic Applications

339

Chapter 11 - Managing Named Pipes

INMAT
Syntax
INMAT()

Description
After you write to a named pipe, the INMAT function return value contains the number of bytes written. This information can help you determine how much space was available in a pipe at the time of an unsuccessful write.

Note
For additional syntax and return values for INMAT, see INMAT in the UniBasic Commands Reference.

340

Developing UniBasic Applications

Troubleshooting

Troubleshooting
The following information could be helpful in troubleshooting processes that manage named pipes: Symptom Data is only partially written to a file or a named pipe. Probable Cause On UniData for UNIX only, a process has tried to write data to a named pipe whose length is greater than PIPE_BUF, and NODELAY was specified in the write statement. Read operations work differently on named pipes versus other file types. Data read from a named pipe is removed from the pipe. Data read from other types of files is copied from the file. If NODELAY is not specified, and a process tries to access (open, read or write) a pipe that is not already open in the opposite mode, the process waits for it to be accessed in the opposite mode before proceeding. UniData cannot temporarily close named pipes to manage the operating systems limitation on the maximum number of files allowed to be opened at a time. Therefore, opening a large number of named pipes at the same time can cause a process to try to open more than the maximum number of files allowed by the operating system. The pipe already contained some data when this process executed OSBWRITE. When UniBasic runs out of room in the pipe, the ON ERROR clause executes, or the program aborts. Tip - The UniBasic INMAT function returns the number of bytes written. Troubleshooting Named Pipe Processes

A named pipe is empty after a read.

A process that manages named pipes appears to be hung.

A process executes the ELSE clause of OSOPEN executed against a named pipe.

Data is only partially written to a named pipe; and the ON ERROR clause executes, or the program aborts.

Developing UniBasic Applications

341

Chapter 11 - Managing Named Pipes

Example
The following example demonstrates opening a named pipe, writing record IDs to it, and closing it:

Screen Example
**************** OPEN.NAMED.PIPES: **************** EOF = '' SELECT FILE.PRIORITY.MAP LOOP READNEXT PRIORITY ELSE EOF = 1 UNTIL EOF PRIORITY.LIST<-1>=PRIORITY PIPE.NAME = 'SF.PIPE.':PRIORITY OSOPEN PIPE.NAME TO PIPE(PRIORITY) ELSE MSG.TXT = 'Unable to open ':PIPE.NAME GOSUB 9999 STOP END REPEAT ... ************************ 2110 * Write ID to pipe ************************ ID.LENGTH = LEN(OUT.ID) PIPE.ID = OUT.ID:STR(' ',PIPE.WIDTH-ID.LENGTH) OSBWRITE PIPE.ID TO PIPE(PRIORITY) RETURN ... *********** CLOSE.PIPES: *********** FOR I = 1 TO DCOUNT(PRIORITY.LIST,@AM) PRIORITY = PRIORITY.LIST<I> OSBWRITE SHUTDOWN TO PIPE(PRIORITY) PRINT "'SHUTDOWN' ISSUED TO SF.PIPE.":PRIORITY NEXT I

342

Developing UniBasic Applications

Example

This next example reads the IDs from the other end of the pipe:

Screen Example
PIPE.WIDTH = 32

*************** OPEN.NAMED.PIPE: *************** PIPE.NAME = 'SF.PIPE.':P.PRIORITY OSOPEN PIPE.NAME TO PIPE ELSE MSG.TXT = 'Unable to open ':PIPE.NAME GOSUB 9999 @USER.RETURN.CODE=0 STOP END ... ************ PROCESS.LOOP: ************ LOOP OSBREAD IN.ID FROM PIPE LENGTH PIPE.WIDTH CONVERT ' ' TO '' IN IN.ID IF IN.ID = "SHUTDOWN" THEN OSCLOSE PIPE @USER.RETURN.CODE = 1 EXIT END READ INBOX.REC FROM IN.FILE, IN.ID THEN IF INBOX.REC<MB$COMPL_FLAG> = 0 THEN GOSUB 2005; * Process INBOX.REC END END REPEAT MSG.TXT = '"SHUTDOWN" received from SF.PH.READ. GOSUB 9999; * Print Message RETURN

Ending program.'

Developing UniBasic Applications

343

Chapter 11 - Managing Named Pipes

344

Developing UniBasic Applications

Chapter 12 - Using CallHTTP

CallHTTP provides customers with the capability of interacting with a web server from UniBasic through the standard HTTP protocol. In order to effectively use the CallHTTP functions, you should have a working knowledge of the HTTP standard. Internet and web technologies have rapidly changed the way business is conducted by enterprises of all categories. E-commerce is increasingly becoming an essential part of any business. Many companies desire the capability to call out to the web from UniBasic so that their now standalone applications can be integrated with other applications through the web. There are many scenarios where this capability can be beneficial. For example, you may want to integrate a general ledger application with a third party application that has already been webenabled. When an account number is given, the general ledger application has to send it to the web application through an HTTP request for validation. The web application then returns a confirmation to the UniBasic application. HTTP is a complex standard with a large number of components and methods. The goal for CallHTTP is to provide a basic yet general implementation that enables UniBasic to act as an HTTP client so that data can be exchanged between a UniBasic application and a web server. CallHTTP provides the plumbing for users to build a specific client, not make UniBasic a browser of its own. CallHTTP is implemented with the Socket Interface as its network transport, and this lower level API is also available for direct access by the user.

Developing UniBasic Applications

345

Chapter 12 - Using CallHTTP

Configuring the Default HTTP Settings


The setHTTPDefault function will configure the default HTTP settings, including proxy server and port, buffer size, authentication credential, HTTP version, and request header values. These settings will be used with every HTTP request that follows.

Syntax
setHTTPDefault(option, value) If you require all outgoing network traffic to go through a proxy server, setHTTPDefault() should be called with value containing the proxy server name or IP address as well as the port (if other than the default of 80).

Parameters
The following table describes each parameter of the syntax. Parameter option value Description A string containing an option name. See the table below for the options currently defined. A string containing the appropriate option value. setHTTPDefault Parameters The following table describes the available options for setHTTPDefault. Option PROXY_NAME PROXY_PORT Description Name or IP address of the proxy server. The port number to be used on the proxy server. This only needs to be specified if the port is other than the default of 80. setHTTPDefault Options

346

Developing UniBasic Applications

Configuring the Default HTTP Settings

Option VERSION

Description The version of HTTP to be used. The default version is 1.0, but it can be set to 1.1 for web servers that understand the newer protocol. The string should be 1.0 or 1.1. The size of the buffer for HTTP data transfer between UniData and the web server. The default is 4096 however, the buffer size can be increased to improve performance. It should be entered as an integer greater than or equal to 4096. The user name and password to gain access. The string should be username:password. Default Basic authentication can also be set. If a request is denied (HTTP staus 401/407), UniBasic will search for the default credential to automatically re-submit the request. The header to be sent with the HTTP request. If default_headers contains an empty string, then any current user-specified default header will be cleared. Currently, the only default header UniBasic sets automatically is User-Agent UniData 5.2. If you do not want to send out this header you should overwrite it with setHTTPDefault(). Per RFC 2616, for net politeness, an HTTP client should always send out this header. UniBasic will also send a date/time stamp with every HTTP request. According to RFC 2616, the stamp will represent time in Universal Time (UT) format. A header should be entered as a dynamic array in the form of <HeaderName>@VM<HeaderValue>@Fm<HeaderName>@VM<HeaderValue>. setHTTPDefault Options

BUFSIZE

AUTHENTICATE

HEADERS

The following table describes the status of each return code. Return Code 0 1 2 Success. Invalid option. Invalid Value. Status

Developing UniBasic Applications

347

Chapter 12 - Using CallHTTP

Note
All defaults set by setHTTPDefault() will be in effect until the end of the current UniData session. If you do not want the setting to affect subsequent programs, you will need to clear it before exiting the current program. If the user wishes to set the Authorization or Proxy-Authorization header as defualts, see the description under setRequestHeader(). To clear the default settings, pass an empty string with PROXY_NAME, AUTHENTICATE and HEADERS, and 0 for PROXY_PORT and BUFSIZE.

348

Developing UniBasic Applications

Getting the Current HTTP Default Settings

Getting the Current HTTP Default Settings


The getHTTPDefault function will return the default values of the HTTP settings. See the section under setHTTPDefault for additional information.

Syntax
getHTTPDefault(option, value)

Parameters
The following table describes each parameter of the syntax: Parameter option Desription Currently, the following options are defined: PROXY_NAME PROXY_PORT VERSION BUFSIZE AUTHENTICATE HEADERS A string containing the appropriate option value. getHTTPDefault Parameters The following table describes the status of each return code. Return Code 0 1 Success. Invalid option. Return Code Status Status

value

Developing UniBasic Applications

349

Chapter 12 - Using CallHTTP

Creating an HTTP Request


The createRequest function creates an HTTP request and returns a handle to the request.

Syntax
createRequest(URL, http_method, request_handle)

Parameters
The following table describes each parameter of the syntax. Parameter URL Option A string containing the URL for a resource on a web server. An accepted URL must follow the specified syntax defined in RFC 1738. The general format is: http://<host>:<port>/<path>?<searchpart>. The host can be either a name string or IP address. The port is the port number to connect to, which usually defaults to 80 and is often omitted, along with the preceding colon. The path tells the web server which file you want, and, if omitted, means home page for the system. The searchpart can be used to send additional information to a web server. A string which indicates the method to be performed on the resource. See the table below for the available (case-sensitive) methods. A handle to the request object. createRequest Parameters The following table describes the available methods for http_method. Method GET createRequest Methods Description

http_method request_handle

350

Developing UniBasic Applications

Creating an HTTP Request

Method POST

Description [:<MIME-type>] For this method, it can also have an optional MIME-type to indicate the content type of the data the request intends to send. If no MIME-type is given, the default content type will be application/x-wwwform-urlencoded. Currently, only multipart/form-data is internally supported, as described in function addRequestParameter() and submitRequest(), although other multipart/* data can also be sent if the user can assemble it on his/her own. (The multipart/form-data format itself is thoroughly described in RFC 2388).

HEAD OPTIONS DELETE TRACE PUT CONNECT /* HTTP/1.1 and later*/ /* HTTP/1.1 and later*/ /* HTTP/1.1 and later*/ /* HTTP/1.1 and later but not supported */ /* HTTP/1.1 and later but not supported */ createRequest Methods The following table describes the status of each return code. Return Code 0 1 2 Success. Invalid URL (Syntactically). Invalid method (For HTTP 1.0, only GET/POST/HEAD) Status

Note
If URL does include a searchpart, it must be in its encoded format (space is converted into +, and other non-alphanumeric characters are converted into %HH format. See addRequestParameter()

Developing UniBasic Applications

351

Chapter 12 - Using CallHTTP

for more details). However, host and path are allowed to have these unsafe characters. UniBasic will encode them before communicating with the web server.

Note
This function can also be used later to support other protocols (like FTP, in which case the URL supplied would be in the form of: ftp://<user>:<pswd>@<host>:<port>/cwd1/.../cwdN>/<name>;type=<typecode> . The http_method option would be ignored for an FTP request.

352

Developing UniBasic Applications

Setting Additional Headers for a Req uest

Setting Additional Headers for a Request


The setRequestHeader function allows the user to set additional headers for a request.

Syntax
setRequestHeader(request_handle, header_name, header_value)

Parameters
The following table describes each parameter of the syntax. Parameter request_handle header_name header_value Description The handle to the request returned by createRequest(). The name of the header. The value of the header. setRequestHeader Parameters The following table describes the status of each return code. Return Code 0 1 2 3 Success. Invalid request handle. Invalid header (Incompatible with method). Invalid header value. Status

Note
Since a user defined header or header value can be transferred, it is difficult to check the validity of parameters passed to the function. UniBasic currently will not perform syntax checking on the parameters, although it will reject setting a response header to a request. Refer to RFC 2616 for

Developing UniBasic Applications

353

Chapter 12 - Using CallHTTP

valid request headers. The header set by this function will overwrite settings by setHTTPDefault().

Note
This function supports Base64 encoding for Basic authentication. If header_name contains either Authorization or Proxy-Authorization, the header_value should then contain ASCII text user credential information in the format of userid:password, as specified by RFC 2617. This function will then encode the text based on Base64 encoding.

Note
Only Basic authentication is supported. Digest authentication may be supported in the future. Basic authentication is not safe and is not recommended for use with transferring secured data.

354

Developing UniBasic Applications

Adding a Parameter to the Request

Adding a Parameter to the Request


The addRequestParameter function adds a parameter to the request.

Syntax
addRequestParameter(request_handle, parameter_name, parameter_value, content_handling)

Parameters
The following table describes each parameter of the syntax. Parameter request_handle parameter_name parameter_value content_handling The handle to the request. The name of the parameter. The value of the parameter. The dynamic MIME type for the parameter value. addRequestParameter Parameters The following table describes the status of each return code. Return Code 0 1 2 3 Success. Invalid request handle. Invalid parameter. Bad content type. Status Description

Developing UniBasic Applications

355

Chapter 12 - Using CallHTTP

Note
For a GET request, content_handling is ignored. For a POST request with default content type, the default for content_handling is ContentType:text/plain if content_handling is not specified. For a POST request with Multipart/* content-type, content_handling is a dynamic array containing Content-* strings separated by field marks (@FM). They will be included in the multipart message before the data contained in parameter_value is sent. An example of content_handling: Content-Type: application/XML @FM Content-Dispostion: attachment; file=C:\drive\test.dat @FM Content-Length: 1923 Specifically, for a POST request with content type multipart/form-data, a Content-Dispostion: form-data header will be created (or, in the case of Content-Dispostion already in content_handling, form-data will be added to it). For both a GET and a POST request with either no content type specified or specified as application/x-www-form-urlencoded, as described in createRequest(), URL encoding is performed on data in parameter_value automatically. Basically, any character other than alpha-numeric is considered unsafe and will be replaced by %HH where HH is the ASCII value of the character in question. For example, # is replaced by %23, and / is replaced by %2F, etc.. One exception is that by convention, spaces ( ) are converted into +. For a POST method with other MIME-type specified, no encoding is done on data contained in parameter_value.

356

Developing UniBasic Applications

Submitting a Request

Submitting a Request
The submitRequest function will submit a request and get a response. The request is formed on the basis of default HTTP settings and previous setRequestHeader() and addRequestParameter() values. Specifically, for a GET method with parameters added, a prameter string (properly encoded) will be created and attached to the URL string after the ? character. For a POST request with non-empty post_data, the data is attached to the request message as is. No encoding is performed, and any parameters added through addRequestParameter() will be totally ignored. Otherwise the following processing will be performed. For a POST request with default content type, the parameter string will be assembled, a ContentLength header created, and then the string is attached as the last part of the request message. For a POST request with multipart/* content type, a unique boundary string is created and then multiple parts will be generated in the sequence they were added through calling addRequestParameter(). Each will have a unique boundary, followed by optional Content-* headers, and data part. The total lentgth is calculated and a Content-Length header is added to the message header. The request is then sent to the Web server identified by the URL supplied with the request and created through createRequest() (maybe via a proxy server). UniBasic is then waiting for the web server to respond. Once the response message is received, the status contained in the response is analyzed. If the response status indicates that redirection is needed (status 301, 302, 305 or 307), it will be performed automatically, up to five consecutive redirections (the limit is set to prevent looping, suggested by RFC 2616). If the response status is 401 or 407 (access denied), the response headers will be examined to see if the server requires (or accepts) Basic authentication. If no Basic authentication request is found, the function will return with an error. Otherwise default Authentication (set by setHTTPDefault) will be used to re-send the request. If no default authentication is set, and no other cached user authentication is found, the function will return with an error. If the user provides authentication information through Authorization or Proxy-Authorization header, the encoded information is cached. If later, a Basic authentication request is raised, no default authentication is found, and only one user/password encoding is cached, then it will be used to re-send the request.

Developing UniBasic Applications

357

Chapter 12 - Using CallHTTP

The response from the HTTP server is disposed into response_header and response_data. It is the users responsibility to parse the headers and data. UniBasic only performs transfer encoding (chunked encoding), and nothing else is done on the data. In other words, content-encoding (gzip, compress, deflate, etc.) are supposed to be handled by the user, as with all MIME types. Also, if a response contains header Content-type: multipart/*, all the data (multiple bodies enclosed in boundary delimiters, see RFC 2046) will be stored in response_data. It is the users responsibility to parse it according to boundary parameter.

Syntax
submitRequest(request_handle, time_out, post_data, response_headers, response_data, http_status)

Parameters
The following table describes each parameter of the syntax. Parameter request_handle time_out post_data response_headers response_data http_status The handle to the request. The timeout value (in milliseconds) before the wait response is abandoned. The data sent with the POST request. A dynamic array to stre header/value pairs. The resultant data (may be in binary format). A dynamic array containing the status code and explanatory text. submitRequest Parameters The following table describes the status of each return code. Return Code 0 1 Success. Invalid request handle. Status Description

358

Developing UniBasic Applications

Submitting a Request

Return Code 2 3 4 Timed out. Network Error. Other Errors.

Status

Developing UniBasic Applications

359

Chapter 12 - Using CallHTTP

Getting a Response Header


This function gets a specific response header value from response headers returned by submitRequest(). It can be used to query if a specific header, for example, Content-encoding, is present in the response.

Syntax
getResponseHeader(request_handle, header_name, header_value)

Parameters
The following table describes each parameter of the syntax. Parameter request_handle header_name header_value The handle to the request. A string containing a response header name. The value of the header, if present. Otherwise, an empty string. getResponseHeader Parameters The following table describes the status of each return code. Return Code 0 1 2 Success. Invalid request handle. Header not found. Status Description

360

Developing UniBasic Applications

Protocol Logging

Protocol Logging
This function will start or stop logging.

Syntax
protocolLogging(log_file, log_action, log_level)

Parameters
The following table describes each parameter of the syntax. Parameter log_file log_action log_level Description The name of the file the logs will be recorded to. The default log file name is httplog and will be created under the current directory. Either ON or OFF. The default is OFF. The detail level of logging from 0 - 10. See table below. protocolLogging Parameters The following table describes each log level and Detail. Level 0 1 2 3 4-10 No logging. Socket open/read/write/close action (no real data) HTTP request: host info(URL) Level 1 logging plus socket data statistics (size, etc.). Level 2 logging plus all data actually transferred. More detailed status data to assist debugging. log_level Details Detail

Developing UniBasic Applications

361

Chapter 12 - Using CallHTTP

The following table describes the status of each return code. Return Code 0 1 Success. Failed to start logging. Return Code Status Status

362

Developing UniBasic Applications

Chapter 13 - Using the Socket Interface

The UniBasic Socket API provides the user with the capability of interacting with an application running on another machine via the sockets interface. The Socket API enables you to write distributed UniBasic applications. For example, one application, written in the server side socket interface, can function as the server while others can function as clients. The server and the clients can cooperate on tasks through socket communication. This is an efficient way for UniBasic applications to communicate, and is easy to implement.

Developing UniBasic Applications

363

Chapter 13 - Using the Socket Interface

Socket Function Error Return Codes


These error return codes are used for all socket-related functions described below. Note that only numeric code should be used in Basic programs. The table below describes each error code and its meaning. Error Code 0 - SCK_ENOERROR 1 - SCK_ENOINITIALISED 2 - SCK_ENETDOWN 3 - SCK_EFAULT 4 - SCK_ENOTCONN 5 - SCK_EINTR 6 - SCK_EINPROGRESS 7 - SCK_EINVAL No error. (NT) A successful WSAStartup() call must occur before using this function. The network subsystem has failed. The addrlen parameter is too small or addr is not a valid part of the user address space. The socket is not connected. The (blocking) call was cancelled. (NT: through WSACancelBlockignCall). A blocking Windows Sockets 1.1 call is in progress, or the service provider is still processing a callback function. This can be caused by several conditions. The listen function was not invoked prior to accept, the socket has not been bound with bind, an unknown flag was specified, or MSG_OOB was specified for a socket with SO_OOBINLINE enabled or (for byte stream sockets only) len was zero or negative. The queue is nonempty upon entry to accept and there are no descriptors available. No buffer space is available. The descriptor is not a socket. Error Code Definitions Definition

8 - SCK_EMFILE 9 - SCK_ENOBUFS 10 - SCK_ENOTSOCK

364

Developing UniBasic Applications

Socket Function Error Return Codes

Error Code 11 - SCK_EOPNOTSUPP 12 - SCK_EWOULDBLOCK 13 - SCK_ENETRESET 14 - SCK_ESHUTDOWN

Definition The referenced socket is not a type that supports connection-oriented service. The socket is marked as nonblocking and the requested operation would block. The connection has been broken due to the keep-alive activity detecting a failure while the operation was in progress. The socket has been shut down. It is not possible to receive on a socket after shutdown has been invoked with how set to SD_RECEIVE or SD_BOTH.

15 - SCK_EMSGSIZE

(For recv()) The message was too large to fit into the specified buffer and was truncated, or (for send()) the socket is message oriented, and the message is larger than the maximum supported by the underlying transport. The virtual circuit was terminated due to a time-out or other failure. The application should close the socket as it is no longer usable.

16 - SCK_ETIMEDOUT 17 - SCK_ECONNABORTED 18 - SCK_ECONNRESET

The connection has been dropped, because of a network failure or because the system on the other end went down without notice.

The virtual circuit was reset by the remote side executing a hard or abortive close. For UPD sockets, the remote host was unable to deliver a previously sent UDP datagram and responded with a Port Unreachable ICMP packet. The application should close the socket as it is no longer usable.

19 - SCK_EACCES

The requested address is a broadcast address, but the appropriate flag was not set. Call setsockopt with the SO_BROADCAST parameter to allow the use of the broadcast address. The remote host cannot be reached from this host at this time.

20 - SCK_EHOSTUNREACH 21 - SCK_ENOPROTOOPT 22 - SCK_ESYSNOTREADY

The option is unknown or unsupported for the specified provider or socket Indicates that the underlying network subsystem is not ready for network communication. Error Code Definitions

Developing UniBasic Applications

365

Chapter 13 - Using the Socket Interface

Error Code 23 SCK_EVERNOTSUPPORTED 24 - SCK_EPROCLIM 25 - SCK_EAFNOSUPPORT 26 - SCK_EPROTONOSUPPORT 27 - SCK_EPROTOTYPE 28 - SCK_ESOCKTNOSUPPORT 29 - SCK_EBADF 30 - SCK_EHOST_NOT_FOUND 31 - SCK_ETRY_AGAIN 32 - SCK_ENO_RECOVERY 33 - SCK_ENO_RECOVERY 34 - SCK_EACCESS 35 - SCK_EADDRINUSE

Definition The version of Windows Sockets support requested is not provided by this particular Windows Sockets implementation. Limit on the number of tasks supported by the Windows Sockets implementation has been reached. The specified address family is not supported. The specifed protocol is not supported. The specified protocol is the wrong type for this socket. The specified socket type is not supported in this address family. For Windows CE AF_IRDA sockets only: the shared serial port is busy. Authoritative Answer Host not found. Nonauthoritative Host not found, or server failure. A nonrecoverable error occurred. Valid name, no data record of requested type. Attempt to connect datagram socket to broadcast address failed because setsockopt option SO_BROADCAST is not enabled. A process on the machine is already bound to the same fully-qualified address and the socket has not been marked to allow address re-use with SO_REUSEADDR. For example, the IP address and port are bound in the af_inet case. (See the SO_REUSEADDR socket option under setsockopt.) The specified address is not a valid address for this machine. The socket is already connected. A nonblocking connect call is in progress on the specified socket. The attempt to connect was forcefully rejected. Memory allocation error. Error Code Definitions

36 - SCK_EADDRNOTAVAIL 37 - SCK_EISCONN 38 - SCK_EALREADY 39 - SCK_ECONNREFUSED 40 - SCK_EMALLOC

366

Developing UniBasic Applications

Socket Function Error Return Codes

Error Code 41 - SCK_EUNKNOWN Other unknown errors. Error Code Definitions

Definition

Developing UniBasic Applications

367

Chapter 13 - Using the Socket Interface

Opening a Socket
Use the openSocket function to open a socket connection in a specified mode and return the status.

Syntax
openSocket(name_or_IP, port, mode, timeout, socket_handle)

Parameters
The following table describes each parameter of the syntax. Parameter name_or_IP port mode timeout socket_handle Description DNS name (x.com) or IP address of a server. Port number. If the port number is specified as a value <= 0, CallHTTP defaults to a port number of 40001. 0:non-blocking mode 1:blocking mode The timeout value expressed in milliseconds. If you specify mode as 0, timeout will be ignored. A handle to the open socket. openSocket Parameters

368

Developing UniBasic Applications

Opening a Socket

The following table describes the return status of each mode. Mode 0 - Non-blocking Return Status The function will return immediately regardless of whether or not the socket is successfully opened. The return code inidcates if the operation is successful. The timeout value is ignored. If a positive timeout is specified, the function will either return with a valid socket handle or will time out after the specified timeout period. If the timeout value is 0, the function will block until either the socket is successfully opened, the underlying TCP/IP connection times out or some other error prevents the socket from opening. Mode Return Status The following table describes the status of each return code. Return Code 0 Non-zero Success. See Socket Function Error Return Codes. Status

1 - Blocking

Developing UniBasic Applications

369

Chapter 13 - Using the Socket Interface

Closing a Socket
Use the closeSocket function to close a socket connection.

Syntax
closeSocket(socket_handle) Where socket_handle is the handle to the socket you want to close. The following table describes the status of each return code. Return Code 0 Non-zero Success. See Socket Function Error Return Codes. Status

370

Developing UniBasic Applications

Getting Information From a Socket

Getting Information From a Socket


Use the getSocketInformation function to obtain information about a socket connection.

Syntax
getSocketInformation(socket_handle, self_ or_ peer, socket_info)

Parameters
The following table describes each parameter of the syntax. Parameter socket_handle self_ or_ peer Description The handle to the open socket. Get information on the self end or the peer end of the socket. Specify 0 to return information from the peer end and non-zero for information from the self end. Dynamic Array containing information about the socket connection. For information about the elements of this dynamic array, see the following table.

socket_info

The following table describes each element of the socket_info dynamic array. Element 1 2 3 4 Open or closed Name or IP Port number Secure or Non-secure getSocketInformation Parameters Description

Developing UniBasic Applications

371

Chapter 13 - Using the Socket Interface

Element 5 Blocking mode

Description

getSocketInformation Parameters The following table describes the status of each return code. Return Code 0 Non-zero Success. See Socket Function Error Return Codes. Status

372

Developing UniBasic Applications

Reading From a Socket

Reading From a Socket


Use the readSocket function to read data in the socket buffer up to max_read_size characters.

Syntax
readSocket(socket_handle, socket_data, max_read_size, time_out, blocking_mode, actual_read_size)

Parameters
The following table describes each parameter of the syntax. Parameter socket_handle socket_data max_read_size time_out blocking_mode Description The handle to the open socket. The data to be read from the socket. The maximum mumber of characters to return. If this is 0, then the entire buffer should be returned. The time (in milliseconds) before a return in blocking mode. This is ignored for non-blocking read. 0:using current mode 1:blocking 2:non-blocking The number of characters actually read. -1 if error. readSocket Parameters

actual_read_size

Developing UniBasic Applications

373

Chapter 13 - Using the Socket Interface

The following table describes the return status of each mode. Mode 0 - Non-Blocking Return Status The function will return immediately if there is no data in the socket. If the max_read_size parameter is greater than the socket buffer then just the socket buffer will be returned. If there is no data in the socket, the function will block until data is put into the socket on the other end. It will return up to the max_read_size character setting. Mode Return Status The following table describes the status of each return code. Return Code 0 Non-zero Success. See Socket Function Error Return Codes. Status

1 - Blocking

374

Developing UniBasic Applications

Writing to a Socket

Writing to a Socket
Use the writeSocket function to write data to a socket connection.

Syntax
writeSocket(socket_handle, socket_data, time_out, blocking_mode, actual_write_size)

Parameters
The following table describes each parameter of the syntax. Parameter socket_handle socket_data time_out blocking_mode Description The handle to the open socket. The data to be written to the socket. The allowable time (in milliseconds) for blocking. This is ignored for a non-blocking write. 0:using current mode 1:blocking 2:non-blocking The number of characters actually written. writeSocket Parameters The following table describes the return status of each mode. Mode 0 - Blocking Return Status The function will return only after all characters in socket_data are written to the socket. Mode Return Status

actual_write_size

Developing UniBasic Applications

375

Chapter 13 - Using the Socket Interface

Mode 1 - Non-Blocking

Return Status The function may return with fewer character written than the actual length (in the case that the socket is full). Mode Return Status

The following table describes the status of each return code. Return Code 0 Non-zero Success. See Socket Function Error Return Codes. Status

376

Developing UniBasic Applications

Setting the Value for a Socket Option

Setting the Value for a Socket Option


The setSocketOptions function sets the current value for a socket option associated with a socket of any type.

Syntax
setSocketOptions(socket_handle, options)

Parameters
The following table describes each parameter of the syntax. Parameter socket_handle options Description The socket handle from openSocket(), acceptSocket(), or initServerSocket(). Dynamic Array containing information about the socket options and their current settings. The dynamic array is configured as: optName1<VM>optValue1a[<VM>optValue1b]<FM> optName2<VM>optValue2a[<VM>optValue2b]<FM> optName3... Where optName is specified by the caller and must be an option name string listed below. The first optValue specifies if the option is ON or OFF and must be one of two possible values: 1 for ON or 2 for OFF. The second optValue is optional and may hold additional data for a specific option. Currently, for the LINGER option it contains the delayed time (in milliseconds)before closing the socket. For all other options, it should not be specified as it will be ignored. setSocketOptions Parameters

Developing UniBasic Applications

377

Chapter 13 - Using the Socket Interface

The following table describes the available options (case-sensitive) for setSocketOptions. Option DEBUG REUSEADDR KEEPALIVE DONTROUTE LINGER BROADCAST OOBINLINE SNDBUF RCVBUF Description Enable/disable recording of debug information. Enable/disable the reuse of a location address (default) Enable/disable keeping connections alive. Enable/disable routing bypass for outgoing messages. Linger on close if data is present. Enable/disable permission to transmit broadcast messages. Enable/disable reception of out-of-band data in band. Set buffer size for output (default 4KB). Set buffer size for input (default 4KB). setSocketOptions Options The following table describes the status of each return code. Return Code 0 Non-zero Success. See Socket Function Error Return Codes. Status

378

Developing UniBasic Applications

Getting the Value of a Socket Option

Getting the Value of a Socket Option


The getSocketOptions function gets the current value for a socket option associated with a socket of any type.

Syntax
getSocketOptions(socket_handle, Options)

Parameters
The following table describes each parameter of the syntax.. Parameter socket_handle Description The socket handle from openSocket(), acceptSocket(), or initServerSocket().

Developing UniBasic Applications

379

Chapter 13 - Using the Socket Interface

Parameter options

Description A Dynamic Array containing information about the socket options and their current settings. When querying for options, the dynamic array is configured as: optName1<FM> optName2<FM> optName... When the options are returned, the dynamic array is configured as: optName1<VM>optValue1a[<VM>optValue1b]<FM> optName2<VM>optValue2a[<VM>optValue2b]<FM> optName3... Where optName contains an option name string listed below. The first optValue describes if the option is ON or Off and must be one of two possible values: 1 for ON or 2 for OFF. The second optValue is optional and may hold additional data for a specific option. Currently, for option LINGER, it contains the delayed time (in milliseconds) before closing the socket.

getSocketOptions Parameters The following table describes the available options (case-sensitive) for getSocketOptions(). Option DEBUG REUSEADDR KEEPALIVE DONTROUTE LINGER BROADCAST OOBINLINE Description Enable/disable recording of debug information. Enable/disable the reuse of a location address (default). Enable/disable keeping connections alive. Enable/disable routing bypass for outgoing messages. Linger on close if data is present. Enable/disable permission to transmit broadcast messages. Enable/disable reception of out-of-band data in band..

380

Developing UniBasic Applications

Getting the Value of a Socket Option

Option SNDBUF RCVBUF TYPE ERROR

Description Get buffer size for output (default 4KB). Get buffer size for input (default 4KB). Get the type of the socket. Get and clear error on the socket. getSocketOptions Parameters

The following table describes the status of each return code. Return Code 0 Non-zero Success. See Socket Function Error Return Codes. Status

Developing UniBasic Applications

381

Chapter 13 - Using the Socket Interface

Initializing a Server Side Socket Connection


Use the initServerSocket function to create a connection-oriented (stream) socket. Associate this socket with an address (name_or_IP) and port number (port), and specify the maximum length the queue of pending connections may grow to.

Syntax
initServerSocket(name_or_IP, port, backlog, svr_socket)

Parameters
The following table describes each parameter of the syntax. Parameter name_or_IP Description DNS name (x.com) or IP address of a server or empty. Empty is equivalent to INADDR_ANY which means the system will choose one for you. Generally, this parameter should be left empty. Port number. If the port number is specified as a value <= 0, CallHTTP defaults to a port number of 40001. The maximum length of the queue of pending connections (i.e. concurrent client-side connections). The handle to the server side socket. initServerSocket Parameters The following table describes the status of each return code. Return Code 0 Non-zero Success. See Socket Function Error Return Codes. Status

port backlog svr_socket

382

Developing UniBasic Applications

Accepting an Incoming Connection Attempt on the Server Side

Accepting an Incoming Connection Attempt on the Server Side


Use the acceptConnection function to accept an incoming connection attempt on the server side socket.

Syntax
acceptConnection(svr_socket, blocking_mode, timeout, in_addr, in_name)

Parameters
The following table describes each parameter of the syntax. Parameter svr_socket blocking_mode Description The handle to the server side socket which is returned by initServerSocket(). 0 - default (blocking) 1 - blocking. In this mode and the current blocking mode of svr_socket is set to blocking, acceptConnection() blocks the caller until a connection request is received or the specfied time_out has expired. 2 - non-blocking. In this mode if there are no pending connections present on the queue, acceptConnection() returns an error status code. In this mode, time_out is ignored. Timeout in milliseconds. The buffer that receives the address of the incoming connection. If NULL, it will return nothing. The variable that receives the name of the incoming connection. If NULL, it will return nothing. acceptConnection Parameters

time_out in_addr in_name

Developing UniBasic Applications

383

Chapter 13 - Using the Socket Interface

Parameter socket_handle

Description The handle to the newly created socket on which the actual connection will be made. The server will use readSocket(), writeSocket(), etc. with this handle to commuinicate with the client. acceptConnection Parameters

The following table describes the status of each return code. Return Code 0 Non-zero Success. See Socket Function Error Return Codes. Status

384

Developing UniBasic Applications

Protocol Logging

Protocol Logging
This function will start or stop logging.

Syntax
protocolLogging(log_file, log_action, log_level)

Parameters
The following table describes each parameter of the syntax. Parameter log_file log_action log_level Description The name of the file the logs will be recorded to. The default log file name is httplog and will be created under the current directory. Either ON or OFF. The default is OFF. The detail level of logging from 0 - 10. See table below. protocolLogging Parameters

Level 0 1 2 3 4-10 No logging

Detail

Socket open/read/write/close action (no real data) HTTP request: host info(URL) Level 1 logging plus socket data statistics (size, etc.). Level 2 logging plus all data actually transferred. More detailed status data to assist debugging. log_level Details

Developing UniBasic Applications

385

Chapter 13 - Using the Socket Interface

The following table describes the status of each return code. Return Code 0 1 Success. Failed to start logging. Status

386

Developing UniBasic Applications

Appendix A - Sample Program

The following sample program, UPDATE_ORDER, demonstrates the use of some UniBasic commands in a simple application. This program uses the demonstration database files that are included with UniData. The program calls an external subroutine, DISPLAY_MESSAGE, which is included at the end of this appendix.

Developing UniBasic Applications

387

Appendix A - Sample Program

UPDATE_ORDER
Program Example
** ** ** ** ** ** ** ** ** ** Program : Programmer : Created : Description: : : : : : : UPDATE_ORDERS Todd Roemmich 04/02/1996 Check and/or alter Order records Display Screen and ask for Order # Read record (if it exists) and display fields Prompt for a command (Alter, Delete, or Quit) A) Allow the user to change price or address D) Delete the record Q) Exit the program

*-------------- Include/Commons -----------------------* * Normally OPEN commands, EQUATEs, and DIMensions done with INCLUDE files. --TER EQU CLS TO @(-1) GOSUB OPEN_FILES *-------------- Main Logic ----------------------------GOSUB INITIALIZE LOOP GOSUB DISPLAY_SCREEN GOSUB GET_ORDER_NUMBER UNTIL ORDER_NUMBER[1,1] = 'Q' GOSUB DISPLAY_DATA IF RECORD_FOUND THEN GOSUB GET_RECORD_COMMAND RELEASE REPEAT GOSUB EXIT

388

Developing UniBasic Applications

UPDATE_ORDER

*------------ Subroutines -------------------------ALTER_RECORD: * Create a new screen, and allow PRICE and ADDRESS to be changed. * Initialize variables and draw the screen NEED.TO.WRITE = 0 DISPLAY CLS:@(15,5):"Alter ORDER": DISPLAY @(10,8):"(Press RETURN to leave unchanged)" DISPLAY @(8,9):"Old Price":@(42,9):"New Price (Enter 2 decimal places)"

* Change the PRICE field (if desired) FOR ENTRY = 1 TO NUM_ENTRIES NEW.PRICE = "" DISPLAY @(10,9+ENTRY):OCONV(ORDER.REC<7,ENTRY>,"MR2$,"): INPUT @(45,9+ENTRY):NEW.PRICE NEW.PRICE = OCONV(NEW.PRICE,"MCN") IF NEW.PRICE # '' AND NUM(NEW.PRICE) THEN ORDER.REC<7,ENTRY> = NEW.PRICE NEED.TO.WRITE = 1 END NEXT ENTRY * Display DISPLAY DISPLAY DISPLAY DISPLAY DISPLAY DISPLAY the current ADDRESS information @(21,12):"Change Address to: ": @(21,13):"Street Line1: ":@(40,13):ADDRESS<2> @(21,14):"Street Line2:" @(40,14):ADDRESS<3>:@(21,15):"City:":@(40,15):CLIENT.REC<6> @(21,16):"State:": @(40,16):CLIENT.REC<7>:@(21,17):"Zip:":@(40,17):CLIENT.REC<8>

* Accept INPUT to change values of address INPUT @(40,13):STREET1 IF STREET1 = '' THEN STREET1 = CLIENT.REC<4> INPUT @(40,14):STREET2 IF STREET2 = '' THEN STREET2 = CLIENT.REC<5> INPUT @(40,15):CITY IF CITY = '' THEN CITY = CLIENT.REC<6> INPUT @(40,16):STATE IF STATE = '' THEN STATE = CLIENT.REC<7> INPUT @(40,17):ZIP IF ZIP = '' THEN ZIP = CLIENT.REC<8>

Developing UniBasic Applications

389

Appendix A - Sample Program

* Compare Old and New values (Write out new record if needed) NEW_CLIENT = STREET1:STREET2:CITY:STATE:ZIP OLD_CLIENT = CLIENT.REC<4>:CLIENT.REC<5>:CLIENT.REC<6>:CLIENT.REC<7>:CLIENT.REC<8> IF (NEW_CLIENT # * Re-assign values CLIENT.REC<4> CLIENT.REC<5> CLIENT.REC<6> CLIENT.REC<7> CLIENT.REC<8> OLD_CLIENT) OR NEED.TO.WRITE THEN to CLIENT.REC = STREET1 = STREET2 = CITY = STATE = ZIP

GOSUB WRITE_RECORD END RETURN

DELETE_RECORD: * (Assuming the order #'s are on line 12) READVU ORDER_LINE FROM CLIENT_FILE,CLIENT_NUMBER,12 THEN LOCATE ORDER_NUMBER IN ORDER_LINE<1> SETTING POSITION THEN DEL ORDER_LINE<1,POSITION> END WRITEV ORDER_LINE ON CLIENT_FILE, CLIENT_NUMBER, 12 END * DELETE ORDERS_FILE, ORDER_NUMBER RELEASE CLIENT_FILE,CLIENT_NUMBER RETURN DISPLAY_DATA: * Display the current information in the desired record. This is * determined by the number the user entered (ORDER_NUMBER). READU ORDER.REC FROM ORDERS_FILE,ORDER_NUMBER THEN * Read with a lock so that no one else can modify it at the same time. RECORD_FOUND = 1 ORDER_DATE = OCONV(ORDER.REC<1>,"D4/") ORDER_TIME = OCONV(ORDER.REC<2>,"MT") CLIENT_NUMBER = ORDER.REC<3>

390

Developing UniBasic Applications

UPDATE_ORDER

ADDRESS = '' READU CLIENT.REC FROM CLIENT_FILE,CLIENT_NUMBER THEN ADDRESS<1> = CLIENT.REC<3> ADDRESS<2> = CLIENT.REC<4> ADDRESS<3> = CLIENT.REC<5> ADDRESS<4> = CLIENT.REC<6>:", ":CLIENT.REC<7>:" ":CLIENT.REC<8> END DISPLAY @(45,7):ADDRESS<1> DISPLAY @(45,8):ADDRESS<2> DISPLAY @(45,9):ADDRESS<3> DISPLAY @(45,10):ADDRESS<4> DISPLAY @(45,11):ADDRESS<5> PRODUCT_LINE = '' COLOR_LINE = '' QUANTITY_LINE = '' PRICE_LINE = '' NUM_ENTRIES = DCOUNT(ORDER.REC<4>,@VM) FOR ENTRY = 1 TO NUM_ENTRIES PRODUCT_NUMBER = ORDER.REC<4,ENTRY> COLOR = ORDER.REC<5,ENTRY> QUANTITY = ORDER.REC<6,ENTRY> PRICE = OCONV(ORDER.REC<7,ENTRY>,"MD2$,") IF PRODUCT_LINE = '' THEN PRODUCT_LINE = PRODUCT_NUMBER "R#10" COLOR_LINE = COLOR "R#10" QUANTITY_LINE = QUANTITY "R#10" PRICE_LINE = PRICE "R#10" END ELSE PRODUCT_LINE := " ":PRODUCT_NUMBER "R#10" COLOR_LINE := " ":COLOR "R#10" QUANTITY_LINE := " ":QUANTITY "R#10" PRICE_LINE := " ":PRICE "R#10" END NEXT ENTRY ORDER_DATA = @(13,7):ORDER_DATE:@(13,8):ORDER_TIME ORDER_DATA := @(13,10):CLIENT_NUMBER "R#5" ORDER_DATA := @(13,11):PRODUCT_LINE:@(13,13):COLOR_LINE ORDER_DATA := @(13,14):QUANTITY_LINE:@(13,15):PRICE_LINE DISPLAY ORDER_DATA END ELSE MESSAGE ="**(Record Does Not Exist)**" RECORD_FOUND = 0

Developing UniBasic Applications

391

Appendix A - Sample Program

CALL DISPLAY_MESSAGE(MESSAGE) END RETURN

DISPLAY_SCREEN: * Display the starting screen SCREEN SCREEN SCREEN SCREEN SCREEN = CLS:@(25,1):"ORDER MAINTENANCE":@(16,5):"(Enter Q to quit)" := @(5,6):"Order #: " := @(5,7):"Date: ":@(5,8):"Time: ":@(5,10):"Client #:" := @(5,11):"Product #: ":@(5,13):"Color: ":@(5,14):"Qty: " := @(5,15):"Price: "

DISPLAY SCREEN RETURN GET_ORDER_NUMBER: * Have the user enter a valid key to a record in the ORDERS file. LOOP DISPLAY @(15,6): INPUT ORDER_NUMBER ORDER_NUMBER = OCONV(ORDER_NUMBER,"MCU") IF NUM(ORDER_NUMBER) OR ORDER_NUMBER[1,1] = "Q" THEN VALID_ORDER_NUMBER = 1 END ELSE VALID_ORDER_NUMBER = 0 MESSAGE = "Order # must be a Number, or the letter 'Q'" CALL DISPLAY_MESSAGE(MESSAGE) END UNTIL VALID_ORDER_NUMBER REPEAT RETURN GET_RECORD_COMMAND: * Enter a valid command to act upon (the Desired record is already shown). DISPLAY @(7,22):"Enter A)lter, D)elete, or Q)uit: ": INPUT COMMAND,1 COMMAND = OCONV(COMMAND[1,1],"MCU") BEGIN CASE

392

Developing UniBasic Applications

UPDATE_ORDER

CASE COMMAND = "A" GOSUB ALTER_RECORD CASE COMMAND = "D" GOSUB DELETE_RECORD CASE COMMAND = "Q" FINISHED = 1 CASE 1 MESSAGE = "Valid options are A, D, or Q. Please try again" CALL DISPLAY_MESSAGE(MESSAGE) END CASE RETURN WRITE_RECORD: * The record(s) have been updated. Make sure that if the RECOVERABLE * FILE System is operational that either BOTH records are updated, or that * None are (using Transaction processing commands). TRANSACTION START ELSE IF STATUS() = 1 THEN DISPLAY "A Transaction had already been started, NESTED Transactions" DISPLAY "are NOT Allowed. (Contact System Administrator)" INPUT PAUSE,1_ END ELSE NULL END END WRITE CLIENT.REC ON CLIENT_FILE,CLIENT_NUMBER WRITE ORDER.REC ON ORDERS_FILE,ORDER_NUMBER TRANSACTION COMMIT IF STATUS() = 1 DISPLAY "The END ELSE DISPLAY "The END END RETURN ELSE THEN TRANSACTION was not started" TRANSACTION could not be committed."

OPEN_FILES: OPEN "CLIENTS" TO CLIENT_FILE ELSE MESSAGE = "The CLIENT file could not be opened."

Developing UniBasic Applications

393

Appendix A - Sample Program

CALL DISPLAY_MESSAGE(MESSAGE) STOP END OPEN "ORDERS" TO ORDERS_FILE ELSE MESSAGE = "The ORDERS file could not be opened." CALL DISPLAY_MESSAGE(MESSAGE) STOP END RETURN

INITIALIZE: DISPLAY CLS PROMPT '' RETURN EXIT: DISPLAY CLS STOP RETURN

394

Developing UniBasic Applications

DISPLAY_MESSAGE

DISPLAY_MESSAGE
The following example shows the external subroutine that is called by UPDATE_ORDERS:

Program Example
SUBROUTINE DISPLAY_MESSAGE(MESSAGE) DISPLAY @(5,20):MESSAGE DISPLAY @(5,21):"Press the (Return) key.": INPUT PAUSE,1_ RETURN

Developing UniBasic Applications

395

Appendix A - Sample Program

396

Developing UniBasic Applications

Appendix B - UniBasic Transaction Processing Concepts

Note
This appendix applies to UniData for UNIX only. UniData for Windows NT or Windows 2000 does not support transaction processing. This chapter describes some basic concepts on which UniData transaction processing (TP) is based. Chapter 9 - UniBasic Transaction Processing introduces the UniData TP commands, including how to create and convert to files, and describes some TP programming problems and proposes solutions for those problems. To learn more about recoverable files, see Administering UniData on UNIX. TP executes a set of statements as a single logical function. This ensures that if a database is in a consistent state before a transaction starts, the database maintains that consistent state when the transaction completes. To create a transaction, you must bind the operations by TRANSACTION START and TRANSACTION COMMIT statements. When the transaction commits, all of the operations take place as one, and UniData can recover the updated file in the event of any failure. If a transaction is aborted, none of the operations within the transaction take place.

Developing UniBasic Applications

397

Appendix B - UniBasic Transaction Processing Concepts

In This Appendix
This appendix explains some of the concepts on which UniData Transaction Processing is based. The following sections are included: Transaction Processing Rules: The ACID Properties Transaction Isolation Transaction Processing Errors What Are Isolation Levels? Programming to Isolation Levels

Updating Nonrecoverable Files in Transactions

398

Developing UniBasic Applications

Transaction Processing Rules: The ACID Properties

Transaction Processing Rules: The ACID Properties


The ACID properties in the following list distinguish transactions from non-TP database updates: Atomicity Consistency Isolation Durability

Atomicity
Logical operations grouped by transaction semantics are treated as one unit. They will all succeed or all fail.

Consistency
The components of a logical operation all succeed or all fail. If a database is in a consistent state before you apply a logical operation, it will be in a consistent state afterwards. For example, writing a new record will guarantee that the record and the indexes are both written. The record will not be written without its associated indexes.

Isolation
Isolation means that operations are based on a consistent database rather than on intermediate results from other operations. Isolation is controlled within UniBasic applications. If locks are properly set and checked, all transactions are properly isolated. In UniData SQL, a desired isolation level is set by specifying the isolation level with the set transaction command. If no isolation level is specified, a default isolation level is handled by the database engine.

Developing UniBasic Applications

399

Appendix B - UniBasic Transaction Processing Concepts

Durability
Durability means that completed transactions are preserved in the database despite failures. If UniData stops running, the database recovers to the last committed transaction when you restart UniData. Recovery techniques are available to provide durability in the case of media failure.

400

Developing UniBasic Applications

Transaction Isolation

Transaction Isolation
Even though transactions appear to process sequentially and atomically, they actually occur serially. Isolation is determined by the degree to which transaction errors are prevented. This section identifies transaction errors, isolation levels, and provides templates for programming to each isolation level.

Note
UniData SQL follows ANSI '92 standards, but UniBasic does not. For this reason, the types of transaction errors that can occur and the isolation levels that can be achieved differ between the two products. UniBasic and UniSQL TP differ in the following ways: UniData SQL always protects against lost updates and dirty reads, but could allow phantoms. UniBasic always protects against dirty reads and phantoms, but could allow lost updates.

Transaction Processing Errors


You can get unexpected results when transactions process concurrently. The following sections describe these errors: Lost Updates Dirty Reads Unrepeatable Reads

Lost updates, dirty reads, and unrepeatable reads can be prevented by designing applications appropriately, as in the examples provided later in this chapter.

Lost Updates
In a lost update, the updates from one transaction are overwritten by those of another.

Reminder
UniData SQL protects against lost updates in all transactions.

Developing UniBasic Applications

401

Appendix B - UniBasic Transaction Processing Concepts

Consider the following example:

Reminder
UniData SQL protects against lost updates in all transactions.

Program Segment 1

Transaction Segment 2
TRANSACTION START THEN PRINT Transaction started.

READ var FROM file.var, record1 ELSE PRINT Record not found. READ var FROM file.var, record1 ELSE PRINT Record not found var += 1 var += 2 WRITE var TO file.var, record1 WRITE var TO file.var, record1 TRANSACTION COMMIT THEN PRINT Transaction committed.

Program Segment 1 and Transaction Segment 2 are run concurrently. Program Segment 1 could lose updates because the record is not locked between its READ and WRITE. In two separate write operations, Transaction Segment 2 changes Record1s value to 2, then to 3. The update in Program Segment 1, made between the two writes, is lost.

Dirty Reads
A dirty read occurs when one of two scenarios takes place: Within a transaction, a process reads an attribute value between two updates made by a second transaction. Because the second transaction changes the value after the read, this read is considered dirty. Within a transaction, a record is changed; a second transaction process reads the record; then the first transaction process aborts and rolls back, changing the record to a different value. The value obtained by the second transaction is dirty.

402

Developing UniBasic Applications

Transaction Isolation

Reminder
UniBasic protects against dirty reads in all transactions. Consider the following example:

Program Segment 1

Transaction Segment 2
TRANSACTION START THEN PRINT Transaction started. ELSE STOP var = 1 WRITE var TO file.var, record1

READ var FROM file.var, record1 ELSE PRINT Record not found. var += 2 WRITE var TO file.var, record1 TRANSACTION COMMIT THEN PRINT Transaction committed.

Program Segment 1 and Transaction Segment 2 are run concurrently. Transaction Segment 2 writes a record; then Program Segment 1 reads it; finally, Transaction Segment 2 makes additional changes to the record. The record value obtained by Program Segment 1 is no longer current.

Unrepeatable Reads
Between two reads of a record in a transaction, another transaction updates the record. As a result, the first transaction obtains a different value for the same attribute on the second read. This is an unrepeatable read.

Developing UniBasic Applications

403

Appendix B - UniBasic Transaction Processing Concepts

Consider the following example:

Program Segment 1

Transaction Segment 2
TRANSACTION START THEN PRINT Transaction started.

READ var FROM file.var, record1 ELSE PRINT Record not found. var = 2 WRITE var TO file.var, record1 READ var FROM file.var, record1 ELSE PRINT Record not found. TRANSACTION COMMIT THEN PRINT Transaction committed.

Program Segment 1 and Transaction Segment 2 run concurrently. Program Segment 1 reads a record twice before and after Transaction Segment 2 updates it. The two read operations return different values for the record.

What Are Isolation Levels?


The isolation level determines the type of transaction errors (including lost updates, dirty reads, and unrepeatable reads) that are allowed or not allowed. UniBasic TP does not implicitly enforce isolation levels. The application program must provide protection from transaction errors by setting shared locks on records being read and by setting exclusive locks on records being updated. UniBasic TP does provide the following protection: records are automatically locked during write operations, and all locks set during a transaction are retained until the transaction is committed or aborted. Unlike UniBasic, UniData SQL automatically initiates and commits transactions and provides the level of isolation appropriate for the type of operation being executed. For more information, see Using UniData SQL. The following list describes four theoretical isolation levels: Isolation level 0 exposes transactions to any type of TP error, but cannot overwrite the updates from transactions at isolation level 1 or higher. To achieve atomicity at this level, the application must set exclusive locks on records being updated.

404

Developing UniBasic Applications

Transaction Isolation

Isolation level 1 protects transactions against lost updates but allows dirty reads and unrepeatable reads. When you execute a read operation, the system automatically locks records being updated. Isolation level 2 protects transactions against lost updates and dirty reads, but allows unrepeatable reads. Note: UniBasic automatically protects against dirty reads; therefore, if you program to protect against lost updates, you achieve isolation level 2. Isolation level 3 protects transactions against all three types of TP errors: lost updates, dirty reads, and unrepeatable reads.

Note
The ANSI-standard isolation levels available through UniData SQL differ from these because of the differences in product design described at the beginning of this section.

Programming to Isolation Levels


This section provides program templates that demonstrate how to program in UniBasic to achieve different levels of isolation.

Example: Programming to Isolation Level 0


The following program template provides no protection against TP errors. It is programmed for isolation level 0. Unlike the theoretical situation depicted by this example, UniBasic and UniData SQL provide some protection against TP errors.

Program Example
TRANSACTION START THEN PRINT "Transaction started." ELSE PRINT "Transaction start failed, STATUS = ":STATUS() READ var FROM file.var, record1 ELSE PRINT "Record not found." var += 2 WRITE var TO file.var, record1 TRANSACTION COMMIT THEN PRINT "Transaction committed." ELSE PRINT "Transaction aborted, STATUS = ":STATUS()

Developing UniBasic Applications

405

Appendix B - UniBasic Transaction Processing Concepts

Example: Programming to Isolation Level 2


The following program template illustrates programming for isolation level 2, which protects against lost updates and dirty reads. After the record is locked by the second read (READU), the transaction is protected against lost updates.

Program Example
TRANSACTION START THEN PRINT "Transaction started." ELSE PRINT "Transaction start failed, STATUS = ":STATUS() READ var FROM file.var, record1 . . . REM "If another transaction changes record1 here, its updates are lost" . . . READU var FROM file.var, record1 ELSE PRINT "Record not found." var += 2 WRITE var TO file.var, record1 TRANSACTION COMMIT THEN PRINT "Transaction committed." ELSE PRINT "Transaction aborted, STATUS = ":STATUS()

406

Developing UniBasic Applications

Transaction Isolation

Example: Programming to Isolation Level 3


The following program template illustrates programming for isolation level 3. All three types of transaction error are prevented because the record is locked by READU the first time it is read.

Program Example
TRANSACTION START THEN PRINT "Transaction started." ELSE PRINT "Transaction start failed, STATUS = ":STATUS() READU var FROM file.var, record1 ELSE PRINT "Record not found." var += 2 WRITE var TO file.var, record1 TRANSACTION COMMIT THEN PRINT "Transaction committed." ELSE PRINT "Transaction aborted, STATUS = ":STATUS()

Developing UniBasic Applications

407

Appendix B - UniBasic Transaction Processing Concepts

Updating Nonrecoverable Files in Transactions


UniData TP is effective only on recoverable files. An update to a nonrecoverable file is never part of a transaction, even when executed within a transaction. UniBasic updates the file, but with the following conditions: The write operation takes place immediately, even before the transaction commits. The write is not rolled back in the event of a transaction abort or restart after a system crash.

Warning
UniData records only updates made while a file is recoverable. Remember this when you change a file to or from the recoverable state.

408

Developing UniBasic Applications

Index

Symbols
!, 70 -, 159 , 46, 303 #, 47, 307 #<, 45, 307 #>, 45, 307 $, 70 $$FUN heading, cfuncdef file, 224 $$LIB heading, cfuncdef file, 226 $$OBJ heading, cfuncdef file, 226 $BASICTYPE, 62, 196 $DEFINE, 62 $ELSE, 63 $IFDEF, 63 $IFNDEF, 63 $INCLUDE, 63 $INSERT, 63 $UNDEFINE, 62 * commenting a program, 18 multiplication, 159 null value and, 303 **

exponentiation, 46, 159 *=, multiplication, 160 +, 159, 303 +=, increment variable, 160 /, division, 46, 159 /=, division, 160 : concatenation, 159 null value and, 317 :=, concatenation, 160 <, 45, 307 <=, 45, 307 <> EXTRACT function, 158 not equal to, 45 null value and, 307 = equal to, 45, 47 in cross reference report, 70 null value and, 307 =, decrement variable, 160 =<, 45, 47, 307 =>, 45, 47, 307 >, 45, 47, 307 ><, 45, 47

Developing UniBasic Applications

409

Index

>=, 45, 47, 307 @ in cross reference report, 70 @AM, 24 @FM, 24 @NULL, 24 @RM, 24 @SM, 24 @TM, 24 @TRANSACTION, 288 @USERNO, 84, 84 @variable @TRANSACTION, 288 introduction to UniBasic, 27 @VM, 24 [] EXTRACT, 157 null value and, 317 replace, 168 ^ exponentiation, 46, 159 null value and, 303 {, delimiter, 150 {}, calculate, 195 |, delimiter, 150 }, delimiter, 150 ~ converting nonprinting characters, 169 delimiter, 150

A
ABORT program control commands, 49 ABS, 164, 303 absolute value, 164 ACID properties, 399 ACOS, 164, 303

active transaction, testing for, 288 add see also WRITE addition, 46, 159 advisory lock, 136 AE compiling in, 59 creating a program with, 53 help, 56 introduction, 59 running a program from, 82 unable to find file, 54 ALPHA, 156 null value and, 316 alphabetic character in array, 158 alternate index creating and managing, 109 alternative editor compiling in, 59 introduction, 53 AND, 47 arc cosine, 164 arc sine, 164 arc tangent, 164 argument command or function and the null value, 303 arithmetic operation introduction, 159 on dates, 176 arithmetic operations and data type , 161 array converting data in, 168 dimensioned, 149 dynamic, 149 finding elements in, 156 left-justifying elements, 169

410

Developing UniBasic Applications

Index

summary of commands, 151, 154 types, 26, 149 array element testing for null value, 316, 318 ASCII character, converting, 168, 172 converting from numeric to, 170 function, 168 null value and, 316 ASIN, 164, 303 assigning null value, 315 value to variables, 25 AT, 330, 334 ATAN, 164, 303 atomicity, 399 attribute virtual, 195 attribute mark in UniBasic, 24

bottleneck, 142 BP file, 53 branching, 48 BREAK, 206 break key in transaction processing, 287 BUILD.INDEX transaction processing and, 294 business rules, 96

C
C program calling UniBasic subroutine, 235 writing, 219 CALCULATE, 195 calculations and data type, 161 CALL executing programs, 193 extending statements over multiple lines , 20 introduced, 33 program control commands, 49 callbas.mk, 218, 236 CallBasic compiling and linking the C program, 235, 243 U_callbas function, 275 udtcallbasic function, 271, 275 udtcallbasic_done function, 275 udtcallbasic_done( ) function , 238 udtcallbasic_init function, 275 writing the UniBasic subroutine, 235, 236, 273 CALLC _cdecl calling convention, 264 data types, 261 PASCAL calling convention, 264 program control commands, 49

B
base logarithm, 164 BASIC compiling programs, 59 options, 60 BASICTYPE, 61, 196 binary data, converting, 172 data, maintaining, 112 BITAND, 303 BITNOT, 303 BITOR, 303 BITXOR, 303 Boolean operator, 44 null value in, 307

Developing UniBasic Applications

411

Index

syntax, 260 called program starting a transaction in, 294 calling C functions from UniBasic, 219 external functions from UniBasic , 264 UniBasic subroutines from C , 235 UniBasic subroutines from external program , 272 CASE conditional tests, 38 introduced, 41 null value in, 305 program control commands, 49 CAT, 317 CATALOG examples, 79 options, 77 cataloged program determining version, 74 cataloging direct, 74 global, 75 local, 75 programs, 73 CATS, 317 cfuncdef, 224 CHAIN in transaction processing, 287 program call commands, 194 program control commands, 49 CHANGE, 181, 316 CHAR, 168, 181, 316 character conversion, 168 lower case, 169 nonprinting, 169 upper case, 169

CHARS, 181, 316 CLEAR.FILE transaction processing and, 294 CLEAR.LOCKS, 136 CLEARDATA, 193 clearing data stacks, 193 select lists, 118 CLEARINPUT, 204 CLEARSQL, 197 CLOSE, 128 CLOSESEQ, 113 closing files in programs, 128 non-UniData files, 114 sequential files, 337 CNAME transaction processing and, 294 code interpretive/object, 59 COL1, 157, 181 COL2, 157, 181 colon null value and, 317 command udtcallbasic_done, 272 udtcallbasic_init, 268 command argument and the null value, 303 commands, UniBasic, 17 comments, adding to a program, 18 COMMIT, 285 committing transactions inappropriately, 282 COMMON, 189, 189 comparison null value in, 302, 307 compiler directive commands, 62

412

Developing UniBasic Applications

Index

directive, defined, 16 UniBasic, 16 compiling programs, 59, 61 concatenation introduction, 159 operator precendence, 46 operators, 160 conditional operator, 44 conditional test see also logical operator defined, 16 null value in, 305 order of precedence, 47 types, 38 consistency, 399 constant representing data, 25 constraint violation, 98, 105 CONTINUE loop commands, 36 program control commands, 49 control statement, defined, 16 controlling looping, 49 program execution, 29 conversion functions, null value in , 313 CONVERT, 169, 316 command, 181 converting characters, 168 data, 166 files to transaction processing, 279 nonprinting characters, 169 null values, 302 numbers, 170 copying

array elements to variable, 158 COS, 164, 303 cosine, 164 COUNT, 156, 181, 317 COUNTS, 156, 182, 317 CPIO format in transaction processing, 290 CREATE.FILE transaction processing, 290 transaction processing and, 294 CREATE.INDEX transaction processing and, 294 creating dimensioned array, 154 recoverable file, 279, 290 UniBasic program, 51 cross reference report, 68 CRT, 205

D
DATA, 20 data binary, updating, 112 converting numbers, 172 determining type, 155 file types, 23 formatting, 166 in UniBasic programs, 93 relational, 44 representing in programs, 147 stack, 191 writing to a file, 123 DATA stack, 203 data type calculations and, 161 character, 168 date and time, 176

Developing UniBasic Applications

413

Index

numeric, 172 UNIX C definitions, 225 database maintaining consistency, 142 triggers, 96 date converting and formatting, 175 internal and display format, 175 rules for entering, 177 DCOUNT, 156, 182, 317 deadlock avoiding, 293 causes, 142 debugger cross referencing in UniBasic, 68 decimal converting data, 172 decrement value in variable, 160 default file, 115 DEFAULT.LOCKED.ACTION BELL, 135 degraded performance, 297 DEL, 317 DELETE, 317 invoking triggers with, 98 non-UniData files, 114 trigger subroutine, 102 DELETE.FILE transaction processing and, 294 DELETE.INDEX transaction processing and, 294 DELETEU triggers, 98 deleting records, 126 delimiter defining, 158 in dynamic arrays, 150

representing in UniBasic, 24 UniBasic, 150 demonstration database UniBasic program using, 387 device, output, 203 DIM, DIMENSION, 154 dimensioned array defined, 26 using, 153 DIR, 317 DIRECT, 78 direct catalog, 74 directing the compiler, 59 directory file (BP), 53 dirty read, 142, 402 DISPLAY, 205 display format, data and time, 176 display terminal commands, 205 DISPLAY_MESSAGE sample program, 395 displaying null value in UniBasic, 301 statements and variables in the debugger , 69 division arithmetic operators, 159 combined operators, 160 operator precedence, 46 DLL CallBasic implementation, 259 CALLC implementation, 259 UniData NT and DLL, 259 double spacing, suppressing, 210 DOWNCASE, 169, 316 DROUND, 303 durability, 400 dynamic array

414

Developing UniBasic Applications

Index

converting data in, 168 defined, 26 using, 149 dynamic link library (DLL), 259

E
E type VOC entry, 262, 266 EBCDIC, 168, 316 ECHO, 206 ED (editor), 53 efficiency transaction processing, 298 ELSE transaction processing, 283 empty strings selected attributes in a file, 211 UDT.OPTIONS and, 211 END program control commands, 49 ENTER, 194 EQ comparison operator, 307 conditional tests, 47 EQS, 307 error message, 203 in C function, 220 in external function, 265 EX, 56 exclusive lock, 133 EXECUTE program control commands, 49 EXECUTE SELECT, 117 EXECUTESQL example, 197 program control commands, 49 executing

UniBasic programs, 82 execution time reporting, 60, 84 EXIT loop commands, 36 program control commands, 49 exit, 265 exit( ), 221 exiting CallBasic, 243, 272 EXP, 164, 303 exponentiation, 46, 159 external date and time format, 176 interaction, 195 subroutines, 33 external interaction getting system information, 27 external program calling UniBasic subroutine, 272 writing, 264 external subroutine sample program, 395 EXTRACT, 158, 317 extracting commands, 155 data from arrays, 157

F
FI, 56 FIB, 59 FIBR, 57, 82 FIELD, 158, 182, 317 field length, exceeding during data entry , 205 FIELDS, 317 FIELDSTORE, 317

Developing UniBasic Applications

415

Index

file accessing unopened, 129 closing, 128 default, 115 deleting records from, 126 locks, 133 maintaining binary, 112 maintaining non-UniData, 112 maintaining through programs, 107 opening, 115 recoverable, 279 recoverable, creating or converting , 290 sequential, closing, 337 type, UniData, 23 UniData.LIB, 259, 274 writing to, 123 FILEINFO writing programs, 110 file-level operation, 294 filing programs, 56 FIND, 152, 157, 317 FINDSTR, 182, 317 FIR, 57 FLOAT.PRECISION, 160 floating point, 160 FMT, 171, 313 FMTS, 168 FOOTING, 209 FOR/NEXT example, 20 loop commands, 36 program control commands, 49 FORCE, 78 formatting arrays and strings, 168 dates, 176

functions for, 166 numbers, 171 FUNCTION writing trigger, 99 function conversion, 166 defined, 17 mathematic, 164 null value in, 303 U_callbas, 275 udtcallbasic, 275 udtcallbasic_done, 275 udtcallbasic_init, 275

G
GE conditional tests, 47 null value and, 307 GES, 307 GET, 182 GETPTR, 317 GETPU, 317 GETREADU, 137 GETUSERGROUP, 317 GETUSERNAM, 317 GLOBAL, 77 global catalog, 75 GOSUB executing subroutines, 31 program control commands, 49 greater than conditional tests, 47 GROUPSTORE, 317 GT conditional tests, 45 null value and, 307

416

Developing UniBasic Applications

Index

GTS, 307

H
hashed file maintaining through programs, 107 HEADING, 209 HELP for AE, 57 hexadecimal converting data, 173 hung process named pipes and, 341 HUSH, 205

I
I/O function, 205 ICONV, 182 converting and formatting, 166 null value and, 313 IF/THEN/ELSE conditional tests, 38 formatting long statements, 20 null value and, 305 program control commands, 49 INCLUDE, 63 increment variable, 160 INDEX, 157, 182 index alternate, 109 indexing null values, 301, 302 initial caps converting to, 169 INMAT dimentioned arrays and, 154

named pipes and, 340 INPUT, 182 input device, 203 into variables, 25 INPUT @, 182 INPUT@, 203 INPUTCLEAR, 204 INPUTERR, 203 INPUTIF, 204 INPUTNULL, 203 INPUTTRAP, 204 INS, 317 INSERT compiler directive, 63 null value and, 317 insert mode in AE, 57 INT, 164, 303 integer converting, 171 obtaining a random, 164 value, 164 interfacing with C invoking C programs, 236, 245 makefile command, 243 U_callbas function, 241 udtcallbasic_done function, 243 UniBasic program, 228 interfacing with external program U_callbas function, 269 udtcallbasic_done function, 272 udtcallbasic_init function, 268 internal format date and time, 175 international format for dates, 176 interpretive code, 59

Developing UniBasic Applications

417

Index

interrupt key in transaction processing, 287 introduction to UniBasic, 13 inverse cosine, sine, tangent , 164 ISNV, ISNVS, 155, 319 isolation defined, 399 dirty reads, 402 levels, 404 lost update, 401 preventing errors, 401 programming to level 0, 405 programming to level 2, 406 programming to level 3, 407 unrepeatable reads, 403

K
key break, in transaction processing , 287

L
L lock, 134 label in program subroutines, 32 layout, statement, 19 LE comparison operator, 307 conditional tests, 47 LEN, 156, 182, 317 length string or array, 156 LENS, 182, 317 LES comparison operator, 307

less than conditional tests, 47 limitation file-level operations, 294 report generator, 294 screen generator, 294 UniData SQL, 294 line editor, 53 Linking programs with UniData, 215 LIST, 69 LIST.QUEUE, 137 LIST.READU, 137, 196 LN null value and, 303 numeric function, 164 LOCAL, 77 local catalog, 75 LOCATE, 152, 157, 317 locating data in array, 157 lock checking status, 137 commands, when to use, 140 obtaining user number, 137 released by TRANSACTION COMMIT, 285 transaction processing, 297 types, 133 UniBasic commands, 137 using UniData, 131 LOCKED clause, 138 logarithm, 164 logical condition, 44 see also conditional test logical operator null value and, 307 long transaction performance, 297

418

Developing UniBasic Applications

Index

LOOP/REPEAT program control commands, 50 looping commands, 36 lost update, 401 lost updates, 142 lowercase converting to, 169 LT, 307 LTS, 307

M
maintaining dimensioned arrays, 154 dynamic arrays, 150 files, 107 make command, 245 makefile, 218, 236, 243 makeudapi, 228 makeudt, 227 MAT, 154 MATBUILD, 155 MATCH, 182, 182, 317 conditional tests, 45 MATCHES, 317 in conditional tests, 45 MATCHFIELD, 182, 317 math functions, 164 MATPARSE, 155 MAXIMUM, 156 McDonnell Douglas, 62 MDPERFORM, 196 MINIMUM, 156 minus operator precedence, 46 MOD null value and, 303

numeric function, 164 modify see also WRITE modifying data, 166 numbers, 171 multiplication, 160 arithmetic operators, 159 operator precedence, 46 multivalued attribute formatting for output, 168

N
named pipes introduction, 323 opening, 326, 327 troubleshooting, 341 write access, 326 writing to, 334 naming variables in C function, 219 variables in external function, 264 NE conditional tests, 45 null value and, 307 NES, 307 nested statement, 21 transaction, 283 transaction in transaction processing, 293 NEWPCODE, 74 NEWVERSION, 78 NODELAY, 326, 330, 334 nonprinting character, converting , 169 NOT, 307 conditional tests, 43

Developing UniBasic Applications

419

Index

not equal to conditional tests, 47 not greater than conditional tests, 47 not less than conditional tests, 47 NOTS, 307 null value ASCII value for, 300 assigning to a variable, 315 CASE, 41 converting, 316 determining ASCII code, 315 effects on conditional tests , 308 found when numeric required, 316 in IF/THEN/ELSE, 38 introduction to UniBasic, 299 NOT, 43 numeric calculations, 161 representing in UniBasic, 24 testing for, 36, 155, 315 NULL_VAL_ALT_KEY, 301 NUM null value and, 316 testing for data type, 156 number arithmetic operations, 159 converting and formatting, 170 locating data in array, 158 random, 164 rounding, 171 scaling, 171 numbering system, converting , 170 numeric calculation null value in, 302 numeric data conditional tests of, 44

numeric functions and the null value, 303 NUMS null value and, 316

O
object code, 59, 226 OCONV, 158, 166, 169, 182, 313 octal converting data, 173 ON ERROR triggers, 98 ON/GOSUB program control, 50 opeining a named pipe, 326 OPEN file commands, 115 non-UniData files, 114 OPENSEQ, 113 operation file-level, 294 operator arithmetic, 17, 159 Boolean, 44 precedence, 46 OR, 307 OSBREAD, 113, 323, 330 OSBWRITE, 113, 323 writing to a named pipe, 334 OSCLOSE, 113, 337 OSDELETE, 113 OSOPEN, 113, 323, 326 OSREAD, 113 OSWRITE, 113 output device, 203

420

Developing UniBasic Applications

Index

P
P, AE command, 57 packed decimal, 171 PAGE printer control, 209 terminal control commands, 205 passing arguments to C functions, 220 arguments to external functions , 264 data through a stack, 191 PCPERFORM program control commands, 50 PERFORM program control commands, 50 performance improving in transaction processing, 297 phonetic code, converting to, 169 Pick Basic, 62 PIPE_BUF, 325, 341 position, determining in array, 155 POWER null value and, 303 precision setting in UniBasic, 161 PRINT, 205, 208, 209 PRINT @, 205 PRINT ON, 209 printer output, 209 PRINTER CLOSE, 209 PRINTER OFF, 209 PRINTER ON, 209 printing in a C function, 220 in an external function, 265 page feeds, 210

report with break line, 210 right-justified data, 211 Proc user exit, 201 profile program, 84 program call interface, 223 cataloging, 73 compiling, 59 executing a transaction start within, 294 profile, 84 stopping with ABORT, 49 UniBasic called from C, 235 writing, 147 program control, 29, 49 programming in UniBasic, 51 PROMPT, 206 PWR null value and, 303 numeric function, 164

Q
Q, quitting AE, 56

R
random integer, returning, 164 range, locating data in array, 158 READ, 114 READBCK, 110 READBCKL, 110 READBCKU, 110 READFWD, 110 READFWDL, 110 READFWDU, 111

Developing UniBasic Applications

421

Index

reading named pipe, 330 records, commands for, 119 unopened files, 129 READSEQ, 113 READT, 212 Reality Basic, 62 record lock, 133, 137 selecting, 116 record ID list, 116 record mark, 24 RECORDLOCKED, 137 recoverable file system see also transaction processing CPIO format, 290 creating or converting a file , 290 introduction, 279 operation limitations, 294 RECOVERABLE keyword, 290 RedBack, 323 relational operator, 45 relational operators null value and, 302 remainder, 164 REMOVE, 158, 183, 317 command, 183 REPEAT, 36 REPLACE, 316 replacing characters in a string, 169 string value, 168 RESIZE transaction processing and, 294 RESIZET, 212 RETURN in internal subroutines, 31

introduced, 33 program control commands, 50 return code from STATUS function, 27 REWIND, 212 RND null value and, 303 numeric function, 164 rounding numbers, 171 RUN from ECL, 82 running UniBasic programs, 82 runtime version of UniData, 227

S
SADD, 303 sample program UniBasic, 387 saving select list, 116 scaling numbers, 171 SCMP null value and, 303 SDIV null value and, 303 searching dynamic array or string, 158 for data in a string or array, 157 SELECT, 117 select list clearing, 118 creating, 116 reading records from, 119 saving and retrieving, 116 SELECTINDEX, 111, 117

422

Developing UniBasic Applications

Index

selecting records, 116 SEQ, 183 and null value, 315 character conversion, 168 SEQS, 183 null value and, 316 sequential file closing, 337 commands for maintaining, 112 SETINDEX, 111, 301 null value and, 321 SETTAPE, 212 setting uninitialized variables, 84 shared lock, 134 significance, 160 SIN, 164 null value and, 303 SMUL null value and, 303 sorting null value, 301 SOUNDEX, 169 null value and, 316 SPACE, 317 space removing from a string, 169 SPACES, 317 SPLICE null value and, 317 SPOOLHELP, 58 SQRT, 164 null value and, 303 square root, 164 SSUB null value and, 303

stack clearing, 193 data, 191 starting transaction within a subroutine, 294 statement conditional, 38 label, 32 layout, 19 multiline, 20 multiple, 19 nested, 21 STATUS named pipes and, 339 RECORDLOCKED, 137 return value for nulls, 313 TRANSACTION COMMIT, 286 TRANSACTION START, 284 values returned from triggers, 105 STOP program control commands, 50 STR, 317 string converting, 168 converting data in, 168 counting substrings in, 156 determining length, 156 extracting a substring, 157 left justifying, 169 locating in array, 157, 158 replacing value, 168 value as constant, 25 string function null value and, 302, 315 STRS null value and, 317 structured programming style, 29

Developing UniBasic Applications

423

Index

subroutine CallBasic, 241, 269 executing a transaction start within, 294 program control commands, 49 types, 31 writing trigger, 99 substring converting, 169 locating in array, 157 occurrence in array elements, 156 SUBSTRINGS, 183, 317 subtraction, 159 operator precedence, 46 subvalue mark, 24 delimiters, 150 SUM, 164 SUPERCLEAR.LOCKS, 136 suppressing double spacing, 210 zero, 171 SWAP, 183 SYSTEM, 317 system information, 27 system time and date, 176

T
T.ATT, 212 TAN, 165 null value and, 303 tape drive commands, 212 terminal beeping for locked record, 135 display commands, 205 testing for active transaction, 288 testing for null value, 315 text mark, 24, 150

THEN TRANSACTION COMMIT, 285 tilde, converting nonprinting characters to, 169 time converting and formatting, 175 reporting program execution, 84 transaction nested, in transaction processing, 283, 293 TRANSACTION ABORT introduction, 287 locks, 135 TRANSACTION COMMIT inappropriate, 282 locks, 135 STATUS values, 286 transaction processing, 397 atomicity, 399 break key in, 287 called programs and subroutines, 294 consistency, 399 defining isolation levels, 404 dirty reads, 402 durability, 400 EXECUTE and, 294 file-level operations, 294 introduction, 282 isolation, 399 limitations, 294 lost update, 401 programming example, 289 programming problems, 293 report generator, 294 screen generator, 294 TRANSACTION ABORT, 287 udfile, 291 UDT.OPTIONS 35, 294 UniData SQL, 294

424

Developing UniBasic Applications

Index

unrepeatable reads, 403 with nonrecoverable files, 408 TRANSACTION START within a subroutine, 294 trigger defined, 96 return value, 100 STATUS return values, 105 troubleshooting, 106 writing UniBasic subroutines, 99, 102 TRIM, 169, 317 TRIMB, 169, 317 TRIMF, 169, 317 TRIMS, 317 troubleshooting processing named pipes, 341 transaction processing, 293

U
U lock, 133 U.S. date format, 176 U_callbas, 241, 269, 275 U_done, 221, 265 U_errout, 220, 265 U_PHANTOM, 241, 269 U_preprint, 220, 265 U_REDIRECT, 241, 269 udfile, 291 UDT.OPTIONS 12, 204 UDT.OPTIONS 18, 204 UDT.OPTIONS 35, 294 UDT.OPTIONS 4, 210 UDT.OPTIONS 5, 210 UDT.OPTIONS 65, 204 UDT.OPTIONS 7, 210 UDT.OPTIONS 88, 263

udtcallbasic, 271, 275 udtcallbasic_done, 243, 272, 275 udtcallbasic_init, 268, 275 UDTEXECUTE, 196 Uni, 181, 182 UniBasic, 183 @variables, 27 CHANGE, 181 CHAR, 181 CHARS, 181 COL1, 181 CONVERT command, 181 COUNT, 181 COUNTS, 182 DCOUNT, 182 features and program components, 15 FIELD, 182 FINDSTR, 182 GET, 182 ICONV, 182 INDEX, 182 INPUT, 182 INPUT @, 182 LEN, 182 LENS, 182 MATCH, 182 MATCHFIELD, 182 OCONV, 182 SEQ, 183 SEQS, 183 subroutine, U_callbas function, 241, 269 SUBSTRINGS, 183 SWAP, 183 UniData locking system, 133 runtime version, 227 UniData SQL

Developing UniBasic Applications

425

Index

executing from UniBasic, 197 executing in transaction processing , 294 UniData.LIB, 259, 274 UniQuery user exits, 200 unknown values, representing, 300 UNLOCK, 136 unrepeatable read, 403 unrepeatable reads, 142 UPCASE, 169 null value and, 316 UPDATE trigger, 99 update see also WRITE UPDATE_ORDER, UniBasic program, 387 upper case converting to, 169 user exit writing, 199 user number for locked records, 137 using UniBasic, sample program for, 387

naming in C function, 219 naming in external function, 264 naming in UniBasic, 25 numeric, 44 testing for null value, 316, 318 uninitialized, setting, 84 VCATALOG, 74 virtual attribute, 195 VOC file CALLC requirements, 266 E type entry, 266

W
web application development, 323 WEOF, 212 WEOFSEQ, 113 WRITE commands, 119 non-UniData files, 114 triggers, 98 WRITEONLY, 326 WRITESEQ, 113 WRITESEQF, 113 WRITET, 212 writing C program from UniBasic, 228 data to files, 123 trigger subroutines, 102 UniBasic subroutine in CallBasic, 236, 273 user exits, 199

V
validating database changes, 96 value integer, 164 mark, 150 value mark, 24 variable @variables in UniBasic, 27 converting, 168 defined, 25

X
XLATE, 317 XREF, 69

426

Developing UniBasic Applications

Index

Z
zero, suppressing, 171

Developing UniBasic Applications

427

Index

428

Developing UniBasic Applications