Project

General

Profile

Record Buffer Statements

In the Progress 4GL, a record buffer is used to perform basic CRUD operations on a table (temporary or from a physical database). To create and delete a record, there are specialized statements which can work only with buffers; for field update and field access, please see the Database Field References chapter of this book. Beside these, the 4GL provides additional statements which help in comparing the records referenced by two buffers and in copying fields from one buffer to another - these statements are explained in separate sections, at the end of the chapter. Some of the record buffer statements can work with either database tables or temporary tables and others can work only with temporary tables; the following sections will describe the record buffer statements as they are used in each context.

Creating Records

In the 4GL, all record creation is done using the CREATE statement. This statement takes as a parameter the buffer name, which is either the implicit buffer name or the name of an explicit buffer defined using the DEFINE BUFFER statement. In either case, in the converted code this buffer name is mapped to an instance variable of the Java class which represents the buffer's containing, external procedure.

Following is the 4GL syntax of the CREATE statement:

CREATE record [ NO-ERROR ]

where record is the buffer name and the NO-ERROR flag suppresses any errors encountered during record creation.

Upon conversion, the result is always a call to the RecordBuffer.create(DataModelObject dmo) method, regardless of the buffer type - temporary or permanent. Please use the following table as a reference to how this statement is converted in various cases:

4GL Code Converted Code Notes
...
create book.
...
...
RecordBuffer.create(book);
...
In the converted code, book is the name given to the instance field which defines this buffer.
...
create book no-error.
...
...
ErrorManager.silentErrorEnable();
RecordBuffer.create(book);
ErrorManager.silentErrorDisable();
...
When the NO-ERROR clause is used, the RecordBuffer.create statement is bracketed with statements which start and end the FWD runtime's silent error mode.
define temp-table t-cost
field book-id as int
field cost as dec.
...
create t-cost.
...
...
RecordBuffer.create(tCost);
...
Even for temporary buffers, the CREATE statement is converted to the RecordBuffer.create call, which takes the buffer field name as a parameter (which in this case is tCost).
define temp-table t-cost
field book-id as int
field cost as dec.
...
create t-cost no-error.
...
...
ErrorManager.silentErrorEnable();
RecordBuffer.create(tCost);
ErrorManager.silentErrorDisable();
...
When the NO-ERROR clause is used, the only differences are the ErrorManager calls which bracket the create record call, to enable the silent error mode.

An important note about this statement is that it affects the transaction type of the block which encloses it, if a transaction is not explicitly opened by it. In this case, to mimic the 4GL behavior, the conversion process will automatically mark the enclosing block as a TRANSACTION block; more details on how and why this is done can be found in the Transactions chapter of Part 4.

Deleting Records

In the 4GL, all record deletion is done using the DELETE statement. This statement takes as a parameter the buffer name, which is either the implicit buffer name or the name of an explicit buffer defined using the DEFINE BUFFER statement. In either case, in the converted code this buffer name is mapped to an instance field of the Java class representing the original, external procedure.

The 4GL syntax of this statement is as following:

DELETE record [ VALIDATE(condition, message) ] [ NO-ERROR ]

where record is the buffer name and the NO-ERROR flag suppresses any errors encountered during record deletion. Note that currently the VALIDATE clause is not supported.

When this statement is converted, the result is always a call to the RecordBuffer.delete(DataModelObject dmo) method, except for cases when the deletion is done in a block which is determined by the conversion engine to be a block dedicated to the removal of all or some records from a temporary table. This special case is discussed below.

The following table shows how the DELETE statement is converted:

4GL Code Converted Code Notes
...
delete book.
...
...
RecordBuffer.delete(book);
...
In the converted code, book is the name given to the instance field which defines this buffer.
...
delete book no-error.
...
...
ErrorManager.silentErrorEnable();
RecordBuffer.delete(book);
ErrorManager.silentErrorDisable();
...
When the NO-ERROR clause is used, the statement is bracketed with statements which start and end the FWD runtime's silent error mode.
define buffer t-cost
field book-id as int
field cost as dec.
...
delete t-cost.
...
...
RecordBuffer.delete(tCost);
...
Even for temporary buffers, the DELETE statement is converted to the RecordBuffer.delete call, which takes the buffer variable as a parameter. Also, note that tCost is the name given to the instance field which holds this temporary buffer.
define buffer t-cost
field book-id as int
field cost as dec.
...
delete t-cost no-error.
...
...
ErrorManager.silentErrorEnable();
RecordBuffer.delete(tCost);
ErrorManager.silentErrorDisable();
...
When the NO-ERROR clause is used, the only differences are the ErrorManager calls which bracket the delete record call, to enable the silent error mode.

