2. Events for one of the ProDataSet's temp-table buffers
3. Events for each individual record created in a temp-table
Because the FILL can apply (either explicitly or by cascading) to the whole ProDataSet or to individual temp-table buffers, there are separate events for each of those levels.
The ProDataSet is passed into each event procedure as an INPUT parameter BY-REFERENCE. This allows the event procedure to operate on the ProDataSet using static ABL to reference its buffers and fields, without the ProDataSet being physically copied. This also means that because the ProDataSet is not copied, changes made to the ProDataSet by the event procedure are made to the same copy all procedures are using.
These are the FILL events for the ProDataSet and its members:
BEFORE-FILL on a ProDataSet handle — Fires at the very beginning of a FILL on the ProDataSet before any records are read or populated. It lets you make a server or database connection or do other preparatory work. Alternatively, you could use the event to intercept and fully replace the default behavior, in a case where the data relationships are such that the standard Data-Relations cannot define them, or where there are perhaps no standard Data-Source objects at all, because the data comes from a non-database source.
BEFORE-FILL on a ProDataSet temp-table buffer handle — Fires at the very beginning of a fill for a ProDataSet's temp-table. This lets you do preparatory work for the individual table. For the parent table in a set of related tables where the FILL event is applied to this top-level table, it could be the same kind of connection code as for the ProDataSet as a whole. Or you could prepare the query for a top-level table in its FILL event. For a child table, the event is fired once for each parent record that is created, and gives you the opportunity to adjust the query for the child table, or possibly cancel the fill for children of that parent altogether.
AFTER-FILL on a ProDataSet handle — Fires at the very end of a FILL of a ProDataSet and can be used to adjust the contents of the ProDataSet, possibly to reject the entire FILL operation or to detach Data-Sources or disconnect from other resources.
AFTER-FILL on a ProDataSet temp-table buffer handle — Fires at the end of a FILL of a ProDataSet buffer's temp-table and can be used to adjust the contents of the table, detach the Data-Source, and so on. As with the BEFORE-FILL event on a buffer, for a child table the event is fired once for each parent record that is created.
You use the SET-CALLBACK-PROCEDURE method to register each of these events for a ProDataSet handle or a ProDataSet buffer handle, which identifies the code to run for each object.
The lowest event level is for the individual record, fired once for each record created in each table during a FILL. These are the ROW-FILL events for the ProDataSet temp-table buffer:
BEFORE-ROW-FILL on a ProDataSet temp-table buffer handle — Fires before the row is created in the temp-table, but after the Data-Source records for it have been read. For example, this code can examine the database buffers or other information and decide not to create the record using the RETURN NO-APPLY statement.
AFTER-ROW-FILL on a ProDataSet temp-table buffer handle — Fires after a row is created in the temp-table. For example, the code can modify field values in the row by supplying values for calculated fields. It can also perform filtering and reject a row simply by deleting it, if the logic determines that it should not be part of the ProDataSet. The event procedure cannot modify record currency using the ProDataSet's buffers in any other way. It can use a separately defined buffer for the temp-table (or for other tables in the ProDataSet) to modify the ProDataSet in other ways. The code can RETURN ERROR to abort the entire FILL or RETURN NO-APPLY to cancel the cascade of the FILL operation down to children of this current record, if any.
The following figure illustrates the order in which these nested events are triggered, using a single parent-child relationship as an example. Multiple levels of nesting would result in multiple levels of nesting of the events for each table. If there are multiple top-level tables, the process is repeated for each top-level table. Events for top-level tables are triggered in the order in which the tables are defined in the ProDataSet definition or added to a dynamic ProDataSet.
Figure 10. Trigger order for nested events
This is the sequence shown in the above figure:
1. The ProDataSet BEFORE-FILL event fires first, before any record reads begin.
2. The table BEFORE-FILL event for the top-level table fires once before each row in that table is populated.
3. A nested table BEFORE-FILL event fires once for each parent row, before any rows in the child table are populated.
4. A BEFORE-ROW-FILL event fires once for each row in the table before it is populated, but after the corresponding records in the Data-Source have been read into their database buffers.
5. An AFTER-ROW-FILL event fires once for each row in the table after it has been created and its field values assigned.
6. The AFTER-FILL event on a nested table fires once for each parent row, after all the rows in the child table have been created and populated.
7. The AFTER-FILL event on a parent table fires once for each parent row, after it and all of its children have been populated.
8. The AFTER-FILL event on the ProDataSet itself fires last of all, after all rows have been populated.
If a callback procedure attempts to raise error for a FILL event, either through the traditional RETURN ERROR or through the structured UNDO, THROW, the ProDataSet ERROR attribute is set to true. Error is not raised to the caller.
As a recursive ProDataSet FILL is proceeding, it creates a clone of the relevant buffers, relations, queries and Data-Sources for each level of recursion. As a new record is added to the ProDataSet, it fires FILL events on the recursed, cloned buffer. From inside the event handler, you might want to see previous iterations of the buffer; its parent, grandparent, great-grandparent, and so on. These iterations are available through several attributes:
CURRENT-ITERATION returns the level of iteration for the cloned buffer handle.
GET-ITERATION returns the buffer handle at a specified recursion level.
NUM-ITERATIONS indicates how many levels deep you are in a recursive FILL.