skip to main content
OpenEdge Development: ADM and SmartObjects
Developing Your Application's Business Logic : ADM event procedures and functions in the SmartDataObject
ADM event procedures and functions in the SmartDataObject
This section documents the internal procedures and functions of the SmartDataObject. For comprehensive details about these procedures and functions, see OpenEdge® Development: ADM Reference and the online help.
The SmartDataObject is supported by two super procedures in addition to smart.p: the query.p procedure contains all logic relative to managing the database query, and the data.p procedure (with its include file data.i) provides the remaining support.
Opening a database query
The openQuery( ) function opens a database query. Typically it is called when the SmartObject is initialized, however, you can override this by setting the OpenOnInit property to FALSE. (The default is TRUE.)
Dynamic query manipulation
The ADM provides a set of functions that allow you to modify a SmartDataObject’s database query dynamically. These functions modify the query’s WHERE clause in various ways:
*assignQuerySelection( ) and addQueryWhere( ) append new criteria to existing criteria.
*removeQuerySelection( ) and columnQuerySelection( ) work with field expressions that you add with assignQuerySelection( ).
*setQuerySort( ) changes the sort criteria.
*setQueryWhere( ) sets the WHERE clause criteria.
The manipulated query is stored in a property and the actual QUERY-PREPARE and QUERY-OPEN do not take place until openQuery( ) is called.
You also can write code to obtain the QueryHandle property and perform your own QUERY-PREPARE. If the SmartDataObject is divided between client and AppServer, such code must be executed on the AppServer for the QueryHandle property to be valid.
The remainder of this section provides more detailed information on the ADM query‑manipulation functions. For more information about the WHERE, QUERY-PREPARE, and QUERY-OPEN keywords, see OpenEdge Development: ABL Reference.
assignQuerySelection( )
The assignQuerySelection( ) function adds field comparison expressions to the query. It supports the majority of standard database queries. With this function:
*The criteria are added to the existing WHERE clause.
*Fields, values, and operators have separate input parameters.
*Each field expression is distributed to the correct table’s WHERE clause.
*A field can be referenced several times, while a field and operator is considered unique. Thus, a field/operator value pair overwrites the value previously added to the WHERE clause with this method.
*An implicit AND is used to add new expressions to existing expressions. (This function does not support OR.)
*An implicit AND is used between field expressions. (This function does not support OR.)
addQueryWhere( )
The addQueryWhere( ) function allows you to add more complex criteria to a query. It:
*Adds criteria to the existing WHERE clause.
*Adds the criteria to ONE of the tables in the queries. (It defaults to the first table reference in the expression.) If you must add criteria to multiple tables, use the function once for each table.
*Accepts an optional parameter to specify which table.
*Accepts an optional parameter to specify to specify OR.
removeQuerySelection( )
This function works with field expressions that are added with assignQuerySelection. Specifically, it allows you to remove criteria from the WHERE clause. Fields and operators have separate input parameters.
columnQuerySelection( )
This function also works with field expressions that are added with assignQueryselection. Specifically, it allows you to inspect all current criteria for a field, and returns all values added for a particular field as a CHR(1)‑separated list of operators and values.
setQuerySort( )
This function changes the SORT phrase.
setQueryWhere( )
This function sets the criteria. It also can be used to add one expression to the query. With it:
*You can pass a complete query prepare string (beginning with the FOR keyword).
*A blank input parameter resets the query to the design expression.
*The criteria is added to ONE of the tables in the queries. (It defaults to the first table reference in the expression.)
Caution: setQuerySort( ) wipes out any other dynamically added criteria from the WHERE clause, including ForeignFields.
Data transfer in queries
Rows are transferred from a database query to a SmartDataObject temp-table, called RowObject, when the first fetch operation takes place. Additional rows are transferred as needed when either of the following operations is performed:
*A fetchNext operation that moves beyond the end of the list of rows currently in the RowObject table
*A fetchLast operation
The transfer of rows is performed through the sendRows procedure, which has both a client and a server component to support transparently a SmartDataObject that is divided between client and AppServer. The number of rows transferred at a time is determined by the RowsToBatch SmartDataObject property. (The initial value is 200.)
By default, the RowObject data set is built up contiguously starting at the beginning, thus the fetchLast operation causes the retrieval of successive batches of rows until it reaches the end of the data set. For a large data set, this can be a very slow process. Progress Software Corporation recommends that for potentially large data sets, you modify this default behavior by changing the Rebuild On Reposition toggle box in the SmartDataObject instance properties dialog box to YES. (This sets the Rebuild On Reposition SmartDataObject property.) The data set is now rebuilt whenever the query is repositioned to a row outside of the current client‑side data set.
You use the fetchFirst, fetchNext, fetchPrev, and fetchLast procedures to position within the RowObject table. Once you are positioned on a row, you use the colValues( ) function to return a list of formatted values for the current row. (See OpenEdge® Development: ADM Reference or the online help for details on the format of the values returned.) Whenever the cursor position in the RowObject query changes, the dataAvailable event occurs. (Objects such as SmartDataViewers subscribe to this event.) Additional procedures and functions allow many useful operations, including but not limited to the following:
*Repositioning to a particular database ROWID (fetchRowIdent)
*Identifying the database ROWID of a record that satisfies a where-clause expression (rowidWhere)
*Obtaining the latest database values for the current row (refreshRow)
The following example illustrates how to reposition to a particular record in a SmartDataObject’s query. In this application, the application user can search a database for a customer name. If the application finds the name, it displays the customer information in the SmartDataViewer.
The SmartWindow in this example contains the following SmartObjects:
*A SmartDataObject for the customer table
*A SmartDataViewer whose data source is the SmartDataObject
*A name‑search field and a button to start the search
The application user enters a customer name into the name‑search field, then presses the search button. The search button contains the following trigger code:
    CSearch   = "Name BEGINS ‘" + NameSrch:SCREEN-VALUE + "’"
    CRowIdent = DYNAMIC-FUNCTION(‘rowidWhere’:U IN h_dcustomers, cSearch).
  IF cRowIdent NE ? THEN
    DYNAMIC-FUNCTION(’fetchRowIdent’:U IN h_dcustomers, cRowIdent, ‘’:U).