In the 4GL, it is also possible to remove all records in a temporary tables in batch, using this statement:

EMPTY TEMP-TABLE table-name [ NO-ERROR ]

where table-name is the name of a temporary table or the name of a buffer associated with a temporary table.

The following examples show how this statement is converted:

4GL Code Converted Code Notes
define buffer t-cost
field book-id as int
field cost as dec.
...
empty temp-table t-cost.
...
...
TemporaryBuffer.clear(tCost);
...
In the converted code, tCost is the name given to the instance field which defines the default buffer for this temporary table.
define buffer t-cost
field book-id as int
field cost as dec.
...
empty temp-table t-cost no-error.
...
...
ErrorManager.silentErrorEnable();
TemporaryBuffer.clear(tCost);
ErrorManager.silentErrorDisable();
...
When the NO-ERROR clause is used, the only differences are the ErrorManager calls which bracket the empty table call, to enable the FWD runtime's silent error mode.

The conversion engine also handles a special case, when all or only some records in a temporary table are deleted using a FOR EACH loop. Such cases are converted to a TemporaryBuffer.clear(DataModelObject dmo) or RecordBuffer.delete(DataModelObject, String where, Object... args) call to bulk-delete the records, depending on whether the query block has a where clause or not.

4GL Code Converted Code Notes
for each t-cost:
delete t-cost.
end.
TemporaryBuffer.clear(tCost); In this case, as the only goal of this block is to clear the temporary table, the entire block is converted to the TemporaryBuffer.clear call.
for each t-cost:
delete t-cost no-error.
end.
TemporaryBuffer.clear(tCost); When the NO-ERROR clause is used, there is no difference in the converted result.
for each t-cost
where t-cost.cost = 0:
delete t-cost.
end.
RecordBuffer.delete(tCost, “cost = 0”); When the query block has a where clause, this clause is passed as a parameter to the RecordBuffer.delete call; the where clause follows the same conversion rules as when converting the where clause for a normal query block: the first parameter after the buffer variable is the where clause, which is followed by the list of arguments: RecordBuffer.delete(buffer,@where clause@, arg1, arg2, …, argn).
for each t-cost
where t-cost.cost = 0:
delete t-cost no-error.
end.
RecordBuffer.delete(tCost, “cost = 0”); In this case the NO-ERROR clause is ignored too.

As with the CREATE statement, the DELETE statement affects the transaction type of the parent block, if not set explicitly and this is not a block which is converted to a bulk-delete statement; in the converted code, the parent block automatically will be marked as a TRANSACTION block. More details on how and why this is done can be found in the Transactions chapter of Part 4.

Copy and Compare Buffers

BUFFER-COPY

This statement copies one or more fields of a buffer to another buffer. The syntax of this statement is:

BUFFER-COPY source [ { EXCEPT | USING } field ... ] TO target [ ASSIGN expression ] [NO-ERROR]

This statement takes as main arguments the source buffer from which one or more of its fields are copied to the target buffer. It is possible to provide a list of exclusive or inclusive fields, if the EXCEPT or USING clause is used and is followed by a list of fields. It is not necessary for the source and target buffers to be for the same table; in this case, the FWD runtime automatically will find the common fields and will copy only the contents of these common fields. If the EXCEPT or ASSIGN clauses are used, BUFFER-COPY automatically will remove from the bulk copy the fields which appear in the EXCEPT list or the non-extent fields which appear on the left-side of the ASSIGN expressions; note that FWD has some limitations when the ASSIGN clause is used, which will be explained later. For details on the syntax of the assign expressions, please see the ASSIGN Statement section of the Data Types chapter of Part 4; the syntax of these expressions is the same as for the ASSIGN statement, with the exception that the EXCEPT clause is not supported.

When converting this statement, the resulting code is an API call to one of the following methods provided by the RecordBuffer class:

