IRowset

IRowset is the base rowset interface. It provides methods for fetching rows sequentially, getting the data from those rows, and managing rows.

IRowset requires IAccessor and IRowsetInfo.

Consumers use the methods in IRowset for all basic rowset operations, including fetching and releasing rows and getting column values.

When a consumer gets an interface pointer on a rowset, usually its first step is to determine the rowset’s capabilities by using IRowsetInfo::GetProperties. This returns information about the interfaces exposed by the rowset, as well as those capabilities of the rowset that do not show up as distinct interfaces. These can include the maximum number of active rows and how many rows may have pending updates at the same time.

For most consumers, the next step is to determine the characteristics, or metadata, or the columns in the rowset. For simple or extended column information, they use IColumnsInfo or IColumnsRowset, respectively. These interfaces are also available on prepared commands prior to execution, allowing advance planning.

The consumer determines which columns it needs, either from the metadata or on the basis of knowing the text command that generated the rowset. It determines the ordinals of the needed columns from the ordering of the column information returned by IColumnsInfo or from ordinals in the column metadata rowset returned by IColumnsRowset.

Some consumers do not use a command or do not want to browse the column information They may know the name or property identifier for the columns they want to use. They call IColumnsInfo::MapColumnIDs to retrieve the column ordinals.

The ordinals are used to specify a binding to a column. A binding is a structure that associates an element of the consumer’s structure with a column. The binding can bind the column’s data value, length, and status value.

A set of bindings is gathered together in an accessor, which is created with IAccessor::CreateAccessor. An accessor can contain multiple bindings so that the data for multiple columns can be retrieved or set in a a single call. The consumer can create several accessors to match different usage patterns in different parts of the application. It can create and release accessors at any time while the rowset remains in existence.

To fetch rows from the database, the consumer calls a method such as GetNextRows or IRowsetLocate::GetRowsAt. To create and initialize a new row to be inserted into the data source, the consumer calls IRowsetChange::InsertRow.

The methods that fetch rows do not actually return data to the consumer. Instead, they return the handles to these rows. A local copy of the rows is stored in the rowset.

After the rows are returned, the consumer can access the data in the rows. The consumer calls GetData and passes the handle to a row, the handle to an accessor, and a pointer to a consumer-allocated buffer. GetData converts the data (if it does not match the native provider storage) and returns the columns as specified in the bindings used to create the accessor. The consumer can call GetData more than once for a row, using different accessors and buffers. Thus, the consumer can have multiple copies of the same data. For example, if a column contains a text document, the consumer might call GetData with an accessor that binds the first 50 bytes of the document. When the user double-clicks the displayed heading text, the consumer can then call GetData with a different accessor to retrieve the entire document.

Data from variable-length columns may be treated several ways. First, such columns can be bound to a finite section of the consumer’s structure, which causes truncation when the length of the data exceeds the length of the buffer. The consumer can determine that truncation has occurred by checking if the status is DBSTATUS_S_TRUNCATED. The returned length is always true length in bytes, so the consumer also determines how much data was truncated. Another way to obtain data from such columns is by reference. For example, if a binary column is bound with a type indicator of DBTYPE_BYTES|DBTYPE_BYREF, the provider allocates memory for all of the data in the column and returns this memory to the consumer.

In both cases, it is likely that such large values may be best optimized as deferred columns and accessed only when necessary. Performance varies with different servers, but BLOB columns usually are stored separately from other records and may be more costly to access than ordinary columns. Therefore, they would not routinely be pulled in for browsing or scanning.

Other providers may handle BLOB columns by requesting that they be delivered as OLE ILockBytes, IStorage, ISequentialStream, or IStream objects.

When the consumer is finished fetching or updating rows, it releases them with ReleaseRows. This releases resources from the rowset’s copy of the rows and makes room for new rows. The consumer can then repeat its cycle of fetching or creating rows and accessing the data in them.

When the consumer is done with the rowset, it calls IAccessor:ReleaseAccessor to release any accessors. To release the rowset, it calls IUnknown::Release on all interfaces exposed by the rowset. When the rowset is released, it forces the release of any remaining rows or accessors the consumer holds. Such handle objects are subordinate to the rowset. That is, they do not take reference counts upon the rowset and cannot cause the rowset to linger after all the interfaces for the rowset have been released. The rowset must clean up all such subordinate objects.

This section discusses the Jet-specific implementation of the IRowset interface and its subsequent methods. This includes IRowset::AddRefRows, IRowset::GetData, IRowset::GetNextRows, IRowset::ReleaseRows, and IRowset::RestartPosition.

Cursor and Cursor Promotion

Cursor types may be promoted or demoted by the OLE DB Driver for Access data. This cursor promotion or demotion is based upon guessing the type of cursor requested by the user, and as such is determined by the properties that are passed to the driver. Cursor types may be promoted without it being requested by the user. The driver will try to promote the cursor type if possible. This means that basetables may be updatable. If the consumer asks for properties that don’t match up with a particular cursor type, then the consumer will be promoted to the next cursor level above the minimum match. A consumer that calls ICommandExecute with no properties, expecting the default behavior, will be given a forward-only snapshot.

Cursor types in order of precedence:

  1. Forward-only snapshot

  2. Snapshot

  3. Keyset

  4. Basetables — Dynaset

  5. Append-only dynasets

Regular Rowset

Becoming a Zombie

Rowsets can become zombies if the internal resource on which they depend goes away because of another transaction completing.

Data type coercion

This OLE DB driver supports all the data type coercions that exist in OLE DB. For better performance, bind to the native data types.

By Reference Data Binding

This OLE DB driver does not support “by reference” data binding only by value as described above.

Delayed Update Mode

In delayed update mode, this driver supports at most one pending change at a time on a rowset.

BLOB Support

The OLE DB Provider for Microsoft Jet supports two kinds of structured storage: ISequentialStream and ILockBytes. ISequentialStream can be used for long text and long binary (memo and OLE object) data types, and must read the whole BLOB. ILockbytes only works on the Red ISAM 3.x and 4.x. ISequentialStream works on any BLOB data that Jet reads. You do not read the whole BLOB with ILockBytes; instead, it provides random access to the user.

There are restrictions on rowsets when using ILockbytes and ISequentialStream. If you have a structured storage interface open on a rowset, it may prevent you from editing a row in delayed update mode. You must release your pointers to structured interfaces to change other rows. Structured storage pointer may be invalid if you abort transactions, resulting in rowsets in a zombie state.

Method Description
AddRefRows Adds a reference count to an existing row handle.
GetData Retrieves data from the rowset’s copy of the row.
GetNextRows Fetches rows sequentially, remembering the previous position.
ReleaseRows Releases rows.
RestartPosition Repositions the next fetch position to its initial position; that is, its position when the rowset was first created.

IRowset::AddRefRows

Adds a reference count to an existing row handle. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

IRowset::GetData

Retrieves data from the rowset’s copy of the row. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

IRowset::GetNextRows

Fetches rows sequentially, remembering the previous position. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

Note   If possible, try to access rows sequentially by using this routine. This will minimize the amount of work that Jet has to do to move to new rows. That is, call GNR(NULL, 0, 1…) to traverse through the rowset in your “default” behavior instead of using GetRowsAtBookmark.

IRowset::ReleaseRows

Releases rows. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

IRowset::RestartPosition

Repositions the next fetch position used by GetNextRows or FindNextRow to its initial position, that is, its position when the rowset was first created. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

Note   This is not an expensive operation on scrollable cursors, but it can cause query re-execution on nonscrollable cursors.