===================================================================== Working with Variables ===================================================================== Product: R:BASE Version: 3.1x, 4.0, 4.5 ===================================================================== Area: Logic and Manipulation Catalog: Programming in R:BASE ===================================================================== Level Products: Variables hold temporary values which cease to exist when you exit from R:BASE. Variables are known across applications unless they are explicitly removed. Variables hold column values, constants, results of calculations and parts of R:BASE commands. The value of the variable determines how it is used. A variable that contains part of an R:BASE command must be evaluated differently than a variable that contains values. There are four different types of R:BASE variables: system, dotted, ampersand and percent. Many R:BASE commands create variables and put values into the variable. Some of the most commonly used are: SET VAR, COMPUTE, SELECT, FILLIN, DIALOG and CHOOSE. Commands whose syntax diagram includes varname indicates a variable can be used in the command. System Variables System variables are those variables defined by R:BASE itself. Their names begin with a "#" character (except for SQLCODE). In general, you don't define or change the value of system variables. You usually use them in expressions to calculate other values, or for comparisons. Not all system variables are displayed with a SHOW VAR command, some remain hidden from view. R:BASE System Variables #DATE Always defined. #DATE is set to the system date of your computer. It cannot be changed except by exiting R:BASE and using the DATE command at your operating system prompt. #TIME Always defined. #TIME is set to the system time of your computer. It cannot be edited except by exiting R:BASE and using the TIME command at your operating system prompt. #PI Always defined. It is set to the value of PI to 14 decimal places, the maximum precision available with a DOUBLE data type. SQLCODE Always defined. SQLCODE is the ANSI SQL error variable. It is the only R:BASE system variable whose name does not begin with "#". Its value is set and changed as SQL commands execute. When a command is successful its value is 0. #printervar Printer variables are defined when a printer (.PRD file) is set. These variables do not display with a SHOW VAR command; however, SHOW VAR #printervar displays the printer code. For example, SHOW VAR #BOLD. Printer variables display in Reports and Label Create/modify under the Print styles menu option. You can't change the value of a printer variable using the SET VAR command. #BROWDATA Holds a value captured using the Shift-F10 key while browsing or editing data with the Info/views menu. #PAGE The page numbering variable used by Reports. You can set #PAGE using the SET VAR command. You set it to enable your report to start printing at a number other than 1. #RETURN Used by variable forms only. #RETURN holds the keystroke that was used to exit the form. Dotted Variables Dotted variables are by far the most common R:BASE variables. Most variables are used to hold values. When you use a variable in a calculation or as a comparison value, you "dot" the variable, i.e. preface its name with a period, ".". The period looks like a dot, hence the name "dotted variable." The dot tells R:BASE to use the value contained in the variable. A rule of thumb for when to "dot" a variable is to always "dot" the variable when it is on the right side of the operator. Dotting a variable basically turns it into a constant value. R:BASE looks only at the value of the variable when it is dotted. R:BASE doesn't look at the datatype, just at the value the variable contains. That's why you can have a TEXT datatype variable (result of a DIALOG command, for example) containing a value that looks like a DATE and use that variable to compare to a DATE datatype column or variable. Note that in expressions, R:BASE checks the datatype of a dotted variable. An expression is anything enclosed in parentheses. R:BASE verifies the datatype in expressions to make sure the expression is valid. You can't add an INTEGER to a TEXT for example. Dotted variables are commonly used in WHERE clauses and in calculations with other variables. For example, SET VAR vdate DATE = 5/30/93 SELECT * FROM transmaster + WHERE transdate <= .vdate selects all the records from the table Transmaster where the value in the column transdate is less than or equal to the value contained in the variable vdate. SELECT netamount, shipmethod + INTO vamount ind1, vship ind2 + FROM orders WHERE ordernum = .vorder IF vship = 'AIR' THEN SET VAR vfreight = $11.00 ELSE SET VAR vfreight = $5.00 ENDIF SET VAR vamount = + (.vamount + .vfreight) SET VAR vtax = + taxrate IN states WHERE stateabr = .vstate SET VAR vamount = + (.vamount = (.vamount * .vtax)) On the right side of the equals sign (the operator), the variable is dotted. On the left side of the operator, in the IF and the SET VAR commands, the variable is referenced by its name only, it is not dotted. When using the syntax diagrams and on-line HELP, varname means use the variable name only, .varname indicates you use a dotted variable in that position. A dotted variable can also be used any place you see value in a syntax diagram. Dotted variables are used in Form and Report expressions as they are in the SET VAR command. On the right side of the operator, dot the variable. Ampersand Variables You can't dot a variable when it contains part of a command - a table or column name, or an ORDER BY or WHERE clause. When a variable contains part of a command, its name is prefaced with an ampersand, "&", and it is called an ampersand variable. The "&" in front of the variable name tells R:BASE that the variable contains part of the command, not a value, and the contents of the variable are used when parsing the command. Don't confuse the ampersand that prefaces a variable name with the ampersand that is used to concatenate TEXT values. Ampersand variables are most often used to hold table and column names and WHERE and ORDER BY clauses. By using ampersand variables to hold column and table names, you can use the same command to select data from different tables. The CHOOSE command displays menus of available tables and columns. For example, CHOOSE vtable FROM #TABLES AT CENTER,CENTER CHOOSE vcollist FROM #COLUMNS IN &vtable + AT CENTER,CENTER CHKBOX Each time, a different table and different columns can be selected. The CHOOSE...FROM #TABLES automatically displays a menu of all user defined tables in the database. Use #TBLVIEWS to include view names in the menu. The CHOOSE...FROM #COLUMNS automatically displays a menu of all the columns in the specified table or view. Including the CHKBOX option lets the user select the columns to view. The values selected from the menus are placed into variables. The variables might look like this: R>SHOW VAR Variable = Value Type ------------------ ------------------------------ -------- #DATE = 07/22/93 DATE #TIME = 8:42:38 TIME #PI = 3.14159265358979 DOUBLE SQLCODE = 0 INTEGER vtable = employee TEXT vcollist = empid,emplname,empphone,empext TEXT The variables are then used in any command that uses a table name or column list. In syntax diagrams where you see colname or tblview you can substitute an ampersand variable that contains the column, table or view name. The variables must be used as ampersand variables to tell R:BASE they contain part of the command. For example, BROWSE &vcollist FROM &vtable To prompt for an ORDER BY clause, use the CHKSORT option instead of CHKBOX on the CHOOSE...FROM #COLUMNS. CHOOSE vorderby FROM #COLUMNS IN &vtable + AT CENTER,CENTER CHKSORT The CHKSORT option prompts for Ascending or Descending just like the R:BASE sort menus. The variable contains ASC or DESC as well as the column names. It might look like this: vorderby = emplname ASC,empfname ASC TEXT In the command, follow the keywords ORDER BY with the ampersand variable containing the columns to order by. BROWSE &vcollist FROM &vtable + ORDER BY &vorderby The easiest way to let users enter their own WHERE clause is to use the CHOOSE...FROM #WHERE. Like the CHOOSE...FROM #COLUMNS, #WHERE requires a table or view name reference. It presents a selection box that looks just like the one for Quick select off the R:BASE main menu. The variable might look like this: vwhere = empstate = 'WA' TEXT It is used in the command as an ampersand variable after the keyword WHERE: BROWSE &vcollist FROM &vtable + WHERE &vwhere ORDER BY &vorderby Here's a complete program that includes simple error checking. You need to check that both a table and a column list are selected. This program simply exits if either a table or a column list is not selected. You could write a message to the user and ask if they want to exit or start over. The program determines whether or not an ORDER BY clause and a WHERE clause were selected. You need to execute different commands if one of them was not selected. CHOOSE vtable FROM #TABLES + AT CENTER,CENTER IF vtable = '[Esc]' THEN RETURN ENDIF CHOOSE vcollist FROM #COLUMNS + IN &vtable CASCADE CHKBOX IF vcollist = '[Esc]' THEN CLS RETURN ENDIF CLS WRITE ' Table:', .vtable AT 3,5 WRITE ' Columns:', .vcollist AT 4,5 WRITE ' Choose columns to sort by...' + AT 6,5 CHOOSE vorderby FROM #COLUMNS + IN &vtable AT 8,12 CHKSORT CLS FROM 6 TO 25 IF vorderby <> '[Esc]' THEN WRITE 'Order by:', .vorderby AT 5,5 ELSE WRITE 'No sort' AT 5,5 ENDIF CHOOSE vwhere FROM #WHERE IN &vtable IF vwhere <> '[Esc]' AND + vorderby <> '[Esc]' THEN BROWSE &vcollist FROM &vtable + WHERE &vwhere ORDER BY &vorderby ELSE IF vwhere = '[Esc]' AND + vorderby <> '[Esc]' THEN BROWSE &vcollist FROM &vtable + ORDER BY &vorderby ELSE IF vwhere <> '[Esc]' AND + vorderby = '[Esc]' THEN BROWSE &vcollist FROM &vtable + WHERE &vwhere ELSE BROWSE &vcollist FROM &vtable ENDIF ENDIF ENDIF Because an ampersand variable is part of a command, it can't be used inside parentheses. Parentheses indicate expressions, expressions are parsed separately from the rest of the command. You need to include the parentheses as part of the variable value. Sub-selects and IN lists are enclosed in parentheses and you can't use an ampersand variable inside them, you need to include the entire sub-select or IN list, including parentheses, as the variable value. For example, you have a vwhere variable, vwhere = custstate = 'CA' TEXT To use it in a sub-select to view transaction information for customers from the state of California you might try: BROWSE * FROM transmaster + WHERE custid IN + (SELECT custid FROM customer + WHERE &vwhere) This command generates an error message. The ampersand variable is inside parentheses and isn't evaluated correctly. You need to put the entire sub-select into the variable. SET VAR vwhere2 = + ('(SELECT * FROM customer WHERE' + & .vwhere + ')') Everything inside the quotes is a text string. The variable concatenates a text string that is part of a command to the vwhere variable value. Notice that vwhere is used here as a dotted variable. The resulting variable, vwhere2, looks like this: vwhere2 = (SELECT * FROM customer WHERE TEXT custstate= 'CA') The command becomes: BROWSE * FROM transmaster + WHERE custid IN &vwhere2 The same is true when you select a list of values from a checkbox menu to use in an IN list. The WHERE clause, ...WHERE colname IN (&varname), returns an error. You need to use ...WHERE colname IN &varname. The variable must include the parentheses. See the article "Use SSUB on Variables that Hold Multiple Checkbox Picks" from the January/February 1992 Exchange for an example of a program that makes an IN list from checkbox menu selections (FAX server document #377). Note that ampersand variables can't be used in Form and Report expressions. Another way to use an ampersand variable is to do ad-hoc commands from an application. This uses what is often called a "fake r> prompt". It appears as if you are typing in commands at the R> prompt, but you are really typing the command into a variable and then executing the command using an ampersand variable. Here's code that does this. SET VAR vcmd TEXT = 'a' WHILE vcmd IS NOT NULL THEN FILLIN vcmd=78,10,600 USING 'r>' &vcmd ENDWH Using a FILLIN box lets you enter commands longer than 79 characters. You don't enter the + for continuation, and won't see the "+>" prompt when the line continues. Just type in the command letting the words wrap. You can even use the F3 key to view and select column and table names. Percent Variables A percent variable is created when a value is passed to a command file from the R> prompt or another command file. Percent variables are created and named by R:BASE. A percent variable is named %n, where n is a number from 1 to 9. You can pass up to nine parameters. The first parameter passed is named %1, the second %2 and so on. Percent variables are commonly used with library routines, a single routine that is used by many different application. Percent variables are commonly referenced as just %n, but the actual name is %n-m, where m is the run level, for example %1-0. From the R> prompt, m is 0, from the first level, m is 1 and so on. Referring to a percent variable as %1 only, automatically refers to the first parameter passed to that particular command file. If you are passing parameters at multiple levels, refer to the variable explicitly, e.g. %1-2. You may have variables, %1-0, %1-2 and %1-3, there is no %1-1 variable if no USING parameter was used at run level 1. Look at this example with multiple levels of passing parameters. R>RUN test.cmd USING 'one', 'two' SHOW VAR Variable = Value Type ------------------ ------------------------------ -------- #DATE = 07/22/93 DATE #TIME = 10:29:27 TIME #PI = 3.14159265358979 DOUBLE SQLCODE = 0 INTEGER %1-0 = one TEXT %2-0 = two TEXT -- two parameters were passed, they are names %1-0 and %1-2 -- from within this file, RUN another file and pass parameters RUN test1.cmd USING 'three','four' -- run level 1, the first nested RUN SET VAR vlevel1 = .%1 SHOW VAR Variable = Value Type ------------------ ------------------------------ -------- #DATE = 07/22/93 DATE #TIME = 10:29:28 TIME #PI = 3.14159265358979 DOUBLE SQLCODE = 0 INTEGER %1-0 = one TEXT %2-0 = two TEXT %1-1 = three TEXT %2-1 = four TEXT vlevel1 = three TEXT -- the parameters passed to file test1 are named with a "-1" -- indicating -- the run level. Notice that setting a variable to just %1 -- sets it to the first parameter passed at this run level. -- From within this file, RUN another file, but don't pass parameters. RUN test2.cmd -- run level 2, the second nested RUN SET VAR vlevel2_1 =.%1 SET VAR vlevel2_2 = .%1-1 SHOW VAR Variable = Value Type ------------------ ------------------------------ -------- #DATE = 07/22/93 DATE #TIME = 10:29:30 TIME #PI = 3.14159265358979 DOUBLE SQLCODE = 0 INTEGER %1-0 = one TEXT %2-0 = two TEXT %1-1 = three TEXT %2-1 = four TEXT vlevel1 = three TEXT vlevel2_1 = .%1 TEXT vlevel2_2 = three TEXT -- no parameters are passed at this level so there are no percent -- variables created. Setting a variable to just %1 finds no value -- because no parameter was passed at this level. The variable takes -- on the literal text value ".%1". -- RUN another file and pass parameters. RUN test3.cmd using 'five','six' -- run level 3, the third nested RUN SHOW VAR Variable = Value Type ------------------ ------------------------------ -------- #DATE = 07/22/93 DATE #TIME = 10:29:32 TIME #PI = 3.14159265358979 DOUBLE SQLCODE = 0 INTEGER %1-0 = one TEXT %2-0 = two TEXT %1-1 = three TEXT %2-1 = four TEXT vlevel1 = three TEXT vlevel2_1 = .%1 TEXT vlevel2_2 = three TEXT %1-3 = five TEXT %2-3 = six TEXT -- Now there are percent variables named "-3", for run level 3. -- We are actually four RUNs deep, but this is the third nested RUN -- command. The first run, from the R> prompt, is named "-0" and -- is not nested. Parameters pass values, they do not pass data types. You cannot pre-type a percent variable. It is recommended to set a percent variable to a defined variable of the appropriate datatype in the command file and then use that variable in subsequent commands rather than using the percent variable itself. Application developer Dennis McGrath uses a command file with passed parameters to manipulate printer variables. Sometimes you want to send printer control codes to the printer directly from an application, not from within a report. You can't define a printer control code at the R> prompt or in an application using the "<" and ">" to enclose the decimal codes. That format is specific to reports, only the report knows to interpret and translate the codes. At the R> prompt or in an application, printer control codes are defined using the CHAR function. For example, to define Landscape mode for an HP Laser Jet printer, define this variable, SET VAR vland = + (CHAR(27) + CHAR(38) + CHAR(108) + + CHAR(49) + CHAR(79)) The SHOW VAR command shows variable vland and contains the ASCII character equivalents of the decimal numbers, vland = <-&l1O. The control code is sent to the printer by using the SHOW VAR vland or WRITE .vland command. You may want to do this to set the printer for printing labels in condensed print, for example. Labels don't have report header and footer lines to set the codes just once, you need to set it every line. It is easier to send the code to the printer, then print the labels, then reset the printer. Use these commands, OUTPUT PRINTER WRITE .vland LBLPRINT emplabel WRITE .vreset OUTPUT SCREEN The problem with defining printer control variables in an application is that you need to know the decimal codes for the printer, and the values are hard coded into the variable definition. They don't change if the printer changes. Using Dennis' program, you can set variables from the codes defined in the specified printer file, printer.PRD. The codes are not hard coded for a particular printer, they change as the printer changes. Two parameters are passed to the program, the name of the printer control variable to be converted (as a dotted variable) and the name of the resulting variable (as a TEXT string). The program reads the decimal codes from the system printer control variable and converts them using the CHAR function. The resulting variable can be used to send the specified printer control code to the printer from an application. There must a printer set to use this program successfully. -- PRINT40.CMD -- use this file with R:BASE 3.x-4.0 -- It takes the data from a system printer variable and places the -- printer control characters into a variable of your choice -- for use where you wish to send printer control characters from -- a command file without the use of a report -- SYNTAX: RUN print40.cmd USING .#printvar 'varname' -- SAMPLE: RUN print40.cmd USING .#Bold 'vboldon' -- The contents of #Bold are converted and placed into vboldon -- initialize variables SET VAR vtempint INT, vcount INT, vtemp TEXT, vcode_conv TEXT = NULL -- set percent variables to program variables SET VAR vcode TEXT = .%1, vprntvar TEXT = .%2 -- get the length of the printer control code and -- strip leading < and trailing > SET VAR vtempint = (SLOC(.vcode,'>') - 2) SET VAR vcode = (SGET(.vcode,.vtempint,2)) -- ssub function uses space delimiter with negative number to -- separate items by blank instead of comma SET VAR vcount = -1 WHILE #PI > 0.0 THEN -- get one code string from vcode, the following commands are -- repeated until no more codes are found, vtemp IS NULL SET VAR vtemp = (SSUB(.vcode,.vcount)) IF vtemp IS NULL THEN BREAK ENDIF -- convert the number from TEXT to INTEGER for the CHAR function -- and add the character to the code string SET VAR vtempint = (INT(.vtemp)) SET VAR vcode_conv = (.vcode_conv + CHAR(.vtempint)) -- decrement the count to get the next code SET VAR vcount = (.vcount - 1) ENDWH -- place the completed printer control code into the result variable -- an ampersand variable is used because vprntvar contains a variable -- name SET VAR &vprntvar = .vcode_conv RETURN -- PRINT45.CMD -- use this file with R:BASE 4.5 -- less code is needed because TEXT functions can be nested. -- It takes the data from a system printer variable and places the -- printer control characters into a variable of your choice -- for use where you wish to send printer control characters from -- a command file without the use of a report -- SYNTAX: RUN print45.cmd USING .#printvar 'varname' -- SAMPLE: RUN print45.cmd USING .#Bold 'vboldon' -- The contents of #Bold are converted and placed into vboldon -- initialize variables SET VAR vcount INT, vtemp INT, vcode_conv = NULL -- set percent variables to program variables SET VAR vcode TEXT = .%1, vprntvar TEXT = .%2 -- get the length of the printer control code and -- strip leading < and trailing > SET VAR vcode = (SGET(.vcode,(SLOC(.vcode,'>') - 2),2)) -- ssub function uses space delimiter with negative number to -- separate items by blank instead of comma SET VAR vcount = -1 WHILE #PI > 0.0 THEN -- get one code string from vcode, the following commands are -- repeated until no more codes are found, vtemp IS NULL SET VAR vtemp = (INT (SSUB(.vcode,.vcount)) ) IF vtemp IS NULL THEN BREAK ENDIF -- convert the number from TEXT to INTEGER for the CHAR function -- and add the character to the code string SET VAR vcode_conv = (.vcode_conv + CHAR(.vtemp)) -- decrement the count to get the next code SET VAR vcount = (.vcount - 1) ENDWH -- place the completed printer control code into the result variable -- an ampersand variable is used because vprntvar contains a variable -- name SET VAR &vprntvar = .vcode_conv RETURN Clearing Variables Variables are cleared by using the CLEAR VARIABLE command. Individual variables can be cleared or all variables can be cleared. The CLEAR ALL VAR command clears all user defined variables, but only clears the system variable #PAGE. All other system variables are still there, including printer variables. To completely clear out the memory allocated for variables, use the command, CLEAR ALL VAR NOW. A fourth argument to the CLEAR ALL VAR command clears all the variable storage blocks, including all printer control code variables and other non-permanent system variables, and reallocates the blocks with just the permanent system variables, #DATE, #TIME, #PI and SQLCODE.