Visual Basic Concepts

Choosing an RDO Cursor Type

An rdoResultset object can be defined as a set of rows accessed with or without a cursor. The CursorDriver property of the rdoEnvironment or rdoConnection object determines which cursor library is used and where the cursor keyset will be created — on the client workstation or on the server. However, the type argument of the OpenConnection method defines how the cursor is built.

When you chose one of the type argument settings, the result set is returned as one of the following types:

Each of these result set types is built based on the type available and the capability of the ODBC interface for your data source. However, if you set the CursorDriver property to rdUseNone, RDO does not create a cursor for your rdoResultset — it always (and only) creates a forward-only, read-only, single-row, cursorless result set.

Forward-Only Result Sets

In cases where you need to retrieve data quickly with the least amount of overhead, use a forward-only rdoResultset. This type of result set can be updatable, but it only exposes one row at a time. No cursor keyset is created, and data values are static — they are not updated as changes are made on the data source. However, because of its efficiency, it might be faster to rebuild a forward-only rdoResultset than to build and maintain a keyset-type rdoResultset.

Unless you use the rdConcurReadOnly option and set the RowsetSize to 1, RDO creates a cursor to manage your forward-only result set, which can impact performance. However, if you choose rdUseNone as the cursor driver, a forward-only, read-only cursorless result set is created by default.

Static Scrollable Cursors

A static-type rdoResultset is somewhat similar to a Jet snapshot-type Recordset object, except that static cursors can be updatable. This type of cursor creates a static copy of the data rows on the client or server, depending on the cursor driver chosen. Static cursor data appears to be unchanging. That is, the membership, order, and values in the result set used by a static cursor are generally fixed when the cursor is opened and fully populated. Rows updated, deleted, or inserted by other users (including other cursors in the same application) are not detected by the cursor until it is closed and reopened.

Keyset Cursors

A keyset-type rdoResultset is similar to a Jet dynaset-type Recordset object. A key (basically a bookmark) is built and saved for each row in the cursor and stored either on the client workstation or on the server machine. When you access each row, the stored key is used to fetch the current data values from the data source. In a keyset-driven cursor, membership is frozen once the keyset is fully populated; therefore, additions or updates that affect membership are not made a part of the rdoResultset until it is rebuilt.

On some ODBC data sources, modifications or additions made directly to keyset cursors using the AddNew and Edit methods are included in the result set, but additions or modifications made using Execute do not affect the cursor. Consult your server manual for details.

To build a keyset-driven cursor, you must provide sufficient resources on the client or server to hold the keys and a block (based on RowsetSize) of data rows.

Changing Membership When Using the AddNew Method

Although you can use the AddNew method to add a row to the database using an updatable keyset or static cursor, the new row is not always added to the rdoResultset — that is, some cursor libraries add rows to both the cursor and to the underlying table but others do not. The following table summarizes how keyset and static cursors are implemented by the various cursor libraries:

Cursor library Rows added to cursor on AddNew
rdUseIfNeeded (server-side) Yes
rdUseODBC (ODBC) No
rdUseServer (server-side) Yes
rdUseClientBatch (client batch) Yes
rdUseNone (no cursor) Not applicable

Dynamic Scrollable Cursors

A dynamic-type rdoResultset is identical to a keyset-driven cursor, except that membership is not frozen and bookmarks are not supported. Because RDO constantly checks that all qualified rows are included in the membership, this type of cursor carries the largest burden of overhead. However, a dynamic cursor may be faster to initiate than a keyset cursor, because the keyset cursor carries the overhead of building the initial keyset.

Generally, dynamic cursors should be avoided, as they are extremely expensive to implement and maintain — that is, this type of cursor uses a great deal of RAM and requires significant network bandwidth to maintain.

Using the RowCount Property

When a keyset-type or static-type rdoResultset is first created, the RowCount property is set to:

When your code references the RowCount property, RDO fully populates the rdoResultset before returning control to your application. RowCount is not applicable to dynamic cursors where the number of rows can change, or to forward-only result sets that only expose one row.

Sequencing Operations

If there is an unpopulated rdoResultset pending on a data source that can only support a single operation on an rdoConnection object, you cannot create additional rdoQuery or rdoResultset objects or use the Refresh method on the rdoTable object until the rdoResultset is flushed, closed, or fully populated. For example, when using SQL Server 4.2 as a data source or a client-side cursor on SQL Server 6.x, you cannot create an additional rdoResultset object until you move to the last row of the current rdoResultset object. To populate the result set, use the MoreResults method to move through all pending result sets, or use the Cancel or Close method on the rdoResultset to flush all pending result sets. This restriction might not apply to your data source. If you need to submit a query and the hStmt is not available, you must open an additional connection.

Managing RDO Concurrency

You can control how the data source and the chosen cursor library manage row and page locking while you make changes to the data by changing the LockType option of the OpenResultset method. The following table describes the four types of locking.

LockType option Description
rdConcurLock Pessimistic concurrency. This technique uses the lowest level of locking sufficient to ensure that the row can be updated.
rdConcurRowVer Optimistic concurrency using row versions. The ODBC cursor library and the data source compare row ID or TIMESTAMP values to determine if the row has changed.
rdConcurValues Optimistic concurrency using row values. The ODBC cursor library and the data source compare data values.
rdConcurReadOnly The cursor is read-only. No changes of any kind are permitted.
rdConcurBatch The client batch cursor library is being used and you wish to defer all updates until you use the BatchUpdate method.

Caution   The rdConcurLock option locks the entire rowset as set by the RowsetSize property. This lock is established immediately as the result set is opened — it does not wait until the Edit method has been executed. As the rowset is repositioned, the rows or pages touched by the rowset are locked and those remaining rows are released. Because of this mechanism, pessimistic locking should only be used in special cases.

Optimistic Locking

With optimistic locking, column data values or row versions are compared to test concurrency. In this case the original row as it was prior to the Edit or AddNew method is compared with the row as it exists in the database prior to being changed by the Update method. If the row data changed in this time frame, a trappable error results. At this point, your temporary row buffer is lost. To carry out the changes, you must refresh the current row using the Move 0 technique, then use the Edit or AddNew method, fill in the new values, and repeat the Update method again.