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.
Contents
Contents
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
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
Contents
When to Use Locking Commands ..........................................................140 Programming Problems ..........................................................................142 Causes ...............................................................................................142 Minimizing Problems .......................................................................142 Locking Example ....................................................................................144
Contents
Modified Functions and Commands .................................................181 Single-Byte Functions .......................................................................183 Multibyte Functions ..........................................................................184
Contents
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
10
Contents
OSCLOSE ...............................................................................................337 STATUS Function Return Values ..........................................................339 INMAT ...................................................................................................340 Syntax ...............................................................................................340 Description ........................................................................................340 Troubleshooting ......................................................................................341 Example ..................................................................................................342
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
Index
12
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.
13
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
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
15
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.
16
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.
17
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
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
19
Reminder
Programs are easier to follow if you place a single command on each line.
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
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
21
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
23
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
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.
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.
25
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
@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.
27
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
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.
29
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
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.
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.
31
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.
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
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.
33
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
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)
35
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
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
37
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 ...
38
Conditional Tests
Type 3
Example IF confirmed = "Y" THEN INFOMESSAGE = ... GOSUB DISPLAY_INFO END ELSE GOSUB DISPLAY_ERR 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.
39
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
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.
41
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
Conditional Tests
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
43
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
Conditional Tests
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.
45
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
Conditional Tests
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
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
47
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
EXECUTESQL EXIT
49
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
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.
51
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
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.
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.
53
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.
Screen Example
*--: i 001=
54
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.
Screen Example
004= *--: <-- line number prompt <-- is replaced by command mode prompt
55
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
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
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)
57
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
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.
59
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
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)
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.
61
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
62
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
63
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
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.
65
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
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
67
-----^ 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.
68
69
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
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
71
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
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
73
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
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.
75
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
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.
LOCAL
77
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)
Reminder
You can execute a cataloged program from the ECL prompt or from any UniBasic program.
78
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 :
79
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
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.
81
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.
82
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
83
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.
(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.
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
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
85
index
%time
self descendents
[1] [2]
100.0
[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
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
87
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
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
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]
89
The next table describes the second line of the preceding report segment. Data
[3]
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
0.02
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
90
Data
1/1
Column called/total
called child
/users/ud_52/sys/CTLG/d/DISPLAY_MESSAGE.
child index
The name of this child (called program). The index number for
/users/ud_52/sys/CTLG/d/ DISPLAY_MESSAGE.
91
92
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.
93
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
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.
95
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.
96
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.
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.
97
98
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.
99
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.
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
Database Triggers
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
101
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)
Program Example
SUBROUTINE TRIG1(exec.stat,dict.flag,trig.name,rec.id.expr,rec.val) exec.stat=5 RETURN
102
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
Tip
You can call an external C routine from the UniBasic subroutine or function that is called from a trigger.
103
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
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)
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
105
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. :
Note
Most UniBasic commands and some ECL commands, within a trigger or outside a trigger, set the value of STATUS.
106
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?
107
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
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
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.
Tip
Create an index for @ID to speed access to data records.
109
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
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
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
READBCK
READFWDL
111
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
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
113
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
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
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.
115
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
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.
117
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
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
119
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.
120
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
121
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)
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
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 = ''
123
MATWRITEU
124
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).
125
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
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.
127
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
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.
129
Program Example
record.ret = OCONV("912","TORDERS;V;;2") PRINT "Record retrieved: ":record.ret END
Screen Example
Record retrieved: 12:30PM
130
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.
131
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
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.
133
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
Note
For more information about using transactions to ensure database consistency, see Chapter 9 UniBasic Transaction Processing.
135
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
Locking Commands
137
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
138
Command READFWDL RECORDLOCKL WRITE WRITEU WRITEVU MATWRITE MATWRITEU DELETE DELETEU
Ignores
Sets Exclusive
Sets Shared X X
Releases
139
When to Use
Action
Files
Dynamic Arrays
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
WRITE
WRITEV
MATWRITE
FILELOCK
READU RECORDLOCKU
READVU
MATREADU
WRITEU
WRITEVU
MATWRITEU
140
When to Use
Action
Files
Dynamic Arrays
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.
READL
Release locks.
FILEUNLOCK
RELEASE
RELEASE
RELEASE
141
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
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.
143
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
Locking Example
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
145
146
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.
147
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
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.
149
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.
150
UniData Arrays
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 < >)
151
Searches the entire array. Can find the nth occurrence of a string. Assumes the records are sorted and justified.
FIND vs. LOCATE For the syntax and use of these commands, see the UniBasic Commands Reference manual.
152
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.
153
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.
154
UniData Arrays
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.
Type
This section describes functions that qualify the data.
155
156
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
157
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.
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
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
159
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.
-=
*=
/=
:=
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
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.
161
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
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
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)
163
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
Function TAN
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.
165
Before getting into the types of data, you should understand the ICONV and OCONV functions.
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
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]? :
167
EBCDIC
CHAR SEQ
168
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
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.
169
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
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
For the syntax and use of these commands, see theUniBasic Commands Reference.
171
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
172
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
To
Hexadecimal
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.
173
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
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
Screen Example
Convert hex to ASCII character. Enter hex number to convert: ?64 hex 64 = ASCII character d
175
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.
176
UDT.OPTIONS
You can invalidate all date input of less than six digits by setting UDT.OPTIONS 82 on.
177
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
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
179
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
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.
181
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)
MATCHFIELD OCONV
182
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)
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
183
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)
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
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.
185
186
This chapter introduces the concepts and commands that enable you to write a UniBasic program that communicates with external software and hardware.
187
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
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.
189
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
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.
191
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
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.
193
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.)
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
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.
195
UDTEXECUTE
Program Example
EXECUTE "LIST.READU"
196
Program Example
EXECUTE "SSELECT CUSTOMER WITH STATE='CO' BY ZIP"
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.
197
198
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.
199
200
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.
201
202
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
203
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
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.
205
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
Reminder
Because @ is a function, it must be used within a statement that contains a command, such as PRINT @ or DISPLAY @.
207
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
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.
209
9 25
EXECUTE, PERFORM UniQuery BREAK.ON keyword OCONV HEADING, @ function HEADING, FOOTING
29 32 34
210
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
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
64
FOOTING
UDT.OPTIONS
For detailed information about UDT.OPTIONS, see the UDT.OPTIONS Commands Reference.
211
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
212
Screen Example
PCPERFORM 'pwd' CAPTURING MY.LOCATION
213
214
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.
215
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
217
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
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.
219
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.
220
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 }
*/
221
For our example, you enter the following commands at the UNIX shell prompt:
Screen Example
$ cc -c c_example.c
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
223
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
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.
225
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
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]
227
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.
228
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
Screen Example
:RUN BP CDEMO Enter an environment variable name (such as PATH or TERM)
229
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
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
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 */ {
231
char int
/* * 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
/* * 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
* 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
233
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
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
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.
235
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.
236
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
237
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
/* 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.
239
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
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)
241
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
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.
243
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)
244
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
245
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
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
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 */
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.
247
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
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
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
249
-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
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 */
251
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
# # # #
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
252
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 $<
Screen Example
typedef struct { char *str; int len; } bstring;
253
< 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
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
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.
255
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
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
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
257
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.
258
Note
For more information about CALLC, see Administering UniData on Windows NT or Windows 2000.
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.
259
Note
For information about linking code into a DLL, see the documentation for your software development environment.
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.
260
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
261
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:
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
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.
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.
263
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.
264
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.
265
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.
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
. . . 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.
267
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.
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
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)
269
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
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.
271
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
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.
273
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 :
274
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 */
275
#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
/* 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.
277
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
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.
279
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
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
281
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.
282
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.
283
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
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.
285
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
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.
287
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
288
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."
289
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).
290
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.
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.
291
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
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.
293
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
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."
295
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
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.
297
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
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.
299
Tip
To determine which ASCII value represents the null value on your system, use the UniBasic command SEQ(@NULL).
300
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.
301
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).
302
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.
303
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
Screen Example
100 + @NULL = null value SIN(@NULL) = null value 99/@NULL = null value 33/99 = 0.3333
Note
UniBasic programs containing @NULL will not compile when null value handling is turned off.
Within these statements, you can nest additional conditions using the following keywords: WHILE UNTIL
305
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
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
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.
307
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
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.
309
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
Screen Example
1b. NOT(NULL) IS FALSE
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
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)
Screen Example
3b. NULL EQ NULL IS FALSE NULL EQ NULL evaluates to 0
Program Example
X = @NULL IF (1 AND X) THEN
311
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)
Screen Example
4b. 1 AND NULL IS FALSE 1 AND NULL evaluates to
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)
Screen Example
5a. 1 OR NULL IS TRUE 1 OR NULL evaluates to 1
312
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.
313
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."
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
Screen Example
STATUS FOR ICONV IS 2
315
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
Commands/Functions IF/THEN/ELSE, CASE, WHILE, UNTIL Note - For more information see The Null Value in Conditional Tests earlier in this chapter.
: (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)
317
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
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
319
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
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
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
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
321
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
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.
323
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
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
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.
325
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
OSOPEN
Reminder
If OSOPEN against a named pipe is successful, the file pointer is set to the beginning of the file.
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
327
328
OSOPEN
329
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.
330
OSBREAD
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
331
2 3
Is the pipe open in the opposite mode? What is the UniBasic STATUS function setting after this operation?
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
OSBREAD
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.
333
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.
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
OSBWRITE
AT no no 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.
no
no
yes
yes
enough space available in the pipe to receive the data? is the UniBasic STATUS function return value after this operation?
2What
335
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
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
337
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
339
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
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 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.
341
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
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.'
343
344
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.
345
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
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
347
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
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
349
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
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()
351
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
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
353
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
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
355
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
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.
357
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
Submitting a Request
Status
359
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
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
361
The following table describes the status of each return code. Return Code 0 1 Success. Failed to start logging. Return Code Status Status
362
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.
363
364
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.
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.
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
365
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
366
Definition
367
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
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
369
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
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
371
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
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
373
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
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
375
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
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
377
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
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().
379
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
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
381
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
382
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
383
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
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
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
385
The following table describes the status of each return code. Return Code 0 1 Success. Failed to start logging. Status
386
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.
387
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
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>
389
* 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
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
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
391
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
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."
393
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
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
395
396
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.
397
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
398
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.
399
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
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.
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.
401
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
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.
403
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.
404
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.
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()
405
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
Transaction Isolation
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()
407
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
Index
Z
zero, suppressing, 171
427
Index
428