Doing a partial ProDataSet FILL to return Order headers
This code example continues over the following sections, each of which illustrates and explains parts of the overall procedure. When the user enters values for one or more of the filtering fields, the window procedure will format that into a where-clause and pass that to a new internal procedure called fetchOrders that returns the ProDataSet.
Add this code for fetchOrders to OrderSupport.p:
PROCEDURE fetchOrders:
DEFINE INPUT PARAMETER pcSelection AS CHARACTER NO-UNDO.
DEFINE OUTPUT PARAMETER DATASET FOR dsOrder BY-VALUE.
This saves off the selection criteria and empties the server-side ProDataSet in preparation for responding to the request.
When the user requests a set of Orders, you do not want to return all the OrderLine detail yet because that can be a lot of data. Instead, you just return the Order headers and wait for the user to select a specific Order to fill in. For this reason fetchOrders needs to set the FILL-MODE attribute for the ttOline and ttItem tables to "NO-FILL." In this way, when you then do a FILL on the ProDataSet, it fills only the ttOrder table and skips the other two tables.
The following four ways change the extent of a FILL, either to limit or to extend the amount of data loaded into the ProDataSet at one time:
Setting the FILL-MODE of one or more tables to "NO-FILL" — When the AVM encounters a NO-FILL table, it does not fill that table and it does not continue down to any children of that table, in effect ending the FILL for that entire branch of the ProDataSet, if there are multiple levels of Data-Relations. In the case of the present example, the ttItem table (which is referenced in the code block above as buffer-handle 3 in the ProDataSet) is the child of ttOline, so ordinarily it would suffice to set ttOline to NO-FILL. But the ProDataSet definition makes the Data-Relation between ttOline and ttItem a REPOSITION relation, which means that at the time of the FILL, ttItem is treated as a top-level table, so that all Items are loaded into the ProDataSet. The REPOSITION relation tells the AVM to automatically select the current Item for each OrderLine in the user interface. For this reason you have to set the FILL-MODE to "NO-FILL" for ttItem, as well as ttOline, to keep the Items from being populated until they are needed.
Setting the ACTIVE attribute of one or more relations to FALSE — Or setting the ProDataSet's RELATIONS-ACTIVE attribute to FALSE, which sets ACTIVE to false for all relations. When you do a FILL on the ProDataSet handle in this case, the AVM does not stop filling children when it encounters a deactivated relation. Instead, it treats every child of a deactivated relation as a top-level table and fills all such tables independently, that is, either with all records from the Data-Source or by using a query if you have defined one for the Data-Source. This method can, therefore, be useful when for some reason you want to define independent queries for a set of tables that are otherwise related, or that should be related after the FILL, when you are navigating the data.
Executing the FILL method on a temp-table buffer handle — Do this rather than on the ProDataSet itself. In this case, the AVM fills only from that table down. This can limit the extent of the fill if there is more than one top-level table in the ProDataSet. Also, when you execute a FILL starting at a specific buffer, the AVM does not treat children of deactivated relations under that buffer as top-level tables. Instead, it simply ends the fill at the level of the parent of the deactivated relation.
Setting the MAXIMUM-LEVEL attribute on a Data-Relation handle — This attribute forces a recursive Data-Relation to stop filling a ProDataSet at a specific number of iterations of a child buffer. When there is a need to return more child records for a parent buffer, the server-side ProDataSet query can be adjusted to return the child records in an OUTPUT APPEND parameter.
These various buffer FILL behaviors are designed to provide you with enough alternatives to satisfy just about any requirement. There is almost always more than one way to get the level of FILL that you want. Do not be unduly confused by all the alternatives. Just pick a way that works for your situation.
To show you an example of one alternative to the one the example uses, the code in fetchOrders could deactivate the first relation in the ProDataSet, between ttOrder and ttOline. In this case, you would have to do the FILL on the ttOrder buffer rather than on the ProDataSet. Otherwise, the ttOrder and ttItem tables would be filled with all the OrderLines and Items in the database, which is not what you want. So the code would look like this:
/* Instead of these statements…
hDataSet:GET-BUFFER-HANDLE(2):FILL-MODE = "NO-FILL". /* ttOline */
hDataSet:GET-BUFFER-HANDLE(3):FILL-MODE = "NO-FILL". /* ttItem */
hDataSet:FILL().
--- you could use these statements to accomplish the same thing: */
hDataSet:GET-RELATION(1):ACTIVE = FALSE.
hDataSet:GET-BUFFER-HANDLE(1):FILL().
Note that in this case you do the FILL on the first buffer handle, not on the ProDataSet. Also, you must remember to set the ACTIVE attribute back to true when you need to fill the detail records later on.
Another point to remember in this case is that any event procedures defined at the level of the ProDataSet will not fire when you do a FILL on the ttOrder table. In our example, the support procedure prepares the Order query in the BEFORE-FILL event for the ProDataSet and detaches the Data-Sources in the AFTER-EVENT for the ProDataSet, so this code would need to be moved to the FILL events for the Order table. These are all things to consider when you are designing the structure of your procedures and event handlers.