RecordBuffer API Details
public static void copy(DataModelObject srcDMO,
DataModelObject dstDMO,
boolean validate)
This API call will be emitted for simple BUFFER-COPY statements, which have specified only the source and target buffers and optionally one or more expressions in the ASSIGN clause. The srcDMO parameter will reference the source buffer, the dstDMO parameter will reference the target buffer and the validate parameter will be set to true only when the ASSIGN clause exists. If there are assign expressions, the validate buffer will postpone validation until all the assign expressions are executed.
public static void copy(DataModelObject srcDMO,
String[] srcProps,
boolean exclusive,
DataModelObject dstDMO,
boolean validate)
This API call will be emitted in cases when the BUFFER-COPY statement has an EXCEPT or USING clause. So, the new parameters for this API are used to handle the specified exclusive or inclusive fields. The srcProps names those source properties to be included or excluded from the copy, depending upon the exclusive flag. The exclusive flag will be true if srcProps represents an exclusive list (i.e. the EXCEPT clause is used) and will be set to false if srcProps represents an inclusive list (i.e. the USING clause is used).

The FWD runtime automatically will determine the list of fields which will need to be copied from the source to the target buffer; this list will contain all fields with the same name in both buffers and will be further restricted using the fields specified for the EXCEPT or USING clause; the point is that the converted result has no dependencies on the buffer types. Following are some examples on how FWD converts this statement and details about the current limitations:

Example 1:

...
def temp-table t-cost
   field book-id as int
   field cost as dec.

def buffer x-t-cost for t-cost.
...
buffer-copy t-cost
  to x-t-cost.
...

Converted code:

...
RecordBuffer.copy(tCost, xTCost, true);
...

Details:

In this example, the tCost parameter names the source buffer, the xTCost parameter names the destination buffer and the third parameter sets the validate flag to true - as there is no ASSIGN clause, the destination buffer validation will done right after all the fields are copied.

Example 2:

...
def temp-table t-cost
   field book-id as int
   field cost as dec.

def buffer x-t-cost for t-cost.
...
buffer-copy t-cost
  to x-t-cost no-error.
...

Converted code:

...
ErrorManager.silentErrorEnable();
RecordBuffer.copy(tCost, xTCost, true);
ErrorManager.silentErrorDisable();
...

Details:

This example adds the NO-ERROR clause; the only difference in the converted result is that the RecordBuffer.copy call will be bracketed by the API calls which enable the silent error mode before executing the bulk copy and disable the silent error mode after the bulk copy is done.

Example 3:

...
def temp-table t-cost
   field book-id as int
   field cost as dec.

def buffer x-t-cost for t-cost.
...
buffer-copy t-cost
  except cost
  to x-t-cost.
...

Converted code:

...
RecordBuffer.copy(tCost, new String[]
{
    "cost" 
}, true, xTCost, true);
...

Details:

This example adds the EXCEPT clause. Note that the second RecordBuffer.copy API is used; the converted code sets the srcProps parameter to the field list and the exclusive parameter to true, so that the specified fields will be excluded from the bulk copy.

Example 4:

...
def temp-table t-cost
   field book-id as int
   field cost as dec.

def buffer x-t-cost for t-cost.
...
buffer-copy t-cost
  using cost
  to x-t-cost.
...

Converted code:

...
RecordBuffer.copy(tCost, new String[]
{
    "cost" 
}, false, xTCost, true);
...

Details:

This example adds the USING clause. Note that the second RecordBuffer.copy API is used; the converted code sets the srcProps parameter to the field list and the exclusive parameter to false, so that only the specified fields will be included in the bulk copy.

Example 5:

...
def temp-table t-cost
   field book-id as int
   field cost as dec.

def buffer x-t-cost for t-cost.
...
buffer-copy t-cost
  to x-t-cost
  assign x-t-cost.book-id = t-cost.book-id
         x-t-cost.cost = t-cost.cost.
...

Converted code:

...
RecordBuffer.startBatch();
RecordBuffer.copy(tCost, xTCost, false);
xTCost.setBookId(tCost.getBookId());
xTCost.setCost(tCost.getCost());
RecordBuffer.endBatch();
...

Details:

This example adds the ASSIGN clause. As validation needs to be done after all both the bulk copy and the ASSIGN expressions are executed, the conversion engine will bracket both the RecordBuffer.copy call and the assignments with RecordBuffer.startBatch and RecordBuffer.endBatch calls. The start/end batch method calls will postpone index and field validation until the bulk copy and the assignments are executed. Also, the assignments are converted using the same rules as when converting the assignment expressions for an ASSIGN statement. For more information about these start/end batch method calls and how the assignment expressions are converted, please see the ASSIGN Statement section of the Data Types chapter of Part 4, as the conversion of the assignment expressions is the same as for to the ASSIGN statement. Also, as validation is not done after the bulk copy is terminated, the validate flag for the RecordBuffer.copy call is set to false.

