5.7.Objects and Interfaces Implemented by the OpenLink Provider
The OpenLink provider implements four OLE DB objects.
-
The data source object, which enables consumers to connect and initialize the interaction with the database.
-
The session, which enables consumers to create a rowset for the data set in the database.
-
The rowset, which exposes a data set to the consumer.
-
The command, which enables consumers to create a SQL string for the data in the database.
These objects, along with the interfaces implemented in the OpenLink provider, are described below.
Table5.2.OLE-DB Interfaces Implemented
Interface name | Method name |
---|---|
IDBInitialize |
Initialize Uninitialize |
IDBCreateSession |
CreateSession |
IDBProperties |
GetProperties GetPropertyInfo SetProperties |
IPersist |
GetClassID |
IGetDataSource |
GetDataSource |
ICommand |
Cancel Execute GetDBSession |
ICommandText |
GetCommandText SetCommandText |
ICommandProperties |
GetProperties SetProperties |
ICommandPrepare |
Prepare Unprepare |
IOpenRowset |
OpenRowset |
IColumnsInfo |
GetColumnInfo MapColumnIDs |
IConvertType |
CanConvert |
IAccessor |
AddRefAccessor CreateAccessor GetBindings ReleaseAccessor |
IRowset |
AddRefRows GetData GetNextRows ReleaseRows RestartPosition |
IRowsetInfo |
GetProperties GetReferencedRowset GetSpecification |
IRowsetChange |
DeleteRows InsertRow SetData |
IRowsetLocate |
Compare GetRowsAt GetRowsByBookmark Hash |
ISessionProperties |
GetProperties SetProperties |
ISupportErrorInfo |
InterfaceSupportsErrorInfo |
IErrorInfo |
GetDescription GetGUID GetHelpContext GetHelpFile GetSource |
IErrorLookup |
GetErrorDescription GetHelpInfo ReleaseErrors |
ISQLErrorInfo |
GetSQLInfo |
5.7.1.Invoking the OpenLink Provider
The OpenLink ODBC Provider (oploleod.dll) can be loaded using a variety of methods, depending on your programming environment.
Using CoCreateInstance
If you are writing an application which calls the OLE DB API directly, a consumer typically creates a provider's Data Source object by passing the provider's class identifier (CLSID) to the COM CoCreateInstance function and requesting the IDBInitialize interface. Every OLE DB provider declares a unique CLSID for itself. The CLSIDs for the OpenLink ODBC Provider, the OpenLink Error Lookup Service and the OpenLink ODBC Data Source Enumerator are given in the example below, which declares macros for each of the class identifiers.
#ifdef DBINITCONSTANTS // Provider CLSID EXTERN_C const CLSID CLSID_OPLOLEDB = {0X2D93A18D, 0XAC86, 0X11D1, {0X9C, 0XEA, 0XE5, 0X2A, 0X53, 0XBE, 0XA0, 0X7D}}; // OpenLink Error Lookup Service CLSID EXTERN_C const CLSID CLSID_OPLOLEDB_ERROR = {0X2D93A18E, 0XAC86, 0X11D1, {0X9C, 0XEA, 0XE5, 0X2A, 0X53, 0XBE, 0XA0, 0X7D}}; // OpenLink ODBC Data Source Enumerator CLSID EXTERN_C const CLSID CLSID_OPLOLEDB_ENUM = {0X2D93A190, 0XAC86, 0X11D1, {0X9C, 0XEA, 0XE5, 0X2A, 0X53, 0XBE, 0XA0, 0X7D}}; #else //DBINITCONSTANTS EXTERN_C const CLSID CLSID_OPLOLEDB; EXTERN_C const CLSID CLSID_OPLOLEDB_ERROR; EXTERN_C const CLSID CLSID_OPLOLEDB_ENUM; #endif //DBINITCONSTANTS IDBInitialize * pIDBInitialize; HRESULT hr; hr = CoCreateInstance(CLSID_OPLOLEDB, NULL, CLSCTX_INPROC_SERVER, IID_IDBInitialize, (void**) &pIDBInitialize); if (FAILED(hr)) { // Display error ... }
5.7.2.Connecting from ADO or .Net
The provider's name is "OpenLinkODBC". The OpenLink OLE DB Provider is invoked from ADO or .Net using a connection string which typically takes the following form:
Provider=OpenLinkODBC; Data Source=w2ks2;User ID=SCOTT;Password=tiger;Extended Properties="Cursors=Driver;BookmarkDefault=On";
5.7.3.Initialization Properties
When the consumer calls IDBInitialize::Initialize, the OpenLink Provider calls the UDBC/ODBC functions SQLSetConnectOption, to set various connection options, and SQLDriverConnect, to connect to an ODBC data source. The values passed to SQLSetConnectOption and SQLDriverConnect come from the initialization properties set by the consumer.
When the data source object is first created, the value of each initialization property is set to a default value.
Initialization Properties Used
The following lists the initialization properties currently used by the OpenLink Provider. Any properties not listed are not used by the OpenLink Provider.
Table5.3.OLE-DB Initialization Properties
Property | Description |
---|---|
DBPROP_AUTH_PASSWORD | Passed as the value of the PWD keyword in SQLDriverConnect. |
DBPROP_AUTH_USERID | Passed as the value of the UID keyword in SQLDriverConnect. |
DBPROP_INIT_CATALOG | Sets the initial catalog for data sources which recognise catalogs. |
DBPROP_INIT_DATASOURCE | Passed as the value of the DSN keyword in SQLDriverConnect. |
DBPROP_INIT_HWND |
Passed as the value of the hwnd argument in SQLDriverConnect. |
DBPROP_INIT_MODE |
Mapped to the ODBC connect option SQL_ACCESS_MODE. |
DBPROP_INIT_PROMPT |
Passed as the value of the fDriverCompletion parameter in SQLDriverConnect. |
DBPROP_INIT_PROVIDERSTRING | Specifies extended properties for controlling ODBC Cursor Library usage (through the 'Cursors' keyword) and the whether bookmarks are exposed by default on rowsets (through the 'BookmarkDefault' keyword). |
DBPROP_INIT_TIMEOUT | Mapped to the ODBC connect option SQL_LOGIN_TIMEOUT. |
Setting and Getting Provider Properties
The data source object is the first object created when a consumer instantiates the provider by calling CoCreateInstance.
The data source object provides the starting point for communications between the provider and consumer. For example, a consumer can call CoCreateInstance and request an IDBInitialize interface pointer to instantiate a data source object. The provider has a CLSID (class ID) that is stored in the Windows Registry. The consumer can use this CLSID with CoCreateInstance to instantiate the data source object. The OpenLink provider setup program registers the OpenLink provider in the Windows Registry.
The data source object is responsible for setting and returning information about the properties supported by the provider and exposing the list of supported keywords and literals. This functionality is supported through the mandatory IDBProperties interface and the optional IDBInfo interface. The IDBProperties interface contains three methods:
-
GetProperties returns the list of properties currently set on the data source object.
-
GetPropertyInfo returns information about supported rowset and data source properties.
-
SetProperties sets the properties on the data source object.
The IDBInfo interface contains two methods:
-
GetKeywords returns a list of supported keywords.
-
GetLiteralInfo returns information about literals used in text commands.
5.7.4.Initializing and Uninitializing the Data Source Object
The IDBInitialize interface contains two methods: Initialize and Uninitialize.
Initialize enables consumers to explicitly initialize a data source object. Consumers must set properties on the data source object before attempting to initialize it; and consumers must supply a valid datasource name to the database in IDBProperties::SetProperties. If the datasource is invalid, the OpenLink provider returns an E_FAIL error on initialization.
Uninitialize enables consumers to return the data source object to an uninitialized state. It is an error to call IDBInitialize::Uninitialize when there are open sessions or rowsets on the data source object.
5.7.5.Creating a Session
After you initialize the data source object, you must create a session object to manage the session and provide the framework needed to create a rowset with IOpenRowset::OpenRowset. The IDBCreateSession::CreateSession interface on the data source object enables you to create a new session object and returns an interface pointer to the session.
Once the session has been created, the provider must expose the interface pointer to the data source object that created the session. This interface pointer is exposed through the mandatory interface IGetDataSource.
5.7.6.Creating a Rowset
The session contains the interface that enables consumers to open a database and create a rowset object containing all rows in the database. The OpenLink provider implements both the IOpenRowset, and the ICommand (and associated ICommandText, ICommandProperties) interfaces on the session to create a rowset..
Instantiating and Exposing a Rowset
The IOpenRowset interface contains a single method: OpenRowset. IOpenRowset is a required interface on the session. IOpenRowset::OpenRowset can be used by consumers that do not support command objects to generate a rowset of all rows in a table or index.
The ICommand interface contains the method: Execute. ICommand::Execute generates a rowset from the SQL query set by ICommandText::SetCommandText.
Consumer and Provider Interactions with the Rowset
After receiving the rowset interface pointer, the consumer can request rowset metadata from the provider through IColumnsInfo. The consumer then creates bindings by requesting IAccessor from the provider and specifying the bindings through IAccessor::CreateAccessor. The provider returns a handle to the accessor to the consumer.
The consumer then requests a number of rows from the provider using IRowset::GetNextRows. The provider retrieves the data for these rows and stores it in the data cache. The provider then returns an array of row handles to the consumer. Each row handle returned by the provider has an initial reference count of one. The consumer is then free to get the data for any rows from the provider using GetData. The consumer supplies GetData with the row handle, the handle of an accessor, and the buffer location into which to return the data; the provider copies the data to the location specified by the consumer.
To update rows, consumers call IRowsetChange::SetData, which sets the data in the data cache to the values specified by the consumer. To delete rows from the rowset, the consumer calls IRowsetChange::DeleteRows. To insert rows into the rowset, the consumer calls IRowsetChange::InsertRow. Note that the OpenLink Provider is not able to fetch back a newly inserted row if the underlying datasource does not provide this functionality.
When the consumer makes any change to data in the data cache, the effects of the change are written to the data source immediately. OLE DB specifies a change-buffering model, which enables the consumer to make changes that are not realized until the consumer calls IRowsetUpdate::Update; this model is not supported by the OpenLink provider.
When the consumer has finished working with a row, it can release the row by calling IRowset::ReleaseRows. ReleaseRows simply decrements the reference count on the row in the data cache. If the reference count for that row reaches zero, the row data is released from the data cache.
5.7.7.Exposing Metadata
the OpenLink Provider expose information about the columns of a rowset through IColumnsInfo. The information for each column is returned in a DBCOLUMNINFO structure. OLE DB also has an optional metadata interface, IColumnsRowset; the OpenLink provider does not implement this interface.
The GetColumnInfo method returns metadata that is most commonly used by consumers: column ID, column name, the ordinal number of the column in the rowset, the column's data type, and so on.
The provider returns the information in an array of DBCOLUMNINFO structures, one DBCOLUMNINFO structure per column in the rowset. The order of the structures returned in the array is the order in which the columns appear in the rowset.
IColumnsInfo
Columns that have an ODBC SQL type of SQL_LONGVARCHAR or SQL_LONGVARBINARY are returned as type DBTYPE_BYTES or DBTYPE_STR, and the DBCOLUMNFLAG_ISLONG is set in the dwFlags element of the DBCOLUMNINFO structure.
Returning Column Ordinals
Columns in a rowset are identified by a column ID, which is a value of type DBID in the DBCOLUMNINFO structure.
The MapColumnIDs method returns column ordinals for all column IDs provided in the rgColumnIDs array. Column ordinals do not change during the life of the rowset, but may change between different instances of the rowset.
5.7.8.Supported Conversions
Before the consumer creates an accessor, it can call IConvertType::CanConvert to determine if the provider supports a particular conversion.
Default Data Type Mapping
The OpenLink Provider binds to the ODBC/UDBC data source using the types in the table below. The SQL type is queried using SQLDescribeCol. The sign of the data type (signed/unsigned) is determined using SQLColAttributes. It is used in deciding which C type to use in internal buffers and which type indicator to return through IColumnsInfo::GetColumnInfo.
Table5.4.OLE-DB Data Type Mappings
SQL Type Indicator | Indicator of C Type Used For Internal Buffers | OLE DB Type Indicator |
---|---|---|
SQL_CHAR, SQL_VARCHAR, SQL_LONGVARCHAR, SQL_DECIMAL, SQL_NUMERIC |
SQL_C_CHAR |
DBTYPE_STR |
SQL_BIT |
SQL_C_BIT |
DBTYPE_BOOL |
SQL_TINYINT, SQL_SMALLINT |
SQL_C_USHORT, SQL_C_SSHORT |
DBTYPE_I2 |
SQL_INTEGER |
SQL_C_ULONG, SQL_C_SLONG |
DBTYPE_I4 |
SQL_BIGINT |
SQL_C_STR |
DBTYPE_STR |
SQL_REAL |
SQL_C_FLOAT |
DBTYPE_R4 |
SQL_FLOAT, SQL_DOUBLE |
SQL_C_DOUBLE |
DBTYPE_R8 |
SQL_BINARY, SQL_VARBINARY, SQL_LONGVARBINARY |
SQL_C_BINARY |
DBTYPE_BYTES |
SQL_DATE |
SQL_C_DATE |
DBTYPE_DATE |
SQL_TIME |
SQL_C_TIME |
DBTYPE_DATE |
SQL_TIMESTAMP |
SQL_C_TIMESTAMP |
DBTYPE_DATE |
Length Binding
For types DBTYPE_UI1, DBTYPE_I2, DBTYPE_I4, DBTYPE_I8, DBTYPE_R4, DBTYPE_R8, DBTYPE_CY, DBTYPE_NUMERIC, the length binding is always set to the fixed size of the destination binding type, rather than the internal source type.
Supported Data Conversions
The follwoing table outlines the supported type conversion implemented in the OpenLink provider. An 'X' means supported, and '-' means not supported.
Table5.5.OLE-DB Data Type Conversions
I1 | I2 | I4 | I8 | UI1 | UI2 | UI4 | UI8 | R4 | R8 | CY | DEC | NUM | BOOL | DATE | DBDATE | DBTIMESTAMP | DBTIME | BYTES | BSTR | STR | WSTR | DISP | UNK | GUID | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
I1 |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
- |
- |
- |
- |
- |
X |
X |
X |
X |
- |
- |
- |
I2 |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
- |
- |
- |
- |
- |
X |
X |
X |
X |
- |
- |
- |
I4 |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
- |
- |
- |
- |
- |
X |
X |
X |
X |
- |
- |
- |
I8 |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
- |
- |
- |
- |
- |
X |
X |
X |
X |
- |
- |
- |
UI1 |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
- |
- |
- |
- |
- |
X |
X |
X |
X |
- |
- |
- |
UI2 |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
- |
- |
- |
- |
- |
X |
X |
X |
X |
- |
- |
- |
UI4 |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
- |
- |
- |
- |
- |
X |
X |
X |
X |
- |
- |
- |
UI8 |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
- |
- |
- |
- |
- |
X |
X |
X |
X |
- |
- |
- |
R4 |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
- |
- |
- |
- |
- |
X |
X |
X |
X |
- |
- |
- |
R8 |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
- |
- |
- |
- |
- |
X |
X |
X |
X |
- |
- |
- |
CY |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
- |
- |
- |
- |
- |
X |
X |
X |
X |
- |
- |
- |
DEC |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
- |
- |
- |
- |
- |
X |
X |
X |
X |
- |
- |
- |
NUM |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
- |
- |
- |
- |
- |
X |
X |
X |
X |
- |
- |
- |
BOOL |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
X |
- |
- |
- |
- |
X |
X |
X |
X |
- |
- |
- |
DATE |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
X |
X |
X |
X |
X |
X |
X |
X |
- |
- |
- |
DBDATE |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
X |
X |
- |
X |
X |
X |
X |
X |
- |
- |
- |
DBTIME |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
X |
- |
X |
X |
X |
X |
X |
X |
- |
- |
- |
DBTIMESTAMP |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
X |
X |
X |
X |
X |
X |
X |
X |
- |
- |
- |
BYTES |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
- |
X |
X |
X |
X |
X |
X |
X |
X |
BSTR |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
- |
- |
X |
STR |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
- |
X |
X |
WSTR |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
X |
- |
X |
X |
DISP |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
X |
- |
- |
- |
X |
X |
- |
UNK |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
X |
- |
- |
- |
- |
X |
- |
GUID |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
X |
X |
X |
X |
- |
- |
X |
5.7.9.Creating and Using Accessors
Consumers describe the memory structure for their buffers through a process called binding. An accessor is a group of bindings. The OpenLink provider does not currently support reference accessors, which allow the consumer direct access to the rowset's data cache.
Accessors are implemented through IAccessor. Accessors are created with IAccessor::CreateAccessor and released with IAccessor::ReleaseAccessor. IAccessor::GetBindings can be used to determine the bindings in an existing accessor. IAccessor::AddRefAccessor enables the consumer to add a reference count to an existing accessor.
Accessors may not always be validated immediately at the time of creation. Instead, they may be validated at the time the first row is fetched. Errors will be returned at the first attempt to use such an accessor.
Creating an Accessor
CreateAccessor associates a set of bindings with an accessor handle that is used to send data to or fetch data from the rowset's data cache. The OpenLink provider supports only the DBACCESSOR_ROWDATA accessor flag, which specifies that the accessor is to be used for rowset data.
Returning Accessor Bindings
GetBindings returns the bindings in an existing accessor.
Adding a Reference Count to an Existing Accessor
AddRefAccessor adds a reference count to an existing accessor.
Releasing an Accessor
ReleaseAccessor decrements the reference count on an accessor; when the reference count reaches zero, the accessor is released.
5.7.10.Rowset MetaData
IRowsetInfo enables consumers to query the properties of a rowset through IRowsetInfo::GetProperties. Consumers can get an interface pointer to the object that created the rowset by calling IRowsetInfo::GetSpecification.
IRowset provides methods for fetching rows sequentially, exposing data from those rows to consumers, and managing the rows in the rowset. IRowset contains five methods: AddRefRows, GetData, GetNextRows, ReleaseRows, and RestartPosition.
Incrementing the Reference Count on Row Handles
AddRefRows increments the reference count on the row handles supplied by the caller. AddRefRows enables consumers to make multiple references to a row in the data cache.
Populating the Data Cache
IRowset::GetNextRows gets the next sequence of rows from the datasource and places them in the rowset's data cache. When GetNextRows is first called, it starts at the beginning of the rowset. After that, GetNextRows maintains information about its current position so it can proceed forward from that position. The OpenLink provider also provides support for IRowset::RestartPosition, which repositions the next fetch position to the beginning of the rowset.
Retrieving Data from the Data Cache
IRowset::GetData enables consumers to retrieve data from the data cache. GetData uses the bindings in the accessor to determine how the data should be returned and what data should be returned to the consumer's buffer. Then, GetData converts the data in the cache to the type specified in the binding and transfers the converted data to the consumer.
Decrementing the Reference Count on Row Handles
IRowset::ReleaseRows decrements the reference count on the rows specified. A consumer must call ReleaseRows once for each time a row was fetched or each time the row had its reference count incremented by AddRefRow. When the reference count reaches zero, the row is released if the rowset is in immediate update mode.
In providers that implement IRowsetUpdate, rows are released unless there are pending changes on the row. The OpenLink provider does not implement this interface; the OpenLink provider always performs rowset updates in immediate mode, which means that changes are immediately applied to the underlying data source. Therefore, the OpenLink provider does not recognize any changes as pending.
Returning to the First Row of the Rowset
IRowset::RestartPosition moves the next fetch position used by GetNextRows to the first row of the rowset.
Updating Rows
IRowsetChange enables consumers to change the values of columns in a row of data. If the consumer wants to change the data, it must first construct an accessor for the columns to be changed. IRowsetChange contains three methods: DeleteRows, InsertRow, and SetData.
Deleting Rows
IRowsetChange also enables consumers to delete rows from the rowset. IRowsetChange::DeleteRows deletes rows from the rowset and are applied to the data source immediately.
IRowsetChange
IRowsetChange is implemented using the UDBC/ODBC function SQLSetPos. It therefore can be exposed only when the underlying datasource supports SQLSetPos. Newly inserted rows cannot be updated.