---------------------------------------------------------
MICROSOFT OLE DB VERSION 2.0 README FILE
(c) 1998 Microsoft Corporation. All rights reserved.
This document provides late-breaking or other information that supplements
the Microsoft Data Access SDK documentation.
--------
CONTENTS
- PRODUCT DESCRIPTION
- WHAT'S NEW IN OLE DB 2.0
- PROPOSED TECHNOLOGIES
- TECHNICAL NOTES
- KNOWN BUGS, LIMITATIONS, AND WORKAROUNDS
-----------------------
1. PRODUCT DESCRIPTION
For a complete overview of OLE DB, see the OLE DB Overview in the Data
Access SDK.
------------------
2. NEW FEATURES
A number of new features, enhancements, tools, and components are included
in version 2.0 of OLE DB to help developers leverage the power of OLE DB.
Some of the major new features include:
* Additions to the OLE DB specification
* New components
* Updated components
These new features are described in more detail below.
2.1 Additions to the OLE DB Specification
The OLE DB 2.0 specification is a fully backward-compatible upgrade to the
OLE DB 1.x specification. Major areas of enhancement include:
* The ability to express hierarchical data through rowsets.
* The ability to represent an index and data in the same rowset.
* The ability to create tables with constraints and to add or drop
constraints through common methods.
* The addition of new data types for variable-length numeric, filetime,
and propvariant data types.
* The ability to persist a command as a stored procedure or view
definition.
* New interfaces for persisting or loading connection information
through strings.
* Numerous clarifications and fixes to the documentation.
2.2 New OLE DB Core Components
There are several new components provided as part of the core Microsoft
Data Access Components:
* Data Links -- The Data Links component provides a common user
interface for creating and managing connections to OLE DB data
sources. To launch data links from an application, you can:
* Call IPromptInitialize::PromptDatasource, or
* Double-click Data Links in the Control Panel, or
* Either double-click, or right-click and select Properties, from
a data link (.udl) file.
* Service Components -- OLE DB service components provide guaranteed
functionality to OLE DB consumers above the minimum level required by
OLE DB providers. The OLE DB 2.0 service components support:
scrolling, Find, and bookmark support against any minimum level OLE DB
provider; updating against SQL providers; resource pooling for pooling
connections in a frequent connect/disconnect scenario; and automatic
transaction enlistment in a Microsoft Transaction Server environment.
* Data Shape Provider -- The OLE DB Data Shape Provider enables ADO or
OLE DB consumers to execute hierarchical queries and navigate through
hierarchical recordsets or rowsets.
* Native SQL Server Provider -- The SQL Server native OLE DB provider
provides direct access to SQL Server, without going through ODBC.
* Native Oracle Provider -- The Oracle native OLE DB provider provides
direct access to Oracle data, without going through ODBC. This native
Oracle provider includes support for data types such as negative-scale
numeric data types that cannot be expressed through ODBC.
* Native OLE DB Provider for Microsoft Access files -- The OLE DB
Provider for Microsoft Access files provides direct, efficient access
to Microsoft Access .mdb files.
* New Technical Papers -- For more information on OLE DB, see the
technical papers included in:
DA SDK Documentation \ Microsoft Data Access Technical Articles \
OLE DB
2.3 Updated OLE DB Core Components
The following OLE DB core components have been significantly enhanced for
the 2.0 release:
* Updated Header (Oledb.h) and Lib (Oledb.lib) Files -- The MSDASDK
contains the most recent Oledb.h and Oledb.lib files. The Oledb.h file
must be located first in the INCLUDE path, before any older versions. In
the Integrated development environment for Visual C++ 6.0, this is
accomplished by going to:
Tools\Options\Directories\Include Files
and putting Oledb.h from the MSDASDK before the one automatically
included by Visual C++ 6.0.
* OLE DB Provider for ODBC -- The ODBC provider delivers rich, high-
performance access to any ODBC database. The 2.0 version of this provider
contains better handling of long data as well as performance and scaling
enhancements. For more information, refer to the ODBC Provider Help in
the Microsoft Data Access SDK under \OLE DB\OLE DB Providers\OLE DB ODBC
Provider.
* Data Conversion Library -- The Data Conversion Library is a common
conversion library that makes it easy for OLE DB provider writers to
expose a rich set of data conversions to OLE DB consumers. The latest
data conversion library provides support for the new data types and
conversion options defined in the OLE DB 2.0 specification. For more
information about using this library, see the Help in the Microsoft Data
Access SDK under \OLE DB\OLE DB Core Components. Important Note: The
updated version of the data conversion library requires the latest
version of Oleaut32.dll. The updated version of Oleaut32.dll is available
as part of Windows NT 4.0, or through the latest Windows 95 Service Pack.
2.4 Updated OLE DB Test Components
The following OLE DB test components have been significantly enhanced for
the 2.0 release:
* Test Suites -- The test suites shipped as source code in the Data Access
SDK 2.0 have been greatly improved. The new tests are more complete and
at the same time more generic, with a number of provider assumptions
removed. The tests that correspond to the minimal provider interfaces,
along with a new set of basic ADO compatibility tests, are now supported
as a proposed set of conformance tests.
* RowsetViewer -- The RowsetViewer is a sample application that was
previously shipped with OLE DB 1.5. The 2.0 release of this component is
greatly improved, and provides an easy way to call almost any method
within your provider in an ad hoc fashion. Although the sample code is
still provided so that you can see how this program works, the program
itself now provides sufficient functionality for testing and debugging
providers.
* LTM -- The Local Test Manager is a tool for running the tests shipped in
the DA SDK 2.0, or custom tests. LTM has been significantly improved and
simplified for the 2.0 release. There is now a single executable file,
which makes it easier to install, set up, and debug tests.
* ITest Ad Hoc Tool -- The version 2.0 release of the Test Tools has been
enhanced to provide better support for creation and management of views
through the ad hoc tool as well as representing OLE DB for OLAP dataset
objects.
------------------------
3. PROPOSED TECHNOLOGIES
OLE DB version 2.0 includes a proposed OLE DB leveling specification and
conformance tests. They are highlighted in this version so developers can
provide feedback prior to their final release. Each of these technologies
is described in more detail below.
(For more information on conformance testing in general, see section 3 in
the Data Access SDK release notes, DASDKreadme.txt.)
3.1 OLE DB Leveling Specification
OLE DB leveling improves interoperability between OLE DB data stores and
applications by defining sets of interfaces and functionality. The leveling
document describes two levels of functionality -- one for data stores and
another for applications. The difference in functionality between the
minimum data store functionality and the base consumer functionality is
made up by components, called "service components," that will provide a
common implementation of extended functionality where required by the
application and not implemented by the data store.
* Data stores implementing at least the minimum provider functionality can
be assured that they will work well with a wide variety of tools and
applications. Data stores are encouraged to implement additional
interfaces that expose native functionality above this minimum provider
level, but must meet at least this level of functionality to be
considered an interoperable OLE DB data store.
* Applications that consume only the base consumer interfaces can be
assured that they will work well with any OLE DB-compliant data store.
Applications can consume additional interfaces outside the base consumer
level, but should have conditional code to handle providers that don't
support that extended functionality if they want to work with any OLE DB-
compliant data store.
NOTE: This leveling document is being presented as a proposed set of
requirements for any OLE DB provider. OLE DB data stores should implement
at least the functionality specified by the minimum provider level in order
to interoperate with the greatest number of consumers and to prepare for
future conformance programs.
3.2 OLE DB Conformance Tests
In conjunction with the leveling of the OLE DB specification described
above, Microsoft will provide a set of conformance tests that verify that
OLE DB data stores correctly implement at least the minimum provider
functionality. The proposed conformance tests are shipped in the
/Conformance/Tests/Proposed directory of the MSDASDK, and include the
following files that test the indicated interfaces:
Test Name Interface
------------------------------
IACCESSR IAccessor
ICOLINFO IColumnsInfo
ICNVTTYP ICanConvertType
IDBCRSES IDBCreateSession
IDBINIT IDBInitialize
IDBPRPTS IDBProperties
IGETDSO IGetDatasource
IOPENRW IOpenRowset
IPERSIST IPersist
IROWSET IRowset
IROWCHNG IRowsetChange::SetData
IROWDEL IRowsetChange::Delete
IROWIDEN IRowsetIdentity
IROWINFO IRowsetInfo
IROWNEW IRowsetChange::InsertRow
ISESPRPT ISessionProperties
THREADS Free-threaded tests for providers marked "Free" or
"Both"
DATALITE Data Conversion tests
To prepare for the conformance tests, OLE DB 2.0 data stores are encouraged
to implement the minimum provider functionality and run the set of proposed
tests that correspond to that minimum provider level. Data stores that
support additional functionality should also run the tests that correspond
to that additional functionality. Additional tests for extended
functionality are also available in an unsupported directory included in
the MSDASDK.
For more information on the conformance tests, see the "Conformance Testing
in OLE DB 2.0" paper in the Data Access SDK under \Microsoft Data Access
SDK\Microsoft Data Access Technical Articles. (Open the Overview, then
click the link to the paper.) Also see the release notes for Conformance
Testing 2.0 in the Data Access SDK release notes, DASDKReadme.txt.
------------------
4. TECHNICAL NOTES
4.1 Connecting to a Provider
In OLE DB 1.x, consumers generally connected to a provider through the root
enumerator, or by passing the provider's CLSID to CoCreateInstance, such as
the following:
//Create an instance of CLSID_MSDASQL
CoCreateInstance(CLSID_MSDASQL, NULL, CLSCTX_INPROC_SERVER,
IID_IDBInitialize,(void**)pIDBInitialize);
In order to take advantage of the common services provided as part of OLE
DB 2.0, OLE DB consumers need to create provider instances by calling
methods in either IDataInitialize or IDBPromptInitialize. The
IDataInitialize interface is supported through the Service Component
Manager, which can be instantiated by using CLSID_MSDAINITIALIZE. The
IDBPromptInitialize interface is supported by the Data Links component,
which can be instantiated by using CLSID_DataLinks.
For example, to create an instance of a provider that can take advantage of
OLE DB 2.0 services, based on the provider's CLSID, the above code would
look like the following:
//Create an instance of the OLE DB Initialization Component
CoCreateInstance(CLSID_MSDAINITIALIZE, NULL, CLSCTX_INPROC_SERVER,
IID_IDataInitialize,(void**)pIDataInitialize);
//Create an instance of CLSID_MSDASQL with supported Services
pIDataInitialize->CreateDBInstance(CLSID_MSDASQL, NULL,
CLSCTX_INPROC_SERVER,
NULL, IID_IDBInitialize,(IUnknown**)pIDBInitialize);
4.2 Visual C++ Requirements for Alpha
On Alpha, OLE DB 2.0 requires Visual C++ 5.0, Service Pack 3, or later.
4.3 The colid Member of DBPROP Structure
Properties are defined by a DBPROP structure that includes the property ID,
options, status, and value. Because some properties apply to individual
columns, the DBPROP structure also contains a colid element to specify the
column to which the property applies. For properties that don't apply to
individual columns, the colid element is not used by the provider.
However, it is important to note that the OLE DB consumer must set this
element to DB_NULLID for properties that do not apply to columns, as
opposed to leaving it uninitialized. Leaving the element uninitialized will
cause problems for remoting or other services that copy DBPROP structures
without knowledge of which properties apply to columns and which do not.
4.4 OLE DB Resource Pooling
4.4.1 Overview
OLE DB 2.0 provides common services that improve the native functionality
and performance of the OLE DB provider. Services include the ability to
scroll or find over provider's rowsets that don't natively support such
functionality, as well as performance and scaling features such as resource
pooling and automatic transaction enlistment within a Microsoft Transaction
Server environment.
The resource pooling and automatic transaction enlistment features of the
OLE DB 2.0 service components go a long way toward helping providers and
consumers build good, scalable applications. However, there are rules that
both the provider and consumer must take into account in order to make the
most out of these services. The following sections describe how OLE DB
resource pooling and transaction enlistment work, and how to leverage them
from your provider or application code.
4.4.2 Details
OLE DB services are automatically invoked any time the consumer creates an
OLE DB data source object through IDataInitialize or IDBPromptInitialize.
OLE DB services are automatically invoked by default when using ADO.
When the application creates an OLE DB data source object by using one of
the above methods, OLE DB services query the provider for supported
information, and provide a proxy data source object (DPO) to the
application. This DPO appears to the consuming application like any other
data source object, but setting properties merely caches the information in
the local proxy.
When the application calls IDBInitialize::Initialize(), the DPO checks
whether any connections already exist that match the specified connection
information and are not in use. If so, rather than creating a new object,
setting properties, and establishing a new connection to the database, the
DPO merely uses the existing initialized data source object.
When the application releases the data source object, it is returned to the
pool. Any data source that is released by the application and not reused
after 60 seconds is automatically released.
Immediately after initializing the data source, the resource pool creates a
session and caches it internally. The first time the application asks for a
Session object, the DPO returns this cached session. When the application
releases the session, the resource pool continues to hold onto it until the
data source is timed out. This is because transaction enlistment goes on at
the session level and can be very expensive. By holding onto the Session
object, the resource pool can ensure that multiple connections enlisted in
the same transaction don't have to re-enlist each time.
OLE DB resource pooling implements multiple, homogeneous pools. That is,
there is a separate pool of connections for each combination of connection
information used. This makes it very fast to identify a candidate
connection. To find a match, it is not necessary to compare multiple
different types of connections within one pool. Because the pool is locked
until a match is found, implementing these separate pools reduces
contention, which is extremely important in a scalable environment. (For
more information about contention, see section 4.4.3 in this document.)
The OLE DB services also cache provider information, such as initialization
properties, default registry information, and even the provider's class
factory. After this information has been obtained for a provider, OLE DB
services never have to ask again. Registry lookups can be expensive and
retrieving property information requires task memory allocations.
Therefore, reducing these calls greatly improves scalability and
performance.
Within a Microsoft Transaction Server environment, the OLE DB services
automatically detect whether or not the calling thread is in a distributed
transaction, and enlist the connection in the transaction, if necessary, by
calling the provider's ITransactionJoin interface.
4.4.3 Writing Scalable OLE DB Providers
There are a few simple rules for writing good, scalable OLE DB providers.
Most rules for working in a multithreaded environment have to do with
making sure that one thread isn't blocking the other threads from doing
work.
The first rule in writing scalable, multithreaded providers is to minimize
the use of global critical sections. Global critical sections prevent other
threads from doing work until the thread that holds a lock completes. When
providers make use of global critical sections, adding more threads
actually hurts performance because the threads continually block each
other. This thread-blocking is known as "contention."
A second rule for writing good scalable providers is to reduce the number
of memory allocations. The use of mpheap, which is shipped as part of MSDN,
improves memory allocation and management in Windows NT 4.0 by allocating
application memory from large blocks of memory acquired from the operating
system and being less aggressive about freeing/compacting that memory. Note
that the Windows NT 5.0 memory allocator will have much of this memory
management built in.
When memory is allocated by one component and freed by another, the memory
must be allocated through the operating system's task memory allocator.
Memory acquired and freed by using this allocator is expensive, so
providers should ensure that the only time they use the task memory
allocator is for memory that must be passed off to the consumer. Providers
should never use the task memory allocator for allocating memory that they
will free themselves.
Within a Microsoft Transaction Server environment, providers must support
distributed transactions if packages are marked as anything other than
nontransactional. To ensure that your provider can be used for
transactional components within Microsoft Transaction Server, you must
support ITransactionJoin to enlist in a distributed transaction. You should
also support ITransactionJoin with a null value to unenlist from a
transaction that has been completed.
Finally, to leverage the scaling support built into the OLE DB services,
you must ensure that your provider works well with OLE DB pooling.
4.4.3.1 Working with OLE DB Pooling
To work well with OLE DB pooling, or with any OLE DB service, your provider
must support aggregation of all objects. This is a requirement of any OLE
DB 1.5 or later provider. It is critical for leveraging services. Providers
that don't support aggregation cannot be pooled, and no additional services
will be provided.
To be pooled, providers must support the free thread model, or at minimum,
the rental thread model. The resource pool determines the provider's thread
model according to the DBPROP_THREADMODEL property.
If the provider has a global connection state that may change while the
data source is in an initialized state, it should support the new
DBPROP_RESETDATASOURCE property. This property is called before a
connection is reused, and gives the provider the opportunity to clean up
state before its next use. If the provider cannot clean up some state
associated with the connection, it can return DBPROPSTATUS_NOTSETTABLE for
the property, and the connection will not be reused.
Providers that connect to a remote database and can detect whether or not
that connection may be lost should support the DBPROP_CONNECTIONSTATUS
property. This property gives the OLE DB services the ability to detect
dead connections and make sure they are not returned to the pool.
Finally, automatic transaction enlistment generally does not work unless it
is implemented at the same level that pooling occurs. Providers that
support automatic transaction enlistment themselves should support
disabling this enlistment by exposing the DBPROP_INIT_OLEDBSERVICES
property and disabling enlistment if the DBPROPVAL_OS_TXNENLISTMENT is
deselected.
4.4.4 Leveraging Pooling in your OLE DB Application
To leverage pooling in your application, you must make sure OLE DB services
are invoked by obtaining your data source through IDataInitialize or
IDBPromptInitialize. If you directly use CoCreateInstance to invoke the
provider based on the provider's CLSID, no OLE DB services will be invoked.
The OLE DB services will maintain pools of connected data sources as long
as a reference to IDataInitialize or IDBPromptInitialize is held, or as
long as there is a connection in use. Pools will also be maintained
automatically within a Microsoft Transaction Server or Internet Information
Server (IIS) environment. If your application will take advantage of
pooling outside of a Microsoft Transaction Server or IIS environment, you
should keep a reference to IDataInitialize or IDBPromptInitialize, or hold
onto at least one connection. To make sure that the pool does not get
destroyed when the last connection is released by the application, keep the
reference or hold onto the connection for the lifetime of your application.
OLE DB services identify the pool from which the connection will be drawn
at the time of Initialize. After the connection is drawn from a pool, it
cannot be moved to a different pool. Therefore, avoid doing things in your
application that will change initialization information, such as calling
UnInitialize, or calling QueryInterface for a provider-specific interface
prior to calling Initialize. Also, connections established with a prompt
value other than DBPROMPT_NOPROMPT will not be pooled. However, the
initialization string retrieved from a connection established through
prompting can be used to establish additional pooled connections to the
same data source.
Some providers must make a separate connection for each session. These
additional connections must be separately enlisted in the distributed
transaction, if one exists. OLE DB services will cache and reuse a single
session per data source, but if the application requests more than one
session at a time from a single data source, the provider may end up making
additional connections and doing additional transaction enlistments that
are not pooled. It is actually more efficient to create a separate data
source for each session in a pooled environment than to create multiple
sessions from a single data source.
Finally, because ADO automatically makes use of OLE DB services, you can
simply use ADO to establish connections and the pooling and enlistment will
happen automatically!
4.4.5 Enabling/Disabling OLE DB Services
The OLE DB Service Component Manager compares the properties specified by
the consumer to those supported by the provider in order to determine
whether or not individual service components could be invoked in order to
satisfy extended functionality requested by the consumer. For example, if
an application requests a scrollable cursor and the provider only supports
a forward-only cursor, the Service Component Manager will invoke the Client
Cursor Engine service component in order to provide scrollable
functionality. If the application is relying on extended functionality
supported by default on the provider's rowset, and the application does not
explicitly set the properties to request that functionaltiy, the
functionality may not appear on the rowset returned by the Client Cursor
Engine. To be interoperable, applications should always set properties to
explicitly request extended functionality where needed.
In some cases, it may be necessary to disable individual OLE DB services in
order to work well with existing applications that make assumptions about
the characteristics of a provider. OLE DB services provide the ability to
disable individual services, or all services, either on a connection-by-
connection basis or for all applications using a single provider.
4.4.5.1 Enabling/Disabling Services for a Provider
Individual OLE DB services can be enabled or disabled by default for all
applications that access a single provider. This is done by adding an
OLEDB_SERVICES registry entry under the provider's CLSID, with a DWORD
value specifying the services to enable or disable as follows:
Default Services Enabled Keyword Value
----------------------------------------------------------
All services (the default) 0xffffffff
All except Pooling and AutoEnlistment 0xfffffffe
All except Client Cursor 0xfffffffb
All except pooling, enlistment, and cursor 0xfffffff0
No services 0x00000000
No aggregation, all services disabled <missing key>
4.4.5.2 Overriding Provider Service Defaults
The provider's registry value for OLEDB_SERVICES is returned as the default
value for the DBPROP_INIT_OLEDBSERVICES initialization property on the data
source object.
As long as the registry entry exists, the provider's objects will be
aggregated and the user can override the provider's default setting for
enabled services by setting the DBPROP_INIT_OLEDBSERVICES property prior to
initialization. To enable or disable a particular service, the user will
generally get the current value of the DBPROP_INIT_OLEDBSERVICES property,
set or clear the bit for the particular property to be enabled or disabled,
and reset the property. DBPROP_INIT_OLEDBSERVICES can be set directly in
OLE DB, or in the connection string passed to ADO or
IDataInitialize::GetDatasource. The corresponding values to enable/disable
individual services are listed below:
DBPROP_INIT_OLEDBSERVICES
Default Services Enabled Property Value
----------------------------------------------------------------------
All services DBPROPVAL_OS_ENABLEALL
All except Pooling and AutoEnlistment (DBPROPVAL_OS_ENABLEALL
&
~DBPROPVAL_OS_RESOURCEPOOLING)
All except Client Cursor (DBPROPVAL_OS_ENABLEALL
&
~DBPROPVAL_OS_CLIENTCURSOR)
All except pooling, enlistment, and cursor (DBPROPVAL_OS_ENABLEALL
&
~DBPROPVAL_OS_RESOURCEPOOLING
&
~DBPROPVAL_OS_CLIENTCURSOR)
No services ~DBPROPVAL_OS_ENABLEALL
Default Services Enabled Value in Connection String
------------------------------------------------------------------------
All services (the default) "OLE DB Services = -1;"
All except Pooling and AutoEnlistment "OLE DB Services = -2;"
All except Client Cursor "OLE DB Services = -5;"
All except pooling, enlistment, and cursor "OLE DB Services = -7;"
No services "OLE DB Services = 0;"
If the registry entry does not exist for the provider, the Component
Managers will not aggregate the provider's objects, and no services will be
invoked, even if explicitly requested by the user.
4.5 Columns Added by DBPROP_UNIQUEROWS Not Included in GetColumnsInfo
pcCols
OLE DB 2.0 adds a new property, DBPROP_UNIQUEROWS, that allows the provider
to add columns to uniquely identify each row of the rowset. The information
for these columns is available in IColumnsRowset and IColumnsInfo. However,
the count of columns returned by the pcCols argument of
IColumnsInfo::GetColumnsInfo does not include these added columns.
4.6 Textual Description of CANFETCHBACKWARDS and CANSCROLLBACKWARDS
OLE DB defines properties to describe the rowset's ability to fetch or
scroll backward. The textual descriptions listed for these properties in
the OLE DB 2.0 Programmer's Reference are "Fetch Backward" and "Scroll
Backward," respectively. Note that the actual textual description should be
"Fetch Backwards" and "Scroll Backwards," as they were defined in OLE DB
1.x.
4.7 Rowset Properties and Non-Row Returning Commands
Properties may be set on a Command object before execution to influence how
the command is executed. Some of these properties, such as COMMANDTIMEOUT,
affect any command execution. Other properties, such as DBPROP_CANHOLDROWS,
affect the resulting rowset, if any. Properties that don't apply to a
particular statement, such as rowset properties set on a non-row returning
command, should be ignored by the provider when executing the command.
4.8 Next Fetch Position and IRowsetFind::FindNextRows
Calling IRowsetFind::FindNextRows with a cbBookmark value of zero searches
for a column value relative to the current GetNextRows position. Following
the call to FindNextRows, the fetch for GetNextRows is also moved. If a
valid bookmark is passed to FindNextRows, then the fetch position of
GetNextRows is unchanged.
According to the OLE DB 2.0 Programmer's Reference, calling FindNextRows
with a cbBookmark value of zero and a cRows value of zero doesn't fetch any
rows but moves the fetch position to the next match or off the end of the
rowset if no match is found. However, the Programmer's Reference doesn't
say whether the position is before or after the next match.
Calling FindNextRows with a cbBookmark value of zero and a cRows value of
zero moves the next fetch position to the same location as calling
FindNextRows with a cbBookmark value of zero and a cRows value of one. In
general, it is not useful to call FindNextRows with a cRows value of zero.
Furthermore, because of this ambiguity in the Programmer's Reference,
consumers should not rely on the fetch position following a call to
FindNextRows with a cbBookmark value of zero and a cRows value of zero.
4.9 Null pFindValue in IRowsetFind::FindNextRow
The description of the pFindValue argument in IRowsetFind::FindNextRow
states that "If this value is NULL, it is compared to other values
according to the DBPROP_NULLCOLLATION property returned in the Data Source
Information property set." Note that this refers to a pFindValue that
indicates a null column value, via a status value of DBSTATUS_S_ISNULL, or
a variant of type VT_NULL. It is an error (E_INVALIDARG) to pass a null
pointer for pFindValue.
4.10 Changing the Current Catalog
Some providers support changing the current catalog (database) on a
connection through provider-specific mechanisms, such as executing SQL SET
statements. OLE DB provides a data source property, DBPROP_CURRENTCATALOG,
for doing this. Consumers should use this common property mechanism, as
opposed to executing provider-specific statements, in order to ensure
interoperability and to prevent inconsistent provider states.
4.11 Enlisting in Distributed Transaction Outcome Events
OLE transactions define an interface, ITransactionOutcomeEvents, that
components enlisted in a distributed transaction can use in order to get
notified of the outcome of a transaction. The OLE DB specification suggests
that providers register for these outcome events prior to enlisting in a
distributed transaction. However, calling the provider's
ITransactionOutcomeEvents can be expensive, as it results in an extra
message. Further, in some cases the Microsoft DTC does not fire these
events, which results in memory leaks from references on the connection
sink not being released, and in connections being held longer than
necessary. For these reasons, providers should avoid registering for
transaction outcome events.
4.12 Specification Addendums
4.12.1 IRowsetResynch
IRowsetResynch is an interface defined in OLE DB 1.x to allow consumers to
retrieve the current values for rows that may have been changed in the data
store since retrieved. In OLE DB 2.0, IRowsetResynch is superceded by
IRowsetRefresh, which provides better control over when data values are
updated from the data store.
In future releases of OLE DB, a common service component will expose
IRowsetRefresh over providers that currently expose only IRowsetResynch. In
the interim, consumers can work with legacy providers that support only
IRowsetResynch by directly calling that interface. This interface is
documented here for such consumers.
4.10.1.1 IRowsetResynch::GetVisibleData
Gets the data in the data source that is visible to the transaction for the
specified row.
HRESULT GetVisibleData (
HROW hRow,
HACCESSOR hAccessor,
void * pData);
Parameters
hRow [in]
The handle of the row for which to get the visible data.
This can be the handle of a row with a pending delete.
hAccessor [in]
The handle of the accessor to use. If hAccessor is the handle
of a null accessor (cBindings in IAccessor::CreateAccessor was
zero), then GetVisibleData does not get any data values.
pData [out]
A pointer to a buffer in which to return the data. The
consumer allocates memory for this buffer.
Return Code
S_OK
The method succeeded. The status of all columns bound by
the accessor is set to DBSTATUS_S_OK, DBSTATUS_S_ISNULL,
or DBSTATUS_S_TRUNCATED.
DB_S_ERRORSOCCURRED
An error occurred while returning data for one or more
columns, but data was successfully returned for at least
one column.
E_FAIL
A provider-specific error occurred.
E_INVALIDARG
pData was a null pointer and hAccessor was not a null accessor.
E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called and the
object is in a zombie state.
DB_E_BADACCESSORHANDLE
hAccessor was invalid. It is possible for a reference accessor
or an accessor that has a binding that uses provider-owned
memory to be invalid for use with this method, even if the
accessor is valid for use with IRowset::GetData or
IRowsetChange::SetData.
DB_E_BADACCESSORTYPE
The specified accessor was not a row accessor.
DB_E_BADROWHANDLE
DB_E_DELETEDROW
hRow referred to a row for which a deletion had been transmitted
to the data source.
DB_E_ERRORSOCCURRED
Errors occurred while returning data for all columns. To
determine what errors occurred, the consumer checks the
status values.
DB_E_ABORTLIMITREACHED
The provider was unable to retrieve the visible data due
to reaching a limit on the server, such as a query execution
timing out.
DB_E_NEWLYINSERTED
DBPROP_STRONGIDENTITY was VARIANT_FALSE and hRow referred
to a row for which an insertion had been transmitted to the
data source.
DB_E_NOTREENTRANT
The provider called a method from IRowsetNotify in the
consumer and the method has not yet returned.
DB_E_PENDINGINSERT
The rowset was in delayed update mode and hRow referred to
a pending insert row.
If this method performs deferred accessor validation and that validation
takes place before any data is transferred, it can also return any of the
following return codes for the applicable reasons listed in the
corresponding DBBINDSTATUS values in IAccessor::CreateAccessor:
E_NOINTERFACE
DB_E_BADBINDINFO
DB_E_BADORDINAL
DB_E_BADSTORAGEFLAGS
DB_E_UNSUPPORTEDCONVERSION
Comments
This method makes no logical change to the state of the object.
A consumer calls GetVisibleData to retrieve the data in the data source
that is visible to the transaction for the specified row. However,
GetVisibleData does not affect the values in the rowset's copy of the row.
If GetVisibleData fails, the memory to which pData points is not freed but
its contents are undefined. If, before GetVisibleData failed, the provider
allocated any memory for return to the consumer, the provider frees this
memory and does not return it to the consumer.
4.12.1.2 IRowsetResynch::ResynchRows
Gets the data in the data source that is visible to the transaction for the
specified rows and updates the rowset's copies of those rows.
HRESULT ResynchRows (
ULONG cRows,
const HROW rghRows[],
ULONG* pcRowsResynched,
HROW** prghRowsResynched,
DBROWSTATUS** prgRowStatus);
Parameters
cRows [in]
The count of rows to resynchronize. If cRows is zero, ResynchRows
ignores rghRows and reads the current value of all active rows.
rghRows [in]
An array of cRows row handles to be resynchronized. If cRows is
zero, this argument is ignored.
pcRowsResynched [out]
A pointer to memory in which to return the number of rows the
method attempted to resynchronize. The caller may supply a null
pointer if no list is desired. If the method fails, the provider
sets *pcRowsResynched to zero.
prghRowsResynched [out]
A pointer to memory in which to return the array of row handles
the method attempted to resynchronize. If cRows is not zero, then
the elements of this array are in one-to-one correspondence with
those of rghRows. If cRows is zero, the elements of this array
are the handles of all active rows in the rowset. When cRows is
zero, ResynchRows will add to the reference count of the rows whose
handles are returned in prghRowsResynched.
The rowset allocates memory for the handles and the client should
release this memory with IMalloc::Free when no longer needed. This
argument is ignored if pcRowsResynched is a null pointer and must
not be a null pointer otherwise. If *pcRowsResynched is 0 on output
or the method fails, the provider does not allocate any memory and
ensures that *prghRowsResynched is a null pointer on output.
prgRowStatus [out]
A pointer to memory in which to return an array of row status
values. The elements of this array correspond one-to-one with
the elements of *prghRowsResynched. If no errors occur while
resynchronizing a row, the corresponding element of
*prgRowStatus is set to DBROWSTATUS_S_OK. If the method fails
while resynchronizing a row, the corresponding element is set
as specified in DB_S_ERRORSOCCURRED. If prgRowStatus is a null
pointer, no row status values are returned.
The rowset allocates memory for the row status values and
returns the address to this memory; the client releases this
memory with IMalloc::Free when it is no longer needed. This
argument is ignored if pcRowsResynched is a null pointer.
If *pcRowsResynched is zero on output or the method fails,
the provider does not allocate any memory and ensures that
*prgRowStatus is a null pointer on output.
Return Code
S_OK
The method succeeded. All rows were successfully resynchronized.
The following value can be returned in *prgRowSTATUS
======
* The row was successfully resynchronized. The corresponding
element of *prgRowStatus contains DBROWSTATUS_S_OK.
DB_S_ERRORSOCCURRED
An error occurred while resynchronizing a row, but at least
one row was successfully resynchronized. Successes can occur
for the reason listed under S_OK. The following errors can
occur:
* An element of rghRows was invalid or referred to a row that
this thread does not have access to. The corresponding
element of *prgRowStatus contains DBROWSTATUS_E_INVALID.
* Resynchronizing a row was canceled during notification.
The row was not resynchronized and the corresponding element
of *prgRowStatus contains DBROWSTATUS_E_CANCELED.
* An element of rghRows referred to a row for which a
deletion had been transmitted to the data source. The
corresponding element of *prgRowStatus contains
DBROWSTATUS_E_DELETED.
* The row was not resynchronized due to reaching a limit
on the server, such as a query execution timing out.
The error in the corresponding element of *prgRowStatus
contains DBROWSTATUS_E_LIMITREACHED.
* An element of rghRows referred to a row on which a
storage object was open. The corresponding element of
*prgRowStatus contains DBROWSTATUS_E_OBJECTOPEN.
* An element of rghRows referred to a pending insert row.
The corresponding element of *prgRowStatus contains
DBROWSTATUS_E_PENDINGINSERT.
* DBPROP_STRONGIDENTITY was VARIANT_FALSE and an element
of rghRows referred to a row for which an insertion had
been transmitted to the data source. The row was not
resynchronized and the corresponding element of
*prgRowStatus contains DBROWSTATUS_E_NEWLYINSERTED.
E_FAIL
A provider-specific error occurred.
E_INVALIDARG
cRows was not zero and rghRows was a null pointer.
pcRowsResynched was not a null pointer and prghRowsResynched
was a null pointer.
E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called and
the object is in a zombie state.
DB_E_ERRORSOCCURRED
Errors occurred while resynchronizing all of the rows.
Errors can occur for the reasons listed under
DB_S_ERRORSOCCURRED.
DB_E_NOTREENTRANT
The provider called a method from IRowsetNotify in the
consumer and the method has not yet returned.
DB_SEC_E_PERMISSIONDENIED
The consumer did not have sufficient permission to
resynchronize the rows.
Comments
ResynchRows refreshes the values in the rowset's copy of each of the
specified rows with the currently visible contents of the underlying row.
Changes made to the row by the current transaction are always visible to
ResynchRows, including changes made by other rowsets in the same
transaction. Whether changes made by other transactions are visible to
ResynchRows depends on the isolation level of the current transaction.
If a specified row has been deleted from the data source and this deletion
is visible, ResynchRows returns DBROWSTATUS_E_DELETED in the error status
array for the row and the row is treated as a deleted row.
Any changes transmitted to the data source are not lost; they will be
committed or aborted when the transaction is committed or aborted. All
pending changes are lost because they exist only in the rowset's copy of
the row and ResynchRows overwrites the contents of this copy. The pending
change status is removed from the row.
4.12.2 IDcInfo
OLE DB 2.0 includes a redistributable Data Conversion Library (MSDADC.DLL)
that providers can use to perform common conversions. IDcInfo provides
methods for the provider using this conversion library to provide
information, such as provider version, that may affect how conversions are
performed. If IDcInfo::SetInfo is not called, the Data Conversion Library
assumes the provider is a 1.0-compliant provider.
IDcInfo can be obtained by calling QueryInterface from IDataConvert by
using IID_IDcInfo. The documentation of the IDcInfo interface follows.
4.12.2.1 IDCInfo::GetInfo
(
ULONG cInfo,
DCINFOTYPE rgeInfoType[],
DCINFO **prgInfo);
Parameters
cInfo [in]
The number of settings for which to return the information.
rgeInfoType[] [in]
An array of DCINFOTYPE structures. The indicator of the
information type. The Data Conversion component supports
the following infotype:
DCINFOTYPE Description
========== ===========
DCINFOTYPE_VERSION The OLE DB version of the provider
prgInfo[][out]
A pointer to memory in which to return an array of DCINFO
structures.
The DCINFO structure is:
typedef struct tagDCINFO {
DCINFOTYPE eInfoType;
VARIANT vData;
} DCINFO;
The elements of this structure are used as follows:
Element Description
======= ===========
eInfoType The type of information.
vData A VARIANT that contains the information to
be set. For DCINFOTYPE_VERSION, the variant
type is VT_UI4.
4.12.2.1 IDCInfo::SetInfo
HRESULT SetInfo(ULONG cInfo, DCINFO rgInfo[]);
Parameters
cInfo [in]
The number of settings for which to set version information.
rgInfo[] [in]
An array of cInfo DCINFO structures.
-------------------------------------------
5. KNOWN BUGS, LIMITATIONS, AND WORKAROUNDS
5.1 OLE DB Specification Issues
This section details known issues or limitations with the OLE DB 2.0
specification.
5.1.1 Support for Aggregation Required
Each method in OLE DB that generates a new object lists a possible return
code of DB_E_NOAGGREGATION. OLE DB 1.5 and later providers must support
aggregation. For OLE DB 1.5 or later providers, this error should only be
returned when the user specifies a controlling unknown and does not request
IID_IUnknown. The description of this error should be changed as follows:
DB_E_NOAGGREGATION
pUnkOuter was not a null pointer, and the provider is an OLE DB
1.0 or 1.1 provider that does not support aggregation of the
object being created.
pUnkOuter was not a null pointer, and riid was not IID_IUnknown.
5.1.2 Fetch Position After Calling FindNextRow with cRows=0
The description of the cRows argument to FindNextRow calls out that, if
cRows is 0 and there are no other errors, no rows are fetched, but the
fetch position for FindNextRow is moved to the next match. Note that this
is only true if cbBookmark is also zero, indicating that the search was
started from the next fetch position. If cbBookmark is nonzero, then the
fetch position is never changed.
Also, the specification doesn't state whether the fetch position is before
or after the next match. The fetch position should be set as if FindNextRow
was called with cRows=1. Calling FindNextRow with cRows=0 is functionally
equivalent to calling FindNextRow with cRows=1 and then releasing the
retrieved hRow. In general, consumers should not rely on the next fetch
position after calling FindNextRow with cRows=0 and cbBookmark = 0, as some
providers may not position correctly.
5.1.3 Property Description Format
OLE DB consumers, such as the Data Links component, may rely on certain
formatting for property descriptions. Note that the property descriptions
returned by GetPropertyInfo should not be localized, and providers should
use the provided descriptions for OLE DB-defined properties. In addition,
property descriptions must be unique across all initialization properties,
and must not contain the equal sign, or single or double quotation marks.
5.1.4 DB_E_BADSTARTPOSITION Fetching Off a Rowset
OLE DB 1.x attempted to distinguish between fetching immediately before or
after the rowset (DB_S_ENDOFROWSET) versus fetching more than one row
before or after the rowset (DB_E_BADSTARTPOSITION). OLE DB 2.0 providers
should always return DB_S_ENDOFROWSET when the user attempts to fetch off
either end of the rowset. However, consumers should be aware that OLE DB
1.x providers, and some OLE DB 2.0 providers, may continue to return
DB_E_BADSTARTPOSITION when positioning more than one row before the first
row, or more than one row after the last row, of the rowset.
5.1.5 Quoting Names in IOpenRowset::OpenRowset
The Comments section of IOpenRowset::OpenRowset says that consumers should
supply fully qualified names as pTableID on providers that support catalog
or schema names, as described in "Fully Qualified Names" in Chapter 4.
Chapter 4, "Creating a Rowset with IOpenRowset," specifies that consumers
may need to construct a fully qualified table name, and describes quoting
of the individual values of the table name, but does not define the cases
under which consumers should qualify names.
Consumers should provide qualified table names only to reference tables in
other schemas or catalogs. Further, consumers should not quote table names
unless such quoting is required to make table names unambiguous (for
example, to enforce case sensitivity). Providers for which quoting a simple
table name has no meaning may not support quoting tables in OpenRowset
while they do support quoting tables as part of a command text for parsing
reasons. Providers should not require table names to be quoted, and should
guarantee that unquoted table names correctly open the specified table,
even if the table name contains special characters, as long as the table
can be unambiguously identified without quoting.
5.1.6 Returned String Buffers
For method calls that return multiple strings within an allocated string
buffer, consumers should not make assumptions about the relationship
between the individual string pointers and the returned string buffer.
Specifically, the consumer should always treat the returned string values
as read-only memory, and should not assume that the individual string
values all occur within the string buffer. The only requirement on the
provider is that freeing the string buffer releases any task memory
allocated for that call. This applies to the ppDescBuffer returned by
GetPropertyInfo to hold the pwszDescription elements of the DBPROPINFO
structure, as well as the ppStringsBuffer returned by GetColumnInfo to
return string values contained within the DBCOLUMNINFO structure.
5.1.7 Disabling Individual Services
The description of the DBPROP_INIT_OLEDBSERVICES property states that
individual services may be deselected by specifying the bitwise-OR of
DBPROPVAL_OS_ENABLEALL along with the bitwise complement of any services to
be deselected. In fact, individual services are deselected by specifying
the bitwise-AND of DBPROPVAL_OS_ENABLEALL along with the bitwise complement
of any services to be deselected.
For example, DBPROPVAL_OS_ENABLEALL & ~DBPROPVAL_OS_TXNENLISTMENT enables
all services except automatic transaction enlistment in a Microsoft
Transaction Server environment.
5.1.8 Specifying a Null punkTransactionCoord in
ITransactionJoin::JoinTransaction
Consumers may specify a null punkTransactionCoord in
ITransactionJoin::JoinTransaction to unenlist from a coordinated
transaction. The documentation currently states that this is an error
(E_INVALIDARG). It is only legal to unenlist from the coordinated
transaction when the coordinated transaction has completed and there is no
pending work. Calling JoinTransaction at any other time with
punkTransactionCoord returns XACT_E_XTIONEXISTS.
5.1.9 State of a Session After a Coordinated Transaction Has Completed
The OLE DB specification does not define the state of a session once the
coordinated transaction in which it is enlisted completes. Once a session
is enlisted in a coordinated transaction, and that coordinated transaction
completes with no retaining semantics, the session is temporarily in a
zombie state, although the provider may not detect this state until the
consumer executes a method that communicates with the data store. In order
to return the session from a zombie state, the consumer must call
ITransactionJoin::JoinTransaction with a valid punkTransactionCoord to
enlist in a new coordinated transaction, or with a null
punkTransactionCoord in order to unenlist from the coordinated transaction.
5.1.10 New Columns in PROCEDURE_PARAMETERS Rowset
OLE DB 2.0 added two new columns, TYPE_NAME and LOCAL_TYPE_NAME, to the
PROCEDURE_PARAMETERS schema rowset. Note that 1.x providers do not support
these columns, and some OLE DB 2.0 providers may not initially support
these new columns. Consumers should be prepared to handle providers that do
not support these columns.
5.1.11 New Columns in FOREIGN_KEYS Rowset
OLE DB 2.0 added three new columns, FK_NAME, PK_NAME, and DEFERRABILITY, to
the FOREIGN_KEYS schema rowset. Note that 1.x providers do not support
these columns, and some OLE DB 2.0 providers may not initially support
these new columns. Consumers should be prepared to handle providers that do
not support these columns.
5.1.12 New Column in PRIMARY_KEYS Rowset
OLE DB 2.0 added a new column, PK_NAME, to the PRIMARY_KEYS schema rowset.
Note that 1.x providers do not support this column, and some OLE DB 2.0
providers may not initially support this new column. Consumers should be
prepared to handle providers that do not support this column.
5.1.13 New DBCONVERT_ISFIXEDLENGTH, DBCONVERT_ISLONG, and
DBCONVERT_FROMVARIANT Flags in IConvertType::CanConvert
OLE DB 2.0 added three new convert flags, DBCONVERTFLAGS_ISFIXEDLENGTH,
DBCONVERTFLAGS_ISLONG, and DBCONVERTFLAGS_FROMVARIANT. These three flags
can coexist with either DBCONVERTFLAGS_COLUMN or DBCONVERTFLAGS_PARAMETER
to provide additional information for determining whether the DBTYPE
specified in dwFromType can be converted to the DBTYPE specified in
wToType. Note that these flags are not supported by OLE DB 1.x providers,
and may not be supported by OLE DB 2.0 providers. These flags are also not
supported by the OLE DB 2.0 Data Conversion Library because they primarily
affect how the provider deals with the data. Consumers that specify these
flags should be prepared for providers to return DB_E_BADCONVERTFLAG if
they don't support the flags.
5.1.14 Length of BLOB Columns
The OLE DB documentation states that consumers can obtain the total number
of bytes to be written to a stream by binding to the DBPART_LENGTH when
retrieving an ISequentialStream interface over the data. Note that binding
to the length of a BLOB bound as a storage object may be expensive for some
providers, and that 1.x and some 2.0 providers may return sizeof(IUnknown*)
for the length when binding to ISequentialStream instead of the actual
number of bytes to be passed in the stream. Consumers should bind to the
length when retrieving an ISequentialStream only if they need to know the
total length before reading the data, and should be prepared to handle
providers that fail to return this information.
5.1.15 Error in Description of DB_E_ERRORSOCCURRED
The description of DB_E_ERRORSOCCURRED in many cases ends with the sentence
"This method can fail to set properties for any of the reasons specified in
DB_S_ERRORSOCCURRED, except the reason that states that it was not possible
to set the property." Everything after the comma should be deleted in this
sentence. Any of the errors that cause DB_S_ERRORSOCCURRED for a property
set with a dwOptions value of DBPROPOPTIONS_OPTIONAL will cause
DB_E_ERRORSOCCURRED if set with a dwOptions value of
DBPROPOPTIONS_REQUIRED.
5.1.16 Case Sensitivity of TableID and ColumnID
OLE DB consumers should treat table and column names within DBIDs as case-
sensitive. This is not clear in the documentation.
5.1.17 hChapter Value for Nonchaptered Rowsets
When calling a method that takes hChapter as an argument, consumers should
set this value to NULL when working with a nonchaptered rowset. Although
nonchaptered providers should ignore this value, passing NULL improves
interoperability between chaptered and nonchaptered providers.
5.1.18 Wrong Definition of IRowsetFind in Appendix D
The function prototype for IRowsetFind in Appendix D of the OLE DB
Programmer's Reference shows the seventh argument as a Boolean argument
named fSkipStartRow. This argument should actually be a LONG named
lRowsOffset, as it appears elsewhere in the specification.
5.1.19 Requesting a View Object When Opening a Rowset
The OLE DB 1.5 specification added support for applying simple filters and
sorts through views. A View object may be returned in place of a rowset
when executing any rowset-returning method. To do this, the consumer
requests one of the following interfaces as the RIID argument specified in
the method that creates the rowset, or requests the associated property
from the View property group when opening the rowset:
IID_IViewChapter
IID_IViewFilter
IID_IViewRowset
IID_IViewSort
Requesting any other method when opening the rowset, and not explicitly
requesting an interface from the View property group, returns the Rowset
object, regardless of other properties specified on the rowset.
5.1.20 Error in Definition of View Properties
Each of the following properties is incorrectly listed as a property
belonging to the Rowset or DataSource property group. All four of these
properties should actually appear only as View properties:
DBPROP_FILTERCOMPAREOPS
DBPROP_MAXORSINFILTER
DBPROP_MAXSORTCOLUMNS
DBPROP_SORTONINDEX
5.1.21 Incorrect Column Order Listed for FOREIGN_KEYS Schema Rowset
The FOREIGN_KEYS rowset contains the following columns in this order:
PK_TABLE_CATALOG
PK_TABLE_SCHEMA
PK_TABLE_NAME
PK_COLUMN_NAME
PK_COLUMN_GUID
PK_COLUMN_PROPID
FK_TABLE_CATALOG
FK_TABLE_SCHEMA
FK_TABLE_NAME
FK_COLUMN_NAME
FK_COLUMN_GUID
FK_COLUMN_PROPID
ORDINAL
UPDATE_RULE
DELETE_RULE
FK_NAME
PK_NAME
DEFERRABILITY
The last three are out of order in the Programmer's Reference.
Default Sort Order: FK_TABLE_CATALOG, FK_TABLE_SCHEMA, FK_TABLE_NAME
5.1.22 Escaping Special Characters in OLE DB 2.0
OLE DB 1.x defined literal values that the provider can use to report
characters for escaping percent and underscore characters in a LIKE
predicate. OLE DB 2.0 added the ability for a provider to report suffix
characters for escaping percent and underscore in a LIKE predicate by
renaming DBLITERAL_ESCAPE_PERCENT