Note that FWD has some limitations for the case when the ASSIGN expressions are used with the BUFFER-COPY statement. When the bulk copy is done by the FWD runtime, it will not be aware of the fields which appear on the left-side of the assignments and they will not be excluded from the bulk copy; so, FWD will copy all fields if no EXCEPT or USING clause is specified. If the EXCEPT or USING clause is specified, FWD will be aware only of the fields specified for these clauses and any fields on the left side of the ASSIGN expressions will be ignored.

Example 6:

...
def temp-table t-cost
   field book-id as int
   field cost as dec.

def buffer x-t-cost for t-cost.
...
buffer-copy t-cost
  using cost
  to x-t-cost
  assign x-t-cost.book-id = t-cost.book-id
         x-t-cost.cost = t-cost.cost no-error.
...

Converted code:

...
ErrorManager.silentErrorEnable();
RecordBuffer.startBatch();
RecordBuffer.copy(tCost, new String[]
{
    "cost" 
}, false, xTCost, false);
xTCost.setBookId(tCost.getBookId());
xTCost.setCost(tCost.getCost());
RecordBuffer.endBatch();
ErrorManager.silentErrorDisable();
...

Details:

This example is more complex, as the USING, ASSIGN and NO-ERROR clauses are used. Note that the API calls which start and end silent error mode are emitted outside the API calls which start and end the batch mode. The converted API call which executes the bulk copy follows the same rules as when there are no ASSIGN and NO-ERROR clauses - it sets both the exclusive and validate flags for the RecordBuffer.copy call to false, as the USING and ASSIGN clauses were used.

BUFFER-COMPARE

This statement compares the fields with the same name of two records and stores the result in a variable or field. The syntax of this statement is:

BUFFER-COMPARE source [ { EXCEPT | USING } field ... } TO target
   [ SAVE [ RESULT IN ] variable-or-field ]
   [ [ EXPLICIT ] COMPARES ] [ NO-ERROR ] :
     [ WHEN field operator expression THEN statement-or-block ] …
   [ END [ COMPARES ] ]

Note that although a BUFFER-COMPARE which has the EXPLICIT COMPARES clause will not fail conversion, the resulting Java code will not compile, as FWD currently doesn't support the EXPLICIT COMPARES clauses. The syntax of the BUFFER-COMPARE as it is supported by FWD is the following:

BUFFER-COMPARE source [ { EXCEPT | USING } field ... } TO target
   [ SAVE [ RESULT IN ] variable-or-field ]

When using this statement, the records currently referenced by the source and target buffers will be compared and the result stored in the specified variable or field. This variable or field which will hold the result can be either a 4GL logical or character type. If the result will be stored in a logical variable, then it will be either true or false, depending on whether all the fields with the same name of the two records are equal or not. If the result will be stored in a character variable, then it will hold the list of comma-separated fields which failed comparison, in alphabetical order. In all cases, the BUFFER-COMPARE statement will be converted to one of the following RecordBuffer API calls, depending on which clauses are used and the type of the result variable:

RecordBuffer API Details
public static void compare(DataModelObject dmo1,
DataModelObject dmo2,
logical result)
This API will compare the properties with the same name of the two records referenced by dmo1 (the source buffer) and dmo2 (the target buffer). The result will be stored in the result parameter and will be set to true if all the common properties of the two records compared to equal.
public static void compare(DataModelObject dmo1,
DataModelObject dmo2,
character result)
This API will compare the properties with the same name of the two records referenced by the dmo1 and dmo2 buffers. The result will be stored in the result parameter and will be hold the list of comma-separated fields which failed comparison, in alphabetical order
public static void compare(DataModelObject dmo1,
String[] propList,
boolean exclusive,
DataModelObject dmo2,
logical result)
This API introduces two new parameters, the propList and exclusive. The propList will hold the list of fields specified for the USING or EXCLUDE clause and the exclusive parameter will be true if the fields are exclusive and false otherwise. The result will be stored as a logical value.
public static void compare(DataModelObject dmo1,
String[] propList,
boolean exclusive,
DataModelObject dmo2,
character result)
In this case, the only difference is that the API will store the result as in a character variable, and will hold the list of comma-separated fields which failed comparison, in alphabetical order.

