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.

When to Implement

IRowset is required for all providers that support general consumers.

When to Call

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

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

For most consumers, the next step is to determine the characteristics, or metadata, of the columns in the rowset. For this they use either IColumnsInfo or IColumnsRowset, for simple or extended column information, 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. For more information about bindings, see "Bindings" in chapter 6.

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 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. For more information about accessors, see "Accessors" in Chapter 6.

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, and 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 it 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 on the displayed heading text, the consumer could 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 the true length in bytes, so the consumer also can determine 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 in general BLOB columns are stored separately from other records and may be more costly to access than ordinary columns, so they would not routinely be pulled in for browsing or scanning. For more information, see "Deferred Columns" in Chapter 4.

Another way to handle BLOB columns may be implemented on some providers, and that is to request they be delivered as OLE ILockBytes, IStorage, ISequentialStream, or IStream objects. For more information, see "BLOBs as Storage Objects" in Chapter 7.

For a description of how to update and delete rows, see Chapter 5, "Updating Data in Rowsets."

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. It calls IUnknown::Release on all interfaces exposed by the rowset to release the rowset. When the rowset is released, it forces the release of any remaining rows or accessors the consumer may hold. 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 beyond the point where all the interfaces for the rowset have been released. The rowset must clean up all such subordinate objects.

Note  If the rowset was generated as a result of executing a command that contained output parameters and the provider populates output parameters when the rowset is released (that is, DBPROP_OUTPUTPARAMETERAVAILABILITY is DBPROP_OA_ATROWRELEASE), the memory for the output parameters bound at Execute time must be valid when the rowset is released. Not doing so is considered a serious programming error and likely will cause a crash.

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.