The program in this chapter allows the user to view and change a list of records displayed on a form. As each record in the program array is added, updated, or deleted, the program logic makes corresponding changes in the rows of the corresponding database table.
This program uses a form and a screen array to allow the user to view and change multiple records of a program array at once. The INPUT ARRAY statement and its control blocks are used by the program to control and monitor the changes made by the user to the records. As each record in the program array is Added, Updated, or Deleted, the program logic makes corresponding changes in the rows of the customer database table.
Figure 1. INPUT ARRAY example on a Windows™ platform
The INPUT ARRAY statement supports data entry by users into a screen array, and stores the entered data in a program array of records. During the INPUT ARRAY execution, the user can edit or delete existing records, insert new records, and move inside the list of records. The program can then use the INSERT, DELETE or UPDATE SQL statements to modify the appropriate database tables. The INPUT ARRAY statement does not terminate until the user validates or cancels the dialog.
INPUT ARRAY cust_arr WITHOUT DEFAULTS FROM sa_cust.* ATTRIBUTES (UNBUFFERED)
The example INPUT ARRAY statement binds the screen array fields in sa_cust to the member records of the program array cust_arr. The number of variables in each record of the program array must be the same as the number of fields in each screen record (that is, in a single row of the screen array). Each mapped variable must have the same data type or a compatible data type as the corresponding field.
The WITHOUT DEFAULTS clause instructs the INPUT ARRAY statement to use and display the rows currently stored in the program array. Without this clause, the INPUT ARRAY would start with an empty list.
When creating a new row with the insert or append action, the REQUIRED attribute is always taken into account by INPUT ARRAY, even if the WITHOUT DEFAULTS clause is used.
The WITHOUT DEFAULTS clause prevents BDL from displaying any default values that have been defined for form fields. You must use this clause if you want to see the values of the program array.
As in the INPUT statement, when the UNBUFFERED attribute is used, the INPUT ARRAY statement is sensitive to program variable changes.
If you need to display new data during the execution, use the UNBUFFERED attribute and assign the values to the program array row; the runtime system will automatically display the values to the screen. This sensitivity applies to ON ACTION control blocks, as well: Before executing the code corresponding to the invoked action, the content of the field is converted and assigned to the corresponding program variable.
INPUT ARRAY supports the COUNT and MAXCOUNT attributes to manage program arrays.
Your program can control and monitor the changes made by the user by using control blocks with the INPUT ARRAY statement.
The control blocks that are used in the example program are:
For a more detailed explanation of the priority of control blocks see INPUT control blocks in the Genero Business Development Language User Guide.
The language provides several built-in functions to use in an INPUT ARRAY statement. The example program uses the ARR_CURR function to tell which array element is being changed. This function returns the row number within the program array that is displayed in the current line of a screen array.
There are some predefined actions that are specific to the INPUT ARRAY statement, to handle the insertion and deletion of rows in the screen array automatically.
As with the predefined actions accept and cancel actions discussed in Chapter 4, if your form specification does not contain action views for these actions, default action views (buttons on the form) are automatically created. Control attributes of the INPUT ARRAY statement allow you to prevent the creation of these actions and their accompanying buttons.
The arrayinput program in chapter 8 uses the INPUT ARRAY statement with a Screen Array to allow the user to modify data in the customer table.
The custallform.per form specification file displays multiple records at once, and is similar to the form used in chapter 7. The item type of field f6, containing the state values, has been changed to COMBOBOX to provide the user with a dropdown list when data is being entered.
01 SCHEMA custdemo 02 03 LAYOUT 04 TABLE 05 { 06 Id Name .. zip_code Contact Phone 07 [f01][f02 ] [f07 ][f08 ][f09 ] 08 [f01][f02 ] [f07 ][f08 ][f09 ] 09 [f01][f02 ] [f07 ][f08 ][f09 ] 10 [f01][f02 ] [f07 ][f08 ][f09 ] 11 [f01][f02 ] [f07 ][f08 ][f09 ] 12 [f01][f02 ] [f07 ][f08 ][f09 ] 13 } 14 END 15 END 16 17 TABLES 18 customer 19 END 20 21 ATTRIBUTES 22 EDIT f01 = customer.store_num, REQUIRED; 23 EDIT f02 = customer.store_name, REQUIRED; 24 EDIT f03 = customer.addr; 25 EDIT f04 = customer.addr2; 26 EDIT f05 = customer.city; 27 COMBOBOX f6 = customer.state, ITEMS = ("IA", "IL", "WI"); 28 EDIT f07 = customer.zip_code; 29 EDIT f08 = customer.contact_name; 30 EDIT f09 = customer.phone; 31 END 32 33 INSTRUCTIONS 34 SCREEN RECORD sa_cust (customer.*); 35 END
The single module program custall.4gl allows the user to update the customer table using a form that displays multiple records at once.
01 SCHEMA custdemo 02 03 DEFINE cust_arr DYNAMIC ARRAY OF RECORD 04 store_num LIKE customer.store_num, 05 store_name LIKE customer.store_name, 06 addr LIKE customer.addr, 07 addr2 LIKE customer.addr2, 08 city LIKE customer.city, 09 state LIKE customer.state, 10 zip_code LIKE customer.zip_code, 11 contact_name LIKE customer.contact_name, 12 phone LIKE customer.phone 13 END RECORD 14 15 16MAIN 17 DEFINE idx SMALLINT 18 19 DEFER INTERRUPT 20 CONNECT TO "custdemo" 21 CLOSE WINDOW SCREEN 22 OPEN WINDOW w3 WITH FORM "custallform" 23 24 CALL load_custall() RETURNING idx 25 IF idx > 0 THEN 26 CALL inparr_custall() 27 END IF 28 29 CLOSE WINDOW w3 30 DISCONNECT CURRENT 31 32 END MAIN
This function loads the program array with rows from the customer database table.
The logic to load the rows is identical to that in Chapter 7. Although this program loads all the rows from the customer table, the program could be written to allow the user to query first, for a subset of the rows. A query-by-example, as illustrated in chapter 4, can also be implemented using a form containing a screen array such as manycust.
01 FUNCTION load_custall() 02 DEFINE cust_rec RECORD LIKE customer.* 03 04 05 DECLARE custlist_curs CURSOR FOR 06 SELECT store_num, 07 store_name, 08 addr, 09 addr2, 10 city, 11 state, 12 zip_code, 13 contact_name, 14 phone 15 FROM customer 16 ORDER BY store_num 17 18 19 CALL cust_arr.clear() 20 FOREACH custlist_curs INTO cust_rec.* 21 CALL cust_arr.appendElement() 22 LET cust_arr[cust_arr.getLength()].* = cust_rec.* 23 END FOREACH 24 25 IF (cust_arr.getLength() == 0) THEN 26 DISPLAY "No rows loaded." 27 END IF 28 29 RETURN cust_arr.getLength() 30 31END FUNCTION
This is the primary function of the program, driving the logic for inserting, deleting, and changing rows in the customer database table.
Each time a row in the array on the form is added, deleted, or changed, the values from the corresponding row in the program array are used to update the customer database table. The variable opflag is used by the program to indicate the status of the current operation.
The value of opflag is tested in an AFTER ROW control block to determine whether an SQL INSERT or SQL UPDATE of the database table is performed.
This example illustrates how the order of execution of the control blocks is used by the program to set the opflag variable appropriately:
01 FUNCTION inparr_custall(idx) 02 03 DEFINE curr_pa SMALLINT, 04 opflag CHAR(1) 05 06 INPUT ARRAY cust_arr WITHOUT DEFAULTS 07 FROM sa_cust.* 08 ATTRIBUTES (UNBUFFERED) 09 10 BEFORE INPUT 11 MESSAGE "OK exits/" || 12 "Cancel exits & cancels current operation" 13 14 BEFORE ROW 15 LET curr_pa = ARR_CURR() 16 LET opflag = "N" 17 18 BEFORE INSERT 19 LET opflag = "T" 20 21 AFTER INSERT 22 LET opflag = "I" 23 24 BEFORE DELETE 25 IF NOT (delete_cust(curr_pa)) THEN 26 CANCEL DELETE 27 END IF 28 29 ON ROW CHANGE 30 IF (opflag <> "I") THEN 31 LET opflag = "U" 32 END IF 33 34 BEFORE FIELD store_num 35 IF (opflag <> "T") THEN 36 NEXT FIELD store_name 37 END IF 38 39 ON CHANGE store_num 40 IF (opflag = "T") THEN 41 IF NOT store_num_ok(curr_pa) THEN 42 MESSAGE "Store already exists" 43 LET cust_arr[curr_pa].store_num = NULL 44 NEXT FIELD store_num 45 END IF 46 END IF 47 48 AFTER ROW 49 IF (INT_FLAG) THEN EXIT INPUT END IF 50 CASE 51 WHEN opflag = "I" 52 CALL insert_cust(curr_pa) 53 WHEN opflag = "U" 54 CALL update_cust(curr_pa) 55 END CASE 56 57 END INPUT 58 59 IF (INT_FLAG) THEN 60 LET INT_FLAG = FALSE 61 END IF 62 63 END FUNCTION -- inparr_custall
When a new record is being inserted into the program array, this function verifies that the store number does not already exist in the customer database table. The logic in this function is virtually identical to that used in Chapter 5.
01 FUNCTION store_num_ok(idx) 02 DEFINE idx SMALLINT, 03 checknum LIKE customer.store_num, 04 cont_ok SMALLINT 05 06 LET cont_ok= FALSE 07 WHENEVER ERROR CONTINUE 08 SELECT store_num INTO checknum 09 FROM customer 10 WHERE store_num = 11 cust_arr[idx].store_num 12 WHENEVER ERROR STOP 13 IF (SQLCA.SQLCODE = NOTFOUND) THEN 14 LET cont_ok = TRUE 15 ELSE 16 LET cont_ok = FALSE 17 IF (SQLCA.SQLCODE = 0) THEN 18 MESSAGE "Store Number already exists." 19 ELSE 20 ERROR SQLERRMESSAGE 21 END IF 22 END IF 23 24 RETURN cont_ok 25 26 END FUNCTION
This function inserts a new row into the customer database table.
01 FUNCTION insert_cust(idx) 02 DEFINE idx SMALLINT 03 04 WHENEVER ERROR CONTINUE 05 INSERT INTO customer 06 (store_num, 07 store_name, 08 addr, 09 addr2, 10 city, 11 state, 12 zip_code, 13 contact_name, 14 phone) 15 VALUES (cust_arr[idx].* ) 16 WHENEVER ERROR STOP 17 18 IF (SQLCA.SQLCODE = 0) THEN 19 MESSAGE "Store added" 20 ELSE 21 ERROR SQLERRMESSAGE 22 END IF 23 24 END FUNCTION
This function updates a row in the customer database table. The functionality is very simple for illustration purposes, but it could be enhanced with additional error checking routines similar to the example in chapter 6.
01 FUNCTION update_cust(idx) 02 DEFINE idx SMALLINT 03 04 WHENEVER ERROR CONTINUE 05 UPDATE customer 06 SET 07 store_name = cust_arr[idx].store_name, 08 addr = cust_arr[idx].addr, 09 addr2 = cust_arr[idx].addr2, 10 city = cust_arr[idx].city, 11 state = cust_arr[idx].state, 12 zip_code = cust_arr[idx].zip_code, 13 contact_name = cust_arr[idx].contact_name, 14 phone = cust_arr[idx].phone 15 WHERE store_num = cust_arr[idx].store_num 16 WHENEVER ERROR STOP 17 18 IF (SQLCA.SQLCODE = 0) THEN 19 MESSAGE "Dealer updated." 20 ELSE 21 ERROR SQLERRMESSAGE 22 END IF 23 24 END FUNCTION
This function deletes a row from the customer database table. A modal Menu similar to that illustrated in Chapter 6 is used to verify that the user wants to delete the row.
01 FUNCTION delete_cust(idx) 02 DEFINE idx SMALLINT, 03 del_ok SMALLINT 04 05 LET del_ok = FALSE 06 07 MENU "Delete" ATTRIBUTES (STYLE="dialog", 08 COMMENT="Delete this row?") 09 COMMAND "OK" 10 LET del_ok = TRUE 11 EXIT MENU 12 COMMAND "Cancel" 13 LET del_ok = FALSE 14 EXIT MENU 15 END MENU 16 17 IF del_ok = TRUE THEN 18 WHENEVER ERROR CONTINUE 20 DELETE FROM customer 21 WHERE store_num = cust_arr[idx].store_num 22 WHENEVER ERROR STOP 23 24 IF (SQLCA.SQLCODE = 0) THEN 25 LET del_ok = TRUE 26 MESSAGE "Dealer deleted." 27 ELSE 28 LET del_ok = FALSE 29 ERROR SQLERRMESSAGE 30 END IF 31 END IF 32 33 RETURN del_ok 34 35 END FUNCTION