Same as for the BUFFER-COPY statement, the FWD runtime automatically will determine the list of fields which need to be compared; this list will contain all fields with the same name in both buffers and will be further restricted using the fields specified for the EXCEPT or USING clause; the point is that the converted result has no dependencies on the buffer types. Also, note that the conversion engine will handle both the SAVE RESULT IN clause and its shorter version, the SAVE clause, the same way. Following are some examples to demonstrate how the BUFFER-COMPARE statement is converted to these API calls:

Example 1:

...
def temp-table t-cost
   field book-id as int
   field cost as dec.

def buffer x-t-cost for t-cost.

def var l as logical.
...
buffer-compare x-t-cost
   to t-cost
   save result in l.
...

Converted code:

...
RecordBuffer.compare(xTCost, tCost, l);
...

Details:

This is a simple example, which compares the two buffers and stores the result in variable l, as logical value.

Example 2:

...
def temp-table t-cost
   field book-id as int
   field cost as dec.

def buffer x-t-cost for t-cost.

def var c as char.
...
buffer-compare x-t-cost
   to t-cost
   save result in c.
...

Converted code:

...
RecordBuffer.compare(xTCost, tCost, c);
...

Details:

This is a simple example, which compares the two buffers and stores the result in character variable c.

Example 3:

...
def temp-table t-cost
   field book-id as int
   field cost as dec.

def buffer x-t-cost for t-cost.

def var c as char.
...
buffer-compare x-t-cost
   to t-cost
   save result in c.
...

Converted code:

...
RecordBuffer.compare(xTCost, tCost, c);
...

Details:

This is a simple example, which compares the two buffers and stores the result in character variable c. Note that the converted result is almost the same as the one in the previous example, the only difference being that at runtime, a different API will be invoked.

Example 4:

...
def temp-table t-cost
   field book-id as int
   field cost as dec.

def buffer x-t-cost for t-cost.

def var l as logical.
...
buffer-compare x-t-cost
   except x-t-cost.book-id
   to t-cost
   save result in l.
...

Converted code:

...
RecordBuffer.compare(xTCost, new String[]
{
    "bookId" 
}, true, tCost, l);
...

Details:

This example introduces the EXCEPT clause, which tells the BUFFER-COMPARE statement to compare all fields, except the specified ones. As with the BUFFER-COPY APIs, when the EXCEPT or USING clause is encountered, the field list and its type (exclusive or inclusive) is emitted. In this case, the propList parameter will hold the fields specified for the EXCEPT clause and the exclusive parameter will be set to true, marking the fields as exclusive.

Example 5:

...
def temp-table t-cost
   field book-id as int
   field cost as dec.

def buffer x-t-cost for t-cost.

def var l as logical.
...
buffer-compare x-t-cost
   using x-t-cost.cost
   to t-cost
   save result in l.
...

Converted code:

...
RecordBuffer.compare(xTCost, new String[]
{
    "cost" 
}, false, tCost, l);
...

Details:

This example introduces the USING clause, which tells the BUFFER-COMPARE statement to compare only the specified fields. The statement gets converted the same was as when using the EXCEPT clause, with the only difference that the exclusive parameter is to false, marking the fields as being inclusive.

Example 6:

...
def temp-table t-cost
   field book-id as int
   field cost as dec.

def buffer x-t-cost for t-cost.

def var l as logical.
...
buffer-compare x-t-cost
   using x-t-cost.cost
   to t-cost
   save result in l no-error.
...

Converted code:

...
ErrorManager.silentErrorEnable();
RecordBuffer.compare(xTCost, new String[]
{
    "cost" 
}, false, tCost, l);
ErrorManager.silentErrorDisable();
...

Details:

This example is the same as the previous one, except it introduces the NO-ERROR clause. The RecordBuffer.compare API call gets converted the same way, with the only difference that it gets bracketed by the silent error enable and silent error disable API calls.

Example 7:

...
def temp-table t-cost
   field book-id as int
   field cost as dec.
def temp-table tt
   field res as logical.

def buffer x-t-cost for t-cost.

def var l as logical.
...
buffer-compare x-t-cost
   to t-cost
   save tt.res.
...

Converted code:

...
RecordBuffer.compare(xTCost, tCost, tt.getRes());
...

Details:

Here the BUFFER-COMPARE statement saves the comparison result in the res field of the tt temporary table, as a logical result. Note that in the converted code the result parameter of the RecordBuffer.compare method will be set to the object referenced by the res property of the current record in the tt buffer. This object will be retrieved using the property's getter, and not using a FieldReference object.


© 2004-2022 Golden Code Development Corporation. ALL RIGHTS RESERVED.