The rowidWhere function in this code returns the ROWID of the first database query row that satisfies the where clause specified in cSearch. The fetchRowIdent function accepts a comma‑separated list of database record ROWIDs for a SmartDataObject row that corresponds to the RowObject’s RowIdent field. If the row is currently in the SmartDataObject’s temp-table, the SmartDataObject repositions to that record, otherwise the database query repositions to that row and rebuilds the temp-table.
After the application repositions the SmartDataObject’s query to the specified customer, it refreshes the SmartDataViewer, displaying the customer data in the SmartDataViewer.
Error handling
Errors that occur during the transfer of data rows between the database and a visualization object can be detected either on the client side or the server side. If they are detected on the client side, error messages can go directly back to the visualization object. Errors detected on the server side, however, must go back to the visualization object through the SmartDataObject handling the row transfer. For more information on error reporting, see the “Validation procedures” section.
Initiating update operations
The event procedures in visual objects (SmartDataViewers and SmartDataBrowsers) that initiate update operations are as follows:
These procedures typically are invoked from the buttons of an Update SmartPanel or a SmartToolbar. They perform only the parts of their operations relevant to the visualization, such as enabling Frame fields or displaying initial values. The actual data update side of the operation occurs in the SmartDataObject, using the following functions:
*submitRow( )
*addRow( )
*copyRow( )
*deleteRow( )
*cancelRow( )
These functions cause changes to the RowObject table. If the SmartDataObject’s AutoCommit property is set to Yes (the default if no Commit Panel is attached), each row’s changes automatically are written to the database through the Commit( ) function, otherwise they remain in the RowObject table until an explicit Commit is performed.
Getting and setting properties
In addition to the standard set and get functions for properties, data.p has several support functions to set and get properties of individual columns in the RowObject; for example, Format and DataType. To differentiate these functions from the normal get/set functions for object properties, they are named assignColumnproperty ( column, value ) to assign a value (for those properties that are writable) and columnproperty (column) to retrieve the value.
Signaling data availability
The SmartDataObject indicates that data is available by PUBLISHing dataAvailable, and the Data-Target that has SUBSCRIBEd to this event responds appropriately. This happens when the SmartDataObject opens its query and when the cursor position in the query changes. This behavior makes sense for a configuration in which the SmartDataObject and its visualization are part of the same Progress client.
However, if the user interface either is not Progress or is in a different process than the SmartDataObject, the Data SmartLink is not present and the dataAvailable event is not sent; you must design your user interface objects to deal with this. For example, a visualization could execute the openQuery function in the SmartDataObject by knowing its handle rather than by using an ADM link, and then immediately start requesting data using other functions. If no other outside source is controlling the SmartDataObject, its visualization does not need the Data link or the dataAvailable event; that is, ADM links and event procedures are supported by SmartDataObjects but are not required by other new objects with which they communicate.
Generally, signaling data availability without the Data SmartLink and the dataAvailable event is of interest when you are using SmartDataObjects from a non‑Progress environment. Within a 4GL SmartObject application, the ADM manages communication between objects using the link and event, and the code in the ADM super procedures handles this automatically.
Special considerations apply when you access a SmartDataObject from a Java application. For details, see the “Java applications and SmartDataObjects” section.