Progress
Language Tutorial
for Windows


The DISPLAY, ENABLE, and ASSIGN Technique

When you worked with variables in previous chapters, you used this technique, as summarized below:

  1. Use a DISPLAY statement to copy data from the variable to the widget that represents that variable.
  2. Use an ENABLE statement to let the user interact with the widget and the screen value.
  3. On some event, use an ASSIGN statement to copy the screen value back to the variable.

Using the form in Figure 8–9, you could follow the same sequence of steps to change database data. Choosing the Update button will be the event that copies the current screen buffer to the record buffer and eventually to the database.

To use this basic technique with database records, you need to learn more about locks, ROWIDs, ENABLE, and ASSIGN.

Locks

The technique listed above may not work well with database records because of the possibility that more than one user might try to use a record at the same time. Since most Progress applications are designed to work with multiple users, this technique needs modification to support multi-user mode. The Progress RDBMS has two basic rules for a database in multi-user mode:

  1. Anyone can access a record at anytime to view its data.
  2. Only one user at a time can access a record to change the data.

When a user retrieves a record, Progress puts the user’s copy of the record in one of three lock states. A lock is a description on the user’s freedom to use the record. Progress has three keywords that describe the lock states, as shown in Table 8–2.

Table 8–2: Locks 
Lock
Description
NO-LOCK
The user can only view the data. Progress puts the user’s record in this state if the NO-LOCK keyword is included in the record phrase.
SHARE-LOCK
In most cases, when a user requests a record, Progress puts it in this state. SHARE-LOCK means that the user can view the data or attempt to modify the data. If the user attempts to modify data and no other SHARE-LOCK exists, Progress upgrades the user’s lock to an EXCLUSIVE-LOCK.
EXCLUSIVE-LOCK
An EXCLUSIVE-LOCK means that the user can view and change the data. While the user holds the EXCLUSIVE-LOCK, other users can only access the record in a NO-LOCK state.

To let the greatest number of users successfully access a particular record, it makes sense to keep the record in NO-LOCK state. The application should only upgrade the lock to EXCLUSIVE-LOCK during the act of changing data. This means that the record is tied up for the shortest possible period of time.

Row IDs (ROWID)

Every record (or row) in a database has a unique internal identifier which is specified by the Progress keyword ROWID. ROWIDs let you identify a particular record quickly and easily.

To work with ROWIDs, you have to get them and store them. Progress supports ROWIDs as a separate data type, which you can specify in a variable definition with the keyword ROWID. To retrieve a ROWID, you can use the ROWID( ) function.

SYNTAX
ROWID ( buffer-name ) 

This code fragment shows how to retrieve and store a ROWID for later use:

DEFINE VARIABLE Current-Record AS ROWID
            .
            .
            .
FIND FIRST Item.
Current-Record = ROWID(Item).
            .
            .
            . 

The programming example at the end of this section demonstrates how to use ROWIDs and how to take specific control over locks. First, you need a more in-depth look at the ENABLE and ASSIGN statements.

ENABLE Statement

You saw in the last section that the DISPLAY statement moves data from the record buffer to the screen buffer. ENABLE allows users to enter data directly into the screen buffer, as shown in Figure 8–10.

Figure 8–10: Data Movement with the ENABLE Statement

This is a partial syntax for the ENABLE statement.

SYNTAX
ENABLE { ALL [ EXCEPT widget ... ] | field ... }
  { [ frame-phrase ] } 

Use the EXCEPT widget syntax to enable all the widgets in a frame, except for those you specify.

ASSIGN Statement

You also have encountered the ASSIGN statement. ASSIGN moves data from the screen buffer to the record buffer, as shown in Figure 8–11.

Figure 8–11: Data Movement with the ASSIGN Statement

Once you move changes from the screen buffer to the record buffer, or make changes directly to the record buffer, Progress understands that you want those changes written to the database. Progress does not immediately write the changes to the database. When the write occurs is based on other factors. The next section describes how Progress handles database writes.

This is the syntax for the ASSIGN statement.

SYNTAX
ASSIGN
  { { field | field = expression } ...
    record [ EXCEPT field ] ...
  } 

You can assign fields or the entire record. The EXCEPT syntax is also valid with ASSIGN.

By using DISPLAY, ENABLE, and ASSIGN together, you create a chain of functionality that presents current data to your users, allows them to change it, and allows them to save the changes.

NOTE: Using the DISPLAY, ENABLE, and ASSIGN technique does not automatically invoke the validation behaviors stored in the Data Dictionary. To force the validation routines to run, you can query the VALIDATION attribute of a widget or frame. For more information, see the Progress Programming Handbook . The other techniques described in this section do invoke Data Dictionary validation.

DISPLAY, ENABLE, and ASSIGN Programming Example

