Try OpenEdge Now
skip to main content
ABL Essentials
Record Buffers and Record Scope : Record scope : Strong-scoped references and containing blocks
 

Strong-scoped references and containing blocks

Record Buffer Rule 5: If you have a strong-scoped reference to a buffer, you cannot have a free reference that raises the scope to any containing block.

The whole point of using a strong-scoping form, such as a DO FOR block, is to force the buffer scope to that block and nowhere else. If the AVM encounters some other statement (such as a FIND statement) outside the strong-scoped block that forces it to try to scope the buffer higher than the strong scope, it cannot do this because this violates the strong-scoped reference. For example:
DEFINE VARIABLE fLimit AS DECIMAL NO-UNDO.

DO FOR Customer:
  FIND FIRST Customer NO-LOCK WHERE Customer.State = "MA".
  DISPLAY Customer.CustNum Customer.Name Customer.CreditLimit.
  fLimit = Customer.CreditLimit.
END.

FIND FIRST Customer NO-LOCK WHERE Customer.CreditLimit > fLimit.
DISPLAY Customer.CustNum Customer.Name Customer.CreditLimit.
If you try to run this procedure you get the error shown in the following figure.
Figure 31. Conflicting table reference error message
Remember this distinction between Rule 1 and Rule 5. Rule 1 says that strong- and weak-scoped references in separate blocks are self-contained, so it is legal to have multiple blocks in a procedure that scope the same buffer to the block. Rule 5 tells you that it is not legal to have some other reference to the buffer that would force the scope to be higher than any of the strong-scoped references to it.
Here are some additional examples that illustrate how these rules interact:

DEFINE VARIABLE iNum AS INTEGER     NO-UNDO.

DO FOR Customer:
  FOR EACH Customer NO-LOCK WHERE Customer.CreditLimit > 80000
    BY Customer.CreditLimit DESCENDING:
    DISPLAY Customer.CustNum Customer.Name Customer.CreditLimit.
    iNum = Customer.CustNum WHEN iNum = 0.
  END.

  FIND Customer NO-LOCK WHERE Customer.CustNum = iNum.
  DISPLAY
    Customer.NAme FORMAT "x(18)"
    Customer.City FORMAT "x(12)"
    Customer.State FORMAT "x(12)"
    Customer.Country FORMAT "x(12)".
END.
This procedure displays all the Customers with CreditLimits over 80000, saving off the Customer number of the highest one. The following figure shows the first result.
Figure 32. Customers with CreditLimits over 80000—first result
It then finds that Customer again with the highest CreditLimit and redisplays it with some more fields, as shown in the following figure.
Figure 33. Customers with CreditLimits over 80000—next result
This example illustrates that it is valid to have a weak-scoped block enclosed in a strong-scoped block. The AVM raises the scope of the Customer buffer to the outer DO FOR block. This allows you to reference the buffer elsewhere in the DO FOR block, such as the FIND statement. The FIND statement raises the scope of the buffer to the DO FOR block, the nearest containing block with block-scoping properties.
This example illustrates raising the buffer scope:

REPEAT:
  FIND NEXT Customer NO-LOCK USE-INDEX NAME.
  IF NAME < "D" THEN NEXT.
  ELSE LEAVE.
END.

DISPLAY Customer.CustNum Customer.Name.
As it processes the procedure, the AVM encounters the FIND statement and tentatively scopes the Customer buffer to the REPEAT block. The REPEAT block by itself does not force a buffer scope without a FOR phrase attached to it but it does have the record-scoping property, so it is the nearest containing block for the FIND statement. This block cycles through Customers in Name order and leaves the block when it gets to the first one starting with D. But after that block ends, the AVM finds a free reference to the Customer buffer in the DISPLAY statement. This forces the AVM to raise the scope of the buffer outside the REPEAT block. Since there is no available enclosing block to scope the buffer to, the AVM scopes it to the procedure. Thus, the Customer buffer from the REPEAT block is available after that block ends to display fields from the record, as shown in the following figure.
Figure 34. Raising buffer scope example result
This next procedure has two free references, each within its own REPEAT block:

REPEAT:
  FIND NEXT Customer NO-LOCK USE-INDEX NAME.
  IF Customer.Name BEGINS "D" THEN DO:
    DISPLAY Customer.CustNum Customer.Name WITH FRAME D.
    LEAVE.
  END.
END.

REPEAT:
  FIND NEXT Customer NO-LOCK USE-INDEX NAME.
  IF NAME BEGINS "E" THEN DO:
    DISPLAY Customer.CustNum Customer.Name WITH FRAME E.
    LEAVE.
  END.
END.
As before, the AVM initially scopes the buffer to the first REPEAT block. But on encountering another FIND statement within another REPEAT block, the AVM must raise the scope to the entire procedure. The first block cycles through Customers until it finds and displays the first one whose name begins with D, and then leaves the block. Because the buffer is scoped to the entire procedure, the FIND statement inside the second REPEAT block starts up where the first one ended, and continues reading Customers until it gets to the first one beginning with E. The following figure shows the result.
Figure 35. Raising buffer scope example 2 result
This is a very important aspect of buffer scoping. Not only are both blocks using the same buffer, they are also using the same index cursor on that buffer. This is different from the earlier examples where multiple strong- or weak-scoped blocks scope the buffer independently. In these cases, each block uses a separate index cursor, so a second DO FOR or FOR EACH starts fresh back at the beginning of the record set. The difference is that the FIND statements inside these REPEAT blocks are free references, so they force the AVM to go up to an enclosing block that encompasses all the free references.