Tutorial Chapter 8: Array Input
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.
The INPUT ARRAY statement
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.
WITHOUT DEFAULTS clause
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.
The UNBUFFERED attribute
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.
COUNT and MAXCOUNT attributes
INPUT ARRAY
supports the COUNT
and
MAXCOUNT
attributes to manage program arrays.
- The
COUNT
attribute ofINPUT ARRAY
defines the number of valid rows in the program array to be displayed as default rows.- When using a static array, if you do not use the
COUNT
attribute, the runtime system cannot determine how much data to display, so the screen array remains empty. - When using a dynamic array, the
COUNT
attribute is ignored: The number of elements in the dynamic array is used.
- When using a static array, if you do not use the
- The
MAXCOUNT
attribute defines the maximum number of data rows that can be entered in the program array. In a dynamic array, the user can enter an infinite number of rows if theMAXCOUNT
attribute is not set.
Control Blocks
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:
- The
BEFORE INPUT
block - executed one time, before the runtime system gives control to the user. You can implement initialization in this block. - The
BEFORE ROW
block - executed each time the user moves to another row, after the destination row is made the current one. - The
ON ROW CHANGE
block - executed when the user moves to another row after modifications have been made to the current row. - The
ON CHANGE
<fieldname> block - executed when the cursor leaves a specified field and the value was changed by the user after the field got the focus. - The
BEFORE INSERT
block - executed each time the user inserts a new row in the array, before the new row is created and made the current one. - The
AFTER INSERT
block - executed each time the user inserts a new row in the array, after the new row is created. You can cancel the insert operation with theCANCEL INSERT
keywords. - The
BEFORE DELETE
block - executed each time the user deletes a row from the array, before the row is removed from the list. You can cancel the delete operation with theCANCEL DELETE
keywords. - The
AFTER ROW
block - executed each time the user moves to another row, before the current row is left. This trigger can also be executed in other situations, such as when you delete a row, or when the user inserts a new row.
For a more detailed explanation of the priority of control blocks see INPUT control blocks in the Genero Business Development Language User Guide.
Built-in Functions - ARR_CURR
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.
Predefined actions
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.
- The
insert
action inserts a new row before current row. When the user has filled this record, BDL inserts the data into the program array. - The
delete
action deletes the current record from the display of the screen array and from the program array, and redraws the screen array so that the deleted record is no longer shown. - The
append
action adds a new row at the end of the list. When the user has filled this record, BDL inserts the data into the program array.
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.
Example: Using a Screen Array to modify Data
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 Form Specification File
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 Main block
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
16
MAIN
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
- Lines
03
thru13
define a dynamic arraycust_arr
having the same structure as thecustomer
table. The array is modular is scope. - Line
17
defines a local variableidx
, to hold the returned value from theload_custall
function. - Line
20
connects to thecustdemo
database. - Line
22
opens a window with the formmanycust
. This form contains a screen arraysa_cust
which is referenced in the program. - Line
24
thru27
call the functionload_custall
to load the array, which returns the index of the array. If the load was successful (the returned index is greater than 0) the functioninparr_custall
is called. This function contains the logic for the Input/Update/Delete of rows. - Line
29
closes the window. - Line
30
disconnects from the database.
Function load_custall
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
.
load_custall
(custall.4gl):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
31
END FUNCTION
- Line
02
defines a local record variable,cust_rec
, to hold the rows fetched inFOREACH
. - Lines
05
thru16
declare the cursorcustlist_curs
to retrieve the rows from thecustomer
table. - Lines
20
thru23
retrieve the rows from the result set into the program array. - Lines
25
thru27
If the array is empty, we display a warning message. - Line
29
returns the number of rows to theMAIN
function.
Function inparr_custall
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.
- N - no action; set in the
BEFORE ROW
control block; this will subsequently be changed if an insert or update of a row in the array is performed. - T - temporary; set in the
BEFORE INSERT
control block; indicates that an insert of a new row has been started. - I - insert; set in the
AFTER INSERT
control block; indicates that the insert of the new row was completed. - U - update; set in the
ON ROW CHANGE
control block; indicates that a change has been made to an existing row.
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:
inparr_custall
(custall.4gl):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
- Line
03
defines the variablecurr_pa
, to hold the index number of the current record in the program array. - Line
04
defines the variableopflag
, to indicate whether the operation being performed on a record is an Insert ("I") or an Update ("U"). - Lines
06
thru57
contain theINPUT ARRAY
statement, associating the program arraycust_arr
with thesa_cust
screen array on the form. The attributeWITHOUT DEFAULTS
is used to use and display existing records of the program array. TheUNBUFFERED
attribute insures that the program array the screen array of the form are automatically synchronized for input and output. - Lines
10
thru12
BEFORE INPUT
control block: before theINPUT ARRAY
statement is executed aMESSAGE
is displayed to the user. - Lines
14
thru16
BEFORE ROW
control block: when called in this block, theARR_CURR
function returns the index of the record that the user is moving into (which will become the current record). This is stored in a variablecurr_pa
, so the index can be passed to other control blocks. We also initialize theopflag
to "N": This will be its value unless an update or insert is performed. - Lines
18
and19
BEFORE INSERT
control block: just before the user is allowed to enter the values for a new record, the variableopflag
is set to "T", indicating an Insert operation is in progress. - Lines
21
and22
AFTER INSERT
control block sets theopflag
to "I" after the insert operation has been completed. - Lines
24
thru27
BEFORE DELETE
control block: Before the record is removed from the program array, the functiondelete_cust
is called, which verifies that the user wants to delete the current record. In this function, when the user verifies the delete, the index of the record is used to remove the corresponding row from the database. Unless thedelete_cust
function returnsTRUE
, the record is not removed from the program array. - Lines
29
thru32
ON ROW CHANGE
control block: After row modification, the program checks whether the modification was an insert of a new row. If not, theopflag
is set to "U" indicating an update of an existing row. - Lines
34
thru37
BEFORE FIELD
store_num
control block: thestore_num
field should not be entered by the user unless the operation is an Insert of a new row, indicated by the "T" value ofopflag
. Thestore_num
column in thecustomer
database table is a primary key and cannot be updated. If the operation is not an insert, theNEXT FIELD
statement is used to move the cursor to the next field in the program array,store_name
, allowing the user to change all the fields in the record of the program array exceptstore_num
. - Lines
39
thru46
ON CHANGE
store_num
control block: if the operation is an Insert, thestore_num_ok
function is called to verify that the value that the user has just entered into the fieldstore_num
of the current program array does not already exist in thecustomer
database table. If thestore number
does exist, the value entered by the user is nulled out, and the cursor is returned to thestore_num
field. - Lines
48
thru55
AFTER ROW
control block: First, the program checksINT_FLAG
to see whether the user wants to interrupt theINPUT
operation. If not, theopflag
is checked in aCASE
statement, and theinsert_cust
orupdate_cust
function is called based on theopflag
value. The index of the current record is passed to the function so the database table can be modified. - Line
57
indicates the end of theINPUT
statement. - Lines
59
thru61
check the value of the interrupt flagINT_FLAG
and reset it toFALSE
if necessary.
Function store_num_ok
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.
store_num_ok
(custall.4gl):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
- Line
02
The index of the current record in the program array is stored in the variableidx
, passed to this function from theINPUT ARRAY
control blockON CHANGE
store_num
. - Line
03
The variablechecknum
is defined to hold thestore_num
returned by theSELECT
statement. - Line
06
sets the variablecont_ok
to an initial value ofFALSE
. This variable is used to indicate whether thestore number
is unique. - Lines
07
thru12
use an embedded SQLSELECT
statement to check whether thestore_num
already exists in thecustomer
table. The index passed to this function is used to obtain the value that was entered into thestore_num
field on the form. The entire database row is not retrieved by theSELECT
statement since the only information required by this program is whether thestore number
already exists in the table. TheSELECT
is surrounded byWHENEVER ERROR
statements. - Lines
13
thru22
testSQLCA.SQLCODE
to determine the success of theSELECT
statement. The variablecont_ok
is set to indicate whether thestore number
entered by the user is unique. - Line
24
returns the value ofcont_ok
to the calling function.
Function insert_cust
This function inserts a new row into the customer
database
table.
insert_cust
(custall.4gl):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
- Line
02
This function is called from theAFTER INSERT
control block of theINPUT ARRAY
statement. The index of the record that was inserted into thecust_arr
program array is passed to the function and stored in the variableidx
. - Lines
04
thru16
uses an embedded SQLINSERT
statement to insert a row into thecustomer
database table. The values to be inserted into thecustomer
table are obtained from the record just inserted into the program array. TheINSERT
is surrounded byWHENEVER ERROR
statements. - Lines
18
thru22
test theSQLCA.SQLCODE
to see if the insert into the database was successful, and return an appropriate message to the user.
Function update_cust
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.
update_cust
(custall.4gl):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
- Line
02
The index of the current record in thecust_arr
program array is passed asidx
from theON ROW CHANGE
control block. - Lines
04
thru16
use an embedded SQLUPDATE
statement to update a row in thecustomer
database table. The index of the current record in the program array is used to obtain the value ofstore_num
that is to be matched in thecustomer
table. Thecustomer
row is updated with the values stored in the current record of the program array. TheUPDATE
is surrounded byWHENEVER ERROR
statements. - Lines
18
thru22
test theSQLCA.SQLCODE
to see if the update of the row in the database was successful, and return an appropriate message to the user.
Function delete_cust
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.
delete_cust
(custall.4gl):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
- Line
02
The index of the current record in thecust_arr
program array is passed from theBEFORE DELETE
control block ofINPUT ARRAY
, and stored in the variableidx
. TheBEFORE DELETE
control block is executed immediately before the record is deleted from the program array, allowing the logic in this function to be executed before the record is removed from the program array. - Line
05
sets the initial value ofdel_ok
toFALSE
. - Lines
07
thru15
display the modal Menu to the user for confirmation of the Delete. - Lines
18
thru22
use an embedded SQLDELETE
statement to delete the row from thecustomer
database table. The variableidx
is used to determine the value ofstore_num
in the program array record that is to be used as criteria in theDELETE
statement. This record in the program array has not yet been removed, since thisdelete_cust
function was called in aBEFORE DELETE
control block. TheDELETE
is surrounded byWHENEVER ERROR
statements. - Lines
24
thru30
test theSQLCA.SQLCODE
to see if the update of the row in the database was successful, and return an appropriate message to the user. The valuedel_ok
is set based on the success of the SQLDELETE
statement. - Line
33
returns the variabledel_ok
to theBEFORE DELETE
control block, indicating whether the Delete of thecustomer
row was successful.