OLE DB Leveling

Choosing the Right Interfaces

Abstract

OLE DB defines a comprehensive set of interfaces for accessing a diverse range of data types, located in a variety of data stores. While the OLE DB Programmer's Reference can be intimidating in size, no one provider is expected to support all of the functionality defined in the reference. Providers only expose the interfaces that reflect the natural functionality of their data store. Providers all support a base level of functionality. Above that base level, common service components provide interoperability through generic implementations of extensions such as scrolling or notifications if the provider doesn't support them.

Introduction — OLE DB as a Component Technology

To meet its goal of providing data access to all types of data in a COM (Component Object Model) environment, OLE DB is designed as a component technology. In OLE DB, data sources expose the interfaces that reflect their functionality, and common components can be built on top of those interfaces to expose more robust data models. To define a component architecture, OLE DB identifies common characteristics between different data providers and services, and defines common interfaces to expose those characteristics. So, for example, while a rowset may be obtained through a number of very different mechanisms, the end result is still a rowset, with well-defined interfaces, methods and characteristics. With OLE DB, navigating the result of a complex multi-table join is no different than navigating the results of an opened text file containing tabular data. Defining common interfaces in this manner allows components to more efficiently augment the individual data provider's native functionality.

An OLE DB provider exposes OLE DB interfaces over some type of data. OLE DB providers include everything from a full SQL DBMS to a text file or data stream. Obviously these data providers have different functionality, and it's important not to limit that functionality. But at the same time it's not reasonable to expect all providers that expose simple tabular data to implement a full-blown query engine as well.

Once the base functionality is defined, the next step is to view the additional functionality as incremental additions to this base functionality. Thus, the more sophisticated providers can expose these advanced features in addition to the base level interfaces. Furthermore, individual service components can be built to implement these features on top of the simpler providers.

Consumers

Developers writing OLE DB consumers can choose their level of interoperability with OLE DB providers. Consumers may be written to consume a specific provider, in which case they are designed to be aware of the functionality of the provider. Or they may be written to consume generic providers. In order to consume generic providers, the consumer may do one of the following:

Base Consumer Functionality

The following table describes the minimum level of functionality that a consumer can expect to be supported when talking to any OLE DB provider. The table is broken into three columns:

Table 1 – Base Consumer Interfaces

Object Base interfaces Updatable
DataSource IConnectionPointContainer for:

- IDBAsynchNotify

IDatasource1
IDBAsynchStatus2
IDBCreateSession
IDBInitialize
IDBProperties
IPersist
ISupportErrorInfo
DataSource Behavior DBPROP_INIT_PROMPT3
Session IGetDataSource
IOpenRowset
ISessionProperties
ISupportErrorInfo
Rowset IAccessor IRowsetChange
IColumnsInfo
IConnectionPointContainer for:

- IDBAsynchNotify

- IRowsetNotify

IConvertType
IDBAsynchStatus2
IRowset
IRowsetFind
IRowsetIdentity
IRowsetInfo
IRowsetLocate
IRowPosition1
IRowsetScroll
ISupportErrorInfo
Rowset Behavior DBPROP_ CANHOLDROWS DBPROP_ REMOVEDELETED
DBPROP_ CANFETCHBACKWARDS DBPROP_ OWNUPDATEDELETE
DBPROP_ CANSCROLLBACKWARDS DBPROP_ OWNINSERT

1These interfaces are always supported by common components in the SDK. They are never implemented directly by providers.

2Support for IDBAsynchStatus does not require that the provider support any operations asynchronously.  In the non-asynch case, the provider would simply block when creating or initializing the object, and return a completion status whenever GetStatus was called.

3DBPROP_INIT_PROMPT is required if any initialization properties are required in order to connect to the provider. If calling Initialize() succeeds on a freshly created Datasource, the provider does not need to support this property.

Providers

Providers support the native functionality of the data which they expose. However, in order to be consumed by generic consumers, providers must be able to support at least the minimum base consumer functionality defined in the previous table. You can develop a provider to support the minimum level of functionality in one of three ways:

In addition, providers may expose interfaces for the extended functionality described in the next section.

All providers must be either apartment or free threaded, and must support aggregation of the DataSource, Session, and Rowset. If applicable, providers must support the aggregation of the Command and View objects.

Minimum Provider Functionality

It's important for provider writers to implement the full set of interfaces that apply to their particular type of data. At a minimum, the provider must implement the interfaces and behavior listed in Table 2 to be considered a generic OLE DB provider. Providers implementing the minimum provider functionality can rely on common service components available in the SDK to implement the  base consumer functionality, as shown in Figure 1.