Follow these steps for a demonstration of the DISPLAY, ENABLE, and ASSIGN technique with explicit control of locks:

  1. Open lt-08-03.p and run it. Note that the form now uses fill-ins instead of text widgets.
  2. Select a record and change some of the fields.
  3. Choose the Next followed by the Prev button. The record does not contain the new data. This procedure does not save your changes unless you choose the Update button.
  4. Select a record, change some of the fields, and choose Update.
  5. Choose the Next followed by the Prev button. The new data is now permanent.
  6. Choose Exit, then press SPACEBAR to return to the Procedure Editor.

Here is the code that created the display:

lt-08-03.p
      /**********  DEFINE QUERY  **********/
       DEFINE QUERY Item-Query FOR Item.
      /**********  DEFINE VARIABLES  **********/
/*3*/  DEFINE VARIABLE Current-Record AS ROWID.
      /**********  DEFINE FORM  **********/
      {lt-08-f1.i}
  
      /**********  DEFINE TRIGGERS  **********/
/*4*/  {lt-08-t1.i}   
  
       ON CHOOSE OF btn-Update 
       DO:
/*5*/     Current-Record = ROWID(Item).
/*6*/     FIND FIRST Item WHERE ROWID(Item) = Current-Record EXCLUSIVE-LOCK.
/*7*/     IF AVAILABLE(Item) THEN
           ASSIGN Item.Item-Name Price On-Hand Allocated Re-Order 
                   On-Order Cat-Page Cat-Description.
/*8*/      ELSE DO:
           FIND FIRST Item WHERE ROWID(Item) = Current-Record NO-LOCK.
           MESSAGE "Record in use. Unable to update." 
               VIEW-AS ALERT-BOX WARNING BUTTONS OK Title "Update Error".
           END.
       END.
      /**********  MAIN LOGIC  **********/
/*1*/  OPEN QUERY Item-Query FOR EACH Item NO-LOCK.
       GET FIRST Item-Query.
       DISPLAY Item.Item-Num Item-Name Price On-Hand Allocated Re-Order 
          On-Order Cat-Page Cat-Description WITH FRAME Frame1.
/*2*/  ENABLE Item.Item-Name Price On-Hand Allocated Re-Order On-Order 
        Cat-Page Cat-Description btn-Prev btn-Next btn-Update btn-Exit 
            WITH FRAME Frame1.
      WAIT-FOR CHOOSE OF btn-Exit.
      CLOSE QUERY Item-Query. 

The following notes help to explain the code:

  1. In the main code block, the OPEN QUERY statement contains the NO-LOCK option. This means that navigating through the records or leaving the application running will not impede other users.
  2. The fields in this example are specifically enabled.
  3. This variable will hold the current ROWID when trying to upgrade a lock.
  4. This include file contains the navigation triggers which you have already studied.
  5. Once the user chooses Update, this trigger executes. Here, the trigger saves the ROWID of the current Item record.
  6. Recall that FIND retrieves an individual record. This trigger uses FIND to get a copy of the record with the EXCLUSIVE-LOCK option. The FIND statement does not affect the defined query.
  7. If any other user has EXCLUSIVE-LOCK or SHARE-LOCK, the FIND will fail, which is why this IF statement checks the record buffer. If the record is there, the user’s changes are saved with the ASSIGN statement.
  8. If the FIND failed, then refind the record with a NO-LOCK and inform the user.

One way to keep data accessible to all users is to keep the data in NO-LOCK and let the user change it. When it is time to assign the changes, use the FIND CURRENT or GET CURRENT statement to get a fresh copy of the record that the user has been modifying with an EXCLUSIVE-LOCK. Then, use the CURRENT-CHANGED function to test for changes to the record while the user was working with it. If the record was changed by another user, you can send a message to the user and display the updated record. If the record is unchanged, you can assign the changes.

You can use this trigger code in place of the trigger code for btn-Update in lt-08-03.p as the following code fragment illustrates:

            .
            .
            .
    ON CHOOSE OF btn-Update 
    DO:
        GET CURRENT Item EXCLUSIVE-LOCK.
        IF AVAILABLE(Item) THEN
            IF NOT CURRENT-CHANGED Item THEN
               ASSIGN Item.Item-Name Price On-Hand Allocated Re-Order
                    On-Order Cat-Page Cat-Description.
            ELSE DO:
               MESSAGE "Record has been changed by another user.
                    Redisplaying new data.".
               DISPLAY Item.Item-Name Price On-Hand Allocated Re-Order
                    On-Order Cat-Page Cat-Description.
            END.
        ELSE DO:
            FIND FIRST Item WHERE ROWID(Item) = Current-Record NO-LOCK.
            MESSAGE "Record in use. Unable to update." 
                VIEW-AS ALERT-BOX WARNING BUTTONS OK Title "Update Error".
            DISPLAY Item.Item-Name Price On-Hand Allocated Re-Order
                    On-Order Cat-Page Cat-Description.
        END.
    END.
            .
            .
            . 


Copyright © 2004 Progress Software Corporation
www.progress.com
Voice: (781) 280-4000
Fax: (781) 280-4095