Figure 1

Table 2 – Minimum Provider Interfaces

Object Any base provider Updatable providers
DataSource IDBCreateSession
IDBInitialize
IDBProperties
IPersist
DataSource Behavior DBPROP_INIT_PROMPT1
Session IGetDataSource
IOpenRowset
ISessionProperties
Rowset IAccessor IRowsetChange
IConvertType
IColumnsInfo
IRowset
IRowsetIdentity
IRowsetInfo
Rowset Behavior DBPROP_CANHOLDROWS DBPROP_ OWNUPDATEDELETE
DBPROP_OWNINSERT
DBPROP_ REMOVEDELETED

1DBPROP_INIT_PROMPT is required if any initialization properties are required in order to connect to the provider.  If calling Initialize() succeeds on a freshly created Datasource, the provider does not need to support this property.

Supporting an interface means supporting all methods within that interface. No methods return E_NOTIMPL.

Supporting a property means supporting the setting of and associated behavior of all possible values for a property.

Supporting the required rowset properties means that setting any combination of the required properties must yield a rowset that reflects at least those properties. It does not mean that those properties are always true for any rowset if the property has not been requested by the user.

Base Providers

Providers that implement the full set of base interfaces can be consumed by general consumers without the support of additional service components, as shown in Figure 2.

Figure 2

Providers implementing the following functionality in addition to the minimum provider functionality are consumed as base providers:

Table 3 – Full Interfaces for Base Providers

Object Any base provider Updatable providers
DataSource IConnectionPointContainer for:

- IDBAsynchNotify

IDBAsynchStatus
ISupportErrorInfo
Session ISupportErrorInfo
Rowset IDBAsynchStatus
IConnectionPointContainer for:

- IRowsetNotify

- IDBAsynchNotify

IRowsetFind
IRowsetLocate
IRowsetScroll
ISupportErrorInfo
Rowset Behavior DBPROP_ CANSCROLLBACKWARDS1
DBPROP_ CANFETCHBACKWARDS1

1Providers that support the property DBPROP_CANSCROLLBACKWARDS must also support the property DBPROP_CANFETCHBACKWARDS.

Extended Interfaces

General purpose providers may support additional functionality. It is advantageous that providers support as many extended interfaces as apply to their particular type of data.

In addition to common interface extensions, providers may expose specialized interfaces for the following sets of extended functionality:

Table 4 – Extended Interfaces

Object Any provider Updatable providers Transacted providers
DataSource IDataSourceAdmin
IDBInfo2
IPersistFile
Session IDBSchemaRowset ITransactionLocal
IDBView1 ITransactionJoin
IDBCreateCommand2 ITransactionObject
IIndexDefinition
ITableDefinition
View1 IViewFilter
IViewSort
IColumnsInfo
IAccessor
ISupportErrorInfo
IViewRowset
IViewChapter
Command2 IAccessor
ICommand
ICommandText
IColumnsInfo
ICommandPrepare
IColumnsRowset
ICommandProperties
ICommandWithParameters
IConvertType
ISupportErrorInfo
Command Behavior MAXTABLESINSELECT>1
Custom Error ISQLErrorInfo
Multiple Results IMultipleResults
Rowset IColumnsRowset IRowsetResynch
IRowsetIdentity IRowsetUpdate
IRowsetLocate
IChapteredRowset1
IRowsetView1
IRowsetIndex3
Rowset Behavior DBPROP_ LITERALBOOKMARKS DBPROP_CHANGE INSERTEDROWS DBPROP_ COMMITPRESERVE
DBPROP_ ORDEREDBOOKMARKS DBPROP_RETURN PENDINGROWS DBPROP_ ABORTPRESERVE
DBPROP_ LITERALIDENTITY DBPROP_ IMMOBILEROWS
DBPROP_ BOOKMARKSKIPPED
DBPROP_OTHERINSERT
DBPROP_ OTHERUPDATEDELETE
DBPROP_ STRONGIDENTITY
BLOB Support DBPROP_OLEOBJECTS
DBPROP_MULTIPLE STORAGEOBJECTS
DBPROP_STRUCTURED STORAGE
ISequentialStream
IStream
IStorage
ILockBytes
DBPROP_BLOCKING STORAGEOBJECTS=FALSE

1Extended functionality that may be exposed by providers which support Rowset Processing.

2Extended functionality that may be exposed by providers which support Commands.

3Extended functionality that may be exposed by providers which support Index navigation.

Generic consumers must be prepared for providers that don't support the extended interfaces. Consumers can handle such providers in several different ways: