Microsoft OLE DB Overview

OLE DB is a set of interfaces that expose data from a variety of sources using the OLE Component Object Model (COM). OLE DB interfaces provide applications with uniform access to data stored in diverse information sources. These interfaces support the amount of DBMS functionality appropriate to the data source, enabling it to share its data.

For information on support for Microsoft data access components, see the Getting Help from Microsoft Technical Support page.

© 1998 Microsoft Corporation. All rights reserved.

The rest of this topic comprises a summary of useful items that accompany this release of OLE DB.

Core Components

Three redistributable components accompany the OLE DB SDK: the OLE DB Data Conversion Library, the OLE DB Row Position Object, and the OLE DB Root Enumerator Object.

Component | File | Interface(s)
Data Conversion Library | MSDADC.DLL | IDataConvert Supports a limited set of conversions.
Row Position Object | MSDADC.DLL | IRowPosition IRowPositionChange
Root Enumerator Object | MSDAENUM.DLL |

For more information about the components, see Core Components.

Data Link API

The Data Link API exposes a user interface that can be implemented in applications to create and manage connections to ADO and OLE DB data sources.

For more information about the Data Link API, see the Data Link API Overview.

OLE DB Programmer's Reference 2.0

The OLE DB Programmer's Reference is divided into three sections: conceptual matter, interface descriptions, and detailed information in the appendixes. The reference contains enhanced explanations and clarifications to the conceptual material. Links to related information have been added for easier navigation, as well as new interfaces, data types, schema rowsets, and properties.

Please see the Web page for OLE DB Release Notes at http://www.microsoft.com/data/oledb/.

OLE DB Providers

Four OLE DB providers are included with this version of OLE DB.

Provider | Description
OLE DB Provider for Oracle | Allows high performance and functional access to Oracle data for Visual Basic® applications.
OLE DB Provider for ODBC | Provides access to ODBC data sources by exposing OLE DB interfaces, methods and properties with certain limitations.
OLE DB Provider for Microsoft® Jet | Allows Microsoft Jet-specific implementation of standard OLE DB interfaces, methods, and properties.
OLE DB provider for Microsoft® SQL Server™ | Exposes interfaces to consumers wanting access to data on one or more SQL Servers.

OLE DB Simple Provider (OSP) Toolkit

The Microsoft OLE DB Simple Provider Toolkit is designed for quickly building OLE DB providers over simple data sources (for example, in-memory arrays) and exposing this data to any OLE DB consumer (such as ActiveX Data Objects, or any OLE DB-compliant application or control). This allows developers to expose OLE DB interfaces without spending the time needed to build an entire OLE DB provider from scratch. The toolkit is language neutral, supporting implementations in Microsoft® Visual Basic®, Microsoft® Visual C++®, or Microsoft® Visual J++™.

For more information about the OSP Toolkit, see the overview of the OLE DB Simple Provider Toolkit.

ActiveX Control Writer Toolkit

The ActiveX Control Writer Toolkit is an introduction for control writers to OLE DB control writing techniques. In addition, it assists in migrating obsolete ICursor interface controls to OLE DB. The toolkit contains the document, header files, libraries with source code, and samples.

OLE DB for Online Analytical Processing (OLAP)

OLE DB for OLAP is a set of COM (Component Object Model) objects and interfaces designed to facilitate communication between providers and consumers of multidimensional data. OLE DB for OLAP is an extension of OLE DB version 2.0.

Test Tools

Nondistributable testing components can be used to assist in the development of OLE DB consumers and providers. For more information about the tools, see OLE DB SDK Test Tools.

Test tool | Description
Interface Test Tool (ITest) | A COM interface testing environment to exercise and explore OLE DB providers and to record the results of these actions.
ITest Spy Tool | Enables the recording of calls executed by an OLE DB consumer.
Local Test Manager (LTM) | Provides a single point of control for automated testing of OLE DB providers.
OLE DB Test Editor | Enters the automated test framework code in a .cpp file for use with the Local Test Manager.
QuikTest Test Module | Produces a useful overview of provider functionality.
Unsupported Sample Test Modules | Sample test modules.

OLE DB Samples

Samples are provided as a quick way to construct and observe OLE DB interfaces. Included with OLE DB are the following:

Sample | Use
Sample Consumer | Demonstrates how to build a simple consumer.
Query Demo | Demonstrates how to build a consumer capable of issuing queries.
Rowset Viewer | Provides a simple way to view and manipulate rowsets.
Table Copy | Demonstrates a simple consumer that reads data from one OLE DB rowset and saves it as a new table.

For more information on samples, see the OLE DB Samples.


OLE DB Core Components

The Core Components are the essential elements of OLE DB, and therefore are required to be present in any redistribution of the product. This document describes each component, as well as additional interfaces that can be used in other APIs, such as the Data Link API. In addition, it refers to the Connection String Syntax, which sets the rules for constructing connection strings.

Providers require other files. For more information on those requirements, refer to the documentation on the specific provider located in the Data Access SDK.

Item | Interface | Location
Data Conversion Library | IDataConvert, including supported data type conversions | MSDADC.DLL
Row Position | IRowPosition IRowPositionChange | MSDADC.DLL
Root Enumerator |  | MSDAENUM.DLL
 | IDataInitialize | MSDASC.DLL
 | IDBPromptInitialize | MSDASC.DLL
Connection String Syntax |  | Data Link API documentation


Data Conversion Library

The OLE DB Data Conversion Library manages data type conversions for OLE DB consumers and providers. The OLE DB Data Conversion Library uses the IDataConvert interface to expose data conversion methods to consumers. Included in the documentation of the Data Conversion Library is a list of data type conversions Supported by MSDADC.DLL. For details about conversions, see "Data Type Conversions Supported by MSDADC.DLL."


IDataConvert

IDataConvert provides methods for data conversion using the OLE DB Data Conversion Library (MSDADC.DLL). Consumers requiring data-conversion services from the OLE DB Data Conversion Library should include the MSDADC.H header file in their project. IDataConvert includes the following methods.

Method | Description
CanConvert | Returns information about whether the conversion from the specified source type to the specified destination type is supported.
DataConvert | Converts the data from the source type to the destination type.
GetConversionSize | Calculates the size of the buffer needed by the destination data type to support a conversion.


IDataConvert::CanConvert

Returns information about whether the conversion from the specified source type to the specified destination type is supported.

HRESULT CanConvert (
   DBTYPE   wSrcType,
   DBTYPE   wDstType);

Parameters

wSrcType

[in]
The source type of the conversion to be verified.

wDstType

[in]
The destination type of the conversion to be verified.

Return Code

S_OK
The method succeeded and the conversion is supported.

S_FALSE
The requested conversion is not supported.

E_INVALIDARG
One or more of the DBTYPE values specified was invalid.


IDataConvert::DataConvert

Converts the data from the source type to the destination type.

HRESULT DataConvert (
   DBTYPE             wSrcType,
   DBTYPE             wDstType,
   ULONG              cbSrcLength,
   ULONG *            pcbDstLength,
   void *             pSrc,
   void *             pDst,
   ULONG              cbDstMaxLength,
   DBSTATUS           dbsSrcStatus,
   DBSTATUS *         pdbsStatus,
   BYTE               bPrecision,
   BYTE               bScale,
   DBDATACONVERT      dwFlags);

Parameters

wSrcType

[in]
The source type of the data to be converted.

wDstType

[in]
The destination type of the data to be converted.

cbSrcLength

[in]
The length of the source data in bytes.

pcbDstLength

[out]
A pointer to memory in which to return the length in bytes of the output data after conversion.

pSrc

[in]
A pointer to the source data. Note that this is a pointer to the underlying data type, based upon the value of wSrcType. For example, this would be the address of the BSTR pointer if wSrcType is DBTYPE_BSTR, not the BSTR pointer itself.

pDst

[out]
A pointer to memory in which to return the destination data. As in the case of pSrc, this is a pointer to the underlying data type, based upon the value of wDstType.

cbDstMaxLength

[in]
The maximum allowable length of the destination data in bytes. This is used only for variable length destination data types.

dbsSrcStatus

[in]
The status value for the source data.

pdbsStatus

[out]
A pointer to the status for the conversion.

bPrecision

[in]
The precision of the output data in bytes, if applicable. This argument is used when converting to DBTYPE_NUMERIC data only.

bScale

[in]
The scale of the output data in bytes, if applicable. This argument is used when converting to DBTYPE_NUMERIC data only.

dwFlags

[in]
The data-conversion flag indicating the requested conversion behavior. These flags can be combined.

The following flags are available:

typedef DWORD DBDATACONVERT;
enum DBDATACONVERTENUM{
 DBDATACONVERT_DEFAULT           = 0,
 DBDATACONVERT_SETDATABEHAVIOR   = 1,
 DBDATACONVERT_LENGTHFROMNTS     = 2
};

The DBDATACONVERT_SETDATABEHAVIOR flag indicates that data truncation is an error and that the memory pointed to by pDst will not be changed if any error occurs.

The DBDATACONVERT_LENGTHFROMNTS flag indicates that the data conversion library should calculate cbSrcLength for string (DBTYPE_STR, DBTYPE_WSTR) source data.

Return Code

S_OK
The conversion succeeded.

E_FAIL
pSrc was a null pointer.

E_OUTOFMEMORY
The data conversion object was unable to allocate memory.

DB_E_BADBINDINFO
The source or destination data type was invalid.

DB_E_DATAOVERFLOW
The requested conversion resulted in an overflow.

DB_E_ERRORSOCCURRED
Errors occurred while converting data. The requested conversion can fail for a number of reasons, including:

  • The source data was unavailable.

  • Data truncation occurred.

  • The precision and scale arguments did not make sense for the data type.

  • There was an invalid character or second decimal point in the source data.

DB_E_UNSUPPORTEDCONVERSION
The requested conversion was not supported.


IDataConvert::GetConversionSize

Calculates the size of the buffer needed by the destination data type to support a conversion.

HRESULT GetConversionSize (
   DBTYPE    wSrcType,
   DBTYPE    wDstType,
   ULONG *   pcbSrcLength,
   ULONG *   pcbDstLength,
   void *    pSrc);

Parameters

wSrcType

[in]
The source type of the data.

wDstType

[in]
The destination type of the data.

pcbSrcLength

[in]
A pointer to the length of the source data, in bytes. This value can be a null pointer.

pcbDstLength

[out]
A pointer to memory in which to return the size of the destination buffer required for the conversion, in bytes.

pSrc

[in]
A pointer to the source data.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
pcbDstLength was a null pointer or the information supplied by the consumer was insufficient to determine the appropriate buffer size.

pcbSrcLength was a null pointer and the conversion size was requested for a conversion from a variable-length data type to DBTYPE_STR, DBTYPE_WSTR, DBTYPE_BSTR, or DBTYPE_VARIANT.

DB_E_UNSUPPORTEDCONVERSION
The requested conversion was not supported.


Data Type Conversions Supported by MSDADC.DLL

The Data Conversion Library supports a limited set of conversions. The DBTYPE_BYREF modifier is only supported on the destination type for variable length data types (DBTYPE_BYTES, DBTYPE_STR, DBTYPE_VARNUMERIC, and DBTYPE_WSTR).

In the conversion tables, an X indicates that the conversion is supported by MSDADC.DLL. All data types can be converted to the same data type.

Note Supported data conversions can be determined through the IConvertType interface.

Conversions Supported from Data Type I1

Convert to type |  | Convert to type |
I2 | X | DBTIME |
I4 | X | DBTIMESTAMP |
I8 | X | BOOL | X
UI1 | X | BSTR | X
UI2 | X | BYTES |
UI4 | X | Empty | X
UI8 | X | ERROR |
R4 | X | GUID |
R8 | X | IDISPATCH |
CY | X | IUNKNOWN |
DECIMAL | X | NULL | X
NUMERIC | X | STR | X
DATE | X | VARIANT | X
DBDATE |  | WSTR | X
FILETIME |  | PROPVARIANT |
VARNUMERIC | X |  |

Conversions Supported from Data Type I2

Convert to type |  | Convert to type |
I1 | X | DBTIME |
I4 | X | DBTIMESTAMP |
I8 | X | BOOL | X
UI1 | X | BSTR | X
UI2 | X | BYTES |
UI4 | X | Empty | X
UI8 | X | ERROR |
R4 | X | GUID |
R8 | X | IDISPATCH |
CY | X | IUNKNOWN |
DECIMAL | X | NULL | X
NUMERIC | X | STR | X
DATE | X | VARIANT | X
DBDATE |  | WSTR | X
FILETIME |  | PROPVARIANT | X
VARNUMERIC | X |  |

Conversions Supported from Data Type I4

Convert to type |  | Convert to type |
I1 | X | DBTIME |
I2 | X | DBTIMESTAMP |
I8 | X | BOOL | X
UI1 | X | BSTR | X
UI2 | X | BYTES | X
UI4 | X | Empty | X
UI8 | X | ERROR |
R4 | X | GUID |
R8 | X | IDISPATCH |
CY | X | IUNKNOWN |
DECIMAL | X | NULL | X
NUMERIC | X | STR | X
DATE | X | VARIANT | X
DBDATE |  | WSTR | X
FILETIME |  | PROPVARIANT | X
VARNUMERIC | X |  |

Conversions Supported from Data Type I8

Convert to type |  | Convert to type |
I1 |  | DBTIME |
I2 |  | DBTIMESTAMP |
I4 |  | BOOL |
I8 | X | BSTR |
UI2 |  | BYTES |
UI4 |  | Empty | X
UI8 | X | ERROR |
R4 |  | GUID |
R8 |  | IDISPATCH |
CY |  | IUNKNOWN |
DECIMAL |  | NULL | X
NUMERIC |  | STR | X
DATE |  | VARIANT | X
DBDATE |  | WSTR | X
FILETIME | X | PROPVARIANT | X
VARNUMERIC |  |  |

Conversions Supported from Data Type UI1

Convert to type |  | Convert to type |
I1 | X | DBTIME |
I2 | X | DBTIMESTAMP |
I4 | X | BOOL | X
I8 | X | BSTR | X
UI2 | X | BYTES |
UI4 | X | Empty | X
UI8 | X | ERROR |
R4 | X | GUID |
R8 | X | IDISPATCH |
CY | X | IUNKNOWN |
DECIMAL | X | NULL | X
NUMERIC | X | STR | X
DATE | X | VARIANT | X
DBDATE |  | WSTR | X
FILETIME |  | PROPVARIANT | X
VARNUMERIC | X |  |

Conversions Supported from Data Type UI2

Convert to type |  | Convert to type |
I1 | X | DBTIME |
I2 | X | DBTIMESTAMP |
I4 | X | BOOL | X
I8 | X | BSTR | X
UI1 | X | BYTES |
UI4 | X | Empty | X
UI8 | X | ERROR |
R4 | X | GUID |
R8 | X | IDISPATCH |
CY | X | IUNKNOWN |
DECIMAL | X | NULL | X
NUMERIC | X | STR | X
DATE | X | VARIANT | X
DBDATE |  | WSTR | X
FILETIME |  | PROPVARIANT | X
VARNUMERIC | X |  |

Conversions Supported from Data Type UI4

Convert to type |  | Convert to type |
I1 | X | DBTIME |
I2 | X | DBTIMESTAMP |
I4 | X | BOOL | X
I8 | X | BSTR | X
UI1 | X | BYTES | X
UI2 | X | Empty | X
UI8 | X | ERROR |
R4 | X | GUID |
R8 | X | IDISPATCH |
CY | X | IUNKNOWN |
DECIMAL | X | NULL | X
NUMERIC | X | STR | X
DATE | X | VARIANT | X
DBDATE |  | WSTR | X
FILETIME |  | PROPVARIANT | X
VARNUMERIC | X |  |

Conversions Supported from Data Type UI8

Convert to type |  | Convert to type |
I1 |  | DBTIME |
I2 |  | DBTIMESTAMP |
I4 |  | BOOL |
I8 | X | BSTR |
UI2 |  | BYTES |
UI4 |  | Empty | X
UI8 | X | ERROR |
R4 |  | GUID |
R8 |  | IDISPATCH |
CY |  | IUNKNOWN |
DECIMAL |  | NULL | X
NUMERIC |  | STR | X
DATE |  | VARIANT | X
DBDATE |  | WSTR | X
FILETIME | X | PROPVARIANT | X
VARNUMERIC |  |  |

Conversions Supported from Data Type R4

Convert to type |  | Convert to type |
I1 | X | DBTIME |
I2 | X | DBTIMESTAMP |
I4 | X | BOOL | X
I8 | X | BSTR | X
UI1 | X | BYTES |
UI2 | X | Empty | X
UI4 | X | ERROR |
UI8 | X | GUID |
R8 | X | IDISPATCH |
CY | X | IUNKNOWN |
DECIMAL | X | NULL | X
NUMERIC | X | STR | X
DATE | X | VARIANT | X
DBDATE |  | WSTR | X
FILETIME |  | PROPVARIANT | X
VARNUMERIC | X |  |

Conversions Supported from Data Type R8

Convert to type |  | Convert to type |
I1 | X | DBTIME |
I2 | X | DBTIMESTAMP |
I4 | X | BOOL | X
I8 | X | BSTR | X
UI1 | X | BYTES |
UI2 | X | Empty | X
UI4 | X | ERROR |
UI8 | X | GUID |
R4 | X | IDISPATCH |
CY | X | IUNKNOWN |
DECIMAL | X | NULL | X
NUMERIC | X | STR | X
DATE | X | VARIANT | X
DBDATE |  | WSTR | X
FILETIME |  | PROPVARIANT | X
VARNUMERIC | X |  |

Conversions Supported from Data Type CY

Convert to type |  | Convert to type |
I1 | X | DBTIME |
I2 | X | DBTIMESTAMP |
I4 | X | BOOL | X
I8 | X | BSTR | X
UI1 | X | BYTES |
UI2 | X | Empty | X
UI4 | X | ERROR |
UI8 | X | GUID |
R4 | X | IDISPATCH |
R8 | X | IUNKNOWN |
DECIMAL | X | NULL | X
NUMERIC | X | STR | X
DATE | X | VARIANT | X
DBDATE |  | WSTR | X
FILETIME |  | PROPVARIANT | X
VARNUMERIC | X |  |

Conversions Supported from Data Type DECIMAL

Convert to type |  | Convert to type |
I1 | X | DBTIME |
I2 | X | DBTIMESTAMP |
I4 | X | BOOL | X
I8 | X | BSTR | X
UI1 | X | BYTES |
UI2 | X | Empty | X
UI4 | X | ERROR |
UI8 | X | GUID |
R4 | X | IDISPATCH |
R8 | X | IUNKNOWN |
CY | X | NULL | X
NUMERIC | X | STR | X
DATE | X | VARIANT | X
DBDATE |  | WSTR | X
FILETIME |  | PROPVARIANT |
VARNUMERIC | X |  |

Conversions Supported from Data Type NUMERIC

Convert to type |  | Convert to type |
I1 | X | DBTIME |
I2 | X | DBTIMESTAMP |
I4 | X | BOOL | X
I8 | X | BSTR | X
UI1 | X | BYTES |
UI2 | X | Empty | X
UI4 | X | ERROR |
UI8 | X | GUID |
R4 | X | IDISPATCH |
R8 | X | IUNKNOWN |
CY | X | NULL | X
DECIMAL | X | STR | X
DATE | X | VARIANT | X
DBDATE |  | WSTR | X

Conversions Supported from Data Type DATE

Convert to type |  | Convert to type |
I1 | X | DBTIME | X
I2 | X | DBTIMESTAMP | X
I4 | X | BOOL | X
I8 | X | BSTR | X
UI1 | X | BYTES |
UI2 | X | Empty | X
UI4 | X | ERROR |
UI8 | X | GUID |
R4 | X | IDISPATCH |
R8 | X | IUNKNOWN |
CY | X | NULL | X
DECIMAL | X | STR | X
NUMERIC | X | VARIANT | X
DBDATE | X | WSTR | X
FILETIME |  | PROPVARIANT | X
VARNUMERIC | X |  |

Conversions Supported from Data Type DBDATE

Convert to type |  | Convert to type |
I1 |  | DBTIME | X
I2 |  | DBTIMESTAMP | X
I4 |  | BOOL |
I8 |  | BSTR | X
UI1 |  | BYTES |
UI2 |  | Empty | X
UI4 |  | ERROR |
UI8 |  | GUID |
R4 |  | IDISPATCH |
R8 |  | IUNKNOWN |
CY |  | NULL | X
DECIMAL |  | STR | X
NUMERIC |  | VARIANT | X
DATE | X | WSTR | X
FILETIME |  | PROPVARIANT |
VARNUMERIC |  |  |

Conversions Supported from Data Type DBTIME

Convert to type |  | Convert to type |
I1 |  | DBDATE | X
I2 |  | DBTIMESTAMP | X
I4 |  | BOOL |
I8 |  | BSTR | X
UI1 |  | BYTES |
UI2 |  | Empty | X
UI4 |  | ERROR |
UI8 |  | GUID |
R4 |  | IDISPATCH |
R8 |  | IUNKNOWN |
CY |  | NULL | X
DECIMAL |  | STR | X
NUMERIC |  | VARIANT | X
DATE | X | WSTR | X
FILETIME |  | PROPVARIANT |
VARNUMERIC |  |  |

Conversions Supported from Data Type DBTIMESTAMP

Convert to type |  | Convert to type |
I1 |  | DBDATE | X
I2 |  | DBTIME | X
I4 |  | BOOL |
I8 |  | BSTR | X
UI1 |  | BYTES |
UI2 |  | Empty | X
UI4 |  | ERROR |
UI8 |  | GUID |
R4 |  | IDISPATCH |
R8 |  | IUNKNOWN |
CY |  | NULL | X
DECIMAL |  | STR | X
NUMERIC |  | VARIANT | X
DATE | X | WSTR | X
FILETIME |  | PROPVARIANT |
VARNUMERIC |  |  |

Conversions Supported from Data Type BOOL

Convert to type |  | Convert to type |
I1 | X | DBDATE |
I2 | X | DBTIME |
I4 | X | DBTIMESTAMP |
I8 | X | BSTR | X
UI1 | X | BYTES |
UI2 | X | Empty | X
UI4 | X | ERROR |
UI8 | X | GUID |
R4 | X | IDISPATCH |
R8 | X | IUNKNOWN |
CY | X | NULL | X
DECIMAL | X | STR | X
NUMERIC | X | VARIANT | X
DATE | X | WSTR | X
FILETIME |  | PROPVARIANT | X
VARNUMERIC | X |  |

Conversions Supported from Data Type BSTR

Convert to type |  | Convert to type |
I1 | X | DBDATE | X
I2 | X | DBTIME | X
I4 | X | DBTIMESTAMP | X
I8 | X | BOOL | X
UI1 | X | BYTES | X
UI2 | X | Empty | X
UI4 | X | ERROR |
UI8 | X | GUID | X
R4 | X | IDISPATCH |
R8 | X | IUNKNOWN |
CY | X | NULL | X
DECIMAL | X | STR | X
NUMERIC | X | VARIANT | X
DATE | X | WSTR | X
FILETIME | X | PROPVARIANT | X
VARNUMERIC | X |  |

Conversions Supported from Data Type BYTES

Convert to type |  | Convert to type |
I1 |  | DBDATE |
I2 |  | DBTIME |
I4 |  | DBTIMESTAMP |
I8 |  | BOOL |
UI1 |  | BSTR | X
UI2 |  | Empty | X
UI4 | X | ERROR |
UI8 |  | GUID |
R4 |  | IDISPATCH |
R8 |  | IUNKNOWN |
CY |  | NULL | X
DECIMAL |  | STR | X
NUMERIC |  | VARIANT | X
DATE |  | WSTR | X
FILETIME |  | PROPVARIANT | X
VARNUMERIC |  |  |

Conversions Supported from Data Type Empty

Convert to type |  | Convert to type |
I1 | X | DBDATE |
I2 | X | DBTIME |
I4 | X | DBTIMESTAMP |
I8 | X | BOOL | X
UI1 | X | BSTR | X
UI2 | X | BYTES |
UI4 | X | ERROR |
UI8 | X | GUID |
R4 | X | IDISPATCH |
R8 | X | IUNKNOWN |
CY | X | NULL | X
DECIMAL | X | STR | X
NUMERIC | X | VARIANT | X
DATE | X | WSTR | X
FILETIME | X | PROPVARIANT | X
VARNUMERIC | X |  |

Conversions Supported from Data Type ERROR

Convert to type | Convert to type |
I1 | DBDATE |
I2 | DBTIME |
I4 | DBTIMESTAMP |
I8 | BOOL |
UI1 | BSTR |
UI2 | BYTES |
UI4 | Empty |
UI8 | GUID |
R4 | IDISPATCH |
R8 | IUNKNOWN |
CY | NULL |
DECIMAL | STR |
NUMERIC | VARIANT | X
DATE | WSTR |
FILETIME | PROPVARIANT |
VARNUMERIC |  |

Conversions Supported from Data Type GUID

Convert to type | Convert to type |
I1 | DBDATE |
I2 | DBTIME |
I4 | DBTIMESTAMP |
I8 | BOOL |
UI1 | BSTR | X
UI2 | BYTES |
UI4 | Empty | X
UI8 | ERROR |
R4 | IDISPATCH |
R8 | IUNKNOWN |
CY | NULL | X
DECIMAL | STR | X
NUMERIC | VARIANT | X
DATE | WSTR | X
FILETIME | PROPVARIANT | X
VARNUMERIC |  |

Conversions Supported from Data Type IDISPATCH

Convert to type |  | Convert to type |
I1 | X | DBDATE |
I2 | X | DBTIME |
I4 | X | DBTIMESTAMP |
I8 |  | BOOL | X
UI1 | X | BSTR | X
UI2 | X | BYTES |
UI4 | X | Empty | X
UI8 |  | ERROR |
R4 | X | GUID |
R8 | X | IUNKNOWN | X
CY | X | NULL | X
DECIMAL | X | STR |
NUMERIC |  | VARIANT | X
DATE | X | WSTR |
FILETIME |  | PROPVARIANT | X
VARNUMERIC |  |  |

Conversions Supported from Data Type IUNKNOWN

Convert to type | Convert to type |
I1 | DBDATE |
I2 | DBTIME |
I4 | DBTIMESTAMP |
I8 | BOOL |
UI1 | BSTR |
UI2 | BYTES |
UI4 | Empty | X
UI8 | ERROR |
R4 | GUID |
R8 | IDISPATCH | X
CY | NULL | X
DECIMAL | STR |
NUMERIC | VARIANT | X
DATE | WSTR |
FILETIME | PROPVARIANT |
VARNUMERIC |  |

Conversions Supported from Data Type NULL

Convert to type | Convert to type |
I1 | DBDATE |
I2 | DBTIME |
I4 | DBTIMESTAMP |
I8 | BOOL |
UI1 | BSTR |
UI2 | BYTES |
UI4 | Empty |
UI8 | ERROR |
R4 | GUID |
R8 | IDISPATCH |
CY | IUNKNOWN |
DECIMAL | STR |
NUMERIC | VARIANT | X
DATE | WSTR |
FILETIME | PROPVARIANT | X
VARNUMERIC |  |

Conversions Supported from Data Type STR

Convert to type |  | Convert to type |
I1 | X | DBDATE | X
I2 | X | DBTIME | X
I4 | X | DBTIMESTAMP | X
I8 | X | BOOL | X
UI1 | X | BSTR | X
UI2 | X | BYTES | X
UI4 | X | Empty | X
UI8 | X | ERROR |
R4 | X | GUID | X
R8 | X | IDISPATCH |
CY | X | IUNKNOWN |
DECIMAL | X | STR | X
NUMERIC | X | VARIANT | X
DATE | X | WSTR | X
FILETIME | X | PROPVARIANT | X
VARNUMERIC | X |  |

Conversions Supported from Data Type VARIANT

Convert to type |  | Convert to type |
I1 | X | DBDATE | X
I2 | X | DBTIME | X
I4 | X | DBTIMESTAMP | X
I8 | X | BOOL | X
UI1 | X | BSTR | X
UI2 | X | BYTES | X
UI4 | X | Empty | X
UI8 | X | ERROR | X
R4 | X | GUID | X
R8 | X | IDISPATCH | X
CY | X | IUNKNOWN | X
DECIMAL | X | STR | X
NUMERIC | X | VARIANT | X
DATE | X | WSTR | X
FILETIME | X | PROPVARIANT | X
VARNUMERIC | X |  |

Conversions Supported from Data Type WSTR

Convert to type |  | Convert to type |
I1 | X | DBDATE | X
I2 | X | DBTIME | X
I4 | X | DBTIMESTAMP | X
I8 | X | BOOL | X
UI1 | X | BSTR | X
UI2 | X | BYTES | X
UI4 | X | Empty | X
UI8 | X | ERROR |
R4 | X | GUID | X
R8 | X | IDISPATCH |
CY | X | IUNKNOWN |
DECIMAL | X | STR | X
NUMERIC | X | VARIANT | X
DATE | X | WSTR | X
FILETIME | X | PROPVARIANT | X
VARNUMERIC | X |  |

Conversions Supported from Data Type FILETIME

Convert to type |  | Convert to type |
I1 |  | DBDATE |
I2 |  | DBTIME |
I4 |  | DBTIMESTAMP |
I8 | X | BOOL |
UI1 |  | BSTR | X
UI2 |  | BYTES |
UI4 |  | Empty | X
UI8 | X | ERROR |
R4 |  | GUID |
R8 |  | IDISPATCH |
CY |  | IUNKNOWN |
DECIMAL |  | STR | X
NUMERIC |  | VARIANT | X
DATE | X | WSTR | X
FILETIME |  | PROPVARIANT | X
VARNUMERIC |  | WSTR | X

Conversions Supported from Data Type PROPVARIANT

Convert to type |  | Convert to type |
I1 | X | DBDATE | X
I2 | X | DBTIME | X
I4 | X | DBTIMESTAMP | X
I8 | X | BOOL | X
UI1 | X | BSTR | X
UI2 | X | BYTES | X
UI4 | X | Empty | X
UI8 | X | ERROR | X
R4 | X | GUID | X
R8 | X | IDISPATCH | X
CY | X | IUNKNOWN | X
DECIMAL | X | STR | X
NUMERIC | X | VARIANT | X
DATE | X | WSTR | X
FILETIME | X | PROPVARIANT | X
VARNUMERIC | X |  |

Conversions Supported from Data Type VARNUMERIC

Convert to type |  | Convert to type |
I1 | X | DBDATE | X
I2 | X | DBTIME | X
I4 | X | DBTIMESTAMP | X
I8 | X | BOOL | X
UI1 | X | BSTR | X
UI2 | X | BYTES | X
UI4 | X | Empty | X
UI8 | X | ERROR |
R4 | X | GUID | X
R8 | X | IDISPATCH |
CY | X | IUNKNOWN |
DECIMAL | X | STR | X
NUMERIC | X | VARIANT | X
DATE | X | WSTR | X
FILETIME | X | PROPVARIANT | X
VARNUMERIC | X |  |


Row Position Object

The OLE DB Row Position Object keeps track of a current hRow so that multiple components can coordinate on the concept of a current row. Consumers create a Row Position Object and set or retrieve the current row position using the IRowPosition interface.


IRowPosition::ClearRowPosition

Clears the value of the current hRow.

HRESULT ClearRowPosition ();

Parameters

None.

Return Code

S_OK
The method succeeded. Calling GetRowPosition returns a phRow value of DB_NULL_ROW and a pdwPositionFlag value of DBPOSITION_NOROW.

E_FAIL
A provider-specific error occurred. The row position is unchanged.

DB_E_CANCELED
The operation was canceled by a client during notification. The row position is unchanged.

E_UNEXPECTED
ClearRowPosition was called twice in a row.

There is no current rowset.

Comments

ClearRowPosition fires the following notification phases with the reason DBREASON_ROWPOSITION_CLEARED:

  • DBEVENTPHASE_OKTODO

  • DBEVENTPHASE_ABOUTTODO

  • DBEVENTPHASE_FAILEDTODO (if either of the above events is canceled)

Calling SetRowPosition fires the remaining phases.

ClearRowPosition must be called prior to the first and any subsequent SetRowPosition calls.


IRowPosition::GetRowPosition

Returns the hRow of the current row position.

HRESULT GetRowPosition (
HCHAPTER *              phChapter,
HROW *                  phRow
DBPOSITIONFLAGS *       pdwPositionFlags);

Parameters

phChapter

[out]
A pointer to memory in which to return the chapter associated with the current row. If the rowset is not chaptered, phChapter is set to DB_NULL_HCHAPTER. If phChapter is a null pointer on input, no chapter value is returned.

phRow

[out]
A pointer to memory in which to return the hRow of the current row position, or DB_NULL_HROW if there is no current row position.

pdwPositionFlags

[out]
A pointer to memory in which to return additional information about the row position. If pdwPositionFlags is a null pointer on input, no additional information is returned for the row position. pdwPositionFlags may indicate one of the following conditions:

Value | Meaning
DBPOSITION_OK | hRow specifies the current row position.
DBPOSITION_NOROW | hRow is DB_NULL_HROW, and the current row position is undefined.
DBPOSITION_BOF | hRow is DB_NULL_HROW, and the row position is before the beginning of the rowset.
DBPOSITION_EOF | hRow is DB_NULL_HROW, and the row position is off the end of the rowset.

Additional values for this flag may be defined in the future. If an unknown value is returned in pdwPosition Flags, the consumer should assume that there is no row if phRow is set to DB_NULL_HROW, and that phRow points to a valid row if it is any other value.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
phRow was a null pointer.

E_UNEXPECTED
IRowPosition::Initialize has not been called; the row position object is in an uninitialized state.

ITransaction::Commit or ITransaction::Abort was called and the object is in a zombie state.

There is no current rowset.

Comments

Before returning, the row position object calls AddRefRows to increment the hRow’s reference count and, if the chapter is non-null, AddRefChapter to increment the phChapter’s reference count.

Calling GetRowPosition prior to the first SetRowPosition returns a value of DB_NULL_HROW for phRow, and a pdwPositionFlags value of DBPOSITION_NOROW.


IRowPosition Code Example

The following code fragment shows how a consumer might use IRowPosition when traversing a rowset.

// Code to generate a pRowset
…

IRowPosition * pRowPosition = NULL;
HRESULT hr;

hr = CoCreateInstance(CLSID_OLEDB_ROWPOSITIONOBJECT, NULL, CLSCTX_INPROC_SERVER,  IID_IRowPosition,
(LPVOID *)&pRowPosition)

if (SUCCEEDED(hr))
   {
   hr = pRowPosition->Initialize(pRowset);   // init with pRowset

   if (FAILED(hr))
       pRowPosition->Release();
   }

return hr;

// Read the rows; 100 rows at a time into the rowset.
While(SUCCEEDED(hr = pRowset->GetNextRows(NULL, 0, 100, &cRowsObtained, &rghRows)) &&
  cRowsObtained) {
 for(irow = 0; irow < cRowsObtained; irow++) {
  // GetData copies the rows into the local buffers, performing the type
  // conversions specified in the binding structures associated with the accessor.
  pRowset->GetData(rghRows[irow], hAccessor, (void*) rgData);

   // Set currently read row as “current row”
   // Note: other clients will choose different notions of current row.
   If (pIRowPosition->ClearRowPosition () == S_OK)
         pIRowPosition->SetRowPosition (NULL, rghRows[irow], DBPOSITION_OK);

   // Convenience function to print the data.
   PrintData(rgData);
 }

 // Release the rows just printed from the rowset.
 PRowset->ReleaseRows(cRowsObtained, rghRows, NULL, NULL, NULL);

}


IRowPosition::GetRowset

Returns a pointer to the underlying rowset.

HRESULT GetRowset (
REFIID           riid,
IUnknown **      ppRowset);

Parameters

riid

[in]
The requested IID for the rowset returned in *ppRowset.

ppRowset

[out]
A pointer to memory in which to return the interface pointer of the underlying rowset.

Return Code

S_OK
The method succeeded.

E_INVALIDARG
ppRowset was a null pointer.

E_NOINTERFACE
riid referenced IID_NULL.

ppRowset did not support the interface specified in riid.

E_UNEXPECTED
There is no current rowset.

ITransaction::Commit or ITransaction::Abort was called and the object is in a zombie state.

Comments

Before ppRowset is returned, it is AddReferenced by the row position object.


IRowPosition::Initialize

Initializes the row position object by setting the source rowset.

HRESULT Initialize (
IUnknown *   pRowset);

Parameters

pRowset

[in]
Pointer to an interface on the source Rowset.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
pRowset was a null pointer.

E_NOINTERFACE
pRowset does not support the IRowset interface.

DB_E_ALREADYINITIALIZED
The row position object has already been initialized.

E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called and the object is in a zombie state.


IRowPosition::SetRowPosition

Sets the current row position to a new hRow.

HRESULT SetRowPosition (
HCHAPTER               hChapter,
HROW                   hRow,
DBPOSITIONFLAGS        dwPositionFlags);

Parameters

hChapter

[in]
The chapter associated with the current row, or DB_NULL_HCHAPTER if the rowset is not chaptered.

hRow

[in]
The new current row.

dwPositionFlags

[in]
A flag indicating additional information about the new row position. One of the following:

Value | Meaning
DBPOSITION_OK | hRow specifies the current row position.
DBPOSITION_NOROW | hRow is DB_NULL_ROW, and the current row position is undefined.
DBPOSITION_BOF | hRow is DB_NULL_ROW, and the row position is before the beginning of the rowset.
DBPOSITION_EOF | hRow is DB_NULL_ROW, and the row position is off the end of the rowset.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

DB_E_BADCHAPTER
hChapter was invalid.

DB_E_BADROWHANDLE
hRow was invalid.

DB_E_CANCELED
The operation was canceled by a client during notification. The current row is set to DB_NULL_ROW with a position flag indicating DBPOSITION_NOROW.

E_INVALIDARG
hRow was set to DB_NULL_ROW and dwPositionFlags indicated DBPOSITION_OK.

dwPositionFlags indicated DBPOSITION_OK, but hRow was an invalid value.

dwPositionFlags indicated DBPOSITION_NOROW, DBPOSITION_BOF, or DBPOSITION_EOF, and hRow was not set to DB_NULL_ROW.

E_UNEXPECTED
SetRowPosition was called without first calling ClearRowPosition.

There is no current rowset.

ITransaction::Commit or ITransaction::Abort was called and the object is in a zombie state.

Comments

ClearRowPosition must be called prior to the first and any subsequent SetRowPosition calls.

SetRowPosition fires the following notification phases with either DBREASON_ROWPOSITION_CHANGED or DBREASON_ROWPOSITION_CHAPTERCHANGED:

  • DBEVENTPHASE_SYNCHAFTER

  • DBEVENTPHASE_DIDEVENT or DBEVENTPHASE_FAILEDTODO

The RowPostion object maintains a reference count on the both the hRow and the hChapter by respectively calling AddRefRows for the hRow and AddRefChapter for the hChapter when SetRowPosition is called.


IRowPositionChange

IRowPositionChange is the callback interface that a consumer must support in order to be notified when the current row position is changed.

The RowPostion object supports IConnectionPointContainer and the consumer calls FindConnectionPoint for IID_IRowPostionChange to obtain the correct IConnectionPoint interface. The consumer then advises that connection point to connect and supplies a pointer to the consumer’s IRowPositionChange interface.

For more information about notifications, see Chapter 9 of the OLE DB Programmer's Reference, “Notifications.”

When to Implement

IRowPositionChange is implemented by consumers that require notification about changes to the current row position.

When to Call

IRowPositionChange is called by the row position object when a consumer attempts to change the current row position.

Method | Description
OnRowPositionChange | Notifies the consumer of a row position object of a change to the current row position.


IRowPositionChange:: OnRowPositionChange

Notifies the consumer of a row position object of a change to the current row position.

HRESULT OnRowPositionChange (
DBREASON               eReason,
DBEVENTPHASE           ePhase,
BOOL                   fCantDeny);

Parameters

eReason

[in]
The reason of the event that caused this change. If this value is not recognized by the method, the method returns S_OK or DB_S_UNWANTEDREASON.

ePhase

[in]
The phase of this notification.

fCantDeny

[in]
When this flag is set to TRUE, the consumer cannot veto the event by returning S_FALSE because the provider cannot undo the event.

Return Code

S_OK
The method succeeded.

S_FALSE
The event or phase is vetoed by reason of logical objection or a failure to be able to implement.

DB_S_UNWANTEDPHASE
The consumer is not interested in receiving this phase for this reason. The provider can optimize by making no further calls with this reason and phase. The phases for other reasons are unaffected.

DB_S_UNWANTEDREASON
The consumer is not interested in receiving any phases for this reason. The provider can optimize by making no further calls with this reason.

E_FAIL
A consumer-specific error occurred. The row position object continues the operation.

Comments

Possible reasons are DBREASON_ROWPOSITION_CLEARED, DBREASON_ROWPOSITION_CHANGED, and DBREASON_ROWPOSITION_CHAPTERCHANGED.

DBREASON_ROWPOSITION_CLEARED generates DBEVENTPHASE_OKTODO and DBEVENTPHASE_ABOUTTODO, and DBEVENTPHASE_FAILEDTODO if the notification is canceled.

DBREASON_ROWPOSITION_CHANGED and DBREASON_ROWPOSITIONCHAPTERCHANGED generate DBEVENTPHASE_SYNCHAFTER and DBEVENTPHASE_DIDEVENT, or DBEVENTPHASE_FAILEDTODO if the notification is canceled.

Consumers are required to handle reasons that not defined in this interface by returning S_OK or DB_S_UNWANTEDREASON.


Root Enumerator Object

The OLE DB Root Enumerator Object enumerates OLE DB providers and enumerators on your system. The root enumerator traverses the registry searching for providers and enumerators listed in the registry. It has a class ID of CLSID_OLEDB_ENUMERATOR. In the rowset returned by ISourcesRowset::GetSourcesRowset, the root enumerator returns values from the registry as follows:

Column | Description
SOURCES_NAME | For enumerators, the value of HKEY_CLASSES_ROOT\CLSID\ EnumeratorCLSID subkey. For data sources, the value of HKEY_CLASSES_ROOT\CLSID\ ProviderCLSID subkey.
SOURCES_DESCRIPTION | For enumerators, the value of the HKEY_CLASSES_ROOT\CLSID\ EnumeratorCLSID\OLE DB Enumerator subkey. For data sources, the value of the HKEY_CLASSES_ROOT\CLSID\ ProviderCLSID\OLE DB Provider subkey.

For more information, see “Enumerating Data Sources and Enumerators” in Chapter 2 and “Registry Entries” in Chapter 14 of the OLE DB Programmer’s Reference.


IDataInitialize

IDataInitialize is called to provide a common entry point for consumers to obtain a data source object (DSO) or get persistent data source information.

Method | Description
CreateDBInstance | Creates a data source object. Analogous to CoCreateInstance.
CreateDBInstanceEx | Creates a data source object. Analogous to CoCreateInstanceEx.
GetDataSource | Given initialization string, instantiates and returns a data source object.
GetInitializationString | Given a data source object, returns stringed connection information.
LoadStringFromStorage | Loads an initialization string from storage.
WriteStringToStorage | Writes an initialization string to storage.

For more information about IDataInitialize, see “IDataInitialize” in the Data Link API Reference.


IDBPromptInitialize

The IDBPromptInitialize interface allows you to access the data link API programmatically from Visual C++®. When accessing OLE DB directly, using the OLE DB IDBPromptInitialize interface, the connection string is passed as a parameter to create an OLE DB data source object.

Method | Description
PromptDataSource | Opens the Data Link Properties dialog box. Returns a data link with the properties set.
PromptFileName | Opens the Select Data Link dialog box. Allows the user to browse and organize data link files. Returns a fully qualified path to the user-selected data link file.

For more information about IDBPromptInitialize, see “IDBPromptInitialize” in the Data Link API Reference.


Connection String Syntax

A data link enables you to easily store connection information within your application or pass it between applications. The format of a data link is a connection string, which is a string version of the initialization properties needed to connect to a data source. Without a connection string, you would be required to store or pass a complex array of structures to access data.

In some cases, it might be desirable to manually build a connection string within an application. Therefore, it is important that the Connection String Syntax adhere to the formatting rules.

For more information about the syntax, see the "Data Link API Overview."


Data Link API Overview

The data link API exposes a user interface that you can implement in your applications to create and manage connections to OLE DB data sources.

Because the data link API is included with OLE DB as a core component, you can always assume that the user interface is available to your application users if ADO or OLE DB is properly installed on their system.

This section includes the following topics:

  • Using the Data Link API

  • Building and Saving Connection Strings

  • Data Link API User Interface Components

Using the Data Link API

Before you can access data from OLE DB, you must provide specific connection information such as:

  • The type of data that you want to access.

  • The server on which the data resides.

  • The database in which the data is stored.

For example, to connect to a Microsoft® SQL Server™ database you need to specify the OLE DB provider for SQL Server, a server name, and a database name.

You can call the data link API to build a string version of this connection information that you can save and reuse in your applications. This string version is referred to as a connection string. The user interface that prompts users to build the connection string is referred to as the data link dialog boxes. The information specified in a connection string may vary depending on the OLE DB provider that is specified.

Building and Saving Connection Strings Using the Data Link Properties Dialog Box

Using the data link dialog boxes, your end users can build connection strings directly within your application. Once created, a connection string can be saved with the application itself or as a separate file with the .udl extension. This file is referred to as a Microsoft Data Link file.

Each method of saving a connection string is compared below.

Saving a connection string with the application data | Saving a connection string as a data link (.udl) file
Connection string is saved and stored with the application. | Connection string is saved as an .udl file, enabling users to save and modify it from their system.
Connection string is part of the application and is thus automatically redistributed with the application. | The .udl file must be included as a separate file when you redistribute your application.
If the connection information changes, the connection string must be modified from within your application. | Users can modify the .udl file at anytime if the connection information changes.

As a programmer, you can use either method to prompt users to build connection strings while using your application. You call the same data link dialog boxes regardless of the method used.

Users can also build connection strings from Control Panel by clicking the Data Links icon. The Data Links icon opens the Organize Data Link Files dialog box, which allows users to create new .udl files or modify the connection string in existing .udl files. Users can store .udl files anywhere on their system or network.

Note You can manually build a connection string as long as it conforms to the syntax rules. For more information about the format of a connection string, see Connection String Syntax.

Data Link API User Interface Components

The data link API includes the following dialog boxes:

  • Data Link Properties

  • Organize Data Link Files

  • Select Data Link

Note To get help about the data link interface, press F1 while viewing an active dialog box.

Data Link Properties Dialog Box

Users typically use the Data Link Properties dialog box either to edit an existing connection string, or to build a new connection string. This is a tabbed dialog box that exposes all of the properties that the selected OLE DB provider supports. For example, the Advanced tab includes default settings that the user can modify as necessary. The Connection tab is provider-specific.

Depending on which OLE DB provider the user selects, the tabs and the options in this dialog box may vary.

Organize Data Link Files Dialog Box

To create or edit data link (.udl) files, which contain connection strings that have been saved, users use the Organize Data Link Files dialog box. Users can also access this dialog box from Control Panel by clicking the Data Links icon.

Select Data Link Dialog Box

If you want your application to reference a .udl file instead of storing a connection string directly in your application, you can prompt users with the Select Data Link File dialog box. This allows the user to select from existing .udl files, as well as to create new ones.

For example, if your application supports .udl files, a user could create an .udl file that points to the user's Customer database. Using the Select Data Link dialog box, the user could then specify this .udl file to access the Customer database.

Referencing an .udl file is beneficial to users if a database is upgraded to a different server or is modified. In this case, the user simply edits the connection string within the .udl file with the updated information and the application that uses this file will instantly reflect the changes.

The Select Data Link File dialog box is the same as the Organize Data Links dialog box, but it returns the selected .udl file to the application.

See Also

Referencing a Connection String in Your Application

Connection String Syntax


Referencing a Connection String in Your Application

As a programmer, you can enable your end users to build, save, and access connection strings and .udl files within your own applications. You can invoke the data link user interface programmatically whenever you need the user to specify what data to access.

The information that is returned from the data link user interface can be used to create OLE DB data source objects (DSOs). For more information about creating data source objects, see the IDataInitialize interface. This interface is part of core OLE DB.

Programmatically Accessing the Data Link User Interface

You can call the data link API from any application development tool that supports the access of COM objects such as Microsoft® Visual C++®.

To access the data link API from applications that can directly access a COM interface, such as C++, use the IDBPromptInitialize interface. This interface is part of core OLE DB.

See Also

Data Link API Overview

Connection String Syntax


Connection String Syntax

A connection string is a string version of the initialization properties needed to connect to a data source and enables you to easily store connection information within your application or pass it between applications. Without a connection string, you would be required to store or pass a complex array of structures to access data.

In ADO, connection strings are retrieved and set using the ADO ConnectionString property to create a Connection object. When accessing OLE DB directly, using the IDataInitialize interface, the connection string is passed as a parameter to create an OLE DB data source object.

In some cases, it might be desirable to manually build a connection string within an application. For example, if you want to provide a custom user interface for your users, rather than using the standard data link user interface, then you would manually build the connection string. In these cases, it is important that the connection string syntax adhere to the formatting rules.

This section includes the following topics:

  • Basic Connection String Format

  • The Provider Keyword

  • Specifying a Keyword

  • Setting Keyword Values

  • Listing Keywords Multiple Times

  • Setting the Window Handle Property

  • Format Limitations

  • A Note to Provider Writers

Basic Connection String Format

The basic format of a connection string is based on the ODBC connection string. The string contains a series of keyword/value pairs separated by semicolons. The equals sign (=) separates each keyword and its value.

Example keyword1=value; keyword2=value

The Provider Keyword

The Provider keyword identifies the OLE DB provider to be used. To specify your OLE DB provider, set the Provider keyword to the VersionIndependentProgID of the provider:

Example Provider=MSDASQL

The value can also be set to the ProgID of the provider, which may have a version attached to it:

Example Provider=MSDASQL.1

If two versions of a provider are installed on the same system, use the ProgID to specify exactly which version to use. If two versions are installed on a system and the VersionIndependentProgID is specified, the most recent version of the provider is used.

If no Provider keyword is in the string, the OLE DB Provider for ODBC (MSDASQL) is the default value. This provides backward compatibility with ODBC connection strings. The following ODBC connection string can be passed in and it will successfully connect:

Example Driver={SQL Server};Server=MyServer;db=pubs;uid=sa;pwd=MyPassword

If the Provider keyword is specified, but specifies a provider that does not exist or is invalid, the connection string returns an error:

Example Provider=;Database=MyDatabase

Specifying a Keyword

To identify a keyword used after the Provider keyword, use the Description property of the OLE DB initialization property that you want to set. Standard OLE DB properties are documented in the OLE DB Programmer's Reference under the OLE DB Initialization property group (DBPROPSET_DBINIT).

For example, the Description property of the standard OLE DB initialization property DBPROP_INIT_LOCATION is Location. Thus, to include this property in a connection string, use the keyword Location:

Example Provider=MSDASQL;Location=Pubs

For more information about OLE DB property groups, see Appendix C of the OLE DB Programmer's Reference.

For information about provider-specific initialization properties, see the documentation that came with your provider.

Keywords can contain any printable character except for the equals sign (=). All of the following examples are correct:

Example Provider=Microsoft.Jet.OLEDB.3.5.1;Jet OLE DB:System Database=c:\system.mda

The keyword is "Jet OLE DB:System Database".

Example Provider=Microsoft.Jet.OLEDB.3.5.1;Authentication;Info=Column 5

The keyword is "Authentication;Info".

If a keyword contains an equals sign (=), it must be preceded by an additional = sign to indicate that it is part of the keyword:

Example Provider=Microsoft.Jet.OLEDB.3.5.1;Verification==Security=True

The keyword is "Verification=Security".

If multiple = signs appear, each one must be preceded by an additional = sign:

Example Provider=MSDASQL;Many====One=Valid

The keyword is "Many==One".

Example Provider=MSDASQL;TooMany===VARIANT_FALSE

The keyword is "TooMany=".

Setting Keyword Values

The value of a keyword must be specified using a string format. The value depends on the property being set and the value that the OLE DB provider expects.

Short Property Values

The data link API uses a shortened, "friendlier" version of the property values defined in the OLE DB specification. The short value of a property is derived by removing the prefix from the documented property value and replacing any underscores with spaces. For example, the OLE DB specification defines the following values for the Cache Authentication (DBPROP_AUTH_CACHE_AUTHINFO) property:

  • VARIANT_TRUE

  • VARIANT_FALSE

The prefix "VARIANT_" is removed and the short values are "TRUE" and "FALSE".

Tip The short property values are case insensitive; thus the values can be made more readable by using "True" and "False".

Example Provider=MSDASQL;Location=Pubs;Cache Authentication=True

Another example is the Prompt (DBPROP_INIT_PROMPT) property for which the OLE DB specification defines the following values:

  • DBPROMPT_PROMPT

  • DBPROMPT_COMPLETE

  • DBPROMPT_COMPLETEREQUIRED

  • DBPROMPT_NOPROMPT

Again, the prefix "DBPROMPT_" is removed and mixed case is used for the short values:

  • Prompt

  • Complete

  • CompleteRequired

  • NoPrompt

    Example Provider=MSDASQL;Location=Pubs;Prompt=Complete

Some of the values defined for the Mode (DBPROP_INIT_MODE) property include:

  • DB_MODE_SHARE_DENY_READ

  • DB_MODE_SHARE_DENY_WRITE

  • DB_MODE_SHARE_EXCLUSIVE

Note that the underscores after the prefix "DB_MODE_SHARE_" are replaced with spaces in the short values:

  • Deny Read

  • Deny Write

  • Share Exclusive

These shortened, "friendlier" property values apply only to those properties that are documented in the OLE DB specification. Note that different OLE DB data providers may use custom properties and/or property values. The OLE DB Initialization Properties: Quick Reference lists the property description and the short property value (where applicable) for each OLE DB initialization property.

Setting Numeric Values

To set a numeric property value, use either a decimal, hex, or octal value.

Examples:

Provider= Microsoft.Jet.OLEDB.3.5.1; Window Handle=123

Provider= Microsoft.Jet.OLEDB.3.5.1; Window Handle=0x123

Provider= Microsoft.Jet.OLEDB.3.5.1; Window Handle=0123

Setting Values that Use Reserved Characters

To include values that contain a semicolon, single-quote character, or double-quote character, the value must be quoted. Typically, the value will be quoted using the double-quote character:

Example Provider=MSDASQL;ExtendedProperties="UID=sa;pwd=sa;Databse=MyDB"

If the value contains both a semicolon and a double-quote character, the value can be quoted using the single-quote character:

Example Provider=MSDASQL;ExtendedProperties='UID=sa;pwd=sa;Databse="My DB"'

The value for the UID keyword is UID=sa;pwd=sa;Databse="My DB".

The single-quote is also useful if the value begins with a double-quote character:

Example Provider=SQLOLEDB;DataSchema='"MyCustTable"'

The value for the DataSchema keyword is "MyCustTable".

Conversely, the double-quote can be used if the value begins with a single-quote:

Example Provider=SQLOLEDB;DataSchema="'MyOtherCustTable'"

The value is 'MyOtherCustTable'.

If the value contains both single- and double-quote characters, the quote character used to surround the value must be doubled each time it occurs within the value:

Example Provider=SQLOLEDB;NewRecordsCaption='"Company''s "new" customer"'

The value is "Company's "new" customer".

Example Provider=SQLOLEDB;NewRecordsCaption="""Company''s ""new"" customer"""

The value is "Company's "new" customer".

Quotes can also be used if the value begins or ends with spaces:

Example Provider=SQLOLEDB;Database Name=" Badly Named Database "

Setting Values that Use Spaces

Any leading or trailing spaces around a keyword or value are ignored. However, spaces within a keyword or value are allowed and recognized.

Example Provider=MSDASQL;MyKeyword=My Value

The value for the MyKeyword keyword is My[space]Value.

Example Provider=MSDASQL;MyKeyword= My Value ;MyNextValue=Value

The value for the MyKeyword keyword is My[space]Value.

To include preceding or trailing spaces in the value, the value must be quoted using either the single or double quote mark.

Example Provider=MSDASQL;MyKeyword= ' My Value  '

The value for the MyKeyword keyword is [space]My[space]Value[space][space].

Example Provider=MSDASQL;MyKeyword= "  My Value "

The value for the MyKeyword keyword is [space][space]My[space]Value[space].

There is one exception to this behavior. If the keyword does not correspond to a standard OLE DB Initialization property—in which case, the keyword value is placed in the Extended Properties (DBPROP_INIT_PROVIDERSTRING) property—the spaces around the value will be included in the value. This is done to support backward compatibility for ODBC connection strings.

Example Driver=SQL Server;uid= MyName;pwd= MyPassword

The actual connection string generated would be:

Provider=MSDASQL;Extended Properties="Driver=SQL Server;uid= MyName;pwd= MyPassword"

The value for the uid keyword is [space]MyName.

The value for the pwd keyword is [space]MyPassword.

In this case, the underlying provider can deal with white spaces in its own manner.

Returning Multiple Values

For the OLE DB initialization properties specified in the OLE DB specification that can return multiple values, such as the Mode property, each value returned is separated with a pipe (|) character. The pipe character can have spaces around it or not:

Example Provider=MSDASQL;Mode=Deny Write|Deny Read

Listing Keywords Multiple Times

If a connection string contains a keyword/value pair multiple times, the last occurrence listed is used in the value set:

Example Provider=MSDASQL;Location=Pubs;Cache Authentication=True;Prompt=DBPROMPT_COMPLETE;Location=Customers

In this case, the Location property will be set to Customers.

One exception to this is the Provider keyword. If this keyword occurs multiple times in the string, the first occurrence is used:

Example Provider=MSDASQL;Location=Pubs; Provider=SQLOLEDB

In this case, MSDASQL will be used.

Setting the Window Handle Property

To set the Window Handle (DBPROP_INIT_HWND) property in a connection string, a long integer value is typically used:

Example Provider=Microsoft.Jet.OLEDB.3.5.1;Window Handle=14829

Note When the string is returned, the Window Handle keyword will not be included in the string because Window Handle is not a property that should be persisted.

Format Limitations

The connection string format has some limitations on what initialization settings it can persist and set.

COLID Properties

The connection string format does not support the ability to persist provider-specific properties that require a COLID for extra information.

Mathematical Operations

No mathematical operations will be performed on the value set. For example, setting a property to "2+3" will be set to the constant "2+3". If the property is not a string data type, it will be an illegal value. Similarly, the C technique of joining items together in a logical OR using the pipe (|) character is not allowed:

Example Provider=MSDASQL;DBType=3|5

Example Provider=MSDASQL;Connection Timeout=(34*23)/8

Each of these examples is illegal, assuming the property data types are not string. If they are string, they will be set to the constant specified.

A Note to Provider Writers

If you are writing an OLE DB data provider, your implementation is independent of any of the formatting rules for connection strings. OLE DB implements and validates these rules seamlessly for you.

See Also

Data Link API Overview

Referencing a Connection String in Your Application

OLE DB Initialization Properties: Quick Reference, which is taken from Appendix C of the OLE DB Programmer's Reference.


IDataInitialize

Use the IDataInitialize interface to create a data source object using a connection string. You can also retrieve a connection string from an existing data source object.

To build a connection string, use the prompting user interface available through the IDBPromptInitialize interface, then use IDataInitialize to get a data source object based on that connection string.

When to Call

Call IDataInitialize to provide a common entry point for consumers to obtain a data source object (DSO) or get persistent data source information.

IDataInitialize Methods

Method | Description
IDataInitialize::CreateDBInstance | Creates a data source object. Analogous to CoCreateInstance.
IDataInitialize::CreateDBInstanceEx | Creates a data source object. Analogous to CoCreateInstanceEx.
IDataInitialize::GetDataSource | Given a connection string, instantiates and returns a data source object.
IDataInitialize::GetInitializationString | Given a data source object, returns a connection string.
IDataInitialize::LoadStringFromStorage | Loads a connection string from storage.
IDataInitialize::WriteStringToStorage | Writes a connection string to storage.


IDataInitialize::CreateDBInstance

Creates a data source object. Analogous to CoCreateInstance.

HRESULT CreateDBInstance(

   REFCLSID         clsidProvider,
   IUnknown *       pUnkOuter,
   DWORD            dwClsCtx,
   LPOLESTR         pwszReserved,
   REFIID           riid,
   IUnknown **      ppDataSource);

Parameters

clsidProvider [in]

The CLSID of the provider to instantiate.

pUnkOuter [in]

A pointer to the controlling IUnknown interface if the data source object is being created as a part of an aggregate; otherwise, it is a null pointer.

dwClsCtx [in]

CLSCTX values.

pwszReserved [in]

Reserved for future use; must be NULL.

riid [in]

Interface requested on the data source.

ppDataSource [out]

A pointer to memory in which to return the interface pointer on the newly created data source.

Return Code

S_OK

The method succeeded.

E_FAIL

A provider-specific error occurred.

E_NOINTERFACE

The data source did not support the interface specified in riid.

riid was IID_NULL.

The object indicated by clsidProvider was not an OLE DB provider.

DB_E_NOAGGREGATION

pUnkOuter was not a null pointer and riid was something other than IID_IUnknown.

pUnkOuter was not a null pointer and the provider does not support aggregation.

pUnkOuter was not a null pointer and dwClsCtx was CLSCTX_LOCAL_SERVER or CLSCTX_REMOTE_SERVER.

E_INVALIDARG

dwClsCtx was not a valid value.

ppDatasource was a null pointer.

REGDB_E_CLASSNOTREG

The provider indicated by clsidProvider was not found.

dwClsCtx indicated a server type not supported by the provider.

Comments

None.


IDataInitialize::CreateDBInstanceEx

Creates a data source object. Analogous to CoCreateInstanceEx.

HRESULT CreateDBInstanceEx(

   REFCLSID         clsidProvider,
   IUnknown *       pUnkOuter,
   DWORD            dwClsCtx,
   LPOLESTR         pwszReserved,
   COSERVERINFO *   pServerInfo,
   ULONG            cmq,
   MULTI_QI *       rgmqResults);

Parameters

clsidProvider [in]

The CLSID of the provider to instantiate.

pUnkOuter [in]

A pointer to the controlling IUnknown interface if the data source object is being created as a part of an aggregate; otherwise, it is a null pointer.

dwClsCtx [in]

CLSCTX values.

pwszReserved [in]

Reserved for future use; must be NULL.

pServerInfo [in]

Machine on which the data source objects are to be instantiated.

cmq [in]

Number of MULTI_QI structures in rgmqResults.

rgmqResults [in, out]

Array of MULTI_QI structures.

Return Code

S_OK

The method succeeded.

E_FAIL

A provider-specific error occurred.

CO_S_NOTALLINTERFACES

At least one, but not all of the interfaces requested in the rgmqResults array were successfully retrieved.

The hr field of each of the MULTI_QI structures in rgmqResults indicates with S_OK or E_NOINTERFACE whether the specific interface was returned.

E_NOINTERFACE

The data source did not support the interface specified in riid.

riid was IID_NULL.

The object indicated by clsidProvider was not an OLE DB provider.

DB_E_NOAGGREGATION

pUnkOuter was not a null pointer and riid was something other than IID_IUnknown.

pUnkOuter was not a null pointer and the provider does not support aggregation.

pUnkOuter was not a null pointer and dwClsCtx was CLSCTX_LOCAL_SERVER or CLSCTX_REMOTE_SERVER.

E_INVALIDARG

dwClsCtx was not a valid value.

ppDatasource was a null pointer.

REGDB_E_CLASSNOTREG

The provider indicated by clsidProvider was not found.

dwClsCtx indicated a server type not supported by the provider.

Comments

None.


IDataInitialize::GetDataSource

Given a connection string, instantiates and returns a data source object.

HRESULT GetDataSource(

   IUnknown *       pUnkOuter,
   DWORD            dwClsCtx,
   LPCOLESTR        pwszInitializationString,
   REFID            riid,
   IUnknown **      ppDataSource);

Parameters

pUnkOuter [in]

A pointer to the controlling IUnknown interface if the data source object is being created as a part of an aggregate; otherwise, it is a null pointer.

dwClsCtx [in]

CLSCTX values.

pwszInitializationString [in]

A pointer to a connection string containing the information to be used for creating, and setting properties on a data source object. If this is NULL, an instance of the OLE DB provider for ODBC data is returned with default properties set.

riid [in]

The requested IID for the data source object returned in *ppDataSource.

ppDataSource [in, out]

A pointer to a data source object.

If *ppDataSource is null on entry, then GetDataSource generates a new data source object based on the information in pwszInitializationString, and returns a pointer to that data source object in ppDataSource.

If *ppDataSource is null and no provider is specified in pwszInitializationString, the data source object will be defaulted to the OLE DB Provider for ODBC.

If *ppDataSource is non-null and provider is specified in pwszInitializationString, then the data source specified by *ppDataSource will be used.

If *ppDataSource is non-null on entry, and a provider is specified in pwszInitializationString, GetDataSource checks to see if the specified provider matches the data source object passed in *ppDatasource. If so, GetDataSource sets the specified properties on the existing data source object. If not, GetDataSource returns an error and leaves *ppDatasource untouched.

Return Code

S_OK

The method succeeded.

E_FAIL

A provider-specific error occurred.

E_NOINTERFACE

The data source did not support the interface specified in riid.

riid was IID_NULL.

*ppDatasource was not null and did not indicate an OLE DB data source generated from IDataInitialize or IDBPromptInitialize.

DB_E_NOAGGREGATION

pwszInitializationString specified a provider, pUnkOuter was not a null pointer, and riid was something other than IID_IUnknown.

pwszInitializationString specified a provider, pUnkOuter was not a null pointer, and the provider does not support aggregation.

pwszInitializationString specified a provider, pUnkOuter was not a null pointer, and dwClsCtx was CLSCTX_LOCAL_SERVER or CLSCTX_REMOTE_SERVER.

E_INVALIDARG

pwszInitializationString specified a provider and dwClsCtx was not a valid value.

ppDatasource was a null pointer.

REGDB_E_CLASSNOTREG

The provider indicated in pwszInitializationString was not found.

dwClsCtx indicated a server type not supported by the provider specified in pwszInitializationString.

DB_E_ERRORSOCCURRED

A property specified in pwszInitializationString was not supported by the provider.

A property or value specified in pwszInitializationString was invalid or not supported by the provider.

*ppDatasource indicated a data source that was already initialized.

A property specified in pwszInitializationString was a read-only property and the property was not set to its default value.

It was not possible to set a property specified in pwszInitializationString.

One or more properties specified in pwszInitializationString conflicted with an existing property or one another.

Comments

None.

Code Example

The following code fragment shows how a consumer might use IDataInitialize to connect to a data source object:

HRESULT               hr                     =S_OK;
IDBInitialize         *pIDBInitialize        =NULL;
IdataInitialize       *pIDataInitialize       =NULL;

hr = CoCreateInstance(
         CLSID_MSDAINITIALIZE,
         NULL,
         CLSCTX_INPROC_SERVER,
         IID_IDBInitialize,
         reinterpret_cast<IUnknown **>(pIDataInitialize));
         if (!SUCCEEDED(hr))
            return hr;

hr = pIDataInitialize->GetDataSource(
         NULL,
         CLSCTX_INPROC_SERVER,
         L"Provider=MSDAORA;User ID=Oracle;Data Source=\\ORACLE",
         IID_IDBInitialize,
         reinterpret_cast<void **>(pIDBInitialize));


IDataInitialize::GetInitializationString

Given a data source object, returns a connection string.

HRESULT GetInitString(

   IUnknown *   pDataSource,
   Boolean      fIncludePassword,
   LPOLESTR *   ppwszInitString);

Parameters

pDatasource [in]

A pointer to a data source object.

fIncludePassword [in]

Whether or not to include the password property, if specified, in the returned initialization string.

ppwszInitString [out]

Returned connection string containing information necessary to re-create the data source object and current properties.

Return Code

S_OK

The method succeeded.

E_FAIL

A provider-specific error occurred.

E_NOINTERFACE

pDatasource did not indicate an OLE DB data source generated from IDataInitialize or IDBPromptInitialize.

E_INVALIDARG

ppDatasource was a null pointer.

ppwszInitString was a null pointer.

Comments

The string returned defines the connection and its current settings. This is done by returning a string version of all of the initialization properties set on the data source object.

The following initialization properties are not included in the string returned by this method:

Initialization Property | Why the Property is Not Returned in the Connection String
Asynchronous Processing (DBPROP_INIT_ASYNCH) | When the user connects asynchronously, the way the consumer is using the connection information is more relevant than the connection itself.
Window Handle (DBPROP_INIT_HWND) | The value is only good for that specific instance.
Prompt (DBPROP_INIT_PROMPT) | The calling application should have control over prompting.
Locale Identifier (DBPROP_INIT_LCID) | Not including this property allows users to share connection strings across different locales.

The password (DBPROP_AUTH_PASSWORD) property, if returned, is treated just like all other initialization properties. It is not encrypted or masked in any way.


IDataInitialize::LoadStringFromStorage

Loads a connection string.

HRESULT LoadStringFromStorage(

   LPCOLESTR     pwszFileName,
   LPCOLESTR *   ppwszInitializationString);

Parameters

pwszFileName [in]

Name of the file.

ppwszInitializationString [out]

On exit, *ppwszInitializationString will contain the connection string.

Return Code

S_OK

The method succeeded.

E_FAIL

A provider-specific error occurred.

E_INVALIDARG

pwszFileName was a null pointer.

ppwszInitializationString was a null pointer.

STG_E_FILENOTFOUND

The file indicated by pwszFileName was not found.

STG_E_ACCESSDENIED

The caller has insufficient permission to open the file.

The file is already open and locked for writing.

STG_E_INVALIDNAME

pwszFileName was an invalid file name.

STG_E_INVALIDHEADER

pwszFileName was a file that is not a UDL format file.

Comments

The ppwszInitializationString retrieved using this method will have any leading or trailing spaces trimmed from it. Hence, the string may not be exactly the same one as written using the WriteStringFromStorage method. For example, passing in the string "   Provider=MSDASQL.1; User ID = sa   " will be returned as "Provider=MSDASQL.1; User ID = sa"


IDataInitialize::WriteStringToStorage

Writes a connection string.

HRESULT WriteStringToStorage(

   LPCOLESTR      pwszFileName,
   LPCOLESTR      pwszInitializationString,
   DWORD          dwCreationDisposition);

Parameters

pwszFileName [in]

Name of the file.

pwszInitializationString [in]

Connection string to write.

dwCreationDisposition [in]

Flags controlling the write operation. Same as Win32® CreateFile dwCreationDisposition (CREATE_NEW, CREATE_ALWAYS, OPEN_EXISTING, OPEN_ALWAYS, TRUNCATE_EXISTING).

Return Code

S_OK

The method succeeded.

E_FAIL

A provider-specific error occurred.

E_INVALIDARG

pwszFileName was a null pointer.

pwszInitializationString was a null pointer.

dwCreationDisposition was an invalid value.

STG_E_FILENOTFOUND

dwCreationDisposition was OPEN_EXISTING and the file indicated by pwszFileName was not found.

dwCreationDisposition was TRUNCATE_EXISTING and the file indicated by pwszFileName was not found.

STG_E_FILEALREADYEXISTS

dwCreationDisposition was CREATE_NEW and the file indicated by pwszFileName already exists.

STG_E_PATHNOTFOUND

The specified pathname does not exist.

STG_E_ACCESSDENIED

dwCreationDisposition was OPEN_EXISTING or TRUNCATE_EXISTING and the caller has insufficient permission to open the file.

dwCreationDisposition was OPEN_EXISTING or TRUNCATE_EXISTING and another caller has the file open.

dwCreationDisposition was CREATE_NEW and the caller has insufficient permission to write to the directory.

STG_E_INVALIDNAME

pwszFileName was an invalid file name.

Comments

The writing of the file will respect any file system flags on an existing file. For example, if the file is marked as read-only, the WriteStringToStorage method will fail and not overwrite the existing file.


IDBPromptInitialize

The IDBPromptInitialize interface allows you to display the data link dialog boxes programmatically. Using the data link user interface, users can build a connection string dynamically, or select an existing data link (.udl) file.

You can then get a data source object based on the resulting connection string or .udl file name using the IDataInitialize interface.

When to Call

Call IDBPromptInitialize to prompt the user to build a connection string or to select a (.udl) file.

IDBPromptInitialize Methods

Method | Description
IDBPromptInitialize::PromptDataSource | Opens the Data Link Properties dialog box. Returns a connection string.
IDBPromptInitialize::PromptFileName | Opens the Select Data Link dialog box. Allows the user to browse and organize .udl files. Returns a fully qualified path to the user-selected .udl file.


IDBPromptInitialize::PromptDataSource

Opens the Data Link Properties dialog box. Returns a connection string.

HRESULT PromptDataSource(

   IUnknown *          pUnkOuter
   HWND                hWndParent,
   DBPROMPTOPTIONS     dwPromptOptions,
   ULONG               cSourceTypeFilter,
   DBSOURCETYPE *      rgSourceTypeFilter,
   LPCOLESTR           pwszszzProviderFilter,
   REFIID              riid,
   IUnknown **         ppDataSource);

Parameters

pUnkOuter [in]

A pointer to the controlling IUnknown interface if the data source object is being created as a part of an aggregate; otherwise, it is a null pointer.

hWndParent [in]

The parent window handle for dialog boxes to be displayed. The dialog box will always be centered within this window.

dwPromptOptions [in]

Specifies whether to prompt with the Create New Data Link wizard or the Data Link Properties dialog box.

Value | Meaning
DBPROMPTOPTIONS_WIZARDSHEET | Prompt with Create New Data Link wizard.
DBPROMPTOPTIONS_PROPERTYSHEET | Prompt with Data Link Properties dialog box.

cSourceTypeFilter [in]

Count of elements in rgSourceTypeFilter. The only valid value is zero.

This is reserved for future use.

rgSourceTypeFilter [in]

This parameter is ignored unless cSourceTypeFilter is greater than zero. Otherwise, this parameter points to a valid array of DBSOURCETYPE values. If it is non-null, the providers presented to the user will be limited to those that match the SOURCE_TYPEs specified in rgSourceTypeFilter.

This is reserved for future use.

pwszszzProviderFilter [in]

A double null-terminated string of ProgIDs.

This parameter must be null, or point to a valid string. If it is non-null, the providers presented to the user will be limited to those that match the providers' ProgIDs specified in pwszszzProviderFilter. If only one provider is specified, and DBPROMPTOPTIONS_WIZARDSHEET is specified, the provider selection page is not displayed.

riid [in]

The requested interface for the data link returned in *ppDataSource.

ppDatasource [in, out]

A pointer to a data source object (DSO).

If *ppDataSource is null on entry, then Prompt generates a new DSO based on the information specified by the user, and returns a pointer to that DSO in *ppDataSource.

If *ppDataSource is non-null on entry, Prompt uses the properties returned by IProperties::GetProperties() as initial values. If the user selects a different provider, Prompt will release the original *ppDataSource and create a new data source. On exit, *ppDataSource will be set to a pointer to the interface specified by riid.

Return Code

S_OK

The method succeeded.

E_FAIL

A provider-specific error occurred.

DB_E_CANCELED

The user canceled the dialog.

E_NOINTERFACE

The data source did not support the interface specified in riid.

riid was IID_NULL.

*ppDatasource was not null and did not indicate an OLE DB data source generated from IDataInitialize or IDBPromptInitialize.

DB_E_NOAGGREGATION

pUnkOuter was not a null pointer and riid was something other than IID_IUnknown.

pUnkOuter was not a null pointer and the provider does not support aggregation.

E_INVALIDARG

ppDatasource was a null pointer.

cSourceTypeFilter was not zero and rgSourceTypeFilter was a null pointer.

An element in rgSourceTypeFilter was not a valid filter.

dwPromptOptions was an invalid value.

Comments

None.

Code Example

The following code fragment shows how a consumer might use IDBPromptInitialize::PromptDataSource to prompt the user for connection information:

// First CoCreate the OLE DB Service Component Manager
//

HRESULT               hr                    =S_OK;
IDataInitialize      *pIDataInitialize      =NULL;

hr = CoCreateInstance(
         CLSID_DataLinks,
         NULL,
         CLSCTX_INPROC_SERVER,
         IID_IDataInitialize,
         reinterpret_cast<void **>(pIDataInitialize));

// CreateDBInstance to instantiate previously stored provider
//

CLSID               clsidProvider     (GetProviderCLSID());
IDBProperties       *pIDBProperties   =NULL;

hr = pIDataInitialize->CreateDBInstance(
          clsidProvider,
          NULL,
          CLSCTX_INPROC_SERVER,
          NULL,
          IID_IDBProperties,
          reinterpret_cast<void **>(&pIDBProperties));

// Set some previously stored properties
//

ULONG             cPropertySets           (GetPropertySetsCount());
DBPROPSET         (*rgPropertySets)        (GetPropertySets());

hr = pIDBProperties->SetProperties(cPropertySets, rgPropertySets);

// Prompt the user to view/edit the connection information
//

IDBPromptInitialize   *pIDBPromptInitialize   =NULL;

hr = pIDataInitialize ->QueryInterface(
         IID_IDBPromptInitialize,
         reinterpret_cast<void **>(&pIDBPromptInitialize));

hr = pIDBPromptInitialize->PromptDataSource(
         NULL,
         hWndParent,
         DBPROMPTOPTIONS_WIZARD,
         0, NULL,
         NULL,
         IID_IDBProperties,
         &pIDBProperties);


IDBPromptInitialize::PromptFileName

Opens the Select Data Link dialog box. Allows the user to browse and organize .udl files. Returns a fully qualified path to the user-selected .udl file.

To reference an .udl file in a connection string, use the File name keyword. For example, if the user selects the file "c:\MyData\Pubs.UDL", reference the connection string as "File name=c:\MyData\Pubs.UDL", then use IDataInitialize to get a data source object based on the connection string.

HRESULT PromptFileName(

   HWND                 hWndParent,
   DBPROMPTOPTIONS      dwPromptOptions,
   LPCOLESTR            pwszInitialDirectory,
   LPCOLESTR            pwszInitialFile,
   LPCOLESTR *          ppwszSelectedFile);

Parameters

hWndParent [in]

The parent window handle for dialog boxes to be displayed. The dialog box will always be centered within this window.

dwPromptOptions [in]

Specifies the dialog box options.

Value | Meaning
DBPROMPTOPTIONS_NODIRECTORYCHANGE | Prevents the user from changing directories.
DBPROMPTOPTIONS_BROWSEONLY | Prevents the user from creating new data link files.

pwszInitialDirectory [in]

A pointer to a string containing the initial directory for the dialog box.

pwszInitialFile [in]

A pointer to a string containing the initial file for the file name edit box of the dialog box.

ppwszSelectedFile [out]

*ppwszSelectedFile points to a string containing the full path to the file the user selected. ppwszSelectedFile cannot be null.

Return Code

S_OK

The method succeeded.

E_FAIL

A provider-specific error occurred.

DB_E_CANCELED

The user canceled the dialog.

E_INVALIDARG

ppwszSelectedFile was a null pointer.

dwPromptOptions was an invalid value.

STG_E_FILENOTFOUND

pwszInitialFile was non-null, and the file was not found.

STG_E_PATHNOTFOUND

pwszInitialDirectory was non-null; pathname does not exist.

STG_E_ACCESSDENIED

pwszInitialDirectory was non-null, and the user has insufficient permission to read from the directory.

STG_E_INVALIDNAME

pwszFileName was an invalid file name.

Comments

None.

Code Example

The following code fragment shows how a consumer might use IDBPromptInitialize::PromptFileName to prompt the user for a data link (.udl) file:

WCHAR      *pwszSelectedFile      =NULL;

hr = pIDBPropmtInitialize->PromptFileName(
         hWndParent,
         DBPROMPTOPTIONS_BROWSEONLY,
         L"C:\\Public",
         L"*.udl",
         &pwszSelectedFile);


OLE DB Initialization Properties: Quick Reference

When manually building a connection string, identify keywords using the Description property of the OLE DB initialization property that you want to set. Set keyword values using the "short" value.

This document lists the Description property and the short value (where applicable) for each standard OLE DB initialization property. For information about provider-specific properties, see the documentation that came with your provider.

DBPROP_AUTH_CACHE_AUTHINFO

Description: Cache Authentication

DBPROP_AUTH_ENCRYPT_PASSWORD

Description: Encrypt Password

DBPROP_AUTH_INTEGRATED

Description: Integrated Security

DBPROP_AUTH_MASK_PASSWORD

Description: Mask Password

DBPROP_AUTH_PASSWORD

Description: Password

DBPROP_AUTH_PERSIST_ENCRYPTED

Description: Persist Encrypted

DBPROP_AUTH_PERSIST_SENSITIVE_AUTHINFO

Description: Persist Security Info

DBPROP_AUTH_USERID

Description: User ID

DBPROP_INIT_ASYNCH

Description: Asynchronous Processing

OLE DB Documented Value | Connection String Short Value
DBPROPVAL_ASYNCH_INITIALIZE | Initialize

DBPROP_INIT_DATASOURCE

Description: Data Source

DBPROP_INIT_HWND

Description: Window Handle

DBPROP_INIT_IMPERSONATION_LEVEL

Description: Impersonation Level

OLE DB Documented Value | Connection String Short Value
DB_IMP_LEVEL_ANONYMOUS | Anonymous
DB_IMP_LEVEL_IDENTIFY | Identify
DB_IMP_LEVEL_IMPERSONATE | Impersonate
DB_IMP_LEVEL_DELEGATE | Delegate

DBPROP_INIT_LCID

Description: Locale Identifier

DBPROP_INIT_LOCATION

Description: Location

DBPROP_INIT_MODE

Description: Mode

OLE DB Documented Value | Connection String Short Value
DB_MODE_READ | Read
DB_MODE_WRITE | Write
DB_MODE_READWRITE | ReadWrite
DB_MODE_SHARE_DENY_READShare | Share Deny Read
DB_MODE_SHARE_DENY_WRITE | Share Deny Write
DB_MODE_SHARE_EXCLUSIVE | Share Exclusive
DB_MODE_SHARE_DENY_NONE | Share Deny None

DBPROP_INIT_PROMPT

Description: Prompt

OLE DB Documented Value | Connection String Short Value
DBPROMPT_PROMPT | Prompt
DBPROMPT_COMPLETE | Complete
DBPROMPT_COMPLETEREQUIRED | CompleteRequired
DBPROMPT_NOPROMPT | NoPrompt

DBPROP_INIT_PROTECTION_LEVEL

Description: Protection Level

OLE DB Documented Value | Connection String Short Value
DB_PROT_LEVEL_NONE | None
DB_PROT_LEVEL_CONNECT | Connect
DB_PROT_LEVEL_CALL | Call
DB_PROT_LEVEL_PKT | Pkt
DB_PROT_LEVEL_PKT_INTEGRITY | Pkt Integrity
DB_PROT_LEVEL_PKT_PRIVACY | Pkt Privacy

DBPROP_INIT_PROVIDERSTRING

Description: Extended Properties

DBPROP_INIT_TIMEOUT

Description: Connect Timeout

See Also

Referencing a Connection String in Your Application

Connection String Syntax

Appendix C of the OLE DB Programmer's Reference


About the OLE DB Documentation

This documentation assumes the following:

  • A working knowledge of the C or C++ programming language

  • Knowledge of COM

In addition, a general knowledge of databases and SQL is helpful.

The code examples in this documentation are designed for illustrative purposes only. Because they are written primarily to demonstrate OLE DB principles, efficiency has sometimes been set aside in the interest of clarity. In addition, whole sections of code have sometimes been omitted for clarity. These include the definitions of non-OLE DB functions and most error handling.

The following typographic conventions are used.

Format | Used for
DBPROPFLAGS | Uppercase letters indicate certain values, SQL statements, and macro names.
#include <oledb.h> | Monospace font is used for sample command lines and program code.
argument | Italicized words indicate information that the user or the application must provide, or word emphasis.
IRowset | Bold type indicates that syntax must be typed exactly as shown, including method names.


Overview of OLE DB

Today, a vast amount of critical information necessary to conduct day-to-day business is found outside of traditional, corporate production databases. Instead, this information is found in file systems, indexed-sequential files such as Btrieve, personal databases such as Microsoft® Access and Paradox, and productivity tools such as spreadsheets, project management planners, and electronic mail. To take advantage of the benefits of database technology, such as declarative queries, transactions, and security, businesses must move the data from its original containing system into a database management system (DBMS).

This process is expensive and redundant. Furthermore, businesses need applications to exploit the advantages of database technology not just when accessing data within a DBMS, but also when accessing data from any other information container.

OLE DB is a set of ActiveX® interfaces that provide applications with uniform access to data stored in diverse information sources. These interfaces support the amount of DBMS functionality appropriate to the data source, enabling it to share its data.


Access to Diverse Sources

OLE DB enables the development of applications that access diverse data sources. For example, consider a sales representative who wants to find all e-mail messages received within the last two days from Seattle customers, including their addresses, to which no one has yet replied. This query involves searching the mailbox file containing the sales representative's e-mail, as well as a Customers table stored in a Microsoft Access DBMS to identify customers. The query can be formulated in an extended SQL syntax as follows. (This query is designed for illustration only. It is not implemented in any product of which we are aware.)

SELECT  m1.*, c.Address FROM
 MakeTable(Mail, d:\mail\smith.mmf) m1,
 MakeTable(Access, d:\access\Enterprise.mdb, Customers) c
 WHERE m1.Date >= date(today(), -2)
  AND m1.From = c.Emailaddr
  AND c.City = "Seattle"
  AND NOT EXISTS
   (SELECT * FROM MakeTable(Mail, d:\mail\smith.mmf) m2
    WHERE m1.MsgId = m2.InReplyTo);

Assume that MakeTable is a function that makes the mail file D:\Mail\Smith.mmf into a table. It also exposes the Customers table from a Microsoft Access database (D:\Access\Enterprise.mdb). The function date takes a date and a number of days as arguments and produces a date.

OLE DB makes it easy for applications to access data stored in diverse DBMS and non-DBMS information sources. DBMS sources may include mainframe databases such as IMS and DB2, server databases such as Oracle and Microsoft® SQL Server™, and desktop databases such as Microsoft Access, Paradox, and Microsoft® FoxPro®. Non-DBMS sources might include information stored in file systems such as Windows NT® and UNIX, indexed-sequential files, e-mail, spreadsheets, project management tools, and many other sources.


Component Database Management Systems

OLE DB defines an open, extensible collection of interfaces that factor and encapsulate orthogonal, reusable portions of DBMS functionality. These interfaces define the boundaries of DBMS components such as row containers, query processors, and transaction coordinators, which enable uniform, transactional access to diverse information sources. A DBMS becomes a conglomerate of cooperating components that consume and produce data through a uniform set of interfaces.

The OLE DB functional areas include data access and updates (rowsets), query processing, catalog information, notifications, transactions, security, and remote data access. By defining a uniform set of interfaces to access data, OLE DB components contribute not only to uniform data access among diverse information sources, but also help to reduce the application's footprints by enabling them to use only the DBMS functionality they need. Initially, OLE DB provides uniform access to tabular data.

A component DBMS architecture using OLE DB interfaces

OLE DB uses the Component Object Model (COM) infrastructure, which reduces unnecessary duplication of services and provides a higher degree of interoperability not only among diverse information sources, but also among existing programming environments and tools. Indeed, OLE DB is the way to access data in a COM environment. The above figure illustrates how an application can employ OLE DB interfaces to initiate actions by a query processor to access different data structures.


Consumers and Providers

A consumer is any piece of system or application code that consumes an OLE DB interface. This includes OLE DB components themselves. A provider is any software component that exposes an OLE DB interface.

OLE DB providers can be classified broadly into two classes. A data provider is any OLE DB provider that owns data and exposes its data in a tabular form as a rowset, which is defined later in this chapter. Examples of data providers include relational DBMSs, storage managers, spreadsheets, ISAMs, and e-mail.

A service component (also referred to as a service component) is any OLE DB component that does not own its own data, but encapsulates some service by producing and consuming data through OLE DB interfaces. A service component is both a consumer and a provider. For example, a heterogeneous query processor is a service component. Suppose a consumer asks to join data from tables in two different data sources. In its role as a consumer, the query processor retrieves rows from rowsets created over each of the base tables. In its role as a provider, the query processor creates a rowset from these rows and returns it to the consumer.

Component DBMSs offer greater efficiency than traditional DBMSs because consumers generally require only a portion of the database management functionality offered, thereby reducing resource overhead. OLE DB allows simple tabular data provides to implement functionality native to their data store: at a minimum, they can use only the interfaces that expose data as tables. This opens the opportunity for the development of query processor components, such as SQL or geographical query processors, which can consume tabular information from any provider that exposes its data through OLE DB. In addition, SQL DBMSs can expose their functionality in a more layered manner by using the OLE DB interfaces.

The following section describes the architecture and main interface areas of OLE DB.


OLE DB Components

OLE DB defines the following components, each of which is a COM object:

  • Enumerators search for available data sources and other enumerators. Consumers that are not customized for a particular data source use enumerators to search for a data source to use.

  • Data source objects contain the machinery to connect to a data source, such as a file or a DBMS. They are a factory for sessions.

  • Sessions provide a context for transactions and can be implicitly or explicitly transacted. A single data source object can create multiple sessions. Sessions are a factory for transactions, commands, and rowsets.

  • Transaction objects are used when committing or aborting nested transactions at other than the lowest level.

  • Commands execute a text command, such as an SQL statement. If the text command specifies a rowset, such as an SQL SELECT statement, the command is a factory for the rowset. A single session can create multiple commands.

  • Rowsets expose data in tabular format. A special case of a rowset is an index. Rowsets can be created from the session or the command.

  • Errors can be created by any interface on any OLE DB object. They contain additional information about an error, including an optional custom error object.

For example, a consumer might use the following objects.

Typical use of OLE DB components by a consumer


Enumerator Objects

An enumerator exposes the ISourcesRowset interface, which returns a rowset describing all data sources and enumerators visible to the enumerator. A root enumerator, shipped in the Microsoft Access Software Development Kit (SDK), traverses the registry looking for data sources and other enumerators. Other enumerators traverse the registry or search in a provider-specific manner. For example, an enumerator over the file system might search for subdirectories.


Data Source Objects and Session Objects

The OLE DB connection model defines how data and service components (also referred to as service components) are located and activated. Two objects are the basis for the OLE DB connection model: the data source object and the session. To access an OLE DB provider, a consumer must first create an instance of a data source object. Each data provider is identified by a unique class identifier (CLSID) in the registry. You call CoCreateInstance, which creates an instance of the object through the object's class factory. The data source object exposes IDBProperties, which the consumer uses to provide basic authentication information such as user ID and password (for cases when the data source does not exist in an authenticated environment) as well as the name of the data source (file or database) containing the data to be accessed. It also exposes IDBInitialize, which the consumer uses to connect to the data source.

After a data source object has been successfully initialized, the consumer can call methods in IDBProperties to query the capabilities of a provider. These capabilities include the interfaces, rowset properties such as scrollability, transaction properties such as supported isolation levels, and the SQL dialects a provider supports. The consumer can also call methods in IDBCreateSession to create a session.

The session acts as a rowset, command, and transaction factory. A data provider that does not support commands exposes the IOpenRowset interface, which enables providers to expose their data directly as rowsets without the use of commands.


Rowset Objects

Rowsets are the central object that enables all OLE DB data providers to expose data in tabular form. Conceptually, a rowset is a set of rows in which each row has columns of data. Base table providers present their data in the form of rowsets. Query processors present the result of queries in the form of rowsets. This makes it possible to layer components that consume or produce data through the same object.

The most basic rowset object exposes four interfaces.

Interface | Used for
IRowset | Contains methods for fetching rows in the rowset sequentially.
IAccessor | Permits the definition of groups of column bindings describing the way tabular data is bound to consumer program variables.
IColumnsInfo | Provides information about the columns of the rowset.
IRowsetInfo | Provides information about the rowset itself.

By using IRowset, a consumer can sequentially traverse the rows in the rowset, including traversing backward if the rowset supports it. The figure illustrates the data structures a generic rowset object might support.

Data structures supported by a generic rowset object

Other rowset interfaces expose additional rowset capabilities. For example, there is an interface to insert, delete, and update rows and interfaces that expose richer row navigation models, such as direct access and scrollability.

Rowsets are created in one of two ways. First, they can be created as the result of a query. Second, they can be created directly as the result of calling IOpenRowset::OpenRowset. All providers support creation by calling IOpenRowset::OpenRowset. However, simple providers — such as those built over a base table, index, file, or in-memory structure — generally do not support creation by query.

Index rowsets are rowsets whose rows are formed from index entries. Index rowsets have the additional property of allowing efficient access to contiguous rows within a range of keys. They are used primarily by query processor components. Indexes abstract the functionality of B-trees and indexed-sequential files.

Interface | Used for
IRowset | Traverses indexes.
IColumnsInfo | Obtains information about the index entries.
IRowsetChange | Performs insertions and deletions.


Data Binding and Accessors

Data binding describes the relationship between an element in a consumer structure and a column in a rowset or parameter in a text command. A consumer uses the binding structure to communicate to the provider how it expects the data to be transferred from the provider to the consumer's buffer, and vice versa. The binding structure includes information such as the column or parameter being accessed, the data type in the consumer's buffer, the offset to the data in the consumer's data structure, the amount of space available for the data value, and the precision and scale for numeric values. For columns containing OLE objects, the consumer can also request the interface that must be returned to the consumer when the object is fetched.

An accessor is a group of binding structures. It enables consumers to define simultaneous access to multiple columns or parameters and enables providers to optimize access to these multiple columns or parameters.


Command Objects

In OLE DB, Data Definition Language (DDL) and Data Manipulation Language (DML) statements are referred to as text commands. A command object contains a text command and encapsulates the query processing services available in today's DBMSs. Commands expose various interfaces representing different areas of functionality of a query processor, including query formulation, preparation, and execution. This figure illustrates a typical OLE DB query processor.

Typical OLE DB query processor

The main purpose of a command object is to execute a text command. Executing a command such as an SQL SELECT statement creates a rowset. Executing a command such as an SQL UPDATE or CREATE TABLE statement does not create a rowset.

Text commands are expressed in a provider-specific language—usually ANSI SQL92.

To use a command, an OLE DB consumer performs the following actions:

  1. Obtains an interface on the command.

  2. Builds a text string representing the command text.

  3. Passes the text string to the command.

  4. Requests properties to be supported by the resulting rowset, if any, including the interfaces it will expose.

  5. Executes the command. (If the command text specified the creation of a rowset, the command returns the rowset to the consumer.)

Because providers can both consume and produce rowsets, it is possible to compose query processors to process distributed, heterogeneous, or parallel queries. It is also possible to compose specialized query processors, such as SQL query processors, text-search query processors, and geographical or image query processors.

Rowset Properties

During command formulation, OLE DB consumers can request certain properties to be satisfied by the rowsets resulting from a command. Common properties include the set of interfaces to be supported by the resulting rowset. Any rowset returned from a command exposes the mandatory interfaces IRowset, IAccessor, IColumnsInfo, and IRowsetInfo, described earlier.

The basic IRowset interface supported by all rowsets enables consumers to, at minimum, navigate forward in the rowset. By requesting the rowset property to scroll backward, a consumer can navigate the rowset in both directions.

To support bidirectional rowsets efficiently, there are additional requirements when creating rowset objects. A forward-only rowset can be fed directly from the query execution plan output. A bidirectional rowset might require materializing the result. In addition to the default interfaces, a consumer may request interfaces to enable direct positioning within a rowset with bookmarks (IRowsetLocate), scrollability (IRowsetScroll), immediate updatability of rows (IRowsetChange), and deferred updatability of rows (IRowsetUpdate). It is also possible to request additional properties that specialize the behavior of certain interfaces — for example, to make some columns of the rowset updatable and the rest read-only.


Schema Information

OLE DB consumers obtain catalog information through IDBSchemaRowset. This interface enables consumers to request information about types, tables, columns, indexes, views, assertions and constraints, statistics, character sets, collations, and domains. All catalog information is returned to the consumer as rowsets. The schemas of these rowsets are based on the ANSI SQL 92 standard.


Transaction Management

OLE DB defines a basic set of interfaces for transaction management. A transaction enables consumers to define units of work within a provider. These units of work have the atomicity, concurrency, isolation, and durability (ACID) properties. They allow the specification of various isolation levels to enable more flexible access to data among concurrent consumers.


Local Transactions

Local transactions refer to transactions running in the context of a resource manager. A provider that supports transactions exposes ITransactionLocal on the session. A call to the ITransactionLocal::StartTransaction method begins a transaction on the session. A session can be inside or outside of a transaction at any time. When created, a session is outside of a transaction and all the work done under the scope of the session is immediately committed on each OLE DB method call. When a session enters a local or coordinated transaction, all the work done under the session — between the StartTransaction and ITransaction::Commit or ITransaction::Abort method calls, including other objects created underneath it (commands or rowsets) — are part of the transaction.

StartTransaction supports various isolation levels that consumers can request when creating a transaction. OLE DB providers do not need to support all possible transaction options defined. A consumer can interrogate the transaction capabilities of a provider through IDBProperties.

For providers that support nested transactions, calling StartTransaction within an existing transaction begins a new nested transaction below the current transaction. Calling the Commit or Abort method commits or aborts, respectively, the transaction at the lowest level.


Distributed Transactions

OLE DB supports distributed transactions as defined in the Microsoft Distributed Transaction Coordinator Resource Manager Implementation Guide. The interfaces specify a model for coordinating transactions among multiple (possibly distributed) data providers.


Event Notifications

OLE DB uses an ActiveX controls notification model to implement notifications among OLE DB components and consumers. Notifications are used by groups of cooperating consumers sharing a rowset. All consumers and the rowset-generating actions are assumed to be working within the same transaction. Local notifications enable cooperating consumers sharing a rowset to be informed about actions on the rowset performed by their peers. For example, consider a form containing two data controls, one displaying data in a grid and the other displaying data as a two-dimensional histogram. Both controls receive their data from the same rowset and operate individually as consumers of the rowset. An end user can send an update to the rowset through the grid control. Notifications enable the other control to be informed of this change as it occurs, giving it the opportunity to update the histogram as appropriate and ultimately providing a consistent view of the data within the form. Consumers register their interest in receiving local notifications though IConnectionPointContainer. Consumers support the IRowsetNotify interface, which is called by the rowset as events occur.

Notifications in OLE DB define a basic mechanism on which to implement active data source behavior. IRowsetNotify represents one of many possible notification contracts between sources and sinks. Providers can define other notification contracts as needed.


Remote Data Access

Remote data access is not fully supported in OLE DB, Version 2.0.

Future Direction

Future versions of OLE DB will enable efficient and transparent access between consumers and providers across threads, process, and machine boundaries. In the following discussion, the term process boundary refers to a process or machine boundary.

OLE DB includes two forms of remoting: data remoting and object (or interface) remoting.

In data remoting, a provider driver exposing OLE DB interfaces is present on the client side. All interaction with the provider is through the driver. All details about communication protocol, data transfer format, and the provider's native interface calls are handled internally by the driver—for example, an OLE DB provider accessing an SQL DBMS through ODBC. In other words, the process or machine boundary cuts through the provider's driver. The consumer/provider relationship is entirely within a single process.

In interface remoting, an OLE DB provider is running remotely in a different process. Interface remoting provides the infrastructure and mechanisms that enable a method call to return an interface pointer to an object created in a different process. The infrastructure that performs the remoting of interfaces is transparent to both the client and the server object. Neither the client or server object are aware that the other party is in a different process. Distributed COM (DCOM) (another COM technology, available in Windows NT 4.0 and later) provides an open specification and reference implementation of such an infrastructure.

The following figure illustrates the various configurations in which consumer and provider code can exist. Clients always call in-process code. Objects are always called by in-process code.

Consumer/Provider code configurations

The proxy/stub agents are agents that take care of marshaling arguments across the process boundary and optimize access over the network by trying to hide the latency and bandwidth. Their design is an open-ended optimization problem. Proxies and stubs may vary in complexity. Simple proxy/stub pairs provide marshaling of arguments to method calls. Sophisticated proxy/stub pairs may provide client-side caching combined with bulk data transfer over the process boundary to minimize network trips, or may even combine method calls, thereby optimizing network trips across multiple method calls.

Providers may choose to implement their remote data access by either approach. The OLE DB Provider for ODBC (included in this Data Access SDK) provides an implementation of data remoting through ODBC. In the ODBC provider, all process and network communications are managed internally by the ODBC driver. Future versions of the OLE DB SDK will provide a reference implementation of interface remoting, packaged as a library of generic proxy/stub pairs for all OLE DB objects. Providers that want to have a tighter control over the performance of their OLE DB implementations across threads, process, and machine boundaries are free to implement their own remoting agents by using the network protocol and optimizations of their choice.


Security

Security is not fully supported in OLE DB, Version 2.0.

Future Direction In the future, OLE DB will define interfaces that support authentication, authorization, and secure administration of access among users, groups, and resources. The role of security in OLE DB is to expose the relevant security functionality encapsulated in the security models of individual data providers or the underlying operating system on which they run, provide a pass-through layer of security interfaces through which the underlying security functionality can be made available to OLE DB consumers, and provide a unified view of security.

For information about the authentication properties defined in OLE DB, see "Initialization Properties" in Appendix C.


Authentication

OLE DB allows authentication through different mechanisms depending on the layer that enforces authentication: operating system, network, or data provider. In password-based authentication, the consumer authenticates itself to the data provider by supplying a name and a password. The data provider enforces authentication in this case. When the consumer and provider operate in-process, passwords can be passed directly to the provider.

In situations where a network connection is involved, the password must be sent to the provider over the network, which presents additional security problems. Domain-based authentication implies the availability of an authentication service provided by the operating system, such as Windows NT. In this environment, users authenticate themselves to the domain by providing a password when logging in to the system. Once the user is authenticated to the domain, the domain provides identification information on behalf of the user in a trusted manner.

Distributed authentication assumes the existence of a distributed authentication service, such as the one provided by the Microsoft Security Support Provider Interface (SSPI), which is modeled after the General Security Support API from Digital Equipment Corporation. In OLE DB, consumers call IDBProperties to request the type of authentication mechanism they want to use. They can also request the quality of service they expect when communicating to the provider across a network.


Authorization

Authorization, or access control, is concerned with the enforcement of the privileges defined among users, groups, roles, services, and so on. In most cases, authorization is enforced internally by the provider. The consumer only gets return codes from method invocations, which determine whether the call was successful or a permission violation occurred. OLE DB, defines the return code DB_SEC_E_PERMISSIONDENIED and status codes such as DBSTATUS_E_PERMISSIONDENIED and DBROWSTATUS_E_ PERMISSIONDENIED for this purpose.

Future Direction

Future versions of OLE DB will take advantage of a set of security interfaces defined by Distributed COM to control the security of RPC calls between proxies and stubs, the launching of objects, and the impersonation of clients by servers.


Administration

Security administration is concerned with managing the granting and revoking of permissions among users, groups, roles, and any provider-specific object (data or code). In OLE DB, data providers that support commands can process security administration requests formulated by consumers by using SQL GRANT and REVOKE statements.

Future Direction

Future versions of OLE DB will define interfaces to enable data providers that do not support commands to expose methods to get and set permissions among users, objects, and groups in a provider-specific manner.


Enumerators, Data Source Objects, and Sessions

Overview of Enumerators, Data Source Objects, and Sessions

Three COM objects are necessary for transactions to occur between consumers and providers: data source, session, and enumerator.

The OLE DB connection model defines how data providers and service components are located and activated. Two objects are the basis for the OLE DB connection model: the data source object and the session. To access an OLE DB provider, a consumer must first create an instance of a data source object.

An enumerator is an object that can be used to search for the data source object rather than searching the registry directly. Once found, a session object is initialized, which then acts as a factory for transactions, commands, and rowsets. The primary function of a session is to define a transaction. A transaction is a mechanism used to define persistent units of work within an application, and to define how the different units relate to each other in a system with parallel activities.

For more information on | Go to
COM (Component Object Model) | “Remote Data Access” in Chapter 1, “Overview of OLE DB”
Data providers and service components | “Consumers and Providers” in Chapter 1, “Overview of OLE DB”
Transactions | Chapter 12, “Transactions”
Consumers and providers | “Consumers and Providers” in Chapter 1, “Overview of OLE DB”
OLE DB connection model | “Data Source Objects and Session Objects” in Chapter 1, “Overview of OLE DB”
Data source object | “Data Source Objects" in this chapter
Session | “Sessions” in this chapter
Enumerator | “Enumerators” in this chapter
Rowsets | Chapter 4, “Rowsets”


Enumerators

An enumerator is an object that searches for data sources and other enumerators.

CoType TEnumerator {
   [mandatory] IParseDisplayName;
   [mandatory] ISourcesRowset;
   [optional]  IDBInitialize;
   [optional]  IDBProperties;
   [optional]  ISupportErrorInfo;
}


Enumerating Data Sources and Enumerators

Generic consumers should use enumerators to search for data sources and other enumerators, rather than searching the registry directly. In this way, consumers will continue to work if the registry information changes or new root enumerators with additional functionality, such as the ability to enumerate data sources on other machines, are later introduced. To create an enumerator, the consumer calls CoCreateInstance with the class ID of the enumerator. In most cases, the consumer requests ISourcesRowset when creating an enumerator.

To list the data sources and enumerators visible to the current enumerator, the consumer calls ISourcesRowset::GetSourcesRowset. This method returns a rowset of information about the currently visible data sources and enumerators. To create an instance of a data source or enumerator listed in this rowset, the consumer first calls IParseDisplayName::ParseDisplayName for the returned display name. This method returns a moniker, which is the name of the object instance. The consumer can then bind to create an instance of the object.

Each row in the rowset returned by ISourcesRowset contains a SOURCES_ISPARENT column. For enumerator rows, this column contains VARIANT_TRUE if the enumerator described by the row is the same one that can enumerate the current one. For example, if the enumerator lists files and directories in the file system, this is the parent directory. The SOURCES_ISPARENT column thus allows the consumer to traverse backward through a hierarchy of enumerators, such as might be found over a file system. The consumer ignores the SOURCES_ISPARENT column for data source rows.

A root enumerator is shipped in the OLE DB Software Development Kit (SDK). This enumerator traverses the registry searching for providers and enumerators. It has a class ID of CLSID_OLEDB_ENUMERATOR. For more information about it, see the "Root Enumerator Object" in the OLE DB Core Components documentation.

Providers also can support enumerators, although they are not required to do so. These enumerators search for data sources and enumerators known to the provider. For example, suppose a provider exposes data in an SQL DBMS that supports multiple, separate databases in a single installation where the DBMS treats each database as a separate data source. The enumerator for this provider might search the DBMS for databases. The rowset returned by the enumerator would include one row for each database it finds, marking that database as a data source.

Or suppose a provider exposes data in text files and treats each directory as a data source and each text file in that directory as a table. The enumerator for this provider might traverse the file system. The rowset returned by the enumerator would include two rows for each directory it finds, marking one row as a data source and the other row as an enumerator. To access the files in a directory, the consumer would create an instance of a data source object over the directory, using the display name from the row marked as a data source. To search for subdirectories, the consumer would create an instance of an enumerator over the directory, using the display name from the row marked as an enumerator.

For information about the registry entries used by enumerators, see "Enumerator Registry Entries" in Chapter 14.


Enumerator States

If an enumerator does not require initialization, it is created in an initialized state and remains in that state. For example, an enumerator that searches the registry does not require initialization.

If an enumerator requires initialization, it is created in an uninitialized state. For example, if the enumerator enumerates directories, the enumerator does not yet know the starting directory. If the enumerator enumerates databases in an SQL DBMS, the enumerator has not yet connected to the DBMS.

To determine whether an enumerator requires initialization, the consumer calls QueryInterface in IDBInitialize. If this interface is exposed, the enumerator requires initialization and must also expose IDBProperties. If it is not exposed, the enumerator does not require initialization. The remainder of this section discusses enumerators that require initialization.

In the uninitialized state, the consumer can do only the following with the enumerator:

  • Call QueryInterface in IUnknown, IDBInitialize, IDBProperties, or ISupportErrorInfo.

  • Call AddRef or Release in any interface obtained from QueryInterface.

  • Call methods in IDBProperties for properties in the Initialization property group.

  • Call methods in IDBInitialize or ISupportErrorInfo.

If the consumer calls any other OLE DB method, the provider returns E_UNEXPECTED. The provider may also return E_UNEXPECTED or E_NOINTERFACE if the consumer calls QueryInterface for an interface other than those listed.

To initialize the enumerator, the consumer first calls IDBProperties::SetProperties to set the value of properties in the Initialization property group. For example, if the enumerator enumerates directories, the consumer might specify the server and directory to start with. If the enumerator enumerates databases in an SQL DBMS, the consumer might specify the name of the server on which the DBMS resides, along with the user ID and password to use.

Certain properties in the Initialization property group are required by the enumerator for initialization. To determine which properties these are, the consumer calls IDBProperties::GetPropertyInfo for properties in the Initialization property group. For required properties, the DBPROPFLAGS_REQUIRED bit is set in the dwFlags element of the returned DBPROPINFO structure.

After the consumer has finished setting properties, it calls IDBInitialize::Initialize. Initialize can prompt for additional information, especially if the consumer has not set values for all required properties. The consumer can control how much Initialize prompts, including suppressing prompting altogether. After Initialize returns successfully, the enumerator is in an initialized state.

If the consumer needs to reinitialize the enumerator after it has been initialized, it must first uninitialize it. For example, if the enumerator enumerates directories and the consumer needs to enumerate directories on a different server, the consumer must disconnect from the first server before connecting to the second. If the enumerator enumerates databases in an SQL DBMS and the consumer needs to enumerate databases in a different installation of that DBMS, the consumer must disconnect from the first DBMS before connecting to the second. To uninitialize the enumerator, the consumer calls IDBInitialize::Uninitialize.

The following figure shows the enumerator states and the methods that change them.

Enumerator states


Data Source Objects

A provider initially creates an instance of a data source object when the consumer calls CoCreateInstance on the class ID for that provider. Consumers don't usually call CoCreateInstance directly, but instead bind to the file moniker of a persisted data source object or to a moniker returned by the enumerator object. The code that performs the binding calls CoCreateInstance on behalf of the consumer. It is important to distinguish the data source object from the data source, which actually contains the data. For example, a data source might be a text file, an SQL database, or an in-memory array in an application.

Data source objects:

CoType TDataSource {
   [mandatory] interface IDBCreateSession;
   [mandatory] interface IDBInitialize;
   [mandatory] interface IDBProperties;
   [mandatory] interface IPersist;
   [optional]  interface IConnectionPointContainer;
   [optional]  interface IDBAsynchStatus;
   [optional]  interface IDBDataSourceAdmin;
   [optional]  interface IDBInfo;
   [optional]  interface IPersistFile;
   [optional]  interface ISupportErrorInfo;
}

For information about the registry entries used by providers, which create instances of data source objects, see "Provider Registry Entries" in Chapter 14.


Data Source Object States

When a data source object is created, it is in an uninitialized state. For example, if the data source is a file, the data source object has not yet opened the file; if the data source is an SQL database, the data source object has not yet connected to the database.

In the uninitialized state, the consumer can do only the following with the data source:

  • Call QueryInterface in IUnknown, IConnectionPointContainer, IDBAsynchStatus, IDBDataSourceAdmin, IDBInitialize, IDBProperties, IPersist, IPersistFile, or ISupportErrorInfo.

  • Call AddRef or Release in any interface obtained from QueryInterface.

  • Call methods in IConnectionPointContainer, IDBDataSourceAdmin, IDBInitialize, IPersist, IPersistFile, or ISupportErrorInfo.

  • Call methods in IDBProperties for properties in the Initialization property group.

If the consumer calls any other OLE DB method, the provider returns E_UNEXPECTED. The provider may also return E_UNEXPECTED or E_NOINTERFACE if the consumer calls QueryInterface for an interface other than those listed.

To initialize the data source, the consumer first calls IDBProperties::SetProperties to set the value of properties in the Initialization property group. For example, if the data source is a text file, the consumer might specify the name and location of the file. If the data source is a database in an SQL database management system, the consumer might specify the name of the server on which the database resides along with the user ID and password to use.

Certain properties in the Initialization property group are required by the data source object for initialization. To determine which properties these are, the consumer calls IDBProperties::GetPropertyInfo for properties in the Initialization property group. For required properties, the DBPROPFLAGS_REQUIRED bit is set in the dwFlags element of the returned DBPROPINFO structure.

After the consumer has finished setting properties, it calls IDBInitialize::Initialize. Initialize can prompt for additional information, especially if the consumer has not set values for all required properties. The consumer can control how much Initialize prompts, including suppressing prompting altogether. After Initialize returns successfully, the data source object is in an initialized state. The consumer can also initialize a data source by calling IDBDataSourceAdmin::CreateDataSource to create and initialize a new data source.

If the consumer needs to reinitialize the data source object after it is initialized, it must first uninitialize the data source. For example, if the data source is a file and the consumer needs to use a different file, the consumer must close the first file before opening the second. If the data source is an SQL database and the consumer needs to use a different database, the consumer must disconnect from the first database before connecting to the second. To uninitialize the data source object, the consumer calls IDBInitialize::Uninitialize.

The following figure shows the data source object states and the methods that change them.

Data source object states


Persisting Data Source Objects

The data source object can be persisted to a file. Any child objects of the data source object, such as sessions, commands, or rowsets, are not persisted with the data source object. The DBPROP_AUTH_PERSIST_SENSITIVE_AUTHINFO property controls whether sensitive authentication information, such as the password, is persisted. If it is persisted, the DBPROP_AUTH_PERSIST_ENCRYPTED property controls whether it is persisted in encrypted form. The provider can make both of these properties read-only to control whether sensitive information can be persisted.

To persist a data source object to a file, the consumer calls IPersistFile::Save; the data source object can be in an uninitialized or initialized state. To reload the data source object, the consumer calls IPersistFile::Load on an uninitialized data source object; if the data source object is initialized, Load returns DB_E_ALREADYINITIALIZED. After loading the persisted file, the consumer must call IDBInitialize::Initialize to initialize the data source object.

When the provider persists a data source object, it persists enough information to return all of the properties in the Initialization property group to the state they were in when the data source object was persisted. The provider is not required to save the values of any properties in other property groups that the consumer has set. In most cases, this requires the provider to persist its class ID and the values of any properties set by the consumer. When the consumer loads the persisted file, the provider retrieves the persisted information and overwrites the values of any properties the consumer has set, returning them to their persisted value if they were persisted, or default value if they were not persisted.

If the provider does not support IPersistFile, the consumer can save the information necessary to re-create the data source object itself. It calls IDBProperties::GetProperties to get the value of all properties in the Initialization property group and calls IPersist::GetClassID to get the class ID of the provider. It then saves this information and later uses it to re-create the data source object.


Sessions

A session makes it possible to create a command and a rowset, and to create or modify tables and indexes. It is a basic part of the OLE DB connection model. The primary function is to define a transaction: that mechanism used to identify persistent units of work within an application.

Sessions:

CoType TSession {
   [mandatory] interface IGetDataSource;
   [mandatory] interface IOpenRowset;
   [mandatory] interface ISessionProperties;
   [optional]  interface IDBCreateCommand;
   [optional]  interface IDBSchemaRowset;
   [optional]  interface IIndexDefinition;
   [optional]  interface ISupportErrorInfo;
   [optional]  interface ITableDefinition;
   [optional]  interface ITransaction;
   [optional]  interface ITransactionJoin;
   [optional]  interface ITransactionLocal;
   [optional]  interface ITransactionObject;
}

If the session supports ITransactionLocal, the consumer can call ITransactionLocal::StartTransaction to start an explicit transaction. The session is then said to be in manual commit mode and any work done in the session must be explicitly committed or aborted. If ITransactionLocal is not supported or if the consumer does not call StartTransaction, the session is said to be in autocommit mode and any work done in the session is automatically committed; it cannot be aborted. For more information about transactions, see Chapter 12, "Transactions."

To create a session, a consumer calls IDBCreateSession::CreateSession in the data source object. A single data source object can support multiple sessions and, therefore, multiple transactions. From a session, the consumer can do the following:

  • Create a command by calling IDBCreateCommand::CreateCommand. A single session can support multiple commands. Some providers do not support commands.

  • Create a rowset by calling IOpenRowset::OpenRowset. This is equivalent to creating a rowset over a single table and is supported by all providers.

  • Create or modify tables and indexes with ITableDefinition and IIndexDefinition. Although providers are not required to support these interfaces, any provider built over a data source that supports creating and modifying tables and indexes should support them. For simple providers, such as a provider built over an array of data, ITableDefinition might be the only way to create or modify tables.


Commands

Overview of Commands

In OLE DB, Data Definition Language (DDL) and Data Manipulation Language (DML) statements are referred to as text commands. A command object contains a text command and its main purpose is to execute the command. Commands expose various interfaces representing different areas of functionality of a query processor, including query formulation, preparation, and execution. Text commands are expressed in a provider-specific language—usually ANSI SQL92. Commands can be in one of four states: Initial, Unprepared, Prepared, or Executed. Parameters can be used with commands to bind to consumer variables at execution time. When executed, commands return results: either a rowset or a row count, or multiple results. This chapter discusses the use and states of command objects, binding input parameters and determining support of output parameters, and the results set.

For more information on | Go to
Using commands | "Using Commands" in this chapter
Command states | "Command States" in this chapter
Input and Output Parameters | "Using Parameters" in this chapter
Multiple results | "Multiple Results" in this chapter

A command is used to execute a provider-specific text command, such as an SQL statement. It is important not to confuse a command, which is an object, and its command text, which is a string. Commands are usually used for data definition, such as creating a table or granting privileges, and data manipulation, such as updating or deleting rows. A special case of data manipulation is creating a rowset—for example, an SQL SELECT statement.

Providers are not required to support commands. In general, providers built on top of a DBMS, such as an SQL DBMS, support commands, and providers built on top of a simple data structure, such as a file or an array of data in an application, do not support commands.

Command objects:

CoType TCommand {
   [mandatory] interface IAccessor;
   [mandatory] interface IColumnsInfo;
   [mandatory] interface ICommand;
   [mandatory] interface ICommandProperties;
   [mandatory] interface ICommandText;
   [mandatory] interface IConvertType;
   [optional]  interface IColumnsRowset;
   [optional]  interface ICommandPrepare;
   [optional]  interface ICommandWithParameters;
   [optional]  interface ISupportErrorInfo;
}


Using Commands

Before a consumer can use a command, it must determine whether commands are supported. To do this, the consumer calls QueryInterface for IDBCreateCommand on a session. If this interface is exposed, the provider supports commands. To create a command, the consumer then calls IDBCreateCommand::CreateCommand on the session. You can use a single session to create multiple commands.

When the command is first created, it does not contain command text. The consumer sets the command text with ICommandText::SetCommandText. Because the text command syntax is provider specific, the consumer passes the GUID of the syntax to use. This can be one of the following values, or a provider-specific value specifying some provider specific syntax.

Dialect Value | Meaning
DBGUID_DEFAULT | The command text is to be interpreted according to the provider's default grammar. If the provider supports multiple syntax including SQL, it must be able to recognize SQL syntax when DBGUID_DEFAULT is passed.
DBGUID_SQL | The command text is to be interpreted according to a superset of ANSI SQL syntax rules. To determine the level of ANSI SQL syntax supported by a provider, a consumer calls IDBProperties::GetProperties for the DBPROP_SQLSUPPORT property. Providers reporting this syntax are free to use extensions to ANSI SQL, or any subset of ANSI SQL according to their reported level of support, but must comply with the rules defined in ANSI SQL for any supported syntax defined in that specification. Note DBGUID__SQL was added in version 1.5 and is not supported in earlier providers. Consumers should use DBGUID_SQL only when calling a version 1.5 or greater provider that supports DBPROP_SQLSUPPORT. In most cases, unless the provider is trying to distinguish between different dialects, the consumer can just pass DBGUID_DEFAULT. Providers that support multiple syntaxes including SQL must be able to recognize SQL syntax when DBGUID_DEFAULT is passed.

To execute the command, the consumer calls ICommand::Execute. If the command text specifies a rowset, such as an SQL SELECT statement, Execute creates an instance of the rowset and returns an interface pointer to it.

Before executing a command that creates a rowset, the consumer can specify various properties of the rowset—such as the interfaces to expose and degree of scrollability—by calling ICommandProperties::SetProperties. For more information, see "Setting Rowset Properties" in Chapter 4. The consumer can also get information about the columns of the rowset by calling IColumnsInfo::GetColumnInfo, which all providers support, or IColumnsRowset::GetColumnsRowset, which providers may support on the command. The command text must be set to use these methods and, if the command supports preparation, it must be prepared.

If a command is to be executed multiple times with the same text command, or if the consumer needs information about the rowset it will create, the consumer can prepare the command by calling ICommandPrepare::Prepare. This is equivalent to compiling the command. Providers are not required to support command preparation.

Prepared commands are more versatile when used with parameters, which are variables in the text command. For example, if the provider supports SQL, the consumer might prepare and repeatedly execute the parameterized SQL statement UPDATE Employees SET (Salary = ?) WHERE EmployeeID = ? to update the salaries of a number of employees.

Parameter support is optional for commands, prepared or otherwise; providers that support them expose ICommandWithParameters on the command. To use parameters, the consumer first describes the parameters to the provider by calling ICommandWithParameters::SetParameterInfo. Some providers can describe parameters themselves and do not need consumers to call SetParameterInfo. The consumer then creates an accessor that specifies the structure of a buffer and places parameter values in this buffer. Finally, it passes the handle of the accessor and a pointer to the buffer to Execute. On later calls to Execute, the consumer simply places new parameter values in the buffer and calls Execute with the accessor handle and buffer pointer. For more information, see Chapter 6, "Getting and Setting Data."


Command States

Commands can be in one of four states:

  • Initial—No command text is set. A command is in this state after IDBCreateCommand::CreateCommand is called.

  • Unprepared—The command text is set but the command is not prepared. A command is in this state after ICommandText::SetCommandText is called.

  • Prepared—The command text is set and the command is prepared. A command is in this state after ICommandPrepare::Prepare is called.

  • Executed—The command text is executed. A command is in this state after ICommand::Execute is called.

The following table lists each state and shows the code returned by each method in a command interface when it is called in that state. This table uses the following abbreviations:

OK= S_OK
NC= DB_E_NOCOMMAND
NP= DB_E_NOTPREPARED
OO= DB_E_OPENOBJECT

Method | Initial | Unprepared | Prepared | Executed
IColumnsInfo |  |  |  |
GetColumnInfo | NC | OK (ICommandPrepare is not supported) - or - NP (ICommandPrepare is supported) | OK | OK (ICommandPrepare is not supported) - or - NP (ICommandPrepare is supported)
MapColumnIDs | NC | OK (ICommandPrepare is not supported) - or - NP (ICommandPrepare is supported) | OK | OK (ICommandPrepare is not supported) - or - NP (ICommandPrepare is supported)
IColumnsRowset |  |  |  |
GetAvailableColumns | NC | OK (ICommandPrepare is not supported) - or - NP (ICommandPrepare is supported) | OK | OK (ICommandPrepare is not supported) - or - NP (ICommandPrepare is supported)
GetColumnsRowset | NC | OK (ICommandPrepare is not supported) - or - NP (ICommandPrepare is supported) | OK | OK (ICommandPrepare is not supported) - or - NP (ICommandPrepare is supported)
ICommand |  |  |  |
Cancel | OK | OK | OK | OK
Execute | NC | OK | OK | OK
GetDBSession | OK | OK | OK | OK
ICommandPrepare |  |  |  |
Prepare | NC | OK | OK | OK (Execute did not create a rowset) - or - OO (Execute created a rowset, and the rowset is still open)
Unprepare | OK | OK | OK | OK (Execute did not create a rowset) - or - OO (Execute created a rowset, and the rowset is still open)
ICommandProperties |  |  |  |
GetProperties | OK | OK | OK | OK
SetProperties | OK | OK | OK | OK (Execute did not create a rowset) - or - OO (Execute created a rowset, and the rowset is still open)
ICommandText |  |  |  |
GetCommandText | NC | OK | OK | OK
SetCommandText | OK | OK | OK | OK (Execute did not create a rowset) - or - OO (Execute created a rowset, and the rowset is still open)
ICommandWithParameters |  |  |  |
GetParameterInfo | OK (SetParameterInfo has been called) - or - NC (SetParameterInfo has not been called) | OK (SetParameterInfo has been called) - or - NP (ICommandPrepare is supported and SetParameterInfo has not been called) | OK | OK
MapParameterNames | OK (SetParameterInfo has been called) - or - NC (SetParameterInfo has not been called) | OK (SetParameterInfo has been called) - or - NP (ICommandPrepare is supported and SetParameterInfo has not been called) | OK | OK
SetParameterInfo | OK | OK | OK | OK (Execute did not create a rowset) - or - OO (Execute created a rowset, and the rowset is still open)

The following figures illustrate the state transitions for a command. The first shows the command text set to an empty string, the second to a nonempty string.

Command text set to an empty string, then a nonempty string


Multiple Results

When executed, commands return results. A result can be either a rowset or a row count, which is the number of rows affected by a command that updates, deletes, or inserts rows. Commands can return multiple results if the command text comprises multiple, separate text commands—such as a batch of SQL statements—or if more than one set of parameters is passed to a command.

Multiple results:

CoType TMultipleResults {
   [mandatory] interface IMultipleResults;
   [optional] interface ISupportErrorInfo;
}

To retrieve multiple results, consumers use multiple results objects. Providers are not required to support these, however. The DBPROP_MULTIPLERESULTS property in the Data Source Information property group specifies whether a provider supports multiple results objects.

To create a multiple results object, the consumer requests the IMultipleResults interface when it calls ICommand::Execute. Execute returns an interface pointer on the multiple results object to the consumer.

To retrieve each rowset or row count created by the command, the consumer calls IMultipleResults::GetResult. GetResult returns results in the order they were specified in the command text. If the current result is a rowset, some providers require the consumer to release the rowset before GetResult returns the next result; the DBPROP_MULTIPLERESULTS property specifies whether this is required.


Using Parameters

Parameters allow the insertion of placeholders, which are bound to consumer variables at execution time, into a command. Input parameters allow consumers to specify a command once and execute it multiple times, each with a different set of values, and are also useful for inserting nonstring values into a command. Output parameters allow consumers to execute a command, such as a stored procedure, that contains output arguments.

The provider determines the manner in which parameters are specified within a command. SQL providers that report at least a DBPROP_SQLSUPPORT value of DBPROPVAL_SQL_ODBC_MINIMUM or DBPROPVAL_SQL_ANSI92_INTERMEDIATE must support parameters in conjunction with the DBGUID_SQL dialect. SQL providers represent parameter markers within a command as question marks (?). In most cases, parameter markers are legal only in certain places within an SQL statement; they are not allowed to take the place of an identifier, such as a column name in the SELECT list or a table name in the FROM clause, and are not allowed in data definition language (DDL) statements.

Not all providers support the ability to describe the parameters embedded within a command. For providers that do support describing parameters, consumers can obtain parameter information by calling ICommandWithParameters::GetParameterInfo. Consumers that know the number and types of the parameters can specify this information by calling ICommandWithParameters::SetParameterInfo. The provider uses this information to determine how to convert parameter data from the type supplied by the consumer to the native type used by the data source. This is particularly useful for providers that cannot describe the parameters embedded within a command, but it can also lead to more efficient code because the provider does not have to go through the potentially expensive operation of describing the command.

The consumer variables from which input parameter values are taken and to which output parameter values are written are passed at run time to ICommand::Execute in the DBPARAMS structure. Multiple sets of parameter values can be passed in a single call to Execute, which has the effect of executing the statement multiple times, once with each set of parameter values. The consumer describes the layout of parameter information within the buffer passed in the DBPARAMS structure by means of a parameter accessor.

Parameters may be named or unnamed, but are always identified within the parameter accessor according to ordinal, using the iOrdinal member of the DBBINDING structure. In most cases, the ordinal of a parameter within a textual command is its one-based position within the command. If the provider supports named parameters and the consumer calls MapParameterNames prior to calling SetParameterInfo or GetParameterInfo, the provider may assign an alternate set of ordinals to identify the parameters. This alternate set of ordinals must remain consistent through MapParameterNames and GetParameterInfo until SetParameterInfo is called or the command statement is changed.

To bind parameters, the consumer does one of the following:

  • If the consumer knows the ordinals and server types of the parameters to be bound, it calls SetParameterInfo, supplying the ordinals of the parameters and specifying the name, if known, along with the data type of each parameter in the DBPARAMBINDINFO structure. The iOrdinal members of the DBBINDING structure correspond to the ordinal values specified in SetParameterInfo.

  • If the consumer knows the names but not the ordinals of the parameters, it can obtain a list of ordinals corresponding to a set of named parameters by calling ICommandWithParameters::MapParameterNames. The consumer calls SetParameterInfo using these ordinal values with the names; the iOrdinal members of the DBBINDING structure correspond to the ordinal values specified in SetParameterInfo. The ordinals returned by MapParameterNames do not necessarily indicate the position of the parameters in the command, but represent how the provider associates the parameter name to the ordinal specified in the accessor.

  • If the consumer does not know the ordinals of the parameters to be bound and the provider supports describing parameters, the consumer can call GetParameterInfo to determine the parameter name, if applicable, along with the type of each parameter ordinal. The iOrdinal members of the DBBINDING structure correspond to the ordinal values returned in GetParameterInfo.

  • If the consumer does not know the ordinals of the parameters and the provider does not support describing the parameters, the consumer must try to determine the number and order of parameters from the user. It must either obtain parameter information from the user or “guess” at the data types to bind each parameter. If the consumer is not trying to do any conversions between the bound type and the type of data on the server and is not trying to use named parameters, it can simply create an accessor using the ordinals of the parameters and execute the statement without calling any method from ICommandWithParameters.

Output parameters are filled in when the provider has completely finished processing the execution. For some providers, this does not occur until the rowset is released, and the memory specified for the output parameters must remain valid until the rowset is released. To determine when output parameters are populated and memory can be released, the consumer should check the DBPROP_OUTPUTPARAMETERAVAILABILITY data source information property.


Rowsets

Overview of Rowsets

A rowset is simply a set of rows in which each row has columns of data. This chapter explains how to create and release rowsets, as well as how to set their properties, such as supported interfaces. Also presented is a description of how to manage the activity of individual rows or rowsets: fetching, reference-counting handles, and column access and reference. Rowset reference and fetching are discussed in the use of bookmarks of various types, chapters, and views. Bookmarks allow the consumer to return quickly to a row, chapters identify a group of rows within a rowset, and a view can expose simple operations such as sorting or filtering a rowset. Hierarchical rowsets are those linked together by using chapters. The interfaces, navigation, properties, lifetime, and notifications when using hierarchical rowsets are all discussed.

For more information on | Go to
Creating and releasing rowsets | "Creating and Releasing Rowsets" in this chapter
Setting properties of rowsets | "Setting Rowset Properties" in this chapter
Column access | "Deferred Columns and Column IDs" in this chapter
Rows | "Rows" in this chapter
Fetching rows | "Fetching Rows" in this chapter
Bookmarks | "Bookmarks" in this chapter
Chapters | "Chapters" in this chapter
Views | "Views" in this chapter
Hierarchical rowsets | "Hierarchical Rowsets" in this chapter
Navigation | "Navigating Hierarchical Rowsets" in this chapter
Properties of hierarchical rowsets | "Setting Properties on Hierarchical Rowsets" in this chapter
Lifetime of rowsets | "Lifetime of Row Handles and Chapters" in this chapter

Rowsets are the central objects that enable all OLE DB data providers to expose data in tabular form. Conceptually, a rowset is a set of rows in which each row has columns of data. Base table providers present their data in the form of rowsets. Query processors present query results in the form of rowsets. This makes it possible to layer components that consume or produce data through the same object.

Rowsets:

CoType TRowset {
   [mandatory] interface IAccessor;
   [mandatory] interface IColumnsInfo;
   [mandatory] interface IConvertType;
   [mandatory] interface IRowset;
   [mandatory] interface IRowsetInfo;
   [optional]  interface IChapteredRowset;
   [optional]  interface IColumnsRowset;
   [optional]  interface IConnectionPointContainer;
   [optional]  interface IDBAsynchStatus;
   [optional]  interface IRowsetChange;
   [optional]  interface IRowsetFind;
   [optional]  interface IRowsetIdentity;
   [optional]  interface IRowsetIndex;
   [optional]  interface IRowsetLocate;
   [optional]  interface IRowsetRefresh;
   [optional]  interface IRowsetScroll;
   [optional]  interface IRowsetUpdate;
   [optional]  interface IRowsetView;
   [optional]  interface ISupportErrorInfo;
}

The most basic rowset exposes the following interfaces:

IRowset | Contains methods for fetching rows in the rowset sequentially.
IAccessor | Permits the definition of groups of column bindings describing the way tabular data is bound to consumer program variables.
IColumnsInfo | Provides information about the columns of the rowset.
IRowsetInfo | Provides information about the rowset itself.
IConvertType | Provides information about the data type conversions supported by the rowset.

Using IRowset, a consumer can sequentially traverse the rows in the rowset, including traversing backward if the rowset supports it.


Creating and Releasing Rowsets

The consumer typically uses one of two procedures for creating rowsets. The first procedure uses the IOpenRowset interface, which deals with the simpler case of retrieving all data from the table. The second procedure involves creating and executing a command to get a rowset that meets specific criteria. All providers must implement the simpler case. Even if the rowset specified by either method has no rows, the rowset is still created. The resulting rowset is fully functional and can be used, for example, to insert new rows or determine column metadata.

Because rowsets are the fundamental objects for getting data, several methods that return information create rowsets, such as IColumnsRowset::GetColumnsRowset, IDBSchemaRowset::GetRowset, and ISourcesRowset::GetSourcesRowset.


Creating a Rowset with IOpenRowset

Some providers need to expose very simple rowsets to their consumers. These rowsets represent the result set of the simple query SELECT * FROM table-name. To allow providers to interpret this simple query without the overhead of supporting a complete query language, OLE DB provides a simple rowset interface, IOpenRowset. IOpenRowset effectively exposes a rowset in the absence of a command. IOpenRowset is implemented on the session as shown in the following figure.

Getting a rowset directly from a session

Fully Qualified Names

A consumer may need to construct a fully qualified table name incorporating the names of the associated schema and catalog. To construct a fully qualified name, a consumer should follow these steps:

If DBPROP_CATALOGLOCATION is DBPROPVAL_CL_START:

  1. If DBLITERAL_CATALOG_NAME is not NULL (fSupported is TRUE in DBLITERALINFO), get the appropriate catalog name for the table (for instance, from the TABLES schema rowset) and append the value of DBLITERAL_CATALOG_SEPARATOR (pwszLiteralValue in DBLITERALINFO).

  2. If DBLITERAL_SCHEMA_NAME is not NULL (fSupported is TRUE in DBLITERALINFO), get the appropriate schema name for the table (for instance, from the TABLES schema rowset) and append the value of DBLITERAL_SCHEMA_SEPARATOR (pwszLiteralValue in DBLITERALINFO). Append this to the catalog information from the previous step.

  3. Append the object (table) name.

If DBPROP_CATALOGLOCATION is DBPROPVAL_CL_END:

  1. If DBLITERAL_SCHEMA_NAME is not NULL (fSupported is TRUE in DBLITERALINFO), get the appropriate schema name for the table (for instance, from the TABLES schema rowset) and append the value of DBLITERAL_SCHEMA_SEPARATOR (pwszLiteralValue in DBLITERALINFO).

  2. Append the object (table) name.

  3. If DBLITERAL_CATALOG_NAME is not NULL (fSupported is TRUE in DBLITERALINFO), append the value of DBLITERAL_CATALOG_SEPARATOR (pwszLiteralValue in DBLITERALINFO) and the appropriate catalog name for the table (for instance, from the TABLES schema rowset).

Note If a DBLITERAL_QUOTE_* character is supported, the provider must support enclosing names/identifiers in this quote character (with each part of the name individually quoted—for example, "catalog"."schema"."table", not "catalog.schema.table"). If a DBLITERAL_QUOTE_* character is not supported, the consumer should not quote identifiers. In addition, a provider should not quote the information passed to it.


Creating a Rowset with a Command

Providers that implement their own query or data definition language can offer their consumers additional functionality by using a command. This process involves three steps:

  1. The consumer creates a session. For more information, see "Sessions" in Chapter 2.

  2. The consumer calls IDBCreateCommand::CreateCommand on the session to get a command. The command holds a text command. The consumer then fills in the text command, thereby stating what data to return in the rowset. The text command can be expressed in SQL and augmented by properties that request various functional services upon the resulting rowset. For more information about properties, see "Setting Rowset Properties" later in this chapter.

  3. The user calls ICommand::Execute on the command. Execute returns a rowset. See the following figure.

Getting a rowset from a session through a command


Setting Rowset Properties

ICommandProperties specifies to the command the properties that must be supported by the rowsets returned by ICommand::Execute. The most commonly requested properties are the interfaces the rowset must support. In addition to interfaces, the consumer can request properties that modify the behavior of the rowset or interfaces.

All rowsets must support IRowset, IAccessor, IColumnsInfo, IRowsetInfo, and IConvertType. Providers can choose to return rowsets exposing other interfaces if doing so is cheaper and exposing the returned interfaces does not affect consumer code that is not expecting them. For example, the rowset can expose IRowsetLocate even though the consumer does not request it, because this interface does not change the behavior of the rowset. The rowset cannot expose IRowsetUpdate if the consumer does not request it, because the existence of this interface changes the behavior of the IRowsetChange interface. In most cases, the riid parameter of Execute is the IID for one of the interfaces listed in the TRowset CoType.

For a complete description of how consumers use properties, see Chapter 11, "Properties."


Releasing Rowsets

Consumers release rowsets with IRowset::Release. Releasing a rowset releases any row handles and accessors held by the consumer on that rowset.


Rows

Handles for rows are used with accessors or other functions to manipulate the contents of the rows. Row handles are of type HROW, defined as follows:

typedef ULONG HROW;
# define DB_NULL_HROW 0x00 // Always invalid

The consumer can use row handles to manage the activity of individual rows or sets of rows. Any vector of row handles is allowed, and the consumer can rearrange them into different sets as long as the consumer keeps track of which rows are actively held and which have been released. Rowsets may have a row capacity (a limit on the number of rows that they can track), which is implied by the rowset specification.

When a consumer fetches a row, the data for the row appears to be cached in the provider and left there until the row is released. The consumer reaches the columns of the row through the functions IRowset::GetData and IRowsetChange::SetData, which use accessors to define how the data is to be transferred. For more information, see "Accessors" in Chapter 6.

Having a local copy of the row allows multiple consumers to get data from the row without making additional trips to the data source—which might reside on a separate server—and to coordinate changes to the row without transmitting them to the data source until all consumers are ready. Whether the row actually exists in the rowset is provider specific; for example, the row could be a representation of some object running on the provider or it could be a handle to a row in a lower-level service component.

However, caching rows in the provider means that it’s costly to keep materialized row handles—those defined by creating an instance. Even if a particular provider does not enforce a preset limit on the number of active row handles, it is likely that keeping these row handles even in the order of the hundreds will deteriorate provider performance. Thus, although some providers might be expected to handle rowsets with millions of rows, no provider is expected to materialize many rows simultaneously. Consumers are encouraged to release row handles whenever possible and keep bookmarks to these rows if it is necessary to refetch them. Consumers can keep a small number of frequently accessed row handles in materialized form.


Fetching Rows

Methods such as IRowset::GetNextRows, IRowsetLocate::GetRowsAt, IRowsetLocate::GetRowsByBookmarks, and IRowsetScroll::GetRowsAtRatio can fetch rows. These methods enable the consumer to get multiple row handles with a single call.

The provider manages its row buffers on behalf of the consumer. When a row is fetched, the provider stores it in a buffer local to the provider. It is probably stored in a native format, although how this is done is provider specific. The consumer does not have direct access to the provider's row buffer, except through reference accessors. Instead, the consumer uses IRowset::GetData to get a copy of the data and IRowsetChange::SetData to make changes to the data. For more information, see Chapter 6, "Getting and Setting Data."

In most cases, rows are processed in parallel. Subject to limitations of the command or to limits upon resources, the consumer can hold and work with as many rows as it needs. There is no concept of a single current row, although there is a next fetch row for IRowset::GetNextRows, which is used for sequential read operations.

If the rowset supports IRowsetLocate, then any pattern of rows can be held. In a sequential rowset (that is, a rowset that does not support IRowsetLocate), a rule may require rows to be released from prior GetNextRows calls before a new GetNextRows call can be made.

Reference-Counting Row Handles

Individual row handles have associated reference counts. Methods that return row handles increment the reference counts of the returned row handles. The provider holds the rows until the consumer releases them with IRowset::ReleaseRows. The consumer must call ReleaseRows for the row once for each time it was fetched or IRowset::AddRefRows was called for it.

Rows are implicitly released only when the consumer calls IRowset::Release. Deleting a row does not release the row handle; whether the deletion is done by calling IRowsetChange::DeleteRows, calling IRowsetUpdate::Update on a row with a pending deletion, or calling IRowsetUpdate::Undo on a row with a pending insert, the consumer must call ReleaseRows to release the row.

Next Fetch Position

The OLE DB provider keeps track of the next fetch position so that a sequence of calls to GetNextRows (with no skips, changes of direction, or intervening calls to FindNextRow, Seek, or RestartPosition) reads the entire rowset without skipping or repeating any row. The next fetch position is changed either by calling IRowset::GetNextRows, IRowset::RestartPosition, or IRowsetIndex::Seek, or by calling FindNextRow with a null pBookmark value. Calling FindNextRow with a non-null pBookmark value has no effect on the next fetch position.

If the rowset supports reversible fetch direction, and the fetch direction is reversed from the previous call to GetNextRows or FindNextRow with a null pBookmark value, then the next fetch position for GetNextRows or FindNextRow in the new direction is the last row that was fetched in the previous direction; otherwise, the next fetch position is the row following the last row fetched by either GetNextRows or FindNextRow with a null pBookmark value.

Fetch Direction

Rowsets can take a negative count of requested rows. This means that the absolute count of rows beginning with the first specified row and then moving backward will be returned. For example, the next fetch row in GetNextRows or the bookmarked row in IRowsetLocate::GetRowsAt is the specified row and those preceding it up to the absolute count will be returned. This behavior is independent of skip (offset) counts, which also can be negative.


Uniqueness of Rows in the Rowset

If a row is fetched more than once without any intervening calls to release it, whether the rowset contains one or multiple copies of the row is determined by the DBPROP_LITERALIDENTITY property.

Providers that can reasonably detect whether a newly fetched row is already represented by a currently held row handle are strongly encouraged to simply return the existing row handle with an incremented reference count. For such providers, the DBPROP_LITERALIDENTITY property is set to VARIANT_TRUE.

If DBPROP_LITERALIDENTITY is set to VARIANT_FALSE, multiple different concurrently held row handles may represent the same row in the data source. To the rowset, these generally appear as separate rows. Thus, changes made through one row handle are not reflected when reading the row through a second row handle, and calling RefreshVisibleData, Undo, or Update only affects changes made through the specified row handle.

The consumer can call IRowsetIdentity::IsSameRow to determine whether two row handles represent the same row. Such comparisons are necessary, for example, when the consumer receives a row handle through one of the methods in IRowsetNotify and needs to know if it is the handle of a row that the consumer already holds. Whether the handles of newly inserted rows—that is, rows inserted during the lifetime of the rowset—can be compared at all depends on the value of the DBPROP_STRONGIDENTITY property.


Deferred Columns

For a deferred column, the provider is not required to retrieve data from the data source until IRowset::GetData is called for that column, or, if the column contains an OLE object, until a method used to access that object is called. It is provider specific when the data in a deferred column is actually retrieved. For example, it might be retrieved when the command is executed, when the row handle is fetched, lazily in the background, or when GetData or a method on another interface is called for the column. If a column is deferred, the DBCOLUMNFLAGS_MAYDEFER enumerated type flag returned by IColumnsInfo::GetColumnsInfo is set for the column.

Whether the data for a deferred column is cached depends on the setting of the DBCOLUMNFLAGS_CACHEDEFERRED flag returned by GetColumnsInfo. If this flag is set, the column value is cached when first read. The cached value is returned whenever the column is read. It can be changed by calling IRowsetChange::SetData or IRowsetRefresh::RefreshVisibleData and is released only when the row handle is released. If the DBCOLUMNFLAGS_CACHEDEFERRED flag is not set, then multiple calls to GetData (or the method used to read the column, if the column contains OLE objects) can return different values. Such calls are not guaranteed to reflect a change to the underlying column unless RefreshVisibleData has been called.

To use a deferred column, the consumer sets the DBPROP_DEFERRED property; columns that contain OLE objects are deferred by default.

The consumer should be aware of an increased isolation risk of using deferred columns because deferred data might be read at a different time than nondeferred data. If the consumer is not using Repeatable Read isolation or better, then it may encounter discrepancies between the original and the deferred access to a row. In particular, the consumer may find that the column is missing because the row has been deleted, or the column may not match the rest of the row. Under some circumstances, use of a row version column in conjunction with both original and deferred access can be used to warn a consumer about a situation where a concurrent error has occurred; a consumer can determine whether a column contains a row version with the DBCOLUMNFLAGS_ISROWVER flag in GetColumnInfo.


Column IDs

Consumers use column IDs to identify columns, primarily in commands where there are no stable ordinals. Because column IDs are relatively slow to use, consumers use column ordinals to identify columns in bindings. A consumer calls IColumnsInfo::MapColumnIDs to retrieve a column ordinal from a column ID:

typedef struct  tagDBID {
 union {
  GUID guid;
  GUID * pguid;
 } uGuid;
 DBKIND eKind;
 union {
  LPOLESTR pwszName;
  ULONG  ulPropid;
 } uName;
} DBID;

The values of DBKIND are described in the following table.

Value | Description
DBKIND_GUID_NAME | Use the guid and pwszName elements.
DBKIND_GUID_PROPID | Use the guid and ulPropid elements.
DBKIND_NAME | Use only the pwszName element; ignore the uGuid element.
DBKIND_PGUID_NAME | Use the pGuid and pwszName elements.
DBKIND_PGUID_PROPID | Use the pGuid and ulPropid elements.
DBKIND_PROPID | Use only the ulPropid element; ignore the uGuid element.
DBKIND_GUID | Use only the guid element; ignore the uName element.

The members of a DBID are represented in schema rowset columns as follows.

DBID eKind | Schema columns
DBKIND_GUID_NAME | _GUID and _NAME column values must be provided.
DBKIND_GUID_PROPID | _GUID and _PROPID column values must be provided.
DBKIND_NAME | _NAME column value must be provided.
DBKIND_PGUID_NAME | _GUID and _NAME column values must be provided.
DBKIND_PGUID_PROPID | _GUID and _PROPID column values must be provided.
DBKIND_PROPID | _PROPID column value must be provided.
DBKIND_GUID | _GUID column value must be provided.

OLE DB providers never return pGuid-style bindings (for example, when the consumer inquires about the bindings for an accessor). They are provided as a convenient shortcut for consumers supplying bindings all covered by the same GUID, which is expected to be common.

The value DB_NULLID is defined as the DBID structure containing all zeroes. Because pwszName is a null pointer for DB_NULLID, you should not attempt to free this string.

When constructing DBIDs to identify tables, indexes, or columns, consumers use the non-null columns in the appropriate schema rowset to determine the DBKIND value of the DBID. For example, when specifying a table ID, a consumer would call IDBSchemaRowset::GetRowset and examine the TABLES rowset. On discovering that TABLE_NAME has a value and TABLE_GUID is NULL, the consumer specifies DBKIND_NAME for the table ID. In methods that take column DBIDs as parameters, the validation of the DBID should include a check on the validity of the DBID's eKind.


Bookmarks

Bookmarks are placeholders designed to allow the consumer to return quickly to a row. Consumers that use bookmarks to identify rows to a provider should treat bookmarks as opaque binary values. Consumers should not try to interpret these values. Bookmarks are valid only during the lifetime of the rowset to which they refer. For indexes that use bookmarks to reference rows in a base table, the bookmark returned as the pointer column in the index rowset is a valid bookmark in the rowset built over the base table; therefore, the bookmark is valid for the table rowset. Bookmarks are returned in columns in the rowset. An application retrieves them in the same manner as it retrieves data from any other column in the rowset. That is, it creates an accessor that binds to the bookmark column and calls IRowset::GetData using this accessor.

Bookmark columns have DBCOLUMNFLAGS_ISBOOKMARK set in their column information. A bookmark may be of a fixed scalar type such as DBTYPE_I4 or it may be a character sequence that appears, for example, as a DBTYPE_STR column. There is no specialized bookmark data type; providers can return any valid type indicator in IColumnsInfo::GetColumnInfo for a bookmark column.

Bookmarks can always be read and passed as binary values. When using a bookmark to identify a row to a provider, consumers can always bind bookmark values as DBTYPE_BYTES. Alternatively, if the consumer knows the actual DBTYPE of the bookmark, it can bind to that specific type. For more information about data types, see Appendix A, "Data Types."

Bookmarks are not the same thing as primary keys or row IDs (see "Bookmark Types," later in this chapter). They can be related, and it is possible to use a primary key or row ID to implement a bookmark, but often there are bookmark schemes that are more compact or swifter that work only for the current contents of the rowset. It is important to take advantage of those optimizations in places such as command optimization or result set navigation. Furthermore, bookmarks are not necessarily the same as row handles because they may have ordering semantics or might be used to track nonresident members of very large rowsets.

A rowset implements only one kind of bookmark. The provider may have incompatible solutions for implementing primary key or row ID semantics on bookmarks, so it cannot support both simultaneously.

Bookmarks operate as logical pointers. The encapsulation allows transient, rowset-level bookmarks to be implemented as pointers, although this would likely be used only for rowsets small enough to be efficiently instantiated.

If IRowsetLocate or one of its direct descendants is present on a rowset, then column 0 is the bookmark for the rows. Column 0 is present in a rowset if, and only if, the rowset has the property DBPROP_BOOKMARKS set to VARIANT_TRUE. Reading this column will obtain a bookmark value that can be used to position itself again to the same row. This is called the self bookmark. Other bookmark columns referring to the same rowset, such as a Spouse column in a query about people, are not self bookmarks.

Bookmark columns can be returned on any rowset, regardless of its source (for example, ICommand::Execute, IOpenRowset::OpenRowset, IColumnsRowset::GetColumnsRowset, or IDBSchemaRowset::GetRowset) or whether bookmarks were requested.

The DBIDs of the bookmark columns in a rowset are based on a property set identified by the GUID DBCOL_SPECIALCOL. These DBIDs are constructed as follows:

  • The eKind element is DBKIND_GUID_PROPID or DBKIND_PGUID_PROPID.

  • The guid (or pguid) element is, or points to, DBCOL_SPECIALCOL.

  • The ulPropid element is 2 for the self bookmark. It is greater than 2 for bookmarks other than the self bookmark. The ulPropid element must not be the same as the ulPropid element in the DBID of another bookmark in the same rowset.

  • If bookmark column A appears before bookmark column B in the specification of the rowset, the ulPropid element in the DBID of bookmark column A is less than the ulPropid element in the DBID of bookmark column B.

The DBIDs of bookmark columns can be passed to IColumnsInfo::MapColumnIDs to determine the ordinals of those columns. There is no need to pass the DBID of the self bookmark column to MapColumnIDs because the ordinal of this column is always zero.


Bookmark Types

OLE DB supports two kinds of bookmarks—numeric and key value. The DBPROP_BOOKMARKTYPE property indicates the bookmarks that a provider supports.

Numeric Bookmarks

Numeric bookmarks are based on a position or index into the rows selected for a rowset. In most cases, the rowset builds a set of information about the rows, which enables the same row to be retrieved easily. It does not guarantee the existence or consistency of those rows throughout the transaction; that still depends on the transaction isolation level. It returns the same row entity if it can.

The numeric value can be of any size. A small rowset might use a 16-bit index, whereas a rowset against an Oracle table might use Oracle ROWIDs, which are around 6 bytes each. There is no significance to the ordering of numeric values, either logical or physical. The implementation should simply be fast.

If the primary key value of a row is changed while working with a row for which the consumer has a numeric bookmark, the bookmark still points to the same row instance. This is required for a numeric bookmark. Suppose the row also had a nonoverlapping candidate key. The primary key may be entirely changed, but the same value would remain in the candidate key column.

Key Value Bookmarks

When a rowset is ordered and there is some combination of columns (not necessarily the primary keys of the base tables) that form a unique key for each row, it becomes useful to use these values as a bookmark. At first it may seem redundant, because one could expect an interface such as IRowsetIndex to take key values and rapidly return a numeric bookmark. However, the numeric bookmark itself may be costly and unwanted. In some cases the key values enable the provider to access the underlying tables without storing any kind of temporary table.

With a key value bookmark, a consumer could, in theory, synthesize a set of values that could occur but do not. The result of doing this is undefined; that is, providers are not required to detect forgeries.

If the key value columns of a row are changed while working with a row for which the consumer has a key value bookmark, the bookmark can become disassociated, or pointing to a new row that usurps the old values.


Comparing Bookmarks

The optimal way to compare bookmarks is by a direct memory comparison. However, some rowsets may include hints within the bookmark that depend on time or rowset generation. This might cause binary comparisons between two bookmarks for the same row to fail. If the provider does put hints in the bookmarks, they must expose IRowsetLocate, which has methods to compare two bookmarks and to return a stable hash value for a bookmark. If the provider does put hints in the bookmarks, it must be able to support IRowsetLocate, if requested, in order to compare two bookmarks and return a stable hash value for a bookmark.

If the DBPROP_LITERALBOOKMARKS property is VARIANT_TRUE, then consumers can do the comparison themselves. The method used depends on the scalar type of the bookmark. For example, if the bookmark was defined as a sequence of DBTYPE_BYTES, then the comparison is of unsigned bytes, with the most-significant byte in position 0.

Bookmarks can be compared only if they belong to the same rowset and are used within the lifetime of that rowset. The results of comparing bookmarks from different rowsets, or outside the lifetime of the rowset to which they belong, are undefined.

A consumer does not need an ordered query to have ordered bookmarks. In particular, it does not need an SQL ORDER BY clause. If a consumer requests SELECT * FROM <table-name> and the smart query processor delivers the scan in the least-cost physical seek order (meaningless ordering in terms of content), then the consumer may still be given a rowset with ordered bookmarks. To guarantee ordered bookmarks, the consumer specifies the DBPROP_ORDEREDBOOKMARKS property as VARIANT_TRUE in ICommandProperties::SetProperties. For some providers, bookmarks are always ordered.

If DBPROP_ORDEREDBOOKMARKS is VARIANT_TRUE, the order of the bookmarks reflects the order of delivery. That is, if the consumer has bookmark i < j, then it calls IRowsetLocate::GetRowsAt for bookmark i and keeps scanning. It is guaranteed to find j unless it is deleted; and if it is deleted, the consumer can see the gap in order as it passes it by. Conversely, if the consumer starts at j and scans forward, it never sees i. It just reaches the end of the rowset.


Standard Bookmarks

Three standard bookmark values are provided: DBBMK_INVALID, DBBMK_FIRST, and DBBMK_LAST. All single-byte (length 1) bookmark values of type DBTYPE_BYTES are reserved for standard bookmarks. Ordinary bookmarks of type DBTYPE_BYTES must be at least length 2. The following constant is defined in OLEDB.H as the standard bookmark length:

#define STD_BOOKMARKLENGTH 1

The values of the following standard bookmarks are guaranteed to be the same for all providers.

Value | Description
DBBMK_INVALID | DBBMK_INVALID has no special meaning in this version of OLE DB. It can be used by applications to initialize and subsequently check bookmark validity, and may be used in future releases to indicate an unused parameter in a method that may take bookmark values.
DBBMK_FIRST The first row of the rowset.

A consumer can fetch forward from this point. For example, if cRows is greater than zero and lRowsOffset is zero, IRowsetLocate::GetRowsAt returns the first cRows rows of the rowset.

A consumer cannot fetch backward before this point. For example, if cRows is less than zero and lRowsOffset is zero, GetRowsAt returns this row; if cRows is less than –1, it then returns DB_S_ENDOFROWSET.
DBBMK_LAST The last row of the rowset. If the rowset is being populated asynchronously, this is the last row identified so far.

A consumer can fetch backward from this point. For example, if cRows is less than zero and lRowsOffset is zero, GetRowsAt returns the last cRows rows of the rowset.

A consumer cannot fetch forward past this point. For example, if cRows is greater than zero and lRowsOffset is zero, GetRowsAt returns this row; if cRows is greater than one, it then returns DB_S_ENDOFROWSET.

The consumer can begin a sequential scan at either the first or last row.


Invalid Bookmarks

A bookmark can become invalid for any of the following reasons:

  • The row it points to is deleted.

  • It is based on a primary key and some of those key values were changed.

  • The row it points to is no longer in the rowset. This occurs only in rowsets that support DBPROP_OWNUPDATEDELETE when a row is changed in such a way that it no longer meets the search criteria of the rowset, is released, and then is refetched by IRowset::GetNextRows. For example, if the rowset contains only those rows with a job title of Assistant Manager, changing the job title in a row to Manager would remove that row from the rowset. From the point of view of that rowset, the row was deleted.

Bookmarks that have been returned by the provider are guaranteed to have predictable behavior, even if they have become invalid. If the DBPROP_BOOKMARKSKIPPED property is VARIANT_TRUE, the provider skips the bookmarked row and proceeds to the next row. If the DBPROP_BOOKMARKSKIPPED property is not VARIANT_TRUE, the provider will usually return an error. Never must the provider terminate abnormally for a bookmark that it has returned to the user.

Note, however, that passing a random bookmark value that has not been obtained from the provider is a programming error. The effect of specifying an unknown, random bookmark value is undefined and may include abnormal termination.


Chapters

Chapters are used to identify a group of rows within a rowset. The group of rows may have a common characteristic, such as satisfying a filter condition or having a common parent in a hierarchical rowset, and may have an imposed order. Chapter handles are defined as follows:

typedef ULONG HCHAPTER;
# define DB_NULL_HCHAPTER  0x00  // Always invalid

Chapters are assumed to have significant overhead, so they must be managed. This is done automatically according to the following rules:

  • The rules for sequential rowsets take precedence, so a grouped sequential rowset's chapters can be opened only in the proper sequence and cannot be revisited after they have been scanned. It may be necessary to close a prior chapter before the next can be opened.

  • A chapter is opened upon use, if it is not already in an active state.

When the reference count on a chapter is released to zero, then the resources needed to manage the chapter can be discarded or marked as discardable. Note, however, that if the reference count on a chapter falls to zero and the chapter contains rows with pending changes, the chapter's resources might not be freed until the pending change status on the rows is cleared, either through a call to IRowsetUpdate::Update or IRowsetUpdate::Undo.


Views

Providers that do not support full command syntax can still expose simple operations such as sorting or filtering a rowset by applying a view. Views may be applied when opening a rowset, or they may be applied to an existing rowset in order to return either a new chapter on that rowset or a completely new rowset with the additional functionality.

Views are similar to commands, with the following restrictions:

  • The source of the rowset is a single table, fixed at the time the view is created.

  • Views support only simple operations such as filtering and sorting. They do not support complex semantics such as subqueries, joins, or dynamic parameters.

  • View operations are expressed through method calls as opposed to textual statements.

Views:

   CoType TView {
      [mandatory] interface IColumnsInfo;
      [optional]  interface IAccessor;
      [optional]  interface ISupportErrorInfo;
      [optional]  interface IViewChapter;
      [optional]  interface IViewFilter;
      [optional]  interface IViewRowset;
      [optional]  interface IViewSort;
   };

Views are created in one of two ways:

  • Applying a view when opening a rowset. In order to apply a view when opening a rowset, the following steps occur:
    1. The consumer requests that IID_IViewRowset be supported when executing a method that returns a rowset, either by setting the DBPROP_IViewRowset property, or by requesting IID_IViewRowset on the returned rowset. The method returns a view object in place of the rowset.

    2. The consumer applies operations such as filtering and sorting to the view object through interfaces such as IViewFilter and IViewSort.

    3. The consumer calls IViewRowset::OpenViewRowset on the view object in order to return the actual rowset.
  • Applying a view to an existing rowset. In order to apply a view to an existing rowset, the following steps occur:
    1. The consumer calls IRowsetView::CreateView on the rowset.

    2. The consumer applies operations such as filtering and sorting to the view object through interfaces such as IViewFilter and IViewSort.

    3. The consumer calls IViewChapter::OpenViewChapter on the view to return a new chapter on the original rowset that reflects the view operations.
      - or -
      The consumer calls IViewRowset::OpenViewRowset on the view in order to return a new rowset with the view operations applied.


Hierarchical Rowsets

A rowset hierarchy is a collection of rowsets linked together via chapters. Hierarchies model 1:N relationships between tables, such as a master-detail relationship between Customers, Orders, and Items. A rowset hierarchy can be stored persistently in a data source or computed by a command. In this chapter we discuss only computed hierarchies.

Chapters are identifiers of groups of details within a rowset. These chapters work as distinct collections with a beginning and an end, but also share the facilities, such as accessors and notifications, of the rowset to which they belong.

The following example illustrates how a computed hierarchy is generated. The database schema contains the following three tables.

CREATE TABLE customer_table
       ( cust_no        INTEGER,
         cust_name      VARCHAR(20),
         city           VARCHAR(20),
         PRIMARY KEY (cust_no)        )

CREATE TABLE order_table
       ( cust_no        INTEGER,
         ord_no         INTEGER,
         ord_date       DATE,
         total          DECIMAL(9,2),
         PRIMARY KEY (ord_no),
         FOREIGN KEY (cust_no) REFERENCES customer_table )

CREATE TABLE order_item_table
       ( ord_no         INTEGER,
         item_no        INTEGER,
         description    VARCHAR(30),
         quantity       SMALLINT,
         unit_price     DECIMAL(6,2),
         PRIMARY KEY (ord_no, item_no),
         FOREIGN KEY (ord_no, REFERENCES order_table )

The following extended SQL command generates a rowset hierarchy. The syntax of this query is used only for illustration purposes. Providers may use other syntaxes to generate hierarchy.

SELECT (cust_no,
        cust_name,
        city,
        SELECT (ord_no,
                ord_date,
                total,
                SELECT (item_no,
                        description,
                        quantity,
                        unit_price,
                        (quantity * unit_price) AS ext_price
                       )
                  FROM order_item_table
                  WHERE order_item_table.ord_no = order_table.ord_no
                  AS item_rows
                )
          FROM order_table
          WHERE order_table.cust_no = customer_table.cust_no
          AS order_rows
        )
  FROM customer_table


One Rowset for Each Hierarchy Level

The result of the above command contains three rowsets. Each rowset represents a complete hierarchy level in the result. The root rowset, which is the rowset returned from ICommand::Execute, which we call the Customer rowset, contains columns cust_no, cust_name, city, and a chaptered column order_rows. The second rowset, which we call the Orders rowset, contains columns ord_no, ord_date, total, and a chaptered column item_rows. Finally, the third rowset, which we call the Items rowset, contains columns item_no, description, quantity, unit_price, and ext_price.

A single rowset may apply to more than one level of a recursive set of objects, but the specification also uses recursion so that the rowset is defined only once in the specification, and all bookmarks or chapters refer to that. Hence there is only one rowset at execution time.


Navigating Hierarchical Rowsets

When the consumer executes a hierarchical query, the method returns the requested interface on the root rowset. This root rowset contains one or more chapter columns.

Before using a chapter, the consumer must obtain a pointer to the child rowset by passing the ordinal of the chapter-valued column to either IParentRowset::GetChildRowset or IRowsetInfo::GetReferencedRowset. Consumers generally use GetChildRowset to obtain an aggregated instance of the child rowset, and GetReferencedRowset to obtain a reference on a previously retrieved rowset, or to create an instance of a nonaggregated rowset if the child rowset does not already exist.

Calling GetReferencedRowset for a child rowset that has already been obtained takes an additional reference count on that rowset. Calling GetReferencedRowset for a child rowset that has not previously been obtained returns the requested interface on a nonaggregated instance of the rowset.

GetChildRowset can be called only once for each child rowset and, if called, must be called before any calls to GetReferencedRowset for that child. The pUnkOuter argument specified in GetChildRowset defines the controlling unknown for all references to that child rowset for the lifetime of the parent. The parent rowset must use addref on this controlling unknown to guarantee that it doesn’t go away before the parent. Even though the controlling unknown is constant for the lifetime of the parent, the consumer must not assume that rowset state, such as accessors, row position, and so on, are held when all references to the rowset are released. Furthermore, providers must ensure that the next fetch position within the child rowset is reset once all references to that child rowset have been released. Calling GetChildRowset for a child rowset that has already been obtained returns DB_E_OBJECTOPEN.

For example, after executing the above command and calling IParentRowset::GetChildRowset with the ordinal of the order_rows column, the consumer can get an additional reference on the Orders rowset by calling pCustomer->GetReferencedRowset on the Customer rowset with the ordinal of the order_rows column.

The consumer navigates rows inside a chapter using methods such as IRowset::GetNextRows, IRowsetLocate::GetRowsAt, and IRowsetLocate::GetRowsByBookmark, passing the chapter handle obtained from a parent rowset as an input argument to the method. For example, suppose the Customer rowset has a row for Jones containing a chapter chJonesOrders. Jones's orders are fetched by calling pOrders->GetNextRows and passing chJonesOrders as an input argument to the method.


Chapter Columns

The consumer obtains chapter handles by calling IRowset::GetData with an appropriate accessor. For instance, the rowset Customer has a chapter-valued column order_rows containing chapter handles referring to rows in the Orders rowset. The order_rows column in Customer will have DBCOLUMNFLAGS_ISCHAPTER set in its column information. Chapter handles are 4-byte values of type DBTYPE_HCHAPTER. The chapter values obtained from Customer are used to navigate rows in the Orders rowset and the chapter values from Customer work as long as the Orders rowset remains active.


Chapters and Detail Groupings

Each grouping of a detail set, such as the orders for a particular customer, is treated as a subset with a particular beginning and end. A chapter identifies this detail group. A chapter is usually a partial key to the details. Because it is partial, it will lead to a set rather than a single row. The functions that take a chapter as a parameter, such as GetRowsAt, use the beginning, membership, and end specified by the chapter to limit the rows that the request may return.

Chapters are opened as they are used, with usage tracked by reference counts. This is done transparently, and allows the provider to minimize overheads dedicated to chapters. A reference count is taken on the chapter handle each time it is retrieved through GetRowsAt on the parent rowset, or by calling IChapteredRowset::ReleaseChapter. The reference counts must be explicitly released using IChapteredRowset::ReleaseChapter on the child rowset (that is, the rowset on which the chapter handle is used).

Although a chapter value may be held in some column of a table, that value is not the same thing as the chapter machinery (instance). It is more like a dormant reference to the instance. The machinery may be constructed when a command is executed or on demand when the chapter value is used. In any case, the machinery belongs not to the rowset that had the value column, but to the rowset within which the content is defined. For example, the chapter handles for the Customer-Orders portion of the hierarchy above are in the order_rows column in Customer, but the members of the chapter are in the Orders rowset. Hence, the release of a chapter is an action upon the rowset owning the content (in the example above, the consumer would use pOrders->ReleaseChapter(…)).

When chapters and bookmarks are used together, the chapter does not qualify the bookmark. But methods that take bookmarks also take a chapter, and if several bookmarks (IRowsetLocate::GetRowsByBookmark) are used they must all be in the same chapter. This is a consequence of managing the overheads of chapters. Because multiple chapters may contain a reference to the same row, the consumer needs to choose which chapter to use. The chapter acts to restrict the domain of rows, which can be retrieved; bookmarks are unique within the chapter from which they are returned.


Lifetime of Row Handles and Chapters

Lifetime of row handles and their containing chapters are handled independently. Releasing a row handle containing a chapter does not imply that the row handles contained in the chapter are released, or that the chapter contained in the row handle being released becomes invalid. To invalidate a chapter, the consumer must explicitly release it. Also, chapters may be used when navigating a child rowset even after their containing row handle in the parent rowset has been released.

Note, however, that some providers may keep an implicit reference on the parent row handle until the chapter handle obtained from that row is released. Thus, if the DBPROP_CANHOLDROWS property is VARIANT_FALSE for the rowset, the consumer will not be able to obtain additional rows from the parent rowset until all chapters are released for the current set of held row handles. In general, consumers wanting to work with any provider should release all references on chapter handles before attempting to retrieve additional rows.

Providers may impose additional restrictions, such as supporting only one active chapter at a time on a rowset. For such providers, the consumer must call ReleaseChapter for any retrieved chapters before calling GetData in order to retrieve (and thus implicitly take a reference on) another chapter handle. Attempting to call GetData on a column containing a chapter handle in this case will return DB_E_CHAPTERNOTRELEASED.


IColumnsInfo and Hierarchical Rowsets

When it is exposed on command objects that return multiple rowsets, such as hierarchical rowsets, IColumnsInfo is associated with the root rowset or the first rowset in the series, respectively. Information about the columns in child rowsets or subsequent rowsets in a series must be obtained through the IColumnsInfo or IColumnsRowset interface exposed by those rowsets.


IColumnsRowset and Hierarchical Rowsets

When it is exposed on command objects that return multiple rowsets, such as hierarchical rowsets, IColumnsRowset is associated with the root rowset or the first rowset in the series, respectively. Information about the columns in child rowsets or subsequent rowsets in a series must be obtained through the IColumnsInfo or IColumnsRowset interface exposed by those rowsets.


Setting Properties on Chaptered Rowsets

With the exception of column properties, properties are set by calling ICommandProperties::SetProperties or ICommandProperties::GetProperties. The command used must return a hierachy of rowsets sets or return properties for the entire set of rowsets.

It is not possible to set column properties on any but the root rowset of the command.


Notifications and Hierarchical Rowsets

A consumer receiving IRowsetNotify notifications may need to know if the row handles affected belong to chapters that the consumer holds. This is achieved through the IRowsetChapterMember interface.


A Rowset Traversal Example

This example illustrates a simple OLE DB program that reads a table through a rowset, printing each row along the way. After an instance of a data source object for an e-mail data provider is created, initialized and its properties are set, the IDBCreateSession interface is requested. This returns a pointer to its IOpenRowset interface. The rowset is opened corresponding to the e-mail file. (First a property set "TablePropSet" is initialized for the properties that specify the e-mail file, then the rowset is opened.) Column type information is retrieved and bindings established. The IAccessor interface is requested from the rowset. An accessor handle is returned after creating the accessor. In the while loop, 100 rows at a time are read into the rowset. GetData copies the rows into the local buffers, performing the type conversions specified in the binding structures associated with the accessor. After rows are printed from the rowset they are released, ending the loop. The accessor handle, rowset, session and data source object are released.

Note Code to check status and errors after each call is omitted for clarity.

#include<oledb.h>
extern GUID   CLSID_MailProvider
IDBInitialize  *pIDBInit;
IDBProperties  *pIDBProps;
OLECHAR    *szColNames;
HRESULT     hr;
HACCESSOR   hAccessor;
IDBCreateSession *pIDBCS;
IOpenRowset  *pIOpenRowset;
IRowset    *pIRowset;
IColumnsInfo   *pIColsInfo;
IAccessor    *pIAccessor;
DBCOLUMNINFO   *rgColInfo;
int     irow;
DBID      TableID;
DBBINDSTATUS  rgStatus[10];
HROW     *rghRows;
ULONG     cCol, cBindings, cRowsObtained;
DBBINDING   rgBindings[10];
DBPROPSET   TablePropSet;
void     *rgData;

void PrintData(void*);
void CreateBindingsFromInfo(struct tagDBCOLUMNINFO*, ULONG*,
          struct tagDBBINDING(*)[10], void**);

int main() {
 // Initialize OLE
 CoInitialize(NULL);

 // Create an instance of a data source object for an e-mail data provider.
 CoCreateInstance(CLSID_MailProvider, 0, CLSCTX_LOCAL_SERVER, IID_IDBInitialize,
        (void**) &pIDBInit);
 // Initialize the data source object for e-mail data provider.
 DBPROP rgProps[4];

 // Initialize the VARIANTs and the options in rgProps.
 for (ULONG i = 0; i <= 3; i++) {
  VariantInit(&rgProps[i].vValue);
  rgProps[i].dwOptions = DBPROPOPTIONS_REQUIRED;
  rgProps[i].vValue.vt = VT_BSTR;
 };

 rgProps[0].dwPropertyID = DBPROP_INIT_LOCATION;
 rgProps[0].vValue.bstrVal =
  SysAllocStringLen(OLESTR("email_server "), wcslen(OLESTR("email_server ")));
 rgProps[1].dwPropertyID = DBPROP_INIT_DATASOURCE;
 rgProps[1].vValue.bstrVal =
  SysAllocStringLen(OLESTR("c:\\mail\\smith.mmf"),
        wcslen(OLESTR("c:\\mail\\smith.mmf")));
 rgProps[2].dwPropertyID = DBPROP_AUTH_PASSWORD;
 rgProps[2].vValue.bstrVal =
  SysAllocStringLen(OLESTR("password"), wcslen(OLESTR("password")));
 rgProps[3].dwPropertyID = DBPROP_AUTH_USERID;
 rgProps[3].vValue.bstrVal =
  SysAllocStringLen(OLESTR("Smith"), wcslen(OLESTR("Smith")));

 // Create the initialization structure.
 DBPROPSET PropSet;
 PropSet.rgProperties  = rgProps;
 PropSet.cProperties   = 4;
 PropSet.guidPropertySet = DBPROPSET_DBINIT;

 // Set the initialization properties.
 pIDBInit->QueryInterface(IID_IDBProperties, (void**) &pIDBProps);
 pIDBProps->SetProperties(1, &PropSet);
 pIDBProps->Release();

 // Initialize the data source object.
 hr = pIDBInit->Initialize();

 // Request the IDBCreateSession interface
 pIDBInit->QueryInterface(IID_IDBCreateSession, (void**) &pIDBCS);

 // Create a session returning a pointer to its
 // IOpenRowset interface
 pIDBCS->CreateSession(NULL, IID_IOpenRowset, (IUnknown**) &pIOpenRowset);

 // Open a rowset corresponding to the e-mail file. First initialize a property set
 // "TablePropSet" for the properties that specify the e-mail file, then open the
 // rowset.
 pIOpenRowset->OpenRowset(NULL,&TableID,NULL, IID_IRowset, 1, &TablePropSet,
          (IUnknown**) &pIRowset );

 // Get information about column types.
 pIRowset->QueryInterface(IID_IColumnsInfo, (void**) &pIColsInfo);
 pIColsInfo->GetColumnInfo(&cCol, &rgColInfo, &szColNames);
 pIColsInfo->Release();

 // Establish bindings using a convenience function.
 CreateBindingsFromInfo(rgColInfo,&cBindings, &rgBindings, &rgData);

 // Request the IAccessor interface from rowset.
 pIRowset->QueryInterface(IID_IAccessor, (void**) &pIAccessor);

 // Create an accessor, return an accessor handle.
 pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, cBindings, rgBindings, 0, &hAccessor,
          rgStatus);

 // Read the rows; 100 rows at a time into the rowset.
 while(SUCCEEDED(hr = pIRowset->GetNextRows(NULL, 0, 100, &cRowsObtained, &rghRows))
   && cRowsObtained) {
  for(irow = 0; irow < cRowsObtained; irow++) {
   // GetData copies the rows into the local buffers, performing the type
   // conversions specified in the binding structures associated with the
   // accessor.
   pIRowset->GetData(rghRows[irow], hAccessor, (void*) rgData);

   // Convenience function to print the data.
   PrintData(rgData);
  }
  // Release the rows just printed from the rowset.
  pIRowset->ReleaseRows(cRowsObtained, rghRows, NULL, NULL, NULL);
 }

 // Release the accessor handle and the rowset.
 pIAccessor->ReleaseAccessor(hAccessor);
 pIAccessor->Release();
 pIRowset->Release();

 // Release the session and data source object.
 pIOpenRowset->Release();
 pIDBCS->Release();
 pIDBInit->Release();

 // Uninitialize OLE.
 CoUninitialize();
 return;
}


Updating Data in Rowsets

Overview of Updating Data in Rowsets

Changing data, also known as updating data, consists of updating and deleting existing rows of data and inserting new rows of data. Consumers can change data either through commands, such as the UPDATE, DELETE, and INSERT commands in SQL, or through rowsets. This chapter discusses how consumers change data through rowsets by calling certain methods in IRowsetChange that alter row data. Two update modes are presented: immediate and delayed. The type of mode affects the state of the row. The visibility of changes is discussed, both as it relates to the state of the row and other changes affecting the row. Inserting rows in the desired position, resynchronizing rows, and refreshing row values are also discussed.

For more information on | Go to
Consumers | “Consumers and Providers” in Chapter 1, “Overview of OLE DB”
Commands | Chapter 3, “Commands”
Rowsets | Chapter 4, “Rowsets”
Changing data by calling SetData, DeleteRows, and InsertRow in IRowsetChange | “Changing Data” in this chapter
Immediate vs. delayed update mode | “Immediate vs. Delayed Update Mode” in this chapter
The state of the row | “Row States” in this chapter
Visibility of changes | “Visibility of Changes” in this chapter
Other changes affecting the row | “Visibility of Other Changes” in this chapter
Inserting rows in the desired position | “Position of Inserted Rows” in this chapter
Resynchronizing rows | “Resynchronizing Rows” in this chapter
Refreshing row values | "Refreshing Row Values” in this chapter


Changing Data

Consumers change data by calling the methods SetData, DeleteRows, and InsertRow in IRowsetChange. These methods change the data for a row in the rowset's copy of the row. Whether the method immediately transmits these changes to the data source depends on whether the rowset exposes IRowsetUpdate.

If the rowset does not expose IRowsetUpdate, it is said to be in immediate update mode. In immediate update mode, the methods in IRowsetChange transmit changes to the data source, where they become visible to other transactions running at the Read Uncommitted transaction isolation level. Such changes are transmitted changes.

If the rowset exposes IRowsetUpdate, it is in delayed update mode. In delayed update mode, the methods in IRowsetChange do not transmit changes to the data source. Instead, they buffer the changes in the rowset; such changes are pending changes. While changes are pending, they are wholly local to the rowset and therefore not visible to other transactions, even those running at the Read Uncommitted isolation level.

To transmit pending changes to the data source, the consumer calls IRowsetUpdate::Update. As in immediate update mode, such changes then become visible to other transactions running at the Read Uncommitted transaction isolation level and are transmitted changes.

If the rowset was created in the context of a transaction, the consumer can commit or abort transmitted changes by calling ITransaction::Commit or ITransaction::Abort to commit or abort them. Committed changes become visible to other transactions running at the Read Committed transaction isolation level. If the consumer commits or aborts transmitted changes and there are still pending changes, then what happens to those pending changes depends on the DBPROP_COMMITPRESERVE and DBPROP_ABORTPRESERVE properties. For more information, see "Rowset Preservation" in "Commit and Abort Behavior" in Chapter 12.

The following figure shows how the methods in IRowsetChange, IRowsetUpdate, and ITransaction move data.

IRowsetChange, IRowsetUpdate, and ITransaction methods moving data

To get a read-only rowset, the consumer does not request IRowsetChange or IRowsetUpdate as a property of the rowset.


Immediate vs. Delayed Update Mode

Whether the consumer uses immediate or delayed update mode depends on how many consumers share the rowset and how they use the rowset. In most cases, the primary user of immediate update mode is a single consumer that wants to transmit changes immediately to the data source. Consumers use delayed update mode for many reasons, including the following:

  • Shared rowsets—If multiple consumers share a rowset, they often use notifications to coordinate multiple changes. By using delayed update mode, consumers can coordinate their changes locally in the rowset before transmitting them to the data source.

  • Multiple changes to the same row—If a consumer makes multiple changes to the same row, such as when multiple accessors are used or when users input changes at different times, the row might be left in an invalid state. For example, if a key consists of several columns and each column is changed in a separate call to IRowsetChange::SetData, the intermediate states might be invalid. By using delayed update mode, the consumer can buffer these changes in the rowset before transmitting them to the data source.

  • Network traffic—If a rowset resides on one node in a network and the data source resides on another node, transmitting changes from the rowset to the data source requires a network call. By using delayed update mode, the consumer can bundle together changes to multiple rows and send them across the network with a single call to IRowsetUpdate::Update. This is particularly critical for wide area networks such as the Internet, on which network calls are very expensive.

  • Undoing changes—IRowsetUpdate::Undo enables the consumer to undo pending changes. By using delayed update mode, consumers can expose an undo capability to users without having to implement it.


Row States

A row has a state that depends on whether it has been changed and whether that change is pending or transmitted. Consumers call IRowsetUpdate::GetRowStatus to determine the state of a row. They call IRowsetUpdate::GetPendingRows to get all rows in a pending change state. The state is indicated by the DBPENDINGSTATUS enumerated type. The values in this type have the following meanings.

Value | State name | Description
DBPENDINGSTATUS_ NEW | Pending Insert | The row has a pending insert.
DBPENDINGSTATUS_ CHANGED | Pending Update | The row has a pending update.
DBPENDINGSTATUS_ DELETED | Pending Delete | The row has a pending delete.
DBPENDINGSTATUS_ UNCHANGED | Unchanged | No changes have been made to the row, or changes have been made to the row and have been transmitted or undone.
DBPENDINGSTATUS_ INVALIDROW | Deleted | The row has been deleted and that deletion has been transmitted to the data source. This status is also used when a row handle passed to GetRowStatus was invalid.

The Unchanged and Deleted states are the nonending change states. The Pending Update, Pending Delete, and Pending Insert states are the pending change states. The following figure shows these states, how methods affect them, and the corresponding DBPENDINGSTATUS values.

Pending change states

If the consumer calls IRowset::ReleaseRows for a row and the reference count for that row falls to zero, release of the row and its resources depends on the row's state. In the nonpending change states, the row and its resources are usually released when the reference count falls to zero, although the actual point of release is provider specific.

In the pending change states, the row and its resources are not released when the reference count falls to zero, nor are the pending changes lost. The row remains valid until the changes are transmitted or undone (the row moves to a nonpending change state), then the row and its resources are released. Rows in a pending change state that have a reference count of zero are still considered active for purposes such as calculating row limits such as those returned by the DBPROP_MAXOPENROWS and DBPROP_MAXPENDINGROWS properties. Although the row handle might remain valid for such rows, consumers should not count on this.


Visibility of Changes

The visibility of a change depends on whether the data returned for a row by IRowset::GetData correctly reflects that change. After a consumer changes a row, the change is guaranteed to remain visible as long as the consumer holds the row. This is because the change is made locally in the rowset's copy of the row and any calls to GetData return the changed data. However, if the consumer completely releases and subsequently refetches the row, such as with IRowset::GetNextRows or IRowsetLocate::GetRowsAt, the visibility of any changes made to the row depends on a number of different properties of the rowset.


Visibility of Pending Changes

If the consumer completely releases and then refetches a row with a pending update or delete, the visibility of that pending update or delete depends on whether the rowset exposes IRowsetIdentity. If the rowset exposes IRowsetIdentity, it appears as if there is only a single instance of any given row within the rowset. Thus, the rowset recognizes that the newly fetched row matches the existing row, which contains the pending update or delete, and returns a handle through which the pending update or delete is visible. If the rowset does not expose IRowsetIdentity, the rowset can contain multiple copies of the same row and might return a handle through which the pending update or delete is not visible. For more information, see "Uniqueness of Rows in the Rowset" in Chapter 4.

Whether methods that fetch rows can retrieve pending insert rows depends on the DBPROP_RETURNPENDINGINSERTS property. Consumers should be aware that many providers cannot return pending inserts because they use the row-returning mechanisms of the underlying data source. Because pending inserts have not yet been transmitted to the data source, the mechanisms there cannot return them.


Visibility of Transmitted Changes

If the consumer completely releases and then refetches a row for which changes have been transmitted to the data source, the visibility of those changes depends on the values of the DBPROP_OWNUPDATEDELETE, DBPROP_OWNINSERT, and DBPROP_REMOVEDELETED properties. When multiple consumers share the same rowset, these properties apply to all changes transmitted by any consumer of the rowset. For more information, see "Rowset Properties" in Appendix C.


Position of Inserted Rows

The DBPROP_IMMOBILEROWS property specifies the position of inserted rows, both pending and transmitted, in the rowset. If this property is VARIANT_TRUE, inserted rows always appear at the end of the rowset. If this property is VARIANT_FALSE, then inserted rows appear in the rowset's proper order if the rowset is ordered, or in indeterminate order if the rowset is not ordered.

For information about whether inserted rows are visible, see "Visibility of Pending Changes" and "Visibility of Transmitted Changes" earlier in this chapter.


Visibility of Other Changes

If the consumer completely releases and then refetches a row for which changes have been made by another rowset in the same transaction or by other applications operating outside the transaction, the visibility of these changes depends on the DBPROP_OTHERUPDATEDELETE and DBPROP_OTHERINSERT properties.

The visibility of changes made by other rowsets in the same transaction depends only on these properties. It does not depend on the transaction isolation level because the changes are in the same transaction.

The visibility of changes made by applications outside the current transaction depends on both the values of these properties and the transaction isolation level. The following table shows whether changes made by applications outside the current transaction are visible, depending on the transaction isolation level and the values of these properties.

Property value | Read uncommitted | Read committed | Repeatable read | Able to Serialize
DBPROP_OTHERUPDATE- DELETE: VARIANT_TRUE VARIANT_FALSE | Yes No | Yes No | No No | No No
DBPROP_OTHERINSERT: VARIANT_TRUE VARIANT_FALSE | Yes No | Yes No | Yes No | No No


Resynchronizing Rows

The methods in IRowsetRefresh enable the consumer to retrieve the data in the data source that is visible to the current transaction according to its isolation level. To retrieve a copy of the currently visible data, the consumer calls IRowsetRefresh::GetLastVisibleData. To update the copy of a row in the rowset with the currently visible data, the consumer calls IRowsetRefresh::RefreshVisibleData.

Although this capability is related to the DBPROP_OWNUPDATEDELETE, DBPROP_OWNINSERT, DBPROP_OTHERUPDATEDELETE, and DBPROP_OTHERINSERT properties, it is independent of them. That is, the value of these properties can be VARIANT_FALSE and the rowset can still expose IRowsetRefresh. However, because of implementation difficulties, many rowsets do not expose IRowsetRefresh unless the values of the DBPROP_OWNUPDATEDELETE and DBPROP_OTHERUPDATEDELETE properties are VARIANT_TRUE.


Refreshing Row Values

Once a row handle is retrieved from the provider, the nondeferred columns of that row are cached such that multiple calls to GetData return the same cached values. IRowsetRefresh gives the consumer a way to retrieve the data in the data source that is visible to the current transaction according to its isolation level corresponding to a set of row handles. This data is referred to as "visible data."

In order to retrieve the visible data for a row, the consumer calls IRowsetRefresh::GetLastVisibleData. To update the copy of a row in the rowset with the currently visible data, the consumer calls IRowsetRefresh::RefreshVisibleData with fOverwrite set to TRUE.

For performance reasons, visible data may itself be cached by the provider. The consumer calls IRowsetRefresh::RefreshVisibleData in order to refresh the provider's visible data cache. For deferred columns and for providers that do not keep a local cache of visible data, RefreshVisibleData with fOverwrite set to FALSE has no effect. However, consumers wanting to work with general providers should always call RefreshVisibleData before calling GetLastVisibleData for any nondeferred columns in order to be sure they are getting the latest data values from the data source.

Although the ability to obtain visible data is related to the DBPROP_OWNUPDATEDELETE, DBPROP_OWNINSERT, DBPROP_OTHERUPDATEDELETE, and DBPROP_OTHERINSERT properties, it is independent of them. In other words, the value of these properties can be VARIANT_FALSE and the rowset can still expose IRowsetRefresh. However, because of implementation difficulties, many rowsets do not expose IRowsetRefresh unless the values of the DBPROP_OWNUPDATEDELETE and DBPROP_OTHERUPDATEDELETE properties are VARIANT_TRUE.


Getting and Setting Data

Overview of Getting and Setting Data

This chapter discusses how data is transferred between the consumer and provider. A consumer is any piece of system or application code that consumes an OLE DB interface. This includes OLE DB components themselves. A provider is any software component that exposes an OLE DB interface. The three parts of data—value, length of the value, and its status—are discussed. Accessors, which are a collection of information that describes how data is stored in the consumer's buffer, are presented, along with a special type called the reference accessor. Defined another way, an accessor is a collection of bindings: Each binding associates a single column or parameter to the consumer's buffer. Bindings and their structures as exposed by DBBINDING are discussed. Obviously an important aspect of data transfer for the consumer is memory management. This, along with procedures for data transfer, completes the topic of “Getting and Setting Data.”

For more information on | Go to
Consumers and providers | “Consumers and Providers” in Chapter 1, “Overview of OLE DB”
OLE DB components | “OLE DB Components” in Chapter 1, “Overview of OLE DB”
Parts of data | “Data Parts” in this chapter
Status of a data value | “Status” in this chapter
Accessors | “Accessors” in this chapter
Reference accessors | “Reference Accessors” in this chapter
Bindings and their structures | “Bindings” in this chapter
Memory management in data transfer | “Memory Management” in this chapter
Data transfer procedures | “Data Transfer Procedures” in this chapter

Data refers collectively to parameter data and rowset data. Parameter data is the data used for parameters in commands. For example, in the text command SELECT * FROM MyTable WHERE Name = ?, the question mark represents a parameter and the consumer sends data for this parameter to the provider when it executes the command. Rowset data is the data returned in a rowset, such as the rowset generated by executing the previous command, and key column values passed to an index rowset when setting the index range or seeking for a key value.

Data is stored in both the consumer and the provider. The consumer stores data in a buffer. A binding associates, or binds, a single column or parameter to the consumer's buffer, and a group of bindings is gathered together in an accessor. When transferring data, the handle to the accessor and the pointer to the buffer are passed to the provider. In the provider, data is stored in a provider-specific way. For example, it may be placed in a structure, such as the rowset's copy of a row, or passed directly through to the data source.

The provider actually transfers the data from consumer to provider and provider to consumer. Getting data is defined as transferring data from the provider to the consumer, as when getting rowset data with IRowset::GetData or getting output parameter data with ICommand::Execute. Setting data is defined as transferring data from the consumer to the provider, as when setting rowset data with IRowsetChange::SetData, key values with IRowsetIndex::SetRange, or input parameter data with Execute.


Data Parts

Data has three parts: the data value, the length of the data value, and the status of the data value. The consumer can bind one, two, or all three of these parts. Except in very rare cases, the consumer always binds the data value and the status, and always binds the length for variable-length data types.

If the consumer is getting data and does not bind a part, the provider does not return that part. Although this is not an error, it is generally of little or no use. For example, if the consumer does not bind the status and the provider returns DB_S_ERRORSOCCURRED, the consumer must assume that the value was not returned correctly.

If the consumer is setting data and does not bind a part, the provider sends the data if it has sufficient information; otherwise it returns an error. For example, if the consumer binds the status but not the value, the provider sends the data if the status is DBSTATUS_S_ISNULL and returns an error if the status is DBSTATUS_S_OK.

The following table explains the behavior of the consumer and provider for each combination of bound parts.

Consumer/Provider behaviors for data binding

Val | Len | Stat | Use when getting data | Use when setting data
α | α | α | Most commonly used combination when getting variable-length data. | Most commonly used combination when setting variable-length data.
α α – Not generally used.

If the method returns DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED, the consumer must assume that it didn’t successfully get or set the value for the column.

If the column is nullable, the consumer cannot determine the validity of the returned value or length because it does not know if the column value is NULL.

 Not generally used.

If the method returns DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED, the consumer must assume that it didn’t successfully get or set the value for the column.

The provider assumes the status is DBSTATUS_S_OK.
α – α Most commonly used combination when getting fixed-length data. Most commonly used combination when setting fixed-length data.

The provider assumes that string data is null terminated.

See note 1.
α – – Not generally used.

If the method returns DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED, the consumer must assume that it didn’t successfully get or set the value for the column.

If the column is nullable, the consumer cannot determine the validity of the returned value or length because it does not know if the column value is NULL.

 Not generally used.

If the method returns DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED, the consumer must assume that it didn’t successfully get or set the value for the column.

The provider assumes the status is DBSTATUS_S_OK.

The provider assumes that string data is null terminated.

If wType in the DBBINDING structure is a variable length DBTYPE, the provider returns a DB_E_ERRORSOCCURRED.
– α α Not generally used. If IColumnsInfo::GetColumnInfo does not return DBCOLUMNFLAGS_ISLONG for the column, the provider returns DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED.
– α – Not generally used.

If the method returns DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED, the consumer must assume that it didn’t successfully get or set the value for the column.

If the column is nullable, the consumer cannot determine the validity of the returned value or length because it does not know if the column value is NULL..

 Not generally used.

If the method returns DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED, the consumer must assume that it didn’t successfully get or set the value for the column.

The provider assumes the status is DBSTATUS_S_OK.
– – α Not generally used. Not generally used.

Can be used to set the value of a column or parameter to NULL or to specify that the provider is to use the default value for an input or input/output parameter in a procedure.

If the status is DBSTATUS_S_OK, the provider returns a status of DBSTATUS_E_UNAVAILABLE.
– – – IAccessor::CreateAccessor returns DBBINDSTATUS_
BADBINDINFO for the binding. 		IAccessor::CreateAccessor returns DBBINDSTATUS_
BADBINDINFO for the binding.
1If wType in the DBBINDING structure is DBTYPE_BYTES, and the status is DBSTATUS_S_OK, the provider uses the value of cbMaxLen in the DBBINDING structure as the number of bytes to read. Note that 1.x providers may return an error for this case; in order to work with 1.x providers, consumers should always include the length data part when specifying non-null values of type DBTYPE_BYTES.


Value

The value is the actual value of the data. For example, the value of a column containing last names might be "Smith." Unless the type indicator of the data is DBTYPE_BSTR or is combined with DBTYPE_BYREF, DBTYPE_ARRAY, or DBTYPE_VECTOR, the data is stored directly at the offset specified in the binding. Sufficient space must be allocated for the data.

For example, if the type indicator is DBTYPE_I2, a two-byte integer is stored at the specified offset and two bytes must be allocated at that offset. If the type indicator is DBTYPE_STR, an ANSI string is stored at the specified offset. If the length of the string in bytes is longer than the number of bytes specified at the offset in the cbMaxLen element of the DBBINDING structure, then the string is truncated.

If the type indicator is DBTYPE_BSTR or is combined with DBTYPE_BYREF, DBTYPE_ARRAY, or DBTYPE_VECTOR, then a pointer to the data is stored at the offset specified in the binding and the data is stored in separately allocated memory. For example, if the type indicator is DBTYPE_BSTR, a pointer to a BSTR is stored at the specified offset and sizeof(BSTR) bytes must be allocated at that offset; the BSTR itself is stored in memory allocated with one of the Automation string allocation functions. If the type indicator is DBTYPE_STR | DBTYPE_BYREF, a pointer to an ANSI string is stored at the specified offset and sizeof(char *) bytes must be allocated at that offset; the string itself is stored in separately allocated memory. For more information, see "Type Indicators" in Appendix A.

The only exception to these rules is when a reference accessor is used, in which case the value is a pointer to the provider's buffer for rowset data, or a pointer to the consumer's buffer for input parameter data. For more information, see "Reference Accessors" later in this chapter.


Length

The length is the length of the data in bytes. It is useful only for variable-length data. For fixed-length data, the length is always the size of the data type. For example, the length of the ANSI string "Johnson" is 7, the length of the Unicode string "Lopez" is 10 (five two-byte characters), and the length of any value stored as a two-byte integer is 2.

The length gives the length of the data as it is stored by the consumer. That is, when getting data, it is the length of the data after it is converted from the provider type to the consumer type. When setting data, it is the length of the data before it is converted from the consumer type to the provider type.

Data Length and Buffer Length

It is important not to confuse the length of the data value with the length of the buffer in which the data value is stored. For example, in the following figure, the length of the buffer is 14 and the length of the data value stored in the buffer is 10.

An example of data length in relation to buffer length

The consumer specifies the buffer length with the cbMaxLen element of the DBBINDING structure. For information about how the provider uses the buffer length in conjunction with the data length, see cbMaxLen in "DBBINDING Structures."

Length Values

The length value itself is a ULONG data type. The value stored in this ULONG depends on the data type as follows:

  • Variable-length data types—The length of variable-length data types is the actual length of the untruncated data value in bytes. For string data, the length does not include the length of the null-termination character. If the length is bound, the string can include embedded null characters; that is, the length supersedes the null-termination character in determining the end of the string. Embedding null characters is not encouraged. If the length is not bound, the string is assumed to be null terminated.

  • Fixed-length data types—When getting data, the returned length of a fixed-length data type is the size of the data type. For DBTYPE_BSTR, this is sizeof(void *), not the length of the BSTR. When setting data, the provider ignores the length of fixed-length data types.

  • DBTYPE_IUNKNOWN, DBTYPE_IDISPATCH—When setting or getting data through ISequentialStream, the length is used to specify the total number of bytes (which does not include space for a null since data returned through ISequentialStream is not null-terminated) that will be sent or returned through ISequentialStream. When binding to any other object as DBTYPE_IUNKNOWN or DBTYPE_IDISPATCH, the returned length is sizeof(IUnknown*), not the size of the object itself; if there is a way to determine the length of the object, it is through a method on the object itself. The provider ignores the length field when binding to an object other than ISequentialStream as DBTYPE_IUNKNOWN or DBTYPE_IDISPATCH.

  • DBTYPE_BYREF—The length is the same as if DBTYPE_BYREF is not used. That is, it is the length of the data being pointed to. For example, if the type indicator is DBTYPE_STR | DBTYPE_BYREF, the length is the length of the string in bytes, not the length of the pointer.

  • DBTYPE_VECTOR, DBTYPE_ARRAY—When getting data, the returned length is zero. The reason is that there is only a single length, while a vector or array can contain multiple values. When setting data, the provider ignores the length.


Status

The status indicates whether the data value or some other value, such as a NULL, is to be used as the value of the column or parameter. If the data value is to be used, the consumer sets the status to DBSTATUS_S_OK on input. On output, the provider returns information about whether the consumer was able to set or get the value.

If the data value is not to be used, the status is set to DBSTATUS_S_ISNULL or DBSTATUS_S_DEFAULT. DBSTATUS_S_ISNULL indicates that a NULL value is to be used for the column or parameter; the data value and length are ignored. DBSTATUS_S_DEFAULT indicates that the default value of an input or input/output parameter in a procedure is to be used on input. The data value and length are ignored on input. However, they are used on output for an input/output parameter.

Status Values Used When Getting Data

To get rowset or output parameter data, use ICommand::Execute, IMultipleResults::GetResults, IRowset::GetData, IRowsetRefresh::GetLastVisibleData, and IRowsetUpdate::GetOriginalData. As the provider returns the data, it sets the status of each column or output parameter so the consumer can determine which data values it got successfully. It produces the return codes as follows:

  • Success return code (generally S_OK)—No errors occurred while getting data for any columns or parameters. Warning conditions may have occurred for any columns or parameters.

    Note Truncation is a warning condition when getting data. The corresponding status is set to DBSTATUS_S_TRUNCATED.

  • DB_S_ERRORSOCCURRED—Errors occurred while getting data for one or more columns or parameters but data was successfully returned for at least one column or parameter. Warning conditions may have occurred for any nonerror columns or parameters.

  • DB_E_ERRORSOCCURRED—Errors occurred while getting data for all columns or parameters. Warning conditions did not occur for any columns or parameters.

If the provider returns a fatal error other than DB_E_ERRORSOCCURRED, such as E_FAIL, the returned status values are undefined. If the consumer did not bind the status for a column or output parameter and the method returns DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED, the consumer must assume that the value for that column or output parameter was not successfully returned.

The following table explains the meaning of each returned status value, whether it is a success, error, or warning condition, and whether data was successfully returned.

Status values for returned data

Status value 		Success, warning,
or error 		Data
returned
Description
DBSTATUS_S_OK | Success | Y | The provider returned a non-NULL value.
DBSTATUS_S_ISNULL | Success | Y | The provider returned a NULL value.
DBSTATUS_S_
TRUNCATED 		Warning Y Variable-length data or nonsignificant digits of numeric data were truncated.
DBSTATUS_E_
BADACCESSOR 		Error N Accessor validation was deferred and was performed while the method returned data. The binding was invalid for this column or parameter.

For a description of the cases that may cause deferred accessor validation to fail, see the description of the DBBINDSTATS values listed under the rgStatus argument of CreateAccessor.
DBSTATUS_E_
CANTCONVERTVALUE 		Error N The data value couldn't be converted for reasons other than sign mismatch or data overflow. For example, the data was corrupted in the data source but the row was still retrievable.
DBSTATUS_E_
CANTCREATE 		Error N One of the following conditions:

The provider could not allocate memory in which to return data.

The type indicator for the column was DBTYPE_IUNKNOWN and a storage object was already open on the column.

The type indicator for the column was DBTYPE_IUNKNOWN, the provider supports only one open storage object at a time (DBPROP_
MULTIPLESTORAGEOBJECTS was VARIANT_FALSE), and a storage object was already open on the rowset.
DBSTATUS_E_
DATAOVERFLOW 		Error N Conversion failed because the data value overflowed the type specified for the value part in the consumer's buffer.
DBSTATUS_E_
SIGNMISMATCH 		Error N Conversion failed because the data value was signed and the type specified for the value part in the consumer's buffer was unsigned.
DBSTATUS_E_
UNAVAILABLE 		Error N The provider could not determine a value. For example, the row was just created, the default for the column was not available, and the consumer had not yet set a new value.

Status Values Used When Setting Data

To set rowset or input parameter data, use ICommand::Execute, IRowsetChange::SetData, IRowsetChange::InsertRow, IRowsetIndex::Seek, and IRowsetIndex::SetRange. When using these methods, the consumer must set the status to one of the following values.

Status value | Description
DBSTATUS_S_OK | The value sent to the provider is not NULL and is passed in the consumer's buffer at the offset specified by obValue in the binding.
DBSTATUS_S_ISNULL | The value sent to the provider is NULL. The provider ignores the contents of the value and length parts of the consumer's buffer.
DBSTATUS_S_ DEFAULT | The provider is to use the default value in SetData or InsertRow, or use it as the input value for an input or input/output parameter in a procedure. The provider ignores the contents of the value and the length parts of the consumer's buffer on input, although it uses them on output for input/output parameters. This value is never returned when getting data. If the provider can determine the default value, that value is returned along with a status of DBSTATUS_S_OK if the default is non-null, or DBSTATUS_S_ISNULL if the default is null. If the provider cannot determine the default value, it returns DBSTATUS_E_UNAVAILABLE when calling GetData for columns set as DBSTAUS_S_DEFAULT. Note that consumers should be prepared for 1.0 or 1.1 providers to return DBSTATUS_E_BADSTATUS when attempting to specify DBSTATUS_S_DEFAULT in SetData or InsertRow.
DBSTATUS_S_IGNORE | The provider is to skip this column when setting data. The provider ignores the contents of the value and the length parts of the consumer's buffer for this column and does not change the underlying column value. DBSTATUS_S_IGNORE is not valid for parameters, and returns DBSTATUS_E_BADSTATUS. Consumers should use DBSTATUS_S_DEFAULT to pass default values as parameters. Consumers should be prepared for 1.x providers to return DBSTATUS_E_BADSTATUS when attempting to specify DBSTATUS_S_IGNORE in SetData or InsertRow.

As the provider transfers data from the consumer to the provider, it sets the status of each column or input parameter so that the consumer can determine which data values were successfully set. It produces return codes as follows:

  • Success return code (usually S_OK)—No errors occurred while setting data for any columns or parameters.

  • DB_S_ERRORSOCCURRED—Errors occurred while setting data for one or more columns or parameters but data was successfully set for at least one column or parameter.

    Note Truncation is an error condition when setting data. The corresponding status is set to DBSTATUS_E_CANTCONVERTVALUE.

  • DB_E_ERRORSOCCURRED—Errors occurred while setting data for all columns or parameters.

If the provider returns a fatal error other than DB_E_ERRORSOCCURRED, such as E_FAIL, the returned status values are undefined. If the consumer did not bind the status for a column or input parameter and the method returns DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED, the consumer must assume that the value for that column or input parameter was not successfully set.

The following table explains the meaning of each returned status value, whether it is a success or error condition, and whether data was successfully sent.

Returned status values for sent data

Status value 		Success, warning,
or error 		Data
sent
Description
DBSTATUS_S_OK | Success | Y | A non-NULL value was sent to the provider.
DBSTATUS_S_ISNULL | Success | Y | A NULL value was sent to the provider.
DBSTATUS_S_
DEFAULT 		Success Y The provider used the default value for an input parameter in a procedure. If the provider used the default value on input for an input/output parameter, the status is set to the status of the output value on output.
DBSTATUS_E_
BADACCESSOR 		Error N Accessor validation was deferred and was performed while the method sent data. The binding was invalid for this column or parameter.

For a description of the cases that may cause deferred accessor validation to fail, see the description of the DBBINDSTATS values listed under the rgStatus argument of CreateAccessor.
DBSTATUS_E_
BADSTATUS 		Error N One of the following conditions:
  • The status value sent to the provider was not DBSTATUS_S_OK, DBSTATUS_S_ISNULL, DBSTATUS_S_IGNORE, or DBSTATUS_S_DEFAULT.

  • The status value was DBSTATUS_S_DEFAULT and the binding was not used in SetData, InsertData, or for an input or input/output parameter in a procedure.

  • The status value was DBSTATUS_S_DEFAULT, the binding was not for an input or input/output parameter in a procedure, and the provider was a 1.0 or a 1.1 provider.

  • The binding used in SetData only contained the status part and the status value was not DBSTATUS_S_ISNULL.

  • The status value sent to the provider was DBSTATUS_S_IGNORE and the binding was for a parameter in a procedure.

  • The status value sent to a provider was DBSTATUS_IGNORE and the provider was a 1.x provider.
DBSTATUS_E_
CANTCONVERTVALUE 		Error N The data value couldn't be converted for reasons other than sign mismatch or data overflow. For example, converting the data would have truncated it, including truncating nonsignificant digits in numeric data.
DBSTATUS_E_
CANTCREATE 		Error N One of the following conditions:

The type indicator for the column was DBTYPE_IUNKNOWN and a storage object was already open on the column.

The type indicator for the column was DBTYPE_IUNKNOWN, the provider supports only one open storage object at a time (DBPROP_
MULTIPLESTORAGEOBJECTS was VARIANT_FALSE), and a storage object was already open on the rowset.
DBSTATUS_E_
DATAOVERFLOW 		Error N Conversion failed because the data value overflowed the type used by the provider.
DBSTATUS_E_
INTEGRITYVIOLATION 		Error N The data value violated the integrity constraints for the column.
DBSTATUS_E_
PERMISSIONDENIED 		Error N The DBPROP_COLUMNRESTRICT property was VARIANT_TRUE and the user did not have permission to write to the column.
DBSTATUS_E_
SCHEMAVIOLATION 		Error N The data value violated the schema for the column.
DBSTATUS_E_
SIGNMISMATCH 		Error N Conversion failed because the data value was signed and the type used by the provider was unsigned.
DBSTATUS_E_
UNAVAILABLE 		Error N The value was not sent for an undetermined reason. For example, the provider encountered an error while sending a column or parameter value and stopped processing. If the provider backs out changes it made before the error, it also sets the status of the columns from which it backs data out to DBSTATUS_E_UNAVAILABLE.
DBSTATUS_E_
UNAVAILABLE 		Error N
  • One of the following conditions:

  • The consumer did not bind the value part of the data, the status was DBSTATUS_S_OK, and IColumnsInfo::GetColumnInfo did not return the DBCOLUMNFLAGS_ISLONG flag for the column.

  • The consumer did not bind the length part of the data, the status was DBSTATUS_S_OK, and wType in the DBBINDING structure was DBTYPE_BYTES.


Accessors

An accessor is a collection of information that describes how data is stored in the consumer's buffer. For example, it contains the type of each piece of data, the offset in the buffer at which it is stored, the scale and precision used by data stored in a DB_NUMERIC structure, and so on. When the consumer calls a method that transfers data, such as IRowset::GetData or ICommand::Execute, it passes the handle to an accessor and a pointer to a buffer. The provider uses this accessor to determine how to transfer the data contained in the buffer.

An accessor is identified by its handle.

typedef ULONG HACCESSOR;
# define DB_INVALID_HACCESSOR 0x00

DB_INVALID_HACCESSOR is an accessor handle value that is always invalid.


Using Accessors

Accessors are created with IAccessor::CreateAccessor, reference-counted with IAccessor::AddRefAccessor, and released with IAccessor::ReleaseAccessor. They can be created and released at any time. An accessor can be a parameter accessor (used for accessing parameter data), a row accessor (used for accessing rowset data), or both. Parameter accessors must be created and used on the command. Row accessors can be created on the command or the rowset and must be used on the rowset. Row accessors created after a row is fetched can still be used against that row.

When ICommand::Execute creates a rowset, the rowset inherits all row accessors that have been created on that command. To the consumer, it appears as if each accessor has been copied from the command to the rowset: the bindings, flags, and handle of each accessor are the same on both the rowset and the command. Once the rowset is opened, it gets its own independent copy of the accessors. Calling IAccessor on the command object has no effect on the accessors available on any existing rowsets, and calling IAccessor on the rowset has no effect on the accessors available on any existing commands.

For example, suppose the text command that creates a rowset contains parameters. The consumer might create multiple rowsets by repeatedly executing this text command with different parameters. Because the rowsets are created from the same text command, all can use the same row accessors. It is therefore more efficient to create these accessors once on the command, rather than creating them separately on each rowset. To create a row accessor on a command, the consumer must know the column ordinals. For a text command such as SELECT * FROM MyTable, there is no way to know the order of the result columns. In such a case, the consumer must define the row accessor on the rowset or modify the text command to list the columns. For commands that generate a hierarchy of rowsets, only the first (root) rowset inherits the row accessors from the command.

Consumers can create and use more than one accessor on a single row. Thus, the consumer can get or set multiple copies of the same data. For example, suppose a rowset contains employee data including a picture of the employee. The consumer might create two accessors: one for all of the columns except the picture column and another for the picture column. The consumer might then pass the handle of the first accessor to IRowset::GetData to get all of the data except the picture. When the user requests the picture, the consumer would then pass the handle to the second accessor to GetData to get the picture.

Row accessors can be optimized. How this is done is provider specific. In general, it is faster to use an optimized accessor than a nonoptimized accessor. For example, the provider might structure its internal row buffers to match the types used in the accessor. Optimized row accessors must be created before any rows are fetched.

Flagging accessors as optimized affects only providers that support optimized accessors. In general, providers are not required to recognize optimized accessors, nor are they required to enforce any of the restrictions associated with optimized accessors.

However, clients written to consume data from general OLE DB providers should never include the same column in more than one optimized accessor, and for all bindings to a particular column, should use a data type coercible from the type specified in any optimized accessor for that column.


Reference Accessors

A special type of accessor, called a reference accessor, enables consumers to get rowset data directly from the provider's buffer and providers to get input parameter data directly from the consumer's buffer. You cannot use these accessors with output parameters or when setting row data.

Reference accessors require the consumer to have prior knowledge of the provider's buffer layout; the consumer cannot discover this information at run time. Because of this, reference accessors are designed for use by tightly coupled consumer/provider pairs. Because they allow direct access to the provider's buffers, providers are not required to support them. Consumers determine whether providers support reference accessors through the DBPROP_BYREFACCESSORS property.

To use reference accessors, the layout of the consumer's buffer must exactly match the layout of the provider's buffer. That is, both buffers must allocate space for the same parts (data value, length, and status), these parts must appear at the same offset in both buffers, the data value parts must have the same type and buffer length in both buffers, and so on. If the provider's buffer does not contain length or status parts, the consumer cannot bind these parts, and must determine length and whether the data value is NULL in the same manner as the provider.

To use a reference accessor, the consumer passes the handle of the accessor and a pointer to a buffer to the provider. For rowset data, the provider returns a pointer to its internal buffer in the buffer supplied by the consumer. The consumer then dereferences this pointer and reads directly from the provider's buffer. For input parameter data, the consumer's buffer contains a pointer to the buffer that actually contains the data. The provider dereferences this pointer and reads directly from the consumer's other buffer.

For example, the following code shows how a consumer would read data directly from the provider's buffer using a reference accessor:

  1. Assume that the consumer already has a pointer to IAccessor on a rowset (pIAccessor) and a pointer to IRowset on the same rowset (pIRowset).
    void *  pv;
ULONG   cRowsObtained;
HACCESSOR hReferenceAccessor;
HROW  rghRows[1];
DBBINDING rgBindings[5];

  2.
Fill in the rgBinding array. The bindings must match the layout of the provider's buffer.

    3.

  3.
Create a reference accessor.
    pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA | DBACCESSOR_PASSBYREF, 5, rgBindings, 0, &hReferenceAccessor, NULL);

    4.

  4.
Get a row.
    pIRowset->GetNextRows(NULL, 0, 1, &cRowsObtained, rghRows);

    5.

  5.
Get a pointer to the start of the provider's buffer for the row.
    pIRowset->GetData(rghRows[0], hReferenceAccessor, &pv);

    6.

  6.
Access the data in the provider's buffer, starting at the address *pv. (Code not shown.)
    7.

The pointer returned by the provider, for rowset data, or passed by the consumer, for input parameter data, does not need to point to the start of the buffer. The only requirement is that the relative offsets of the elements stored in the buffer match the offsets specified in the accessor. The consumer, for rowset data, or provider, for input parameter data, must not write to this buffer, nor can it free this buffer.

For rowset data, the pointer is guaranteed to remain valid until the consumer calls IRowsetChange::SetData, IRowsetChange::DeleteRows, IRowsetRefresh::RefreshVisibleData, IRowsetUpdate::Undo, or IRowsetUpdate::Update for the row, calls IRowset::ReleaseRows for the row and the reference count falls to zero, or releases the rowset. For input parameter data, the pointer is guaranteed to remain valid only until ICommand::Execute returns.


Bindings

An accessor is a collection of bindings. Each binding associates, or binds, a single column or parameter to the consumer's buffer. You can bind columns and output parameters more than once in a single accessor, but input parameters only once in a single accessor. It is usually a consumer programming error to bind a column more than once in a single accessor and then use that accessor to set the value of the column, such as with IRowsetChange::SetData.

Each binding contains information about the consumer's buffer. The most notable information is the ordinal of the column or parameter to which the binding applies, what is bound (the data value, its length, and its status), the offsets in the buffer to each of these parts, and the length and type of the data value as it exists in the consumer's buffer. For more information, see "Data Parts," earlier in this chapter, and "DBBINDING Structures" later in this chapter.

For example, the following figure shows a consumer's buffer containing 10 bytes for a string, two unused bytes to properly align the string's status and length values, four bytes for the string's status, four bytes for the string's length, two unused bytes to properly align the integer's status value, two bytes for a two-byte integer, and four bytes for the integer's status. It also shows the main binding information used to bind the string to column 1 and the integer to column 2.

Main binding information as shown in a consumer’s buffer

Element | String binding | Integer binding
Ordinal | 1 | 2
Bound parts | DBPART_VALUE | DBPART_LENGTH | DBPART_STATUS | DBPART_VALUE | DBPART_STATUS
Offset to data value | 0 | 22
Offset to status | 12 | 24
Offset to length | 16 | N/A
Data type | DBTYPE_STR | DBTYPE_I2
Buffer length | 10 | 2

When getting data, the provider uses the information in each binding to determine where and how to retrieve data from the consumer's buffer. When setting data, the provider uses the information in each binding to determine where and how to return data in the consumer's buffer.


DBBINDING Structures

A binding associates a single column or parameter to the consumer's buffer, and contains information about the buffer.

The DBBINDING structure describes a single binding.

typedef struct tagDBBINDING {
   ULONG   iOrdinal;
   ULONG   obValue;
   ULONG   obLength;
   ULONG   obStatus;
   ITypeInfo * pTypeInfo;
   DBOBJECT * pObject;
   DBBINDEXT * pBindExt;
   DBPART   dwPart;
   DBMEMOWNER   dwMemOwner;
   DBPARAMIO eParamIO;
   ULONG   cbMaxLen;
   DWORD   dwFlags;
   DBTYPE   wType;
   BYTE   bPrecision;
   BYTE   bScale;
} DBBINDING;

The elements of this structure are used as follows.

Element | Description
iOrdinal The ordinal of the column or parameter to which the binding applies.

Column ordinals are fixed for the lifetime of the rowset. The self bookmark column is column 0 and other columns start with column 1. To retrieve column ordinals, consumers call IColumnsInfo::GetColumnInfo, IColumnsRowset::GetColumnsRowset, or IColumnsInfo::MapColumnIDs. If the rowset is generated from a command, the column ordinals returned by GetColumnInfo or GetColumnsRowset on the rowset must match the column ordinals returned by the same methods on the command.

Parameter ordinals are determined from the text command. The first parameter is parameter one. To retrieve parameter ordinals, consumers call ICommandWithParameters::GetParameterInfo.
obValue The offset in bytes in the consumer's buffer to the value part. obValue is ignored unless the DBPART_VALUE bit is set in dwPart. For more information about the value part, see "Value" earlier in this chapter.

This offset must be properly aligned for the processor architecture of the consumer's machine. If data is improperly aligned, consumers can expect alignment faults to occur when getting and setting data values. Consumers should use addresses that are multiples of the size of the data type. For example, if wType is DBTYPE_I4, the value part should be DWORD aligned.
obLength The offset in bytes in the consumer's buffer to the length part. obLength is ignored unless the DBPART_LENGTH bit is set in dwPart. For more information about the length part, see "Length" earlier in this chapter.

The length itself is a ULONG. This offset must be properly aligned for the processor architecture of the consumer's machine; that is, it should be a multiple of sizeof(ULONG). If it is improperly aligned, consumers can expect alignment faults when getting and setting length values.
obStatus The offset in bytes in the consumer's buffer to the status part. obStatus is ignored unless the DBPART_STATUS bit is set in dwPart. For more information about the status part, see "Status" earlier in this chapter.

The status part itself is a DBSTATUS value, which is a DWORD. This offset must be properly aligned for the processor architecture of the consumer's machine; that is, it should be a multiple of sizeof(DWORD). If it is improperly aligned, consumers can expect alignment faults when getting and setting status values.
pTypeInfo | Reserved for future use. Consumers should set pTypeInfo to a null pointer.
pObject Pointer to a DBOBJECT structure. This structure describes how OLE objects in columns or parameters should be accessed.
typedef struct tagDBOBJECT {
 DWORD dwFlags;
 IID iid;
} DBOBJECT;

The elements of this structure are used as follows:

  • dwFlags—The combined storage mode flags to use when creating an instance of the OLE object.

  • iid—The interface to be exposed on the OLE object.

The storage mode flags are defined by the Structured Storage model in OLE. These are:

  • STGM_READ = OF_READ

  • STGM_WRITE = OF_WRITE

  • STGM_READWRITE = OF_READWRITE

  • STGM_SHARE_DENY_NONE = OF_SHARE_DENY_NONE

  • STGM_SHARE_DENY_READ = OF_SHARE_DENY_READ

  • STGM_SHARE_DENY_WRITE = OF_SHARE_DENY_WRITE

  • STGM_SHARE_EXCLUSIVE = OF_SHARE_EXCLUSIVE

  • STGM_DIRECT

  • STGM_TRANSACTED

  • STGM_CREATE = OF_CREATE

  • STGM_CONVERT

  • STGM_FAILIFTHERE

  • STGM_PRIORITY

  • STGM_DELETEONRELEASE

pObject is ignored unless the DBPART_VALUE bit is set in dwPart and wType is DBTYPE_IUNKNOWN. If neither of these conditions is true, it should be set to a null pointer.

Note Consumers should not set wType to DBTYPE_IUNKNOWN and the DBPART_VALUE bit in dwPart without specifying a non-null value for pObject. However, providers can assume in this case that the interface to be bound is IID_IUnknown.
pBindExt The DBBINDEXT structure can be used for future extensions to the binding structure. pBindExt is reserved for future use and consumers should set it to a null pointer.
typedef struct tagDBBINDEXT{
 BYTE * pExtension;
 ULONG  ulExtension;
} DBBINDEXT;
dwPart Specifies which buffer parts are to be bound to the column or parameter. It is one or more DBPARTENUM values combined; these values have the following meaning:
  • DBPART_VALUE—The binding includes a value part.

  • DBPART_LENGTH—The binding includes a length part.

  • DBPART_STATUS—The binding includes a status part.
dwMemOwner For bindings in row accessors, specifies who owns memory allocated by the provider for a column and for which a pointer is returned to the consumer. For example, when wType is DBTYPE_BYREF | DBTYPE_STR, the provider allocates memory for the string and returns a pointer to the string to the consumer; dwMemOwner specifies whether the consumer or provider owns this memory. The provider ignores this when setting rowset data.

The following is a description of each of these values:

  • DBMEMOWNER_CLIENTOWNED—The consumer owns the memory and is responsible for freeing it.

  • DBMEMOWNER_PROVIDEROWNED—The provider owns the memory and is responsible for freeing it. When getting data, the provider returns a pointer into its copy of the row. When using provider-owned memory, the consumer's type must exactly match the provider's type, except that if wType is X | DBTYPE_BYREF, the provider's type can be X or X | DBTYPE_BYREF. If a column is NULL, the provider returns a null pointer for the data value.

    The consumer must not write to or free the memory to which the pointer points. The lifetime of the pointer is provider specific. However, pointers to row data are guaranteed to be valid until IRowset::ReleaseRows is called for the row and its reference count falls to zero; a method is called for the row that might invalidate the pointer; or the rowset is released, whichever occurs first. The methods that might invalidate the pointer depend on the method that returned it. These are as follows:

    Ptr returned by:Might be invalidated by:

    GetDataDeleteRows (immediate update mode)
    RefreshVisibleData
    SetData
    Update
    Undo

    GetLastVisibleDataGetLastVisibleData

    GetOriginalDataRefreshVisibleData
    SetData
    Update

    Although the consumer must not write to provider-owned memory, it can pass pointers to provider-owned memory when setting data. For example, suppose that the consumer wants to copy data efficiently from row A to row B. The consumer creates an accessor to the columns in row A in which wType is X | DBTYPE_BYREF and sets dwMemOwner to DBMEMOWNER_PROVIDEROWNED. When the consumer calls IRowset::GetData, the provider returns pointers to its copy of row A. The consumer then calls IRowsetChange::SetData for row B with the same accessor and a pointer to the memory passed to GetData. The provider dereferences the pointers and copies the data from the rowset's copy of row A to the rowset's copy of row B.

For bindings in row accessors, consumer-owned memory must be used unless wType is DBTYPE_BSTR, X | DBTYPE_BYREF, X | DBTYPE_ARRAY, or X | DBTYPE_VECTOR, in which case either consumer- or provider-owned memory can be used. However, provider-owned memory cannot be used if IColumnsInfo::GetColumnInfo returns the DBCOLUMNFLAGS_ISLONG for the column. Consumers can mix bindings for provider- and consumer-owned memory in the same accessor.

For bindings in parameter accessors, consumer-owned memory must always be used.

dwMemOwner is ignored in reference accessors.

All providers must support binding to client-owned memory. Providers support provider-owned memory only if they can efficiently return a pointer to a cached value. Providers are not required to support binding deferred columns to provider-owned memory.
eParamIO For parameter accessors, eParamIO specifies whether the parameter with which the binding is associated is an input, input/output, or output parameter. Providers support only those parameter I/O types that are supported by their underlying data source. eParamIO is one or more DBPARAMIOENUM values combined. These values have the following meaning:
  • DBPARAMIO_NOTPARAM—The accessor is not used for parameters. In most cases eParamIO is set to this value in row accessors to remind the programmer that it is ignored.

  • DBPARAMIO_INPUT—The parameter is an input parameter.

  • DBPARAMIO_OUTPUT—The parameter is an output parameter.

For row accessors, eParamIO is ignored.
cbMaxLen The length in bytes of the consumer's data structure allocated for the data value. That is, it is the number of bytes allocated at offset obValue for the data value. cbMaxLen is ignored unless the DBPART_VALUE bit is set in dwPart and the data value stored at that location is of variable length. For information about variable-length data types, see "Fixed- and Variable-Length Data Types" in Chapter 10.

cbMaxLen is used as follows:

  • Variable-length data types—The provider checks the length in bytes of variable-length data types against cbMaxLen. If the length is greater than cbMaxLen, the provider truncates the data to cbMaxLen bytes.

    When getting data, this is a warning and the provider sets the status to DBSTATUS_S_TRUNCATED and sets the length to the actual number of bytes of data available after conversion and before truncation. Thus, the consumer can determine the actual number of bytes of data available.

    When setting data, this is an error and the provider sets the status to DBSTATUS_E_CANTCONVERTVALUE; in most cases, this is a consumer programming error.

  • Fixed-length data types—The provider ignores cbMaxLen for fixed-length data types and assumes that the number of bytes available in the consumer's buffer for the data is the size of the data type.

  • DBTYPE_BYREF, DBTYPE_VECTOR, DBTYPE_ARRAY—The provider ignores cbMaxLen and assumes that the number of bytes available in the consumer's buffer is the size dictated by DBTYPE_BYREF, DBTYPE_VECTOR, or DBTYPE_ARRAY. For DBTYPE_BYREF and DBTYPE_ARRAY, this is sizeof(void *). For DBTYPE_VECTOR, this is sizeof(DBVECTOR).

    It is important to note that if DBTYPE_BYREF is combined with the type indicator for a variable-length data type, the data is not truncated to cbMaxLen bytes. This is because the data is in separately allocated memory, not the consumer's buffer, and cbMaxLen does not apply to this memory. The provider allocates this memory based on the length of the data, as bound at the offset obLength, so consumers can guarantee that variable-length data is not truncated if it is retrieved in this manner.
dwFlags dwFlags specifies information about how data is returned. The following flag is recognized:
DBBINDFLAG_HTML

The provider should format the returned data in HTML. If the provider does not support HTML formatting, the bit is ignored and regular text is returned. The provider will not be able to use an accessor created with the HTML flag to set data.
wType The indicator of the data type of the value part of the buffer. For more information about type indicators, see "Type Indicators" in Appendix A.

This type forms an implied conversion between the buffer type and the type of the column or parameter. For information about conversions providers are required to support, see "Data Type Conversion" in Chapter 10.
bPrecision | The maximum precision to use when getting data and wType is DBTYPE_NUMERIC. This is ignored when setting data, wType is not DBTYPE_NUMERIC, or the DBPART_VALUE bit is not set in dwPart. For more information, see "Conversions Involving DBTYPE_NUMERIC or DBTYPE_DECIMAL" in Appendix A.
bScale | The scale to use when getting data and wType is DBTYPE_NUMERIC or DBTYPE_DECIMAL. This is ignored when setting data, wType is not DBTYPE_NUMERIC or DBTYPE_DECIMAL, or the DBPART_VALUE bit is not set in dwPart. For more information, see "Conversions Involving DBTYPE_NUMERIC or DBTYPE_DECIMAL" in Appendix A.

Note The offsets specified in obValue, obLength, and obStatus must not point to overlapping areas of memory. The provider is not required to check if this condition occurs, and consumers must be careful to avoid this condition when constructing bindings.


Memory Management

When the consumer calls a method that gets or sets data, such as IRowset::GetData, IRowsetChange::SetData, or ICommand::Execute, it passes a pointer to memory containing the data. The consumer allocates and frees this memory by the method it chooses. The provider can only read this memory. It must not free this memory, nor should it assume that this memory exists after the method returns.

If the type indicator is DBTYPE_BSTR or is combined with DBTYPE_BYREF, DBTYPE_ARRAY, or DBTYPE_VECTOR, the consumer's memory contains a pointer to other memory. The other memory actually contains the data value, as distinct from the length and status, for the column or parameter. For example, if the type indicator is DBTYPE_BYREF | DBTYPE_STR, the data value in the consumer's memory is a pointer to a string; if the type indicator is DBTYPE_BSTR, the data value is a pointer to a BSTR. The remainder of this section discusses how this other memory is managed.

Note It could be possible for a consumer to bind an input/output parameter with a type indicator of DBTYPE_BSTR or with a type indicator that is combined with DBTYPE_BYREF, DBTYPE_ARRAY, or DBTYPE_VECTOR. In this example, when the provider returns the output value for the parameter, it overwrites the pointer in the consumer's buffer that points to the input value. To ensure access to the input value after the provider returns the output value—for example, to free it—the consumer should keep a separate copy of the pointer to the input value.

When setting data, the consumer is responsible for allocating and freeing the other memory by whatever method it chooses. The provider can only read this memory. It must not free it, nor should it assume that it exists after the method returns.

When getting data, the provider is responsible for allocating the other memory. The method by which the provider allocates this memory and the responsibility for freeing it is determined by the dwMemOwner element of the applicable binding.

If dwMemOwner is DBMEMOWNER_PROVIDEROWNED, the provider allocates the memory and frees it by whatever method it chooses. The consumer can only read the memory and must not free it or write to it. The memory is part of the rowset's copy of the row, which saves the provider from separately allocating it and the consumer from freeing it. Consumers usually use provider-owned memory when they need fast, read-only access to the data. For more information about provider-owned memory, see dwMemOwner in "DBBINDING Structures" earlier in this chapter.

If dwMemOwner is DBMEMOWNER_CLIENTOWNED, the provider allocates and the consumer frees the memory as noted below. Although the provider must not access this memory after the method returns, the consumer can access it at any time until it is freed. Consumers use consumer-owned memory when they need their own copy of the data and when they send data to the provider.

  • DBTYPE_BSTR—BSTRs are allocated and freed with the Sys*String functions. For more information, see "Data Types".

  • DBTYPE_BYREF—Memory is allocated and freed with IMalloc.

  • DBTYPE_ARRAY—SAFEARRAYs are allocated and freed with the SafeArray* functions. For more information, see "Data Types".

  • DBTYPE_VECTOR—Memory is allocated and freed with IMalloc.

The consumer can mix bindings in which pointers to the data and the data itself are returned. For example, suppose a rowset has two columns containing ANSI strings, one of which has a maximum length of 10 characters and the other of which has a maximum length of 32,000 characters. The consumer might choose to get the first string directly in its buffer (wType is DBTYPE_STR, cbMaxLen is 11, and dwMemOwner is DBMEMOWNER_CLIENTOWNED) and get a pointer to the provider's data cache for the second string (wType is DBTYPE_STR | DBTYPE_BYREF and dwMemOwner is DBMEMOWNER_PROVIDEROWNED).

The consumer can also mix bindings in which the consumer and provider own the memory. For example, suppose a rowset contains two columns containing ANSI strings with a maximum length of 32,000 characters, but only one of which is updatable. The consumer might choose to get pointers to both strings (wType is DBTYPE_STR | DBTYPE_BYREF) but to use consumer-owned memory (dwMemOwner is DBMEMOWNER_CLIENTOWNED) for the updatable string and provider-owned memory (dwMemOwner is DBMEMOWNER_PROVIDEROWNED) for the read-only string.


Data Transfer Procedures

The following sections discuss what the consumer and provider do to transfer data:

  • Getting Data

  • Setting Data


Getting Data

To get data, such as with IRowset::GetData for rowset data or ICommand::Execute for output parameter data, the consumer performs the following actions:

  1. Creates an accessor to bind the columns or output parameters as desired.

  2. Allocates a buffer to hold the data returned by the provider.

  3. Calls a method that gets data and passes the handle to the accessor and a pointer to the buffer.

For each column or output parameter specified in the accessor, the provider performs the actions in the following procedure. This procedure assumes that the consumer has bound the value, length, and status for each column or output parameter. If any of these are not bound, the procedure is the same except that the provider does not return the unbound part.

Note If the accessor is a reference accessor, the provider does not follow this procedure but returns a pointer to the provider's memory that contains the data. For more information, see "Reference Accessors" earlier in this chapter.

  1. Validates the accessor against the metadata if it has not already done so.

    The provider can validate the entire accessor before returning any data or on a binding-by-binding basis while returning data. If validation fails in the former case, the provider produces the appropriate return code and does not return any data. If it fails in the latter case, the provider sets the status of the column or output parameter to DBSTATUS_E_BADACCESSOR and proceeds to the next column or output parameter.

  2. Checks if the data is NULL.

    If it is, the provider sets the status to DBSTATUS_S_ISNULL or VT_NULL, sets the length to zero, ignores the value, and proceeds to the next column or output parameter. The address at which the status value is placed is calculated from the buffer address passed to the method and the obStatus element of the binding. A variant whose type is VT_NULL is different from a column status of DBSTATUS_S_ISNULL. Providers supporting variants, as well as consumers binding to variants, must be prepared to handle either type of NULL. However, if DBSTATUS_S_ISNULL is specified in the status binding, the contents of the value binding, regardless of type, are undefined.

  3. Converts the data to the type specified by the wType element of the binding. If an error occurs while converting the data, the provider sets the status accordingly and proceeds to the next column or output parameter.

    For a list of status values that describe conversion errors, see "Status Values Used When Getting Data" in "Status," earlier in this chapter. For more information about converting data, see "Data Type Conversion Rules" in Appendix A.

  4. Sets the length. The address at which the length value is placed is calculated from the buffer address passed to the method and the obLength element of the binding.
    • Variable-length data types—The length of variable-length data types is the length of the untruncated data in bytes, not counting the null-termination character for strings.

    • Fixed-length data types—The length of fixed-length data types is the size of the data type. If wType is DBTYPE_IUNKNOWN or DBTYPE_IDISPATCH, the length is sizeof(IUnknown*), not the size of the object to which the interface pointer points, and if wType is DBTYPE_BSTR, the length is sizeof(BSTR), not the length of the string itself.

    • DBTYPE_BYREF—DBTYPE_BYREF does not affect the length value. For example, if wType is DBTYPE_BYREF | DBTYPE_STR, the length is the length in bytes of the string, not the length of the pointer.

    • DBTYPE_VECTOR, DBTYPE_ARRAY—If the type indicator is joined in a logical OR with DBTYPE_ARRAY or DBTYPE_VECTOR, the length is zero.
  5. Sets the data value to the converted data. The address at which the data value is placed is calculated from the buffer address passed to the method and the obValue element of the binding.
    • Variable-length data types—If the length in bytes of the converted, variable-length data is greater than cbMaxLen bytes, the provider truncates the data to cbMaxLen bytes before placing it in the consumer buffer and sets the status to DBSTATUS_S_TRUNCATED. Otherwise, the provider places the data in the consumer buffer without truncating it.

    • Fixed-length data types—The provider places the converted, fixed-length data in the consumer's buffer. The provider ignores cbMaxLen; it does not truncate the data before placing it in the consumer's buffer.

      If the type indicator is DBTYPE_IUNKNOWN or DBTYPE_IDISPATCH, the data value is a pointer to an interface on the OLE object, not the object itself. For more information, see Chapter 7, "BLOBs and OLE Objects."

      If the type indicator is DBTYPE_BSTR, the provider places the converted data in a BSTR and places a pointer to the BSTR in the consumer's buffer. The provider ignores cbMaxLen; it does not truncate the data before placing it in the consumer's buffer. For information about how the BSTR is allocated and freed, see "Memory Management" earlier in this chapter.

    • DBTYPE_BYREF, DBTYPE_VECTOR, DBTYPE_ARRAY—For DBTYPE_BYREF, the provider places the converted data in separately allocated memory and places a pointer to this memory in the consumer's buffer. For DBTYPE_VECTOR, the provider places the converted data in an array and places the count of array elements and a pointer to the array in a DBVECTOR structure in the consumer's buffer. For DBTYPE_ARRAY, the provider places the converted data in a SAFEARRAY and places a pointer to the SAFEARRAY in the consumer's buffer. In each case, the provider ignores cbMaxLen; that is, it does not truncate the data before placing it in the separate memory, array, or SAFEARRAY. For information about how the separate memory, array, or SAFEARRAY is allocated and freed, see "Memory Management" earlier in this chapter.
  6. Sets the status to DBSTATUS_S_OK if it hasn't already been set to another value.

If the provider encounters an error while returning a column or output parameter value, it sets the status value of that column or output parameter. The provider then continues to process the remaining columns or output parameters. After it has processed all columns or output parameters, it produces one of the following return codes.

  • DB_S_ERRORSOCCURRED—The method successfully returned data for at least one column or output parameter.

  • DB_E_ERRORSOCCURRED—The method did not successfully return data for any columns or output parameters.

The consumer checks the status values to determine the columns or output parameters for which data was successfully returned. If the consumer did not bind a status value for a column or output parameter and a method returns DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED, the consumer must assume that the column or output parameter value was not successfully returned.

The provider frees any memory it allocated for return to the consumer but did not return to the consumer due to an error. If the method fails completely, the contents of the consumer's buffer are undefined.


Setting Data

To set data, such as with IRowsetChange::SetData for rowset data, or ICommand::Execute for input parameter data, the consumer performs the following actions:

  1. Creates an accessor to bind the columns or input parameters.

  2. Allocates a buffer to hold the data to pass to the provider.

  3. Places data in this buffer. For more information, see the next procedure.

  4. Calls a method that sets data and passes the handle to the accessor and a pointer to the buffer.

For each column, except as noted later, or input parameter specified in the accessor, the consumer performs the actions in the following procedure. When passing key values to an index rowset, the consumer performs this procedure only for the number of key columns specified in the cKeyValues argument in IRowsetIndex::Seek, or the cStartKeyValues or cEndKeyValues arguments in IRowsetIndex::SetRange.

Note This procedure assumes that the consumer has bound the value, length, and status for each column or input parameter. If any of these are not bound, the procedure is the same except that the consumer does not set the unbound part.

  1. If the data is NULL, sets the status to DBSTATUS_S_ISNULL and proceeds to the next column or input parameter. If the provider is to use the default as the input value for an input or input/output parameter in a procedure, sets the status to DBSTATUS_S_DEFAULT and proceeds to the next column or input parameter. If the provider is to ignore this column in performing the insert or update, sets the status to DBSTATUS_S_IGNORE. Otherwise, sets the status to DBSTATUS_S_OK. The address at which the status value is placed is calculated from the buffer address passed to the method and the obStatus element of the binding.

  2. If wType is DBTYPE_STR, DBTYPE_WSTR, and DBTYPE_BYTES, or any of these values combined with DBTYPE_BYREF, sets the length to the length of the data in bytes, not counting the null-termination character for strings. For all other data types, the provider ignores the length, so the consumer does not need to set it.

  3. Sets the data value.

    The address at which the data value is placed is calculated from the buffer address passed to the method and the obValue element of the binding. If wType is DBTYPE_BSTR or is combined with DBTYPE_BYREF, DBTYPE_VECTOR, or DBTYPE_ARRAY, the consumer places the data in separately allocated memory and places a pointer to the data in its buffer. For information about how this memory is allocated and freed, see "Memory Management" earlier in this chapter.

For each column, except as noted later, or input parameter specified in the accessor, the provider performs the actions in the following procedure. When passing key values to an index rowset, the provider performs this procedure only for the number of key columns specified in the cKeyValues argument in Seek, or the cStartKeyValues or cEndKeyValues arguments in SetRange. This procedure assumes that the consumer has bound the value, length, and status for each column or input parameter. If any of these are not bound, the procedure is the same except that the provider does not attempt to retrieve the unbound part from the consumer's buffer; for more information, see "Data Parts" earlier in this chapter.

  1. Validates the accessor against the metadata if it has not already done so.

    The provider can validate the entire accessor before setting any data or on a binding-by-binding basis while setting data. If validation fails in the former case, the provider produces the appropriate return code and does not set any data. If it fails in the latter case, the provider sets the status to DBSTATUS_E_BADACCESSOR and proceeds to the next column or output parameter.

  2. Retrieves the status value from the consumer’s buffer.

    If the status is DBSTATUS_S_ISNULL, the provider uses a null value and proceeds to the next column or input parameter. If the status is DBSTATUS_S_DEFAULT, the provider uses the default value and proceeds to the next column or input parameter. If the status is DBSTATUS_S_IGNORE, the provider skips the column and proceeds to the next column to be inserted or updated. If the status is DBSTATUS_S_OK, the provider proceeds to the next step. If the status is any other value, the provider sets the status to DBSTATUS_E_BADSTATUS and proceeds to the next column or input parameter. The address from which the status value is retrieved is calculated from the buffer address passed to the method and the obStatus element of the binding.

  3. If wType is DBTYPE_STR, DBTYPE_WSTR, DBTYPE_BYTES, or one of these indicators combined with DBTYPE_BYREF, the provider retrieves the length from the consumer's buffer.

For all other data types, the provider ignores the length. The address from which the length value is retrieved is calculated from the buffer address passed to the method and the obLength element of the binding.

  4. Retrieves the data value from the consumer's buffer. The address from which the data value is retrieved is calculated from the buffer address passed to the method and the obValue element of the binding.
    • Variable-length data types—The provider retrieves the number of bytes of variable-length data as specified by the length, up to cbMaxLen bytes. It is generally a consumer programming error if the length is greater than cbMaxLen. If no length is bound, the provider retrieves strings up to the first null-termination character; this is an error for DBTYPE_BYTES.

    • Fixed-length data types—The provider retrieves the number of bytes of fixed-length data from the consumer's buffer based on the size of the data type; it ignores cbMaxLen.

      If the type indicator is DBTYPE_IUNKNOWN or DBTYPE_IDISPATCH, the provider retrieves a pointer to an interface on the object from the consumer's buffer. For information about how the provider retrieves the object itself, see Chapter 7, "BLOBs and OLE Objects."

      If the type indicator is DBTYPE_BSTR, the provider retrieves the pointer to the BSTR from the consumer's buffer and the BSTR from the memory to which this pointer points. The provider ignores cbMaxLen; that is, it does not truncate the BSTR to cbMaxLen bytes.

    • DBTYPE_BYREF—The provider retrieves a pointer to the data from the consumer's buffer and the data from the memory to which this pointer points. The provider retrieves the data from this memory as if it were retrieving it directly from the consumer's buffer, except that it always ignores cbMaxLen, even when DBTYPE_BYREF is combined with DBTYPE_STR, DBTYPE_WSTR, or DBTYPE_BYTES.

    • DBTYPE_VECTOR—The provider retrieves a pointer to an array of data and a count of the number of elements in the array from a DBVECTOR structure in the consumer's buffer. It retrieves the data itself from the array to which this pointer points. The provider retrieves the data from this memory as if it were retrieving it directly from the consumer's buffer.

    • DBTYPE_ARRAY—The provider retrieves a pointer to a SAFEARRAY from the consumer's buffer. It retrieves the data from the SAFEARRAY to which this pointer points.
  5. Converts the data from the type that is stored in the consumer's buffer, as specified by the wType element of the binding, to the type of the column or input parameter.

If an error occurs while converting the data, the provider sets the status accordingly and proceeds to the next column or output parameter. For a list of status values that describe conversion errors, see "Status Values Used When Setting Data" in "Status," earlier in this chapter. For more information about converting data, see "Data Type Conversion Rules" in Appendix A.

If the provider encounters an error while retrieving a column or input parameter value, it sets the status value of that column or input parameter. Depending on the provider, it does one of the following:

  • Stops processing and backs out any changes already made.

  • Stops processing and leaves any changes already made.

  • Continues processing some or all of the remaining columns or input parameters.

If the provider stops processing, it must set the status value of any column or input parameter that was not in error and was not successfully set to DBSTATUS_E_UNAVAILABLE. This way, the consumer can determine which column or input parameter values were valid, which were invalid, and which were not validated.

When setting column data, except when seeking a key value or setting the index range, the provider then produces one of the following return codes:

  • DB_S_ERRORSOCCURRED—The method successfully set data for at least one column.

  • DB_E_ERRORSOCCURRED—The method did not successfully set data for any columns.

When setting input parameter data, seeking a key value, or setting the index range, the provider then returns DB_E_ERRORSOCCURRED.

The consumer checks the status values to determine the columns or input parameters for which data was successfully set. If the consumer did not bind a status value for a column or input parameter and a method returns DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED, it must be assumed that the column or input parameter value was not successfully set.


BLOBs and OLE Objects

Overview of BLOBs and OLE Objects

Rowsets must support the efficient storage and retrieval of binary large objects (BLOBs) and OLE objects. To a rowset, a BLOB or OLE object is a large sequence of uninterpreted bytes that a consumer stores in a column. The consumer is entirely responsible for interpreting the contents of the BLOB or OLE object. The provider is responsible only for providing the mechanism or mechanisms with which the consumer stores and retrieves BLOBs and OLE objects.

This chapter discusses the methods of accessing BLOB data as in-memory data, storage objects, and columns. Where in-memory data is treated as a sequence of bytes sent or retrieved in one piece, a storage object, which is the rowset that exposes storage interfaces, is retrieved or sent in pieces. The rows in the rowset act as containers of the storage objects. Limitations and lifetimes of storage objects are also discussed. OLE objects can also be stored in columns in a rowset, and are known as IPersist* objects. The manipulation, update semantics, and transaction considerations of IPersist* objects are explained.

For more information on | Go to
Accessing BLOB data | “Accessing BLOB Data” in this chapter
Column data | “Creating BLOB Columns” in this chapter
In-memory data | “BLOBs as In-Memory Data” in this chapter
Storage object | “BLOBs as Storage Objects” in this chapter
Rowsets | Chapter 4, “Rowsets”
Limitations and lifetimes of storage objects | “Limitations of Storage Objects” in this chapter “Lifetime of Storage Objects” in this chapter
IPersist* objects | “IPersist* Objects” in this chapter

BLOBs and OLE objects fall into the following categories:

  • BLOB—An uninterpreted sequence of bytes or characters. There is no behavior encapsulated in a BLOB. All the logic for interpreting a BLOB is in the consumer. The type indicator of a BLOB column is DBTYPE_BYTES, DBTYPE_STR, or DBTYPE_WSTR.

  • IPersist* object—An OLE object supporting IPersistStream, IPersistStreamInit, or IPersistStorage. The type indicator of an IPersist* column is DBTYPE_IUNKNOWN.

  • Non-IPersist* object—A COM object supporting some persistence model other than IPersistStream, IPersistStreamInit, or IPersistStorage. These are not supported in OLE DB, Version 1.5.

  • Abstract Data Type (ADT)—An encapsulated object supporting a behavior not in the form of a COM interface. These are not supported in OLE DB, Version 1.5.

A consumer determines what types of BLOBs and OLE objects a provider supports by calling IDBProperties::GetProperties with the DBPROP_OLEOBJECTS property.


Accessing BLOB Data

BLOBs can be accessed as in-memory data, that is, a sequence of bytes sent or retrieved in one piece; or as a storage object, that is, through ISequentialStream, IStream, IStorage, or ILockBytes. The method a consumer uses depends on the setting of the DBCOLUMNFLAGS_ISLONG flag returned by IColumnsInfo::GetColumnInfo and the length of the BLOB data.

The provider sets the DBCOLUMNFLAGS_ISLONG flag to inform the consumer that the provider supports data access through a storage interface as the preferred way to access the BLOB data. Although the consumer can still choose to access the BLOB as in-memory data, there may be provider-specific problems in doing so. For example, the BLOB might be truncated due to machine limits on memory or IRowset::GetData might fail if called more than once for the BLOB.

If the DBCOLUMNFLAGS_ISLONG flag is not set, the consumer can safely access the BLOB as in-memory data. It is provider specific whether the provider supports reading such data through a storage interface. In general, consumers allocate sufficient memory to hold such columns in their entirety and treat them as in-memory data.


Creating BLOB Columns

To create a BLOB column in a table, the consumer either uses a text command or calls CreateTable or AddColumn in ITableDefinition. In either case, it passes the maximum length in bytes (for binary data) or characters (for character data) of the BLOB column to the provider. Whether the column will best be accessed as in-memory data or as a storage object depends on the value of the IS_LONG column in the PROVIDER_TYPES schema rowset for the data type. The value in this column determines the setting of the DBCOLUMNFLAGS_ISLONG flag returned by IColumnsInfo::GetColumnInfo. For more information, see "Accessing BLOB Data" earlier in this chapter.


BLOBs as In-Memory Data

When a BLOB is treated as in-memory data, it is sent or retrieved in a single piece of memory. If the consumer allocates the memory for the BLOB, it usually binds the column with a type indicator of DBTYPE_BYTES, DBTYPE_STR, or DBTYPE_WSTR, depending on the data contained in the BLOB. When getting data, if the consumer wants the provider to allocate the memory for the BLOB, it usually binds the column with one of the preceding type indicators combined with DBTYPE_BYREF.

To get the BLOB data, the consumer calls a method such as IRowset::GetData with an accessor containing this binding, and the provider returns the entire contents of the BLOB to the consumer. To set the BLOB data, the consumer calls a method such as IRowsetChange::SetData with an accessor containing this binding and sends the entire contents of the BLOB to the provider. This is no different from binding and getting or setting data in other columns, such as getting or setting data in an integer column. If you get or set character data in this manner, the data is null terminated.

For example, the following code retrieves a 5000-byte BLOB and stores it in consumer memory:

#include<oledb.h>

IMalloc* pIMalloc;
IAccessor* pIAccessor;
IRowset* pIRowset;

int main() {
 HACCESSOR  hAccessor;
 DBBINDSTATUS rgStatus[1];
 DBBINDING  rgBinding[1] = {
  1,              // Column 1
  0,              // Offset to data
  5000,             // Offset to length
  5004,             // Offset to status
  NULL,             // No type info
  NULL,             // Not an object
  NULL,             // No binding extensions
  DBPART_VALUE|DBPART_LENGTH|DBPART_STATUS, // Bind value, length, status
  DBMEMOWNER_CLIENTOWNED,       // Memory owned by consumer
  DBPARAMIO_NOTPARAM,         // Not a parameter
  5000,             // Max length
  0 ,             // Reserved
  DBTYPE_BYTES,           // Type DBTYPE_BYTES
  0,              // Precision not applicable
  0               // Scale not applicable
 };

 pIRowset->QueryInterface(IID_IAccessor, (void**) &pIAccessor);
 pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, rgBinding,
        (5000 + sizeof(ULONG) + sizeof(DBSTATUS)), &hAccessor, rgStatus);
 pIAccessor->Release();

 // Allocate memory for the in-memory data. The first 5000 bytes are for the data,
 // the next sizeof(ULONG) bytes are for the length, and the final sizeof(DBSTATUS)
 // bytes are for the status.

 void * pData = pIMalloc->Alloc(5000 + sizeof(ULONG) + sizeof(DBSTATUS));

 // Get the next row, get the data in that row, and process the data. Assume the
 // length of the data is known to be <=5000 bytes, so no need to check truncation.
 HROW* rghRows = NULL;
 ULONG cRows;

 pIRowset->GetNextRows(NULL, 0, 1, &cRows, &rghRows);
 pIRowset->GetData(rghRows[0], hAccessor, pData);

 if ((DBSTATUS)(((BYTE*) pData)[rgBinding[0].obStatus]) == DBSTATUS_S_ISNULL) {
  // Process NULL data
 } else if ((DBSTATUS)((BYTE*)pData)[rgBinding[0].obStatus] == DBSTATUS_S_OK) {
  // Process data.  Length is (ULONG)pData[rgBinding[0].obLength].
 }

 pIMalloc->Free(rghRows);

};


BLOBs as Storage Objects

When a BLOB is treated as a storage object, you can get or set it in pieces using ISequentialStream, IStream, IStorage, or ILockBytes, collectively known as the storage interfaces. The rowset acts as a factory for the objects that expose these interfaces, which are collectively known as storage objects.

The rows in the rowset act as containers of the storage objects. The relationship between a storage object and the row that contains it is the same as the relationship between an IStream or IStorage object and its parent IStorage object.

This relationship dictates the behavior of the storage object with respect to its containing row in many circumstances. For example, the Structured Storage specification states that, when a parent IStorage is released, all child IStorage and IStream objects are invalidated. Similarly, if a row handle is released with IRowset::ReleaseRows, any storage objects open in that row enter a zombie state, where releasing the object generally is the only valid consumer action. Any questions not specifically answered in this specification about the relationship between rows and storage objects can be answered by looking at the Structured Storage specification.

The following table shows the rowset methods and interfaces that are analogous to IStorage methods and modes.

Analogous methods, modes, and interfaces for IStorage and IRowset

IStorage method or mode | Rowset method or interface
Transacted mode | Delayed update mode
Direct mode | Immediate update mode
IStorage::Commit | IRowsetUpdate::Update
IStorage::Revert | IRowsetUpdate::Undo
IStorage::CreateStream, IStorage::OpenStream, IStorage::CreateStorage, IStorage::OpenStorage IRowset::GetData

By default, BLOB columns are deferred. In most cases, their data is not fetched and storage objects are not created until GetData is called. Methods that fetch rows, such as IRowset::GetNextRows, do not usually return data for BLOB columns in the rowset's copy of the row.

For more information, see "Getting and Setting BLOB Data with Storage Objects" later in this chapter and "Deferred Columns" in Chapter 4.
IStorage::DestroyElement | IRowsetChange::SetData. For more information, see "Getting and Setting BLOB Data with Storage Objects," later in this chapter.

The only significant difference between a parent IStorage object in the Structured Storage model and a row in a rowset is that a row can contain a storage object that exposes ISequentialStream, IStream, IStorage, or ILockBytes, whereas a parent IStorage object can expose only a child that exposes IStorage or IStream. For purposes of the analogy between Structured Storage and rows, ISequentialStream and ILockBytes storage objects are usually treated as IStream storage objects.


Limitations of Storage Objects

Storage objects are subject to the following limitations. Whether these limitations always exist is provider specific.

  • Number of storage objects—Some providers can support only one open storage object at a time, while others can support multiple open storage objects. If the provider supports only one open storage object at a time, any method that attempts to open a second storage object returns a status of DBSTATUS_E_CANTCREATE for the column or parameter, regardless of whether the objects are constructed over the same column, different columns in the same row, or different rows. A consumer determines whether a provider supports one or many storage objects by calling IDBProperties::GetProperties for the DBPROP_MULTIPLESTORAGEOBJECTS property.

  • Exclusionary behavior of storage objects—Because of the way storage objects are implemented by some providers, using a storage object might prevent the use of other methods on the rowset. That is, after a storage object is created and before it is released, methods other than those on the storage object might return E_UNEXPECTED. A consumer determines whether storage objects might prevent other methods by calling IDBProperties::GetProperties in the DBPROP_BLOCKINGSTORAGEOBJECTS property.

  • Length of storage object must be known—Some providers need to know the number of bytes of BLOB data that will be sent before any of the data is sent. This is usually related to a similar requirement in the underlying DBMS. For more information, see "Getting and Setting BLOB Data with Storage Objects" later in this chapter.


Lifetime of Storage Objects

The consumer releases a storage object created by the provider by calling Release on that object. If a provider supports DBPROP_MULTIPLESTORAGEOBJECTS = VT_FALSE, then the consumer must always call Release on the storage object to guarantee that a new storage object on any row can be opened. In addition, the storage object may enter a zombie state if one of the following events occurs:

  • The consumer calls Release to release the rowset containing the row.

  • The consumer calls IRowset::ReleaseRows to release the row containing the storage object.

  • The consumer calls ITransaction::Commit or ITransaction::Abort and the retaining flag is set so that the rowset enters a zombie state.

  • The consumer deletes the row containing the storage object and transmits the deletion to the data source.

When a storage object enters a zombie state, it is not automatically released. Instead, if the consumer calls any method on the storage object except Release, that method returns E_UNEXPECTED. It is the consumer's responsibility to release the storage object, even if it is in a zombie state.


Getting and Setting BLOB Data with Storage Objects

The following sections discuss how to bind, get, and set BLOB data using storage objects.

Binding BLOB Data as a Storage Object

To bind to BLOB data as a storage object, a consumer creates an accessor that includes a binding to the BLOB column. The consumer performs the following actions:

  1. Sets the wType element of the DBBINDING structure for the BLOB column to DBTYPE_IUNKNOWN.

  2. Sets the iid element of the DBOBJECT structure in the binding to IID_ISequentialStream, IID_IStream, IID_IStorage, or IID_ILockBytes. If the consumer specifies an interface that is not supported by the provider, the provider returns E_NOINTERFACE when it validates the accessor; this can occur in IAccessor::CreateAccessor or a method that uses the accessor, such as IRowset::GetData. To determine which interfaces are supported by the provider, the consumer calls IDBProperties::GetProperties with the DBPROP_STRUCTUREDSTORAGE property.

  3. Sets the dwFlags element of the DBOBJECT structure in the binding. If the consumer specifies any invalid or unsupported flags, the provider returns DB_E_BADSTORAGEFLAG when it validates the accessor; this can occur in CreateAccessor or a method that uses the accessor, such as GetData.

Getting BLOB Data with Storage Objects

To get BLOB data using a storage object, a consumer performs the following actions:

  1. Creates an accessor that includes a binding for the column. For more information, see "Binding BLOB Data as a Storage Object" earlier in this section.

  2. Calls IRowset::GetData, IRowsetRefresh::GetLastVisibleData, or IRowsetUpdate::GetOriginalData with this accessor.

    The provider creates a storage object over the BLOB's data and returns a pointer to the requested storage interface (ISequentialStream, IStream, IStorage, or ILockBytes) on this object. If the provider supports only a single open storage object at a time and another storage object is open, the method returns a status of DBSTATUS_E_CANTCREATE for the column.

  3. Calls methods on the storage interface to get the BLOB's data, such as ISequentialStream::Read, IStream::Read, ILockBytes::ReadAt, or IStorage::OpenStream. If you use these methods to get character data, it is not null terminated.

If the consumer calls GetData, GetLastVisibleData, or GetOriginalData multiple times for the BLOB column, the provider returns distinct pointers to storage interfaces on each call. This is similar to opening a file multiple times and returning a different file handle each time. It is the consumer's responsibility to call Release on each of these storage interfaces separately.

For example, the following code binds to a BLOB column and uses ISequentialStream::Read to get the data:

#include<oledb.h>

IRowset* pIRowset;
IAccessor* pIAccessor;
IMalloc* pIMalloc;

int main() {
 // Assume the consumer has a pointer (pIRowset) that points to the rowset. Create an
 // accessor for the BLOB column. Assume it is column 1.

 HACCESSOR  hAccessor;
 DBBINDSTATUS rgStatus[1];
 DBOBJECT   ObjectStruct;
 DBBINDING  rgBinding[1] = {
  1,             // Column 1
  0,             // Offset to data
  0,             // Ignore length field
  sizeof(IUnknown *),        // Offset to status field
  NULL,            // No type info
  &ObjectStruct,         // Object structure
  NULL,            // Ignore  binding extensions
  DBPART_VALUE|DBPART_STATUS,      // Bind value and status
  DBMEMOWNER_CLIENTOWNED,      // Consumer owned memory
  DBPARAMIO_NOTPARAM,        // Not a parameter
  0,             // Ignore size of data
  0,             // Reserved
  DBTYPE_IUNKNOWN,         // Type DBTYPE_IUNKNOWN
  0,             // Precision not applicable
  0              // Scale not applicable
 } ;

 // Set the elements in the object structure so that the provider creates a readable
 // ISequentialStream object over the column. The consumer will read data from this
 // object.
 ObjectStruct.dwFlags = STGM_READ;
 ObjectStruct.iid   = IID_ISequentialStream;

 pIRowset->QueryInterface(IID_IAccessor, (void**) &pIAccessor);
 pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, rgBinding,
         sizeof(IUnknown *) + sizeof(ULONG), &hAccessor, rgStatus);
 pIAccessor->Release();

 // Allocate memory for the returned pointer and the status field. The first
 // sizeof(IUnknown*) bytes are for the pointer to the object and the next
 // sizeof(ULONG) bytes are for the status.

 void * pData = pIMalloc->Alloc(sizeof(IUnknown *) + sizeof(ULONG));

 // Get the next row, get the pointer to ISequentialStream.
 HROW * rghRows = NULL;
 ULONG  cRows;

 pIRowset->GetNextRows(NULL, 0, 1, &cRows, &rghRows);
 pIRowset->GetData(rghRows[0], hAccessor, pData);

 // Read and process 5000 bytes at a time.
 BYTE  rgBuffer[5000];
 ULONG cb;

 if ((ULONG)((BYTE*)pData)[rgBinding[0].obStatus] == DBSTATUS_S_ISNULL) {
  // Process NULL data
 } else if ((ULONG)((BYTE *)pData)[rgBinding[0].obStatus] == DBSTATUS_S_OK) {
  do {
   (*((ISequentialStream **)pData))->Read(rgBuffer, sizeof(rgBuffer), &cb);
   if (cb > 0) {
    ; // Process data
   }
  } while (cb >= sizeof(rgBuffer));
 };

 pIMalloc->Free(rghRows);
};

Setting BLOB Data with Storage Objects

There are two ways a consumer can set BLOB data using a storage object:

  • By writing data directly to the provider's storage object—The provider creates a storage object over the BLOB column and returns a pointer to this storage object to the consumer. The consumer writes data directly to this storage object.

  • By passing a pointer to a consumer storage object—The consumer creates a storage object containing the data and passes a pointer to this storage object to the provider. The provider then reads data from the consumer storage object and writes it to the BLOB column.

Writing Data Directly to the Provider's Storage Object

To write data directly to the provider's storage object, the consumer:

  1. Creates an accessor that binds the value of the BLOB column. For more information, see "Binding BLOB Data as a Storage Object" earlier in this section.

  2. Calls IRowset::GetData with the accessor that binds the value of the BLOB column.

    The provider creates a storage object over the BLOB's data and returns a pointer to the requested storage interface (ISequentialStream, IStream, IStorage, or ILockBytes) on this object. If the provider supports only a single open storage object at a time and another storage object is open, the method returns a status of DBSTATUS_E_CANTCREATE for the column.

  3. Calls a method on the storage interface to set data, such as ISequentialStream::Write, IStream::Write, or ILockBytes::WriteAt. Character data set with these methods is not null terminated.

    If the storage object is transacted (that is, the STGM_TRANSACTED flag is set in the dwFlags element of the DBOBJECT structure in the binding), the storage object does not publish the changes to the containing rowset until the consumer calls Commit on the storage interface. If the storage object is not transacted (that is, the STGM_DIRECT flag is set), the storage object publishes the changes to the containing rowset when the consumer calls a method on the storage interface to set the changes.

For a similar code example, see "Getting BLOB Data with Storage Objects" earlier in this section. The code for setting BLOB data is essentially the same, except that the consumer must set the dwFlags element of the ObjectStruct structure so that it can write to the storage object. Also, the consumer calls ISequentialStream::Write instead of ISequentialStream::Read.

Passing a Pointer to a Consumer Storage Object

To pass a pointer to its own storage object, the consumer:

  1. Creates an accessor that binds the value of the BLOB column. For more information, see "Binding BLOB Data as a Storage Object" earlier in this section.

    If the consumer's storage object exposes ISequentialStream and the provider needs to know the number of bytes of BLOB data that will be sent before any of the data is sent, this accessor must also bind the length of the BLOB column. For more information, see "Limitations of Storage Objects" earlier in this chapter.

  2. Calls IRowsetChange::SetData or IRowsetChange::InsertRow with the accessor that binds the BLOB column. It passes a pointer to a storage interface on the consumer's storage object.

Comments

If the provider already has a storage object open over the BLOB's data, the method returns a status of DBSTATUS_E_CANTCREATE for the column. Otherwise, the provider copies the data from the consumer's storage object to the BLOB column. This is equivalent to the provider destroying the existing data, creating a new (empty) storage object over the BLOB column, repeatedly calling methods to read data from the consumer's storage object and write data to the storage object over the BLOB column, and, if the storage mode is transacted, committing the changes on the storage object over the BLOB column. Character data set in this manner is not null terminated.

If the provider needs to know the number of bytes of BLOB data that will be sent before any of the data is sent, it retrieves this number with the IStream::Stat, IStorage::Stat, or ILockBytes::Stat method on the consumer's storage object (if the consumer's storage object exposes IStream, IStorage, or ILockBytes) or from the length part of the binding (if the consumer's storage object exposes ISequentialStream). If the consumer binds the length and the provider does not need to know this information or the consumer's storage object exposes IStream, IStorage, or ILockBytes, the provider ignores the bound length value.

When the provider has finished using the consumer's storage object, it calls IUnknown::Release to release the pointer. After SetData or InsertRow returns, the provider must not hold any pointers or reference counts on the consumer's storage object. If the consumer wants to ensure access to its storage object after SetData returns, it must call IUnknown::AddRef on the pointer before calling SetData.

To set the BLOB column to a zero-length value, the consumer sets the status flag to DBSTATUS_S_OK. It then either passes a null pointer (instead of a pointer to its own storage object) or passes a pointer to a storage object that contains no data. The provider sets the column to a zero-length value. This is different from setting the status value for the column to DBSTATUS_S_ISNULL. If the consumer sets the column value to NULL and then calls GetData, GetData returns a status value of DBSTATUS_S_ISNULL. If the consumer sets the column to a zero-length value and then calls GetData, GetData returns a pointer to a storage object that contains no data.

For example, the following code passes a pointer to a different ISequentialStream object to SetData to overwrite the existing value:

#include<oledb.h>

IRowsetChange *   pIRowsetChange;
IAccessor *   pIAccessor;
ISequentialStream * pISeqStr;
HROW      hrow;

int main() {

 // Assume the consumer has a pointer (pIRowsetChange) that points to the rowset and
 // a pointer (pISeqStr) that points to an ISequentialStream object not in the
 // rowset. Create an accessor for the BLOB column. Assume it is column 1.

 HACCESSOR  hAccessor;
 DBBINDSTATUS rgStatus[1];
 DBOBJECT   ObjectStruct;
 DBBINDING  rgBinding[1] = {
  1,           // Column 1
  0,           // Offset to data
sizeof(IUnknown*),   // obLength length field

  0,           // Ignore status field
  NULL,          // No type info
  ObjectStruct,       // Object structure
  NULL,          // Ignore binding extension
  DBPART_VALUE|DBPART_LENGTH, // Bind value and length
  DBMEMOWNER_CLIENTOWNED,    // Consumer owned memory
  DBPARAMIO_NOTPARAM,      // Not a parameter binding
  0,           // Ignore maxlength
  0,           // Reserved
  DBTYPE_IUNKNOWN,       // Type DBTYPE_IUNKNOWN
  0,           // Precision not applicable
  0,           // Scale not applicable
 };

 // Set the elements in the object structure so that the provider creates a writable
 // ISequentialStream object over the column.  The provider will read data from the
 // ISequentialStream object passed to SetData and write it to this object.
 ObjectStruct.dwFlags = STGM_READ ;
 ObjectStruct.iid   = IID_ISequentialStream;

 pIRowsetChange->QueryInterface(IID_IAccessor, (void**) &pIAccessor);
 pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, rgBinding, sizeof(IUnknown *),
pIAccessor->Release();

//Setup pData row buffer
BYTE* pData=(BYTE*)pIMalloc->Alloc(sizeof(IUnknown*)+sizeof(ULONG));

//Value - pass ISequentialStream pointer to the provider
*(ISequentialStream**)(pData+rgBinding[0].obVaule)=pISeqStr;
//LENGTH - Some providers need to know the length of the stream ahead of time...
*(ULONG*)(pData+rgBinding[0].oblength)=5000;

//SetData - The provider will then do a ISequentialStream::Read
//on the pISeqStr pointer passed in... pIRowsetChange->SetData(hrow, hAccessor, &pISeqStr);
return 0;
} ;

Getting and Setting BLOB Data with Storage Objects with Parameters

Consumers get and set BLOBs and OLE objects in command parameters in a manner similar to accessing BLOB columns.

To get BLOB data using a storage object as output parameters, a consumer performs the following actions:

  1. Calls ICommandText::SetCommandText, calls ICommandPrepare::Prepare, and calls ICommandWithParameters::GetParameterInfo to get information about parameters.

  2. Creates an accessor that includes a binding for the column. For more information, see "Binding BLOB Data as a Storage Object" earlier in this section.

  3. Calls ICommand::Execute, passing the accessor obtained in Step 2 in the hAccessor element of DBPARAMS. The storage object interface pointer will be returned in the value part of pData. The timing of the availability of the storage object depends on the value of the property DBPROP_OUTPUTPARAMETERAVAILABILITY.

To set BLOB data using a storage object as input parameters, a consumer performs the following actions:

  1. Calls ICommandText::SetCommandText, calls ICommandPrepare::Prepare, and calls ICommandWithParameters::GetParameterInfo to get information about parameters.

  2. Creates an accessor that includes a binding for the column. For more information, see "Binding BLOB Data as a Storage Object" earlier in this section.

  3. Builds the storage object.

  4. Calls ICommand::Execute, passing the accessor obtained in Step 2 and the storage object interface pointer from Step 3 in the hAccessor and value part of pData in DBPARAMS, respectively.


Storage Objects and Rowset Update Semantics

When does the data set in a storage object become visible to other transactions operating at the Read Uncommitted isolation level? For providers built over database servers, this question can also be phrased as, "When does data set in a storage object become visible at the data source?"

The rules for storage objects are similar to the rules for data in other columns. That is, if the rowset is in immediate update mode, data made available to the rowset is immediately made available to Read Uncommitted transactions. If the rowset is in delayed update mode, data made available to the rowset is not made available to Read Uncommitted transactions until the consumer calls IRowsetUpdate::Update.

However, storage objects differ in two ways from other, nonstorage columns in the rowset:

  • Storage mode—Storage objects can have another layer of buffering based on the storage mode in which they operate. If the storage object operates in transacted mode, data is not available to the rowset until the consumer calls Commit on the storage interface. If the storage object operates in direct mode, data is immediately available to the rowset when the consumer sets it in the storage object.

  • Ignore delayed update mode—Because storage objects can be large, using them in delayed update mode can be expensive because the provider must locally cache any changes to them until Update is called. Consumers need to control this expense, so the DBPROP_DELAYSTORAGEOBJECTS property controls whether storage objects are always in immediate update mode, regardless of whether the rowset is in immediate or delayed update mode. For some providers this is a read-only property. See the following figure.

Storage object boundary controls

The following table shows the sequence of calls needed to make data set in a storage object visible to other transactions operating at the Read Uncommitted isolation level. In this table, Write refers collectively to any method on a storage interface that sets data, such as ISequentialStream::Write, IStream::Write, or ILockBytes::WriteAt, and SetData refers collectively to IRowsetChange::SetData and IRowsetChange::InsertRow. ILockBytes storage objects are always in Direct mode.

Call sequence required for storage object visibility

Storage mode | Update mode | DBPROP_DELAY STORAGEOBJECTS | Calls needed to expose data to other transactions running at Read Uncommitted
Direct | Immediate | Not applicable | Write – or – SetData
Direct | Delayed | VARIANT_FALSE | Write – or – SetData
Direct | Delayed | VARIANT_TRUE | Write Update – or – SetData Update
Transacted | Immediate | Not applicable | Write Commit on storage interface – or – SetData
Transacted | Delayed | VARIANT_FALSE | Write Commit on storage interface – or – SetData
Transacted | Delayed | VARIANT_TRUE | Write Commit on storage interface Update – or – SetData Update

When the rowset is in delayed update mode, the consumer cannot undo any pending changes made through a storage object if that object is still open. That is, if the provider creates a storage object over the BLOB and returns a pointer to it through IRowset::GetData, the consumer must release this storage object before calling IRowsetUpdate::Undo. If the consumer fails to release the storage object, Undo returns a status of DBROWSTATUS_E_OBJECTOPEN for the row.


Storage Objects and Transactions

Whether storage objects are transacted on the data source depends on the DBPROP_TRANSACTEDOBJECT property, which can be set on a per-column basis. If the value of this property is VARIANT_TRUE, then any storage object created on the column is transacted. That is, when a consumer calls ITransaction::Commit and the provider commits all data visible to the rowset, this includes data made visible to the rowset through storage objects. When a consumer calls ITransaction::Abort and the provider rolls back all data visible to the rowset, this includes data made visible to the rowset through storage objects. For information about what data in storage objects is visible to the rowset, see "Storage Objects and Rowset Update Semantics" earlier in this chapter.

Whether uncommitted data in transacted storage objects remains valid and whether the storage objects themselves remain valid when the transaction is committed or aborted depends on the retaining flag (fRetaining) used in the call to Commit or Abort. The retaining flag set to TRUE implicitly begins a new unit of work. If a commit is retaining, then the storage objects and any uncommitted data in them remain valid. If an abort is retaining, then the storage objects remain valid, but any uncommitted data in them is lost; furthermore, the provider must synchronize the object with its state in the data source after the transaction was aborted there. If the rowset is not preserved after a commit or abort, then the storage objects enter zombie states and any uncommitted data in them is lost; the consumer can only call Release on such objects—all other methods return E_UNEXPECTED. Whether the rowset is preserved depends on the DBPROP_COMMITPRESERVE and DBPROP_ABORTPRESERVE properties. For more information on committing and aborting a transaction, see “Transaction Objects” in Chapter 12, “Transactions.”

If the value of DBPROP_TRANSACTEDOBJECT is VARIANT_FALSE, then any storage object created on the column is not transacted. That is, all changes to the storage object are permanent once they are made visible to the data source. Whether a nontransacted storage object remains valid after Commit or Abort is called depends on the implementation of the rowset and the object itself.


IPersist* Objects

OLE objects can be stored in columns in a rowset. Such objects are required to expose either IPersistStream, IPersistStreamInit, or IPersistStorage. The rowset has to know how to create instances of these objects, so it must store the objects’ class IDs. How this is done is provider specific. For example, the provider might do one of the following:

  • Store a separate class ID in the data source with each object (as might be the case for a column that can store objects that use arbitrary class IDs).

  • Store a single class ID for an entire column in the metadata for that column (as might be the case when a column can store only a single type of object). See DBCOLUMN_CLSID in IColumnsRowset::GetColumnsRowset.

  • Not store a class ID at all and always use a default class ID (as might be the case when the provider can support only a single type of object).


Getting and Setting IPersist* Objects

The following sections explain how to get and set IPersist* objects.

Getting IPersist* Objects

To get an IPersist* object from a column, a consumer performs the following actions:

  1. Creates an accessor that includes a binding to the column. It sets the wType element of the DBBINDING structure to DBTYPE_IUNKNOWN and sets the iid element of the DBOBJECT structure in the binding to the needed interface.

  2. Calls IRowset::GetData, IRowsetRefresh::GetLastVisibleData, or IRowsetUpdate::GetOriginalData.

If the object is not currently loaded, the provider, in turn:

  1. Creates an instance of the IPersist* object and obtains an IPersistStorage, IPersistStreamInit, or IPersistStream pointer on it. For example, it might call CoCreateInstance with rclsid set to the class ID of the object and riid set to IID_IPersistStorage. How the provider determines the class ID of the object is provider-specific. For more information, see "IPersist* Objects," earlier in this chapter.

  2. Creates an IStream or IStorage instance over the data in the column. If there is no data in the column, as is the case for a newly created row, the provider then calls InitNew on either IPersistStorage or IPersistStreamInit.

    If the provider supports only a single open storage object at a time and another storage object is open, the method returns a status of DBSTATUS_E_CANTCREATE for the column.

  3. Loads the IPersist* object by calling Load on the IPersist* interface on the object and passing the interface pointer obtained in step 2.

  4. Performs the operations required to enable future calls to the method to detect that the object has already been loaded (for example, using a moniker to the object) and to return a pointer on the loaded object.

  5. Returns a pointer to the requested interface to the consumer. If the object does not support the requested interface, the method returns E_NOINTERFACE. After the method returns, the rowset does not hold any reference counts on the object.

If the object is currently loaded—which the provider can determine by virtue of the operations performed in step 4 in the preceding procedure—the provider performs the following action:

  • Returns a pointer to the requested interface to the consumer.

  • If the object does not support this interface, the method returns E_NOINTERFACE.

If any errors occur during these procedures, the provider ensures that no objects remain instantiated when GetData, GetLastVisibleData, or GetOriginalData returns.

Setting IPersist* Objects

To set an IPersist* object in a column, a consumer uses one of two procedures. In the first procedure, the consumer performs the following actions:

  1. Calls IRowset::GetData to retrieve an IStorage or IStream pointer over the object's data. It does this in the same way it retrieves an IStorage or IStream pointer over BLOB data. The provider does not load the object, as is the case when the consumer calls GetData and requests a different interface, such as IPersistStorage. If the provider supports only one open storage object at a time and another storage object is open, the method returns a status of DBSTATUS_E_CANTCREATE for the column.

  2. Calls Save on the IPersist* interface of the IPersist* object and passes it the storage interface pointer retrieved in step 1.

  3. Calls IPersistStorage::SaveCompleted on the IPersist* object if IPersistStorage is used. The consumer should be careful if it sets the pStgNew parameter to a non-null pointer, because the provider does not dissociate the column's storage object from the IPersist* object.

This procedure should be used only by consumers that know what kind of IPersist* objects should be stored in a column. Because the storage object, which is implemented by the rowset, does not have a pointer to the IPersist* object, the rowset cannot determine whether the IPersist* object uses a class ID that matches the class ID used by the objects in the column. The possibility therefore arises that the consumer could store an object in the column of a type of which the rowset will not be able to create an instance later.

  • In the second procedure, the consumer performs the following action. Note that this procedure can be used by any consumer. It should be used by consumers that do not know what kind of IPersist* objects can be stored in the column because the provider can check the class ID of the object before storing it.Calls IRowsetChange::SetData or IRowsetChange::InsertRow and passes a pointer to an interface on the IPersist* object.

The provider performs the following actions, in turn:

  1. Creates a new storage object for the column and retrieves a storage interface pointer on that object.

    If there is an existing storage object on the column that the provider cannot release, SetData or InsertRow returns a status of DBSTATUS_E_CANTCREATE for the column. For example, this occurs if there is an IPersist* object loaded on the column. If the provider supports only one open storage object at a time and a storage object is open over another column or row, the method returns a status of DBSTATUS_E_CANTCREATE for the column.

  2. (Optional) Calls IPersist::GetClassID on the IPersist* object to verify that the specified IPersist* object is of a type that can be stored in the column.

  3. Calls Save on the IPersist* interface of the IPersist* object and passes it the storage interface pointer retrieved in step 1.

    If the provider calls IPersistStorage::Save, it sets the fSameAsLoad argument to FALSE; if the provider calls IPersistStream::Save or IPersistStreamInit::Save, it sets the fClearDirty flag to TRUE.

  4. Calls IPersistStorage::SaveCompleted on the IPersist* object if IPersistStorage is used. The provider sets the pStgNew argument to a null pointer.

  5. Performs the operations required to enable future calls to GetData, GetLastVisibleData, or GetOriginalData to detect whether the object is loaded and to return a pointer on the loaded object.

    After SetData or InsertRow returns, the rowset does not hold any reference counts on the IPersist* object.


Lifetime of IPersist* Objects

An IPersist* object instance created by the provider remains valid until it is released. However, if the row associated with the IPersist* object is released, the storage object associated with the IPersist* object enters a zombie state and the IPersist* object will no longer be able to use it.


IPersist* Objects, Rowset Update Semantics, and ITransaction

IPersist* objects provide yet another layer between the object and the data on the data source. Data stored in an IPersist* object is not exposed to its underlying storage object—and hence, to other transactions—until it is persisted with IPersist*. After the data is persisted, the storage object to which it was persisted controls when it is available to the rowset and to other transactions. For more information, see "Storage Objects and Rowset Update Semantics" and "Storage Objects and Transactions" earlier in this chapter.

The methods on ITransaction can indirectly affect IPersist* objects; for example, committing or aborting a transaction might send into a zombie state the storage object associated with the IPersist* object. For more information, see "Storage Objects and Transactions" earlier in this chapter.


OLE Object Storage Systems

Providers can be built on top of OLE object storage systems. In this case, the columns of the rowset generally correspond to the properties of the objects, rather than to the objects themselves. One of these properties is known as the self property. The value stored in the column corresponding to the self property is the OLE object itself. That is, when the consumer gets the value of this column, it gets the OLE object to which the other properties in the rowset apply. For information about how the consumer gets or sets this object, see "Getting and Setting IPersist* Objects" earlier in this chapter.

In the column ID for this column, the guid element is DBCOL_SELFCOLUMNS and the pwszName or ulPropid element is assigned by the object (command or session) that created the rowset. The pwszName or ulPropid elements will be needed, for example, when joining two rows, each of which contains an OLE object and each of which therefore has a column corresponding to the self property.


Indexes

Overview of Indexes

An OLE DB index, also known as an index rowset, is a rowset built over an index in a data source. It is generally used in conjunction with a rowset built over a base table in the same data source. Each row of the index rowset contains a bookmark that points to a row in the base table rowset. Thus, the consumer can traverse the index rowset and use it to access rows in the base table rowset. This chapter discusses the interfaces, use, and structure of an index rowset. Index structures comprise either of two types: one or more columns that form a key value, or a column containing a bookmark. An index example is also provided.

For more information on | Go to
Data source | “OLE DB Components” in Chapter 1, “Overview of OLE DB”
Rowset | Chapter 4, “Rowsets”
Bookmark | “Bookmarks” in Chapter 4, “Rowsets”
Structure | “Structure of Index Rowsets” in this chapter
Use of indexes | “Using Index Rowsets” in this chapter
Example provided | “Index Example” in this chapter

Indexes:

CoType TIndex {
   [mandatory] interface IAccessor;
   [mandatory] interface IColumnsInfo;
   [mandatory] interface IConvertType;
   [mandatory] interface IRowset;
   [mandatory] interface IRowsetIndex;
   [mandatory] interface IRowsetInfo;
   [optional]  interface IRowsetChange;
   [optional]  interface IRowsetFind;
   [optional]  interface IRowsetIdentity;
   [optional]  interface IRowsetLocate;
   [optional]  interface IRowsetRefresh;
   [optional]  interface IRowsetScroll;
   [optional]  interface IRowsetUpdate;
   [optional]  interface IRowsetView;
   [optional]  interface ISupportErrorInfo;
};

The primary consumer for the IRowsetIndex interface is a query processor component that uses it during query execution. Although an SQL provider can expose IRowsetIndex by translating IRowsetIndex methods into SQL statements, this is not efficient. Instead, typical consumers of SQL providers should use the ICommand interface as the primary data access mechanism and rely on the query processor in the SQL provider to optimize access to data.

The primary index interface, IRowsetIndex, exposes the functionality required by file access methods based on ISAM and B+-trees. It does not support functionality, such as hashing, required by unordered indexes; R+-trees, required by access methods for spatial data; or signature files, required by access methods for text.

Index rowsets can be used in the following situations:

  • Reading records efficiently by means of a key, which is one or more columns whose values identify a row. The following are example SQL queries whose execution plans can exploit the capabilities of the IRowsetIndex interface:
    • Retrieving rows ordered by some column. For example, SELECT * FROM Table ORDER BY Table.x, where x is indexed.

    • Scanning records having a column value within a continuous range. For example, SELECT * FROM Table WHERE Table.x BETWEEN 4 AND 20.

    • Computing joins. For example, SELECT * FROM R INNER JOIN S ON R.x = S.x.

    • Retrieving distinct rows. For example, SELECT DISTINCT R.x FROM R, where R.x is indexed.

    • Positioning at a particular index entry, such as skipping over key entries within a range.
  • Supporting indexes with multicolumn keys. The values in each column can be in ascending or descending order.

  • Traversing indexes in ascending or descending order to support ORDER BY clauses.


Structure of Index Rowsets

A row in an index rowset consists of at least two entries: one or more columns that form a key value used to search in the index rowset, and a column containing a bookmark (primary key or row identifier). The bookmark points to the row in the corresponding base table rowset that contains the key value. The provider must guarantee that these bookmarks consistently identify a row in the base table rowset.

The key columns must occur in the order of most significant key column to least significant key column. The consumer can determine this order from the order in which column metadata is returned by IColumnsInfo::GetColumnInfo. The consumer can determine which columns are key columns from the DBINDEXCOLUMNDESC structure returned by IRowsetIndex::GetIndexInfo.

The bookmark that points to the base table rowset is not the self bookmark. That is, it is not the bookmark in column 0 that points to the row in the index rowset. Instead, it is a column with an ordinal greater than zero for which the DBCOLUMNFLAGS_ISBOOKMARK flag returned by GetColumnInfo is set.

The index rowset can contain other columns in addition to the key value columns and bookmark column. For example, it might contain a timestamp column. Such columns should be discussed in the documentation that describes the index provider. Although such columns are not barred from containing bookmarks, additional bookmark columns preclude the ability of generic query processors to use the index rowset. That is, such query processors are unable to determine which column contains the bookmark pointing to the base table rowset and which column contains a bookmark pointing elsewhere.


Using Index Rowsets

Index implementations vary in their support of certain capabilities. Before an index rowset is created, consumers can determine the capabilities of the underlying index by calling IDBSchemaRowset::GetRowset for the INDEXES schema rowset. They can determine the capabilities of an index rowset to which they already have an interface pointer by calling IRowsetIndex::GetIndexInfo.

To use an index rowset, a consumer usually performs the following actions:

  1. Creates an instance of a rowset over a base table and a rowset over an index designed for use with that base table.

  2. Calls IColumnsInfo::GetColumnInfo on the index rowset to determine which column contains the bookmarks that point to rows in the base table column.

    The ordinal of this column is greater than zero and the DBCOLUMNFLAGS_ISBOOKMARK flag is set on it. GetColumnInfo also returns the key columns in the order of most significant column to least significant column. The consumer can determine which columns are the key columns by inspecting the DBINDEXCOLUMNDESC structure returned by GetIndexInfo.

  3. (Optional) Calls IRowsetIndex::SetRange on the index rowset to set the range in which to search for key values in the index. If the consumer does not call SetRange, the index rowset will search the entire index.

  4. Calls IRowsetIndex::Seek on the index rowset to set the next fetch position to a specified key value. This key value must be in the range set in step 3, which is optional.

  5. Calls IRowset::GetNextRows on the index rowset to get the row or rows from the index starting with the key value specified in step 4. GetNextRows is limited to the range set in step 3, which is optional.

  6. Calls IRowset::GetData on the index rowset for each fetched row to get the bookmarks pointing to the base table rowset.

  7. Calls IRowsetLocate::GetRowsByBookmark on the base table rowset to retrieve the rows in that rowset that correspond to the index rows fetched in step 5. this occurs only if step 3, the call to IRowsetIndex::SetRange on the index rowset, was performed.

Because an index is a rowset, the consumer can treat it like any other rowset. For example, the consumer can call methods in IRowsetChange to add, delete, and update rows in the index.


Index Example

The following code accesses the Employees table through the Emp_LastName_Index index, using IRowsetIndex for the index and IRowsetLocate for the table.

The index has two columns. The first column, Emp_LastName, is of type DBTYPE_WSTR with a length of 30 characters and is the key column. The second column is of type DBTYPE_BYTES with a length of 4 characters and contains the bookmark for the Employees table. In the information returned by IColumnsInfo::GetColumnInfo for this column, the DBCOLUMNFLAGS_ISBOOKMARK is set.

The code sample performs the following actions:

  1. Initializes the database and creates a session (not shown).

  2. Obtains interfaces for the table and index. Only the latter is shown.

  3. Gets information about the column types of the index rowset (not shown).

  4. Establishes bindings for the table and index columns. Only the latter is shown.

  5. Creates accessors for the table and index rowsets. Only the latter is shown.

  6. Reads the index rowset and retrieves the corresponding row from the table rowset.
#include<oledb.h>
#include<stddef.h>

GetTableRowsetLocate(IOpenRowset* , BSTR, IRowsetLocate**);
PrintData(OLECHAR*, ULONG, HROW*);
IMalloc * pMalloc;  // pMalloc is the default memory allocator
HRESULT hr;

int main() {
 IOpenRowset  *pIOpenRowset = NULL;
 DBID    IndxId;
 IRowsetIndex *pIndex = NULL;
 IRowset    *pIndexRows = NULL;
 IRowsetLocate  *pIRSLocate =NULL;
 ULONG    *pColumns = NULL;
 DBCOLUMNINFO  **prgInfo;
 OLECHAR   **ppStringsBuffer;
 IColumnsInfo *pIndxColsInfo = NULL;
 IAccessor  *pIndxAccsr = NULL;
 HACCESSOR   hIndexAccBmk = DB_INVALID_HACCESSOR;

 // Initialize the database, create a session, and obtain, from the session object, a
 // pointer pIOpenRowset to an IOpenRowset interface. Code not shown.

 // Use IOpenRowset::OpenRowset to obtain a pointer to IRowsetIndex.
 // Set the Index's DBID
 IndxId.eKind = DBKIND_NAME;
 IndxId.uName.pwszName = OLESTR("Emp_LastName_Index");

 // Open the Index with default properties and default interfaces. (IRowsetIndex,
 // IAccessor, IRowset, IColumnsInfo, IRowsetInfo).
 pIOpenRowset->OpenRowset(NULL, NULL, &IndxId, IID_IRowsetIndex, 0, NULL,
          (IUnknown**) &pIndex);

 // Get a rowset for the table using a helper function.
 GetTableRowsetLocate(pIOpenRowset, OLESTR("Employees"), &pIRSLocate);

 // Get an accessor for the index.
 pIndex->QueryInterface(IID_IAccessor, (void**) &pIndxAccsr);

 // Get a rowset for index traversal.
 pIndex->QueryInterface(IID_IRowset, (void**) &pIndexRows);

 // Get a pointer to IColumnsInfo on the index and get information about the columns
 // of the index.
 pIndex->QueryInterface(IID_IColumnsInfo, (void**) &pIndxColsInfo);
 pIndxColsInfo->GetColumnInfo(pColumns, prgInfo, ppStringsBuffer);

 // Explore the DBCOLUMNINFO structures. The structure not corresponding to column
 // 0 and with a flag DBCOLUMNFLAGS_ISBOOKMARK set is the bookmark to the base
 // table. Suppose that the IColumnsInfo says that there are two columns, that
 // column 1 (which corresponds to the key) contains a string, and that the base
 // table bookmark is in column 2 and that it needs 4 bytes. Code not shown.
 // Create index and table bindings and corresponding accessors.
 typedef struct tagBmk{
  OLECHAR * Name;
  ULONG   cBookmark;
  BYTE  vBookmark[4];
 } Bmk;

 DBBINDSTATUS rgStatus[2];
 static DBBINDING IndxBinds[2]= {
  {
   1,         // Ordinal of key column
   0,         // obValue
   0,         // No length
   0,         // No status
   NULL,        // No TypeInfo
   NULL,        // No Object
   NULL,        // No binding extensions
   DBPART_VALUE,      // Bind value
   DBMEMOWNER_CLIENTOWNED,  // Client-owned memory
   DBPARAMIO_NOTPARAM,
   0,         // cbMaxLen ignored
   0,
   DBTYPE_WSTR | DBTYPE_BYREF, // DBTYPE
   0,         // No Scale
   0          // No Precision
  },
  {
   2,          // Ordinal of base table bookmark
   offsetof(Bmk, vBookmark),   // Offset to value
   offsetof(Bmk,cBookmark),  // Offset to length
   0,          // No status
   NULL,         // No Type Info
   NULL,         // No object
   NULL,         // No binding extensions
   DBPART_VALUE | DBPART_LENGTH, // Bind value and length
   DBMEMOWNER_CLIENTOWNED,   // Client-owned memory
   DBPARAMIO_NOTPARAM,
   4,          // max length
   0,
   DBTYPE_BYTES,       // DBTYPE
   0,          // No Scale
   0           // No Precision
  }
 };

 pIndxAccsr->CreateAccessor(DBACCESSOR_ROWDATA, 2, IndxBinds, 0, &hIndexAccBmk,
          rgStatus);
 //...

 // Set a range Emp_LastName LIKE "Smith*". Notice that only the Name element of the
 // Bmk structure is used.
 Bmk Bookmark;
 Bookmark.Name = OLESTR("Smith");
 pIndex->SetRange(hIndexAccBmk, 1, &Bmk, 0, NULL, DBRANGE_PREFIX);
 pIndex->Seek(hIndexAccBmk, 1, &Bmk, DBSEEK_GE);

 // Traverse index within the range. For each matching index entry, retrieve the
 // corresponding record in the Employees table.
 ULONG    cIdxRows = 0, cTabRows = 0;
 HROW    *phIdxRows = NULL, *phTabRows = NULL;
 DBROWSTATUS  rgRowStatus[1];

 while(SUCCEEDED(hr = pIndexRows->GetNextRows(0, 0, 1, &cIdxRows, &phIdxRows)) &&
   cIdxRows > 0) {
  // Extract the bookmark from the index and read the table directly.
  pIndexRows->GetData(*phIdxRows, hIndexAccBmk, &Bookmark);
  pIRSLocate->GetRowsAt(0, NULL, Bookmark.cBookmark, &(Bookmark.vBookmark[0]), 0,
          1, &cTabRows, &phTabRows);
  PrintData(Bookmark.Name, cTabRows, phTabRows);

  // Release memory
  pMalloc->Free(Bookmark.Name);

  // Release the index and table rows
  pIndexRows->ReleaseRows(cIdxRows, phIdxRows, NULL, rgRowStatus, rgRowOptions);
  pIRSLocate->ReleaseRows(cTabRows, phTabRows, NULL, rgRowStatus, rgRowOptions);
 };

 //Release the accessor
pIndxAccsr->ReleaseAccessor(hIndexAccBmk);

 //...
};


Notifications

Overview of Notifications

Notifications permit an application to be aware of changes to its data. Frequently, numerous consumers require access and/or updating ability to the same data. It is therefore necessary for these consumers to know when changes occur. In OLE DB, notifications are sent by the provider when significant changes occur to row or rowset state. This chapter explains when notifications are sent, as well as how to nest and group them. It also discusses ways to screen out unwanted events or phases, enhancing the consumer’s knowledge of the data's state. To illustrate a method issuing notifications, states and state transitions in providers and consumers are diagrammed.

For more information on | Go to
Consumers and providers | “Consumers and Providers” in Chapter 1, “Overview of OLE DB”
When notifications are sent | “When Notifications Are Sent” in this chapter
Nested notifications | “When Notifications Are Sent” in this chapter
Grouped notifications | “When Notifications Are Sent” in this chapter
States and state transitions in providers | “States and State Transitions in Providers” in this chapter
States and state transitions in consumers | “States and State Transitions in Consumers” in this chapter
Threading and notifications | "Threading and Notifications" in Chapter 14, "Programming Considerations"

Consumers may need to be aware of changes other consumers make to a shared rowset. For example, suppose multiple consumers share a rowset and some of these consumers update the rowset directly by using IRowsetChange or IRowsetUpdate. The nonupdating consumers may want to be notified of changes made by updating consumers to be able to refresh their local state.

To be notified of such changes, which are called endogenous changes, consumers implement IRowsetNotify and register it on a connection point with the provider. Providers call the methods in this interface to notify the consumers of changes made by other consumers of the same rowset.


When Notifications Are Sent

Notifications are sent when significant changes occur to row or rowset state. Many of the events have multiple phases, which occur because multiple consumers may need to cooperate to validate and safely perform the transitions. These phases correspond to initial validation, preparation, commit, and operation completion.

Providers do not send notifications when no work will be done. For example, providers would not send notifications in the following cases:

  • The method returns a trivial error, such as E_INVALIDARG.

  • The method accepts an array of values to operate on, and the array size is zero.

  • The provider is in a state where the method will always fail.

It is possible for notifications to be nested. That is, sending one notification can cause a consumer to take actions that cause another notification to be sent.

Notifications also can be grouped. If a provider supports grouping of notifications, it will report a value of DBPROPVAL_NT_MULTIPLEROWS for the property DBPROP_NOTIFICATIONGRANULARITY. If an action causes the provider to send multiple notifications, all of those notifications must be approved by the consumer before the provider performs the action. If a consumer cancels a single notification, the provider does not perform the action and returns DB_E_CANCELED. For example, if a call to IRowsetChange::SetData changes multiple columns, the consumer must approve the changes to all of the columns before the provider changes any of the columns. If the consumer does not allow a change to any of the columns, no columns are updated.

Consumers can screen out unwanted events or phases of an event by returning DB_S_UNWANTEDREASON or DB_S_UNWANTEDPHASE from the methods on IRowsetNotify. There is no guaranteed effect, but an optimized provider may cease sending the unwanted event or phase of an event. Screening out a phase applies only to that particular event. Other events will still send out notifications for all phases unless they are also screened.


States and State Transitions in Providers

The following diagram displays finite-states and the text describes the events that take place within a rowset method, such as IRowsetChange::SetData implementing notifications.

Provider state/state transition diagram

States

  • Enter Method and Exit Method are conceptual states representing the initial and final states of a method issuing notifications.

  • OKTODO, ABOUTTODO, SYNCHAFTER, DIDEVENT, and FAILEDTODO are states representing notifications sent by the method to consumers.

  • Preliminary Work, Permanent Work, and Undo Work are internal states representing work done by the provider between notification phases.

Transitions

  • Arrows with a heavy continuous line represent transitions within states when all consumers return S_OK as a result of the notification sent by the state at the start of the arrow. In general, a transition through a heavy continuous line represents an acceptance path by the consumer on the notification.

  • Arrows with a light continuous line represent transitions within states when at least one consumer returns S_FALSE as a result of the notification sent by the state at the start of the arrow. In general, a transition through a light continuous line represents a cancellation path by the consumer on the notification.

  • Arrows with a dashed line represent internal transitions within the method. The transitions from Preliminary Work or Permanent Work to FAILEDTODO are transitions that result from some error in the execution of the method. A transition from Enter Method to Exit Method represents a path where the method detects some error condition that implies no notification work.


States and State Transitions in Consumers

The following diagram displays finite states and the text describes the actions that can take place within a consumer inside each method of the IRowsetNotify interface.

Consumer state/state transition diagram

States

  • Initial is a conceptual state denoting the initial state of any IRowsetNotify method before any phase starts. Full phased events such as DBREASON_COLUMN_SET can at this point receive an OKTODO or FAILEDTODO phase. Any other phase at this point is unexpected, causing the method to undergo a transition to the Undo Work2 state that returns S_FALSE.

  • OKTODO, ABOUTTODO, SYNCHAFTER, and DIDEVENT are states to which the method undergoes a transition upon receiving the phase of the same name. All these states may involve some internal work by the consumer. All states, except DIDEVENT, may receive a FAILEDTODO notification, which causes the method to undergo a transition to the Undo Work1 state.

  • Undo Work1 is an internal state to which the listener undergoes a transition from any state (except DIDEVENT) upon reception of a FAILEDTODO phase. In this state, any work done by the consumer is undone. After the work is undone, the method reverts back to the Initial state and returns S_OK.

  • Undo Work2 is an internal state that captures any unexpected phase. In this state, any work done by the consumer is undone. Once the work is undone, the method reverts back to the Initial state and returns S_FALSE.

Transitions

  • Arrows with a continuous heavy line represent transitions within states upon reception of some expected phase.

  • Arrows with a continuous light line represent transitions resulting from receiving an unexpected phase.

  • Arrows with a dashed line represent an internal transition by the consumer.


Data Types in OLE DB

Overview of OLE DB Data Types

Data is defined as and stored by many types, so it is required that both the provider and consumer be able to understand the type. Presented here is the topic of determining data type in OLE DB by the use of type indicators when creating tables and getting and setting data, handling string data, fixed- and variable-length data, and converting data. Type indicators in an ANSI SQL Provider, a Microsoft Access provider, and a Microsoft service component are discussed in detail. Determining the type of data from a string (collectively referring to ANSI, Unicode, and BSTR), and provider support for data type conversions are also presented.

For more information on | Go to
Consumers and providers | "Consumers and Providers" in Chapter 1, "Overview of OLE DB"
Using type indicators in providers | "Using Type Indicators in Providers" in this chapter
Type indicators in an ANSI SQL Provider | "Type Indicators in an ANSI SQL Provider" in this chapter
Type indicators in a Microsoft Access provider | "Type Indicators in a Microsoft Access Provider" in this chapter
Type indicators in a Microsoft SQL service component | "Type Indicators in a Microsoft SQL Server Provider" in this chapter
Using type indicators in consumers | "Using Type Indicators in Consumers" in this chapter
Using type indicators when creating tables in consumers | "Using Type Indicators When Creating Tables" in this chapter
Using type indicators when getting and setting data in consumers | "Using Type Indicators When Getting and Setting Data" in this chapter
Determining the type of data from a string | "String Data" in this chapter
Fixed- and variable-length data | "Fixed- and Variable-Length Data Types" in this chapter
Provider support for data type conversions | "Data Type Conversion" in this chapter

OLE DB uses standard OLE and Windows data types. To describe a data type, a type indicator is used. This is a variable of the enumerated type DBTYPE that is passed as metadata. Although it is easy to confuse the two, type indicators are not data types. For example, DBTYPE_GUID is an integer with a value of 72, not a GUID. It is used to indicate that a piece of memory contains a GUID. Type indicators are used in many places, including the following:

  • Metadata. Type indicators are used to specify the data type of a column or parameter.

  • DBBINDING structures. The wType element of the DBBINDING structure describes the type of the bound data value. When getting data, this is the type to which the provider converts the data before returning it to the consumer. When setting data, this is the type from which the provider converts the data after retrieving it from the consumer.

  • VARIANT structures. The vt element, a short integer, of the VARIANT structure indicates the type of the data that is stored in the structure. The remainder of the structure is 14 bytes, which are structured according to the vt tag. The DBTYPE enumerated type is a superset of the VARENUM enumerated type, so some DBTYPE values cannot be used in VARIANT structures.

For a complete list of type indicators, see "Type Indicators" in Appendix A.


Using Type Indicators in Providers

A provider begins by selecting a subset of types—as represented by type indicators—it wants to support. The provider maps its underlying data types to these types and advertises them through the PROVIDER_TYPES schema rowset. The underlying data types are unimportant to the consumer. What is important is that the provider guarantees that it can always safely transfer data in the type represented by the indicator it advertises for a column or parameter. For examples of how various providers might map underlying data types to OLE DB data types, see the following sections.

Providers are not required to support all OLE DB data types, nor are they required to support all possible conversions among them. For information about what conversions a provider is required to support, see "Data Type Conversion."

Although one goal of OLE DB is to provide a unified type system, there are two exceptions to this goal:

  • It does not standardize type names as used in text commands. The TYPE_NAME column in the PROVIDER_TYPES schema rowset contains the type name consumers should use when executing Data Definition Language (DDL) commands. This saves providers from having to parse such commands before passing them to the data source.

  • It exposes data source – specific type names for parameters in text commands. For more information, see ICommandWithParameters::SetParameterInfo.

In future versions of OLE DB, consumers and providers will be able to define their own data types through the DBTYPE_UDT (user-defined data type) type indicator.


Type Indicators in an ANSI SQL Provider

This table shows how an ANSI SQL provider might map its data types to OLE DB data types.

SQL data type | OLE DB type indicator
CHAR (n) 1≤ n ≤255 | DBTYPE_STR
VARCHAR (n) 1≤ n ≤255 | DBTYPE_STR
CLOB | DBTYPE_STR
DECIMAL(p,s) | DBTYPE_NUMERIC
NUMERIC(p,s) | DBTYPE_NUMERIC
BIT | DBTYPE_BOOL
TINYINT (signed) | DBTYPE_I1
TINYINT (unsigned) | DBTYPE_UI1
SMALLINT (signed) | DBTYPE_I2
SMALLINT (unsigned) | DBTYPE_UI2
INTEGER (signed) | DBTYPE_I4
INTEGER (unsigned) | DBTYPE_UI4
BIGINT (signed) | DBTYPE_I8
BIGINT (unsigned) | DBTYPE_UI8
REAL | DBTYPE_R4
FLOAT | DBTYPE_R8
DOUBLE PRECISION | DBTYPE_R8
BINARY (n) | DBTYPE_BYTES
VARBINARY(n) | DBTYPE_BYTES
BLOB | DBTYPE_BYTES
DATE | DBTYPE_DBDATE
TIME | DBTYPE_DBTIME
TIMESTAMP | DBTYPE_DBTIMESTAMP


Type Indicators in a Microsoft Access Provider

This table shows how a Microsoft Access provider might map its data types to OLE DB data types.

Microsoft Access data type | OLE DB type indicator
BINARY (< 255 bytes) | DBTYPE_BYTES
BIT | DBTYPE_BOOL
BYTE | DBTYPE_UI1
COUNTER | DBTYPE_I4
CURRENCY | DBTYPE_CY
DATETIME | DBTYPE_DATE
DOUBLE | DBTYPE_R8
GUID | DBTYPE_GUID
LONG | DBTYPE_I4
LONGBINARY | DBTYPE_BYTES
LONGTEXT | DBTYPE_STR
SHORT | DBTYPE_I2
SINGLE | DBTYPE_R4
TEXT (< 255 bytes) | DBTYPE_STR


Type Indicators in a Microsoft SQL Server Provider

This table shows how a Microsoft SQL Server provider might map its data types to OLE DB data types.

Microsoft SQL Server data type | OLE DB type indicator
char [(n)] (1 ≤ n ≤ 255) | DBTYPE_STR
varchar [(n)] (1 ≤ n ≤ 255) | DBTYPE_STR
binary [(n)] (1 ≤ n ≤ 255) | DBTYPE_BYTES
varbinary [(n)] (1 ≤ n ≤ 255) | DBTYPE_BYTES
numeric [(p[,s])] | DBTYPE_NUMERIC
decimal [(p[,s])] | DBTYPE_NUMERIC
Tinyint | DBTYPE_UI1
Smallint | DBTYPE_I2
Int | DBTYPE_I4
Real | DBTYPE_R4
float [(n)] | DBTYPE_R8
Smalldatetime | DBTYPE_DATE, DBTYPE_DBTIMESTAMP
Datetime | DBTYPE_DATE, DBTYPE_DBTIMESTAMP
Timestamp | DBTYPE_BYTES (DBCOLUMNFLAGS_ISROWVER is set)
text (≤ 2**31 bytes) | DBTYPE_STR
image (≤ 2**31 bytes) | DBTYPE_BYTES
Smallmoney | DBTYPE_CY
Money | DBTYPE_CY
user-defined-type | DBTYPE_UDT


Using Type Indicators in Consumers

Consumers use type indicators to create and alter tables and when getting and setting data.


Using Type Indicators When Creating Tables

A consumer can create a table through ICommandText or ITableDefinition. If a provider supports ICommandText, then a consumer builds a text command to create a table, such as an SQL CREATE TABLE statement, using the value of the TYPE_NAME column in the PROVIDER_TYPES schema rowset. OLE DB does not define standard type names, which saves providers from having to parse such text commands and replace standardized names with provider-specific names. No type indicators are involved in this process.

If the provider supports ITableDefinition, then columns are described using the DBCOLUMNDESC structure. The wType and pwszTypeName elements of this structure correspond to the DATA_TYPE and TYPE_NAME column values from the PROVIDER_TYPES schema rowset. In most cases, there is no reason for a consumer to specify values for wType and pwszTypeName that are different than the values listed in the PROVIDER_TYPES schema rowset. A consumer is guaranteed to always get a safe conversion when getting or setting values of the column using the type indicator specified in wType.

For example, a provider built on top of an ANSI SQL92–compliant DBMS might contain the following rows in the PROVIDER_TYPES schema rowset.

TYPE_NAME | DATA_TYPE | COLUMN_ SIZE | MINIMUM_ SCALE | MAXIMUM_ SCALE
"CHAR" | DBTYPE_STR | 32000 | NULL | NULL
"VARCHAR" | DBTYPE_STR | 32000 | NULL | NULL

For the CHAR type, COLUMN_SIZE indicates the maximum length in characters of a value of that type supported by this provider. When defining a column of this type, the consumer is free to assign any value between 1 and 32000 to the ulColumnSize element of the DBCOLUMNDESC structure associated with the column. Internally, the provider may choose any representation for values of the type, provided that it always returns data to the consumer as a null-terminated string.


Using Type Indicators When Getting and Setting Data

When consumers allocate memory in which to get or set data, they generally use the type corresponding to the indicator returned by IColumnsInfo::GetColumnInfo for the column. When a consumer gets or sets data according to the type returned, the provider guarantees it can always transfer the data safely. The consumer specifies this indicator in the wType element of the DBBINDING structure used to bind the memory.

The consumer can allocate and bind memory of a different type only if the provider supports a conversion from that type to the type returned by GetColumnInfo. Such conversions are not guaranteed to be safe. For more information, see "Data Type Conversion" in this chapter.


String Data

The term string is used collectively to refer to ANSI strings, Unicode strings, and BSTRs. Where a specific type is intended, that type can be determined from looking at the data type of the programming construct that contains the string. When used as metadata, such as in a schema rowset, a metadata interface such as IColumnsInfo, or a data definition interface such as ITableDefinition, string lengths are always measured in characters. When passing data, such as with IRowset::GetData, string lengths are always measured in bytes.

When a consumer gets string data from a provider, the provider generally returns a null-terminated string to the consumer. If the consumer's buffer is smaller than the length in bytes of the character data plus the null-termination character (that is, cbMaxLen in the DBBINDING structure is less than the length in bytes), the provider truncates the data and adds a null-termination character to the truncated data. String data retrieved through ISequentialStream is not null-terminated.

To ensure that strings returned by the provider are not truncated, consumers should allocate an additional byte for ANSI strings, or two bytes for Unicode strings, in their buffer to allow for the null-termination character. The following examples illustrate three cases where the buffer size determines whether all significant data is copied into the consumer's buffer:

  • When the size of the buffer is less than the size in bytes of the string (that is, cbMaxLen is less than the length in bytes), the string is truncated and a null-termination character is added. For example, the ANSI string ABCDEF\0 is stored in a five-byte buffer as ABCD\0.

  • When the size of the buffer is equal to the size in bytes of the character data but no allowance has been made for the null-termination character (that is, cbMaxLen equals the length in bytes), the string is truncated and a null-termination character is added. One character of data is lost, even though the size of the buffer is equal to the size in bytes of the character data. For example, the ANSI string ABCDEF\0 is stored in a six-byte buffer as ABCDE\0.

  • When the size of the buffer is greater than or equal to the size in bytes of the character data plus the null-termination character (that is, cbMaxLen is greater than or equal to the length in bytes plus one for ANSI strings, or two for Unicode strings), the complete string is copied into the consumer's buffer. For example, the ANSI string ABCDEF\0 is stored in a 7-byte buffer as ABCDEF\0.

Whether character data is stored in the provider with a null-termination character is specific to each provider. If the consumer binds the character as another type, such as binary, the character data is returned without a null-termination character.

For ANSI strings and Unicode strings, the length returned in the length part of the binding is the actual length of the data in bytes. The null-termination character is not counted. For BSTRs, the length returned in the length part of the binding is sizeof(BSTR), not the length of the actual string. The length of the actual string is stored in a DWORD value preceding the string. For more information, see "Type Indicators" in Appendix A.


Fixed- and Variable-Length Data Types

Data types are either fixed-length or variable-length. This refers to whether the provider can predict how much memory is allocated for the data based on its type indicator. If the provider can predict this (fixed-length data types), it ignores the cbMaxLen element of the DBBINDING structure. If the provider cannot predict this (variable-length data types), the consumer must specify how much memory was allocated for the data in cbMaxLen.

When the length part of a binding is applied to a column, the length includes all bytes of the value. It should be the length passed to IMalloc to allocate a buffer big enough to hold the data value. If the value is converted, the length should be the length in bytes that the value has after conversion, in the type specified by the consumer.

If a value is truncated when it is copied into the consumer's structure, then it fills the buffer up to the limit of the specified width and is truncated abruptly. No attempt is made to trailing-align or trim the value, although strings are null-terminated. The returned length is the length in bytes of the untruncated value, which the consumer can compare to cbMaxLen to determine how many bytes were truncated.

DBTYPE_BYREF, DBTYPE_ARRAY, and DBTYPE_VECTOR are modifiers of data types and do not affect whether a data type is fixed-length or variable-length. However, these do affect how data is stored and the meaning of cbMaxLen in the DBBINDING structure.

For a complete list of fixed-length and variable-length data types, see "Fixed-Length Data Types" and "Variable-Length Data Types" in Appendix A.


Data Type Conversion

Providers are required to support the following data type conversions:

  • In the type used by the provider for the column or parameter, as returned by IColumnsInfo::GetColumnInfo or ICommandWithParameters::GetParameterInfo. Such transfers must be performed without loss of data.

  • To and from DBTYPE_WSTR if such a conversion is defined by OLE DB. For more information, see "Conversion Tables" in Appendix A. Conversions to and from DBTYPE_WSTR are defined for virtually all types.

Support for additional conversions is optional and might result in truncation of data or a conversion error. The consumer can determine what conversions a provider supports by calling IConvertType::CanConvert with the indicators for the “to” and “from” types. If a provider offers additional functions that the consumer can call to convert data types, these functions are described in a type library pointed to by the PROVIDER_TYPES schema rowset.

For more information about how data is converted, see "Data Type Conversion Rules" in Appendix A.


Overview of Properties

Properties are attributes of an object. For example, a rowset has properties that describe the maximum number of rows that can be open at one time, whether or not the rowset exposes bookmarks, and the rowset's threading model.

Consumers set property values to request specific object behavior. For example, consumers use properties to specify which interfaces they want a rowset to expose.

Consumers get property values to determine the capabilities of an object. For example, consumers use properties to determine what transaction isolation levels a data source can support.

Each property has a value, a type, a description, a read/write attribute, and, for rowset properties, an indicator of whether it can be applied on a column-by-column basis.

This chapter defines and discusses properties, explains property sets as a way of determining which provider defined the property, and defines property groups as a set of properties that are logically related. Consumers can expose and set object properties as well as retrieve information about properties. Property structures and enumerated types are used to pass property values and information about properties. This chapter explains how this is accomplished and provides a detailed definition list of these structures and types.

For more information on | Go to
Rowsets | Chapter 4, "Rowsets"
Data source object | "Data Source Objects" in Chapter 2, "Enumerators, Data Source Objects, and Sessions"
Transaction isolation level | "Isolation Levels" in Chapter 12, "Transactions"
Bookmarks | "Bookmarks" in Chapter 4, "Rowsets"
Threading models | "Provider Threading Models" in Chapter 14, "Programming Considerations"
Property sets and groups | "Property Sets and Property Groups" in this chapter
How a consumer sets property values | "Setting Property Values" in this chapter
How a consumer gets property values | "Getting Property Values" in this chapter
How a consumer gets property information | "Getting Information About Properties" in this chapter
Property structures and enumerated types | "Property Structures and Enumerated Types" in this chapter


Property Sets and Property Groups

A property is identified by a GUID and an integer representing the property ID. A property set is the set of all properties that share the same GUID. Generally, a property set is a way of determining which provider defined the property. OLE DB predefines a number of property sets and the properties in them. Providers also can define provider-specific property sets containing their own properties.

Each property belongs to one or more property groups. A property group is a set of logically related properties that could apply to either an OLE DB object, such as a rowset, or a data source object. Property groups also could include an operation on an OLE DB object, such as the creation of a data source, or an object that is not directly represented as an OLE DB object, such as a table or column. Property groups are identified by a DBPROPFLAGS value that is returned in the DBPROPINFO structure. OLE DB defines property groups. Providers cannot define provider-specific property groups.

Property sets and property groups overlap. That is, a property group can contain properties from more than one property set and a property set can contain properties from more than one property group. For example, a provider could define properties A and B in the property set identified by a GUID generated by the provider, where property A is in the Data Source property group and property B is in the Rowset property group.


Property Sets

The table shows property sets that are defined by OLE DB. Providers also can define their own property sets.

Property sets defined by OLE DB

Property set GUID | Description
DBPROPSET_COLUMN | All OLE DB–defined properties in the Column property group.
DBPROPSET_DATASOURCE | All OLE DB–defined properties in the Data Source property group.
DBPROPSET_
DATASOURCEINFO 		All OLE DB–defined properties in the Data Source Information property group.
DBPROPSET_DBINIT | All OLE DB–defined properties in the Initialization property group.
DBPROPSET_INDEX | All OLE DB–defined properties in the Index property group.
DBPROPSET_
PROPERTIESINERROR 		If ICommand::Execute, ICommandPrepare::Prepare, or IDBInitialize::Initialize returns DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED, the consumer can immediately call ICommandProperties::GetProperties or IDBProperties::GetProperties with a single DBPROPIDSET structure to return all the properties that were in error. This property set is not returned from GetProperties.

In the DBPROPIDSET structure, the consumer sets guidPropertySet to DBPROPSET_PROPERTIESINERROR, cPropertyIDs to 0, and rgPropertyIDs to a null pointer. If the consumer fails to set any of these correctly, GetProperties returns E_INVALIDARG.

It is an error to pass this property set to any method that sets properties—the method returns the same error it would for any other unsupported property set—and methods that return information about properties do not return information about this property set. If any other property sets are passed to GetProperties with this property set, GetProperties returns E_INVALIDARG. Calling GetProperties with DBPROPSET_PROPERTIESINERROR at a time other than immediately after the call that returns DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED might yield inconsistent results, because the consumer's actions might have caused the provider to clear or alter the list of properties in error.
DBPROPSET_ROWSET | All OLE DB–defined properties in the Rowset property group.
DBPROPSET_SESSION | All OLE DB–defined properties in the Session property group.
DBPROPSET_TABLE | All OLE DB–defined properties in the Table property group.


Property Groups

The table shows the property groups that are defined by OLE DB. Providers cannot define their own property groups. Property groups are a set of logically related properties that could apply to either an OLE DB object, an operation on an OLE DB object, or an object that is not directly represented as an OLE DB object.

Property groups defined by OLE DB

Property group | Description
Column | Properties used to create columns.
Data Source | Properties that apply to data sources. These properties can be set for some providers.
Data Source Creation | Properties used to create data sources.
Data Source Information | Properties that describe data sources. These properties are read-only for all providers and constitute a set of static information about the provider and data source.
Initialization | Properties used to initialize the data source object or enumerator.
Index | Properties used to create and describe indexes.
Rowset | Properties that apply to rowsets.
Session | Properties that apply to sessions.
Table | Properties used to create tables.

For information about how a consumer determines which group a property is in, see "Getting Information about Properties" later in this chapter.


Setting Property Values

To set property values, a consumer performs the following actions:

  1. Determines the properties for which to set values. To set the value of a property to a different value, the property must be updatable. However, consumers can set the value of a read-only property to its current value. For information about how to determine if a property is writable, see "Getting Information about Properties" in this chapter.

  2. Determines the property sets that contain the identified properties.

  3. Allocates an array of DBPROPSET structures, one for each identified property set.

  4. Allocates an array of DBPROP structures for each property set. The number of elements in each array is the number of properties, identified in step 1, that belong to that property set.

  5. Places the property's value and a flag showing whether it is required or optional in the DBPROP structure for each property. If the provider cannot set the value of a required property, it will fail to create the object to which the property applies and the method will return DB_E_ERRORSOCCURRED. If the provider cannot easily set the value of an optional property, it will create the object to which the property applies and the method will return DB_S_ERRORSOCCURRED.

    If the property belongs to the Rowset property group and applies to a single column, the consumer sets the colid element of the DBPROP structure to the ID of the column.

  6. For each property set, places the property set GUID in the DBPROPSET structure. It then places the count of elements in and the pointer to the corresponding DBPROP array in the DBPROPSET structure.

  7. Calls a method that sets properties and passes it the count and array of DBPROPSET structures. The provider returns the status of each property in the dwStatus element of the DBPROP structure for that property; that is, whether the property was set and, if not, why it was not set.

For example, assume that the consumer wants to set values for five properties, where properties 1, 2, and 3 are in property set A and properties 4 and 5 are in property set B. The consumer uses the following structures.

An example of setting property values

The following table lists the methods that consumers use to set property values.

Methods used by consumer to set property values

Property group | Methods used to set property values
Column | ITableDefinition::CreateTable
Data Source | IDBProperties::SetProperties
Data Source Creation | IDBDataSourceAdmin::CreateDataSource
Data Source Information | IDBProperties::SetProperties
Initialization | IDBProperties::SetProperties
Index | IIndexDefinition::CreateIndex
Rowset IColumnsRowset::GetColumnsRowset ,
 ICommandProperties::SetProperties ,
 IDBSchemaRowset::GetRowset ,
 IOpenRowset::OpenRowset ,
 ISourcesRowset::GetSourcesRowset ,
 ITableDefinition::CreateTable
Session | ISessionProperties::SetProperties
Table | ITableDefinition::CreateTable

Read-only properties can always be set to their default values or to VT_EMPTY. Setting read-only properties to any other values will cause an error.


Getting Property Values

To get property values, a consumer performs the following actions:

  1. Determines the properties for which to get values.

  2. Determines the property sets that contain the identified properties.

  3. Allocates an array of DBPROPIDSET structures, one for each identified property set.

  4. Allocates an array of DBPROPIDs for each property set. The number of elements in each array is the number of properties identified in step 1 that belong to that property set.

  5. Places the ID of each property in the DBPROPID array.

  6. Places the property set GUID in the DBPROPIDSET structure for each property set. It then places the count of elements in and the pointer to the corresponding DBPROPID array in the DBPROPIDSET structure.

  7. Calls a method that gets properties and passes it the count and array of DBPROPIDSET structures.

To return property values, the provider performs the following actions:

  1. Allocates an array of DBPROPSET structures. The elements of this array correspond to the elements of the DBPROPIDSET array.

  2. For each element of the DBPROPIDSET array, allocates an array of DBPROP structures. The elements of the DBPROP array correspond to the elements of the DBPROPID array.

  3. Places the value, column ID, whether the property is required or optional, and status of each property in the DBPROP structure that corresponds to the DBPROPID value for the property.

  4. Places the property set GUID in the DBPROPSET structure for each property set. It then places the count of elements in and the pointer to the corresponding DBPROP array in the DBPROPSET structure.

  5. Returns the count and array of DBPROPSET structures to the consumer.

For example, assume that the consumer wants to get values for five properties, where properties 1, 2, and 3 are in property set A and properties 4 and 5 are in property set B. The consumer uses the following structures.

An example of how a consumer gets property values

The provider returns the following structures.

An example of what property values a provider returns

The following table lists the methods that consumers use to get property values.

Methods used by consumers to get property values

Property group | Methods used to get property values
Data Source | IDBProperties::GetPropertiesoledbIDBProperties__GetProperties
Data Source Information | IDBProperties::GetPropertiesoledbIDBProperties__GetProperties
Index | IRowsetIndex::GetIndexInfo
Initialization | IDBProperties::GetPropertiesoledbIDBProperties__GetProperties
Rowset ICommandProperties::GetProperties ,
 IRowsetInfo::GetProperties
Session | ISessionProperties::GetPropertiesoledbISessionProperties__GetProperties


Getting Information About Properties

To get information about properties, a consumer performs the following actions:

  1. Determines the properties for which to get information.

  2. Determines the property sets that contain the identified properties.

  3. Allocates an array of DBPROPIDSET structures, one for each identified property set.

  4. Allocates an array of DBPROPIDs for each property set. The number of elements in each array is the number of properties identified in step 1 that belong to that property set.

  5. Places the ID of each property in the DBPROPID array.

  6. Places the property set GUID in the DBPROPIDSET structure for each property set. It then places the count of elements in and the pointer to the corresponding DBPROPID array in the DBPROPIDSET structure.

  7. Calls a method that gets properties and passes it the count and array of DBPROPIDSET structures.

To return property values, the provider performs the following actions:

  1. Allocates an array of DBPROPINFOSET structures. The elements of this array correspond to the elements of the DBPROPIDSET array.

  2. For each element of the DBPROPIDSET array, allocates an array of DBPROPINFO structures. The elements of the DBPROPINFO array correspond to the elements of the DBPROPID array.

  3. Places the ID, description, type, property group, whether the property can be read from or written to, and list of valid values of each property in the DBPROPINFO structure that corresponds to the DBPROPID value for the property.

  4. Places the property set GUID in the DBPROPINFOSET structure for each property set. It then places the count of elements in and the pointer to the corresponding DBPROPINFO array in the DBPROPINFOSET structure.

  5. Returns the count and array of DBPROPINFOSET structures to the consumer.

For example, assume that the consumer wants to get information about five properties, where properties 1, 2, and 3 are in property set A and properties 4 and 5 are in property set B. The consumer uses the following structures.

An example of how a consumer gets information about properties

The provider returns the following structures.

An example of what property information a provider returns

The following table lists the methods consumers use to get information about properties.

Methods used by consumers to get property information

Property group | Methods used to get information about properties
All property groups | IDBProperties::GetPropertyInfo
Data Source Creation | IDBDataSourceAdmin::GetCreationProperties
Initialization | IDBDataSourceAdmin::GetCreationProperties
Session | IDBDataSourceAdmin::GetCreationProperties


Property Structures and Enumerated Types

The following structures and types are used to pass property values and information about properties. They are described in other sections in this chapter.

Structures and types used to pass property values and information

Structure or enumerated type | Description
DBPROP | A structure used by the consumer or provider to pass a value for a single property.
DBPROPFLAGS | A value used by the provider in a DBPROPINFO structure to describe a single property.
DBPROPID | A value used by the consumer or provider to identify a property. This value is used in conjunction with a property set GUID.
DBPROPIDSET | A structure used by the consumer to pass an array of DBPROPID values that identify properties for which the consumer wants to get values or information.
DBPROPINFO | A structure used by the provider to return information about a single property.
DBPROPINFOSET | A structure used by the provider to return an array of DBPROPINFO structures.
DBPROPOPTIONS | A value used in a DBPROP structure by the consumer or provider to specify whether a property value is required or optional.
DBPROPSET | A structure used by the consumer or provider to pass an array of DBPROP structures.
DBPROPSTATUS | A value used in a DBPROP structure by the provider to specify whether it was able to set a property value.


DBPROP Structure

Consumers and providers use DBPROP structures to pass property values. An array of DBPROP structures is passed in a DBPROPSET structure.

The DBPROP structure is defined as:

typedef struct tagDBPROP {
 DBPROPID  dwPropertyID;
 DBPROPOPTIONS dwOptions;
 DBPROPSTATUS  dwStatus;
 DBID    colid;
 VARIANT   vValue;
} DBPROP;

The elements of this structure are used as described in the following table.

Elements of DBPROP structure

Element | Description
dwPropertyID | The ID of the property.
dwOptions | The options that further describe the property. For example, when setting properties, the consumer can specify that the property is optional, and the provider should continue even if it cannot set the property value. Alternatively, consumers can specify that a property is required, in which case the provider should fail if it cannot support the property value.
dwStatus | The status returned by the provider to indicate the success or failure in setting or getting the property value. This value is ignored on input. When getting property values, and the provider returns DBPROPSTATUS_NOTSUPPORTED in this element, the values of dwOptions, colid, and vValue are undefined.
colid | An optional ID for a column to which the property applies. To specify that a particular property applies to all columns, the consumer sets colid to DB_NULLID. This is ignored unless the property can be applied to a column; that is, the DBPROPFLAGS_COLUMNOK flag is returned for the property in the dwFlags element of the DBPROPINFO structure.
vValue | The value of the property. When getting property values, if the consumer requests the value of a property that has not been set and has no default, VT_EMPTY is returned. When setting property values, specifying VT_EMPTY returns the property to its default value.


DBPROPFLAGS Enumerated Type

Providers use DBPROPFLAGS values to describe the property, such as the group to which a property belongs and whether the property can be read from or written to. DBPROPFLAGS is used in the DBPROPINFO structure.

The DBPROPFLAGS enumerated type is defined as described in the following table. These flags can be combined except as noted.

DBPROPFLAGS enumerated type

Value | Description
DBPROPFLAGS_NOTSUPPORTED The property is not supported or the consumer has requested information about properties not in the Initialization property group and the data source object is uninitialized.

If this value is returned, it cannot be combined with any of the other DBPROPFLAGS values.
DBPROPFLAGS_COLUMN The property belongs to the Column property group.

The flags DBPROPFLAGS_COLUMN, DBPROPFLAGS_DATASOURCE, DBPROPFLAGS_DATASOURCECREATE, DBPROPFLAGS_DATASOURCEINFO, DBPROPFLAGS_DBINIT, DBPROPFLAGS_INDEX, DBPROPFLAGS_ROWSET, DBPROPFLAGS_SESSION,
DBPROPFLAGS_TABLE
are mutually exclusive.

For a description of property groups, see "Property Groups" earlier in this chapter.
DBPROPFLAGS_DATASOURCE | The property belongs to the Data Source property group.
DBPROPFLAGS_DATASOURCECREATE | The property belongs to the Data Source Creation property group.
DBPROPFLAGS_DATASOURCEINFO | The property belongs to the Data Source Information property group.
DBPROPFLAGS_DBINIT | The property belongs to the Initialization property group.
DBPROPFLAGS_INDEX | The property belongs to the Index property group.
DBPROPFLAGS_ROWSET | The property belongs to the Rowset property group.
DBPROPFLAGS_SESSION | The property belongs to the Session property group.
DBPROPFLAGS_TABLE | The property belongs to the Table property group.
DBPROPFLAGS_COLUMNOK This flag applies only to properties in the Rowset property group and can be set only when DBPROPFLAGS_ROWSET is set.

If it is set, the property can be applied to all columns in the rowset or to a specific column in the rowset. Whether the property applies to a specific column or all columns depends on the value of colid in the DBPROP structures used to set or get the property value.

If it is not set, the property applies to all columns in the rowset.

Setting this flag means the provider supports the property on a per-column basis. This flag can only be returned for rowset properties whose description indicates Column? Y.
DBPROPFLAGS_READ | A method can get the property's value. For a list of methods that get properties, see "Getting Property Values" in this chapter.
DBPROPFLAGS_WRITE | A method can set the property's value. For a list of methods that set properties, see "Setting Property Values" in this chapter.
DBPROPFLAGS_REQUIRED When returned by IDBProperties::GetPropertyInfo, this flag applies only to properties in the Initialization property group and can be set only when DBPROPFLAGS_DBINIT is set. If it is set, the consumer must specify a value for the property before calling IDBInitialize::Initialize to initialize the data source or enumerator. If it is not set, the data source or enumerator can be initialized even if the consumer does not specify a value for the property before calling IDBInitialize::Initialize. Whether or not a property is required for a particular provider may vary depending on other properties specified (for example, DBPROP_DATASOURCE), or on other provider-specific factors. Providers should only mark as required those properties that are always required for that instance of the DSO. Consumers should be aware that, depending on other factors, such as the combination of initialization properties specified, additional properties not marked as required may need to be specified.

When returned by IDBDataSourceAdmin::
GetCreationProperties, this flag applies to properties in the Data Source Creation and Session property groups and properties in the Initialization property group that can be used in data source creation. If it is set, the consumer must specify a value for the property when calling IDBDataSourceAdmin::
CreateDataSource. If it is not set, the data source can be created even if the consumer does not specify a value for the property.


DBPROPID Type

Consumers and providers use DBPROPID values, in conjunction with property set GUIDs, to identify properties. An array of DBPROPID values is passed in a DBPROPIDSET structure.

Within a property set, each property has a unique ID. The property IDs within a property set begin with the number 2.

The DBPROPID type is defined as:

typedef DWORD DBPROPID;


DBPROPIDSET Structure

Consumers use DBPROPIDSET structures to pass an array of property IDs for which the consumer wants to get values or information. The properties identified in a single DBPROPIDSET structure all belong to the same property set.

The DBPROPIDSET structure is defined as:

typedef struct tagDBPROPIDSET {
 DBPROPID * rgPropertyIDs;
 ULONG  cPropertyIDs
 GUID   guidPropertySet;
} DBPROPIDSET;

The elements of this structure are used as described in the following table.

Elements of DBPROPIDSET structure

Element | Description
rgPropertyIDs | A pointer to an array of property IDs for which to return values or information. If cPropertyIDs is zero, this is ignored.
cPropertyIDs | The number of properties from the property set for which to return values or information. If cPropertyIDs is zero, the method ignores rgPropertyIDs and returns values for or information about all of the properties in the property set.
guidPropertySet | A GUID that identifies the property set to which the properties belong. If the provider does not support this property set, the method to which this is passed returns DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED. If cPropertyIDs is zero, the method sets cProperties and rgProperties in the corresponding DBPROPSET structure or cPropertyInfos and rgPropertyInfos in the corresponding DBPROPINFOSET structure to zero and a null pointer, respectively. If cPropertyIDs is not zero, then for each property specified in rgPropertyIDs, the method sets dwStatus in the corresponding DBPROP structure to DBPROPSTATUS_NOTSUPPORTED or dwFlags in the corresponding DBPROPINFO structure to DBPROPFLAGS_NOTSUPPORTED.


DBPROPINFO Structure

Providers use DBPROPINFO structures to return information about properties. An array of DBPROPINFO values is passed in a DBPROPINFOSET structure.

The DBPROPINFO structure is defined as follows:

typedef struct tagDBPROPINFO {
 LPOLESTR  pwszDescription;
 DBPROPID  dwPropertyID;
 DBPROPFLAGS dwFlags;
 VARTYPE   vtType;
 VARIANT   vValues;
} DBPROPINFO;

The elements of this structure are used as described in the following table.

Elements of DBPROPINFO structure

Element | Description
pwszDescription | A text description of the property. For properties defined by OLE DB, this should be the description listed for the property in Appendix C, "OLE DB Properties." The property description is used to construct stringed connection information and should not be localized.
dwPropertyID | The ID of the property.
dwFlags A bitmask that describes the property, such as the group it belongs to and whether it can be read from or written to. For more information, see "DBPROPFLAGS Enumerated Type" in this chapter.

If the property is not supported, this is DBPROPFLAGS_NOTSUPPORTED. In this case, pwszDescription is set to null, and vtType and vValues are set to indicate VT_EMPTY.
vtType | The type indicator of the property. This is used in the vt element of the VARIANT structure passed in the DBPROP structure.
vValues A list of the supported values for the property. This list is returned only if the property can be set and either:
  • The property type is an integer and the possible values of the property are an enumerated type.

  • The property type is a string and the possible values form a small, well-defined set. The definition of a small, well-defined set is provider-specific. For example, the provider might return the list of possible authentication services (DBINIT_OPT_AUTH_INTEGRATED property), but not the list of possible user IDs (DBINIT_OPT_AUTH_USERID property).

If this list is returned, the vt element in vValues is VT_ARRAY combined with the type indicator returned in vtType. If this list is not returned, the vt element is VT_EMPTY. The consumer must free any memory pointed to by elements of vValues by calling VariantClear.


DBPROPINFOSET Structure

Providers use DBPROPINFOSET structures to return an array of DBPROPINFO structures. The properties identified in a single DBPROPINFOSET structure all belong to the same property set.

The DBPROPINFOSET structure is defined as follows:

typedef struct tagDBPROPINFOSET {
 DBPROPINFO * rgPropertyInfos;
 ULONG    cPropertyInfos;
 GUID   guidPropertySet;
} DBPROPINFOSET;

The elements of this structure are used as described in the following table.

Elements of DBPROPINFOSET structure

Element | Description
rgPropertyInfos | A pointer to an array of DBPROPINFO structures in which information is being returned.
cPropertyInfos | The number of properties from the property set for which information is being returned.
guidPropertySet | A GUID that identifies the property set to which the properties belong.


DBPROPOPTIONS Enumerated Type

Consumers use DBPROPOPTIONS values to specify whether a property value is required or optional. The values of DBPROPOPTIONS are mutually exclusive. DBPROPOPTIONS is used in the DBPROP structure.

The values of DBPROPOPTIONS have the following meanings.

DBPROPOPTIONS values

Value Description
DBPROPOPTIONS_
REQUIRED 		The specified property value is required.

If the provider does not support the specified property, it sets the dwStatus element of the DBPROP structure to DBPROPSTATUS_NOTSUPPORTED and returns DB_E_ERRORSOCCURRED. Whatever action the method was to take, such as initializing a data source or creating a rowset, fails.

If the provider supports the specified property, then all methods except IcommandProperties::SetProperties can determine whether the specified value is supported. If the provider does not support the specified value, it sets dwStatus to DBPROPSTATUS_NOTSUPPORTED and returns DB_E_ERRORSOCCURRED. Whatever action the method was to take, such as initializing a data source or creating a rowset, fails.
If SetProperties can determine that the provider cannot support the specified value, it sets dwStatus to DBPROPSTATUS_NOTSUPPORTED and returns DB_S_ERRORSOCCURRED. If SetProperties cannot determine whether the provider can support the specified value, it sets dwStatus to DBPROPSTATUS_OK. When ICommand::Execute is called, the provider determines whether it can support the specified value. If the provider cannot support the specified value, Execute returns DB_E_ERRORSOCCURRED.
DBPROPOPTIONS_
OPTIONAL 		The provider should set the property to the requested value only if it can.

If the provider does not support the specified property, it sets the dwStatus element of the DBPROP structure to DBPROPSTATUS_NOTSUPPORTED and returns DB_S_ERRORSOCCURRED. Whatever action the method was to take, such as initializing a data source or creating a rowset, succeeds.

If the provider supports the specified property, then all methods except SetProperties can determine whether the specified value can be supported. If the provider cannot support the specified value, it sets dwStatus to DBPROPSTATUS_NOTSET and returns DB_S_ERRORSOCCURRED. Whatever action the method was to take, such as initializing a data source or creating a rowset, succeeds.

If SetProperties can determine that the provider can support the specified value, it sets dwStatus to DBPROPSTATUS_OK. ICommandProperties::GetProperties returns the specified value in the vValue element of the DBPROP structure and DBPROPOPTIONS_REQUIRED in the dwOptions element of the DBPROP structure. If SetProperties can determine that the provider cannot support the specified value, it sets dwStatus to DBPROPSTATUS_NOTSET and returns DB_S_ERRORSOCCURRED. GetProperties returns the specified value vValue and DBPROPOPTIONS_REQUIRED in dwOptions.

If SetProperties cannot determine whether the provider can support the specified value, it sets dwStatus to DBPROPSTATUS_OK. GetProperties returns the specified value in vValue and DBPROPOPTIONS_OPTIONAL in dwOptions. When Execute is called, the provider determines whether it can support the specified value. If the provider cannot support the specified value, Execute returns DB_S_ERRORSOCCURRED, but successfully executes the command.


DBPROPSET Structure

Consumers and providers use DBPROPSET structures to pass an array of DBPROP structures. The properties identified in a single DBPROPSET structure all belong to the same property set.

The DBPROPSET structure is defined as follows:

typedef struct tagDBPROPSET {
 DBPROP * rgProperties;
 ULONG  cProperties;
 GUID   guidPropertySet;
} DBPROPSET;

The elements of this structure are used as described in the following table.

Elements of DBPROPSET structure

Element | Description
rgProperties | A pointer to an array of DBPROP structures. These structures contain property values. If cProperties is zero, this is ignored.
cProperties | The number of properties from the property set for which to set or get values. If cProperties is zero, the provider ignores rgProperties and does not set or get values for any properties in the set.
guidPropertySet | A GUID that identifies the property set to which the properties belong.


DBPROPSTATUS Enumerated Type

Providers use DBPROPSTATUS values to return information about whether property values could be set. DBPROPSTATUS is used in the DBPROP structure.

The values of DBPROPSTATUS have the following meanings.

DBPROPSTATUS values

Value | Description
DBPROPSTATUS_OK | The property's value was successfully set. This includes the case where the value of a read-only property was set to its current value.
DBPROPSTATUS_BADCOLUMN | The colid element of the DBPROP structure was invalid.
DBPROPSTATUS_BADOPTION | The value of dwOptions was invalid.
DBPROPSTATUS_BADVALUE The data type in vValue was not the data type of the property or was not VT_EMPTY. For example, the property was DBPROP_MEMORYUSAGE, which has a data type of VT_I4, and the data type was VT_I8.

The value in vValue was not a valid value. For example, the property was DBPROP_MEMORYUSAGE and the value was negative.

The value in vValue was a valid value for the property, and the provider supports the property as a writable property, but the provider does not support the value specified by vValue.
DBPROPSTATUS_CONFLICTING | The property's value was not set because doing so would have conflicted with an existing property.
DBPROPSTATUS_NOTALLSETTABLE | A property was specified to be applied to all columns, but could not be applied to one or more of them.
DBPROPSTATUS_NOTSET | The property's value was not set to the specified value because dwOptions was DBPROPOPTIONS_OPTIONAL and setting the property to the specified value would not have been possible.
DBPROPSTATUS_NOTSETTABLE | The property was read-only or the consumer attempted to set values of properties in the Initialization property group after the data source object was initialized. Consumers can set the value of a read-only property to its current value.
DBPROPSTATUS_NOTSUPPORTED The property's value was not set because the provider did not support the property, the provider did not support the property set, or the consumer attempted to get or set values of properties not in the Initialization property group and the data source object is uninitialized.

The provider may return DBPROPSTATUS_NOTSUPPORTED for properties that do not apply to the provider. For example, a read-only provider may return DBPROPSTATUS_NOTSUPPORTED if the consumer called IDBProperties::SetProperties or IDBProperties::GetProperties for DBPROP_UPDATABILITY, or may return DBPROPFLAGS_NOTSUPPORTED if the consumer called IDBProperties::GetPropertyInfo for DBPROP_UPDATABILITY.


Transactions

Overview of Transactions

A transaction enables consumers to define units of work within a provider, and when data is shared, defines the relationship of those units to each other. This chapter discusses the relationship of a session to a simple transaction; the behaviors for transaction retention and rowset preservation; and nested transactions. A nested transaction occurs when a database-transaction object (the parent transaction) sets up a transaction. Providers that support extended transaction functionality are able to commit or abort a transaction at different levels. ITransactionOutcomeEvents is an interface implemented by the consumer in order to discover the outcome of transaction events. Methods to perform coordinated transactions, if supported by the provider, are given, and transaction isolation levels are defined. Locking resources to regulate sharing and isolation are also discussed.

For more information on | Go to
Consumers and providers | "Consumers and Providers" in Chapter 1, "Overview of OLE DB"
Simple transactions | "Simple Transactions" in this chapter
Transaction retention | "Transaction Retention" in this chapter
Rowset preservation | "Rowset Preservation" in this chapter
Nested transactions | "Nested Transactions" in this chapter
Extended transaction functionality | "Transaction Objects" in this chapter
Commit or abort a transaction at different levels | "Committing or Aborting a Transaction Object" in this chapter
Discover the outcome of transaction events | "Notification Events" in this chapter
Methods to perform coordinated transactions | "Coordinated Transactions" in this chapter
Transaction isolation levels | "Isolation Levels" in this chapter
Locking resources to regulate sharing and isolation | "Locking" in this chapter

Transactions are mechanisms used to define persistent units of work within an application, and to define how the different units relate to each other in a system with parallel activities.

Transactions:

CoType TTransaction {
   [mandatory] interface IConnectionPointContainer;
   [mandatory] interface ITransaction;
   [optional]  interface ISupportErrorInfo;
};

Transaction Options:

CoType TTransactionOptions {
   [mandatory] interface ITransactionOptions;
   [optional]  interface ISupportErrorInfo;
};


Simple Transactions

If the provider supports transactions, the session supports ITransactionLocal, which inherits from ITransaction. Calling ITransactionLocal::StartTransaction begins a transaction on the session.

A session may be inside or outside a transaction at any point in time. When created, a session is outside a transaction, and all work done within the scope of that session is immediately committed on each method call. When a session enters a local or coordinated transaction, all work done by the session, or the commands and rowsets associated with that session, are part of that transaction.

Multiple sessions can be associated with a single data source object. Each of these sessions may be inside or outside a transaction at any point in time, and each session that is in a transaction defines the scope of objects affected by that transaction.


Commit and Abort Behavior

On completion of a transaction, two behaviors are of interest:

  • Retention — whether another work unit is implicitly started.

  • Preservation with respect to the capabilities of rowsets.


Nested Transactions

A nested transaction occurs when a database-transaction object (the parent transaction) sets up a transaction. Changes made within the inner transaction are completely invisible to the outer transaction until the inner transaction is committed. Even then, the changes are not visible outside the outer transaction until that transaction is committed. The inner transaction object must be released before the parent can be released.

Providers that expose ITransactionLocal may support a limited number of nested transactions, including zero. For providers that support nested transactions, calling ITransactionLocal::StartTransaction with an existing transaction begins a new transaction nested below the current transaction. StartTransaction returns the level of the new transaction, starting with *pulTransactionLevel = 1 for the root transaction. Calling ITransactionLocal::Commit or ITransactionLocal::Abort commits or aborts the transaction at the current (lowest) level. If the fRetaining flag is set to TRUE, a new unit of work for the nested transaction is begun; if the fRetaining flag is set to FALSE, no new nested transaction is begun and additional work is done within the scope of the parent transaction.


Transaction Objects

Providers that support extended transaction functionality support ITransactionObject on the session. The consumer can call ITransactionObject::GetTransactionObject to get the transaction object associated with a particular transaction level.


Committing or Aborting a Transaction Object

The transaction object supports ITransaction for committing or aborting a transaction directly. This enables the consumer to commit or abort at other than the current (lowest) level of a nested transaction. Calling ITransactionLocal::Commit or ITransactionLocal::Abort on the session is equivalent to calling ITransaction::Commit or ITransaction::Abort on a transaction object associated with the current transaction.

Calling Commit or Abort on the transaction object with the fRetaining flag set to TRUE implicitly begins a new unit of work and the transaction object remains valid. Calling Commit or Abort with fRetaining set to FALSE terminates the transaction and the transaction object enters a zombie state. At this point, the only valid action that can be performed on the transaction object is to release it.


Notification Events

The transaction object may also support ITransactionOutcomeEvents as a notification sink. Using this notification sink, the consumer can be notified when a transaction is committed or aborted.

ITransactionOutcomeEvents is an interface implemented by the consumer in order to discover the outcome of transaction events. The consumer can call QueryInterface for the IConnectionPointContainer interface on the session, obtain the IConnectionPoint interface for IID_ITransactionOutcomeEvent, and pass its ITransactionOutcomeEvents interface to that connection point. When a commit or abort is processed, the session notifies each registered ITransactionOutcomeEvents interface of the outcome of the transaction.


Coordinated Transactions

If the provider can participate in coordinated transactions, it supports ITransactionJoin on the session. The consumer calls QueryInterface on ITransactionJoin to determine whether the provider supports coordinated transactions. The consumer uses this interface to enlist the session in a coordinated transaction. Once the consumer has joined a coordinated transaction, it calls the ITransaction interface of the transaction coordinator to commit or abort the transaction. If the consumer calls ITransaction::Commit or ITransaction::Abort on the transaction coordinator with the fRetaining flag set to TRUE, the transaction commits or aborts, ITransaction remains valid, and the session remains enlisted in the transaction. Calling ITransaction::Commit or ITransaction::Abort with the fRetaining flag set to FALSE commits or aborts the transaction, ITransaction becomes invalid, and the session is no longer part of a coordinated transaction.

It is an error to call ITransactionJoin::JoinTransaction if the session is already participating in either a local or coordinated transaction.

If the consumer calls ITransactionLocal::StartTransaction to begin a local transaction while in a coordinated transaction, that new transaction acts as a nested transaction within the coordinated transaction.


Isolation Levels

The following phenomena are commonly used to characterize isolation levels:

  • Dirty Read—Transaction A changes a row. Transaction B reads the changed row before transaction A commits the change. If transaction A aborts the change, transaction B will have read a row that is considered to have never existed.

  • Nonrepeatable read—Transaction A reads a row. Transaction B updates or deletes that row and commits this change. If transaction A attempts to reread the row, it will receive different row values or discover that the row has been deleted.

  • Phantom—Transaction A reads a set of rows that satisfy some search criteria. Transaction B inserts a row that matches the search criteria. If transaction A re-executes the statement that read the rows, it receives a different set of rows.

According to these phenomena, the isolation levels defined by OLE DB are as follows:

  • Read Uncommitted (Browse)—A transaction operating at the Read Uncommitted level can see uncommitted changes made by other transactions. At this level of isolation, dirty reads, nonrepeatable reads, and phantoms are all possible.

  • Read Committed (Cursor Stability)—A transaction operating at the Read Committed level cannot see changes made by other transactions until those transactions are committed. At this level of isolation, dirty reads are not possible, but nonrepeatable reads and phantoms are possible.

  • Repeatable Read—A transaction operating at the Repeatable Read level is guaranteed not to see any changes made by other transactions in values it has already read. At this level of isolation, dirty reads and nonrepeatable reads are not possible, but phantoms are possible.

  • Serializable (Isolated)—A transaction operating at the Serializable level guarantees that all concurrent transactions will interact only in ways that produce the same effect as if each transaction were entirely executed one after the other. At this isolation level, dirty reads, nonrepeatable reads, and phantoms are not possible.


Locking

Transactions lock resources to regulate their sharing and isolation.

Locking may occur with any of the methods that interact with rows on the data source; these include IRowsetLocate::GetRowsAt, IRowsetLocate::GetRowsByBookmark, IRowsetUpdate::Update, and others. These operations take locks and may wait for locks to be released by other transactions. A lock held by another transaction usually does not invalidate an operation, but the lock delays the transaction until the other transaction has finished and releases the lock. Therefore, the default behavior on encountering a lock is to queue until it is released. There is a time limit on how long to queue before returning with a timeout failure, and the timeout can be set to zero for infinite timeout. These Various timeout settings are transaction options.

The provider implements locking using a strategy that corresponds to the isolation level. These strategies vary by provider.


Errors

Overview of Errors

This chapter addresses the ability of OLE DB to process errors by using methods, which either return a code or create an error object. Error objects, which are a basic OLE DB component, can return detailed error information. Handling of multiple error items is discussed under "Arrays of Errors."

Automation is the ability to expose one software application's services to another application by using interfaces, such as COM. The mechanics of how an Automation component returns and/or retrieves an error object, as well as its use of threads, is presented here. OLE DB extends Automation error objects by adding the ability for an error object to contain multiple error records. Provider-specific error lookup services are also discussed.

For more information on | Go to
Return codes | "Return Codes" in this chapter
Basic OLE DB components | "OLE DB Components" in Chapter 1, "Overview of OLE DB"
Arrays of errors | "Arrays of Errors" in this chapter
How an Automation component returns an error object | "How an Automation Component Returns an Error Object" in this chapter
How an Automation consumer retrieves an error object | "How an Automation Consumer Retrieves an Error Object" in this chapter
OLE DB error objects | "OLE DB Error Objects" in this chapter
Error records | "Error Records" in this chapter
Interfaces used by OLE DB error objects | "Interfaces Used by OLE DB Error Objects" in this chapter
How an Automation component returns an error object | "How a Provider Returns an OLE DB Error Object" in this chapter
How an Automation component retrieves an error object | "How a Consumer Retrieves an OLE DB Error Object" in this chapter
Use of threads | "OLE DB Error Objects and Threads" in this chapter "Threading and Error Objects" in Chapter 14, "Programming Considerations"
Provider-specific error lookup services | "Error Lookup Services" in this chapter "Error Lookup Service Registry Entries" in Chapter 14, "Programming Considerations"
Consumers and providers | "Consumers and Providers" in Chapter 1, "Overview of OLE DB"

Methods return error information in two ways. The code returned by a method indicates the overall success or failure of the method, and error objects provide detailed information about the error, such as text describing the error, the Help file containing information about the error, a text description of the program that was the source of the error, and any provider-specific error information. Error objects in OLE DB are an extension of the error objects in Automation, use many of the same mechanisms, and can be used as Automation error objects.

Errors:

CoType TErrorObject {
   [mandatory] interface IErrorRecords;
}

Custom errors:

CoType TCustomErrorObject {
   [optional]  interface ISQLErrorInfo
   [optional]  interface IErrorLookup;
}

Error records:

CoType TErrorRecord {
   [mandatory] interface IErrorInfo;
}


Return Codes

Each method returns a code, known as the return code, that indicates the overall success or failure of the method. These return codes are of type HRESULT. Program logic is usually based on return codes. There are two classes of return codes: success and warning codes, and error codes.

Success and warning codes begin with S_ or DB_S_ and indicate that the method successfully completed. If the return code is other than S_OK or S_FALSE, it is likely that an error occurred from which the method was able to recover. For example, IRowset::GetNextRows returns DB_S_ENDOFROWSET when it is unable to return the requested number of rows due to reaching the end of the rowset or chapter. If a single warning condition occurs, the method returns the code for that condition; if multiple warning conditions occur, the method describes the hierarchy of warning return codes—that is, which warning code should be returned when given a choice between multiple warning return codes.

Error codes begin with E_ or DB_E_ and indicate that the method failed completely and was unable to do any useful work. For example, GetNextRows returns E_INVALIDARG when the pointer in which it is to return a pointer to an array of row handles (prghRows) is null. One exception is that some of the methods that return DB_E_ERRORSOCCURRED allocate memory in which to return additional information about these errors. Consumers must free this memory. For information about which methods allocate memory in this case, see the methods that return DB_E_ERRORSOCCURRED. Although error codes can indicate run-time errors such as running out of memory, they usually indicate programming errors. If multiple errors occur, which code is returned is provider-specific. If both errors and warnings occur, the method fails and returns an error code.

All methods can return S_OK, E_FAIL, and E_OUTOFMEMORY. The reference sections list S_OK and E_FAIL for all methods. In most cases, E_OUTOFMEMORY is listed only for those methods which allocate memory that is returned to the consumer. The reason for listing E_OUTOFMEMORY in these sections is to remind consumer programmers that they can call the method successfully by requesting fewer returned values, such as fewer rows from GetNextRows.

Note Methods that return DB_S_ERRORSOCCURRED return this error only when one or more errors occur. Consumers should be aware that some providers incorrectly return DB_S_ERRORSOCCURRED when no errors but one or more warnings occur.


Arrays of Errors

A number of methods operate on multiple items or arrays of items. For example, IRowsetLocate::GetRowsByBookmark accepts an array of bookmarks and retrieves the rows associated with those bookmarks. When methods that operate on multiple items encounter an item-level error, such as when the row associated with a bookmark has been deleted, they flag that error and continue processing. They then return the code DB_S_ERRORSOCCURRED if at least one item was processed successfully, or the code DB_E_ERRORSOCCURRED if no items were processed successfully.

If the consumer requests it, these methods also return an array of DBROWSTATUS values, which detail the specific errors and warnings that occurred. The array is returned if the method returns a success code (such as S_OK), a warning code (such as DB_S_ERRORSOCCURRED), or the error code DB_E_ERRORSOCCURRED; it is not returned if the method returns any other error (E_ or DB_E_) code. DB_S_ERRORSOCCURRED is returned only if an error occurred. If only successes or warnings occurred, the method returns a success code or a warning code other than DB_S_ERRORSOCCURRED.

DBROWSTATUS Values

Condition | Results of the Row Operation | Exceptions
The DBROWSTATUS value includes the characters "_S_" | Successful, but the status is a warning that something else occurred | DBROWSTATUS_S_OK simply indicates success
The DBROWSTATUS value includes the characters "_E_" | Failed | None

DBROWSTATUS values are defined as follows. For information about the conditions under which each is returned, see the methods that return them.

  • DBROWSTATUS_S_OK

  • DBROWSTATUS_S_MULTIPLECHANGES

  • DBROWSTATUS_S_PENDINGCHANGES

  • DBROWSTATUS_S_NOCHANGE

  • DBROWSTATUS_E_CANCELED

  • DBROWSTATUS_E_CANTRELEASE

  • DBROWSTATUS_E_CONCURRENCYVIOLATION

  • DBROWSTATUS_E_DELETED

  • DBROWSTATUS_E_FAIL

  • DBROWSTATUS_E_INTEGRITYVIOLATION

  • DBROWSTATUS_E_INVALID

  • DBROWSTATUS_E_LIMITREACHED

  • DBROWSTATUS_E_MAXPENDCHANGESEXCEEDED

  • DBROWSTATUS_E_NEWLYINSERTED

  • DBROWSTATUS_E_OBJECTOPEN

  • DBROWSTATUS_E_OUTOFMEMORY

  • DBROWSTATUS_E_PENDINGINSERT

  • DBROWSTATUS_E_PERMISSIONDENIED

  • DBROWSTATUS_E_SCHEMAVIOLATION

Note Consumers should be prepared to handle DBROWSTATUS values outside of those defined in the specification.


Automation Error Objects

The following topics comprise a brief description of Automation error objects. OLE DB error objects use many of the same mechanisms.

  • How an Automation Component Returns an Error Object

  • How an Automation Consumer Retrieves an Error Object

  • Automation Error Objects and Threads


How an Automation Component Returns an Error Object

When a component using Automation causes an error, it can return an object to describe that error. To do this, the component makes the following calls:

  1. Calls CreateErrorInfo in the Automation DLL. This function creates an Automation error object that exposes ICreateErrorInfo and IErrorInfo.

  2. Calls the methods in ICreateErrorInfo to store information in the error object, such as the string describing the error and the GUID of the interface that caused the error.

  3. Calls QueryInterface to retrieve the IErrorInfo interface pointer on the error object. This interface pointer will identify the error object to all Automation components.

  4. Calls SetErrorInfo in the Automation DLL and passes it the IErrorInfo interface pointer. SetErrorInfo replaces its current error object, if any, with the new error object and adds a reference count to the new error object.

  5. Calls Release to release its reference count on the error object. This effectively transfers ownership of the error object from the component that caused the error to the Automation DLL.

The following code shows an example of how an Automation component might create an Automation error object and transfer ownership of the object to the Automation DLL.

#include <oledb.h>
int main() {
 ICreateErrorInfo *pcerrinfo;
 IErrorInfo   *perrinfo;

 // Error occurs in a method in the provider. (Not shown.)
 //Create an Automation error object.
 CreateErrorInfo(&pcerrinfo);

 // Use the returned ICreateErrorInfo interface pointer to add error information to
 // the object. (Not shown.)

 // Retrieve an IErrorInfo interface pointer on the object and call SetErrorInfo to
 // pass the error object to the Automation DLL.
 pcerrinfo->QueryInterface(IID_IErrorInfo, (void**)&perrinfo);

 SetErrorInfo(0, perrinfo);

 // Release the interface pointers on the object to finish transferring ownership of
 // the object to the Automation DLL.
 perrinfo->Release();
 pcerrinfo->Release();
} ;


How an Automation Consumer Retrieves an Error Object

When an Automation consumer retrieves an error object in response to a return code it received, the consumer makes the following calls:

  1. Calls QueryInterface on the component that returned the code to retrieve an ISupportErrorInfo interface pointer on that component. This interface must be supported by all components that create error objects.

  2. Calls the InterfaceSupportsErrorInfo method in this interface and passes it the IID of the interface containing the method that returned the code. The InterfaceSupportsErrorInfo method returns S_OK if the interface supports error objects (meaning the consumer should retrieve the current error object, if one exists) and S_FALSE if the interface does not support error objects (meaning the consumer should not retrieve the current error object because it applies to another interface and method).

  3. Calls the GetErrorInfo function in the Automation DLL. This function returns an IErrorInfo interface pointer on the current error object and releases its reference count on the error object, thus transferring ownership of the error object to the consumer.

  4. Calls the methods in the IErrorInfo interface to retrieve information from the error object.

  5. Calls Release on the error object to release it.

The following code shows an example of how an Automation consumer might retrieve and use an Automation error object:

#include <oledb.h>
IUnknown *pMyObject;
extern GUID IID_IMyInterface;

int main() {
 IErrorInfo    *perrinfo;
 ISupportErrorInfo *pserrinfo;
 HRESULT    hr;

 // Error occurs when calling method. (Not shown.)
 //Check that the current interface supports error objects.
hr = pMyObject->QueryInterface(IID_ISupportErrorInfo, (void**) &pserrinfo);
 if (SUCCEEDED(hr)) {
  hr = pserrinfo->InterfaceSupportsErrorInfo(IID_IMyInterface);
  if (hr == S_OK) {
   // Get the current error object.
   GetErrorInfo(0, &perrinfo);

   // Use the returned IErrorInfo interface to retrieve error information.
   // (Not shown).

   // Release the error object.
   perrinfo->Release();
  }
 }
} ;


Automation Error Objects and Threads

The Automation DLL maintains one error object per thread. Because the consumer is not required to retrieve the error object, the Automation DLL might be holding the error object generated by a previous method when a new method is called. Thus, components that return error objects must call the SetErrorInfo function with a null pointer at the start of each method in an interface that can generate error objects. This clears the current error object on the thread and ensures that any error object available after the method returns applies to the current method.


OLE DB Error Objects

Automation error objects do not support two capabilities required by OLE DB:

  • The ability to return multiple error records from a single call.

  • The ability to return provider-specific error information.

For example, each service component, even one in the chain of service components, might want to add its own error information and a provider might want to expose error information that is unique to it.

To solve this problem, OLE DB extends Automation error objects. In particular, it adds the ability for an error object to contain multiple error records. That is, an Automation error object effectively contains a single error record and looks like this:

Automation error object

An OLE DB error object contains multiple error records and looks like this:

OLE DB error object

Support for OLE DB error objects is optional. Providers can choose to generate them from all of their interfaces, from some of their interfaces, or from none of their interfaces. OLE DB error objects can be created by any method call. Although they are most commonly created when the method returns an error or warning, such as the DB_E_ERRORSINCOMMAND code returned by ICommand::Execute or the DB_S_ERRORSOCCURRED code returned by IRowsetUpdate::Update, they can also be returned when the method succeeds completely and returns S_OK or S_FALSE.

OLE DB Providers are not required to generate error objects, even if a method on an interface that supports error objects returns an error. In this case, even though ISupportErrorInfo returns S_OK, calling GetErrorInfo returns a null value. However, the provider is required to clear any existing error objects when any method on an interface that supports error objects is called, whether that method returns an error or not. Thus, the consumer can be assured that, if an error object does exist after calling a method on an interface that supports error objects, that the error object describes the outcome of that method.


Error Records

Records in OLE DB error objects are numbered starting from zero. The methods in IErrorRecords allow consumers to specify the record from which to retrieve error information. As error records are added, they are added to the top of the list (that is, the number of the existing error records is increased by 1 and a new record 0 is added), so consumers can start with the highest level of error information and then retrieve increasingly more detailed information.

Each error record is composed of five parts: an ERRORINFO structure, error parameters, a pointer to a custom error object, a dynamic error ID, and a lookup ID.

ERRORINFO Structure

The ERRORINFO structure returns most of the basic information associated with an error:

typedef struct tagERRORINFO {

HRESULT hrError;

DWORD dwMinor;

CLSID clsid;

IID iid;

DISPID dispid;

} ERRORINFO;

The elements of this structure are used as follows.

The ERRORINFO structure

Element | Description
hrError | The code returned by the method. This might be different in different records in the error object. For example, suppose a query processor service component opens rowsets provided by base table data providers. If ICommand::Execute in the query processor calls IOpenRowset::OpenRowset in a base table data provider and OpenRowset fails with DB_E_NOTABLE, Execute might return DB_E_ERRORSINCOMMAND.
dwMinor | A provider-specific error code.
clsid | The class ID of the object that returned the error.
iid | The interface ID of the interface that generated the error. iid can be different from the ID of the interface that defines the method the consumer called. For example, if a consumer calls Execute in a query processor, the query processor calls OpenRowset in a base table data provider, and OpenRowset fails, iid is IID_IOpenRowset, not IID_ICommand. If the method that generates an error belongs to more than one interface due to inheritance, this is the ID of the first interface in which the method is defined. For example, suppose IRowsetLocate::GetRowsAt is called through a pointer to IRowsetScroll, which inherits from IRowsetLocate. If GetRowsAt fails, iid is IID_IRowsetLocate, not IID_IRowsetScroll.
dispid | If defined, this might indicate the method that returned the error.

Error Parameters

Error parameters are provider-specific values that are incorporated into error messages. For example, the provider might associate the error message Cannot open table <param1> with a dwMinor value of 10.

In the example, the error parameters would be used to supply the name of the table that could not be opened. Error parameters are inserted into error messages by a provider-specific error lookup service. Thus, the format of error parameters and how they are substituted into error messages are completely provider specific. The code in IErrorInfo calls the error lookup service. For more information, see "Error Lookup Services" later in this chapter.

Custom Error Objects

Associated with each error record is a custom error object. An interface pointer to the object is stored in the record. If no custom error object exists, this pointer is null. The custom error object is the mechanism by which OLE DB error objects are extensible.

When an error record is added, the error object calls AddRef to add a reference to the custom error object. The provider that created the custom error object calls Release to release its hold on the custom error object. Thus, ownership of the custom error object is effectively transferred from the provider to the error object. The error object releases all custom error objects when it is released.

For example, ODBC-related providers can expose ISQLErrorInfo on a custom error object to return the SQLSTATE value. For more information, see ISQLErrorInfo.

Dynamic Error ID

The dynamic error ID is the ID of an error message created at run time by the error lookup service, as opposed to an error message hard-coded in the lookup service. The error object uses the dynamic error ID to release the dynamic error message when the error object is released. In most cases, all error messages associated with a single error object have the same dynamic error ID. For more information, see "Error Lookup Services" later in this chapter.

Lookup ID

The error lookup service uses the lookup ID in conjunction with the return code to identify the error description, Help file, and context ID for a specific error. The lookup ID also can be a special value, IDENTIFIER_SDK_ERROR, that tells the implementation of IErrorInfo that is shipped with the OLE DB SDK to ignore the provider's lookup service and use the description supplied in the error resource DLL shipped with the OLE DB SDK. For more information, see "Error Lookup Services" later in this chapter.


Interfaces Used by OLE DB Error Objects

The following table describes which interfaces providers must implement to support OLE DB error objects, which interfaces are implemented by code in the OLE DB SDK, and which functions are implemented by the Automation DLL.

Interfaces required of providers to support OLE DB error objects

Interface or function Comments
ISupportErrorInfo Defined by:Automation
Implemented by:Provider
Implemented on:OLE DB object (for example, rowset or command)

The consumer uses this interface to determine whether an object can return OLE DB error objects and, if so, which interfaces on that object can return OLE DB error objects.
IErrorLookup Defined by:OLE DB
Implemented by:Provider
Implemented on:Error lookup service

The error lookup service is required by the error object code in the OLE DB SDK and is used by that code to implement IErrorRecords and IErrorInfo. For more information, see "Error Lookup Services" later in this chapter.
IErrorRecords Defined by:OLE DB
Implemented by:OLE DB SDK
Implemented on:OLE DB error object

This is the main interface through which OLE DB error objects are accessed.
IErrorInfo Defined by:Automation
Implemented by:OLE DB SDK
Implemented on:OLE DB error object

OLE DB error objects expose IErrorInfo at the top level (that is, on the object itself) only so they can be treated by the Automation DLL and by Automation consumers as Automation error objects. If the methods on this interface are called, they will retrieve the information from record 0 using the default locale ID.

To retrieve error information from other records, the consumer calls IErrorRecords::GetErrorInfo and passes a record number and locale ID. The OLE DB error object returns a pointer to an IErrorInfo interface, which will return error information from the specified error record using the specified locale ID. The IErrorInfo pointer returned on a particular record is not the same as the IErrorInfo interface pointer exposed by the OLE DB error object through QueryInterface. For more information about how these interface pointers are used, see "How a Consumer Retrieves an OLE DB Error Object" and "Error Lookup Services" later in this chapter.
IClassFactory Defined by:Automation
Implemented by:OLE DB SDK
Implemented on:OLE DB error object class factory

OLE DB error objects are created by a class factory exposed by the error object code in the OLE DB SDK. You can create them by calling CoCreateInstance with the CLSID_EXTENDEDERRORINFO class ID or by calling IClassFactory::CreateInstance on a class factory object retrieved with DllGetClassObject. If the provider frequently creates error objects, the latter method is faster and therefore preferred.

OLE DB error objects cannot be created by the CreateErrorInfo function in the Automation DLL because the function can create only Automation error objects.
GetErrorInfo Defined by:Automation
Implemented by:Automation DLL
Implemented on:N/A

GetErrorInfo transfers ownership of the OLE DB error object on the current thread from the Automation DLL to the consumer. It identifies error objects by their IErrorInfo interface pointer, which is one reason OLE DB error objects directly expose IErrorInfo.
SetErrorInfo Defined by:Automation
Implemented by:Automation DLL
Implemented on:N/A

SetErrorInfo transfers ownership of the OLE DB error object on the current thread from the provider to the Automation DLL. It identifies error objects by their IErrorInfo interface pointer, which is one reason OLE DB error objects directly expose IErrorInfo.


How a Provider Returns an OLE DB Error Object

When an error occurs in a provider, the provider can return an OLE DB error object to describe that error. To do this, it makes the following calls:

  1. Calls GetErrorInfo in the Automation DLL to obtain ownership of the error object on the current thread and to retrieve an IErrorInfo interface pointer on it. Such an error object will exist only if added by a lower level provider. Thus, a provider at the bottom of a chain of providers will not find an error object, while a provider in the middle (a service component) might find such an object. In the latter case, the service component is merely gaining temporary ownership of the error object before returning it to the Automation DLL.

  2. Creates an OLE DB error object if none was retrieved in step 1. To do this, the provider calls CoCreateInstance with the CLSID_EXTENDEDERRORINFO class ID or IClassFactory::CreateInstance on a class factory object created earlier for this class. The latter method is faster and therefore preferred if the provider frequently creates error objects.

  3. Calls IErrorRecords::AddErrorRecord to add one or more error records to the error object. To AddErrorRecord it passes an ERRORINFO structure, the lookup ID, the error parameters, if any, an interface pointer on the custom error object that further describes the error, and a dynamic error ID.

  4. Calls QueryInterface to retrieve an IErrorInfo interface pointer on the error object. This interface pointer identifies the error object to all Automation components.

  5. Calls the function SetErrorInfo in the Automation DLL and passes it the IErrorInfo interface pointer. SetErrorInfo replaces the current error object on the thread, if any, with the new error object and adds a reference count to hold the new error object.

  6. Calls Release to release its reference count on the error object. This transfers ownership of the error object from the provider to the Automation DLL.

The following code shows an example of how a provider might create an OLE DB error object in response to being unable to open a table and transfer ownership of the object to the Automation DLL:

#include <oledb.h>

IClassFactory   *m_pErrorObjectFactory ;
IParseDisplayName *pppwow;
DWORD      errCantOpenTable;
IOpenRowset   *pSimpleProvider;
DBPROPSET    rgPropertySets[1];
IRowset     *pRowset;
DBID     TableID;
extern GUID    CLSID_THISISAM;
extern DISPID  DISPID_OpenRowset;
BSTR     bstrTableName;

int main() {
 ERRORINFO  ErrorInfo;
 HRESULT    hr, hrErr;
 IErrorInfo  *pErrorInfo;
 IErrorRecords *pErrorRecords;

 // Clear the current error object.
 SetErrorInfo(0, NULL);

 // Try to open the table or call another provider to do it.
 hrErr = pSimpleProvider->OpenRowset(NULL, &TableID, NULL, IID_IRowset, 1,
             rgPropertySets, (IUnknown**) &pRowset);

 if (!FAILED(hrErr))
  return hrErr;

 // An error or warning occurred while opening the table.
 //Get the current error object. If one does not exist, create a new one.

 GetErrorInfo(0, &pErrorInfo);

 if (!pErrorInfo)
  hr = m_pErrorObjectFactory->CreateInstance(NULL, CLSID_EXTENDEDERRORINFO,
                 (void**) &pErrorInfo);

 // Get an IErrorRecords interface pointer on the error object.
 hr = pErrorInfo->QueryInterface(IID_IErrorRecords, (void**) &pErrorRecords);

 // Set up a parameter to pass to the object.
 VARIANTARG  varg;
 VariantInit (&varg);
 DISPPARAMS  dispparams = {&varg, NULL, 1, 0};

 varg.vt = VT_BSTR;
 varg.bstrVal = SysAllocString(bstrTableName);

 // Fill in the ERRORINFO structure and add the error record.

 ErrorInfo.hrError = hrErr;
 ErrorInfo.dwMinor = errCantOpenTable;
 ErrorInfo.clsid = CLSID_THISISAM;
 ErrorInfo.iid   = IID_IOpenRowset;
 ErrorInfo.dispid  = DISPID_OpenRowset;
 hr = pErrorRecords->AddErrorRecord(&ErrorInfo,ErrorInfo.dwMinor,&dispparams,NULL,0);

 VariantClear(&varg);

 // Call SetErrorInfo to pass the error object to the Automation DLL.
 hr = SetErrorInfo(0, pErrorInfo);

 // Release the interface pointers on the object to finish transferring ownership of
 // the object to the Automation DLL.

 pErrorRecords->Release();
 pErrorInfo->Release();
 return hr;
};

The following code example shows how a provider adds a record to an OLE DB error object that includes a pointer to a custom error object:

#include <oledb.h>

class CSQLStateObject:public ISQLErrorInfo {
 public:
  CSQLStateObject(OLECHAR );
  CSQLStateObject();
  HRESULT __stdcall QueryInterface(REFIID, void** );
  ULONG __stdcall AddRef(void);
  ULONG __stdcall Release(void);
  HRESULT __stdcall GetSQLInfo(
   /* [out] */ BSTR __RPC_FAR *pbstrSQLState,
   /* [out] */ LONG __RPC_FAR *plNativeError);
};

IClassFactory *g_pErrorObjectFactory ;
extern DISPID  DISPID_GetData;
extern GUID  CLSID_THISISAM;
DWORD    errGeneralError;

int main() {
 ERRORINFO  ErrorInfo;
 HRESULT    hr, hrErr;
 IErrorInfo  *pErrorInfo;
 IErrorRecords *pErrorRecords;

 // Clear the current error object.
 SetErrorInfo(0, NULL);

 // Do something that causes an error to occur. (Not shown.)

 if (!FAILED(hrErr))
  return hrErr;

 // An error or warning occurred. Get the current error object. If one    //does not exist, create one.

 GetErrorInfo(0, &pErrorInfo);
 if (!pErrorInfo)
  hr = g_pErrorObjectFactory->CreateInstance(NULL, CLSID_EXTENDEDERRORINFO,
                 (void**) &pErrorInfo);

 // Get an IErrorRecords interface pointer on the error object.
 hr = pErrorInfo->QueryInterface(IID_IErrorRecords, (void**) &pErrorRecords);

 // Create a custom error object.
 CSQLStateObject* pMyErrObj = new CSQLStateObject(OLESTR("HY000"));

 // Fill in the ERRORINFO structure and add the error record.
 ErrorInfo.hrError = hrErr;
 ErrorInfo.dwMinor = errGeneralError;
 ErrorInfo.clsid = CLSID_THISISAM;
 ErrorInfo.iid   = IID_IRowset;
 ErrorInfo.dispid  = DISPID_GetData;
 hr = pErrorRecords->AddErrorRecord(&ErrorInfo, ErrorInfo.dwMinor, NULL,
              (void**) pMyErrObj, 0);

 // Release the interface pointer on the custom error object to finish transferring
 // ownership of it to the error object.
 pMyErrObj->Release();

 // Call SetErrorInfo to pass the error object to the Automation DLL.
 hr = SetErrorInfo(0, pErrorInfo);

 // Release the interface pointers on the object to finish transferring ownership of
 // the object to the Automation DLL.
 pErrorRecords->Release();
 pErrorInfo->Release();
 return hr;
};


How a Consumer Retrieves an OLE DB Error Object

When a consumer wants to retrieve an error object in response to a return code it received, it performs the following steps:

  1. Calls QueryInterface on the component that returned the code to retrieve an ISupportErrorInfo interface pointer on that component. This interface must be supported by all components that create error objects.

  2. Calls the InterfaceSupportsErrorInfo method in this interface and passes it the IID of the interface containing the method that returned the code. The InterfaceSupportsErrorInfo method returns S_OK if the interface supports error objects (meaning the consumer should retrieve the current error object, if one exists) and S_FALSE if the interface does not support error objects (meaning the consumer should not retrieve the current error object because it applies to another interface and method).

  3. Calls the GetErrorInfo function in the Automation DLL. This function returns an IErrorInfo interface pointer on the current error object and releases its reference count on the error object, thus transferring ownership of the error object to the consumer.

  4. Calls QueryInterface to return an IErrorRecords interface pointer on the error object.

  5. Calls the methods in IErrorRecords to retrieve information from the error object. To retrieve the error message, source, Help file, or Help context ID from a particular record, it first calls IErrorRecords::GetErrorInfo to retrieve an IErrorInfo interface pointer for that record. (This interface pointer is different from the one retrieved in step 3 as it applies to a specific record.) The consumer then calls methods using this pointer.

  6. Calls Release on the error object to release it.

For example, the following code shows how a consumer might retrieve and use an OLE DB error object:

#include <oledb.h>
#include <stdio.h>
IUnknown *pMyObject;
extern GUID IID_IMyInterface;
DWORD   MYLOCALEID;
BSTR    bstrSourceOfError, bstrDescriptionOfError;

int main() {
 IErrorInfo    *pErrorInfo;
 IErrorInfo    *pErrorInfoRec;
 IErrorRecords   *pErrorRecords;
 ISupportErrorInfo *pSupportErrorInfo;
 HRESULT    hr, hrErr;
 ULONG      i, ulNumErrorRecs;
 ERRORINFO    ErrorInfo;

 // Error or warning occurs when calling method.  (Not shown.)
 if (!FAILED(hrErr))
  return (hrErr);

 // Check that the current interface supports error objects.
 hr = pMyObject->QueryInterface(IID_ISupportErrorInfo,
            (void**) &pSupportErrorInfo);

 if (SUCCEEDED(hr)) {
  hr = pSupportErrorInfo->InterfaceSupportsErrorInfo(IID_IMyInterface);
  if (hr == S_OK) {
   // Get the current error object. Return if no error object exists.
   GetErrorInfo(0,&pErrorInfo);
   if (!pErrorInfo) return (hrErr);

   // Get the IErrorRecord interface and get the count of error recs.
   pErrorInfo->QueryInterface(IID_IErrorRecords, (void**) &pErrorRecords);
   pErrorRecords->GetRecordCount(&ulNumErrorRecs);

   // Read through error records and display them.
   for (i = 0; i < ulNumErrorRecs; i++) {
    // Get basic error information.
    pErrorRecords->GetBasicErrorInfo(i, &ErrorInfo);

    // Get error description and source through the IErrorInfo interface
    // pointer on a particular record.
    pErrorRecords->GetErrorInfo(i, MYLOCALEID, &pErrorInfoRec);
    BSTR bstrDescriptionOfError = NULL;
    BSTR bstrSourceOfError = NULL;

    pErrorInfoRec->GetDescription(&bstrDescriptionOfError);
    pErrorInfoRec->GetSource(&bstrSourceOfError);

    // At this point, you could call GetCustomErrorObject and query for
    // additional interfaces to determine what else happened.

    wprintf(
     OLESTR("HRESULT: %lx, Minor Code: %lu, Source: %s, Description: %s"),
     ErrorInfo.hrError,
     ErrorInfo.dwMinor,
     bstrSourceOfError,
     bstrDescriptionOfError);

    // Free the resources.
    SysFreeString(bstrDescriptionOfError);
    SysFreeString(bstrSourceOfError);
    pErrorInfoRec->Release();
   }

   // Release the error object.
   pErrorInfo->Release();
   pErrorRecords->Release();
  }
 }
 return (hrErr);
};


OLE DB Error Objects and Threads

Like Automation error objects, OLE DB error objects use the Automation DLL to maintain one error object per thread. Because the consumer is not required to retrieve the error object, the Automation DLL might be holding the error object generated by the previous method when a new method is called. Thus, providers that return error objects must call the SetErrorInfo function with a null pointer at the start of each method in an interface that can generate error objects. This clears the current error object on the thread and ensures that any error object available after the method returns applies to the current method.

Because providers at each level must clear the current error object, providers should be careful not to transfer ownership of an error object to the Automation DLL and then call a method in a lower-level provider. Doing so might result in the lower-level provider clearing the just-created error object from the thread and losing the information it contains.


Error Lookup Services

The ERRORINFO structure does not contain the error messages and help file information that IErrorInfo can return. Instead, this information is stored in a provider-specific lookup service. There are several reasons for this. First, it allows the information to be easily localized. Second, it means that the information is not created and passed around until it is requested. Third, it provides an easy mechanism by which the provider can parameterize error messages.

The information in this lookup service can be created when the lookup service is written or it can be created at run time. The former case, known as static errors, is common when the lookup service is tied to a single database and the programmer knows ahead of time what the error messages are. For example, a provider built directly over an Oracle database might use static errors.

The latter case, known as dynamic errors, is common when the lookup service is not tied to any particular database and the programmer cannot predict the error messages. For example, a provider built on top of any ODBC driver might use dynamic errors. The lookup service creates dynamic errors at run time and releases them when the error object with which they are associated calls IErrorLookup::ReleaseErrors as part of its release code. Dynamic errors are identified by a dynamic error ID. In most cases, all dynamic errors associated with a single error object have the same ID. This is not required, but it is more efficient when releasing error objects.

An error lookup service must be created by each provider that supports OLE DB error objects. The lookup service exposes the IErrorLookup interface. For information about the registry entries used by error lookup services, see "Error Lookup Service Registry Entries" in Chapter 14.

When a consumer calls IErrorInfo::GetDescription or IErrorInfo::GetSource for a particular record in an OLE DB error object, the error object creates an instance of the error lookup service. It then calls IErrorLookup::GetErrorDescription and passes the hrError element of the ERRORINFO structure in the record, along with the lookup ID, the locale ID, and the parameters. GetErrorDescription uses the information provided to retrieve the error description and inserts the parameters into this description. However, if the lookup ID is set to IDENTIFIER_SDK_ERROR, the error object bypasses the error lookup service and retrieves the error description from the error resource DLL shipped with the OLE DB SDK. Finally, the error object returns the completed, localized error message and source to the error object, which returns them to the consumer.

A similar process occurs when the consumer calls IErrorInfo::GetHelpFile or IErrorInfo::GetHelpContext, although no parameters are passed. If the lookup ID is set to IDENTIFIER_SDK_ERROR, then the error object uses the error lookup service to get the Help file and Help context ID.


Programming Considerations

Overview of Programming Considerations

This chapter discusses various OLE DB programming considerations.

For more information on | Go to
OLE DB support for aggregation | "Aggregation" in this chapter
Registering OLE DB components | "Registry Entries" in this chapter
Identifying enumerators in the registry | "Enumerator Registry Entries" in this chapter
Identifying provider registry entries | "Provider Registry Entries" in this chapter
Identifying error lookup registry entries | "Error Lookup Service Registry Entries" in this chapter
Provider support for single, apartment, and free-threaded OLE DB objects | "Provider Threading Models" in this chapter
Free-threaded consumer responsibilities | "Consumer Threading Responsibilities" in this chapter
Preventing interruptions from the wrong thread | "Threading and Notifications" in this chapter
Error objects and thread types | "Threading and Error Objects" in this chapter
Threading and transaction assumptions | "Threading and Transactions" in this chapter
Storage objects and IPersist* objects threading types | "Threading and OLE Objects" in this chapter
Freeing memory by the consumer | "Argument Memory" in this chapter


Aggregation

OLE DB objects, like other COM objects, can support aggregation. To facilitate this, all methods that create a new object accept an IUnknown interface pointer to the controlling unknown. If a method, such as IRowsetInfo::GetSpecification, returns an interface pointer to an existing object, it should use the controlling unknown of the object to return the interface pointer, as opposed to using the object itself.


Registry Entries

OLE DB defines registry entries for providers, enumerators, and error lookup services. These entries contain the class ID of the component and its associated DLL, description, display name, and so on. There are several reasons for registering these OLE DB components:

  • Registration is required as part of being an OLE COM component, and provides a way to create an instance of an OLE object.

  • Registration provides the mechanism for the root enumerator shipped with the OLE DB component of the Data Access SDK to obtain a list of providers on the machine.

  • Registration provides the mechanism for the root enumerator to obtain a list of other enumerators. An enumerator is an OLE COM server that implements ISourcesRowset.

  • Registration provides the mechanism for the standard OLE DB error object to invoke the provider's custom error lookup service.

  • Data may be available in different formats. Registration provides a way to obtain a list of data formats and the providers that support those data formats. Data formats are created by providers, and could be provider-specific. Each provider may consider itself capable of supporting many data formats. A usage example is to group providers together and help consumers interactively decide which provider might work for them.

For more information about registry entries, see the Object Linking and Embedding Programmer's Reference, Volume 1.


Enumerator Registry Entries

Enumerators are identified in the registry by the OLE DB Enumerator subkey under the class ID of the enumerator. Under HKEY_CLASSES_ROOT,, enumerators must have the following subkeys and values for the programmatic identifier (ProgID):

Keys and Subkeys |  |  |  |  | Values
HKEY_CLASSES_ROOT
 | CLSID |  |  |  |
 |  | EnumeratorCLSID |  |  | FriendlyDisplayName
 |  |  | ProgID |  | EnumeratorProgID
 |  |  | VersionIndependentProgID |  | VersionIndependentProgID
 |  |  | InprocServer32 |  | EnumeratorDLLFilename
 |  |  |  | ThreadingModel | Apartment|Free|Both
 |  |  | OLE DB Enumerator |  | Description

For more information about enumerators, see "Enumerators" in Chapter 2.


Provider Registry Entries

Providers are identified in the registry by the OLE DB Provider subkey under the class ID of the provider. Under HKEY_CLASSES_ROOT, providers must have the following subkeys and values for the extension of persisted data source object files and for the programmatic identifier :

Keys and Subkeys |  |  |  |  |  | Values
HKEY_CLASSES_ROOT
 | ProviderProgID |  |  |  |  | PersistedFilenameExtension
 |  | CLSID |  |  |  | ProviderCLSID
 |  |  | ProviderCLSID |  |  | FriendlyDisplayName
 |  |  |  | ProgID |  | ProviderProgID
 |  |  |  | VersionIndependentProgID |  | VersionIndependentProgID
 |  |  |  | InprocServer32 |  | ProviderDLLFilename
 |  |  |  |  | ThreadingModel | Apartment|Free|Both
 |  |  |  | OLE DB Provider |  | Description

When an instance of the object is first created, providers return an interface pointer on a data source object. For more information about data source objects, see "Data Source Objects" in Chapter 2.


Error Lookup Service Registry Entries

Error lookup services are identified in the registry by the ExtendedErrors subkey under the class ID of the error lookup service. Under HKEY_CLASSES_ROOT, error lookup services must have the following subkeys and values for programmatic identifier (ProgID):

Keys and Subkeys |  |  |  |  |  |  |  | Values
HKEY_CLASSES_ROOT
 | ProviderProgID Errors |  |  |  |  |  |  | FriendlyDisplayName
 |  | CLSID |  |  |  |  |  | LookupCLSID
 |  |  | ProviderCLSID |  |  |  |  |
 |  |  |  | ExtendedErrors |  |  |  | FriendlyDisplayName
 |  |  |  |  | LookupCLSID |  |  | Description or DisplayName
 |  |  |  |  |  | ProgID |  | LookupProgID
 |  |  |  |  |  | VersionIndependentProgID |  | VersionIndependentProgID
 |  |  |  |  |  | InprocServer32 |  | ErrorLookupServiceDLLFilename
 |  |  |  |  |  |  | ThreadingModel | Apartment|Free|Both

Note that the first two class ID entries are entered under the class ID of the provider to identify the error lookup service for that provider. For more information about error lookup services, see "Error Lookup Services" in Chapter 13.


Provider Threading Models

Providers can support single, apartment, or free-threaded OLE DB objects as long as they advertise which models are supported. OLE DB recommends that providers build free-threaded implementations, which provide the best combination of speed and safety.

The threading model of a provider is described by a combination of the threading model registered for its Class ID's in the registry, the DBPROP_DSOTHREADMODEL property, and the DBPROP_ROWTHREADMODEL property. The threading model for all nonrowset objects supported by the provider is described by the property DBPROP_DSOTHREADMODEL, and the threading model for rowsets is described by the property DBPROP_ROWTHREADMODEL. The threading model returned in DBPROP_DSOTHREADMODEL and DBPROP_ROWTHREADMODEL may be stricter than the thread model registered under the provider's CLSID in the case where additional threading limitations are imposed by underlying components, or where different services are invoked to support different objects. For more information, see "Rowset Properties" in Appendix C.


Single-Threaded Providers and Consumers

Single-threaded providers work reliably only with single-threaded consumers. Single-threaded consumers can work with all types of objects.


Provider Threading Responsibilities

Free-threaded in-process OLE DB objects have the following threading responsibilities:

  • To implement the guarding mechanisms necessary to guarantee safe operation of the OLE DB objects, when objects are called from different threads. The choice of these mechanisms is provider-specific.

  • To guarantee an operation free of deadlocks and of starvation.

  • In the case of rowset objects, to shield other threads executing concurrently from the effects of methods like IRowsetUpdate::Update or IRowsetRefresh::ResynchVisibleData that may change the state of the rowset's copies of the rows. Update should be an atomic operation. While working in immediate update mode, all methods that modify the rowset's copies of the rows (IRowsetChange::SetData, IRowsetChange::DeleteRows, and so on) should also be atomic operations.

  • In the case of rowset objects, to guarantee that all other provider-implemented objects for which the rowset is a factory (such as other rowsets or OLE storage objects) are also free-threaded.

  • To guarantee a minimum of marshaling support for their use by apartment-model consumers; for instance, by aggregating to them an IMarshal object obtained from CoCreateFreeThreadedMarshaler. For more information, see the Microsoft Win32® SDK, OLE Programmer's Reference. The provider can perform this aggregation lazily; that is, defer it until QueryInterface requests IMarshal.

Apartment model in-process OLE DB objects have the following threading responsibilities:

  • The provider must assume that the OLE DB object will always receive direct calls on the same thread (apartment) on which it was created. Objects, however, can be created on different threads. Apartment objects belong to the same thread for all their existence.

  • Apartment objects must use a proxy-stub mechanism to accept calls from other apartments. For this, CoMarshalInterface accepts a flag (MSHCTX_INPROC) to describe an interapartment communication. In the apartment model, calls using these proxy-stubs are synchronized by OLE in the same manner as Windows messages synchronize calls.

Apartment model in-process OLE DB objects

  • In the case of rowset objects, the provider should guarantee that all other objects created by the rowset and returned by it—such as other rowsets or OLE storage objects—are also apartment model and belong to the same thread as their ancestor rowset.

  • Apartment OLE DB objects should not share state with other OLE DB objects because these objects may reside in different threads. The exception to this rule is OLE DB objects that have an ancestor/descendant relationship, such as those created by IColumnsRowset::GetColumnsRowset. Because these objects belong to the same apartment, they are guaranteed to execute serially and may share state safely, if needed.

  • Strictly speaking, the provider should furnish marshaling support (proxy/stub) for all the object's interfaces.


Consumer Threading Responsibilities

Free-threaded consumers have the following threading responsibilities:

  • To call CoInitializeEx on each thread, with a null pointer as the first parameter and COINIT_MULTITHREADED as the second parameter, to initialize it as free-threaded.

  • When passing interfaces to other threads, to transfer pointers directly only when:
    • the other thread is free-threaded, and

    • the pointers correspond to free-threaded objects.

    The set of threads initialized as free-threaded acts similarly to single big apartment, so pointers can be freely passed between these threads. To transfer a free-threaded object to a real apartment, it is recommended, although not required, to marshal the pointer at the source and unmarshal it at the destination. To transfer an apartment-threaded object to any free-threaded thread, it is necessary to marshal the pointer at the source and unmarshal it at the destination.

Apartment-threaded consumers have the following threading responsibilities:

  • To implement a message queue.

  • To call CoInitialize or OLEInitialize on the thread.

  • To always marshal pointers of OLE DB (and other OLE) objects to other apartments by using CoMarshalInterface and CoUnmarshalInterface calls. Calls to these methods trigger the deployment of proxies and stubs by the COM infrastructure. Well-behaved apartment-model consumers may interact efficiently with free-threaded servers if the free-threaded server aggregates with a generic free-threaded marshaler that COM supplies by using CoCreateFreeThreadedMarshaler.


Threading and Notifications

In the case of free-threaded rowset objects, IConnectionPoint should be enabled to handle apartment-model or single-thread listeners. Single-threaded listeners should always be called on their thread so the listeners are not unduly interrupted from the wrong thread.

The creator of a rowset must use CoMarshalInterface when making the object's interfaces available to other threads. Because these are free-threaded rowsets, COM will stay out of the way. If IConnectionPoint receives an interface from IRowsetNotify and it wants to use that interface on another thread, it must also use CoMarshalInterface to move the interface to another thread. Whatever moves the interface to another thread must call CoMarshalInterface. The receiving thread doesn't just get the pointer, but becomes involved by extracting the pointer through CoUnmarshalInterface.

This entails that a rowset is written internally to use multiple threads, not just to run on multiple threads. As a consequence of this, a rowset should keep track of the threads on which a listener is connected to it.

Multiple threads


Threading and Error Objects

Free-threaded OLE DB objects should produce free-threaded error objects. Apartment-threaded OLE DB objects should produce apartment-model error objects, which will be associated to the same thread.

Because Automation maintains an error object for each thread, a provider that implements free-threaded rowsets must take care of calling SetErrorInfo by using the same thread that invoked the offending method.


Threading and Transactions

All consumers accessing a shared object such as a rowset through different threads are assumed to be within the same transaction.


Threading and OLE Objects

OLE DB supports two kinds of OLE objects in columns and parameters: storage objects and IPersist* objects.

Rowsets are factories of storage objects and determine the threading model of these objects. While accessing a storage object through IRowset::GetData, free-threaded rowsets return free-threaded objects, and apartment rowsets return apartment objects that will belong to the same thread.

IPersist* objects are implemented by the other parties. Their threading model is dictated by threading marking of their class factory. This marking may be absent and, if present, it can be either "Apartment" or "Both." Consumers need mechanisms to obtain information about the IPersist* object's threading marking when GetData returns the object.

For more information about storage objects and IPersist* objects, see Chapter 7, "BLOBs and OLE Objects."


Argument Memory

The consumer can safely free memory at any time after a method returns. It does not matter if this memory was allocated by the consumer and passed to the provider or allocated by the provider and returned to the consumer. From the provider's point of view, this means that the provider must not access any memory used by a method except while that method is executing.


Asynchronous Processing

Overview of Asynchronous Processing

Asynchronous processing enables methods to return immediately without blocking on the calling thread. Consumers request asynchronous processing when initializing a data source, or when opening or populating a rowset. The consumer must explicitly request asynchronous processing. Otherwise, the provider can perform asynchronous operations in the background, but must behave synchronously, blocking if necessary, until the underlying asynchronous operation completes.

Data Source Initialization properties are used when consumers initialize a data source asynchronously. Consumers wishing to asynchronously open a rowset use the Command Execution and Rowset Generation properties. Consumers wishing to asynchronously populate a rowset use properties of Rowset Population.

For more information on | Go to
Calling thread | "Provider Threading Models" in Chapter 14, "Programming Considerations"
Consumers | "Consumers and Providers" in Chapter 1, "Overview of OLE DB"
Data source | "Data Source Objects" in Chapter 2, "Enumerators, Data Source Objects, and Sessions"
Rowset | Chapter 4, "Rowsets"
Asynchronously initialize a data source | "Asynchronous Data Source Initialization" in this chapter
Asynchronously open a rowset | "Asynchronous Command Execution and Rowset Generation" in this chapter
Asynchronously populate a rowset | "Asynchronous Rowset Population" in this chapter

Consumers can poll for the status of an asynchronous operation by calling IDBAsynchStatus::GetStatus on the object being asynchronously processed, or can register for progress notifications by passing the consumer's IDBAsynchNotify interface to the connection point of the object being asynchronously processed.

Asynchronous processing can occur in phases. If the object is asynchronously initialized, the initialization phase occurs first. While the object is in the initialization phase it is in an uninitialized state. Attempting to call other interfaces may fail, and attempting to call QueryInterface for interfaces other than IConnectionPointContainer may return E_NOINTERFACE.

Once all asynchronous processing has completed for an operation, the provider calls IDBAsynchNotify::OnProgress with ulAsynchPhase set to DBASYNCHPHASE_COMPLETE for that operation, followed by IDBAsynchNotify::OnStop to notify listeners that asynchronous processing has completed. Any calls to IDBAsynchStatus::GetStatus for that operation return an HRESULT indicating the outcome of the operation, with ulAsynchPhase set to DBASYNCHPHASE_COMPLETE, pulProgress equal to pulProgressMax, and ppwszStatusString set to a provider-specific value indicating completion, or NULL.


Asynchronous Data Source Initialization

Consumers wishing to initialize a data source asynchronously set the DBPROPVAL_ASYNCH_INITIALIZE bit in the DBPROP_INIT_ASYNCH property prior to calling IDBInitialize::Initialize. When this property is set, the provider returns immediately from the call to Initialize with either S_OK if the operation has completed immediately, or DB_S_ASYNCHRONOUS if the initialization will continue asynchronously. Consumers can call IDBAsynchStatus::GetStatus to get the status of the initialization, or can register for progress notifications by passing the consumer's IDBAsynchNotify interface to the data source's connection point. The consumer must explicitly request Asynchronous Data Source Initialization. Otherwise, IDBInitialize::Initialize does not return until the data source is completely initialized.


Asynchronous Command Execution and Rowset Generation

Consumers that want to asynchronously open a rowset set the DBPROPVAL_ASYNCH_INITIALIZE bit in the DBPROP_ROWSET_ASYNCH property. When setting this bit, it is permitted to ask for IConnectionPointContainer to obtain IID_IDBAsynchNotify, or IID_IDBAsynch, or IID_IUnknown (and QueryInterface for either of the other two interfaces).

Additionally, if the consumer calls Execute, OpenRowset, GetRowset, GetColumnsRowset, GetResult, GetSourcesRowset, or any other method that returns a rowset, riid must be set to IID_IDBAsynchStatus or IID_IConnectionPointContainer.

The method returns immediately with S_OK if the rowset initialization completes immediately, or DB_S_ASYNCHRONOUS if the rowset will continue initializing asynchronously, with ppRowset set to the requested interface on the rowset. Until the rowset is fully initialized, it behaves as if it is in a zombie state, and calling QueryInterface for interfaces other than IID_IConnectionPointContainer or IID_IDBAsynchStatus may return E_NOINTERFACE. Unless the consumer explicitly requests asynchronous processing, the rowset is initialized synchronously. All requested interfaces are available when the method requesting the rowset returns. This does not necessarily mean that the rowset is fully populated, but it must be complete and fully functional.

If the executed command does not return a rowset, it still returns immediately with an object that supports IDBAsynchStatus.

If output parameters are specified on the command, they are usually not available until the rowset is completely initialized, and may not be available until all of the data has been read from the rowset.

To obtain multiple results, the consumer sets the DBPROPVAL_ASYNCH_INITIALIZE bit of the DBPROP_ROWSET_ASYNCH property prior to executing the command, then calls ICommand::Execute with riid set to IID_IMultipleResults, and finally IMultipleResults::GetResult with riid set to IID_IDBAsynchStatus to retrieve individual results asynchronously. When consumers request a multiple-results object from Execute, providers should return immediately with the multiple-results object, and save processing until the consumer calls GetResult.

To cancel creation of the rowset, the consumer may call IDBAsynchStatus::Abort, or may simply release all interfaces on the rowset. Once the rowset's reference count goes to zero, any asynchronous processing is canceled and the rowset is released. Calling IDBAsynchStatus::Abort still requires releasing the interface.


Asynchronous Rowset Population

Consumers wishing to asynchronously populate a rowset set the DBPROPVAL_ASYNCH_SEQUENTIAL or DBPROPVAL_ASYNCH_RANDOM bits of the DBPROP_ROWSET_ASYNCH property prior to opening the rowset.

During the DBASYNCHPHASE_POPULATION phase, even though the rows are not fully populated, the rowset itself is fully initialized; that is, all interfaces supported by the rowset are available and fully functional (although the set of rows returned may be restricted).

An asynchronously populated rowset is distinct from a lazily populated rowset. A lazily populated rowset may return a rowset prior to completely populating the rows, but prevents additional method calls, if necessary, in order to return the requested rows. Thus, a lazily populated rowset appears as a synchronously populated rowset to the consumer. In contrast, an asynchronously populated rowset returns from a request to retrieve rows immediately with as many of the requested rows as are available at the moment. If fewer than the requested number of rows are available at the time of the call, the method returns DB_S_ENDOFROWSET even though additional rows may appear later. The consumer must request notifications or call IDBAsynchStatus in order to determine when population is complete. Unless the consumer explicitly requests asynchronous population, the provider must behave as if the rowset is fully populated upon creation. Requesting rows blocks additional calls until the requested number of hRows are obtained or the end of the rowset is reached.

Asynchronously populated rowsets may be sequentially asynchronously populated, if rows are always appended to the end of the rowset, or randomly asynchronously populated if new rows may be inserted randomly within the rowset.

Sequentially asynchronously populated rowsets call IDBAsynchNotify::OnProgress to signal the addition of new rows to the end of the rowset as a result of asynchronous population. Randomly asynchronously populated rowsets call IRowsetNotify::OnRowChange with DBREASON_ROW_ASYNCHINSERT to signal the addition of new rows as a result of asynchronous population.


Integrated Indexes

Overview of Integrated Indexes

OLE DB indexes are designed for use with base tables (tables opened by IOpenRowset::OpenRowset). Providers built over command processors, such as SQL query processors, are expected to use indexes internally but not expose them through OLE DB. The primary consumers of OLE DB indexes are:

  • Query processors accessing data in OLE DB providers, such as DBMS storage engines and ISAMs, which do not support commands.

  • Service components or providers that implement IViewRowset, IViewFilter, or IViewSort.

OLE DB provides the ability to open separate rowsets over base tables and indexes, allowing the consumer to traverse each separately. However, certain providers can support indexes and base table data on the same rowset (also known as a clustered index). Providers can expose this as an integrated index by exposing IRowsetIndex directly on the base table rowset. This allows the consumer to set index ranges and seek index values directly on the base table rowset, without having a separate (and in this case redundant) index rowset. Such rowsets are said to have integrated index behavior. Providers can support separate indexes, integrated indexes, or both.

This chapter discusses requesting, using, and the structure of integrated indexes. The structure is more loosely defined than a nonintegrated index rowset. An element of their use, of course, is the ability to fetch data. The topic of row fetching includes rowset order, next fetch position, range, updating index rows, visibility of index changes, and the effect of changes on the next fetch position and index range. Accessors, the collection of information that describes how data is stored in the consumer's buffer, are discussed in light of methods and restrictions. Restrictions on integrated indexes and executing commands are discussed also. An integrated index example is given.

For more information on | Go to
Nonintegrated indexes | Chapter 8, "Indexes"
Requesting integrated indexes | "Requesting Integrated Indexes" in this chapter
Using integrated indexes | "Using Integrated Indexes" in this chapter
Structure of integrated indexes | "Structure of Integrated Indexes" in this chapter
Fetching rows | "Fetching Rows" in this chapter
Accessor methods and restrictions | "Accessors" in this chapter "Accessors" in Chapter 6, "Getting and Setting Data"
Restrictions on integrated indexes and commands | "Integrated Indexes and Commands" in this chapter Chapter 3, "Commands"
Calling threads | "Provider Threading Models" in Chapter 14, "Programming Considerations"


Requesting Integrated Indexes

Whether an index on a base table is integrated or not is reported in the INTEGRATED column of the INDEXES schema rowset (see "Schema Rowsets" later in this chapter).

You can obtain integrated index behavior by opening a rowset on an integrated index—that is, passing valid table and index IDs to OpenRowset. In this case, the consumer deals with a single rowset to read the data in index order, to have access to all base table columns, and to update both the base table and associated index based on calls to IRowsetChange on the rowset.


Using Integrated Indexes

To fetch base table rows using two separate rowsets, one over the base table and another over the index, the consumer:

  1. Calls IRowsetIndex::SetRange on the index rowset to set the range.

  2. (Optional) Calls IRowsetIndex::Seek on the index rowset to seek a particular index value. This sets the next fetch position on the index rowset to the first row in the index rowset that matches the specified value.

  3. Calls IRowset::GetNextRows on the index rowset to fetch the index entry.

  4. Extracts a bookmark from the index row that points to the corresponding row in the base table rowset.

  5. Calls IRowsetLocate::GetRowsAt on the base table rowset with the extracted bookmark to get the base table row.

To fetch base table rows using an integrated index, the consumer:

  1. Calls IRowsetIndex::SetRange on the base table rowset to set the range.

  2. (Optional) Calls IRowsetIndex::Seek on the base table rowset to seek a particular index value. This sets the next fetch position on the base table rowset to the first row in the index that matches the specified value.

  3. Calls IRowset::GetNextRows on the base table rowset to fetch the base table row.

When using an integrated index rowset, there are three important differences from using separate index and base table rowsets:

  • The consumer sets index ranges and seeks for index values on the base table rowset itself.

  • No bookmarks are used in the process. This means that the base table rowset does not need to expose bookmarks, nor does it need to expose IRowsetLocate.

  • Updates applied on this rowset are simultaneously applied to the base table and index.


Structure of Integrated Indexes

The structure of a nonintegrated index rowset is rigidly defined: The key columns of the index occur in order and there is a separate column—not column 0—that contains the bookmarks that point to the base table rowset.

These restrictions do not apply to rowsets with integrated indexes. Forcing the key columns to occur in order could force the rowset to reorder the rowset columns each time the consumer changes the current index, an unacceptable result. The separate column of bookmarks pointing to base tables is not needed, as was explained earlier in this chapter in "Using Integrated Indexes."

To determine which columns are key columns and the order in which they occur, the consumer calls IRowsetIndex::GetIndexInfo. The provider returns an array of DBINDEXCOLUMNDESC structures with the DBIDs of the key columns and the key types (ascending or descending) of each column. The arrays are returned in order from most significant to least significant. To determine the ordinals of the key columns, the consumer calls IColumnsInfo::MapColumnIDs with the returned DBIDs.


Rowset Order

An index implies an order in which a base table is to be traversed. With nonintegrated indexes, this order is entirely contained in the index rowset. The consumer imposes this order on the base table rowset by reading the index rowset sequentially and fetching rows in the base table rowset by bookmark.

With integrated indexes, this order is imposed directly on the base table rowset. The methods that require a rowset order (IRowset::GetNextRows, IRowsetLocate::GetRowsAt, and IRowsetScroll::GetRowsAtRatio) fetch rows in the order specified by the current index. For GetNextRows, this means that repeated calls will traverse the entire rowset in index order. For GetRowsAt and GetRowsAtRatio, this means that the method will start at the specified row and, if more than one row is requested, it will fetch rows in index order. IRowsetLocate::GetRowsByBookmark is not affected because it fetches only single, isolated rows.

If there is no current index (the base table rowset was opened without an index), these methods traverse the rowset in an undefined order, typically in the order that data is stored in the base table.


Next Fetch Position

With nonintegrated indexes, the next fetch position on the base table rowset was affected only by GetNextRows and RestartPosition. In a rowset with an integrated index, this can also be changed by IRowsetIndex::Seek. This is the same as the Seek functionality on separate index rowsets.


Range

With nonintegrated indexes, the range on the base table rowset used by GetNextRows, GetRowsAt, and GetRowsAtRatio was always the entire rowset. In a rowset with an integrated index, this can be changed by IRowsetIndex::SetRange. This is the same as the SetRange functionality on nonintegrated index rowsets.


Updating Index Rows

When the consumer updates, deletes, or inserts a base table row with IRowsetChange, the provider automatically updates, deletes, or inserts the corresponding entries in all integrated indexes associated with the base table. In other words, for integrated indexes, DBPROP_INDEX_AUTOUPDATE is always VARIANT_TRUE.


Visibility of Changes in the Index

The visibility of pending changes in the index is governed by the same mechanisms that govern the visibility of pending changes in the base table, with one important difference: If an index column in a row is updated, this is considered to be the same as deleting that row from the index and inserting a new row with the new value. Thus, the visibility of pending deletes for completely released and refetched rows depends on whether the rowset exposes IRowsetIdentity. The visibility in the index of pending inserts depends on DBPROP_RETURNPENDINGINSERTS. Consumers should be aware that pending updates to key columns might not appear in the correct position in the index until the update is transmitted to the data source.

The visibility in the index of transmitted changes is governed by the same mechanisms that govern the visibility of transmitted changes in the base table. In other words, it depends on the DBPROP_OWNUPDATEDELETE, DBPROP_OWNINSERT, and DBPROP_REMOVEDELETED properties.


Effect of Changes on the Next Fetch Position

The next fetch position is unaffected by updates, deletes, and inserts. However, the definition of "unaffected" is provider specific due to the complexity of implementation strategies.

For example, suppose the current index applies to column 1, which has values in succeeding rows of "A", "B", "C", "D", "E", and "F". If the last row fetched contains the value "C", and the consumer changes this value to "EE", in most cases the next row fetched will contain the value "D", not "F". Assuming changes are visible, they will be fetched in the expected order. For example, if the last row fetched contains the value "C" and the consumer changes this value to "CC", the next row fetched might contain the value "CC" or "D".


Effect of Changes on the Index Range

The current index range is unaffected by updates, deletes, and inserts.

Whether and where changed rows are visible within the current range depends on the rules governing visibility of changes in the index. For example, suppose the range on an index column is "A" to "L" and the last row fetched had a value in this column of "H". If the value of that column in a currently held row is changed from "B" to "K", the change is transmitted to the data source, and transmitted updates are visible in the index, then the row will be fetched if the consumer completes the scan to the end of the range. If the value of the column is changed instead to "M" and transmitted, the row will not be fetched.


Accessors

Accessors used with IRowsetIndex::SetRange and IRowsetIndex::Seek for an integrated index follow the same restrictions as accessors used with these methods for a separate index:

  • For each key column bound in the accessor, all more-significant key columns must also be bound.

  • Key columns must be bound in order from most significant to least significant.

  • If the accessor binds any non-key columns, key columns must be bound first.

Accessors used with SetRange and Seek for an integrated index can also be used with other methods, such as IRowset::GetData, and vice versa.

Because the order of significance of columns may change for each index, it is usually necessary to define a different accessor to be used on SetRange or Seek calls for each index.


Integrated Indexes and Commands

Integrated indexes usually cannot be exposed by rowsets generated by executing a command. Thus, if the consumer attempts to set DBPROP_IRowsetIndex to VARIANT_TRUE, ICommandProperties::SetProperties returns DBPROPSTATUS_NOTSUPPORTED in the DBPROP structures for this property.

The primary reason for this restriction is that commands can impose an order on a rowset, such as the ORDER BY clause in SQL, and this would conflict with the order imposed by the index.


Integrated Index Example

The following code performs the same function as the code example in "Index Example," except that the index is integrated, not separate. It gains access to the Employees table through the Emp_LastName_Index index, using IRowsetIndex to specify and seek that range. The index has a single key column, Emp_LastName, of type DBTYPE_WSTR with a length of 30 characters.

The code sample performs the following actions:

  1. Initializes the database and creates a session (not shown).

  2. Sets properties to request IRowsetIndex, which tells the rowset to use integrated indexes.

  3. Opens the Employees table with the Emp_LastName_Index index and obtains interfaces for the rowset.

  4. Gets information about the key columns in the Emp_LastName_Index.

  5. Establishes bindings for the index columns and creates an accessor for the key column.

  6. Sets a range on the rowset.

  7. Reads rows from the rowset in the specified range.
#include <oledb.h>
#include <stddef.h>

PrintData(OLECHAR*, DWORD);

int main() {
   IOpenRowset         *pIOpenRowset = NULL;
   DBID                 IndexId, TableId;
   IRowsetIndex        *pIRowsetIndex = NULL;
   IRowset             *pIRowset = NULL;
   IColumnsInfo        *pIColumnsInfo = NULL;
   IAccessor           *pIAccessor = NULL;
   ULONG                cColumns, cKeyCols, cProperties;
   DBCOLUMNINFO        *rgInfo;
   OLECHAR             *pStringsBuffer;
   DBINDEXCOLUMNDESC   *rgIndexColDesc;
   DBPROPSET           *rgProperties;
   HACCESSOR            hIndexAccessor = DB_NULL_HACCESSOR;
   HRESULT              hr;

   // Initialize the database, create a session, and obtain, from the session object, a
   // pointer pIOpenRowset to an IOpenRowset interface. Code not shown.

   // Set the DBIDs for the table and the index.
   TableId.eKind = DBKIND_NAME;
   TableId.uName.pwszName = OLESTR("Employees");
   IndexId.eKind = DBKIND_NAME;
   IndexId.uName.pwszName = OLESTR("Emp_LastName_Index");

   // Set properties to request IRowsetIndex.
   DBPROPSET rgPropSet[1];
   DBPROP    rgProp[1];

   rgPropSet[0].rgProperties    = rgProp;
   rgPropSet[0].cProperties     = 1;
   rgPropSet[0].guidPropertySet = DBPROPSET_ROWSET;

   rgProp[0].dwPropertyID      = DBPROP_IRowsetIndex;
   rgProp[0].dwOptions         = DBPROPOPTIONS_REQUIRED;
   rgProp[0].dwStatus          = DBPROPSTATUS_OK;
   rgProp[0].colid             = DB_NULLID;
   V_VT(&(rgProp[0].vValue))   = VT_BOOL;
   V_BOOL(&(rgProp[0].vValue)) = VARIANT_TRUE;

   // Use IOpenRowset::OpenRowset to open the table with the Emp_LastName_Index index
   // and get a pointer to IRowsetIndex. Call QueryInterface to get pointers to
   // IRowset, IAccessor, and IColumnsInfo. Note that all of these interfaces are on
   // the base table rowset.
   pIOpenRowset->OpenRowset(NULL, &TableId, &IndexId, IID_IRowsetIndex, 1, rgPropSet,
                            (IUnknown**) &pIRowsetIndex);
   pIRowsetIndex->QueryInterface(IID_IRowset, (LPVOID FAR*) &pIRowset);
   pIRowsetIndex->QueryInterface(IID_IAccessor, (LPVOID FAR*) &pIAccessor);
   pIRowsetIndex->QueryInterface(IID_IColumnsInfo, (LPVOID FAR*) &pIColumnsInfo);

   // Get information about the rowset columns and information about the index.
   pIColumnsInfo->GetColumnInfo(&cColumns, &rgInfo, &pStringsBuffer);
   pIRowsetIndex->GetIndexInfo(&cKeyCols, &rgIndexColDesc, &cProperties, rgProperties);

   // Explore the DBINDEXCOLUMNDESC and DBCOLUMNINFO structures to determine which
   // columns are key columns and what their metadata is. For each key column, call
   // IColumnsInfo::MapColumnIds to determine the ordinal of the column. Suppose that
   // these structures show that there is a single key column (column 1) that contains
   // a 30-byte string. Free the structures allocated by the methods. Code not shown.

   // Create an accessor to use when setting the index range and seeking for values.
   typedef struct tagNameStruct {
      DWORD   dwStatus;
      OLECHAR szLastName[30];
   } NameStruct;
   DBBINDSTATUS     rgStatus[1];
   static DBBINDING rgIndexBinding[1] = {
      {
         1,                                // Ordinal of key column
         offsetof(NameStruct, szLastName), // Offset to value
         0,                                // No length -- assume null termination
         offsetof(NameStruct, dwStatus),   // Offset to status
         NULL,                             // No TypeInfo
         NULL,                             // No object
         NULL,                             // No binding extensions
         DBPART_VALUE | DBPART_STATUS,     // Bind value and status
         DBMEMOWNER_CLIENTOWNED,           // Client-owned memory
         DBPARAMIO_NOTPARAM,               // Not a parameter
         30,                               // cbMaxLen
         0,                                // Flags ignored
         DBTYPE_WSTR,                      // Bind as fixed-length string
         0,                                // No scale
         0                                 // No precision
      }
   };

   pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, rgIndexBinding, 0,
                              &hIndexAccessor, rgStatus);

   // Set the range LastName LIKE "Smith*" and position the next fetch position to the
   // start of this range.
   NameStruct nsNameStruct;
   nsNameStruct.szLastName = OLESTR("Smith");
   nsNameStruct.dwStatus = DBSTATUS_S_OK;
   pIRowsetIndex->SetRange(hIndexAccessor, 1, &nsNameStruct, 0, NULL, DBRANGE_PREFIX);

   // Traverse the Employees table within the range and print each name found.
   ULONG        cRows = 0;
   HROW        *rghRows = NULL;
   DBROWSTATUS  rgRowStatus[1];

   while(SUCCEEDED(hr=pIRowset->GetNextRows(0, 0, 1, &cRows, &rghRows)) && cRows > 0) {
      // Reuse the same accessor to get the name from the row and print it.
      pIRowset->GetData(rghRows[0], hIndexAccessor, &nsNameStruct);
      PrintData(nsNameStruct.szLastName, nsNameStruct.dwStatus);

      // Release the row.
      pIRowset->ReleaseRows(cRows, rghRows, NULL, NULL, rgRowStatus);
   };

   // Release the accessor.
   pIAccessor->ReleaseAccessor(hIndexAccessor);

   // Release the rowset.
   pIRowsetIndex->Release();
   pIColumnsInfo->Release();
   pIAccessor->Release();
   pIRowset->Release();
};


IAccessor

IAccessor provides methods for accessor management. For information about accessors, see "Accessors" in Chapter 6.

When to Implement

All rowsets and commands must implement IAccessor.

Accessors can be used for rowset data, parameter data, or both. Row and parameter accessors can be created on a command. The consumer must verify that row accessors created on the command are still valid following a change to the command. Accessors created on a command are not persisted with that command. Accessors created on a command are inherited by the rowsets it creates. To the consumer, it appears as if each accessor has been copied from the command to the rowset: the bindings, flags, and handle of each accessor are the same on both the rowset and the command.

Accessors created on a rowset are only available to that rowset. They are not available to the command that created the rowset or other rowsets created by the command.

When an accessor is created on a rowset or command, it has a reference count of 1. If a rowset inherits an accessor from a command, the accessor has a reference count of 1 on the rowset, regardless of the reference count of the same accessor on the command. Calls to AddRefAccessor increment the reference count of the accessor and calls to ReleaseAccessor decrement the reference count of the accessor. When the reference count of an accessor reaches zero, the accessor and all resources used by that accessor are released. If the accessor is created on a command and inherited by the rowset, releasing the accessor on the command does not affect the "copy" of the accessor on the rowset and vice versa.

When the reference count of a rowset reaches zero, all accessors created on that rowset or inherited from the command that created it are released completely. If any accessors were inherited from the command, the "copies" of these parent accessors on the command are not affected.

When the reference count of a command reaches zero, all accessors created on the command are released completely. If any rowsets inherited accessors from the command, the "copies" of these accessors on the rowsets are not affected.

When to Call

To create an accessor, a consumer calls CreateAccessor. The consumer may create and release accessors at any time while the rowset or command remains in existence. When one thread of a consumer shares an accessor with another thread, it calls AddRefAccessor to increment the reference count of that accessor.

When the consumer is done with a rowset, it calls ReleaseAccessor to release any accessors on the rowset, including accessors inherited from the command. When the consumer is done with a command, it calls ReleaseAccessor to release any accessors created on the command. In both cases, the consumer must call ReleaseAccessor once for each reference count on the accessor.

Method | Description
AddRefAccessor | Adds a reference count to an existing accessor.
CreateAccessor | Creates an accessor from a set of bindings.
GetBindings | Returns the bindings in an accessor.
ReleaseAccessor | Releases an accessor.


IAccessor::AddRefAccessor

Adds a reference count to an existing accessor.

HRESULT AddRefAccessor (
   HACCESSOR   hAccessor,
   ULONG *         pcRefCount);

Parameters

hAccessor

[in]
The handle of the accessor for which to increment the reference count.

pcRefCount

[out]
A pointer to memory in which to return the reference count of the accessor handle. If pcRefCount is a null pointer, no reference count is returned.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called and the object is in a zombie state. This error can be returned only when the method is called on a rowset.

DB_E_BADACCESSORHANDLE
hAccessor was invalid.

Comments

The consumer must increment the reference count on an accessor by calling AddRefAccessor before passing it to another thread.

See Also

IAccessor::CreateAccessor, IAccessor::ReleaseAccessor


IAccessor::CreateAccessor

CreateAccessor creates an accessor from a set of bindings.

HRESULT CreateAccessor (
   DBACCESSORFLAGS      dwAccessorFlags,
   ULONG                     cBindings,
   const DBBINDING            rgBindings[],
   ULONG                     cbRowSize,
   HACCESSOR *               phAccessor,
   DBBINDSTATUS            rgStatus[]);

Parameters

dwAccessorFlags

[in]
A bitmask that describes the properties of the accessor and how it is to be used. These flags have the following meanings.

Value Description
DBACCESSOR_
INVALID 		This flag is used by GetBindings to indicate that the method failed.
DBACCESSOR_
PASSBYREF 		The accessor is a reference accessor.

The value passed in the consumer buffer is a pointer to the passer's internal buffer. This pointer need not point to the start of the internal buffer, as long as the relative offsets of all elements of the buffer align with the offsets specified in the accessor. The passee must know the internal structure of the passer's buffer in order to read information from it. The passee must not free the buffer at the pointer, nor may it write to this buffer.

For row accessors, this buffer is the rowset's copy of the row. The consumer reads information directly from this copy of the row at a later point in time, so the provider must guarantee that the pointer remains valid.

For parameter accessors, this buffer is the consumer's buffer. The provider reads data from this buffer only when ICommand::Execute is called, so the pointer is not required to remain valid after Execute returns.

Support for this flag is optional. A consumer determines whether a provider supports this bit by calling IDBProperties::GetProperties for the DBPROP_BYREFACCESSORS property.
When this flag is used, the dwMemOwner in the DBBINDING structure is ignored. If the accessor is used for row data, the accessor refers to the provider's memory; the consumer must not write to or free this memory. If the accessor is used for input parameters, the provider copies the row of data without assuming ownership.

It is an error to specify an output or input/output parameter in a reference accessor.

For more information, see "Reference Accessors" in Chapter 6.
DBACCESSOR_
ROWDATA 		The accessor is a row accessor and describes bindings to columns in the rowset.

An accessor may be a row accessor, a parameter accessor, or both.
DBACCESSOR_
PARAMETERDATA 		The accessor is a parameter accessor and describes bindings to parameters in the command text. In a parameter accessor, it is an error to bind an input or an input/output parameter more than once.
DBACCESSOR_
OPTIMIZED 		The row accessor is to be optimized. This hint may affect how a provider structures its internal buffers. A particular column can be bound by only one optimized accessor. The column can also be bound by other, nonoptimized accessors, but the types specified in the nonoptimized accessors must be convertable from the type in the optimized accessor. All optimized accessors must be created before the first row is fetched with IRowset::GetNextRows, IRowsetLocate::GetRowsAt, IRowsetLocate::GetRowsByBookmark, or IRowsetScroll::GetRowsAtRatio.

This flag is ignored for parameter accessors.

cBindings

[in]
The number of bindings in the accessor.

If cBindings is zero, CreateAccessor creates a null accessor. Null accessors are used only by IRowsetChange::InsertRow to create a new row in which each column is set to its default value, NULL, or a status of DBSTATUS_E_UNAVAILABLE. Providers that support InsertRow must support the creation of null accessors. For more information, see InsertRow.

rgBindings

[in]
An array of DBBINDING structures. For more information, see "DBBINDING Structures" in Chapter 6.

cbRowSize

[in]
The numer of bytes allocated for a single set of parameters or criteria values in the consumer's buffer.

cbRowSize is used by ICommand::Execute to process multiple sets of parameters, and by IViewFilter::GetFilter and IViewFilter::SetFilter to get and set multiple OR' conditions in criteria. In both cases, a single accessor may describe multiple sets of values. cbRowSize is generally the size of the structure that contains a single set of parameter or critieria values and is used as the offset to the start of the next set of values within the array of structures. For example, if cParamSets is greater than 1 in the DBPARAMS structure passed to Execute, the provider assumes that the pData element of this structure points to an array of structures containing parameter values, each cbRowSize bytes in size. Similarly, if cRows is greater than 1 in SetFilter, the provider assumes that the pCriteriaData argument points to an array of structures containing criteria values, each cbRowSize bytes in size.

cbRowSize must be large enough to contain the structure defined by the bindings in rgBindings. The provider is not required to verify this, although it may.

cbRowSize is not used when fetching rowset data.

phAccessor

[out]
A pointer to memory in which to return the handle of the created accessor. If CreateAccessor fails, it must attempt to set *phAccessor to a null handle.

rgStatus

[out]
An array of cBindings DBBINDSTATUS values in which CreateAccessor returns the status of each binding; that is, whether it was successfully validated or not. If rgStatus is a null pointer, no bind status values are returned. The consumer allocates and owns the memory for this array. The bind status values are returned for the following reasons.

Value | Description
DBBINDSTATUS_OK | No errors were found in the binding. Because accessor validation can be deferred, a status of DBBINDSTATUS_OK does not necessarily mean that the binding was successfully validated.
DBBINDSTATUS_
BADORDINAL 		A parameter ordinal was zero in a parameter accessor.

If the accessor is validated against the metadata when CreateAccessor is called, DBBINDSTATUS_BADORDINAL can be returned for the following reasons:

  • In a row accessor, a column ordinal in a binding was outside the range of available columns on the rowset.

  • In a parameter accessor, a parameter ordinal was greater than the number of parameters in the command text.

These reasons cause a status value of DBSTATUS_E_BADACCESSOR to be returned if the accessor is validated when used.

Some providers may support binding more parameters than the number of parameters in the command text, and such providers do not return DBBINDSTATUS_BADORDINAL in this case.
DBBINDSTATUS_
UNSUPPORTED
CONVERSION 		If the accessor is validated against the metadata when CreateAccessor is called, DBBINDSTATUS_UNSUPPORTEDCONVERSION can be returned for the following reason:
  • The specified conversion was not supported by the provider. For a list of conversions the accessor must support, see "Data Type Conversion" in Chapter 10.

  • The consumer attempted to convert a column to a storage object (ISequentialStream, IStorage, IStream, ILockBytes) and the particular conversion is not supported by the provider.

These reasons cause a status value of DBSTATUS_E_BADACCESSOR to be returned if the accessor is validated when used.
DBBINDSTATUS_
BADBINDINFO 		dwPart in a binding was not one of the following:

DBPART_VALUE
DBPART_LENGTH
DBPART_STATUS

eParamIO in a binding in a parameter accessor was not one of the following:

DBPARAMIO_INPUT
DBPARAMIO_OUTPUT
DBPARAMIO_INPUT | DBPARAMIO_OUTPUT

A row accessor was optimized and a column ordinal in a binding was already used in another optimized accessor.

In a parameter accessor, two or more bindings contained the same ordinal for an input or input/output parameter.

wType in a binding was DBTYPE_EMPTY or DBTYPE_NULL.

wType in a binding was one of the following:

DBTYPE_BYREF | DBTYPE _EMPTY,
DBTYPE_BYREF | DBTYPE_NULL, or
DBTYPE_BYREF | DBTYPE_RESERVED.

wType in a binding was used with more than one of the following mutually exclusive type indicators: DBTYPE_BYREF, DBTYPE_ARRAY, or DBTYPE_VECTOR.

wType in a binding was DBTYPE_IUNKNOWN, and pObject in the same binding was a null pointer.

Provider-owned memory was specified for a nonpointer type in a nonreference row accessor.

Provider-owned memory was specified for a column and the provider does not support binding to provider-owned memory for this column.

Provider-owned memory was specified for a column for which IColumnsInfo::GetColumnInfo returned DBCOLUMNFLAGS_ISLONG.

In a nonreference parameter accessor, a binding specified provider-owned memory.

An output or input/output parameter was specified in a parameter reference accessor.

dwFlags in a binding was set to DBBINDFLAG_HTML, and wType for the same binding was not a string value.

If the accessor is validated against the metadata when CreateAccessor is called, DBBINDSTATUS_BADBINDINFO can be returned for the following reasons:

  • A row accessor was not optimized, a column number in a binding specified a column that was already used in an optimized accessor, and the provider did not support a conversion from the type specified in the optimized accessor for the column to the type specified in wType.

  • In a parameter accessor, eParamIO in a binding specified the incorrect I/O type for the parameter. Some providers cannot determine parameter I/O types and never return DBBINDSTATUS_BADBINDINFO in this case.

  • In a reference accessor, the value specified for dwPart, obValue, cbMaxLen, or wType in a binding did not match the format of the corresponding element in the rowset's copy of the row.

  • In a nonreference accessor, a binding specified provider-owned memory, wType was X | DBTYPE_BYREF, and the data type of the corresponding element of the rowset's copy of the row was not X or X | DBTYPE_BYREF.

  • In a nonreference accessor, a binding specified provider-owned memory, wType was DBTYPE_BSTR, and the data type of the corresponding element of the rowset's copy of the row was not DBTYPE_BSTR.

  • The accessor was used for passing key column values in IRowsetIndex::Seek or IRowsetIndex::SetRange, and the order in which the key columns were bound did not match the order in which they were returned in IColumnsInfo::GetColumnInfo.

  • The accessor was used for passing key column values in Seek or SetRange, and a less significant key column was bound without binding all more significant key columns.

  • The accessor was used for passing key column values in Seek or SetRange, and a non-key column was bound before the last bound key column.

  • These reasons cause a status value of DBSTATUS_E_BADACCESSOR to be returned if the accessor is validated when used.
DBBINDSTATUS_
BADSTORAGE
FLAGS 		dwFlags, in the DBOBJECT structure pointed to by a binding, specified invalid storage flags.

If the accessor is validated against the metadata when CreateAccessor is called, DBBINDSTATUS_BADSTORAGEFLAGS can be returned for the following reason:

  • dwFlags, in the DBOBJECT structure pointed to by a binding, specified a valid storage flag that was not supported by the object.

These reasons cause a status value of DBSTATUS_E_BADACCESSOR to be returned if the accessor is validated when used.
DBBINDSTATUS_
NOINTERFACE 		If the accessor is validated against the metadata when CreateAccessor is called, DBBINDSTATUS_NOINTERFACE can be returned for the following reasons:
  • The provider did not support the storage interface specified in iid in the DBOBJECT structure pointed to by a binding in the accessor.

  • The OLE object in a column or parameter did not support the interface specified in iid in the DBOBJECT structure pointed to by the corresponding binding in the accessor.

  • The provider supports only one open storage object at a time—that is, DBPROP_MULTIPLESTORAGEOBJECTS is VARIANT_FALSE—and wType in more than one binding was DBTYPE_IUNKNOWN.

These reasons cause a status value of DBSTATUS_E_BADACCESSOR to be returned if the accessor is validated when used.

Return Code

S_OK
The method succeeded. If rgStatus is not a null pointer, each element is set to DBBINDSTATUS_OK.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
phAccessor was a null pointer.

cBindings was not zero and rgBindings was a null pointer.

E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called and the object is in a zombie state. This error can be returned only when the method is called on a rowset.

DB_E_BADACCESSORFLAGS
dwAccessorFlags was invalid.

The DBACCESSOR_PARAMETERDATA bit was set in dwAccessorFlags, and the provider does not support parameters.

Neither the DBACCESSOR_PARAMETERDATA bit nor the DBACCESSOR_ROWDATA bit was set in dwAccessorFlags.

A method that fetches rows (IRowset::GetNextRows, IRowsetLocate::GetRowsAt, IRowsetLocate::GetRowsByBookmark, or IRowsetScroll::GetRowsAtRatio) had already been called and the DBACCESSOR_OPTIMIZED bit in dwAccessorFlags was set.

The DBACCESSOR_ PARAMETERDATA bit was set and CreateAccessor was called on a rowset.

DB_E_BYREFACCESSORNOTSUPPORTED
dwAccessorFlags was DBACCESSOR_PASSBYREF and the value of the DBPROP_BYREFACCESSORS property is VARIANT_FALSE.

DB_E_ERRORSOCCURRED
Accessor validation failed. To determine which bindings failed, the consumer checks the values returned in rgStatus, at least one of which is not DBBINDSTATUS_OK.

DB_E_NOTREENTRANT
The provider called a method from IRowsetNotify in the consumer and the method has not yet returned.

DB_E_NULLACCESSORNOTSUPPORTED
cBindings was zero and the rowset does not support IRowsetChange::InsertRow, or CreateAccessor was called on a command.

Comments

For general information about accessors, see "Accessors" in Chapter 6.

CreateAccessor always checks all error conditions that do not require it to validate the accessor against the metadata. As a general rule, this means it checks the error conditions for all of the return codes except those that return DB_E_ERRORSOCCURRED. CreateAccessor may validate the accessor against the metadata for row accessors created on the rowset; it never validates the accessor against the metadata for row or parameter accessors created on the command. When CreateAccessor validates the accessor against the metadata, it validates each binding in the accessor, setting the appropriate DBBINDSTATUS values as it goes. If CreateAccessor fails in any way, it does not create the accessor and sets *phAccessor to a null handle.

If CreateAccessor does not validate the accessor against the metadata, then the validation is said to be delayed. CreateAccessor simply creates the accessor and validation is done by the first method that uses the accessor. If the accessor is found to be invalid, it remains in existence and can be used again.

If accessor validation is delayed, the provider determines whether the method validating the accessor validates it against the metadata before or during data transfer. If the method validates the accessor before transferring any data, it can return any of the return codes listed below for this purpose. If the method validates the accessor while transferring the data, it sets the status value of any column or parameter for which the accessor is invalid (within the context of the method) to DBSTATUS_E_BADACCESSOR and returns DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED. Whether the method continues processing other columns or parameters depends on both the method and the provider.

The following return codes are returned by methods that perform delayed accessor validation before transferring any data. The DBBINDSTATUS value to which each corresponds is also listed.

Return code | DBBINDSTATUS value
E_NOINTERFACE | DBBINDSTATUS_NOINTERFACE
DB_E_BADBINDINFO | DBBINDSTATUS_BADBINDINFO
DB_E_BADORDINAL | DBBINDSTATUS_BADORDINAL
DB_E_BADSTORAGEFLAGS | DBBINDSTATUS_ BADSTORAGEFLAGS
DB_E_UNSUPPORTEDCONVERSION | DBBINDSTATUS_ UNSUPPORTEDCONVERSION

Providers that do not support conversion of BLOB or non-BLOB data to a data structured storage interface should fail the accessor creation method with DB_E_ERRORSOCCURED and report DBBINDSTATUS_UNSUPPORTEDCONVERSION on the column specifying the incorrect binding.

See Also

IAccessor::GetBindings, IAccessor::ReleaseAccessor, IConvertType::CanConvert, IRowset::GetData, IRowsetChange::SetData


IAccessor::GetBindings

Returns the bindings in an accessor.

HRESULT GetBindings (
   HACCESSOR               hAccessor,
   DBACCESSORFLAGS *   pdwAccessorFlags,
   ULONG *                     pcBindings,
   DBBINDING **               prgBindings);

Parameters

hAccessor

[in]
The handle of the accessor for which to return the bindings.

pdwAccessorFlags

[out]
A pointer to memory in which to return a bitmask that describes the properties of the accessor and how it is intended to be used. For more information, see dwAccessorFlags in CreateAccessor. If this method fails, *pdwAccessorFlags is set to DBACCESSOR_INVALID.

pcBindings

[out]
A pointer to memory in which to return the number of bindings in the accessor. If this method fails, *pcBindings is set to zero.

prgBindings

[out]
A pointer to memory in which to return an array of DBBINDING structures. One DBBINDING structure is returned for each binding in the accessor. The provider allocates memory for these structures and any structures pointed to by elements of these structures; for example, if pObject in a binding structure is not a null pointer, the provider allocates a DBOBJECT structure for return to the consumer. The provider returns the address to the memory for these structures; the consumer releases the memory for these structures with IMalloc::Free when it no longer needs the bindings. If *pcBindings is zero on output or the method fails, the provider does not allocate any memory and ensures that *prgBindings is a null pointer on output. For information about bindings, see "DBBINDING Structures" in chapter 6.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
pdwAccessorFlags, pcBindings, or prgBindings was a null pointer.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to return the binding structures.

E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called and the object is in a zombie state. This error can be returned only when the method is called on a rowset.

DB_E_BADACCESSORHANDLE
hAccessor was invalid.

DB_E_NOTREENTRANT
The provider called a method from IRowsetNotify in the consumer and the method has not yet returned.

Comments

This method makes no logical change to the state of the object.

If the accessor is a null accessor, then the method sets *pcBindings to zero and *prgBindings to a null pointer.

See Also

IAccessor::CreateAccessor


IAccessor::ReleaseAccessor

Releases an accessor.

HRESULT ReleaseAccessor (
   HACCESSOR   hAccessor,
   ULONG *         pcRefCount);

Parameters

hAccessor

[in]
The handle of the accessor to release.

pcRefCount

[out]
A pointer to memory in which to return the remaining reference count of the accessor handle. If pcRefCount is a null pointer, no reference count is returned.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

DB_E_BADACCESSORHANDLE
hAccessor was invalid.

DB_E_NOTREENTRANT
The provider called a method from IRowsetNotify in the consumer and the method has not yet returned.

Comments

ReleaseAccessor decrements the reference count of the accessor. If the reference count reaches zero, it releases the accessor and all resources used by the accessor. After an accessor is released, methods called with the handle to that accessor return DB_E_BADACCESSORHANDLE.

On rowsets, accessors are read-only and can be shared among threads in a free-threaded style without synchronization. The consumer must call ReleaseAccessor to decrement the reference count on an accessor that has been passed to a thread and is no longer needed by that thread.

This method can be called while the rowset is in a zombie state to enable the consumer to clean up after a transaction has been committed or aborted.

See Also

IAccessor::AddRefAccessor, IAccessor::CreateAccessor


IAlterIndex

The IAlterIndex interface exposes methods to alter tables, indexes, and columns.

Method | Description
AlterIndex | Alters the index ID and/or properties associated with an index.


IAlterIndex::AlterIndex

AlterIndex alters the ID and/or properties associated with an index.

HRESULT AlterIndex(
   DBID *   pTableID,
   DBID *   pIndexID,
   DBID *   pNewIndexID,
   ULONG   cPropertySets,
   DBPROPSET   rgPropertySet[]);

Parameters

pTableID

[in]
The DBID of the indexed base table.

pIndexID

[in]
The DBID of the existing index to alter.

pNewIndexID

[in]
The new DBID of the index. If this is the same as pIndexID, or a null pointer, then the ID of the index is unchanged.

cPropertySets

[in]
The number of DBPROPSET structures in rgPropertySets. If this is zero, the provider ignores rgPropertySets.

rgPropertySets

[in/out]
An array of DBPROPSET structures containing properties and values to be set. The properties specified in these structures must belong to the index property set.

Return Code

S_OK

The method succeeded.

E_FAIL

A provider-specific error occurred.

E_INVALIDARG

pTableID was a null pointer.

pIndexID was a null pointer.

DB_E_NOTABLE

The table specified in *pTableID does not exist in the current data source.

DB_E_NOINDEX

The index specified in *pIndexID does not exist in the current data source or did not apply to the specified table.

DB_DUPLICATEINDEXID

The index specified in *pNewIndexID already exists in the current data source.

DB_SEC_E_PERMISSIONDENIED

The consumer did not have sufficient permission to alter the index.

DB_E_BADINDEXID

*pNewIndexID was an invalid index ID.

DB_E_INDEXINUSE

The specified index was in use.

DB_E_TABLEINUSE

The specified table was in use and the provider could not modify the index with the table open.

Comments

AlterIndex applies the new properties and index ID to the existing index. The altered index preserves all properties associated with the existing index that are not explicitly changed by properties specified in *prgProperties. If pNewIndexID is null, or the same as pIndexID, then the index ID is unchanged. If cPropertySets is zero, then no properties are changed. If pNewIndexID is null, or the same as pIndexID, and cPropertySets is zero, then the method makes no change to the state of the index and returns S_OK.


IAlterTable

The IAlterTable interface exposes methods to alter the definition of tables, indexes, and columns.

Providers that implement IAlterTable should expose the data source information property DBPROP_ALTERCOLUMN.

Method | Description
AlterColumn | Alters the column ID and/or properties associated with a column in a table.
AlterTable | Alters the table ID and/or properties associated with a table.


IAlterTable::AlterColumn

AlterColumn alters the column ID and/or properties associated with a column in a table.

HRESULT AlterColumn(
   DBID *   pTableID,
   DBID *   pColumnID,
   DBCOLUMNDESCFLAGS   ColumnDescFlags,
   DBCOLUMNDESC*      pColumnDesc);

Parameters

pTableID

[in]
The DBID of the base table containing the column to alter.

pColumnID

[in]
The current DBID of the column to alter.

ColumnDescFlags

[in]
Values as described in the enumeration DBCOLUMNDESCFLAGS. These bits define which portions of the DBCOLUMNDESC structure defined in pColumnDesc should be used when altering the column. A provider returns values it supports in the DBPROP_ALTERCOLUMN property. For more information about this field, please see the Comments section.

Flag | Associated Portion of DBCOLUMNDESC
DBCOLUMNDESCFLAG_TYPENAME | pwszTypeName
DBCOLUMNDESCFLAG_ITYPEINFO | ITypeInfo *
DBCOLUMNDESCFLAG_PROPERTIES | rgPropertySets AND cPropertySets
DBCOLUMNDESCFLAG_CLSID | pclsid
DBCOLUMNDESCFLAG_COLSIZE | ulColumnSize
DBCOLUMNDESCFLAG_DBCID | dbcid
DBCOLUMNDESCFLAG_WTYPE | wType
DBCOLUMNDESCFLAG_PRECISION | bPrecision
DBCOLUMNDESCFLAG_SCALE | bScale

pColumnDesc

[in]
A pointer to a DBCOLUMNDESC structure defining new values for the columns. Only the values defined in ColumnDescFlags are used—the provider ignores other fields in the structure. For a description of the DBCOLUMNDESC structure, please refer to ITableDefinition::CreateTable.

Return Code

S_OK

The method succeeded.

E_FAIL

A provider-specific error occurred.

E_INVALIDARG

pTableID or pColumnID was a null pointer.

pColumnDesc was a null pointer.

DB_E_NOCOLUMN

The column specified in *pColumnID does not exist in the specified table.

DB_E_NOTABLE

The specified table does not exist in the current data source.

DB_E_DUPLICATECOLUMNID

The ColumnDescFlags structure indicated that the dbcid portion of the pColumnDesc argument should be used, and the column ID specified in dbcid was the same as an existing column ID.

DB_SEC_E_PERMISSIONDENIED

The consumer did not have sufficient permission to alter the columns of this table.

DB_E_TABLEINUSE

The specified table was in use and the provider could not alter the column while the table was open.

DB_E_NOTSUPPORTED

ColumnDescFlags was an invalid value. Providers return valid values in the property DBPROP_ALTERCOLUMN.

Comments

AlterColumn applies the new properties and column ID to the existing column. The altered column preserves all properties associated with the existing column that are not explicitly changed by properties specified in *prgProperties. If pNewColumnID is null, or the same as pColumnID, then the column name is unchanged. If cPropertySets is zero, then no properties are changed. If pNewColumnID is null, or the same as pColumnID, and cPropertySets is zero, then the method makes no change to the state of the table and returns S_OK.

Providers may also return any of the return codes listed in ITableDefinition::CreateTable or ITableDefinition::AddColumn associated with errors in the DBCOLUMNDESC structure (for example, DB_E_BADCOLUMNID). However, the provider should not evaluate portions of the structure that are not explicitly listed in the ColumnDescFlags parameter.

Calls to AlterColumn should be atomic. Failure should leave the column in the same state as before the call to the method.


IAlterTable::AlterTable

AlterTable alters the ID and/or properties associated with a table.

HRESULT AlterTable(
   DBID *   pTableID,
   DBID *   pNewTableID,
   ULONG   cPropertySets,
   DBPROPSET   rgPropertySet[]);

Parameters

pTableID

[in]
The DBID of the base table to alter.

pNewTableID

[in]
The new DBID of the base table. If this is the same as pTableID, or a null pointer, then the ID of the table is unchanged.

cPropertySets

[in]
The number of DBPROPSET structures in rgPropertySets. If this is zero, the provider ignores rgPropertySets.

rgPropertySets

[in/out]
An array of DBPROPSET structures containing properties and values to be set. The properties specified in these structures must belong to the table property set.

Return Code

S_OK

The method succeeded.

E_FAIL

A provider-specific error occurred.

E_INVALIDARG

pTableID was a null pointer.

DB_E_BADTABLEID

*pTableID was an invalid table ID.

DB_E_NOTABLE

The table specified in *pTableID does not exist in the current data source.

DB_DUPLICATETABLEID

The table specified in *pNewTableID already exists in the current data source.

DB_SEC_E_PERMISSIONDENIED

The consumer did not have sufficient permission to alter the table or index.

DB_E_TABLEINUSE

The specified table was in use and the provider could not alter the table while it was open.

Comments

AlterTable applies the new properties and table ID to the existing table. The altered table preserves all properties associated with the existing table that are not explicitly changed by properties specified in *prgProperties. If pNewTableID is null, or the same as pTableID, then the table ID is unchanged. If cPropertySets is zero, then no properties are changed. If pNewTableID is null, or the same as pTableID, and cPropertySets is zero, then the method makes no change to the state of the table and returns S_OK.


IChapteredRowset

IChapteredRowset is the interface that allows consumers to manage chapters.

When to Implement

IChapteredRowset is required for all providers that support chapters.

When to Call

When a chapter handle is retrieved, either by calling GetData on a column containing chapter values, or from a method that returns a chapter handle, a reference count is taken against that chapter handle. When the chapter is subsequently used, no additional reference counts are taken; however, the reference count on a chapter can be explicitly incremented by calling AddRefChapter. Both reference counts must be decremented by calling ReleaseChapter.

If any HROWs in the chapter have pending changes when ReleaseChapter is called, the reference count of the chapter is decremented but the chapter remains valid, even if its reference count drops to zero. In such a situation, the chapter value might remain valid, but consumers should not count on such behavior. The chapter and its resources are freed when the pending change status on the rows is cleared, either by a call to IRowsetUpdate::Update or IRowsetUpdate::Undo. In particular, a single-chaptered provider will not be able to open another chapter until the rows are updated or undone and the chapter's resources are freed. For more information, see IRowset::ReleaseRows.

Method | Description
AddRefChapter | Adds a reference count to a Chapter Handle.
ReleaseChapter | Releases a reference count on a Chapter Handle.


IChapteredRowset::AddRefChapter

Adds a reference count to an existing chapter.

HRESULT AddRefChapter (
HCHAPTER hChapter,

ULONG *pcRefCount);

Parameters

hChapter

[in]
The handle of the chapter for which to increment the reference count.

pcRefCount [out]

A pointer to memory in which to return the reference count of the chapter handle. If pcRefCount is a null pointer, no reference count is returned.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

DB_E_BADCHAPTER
hChapter was invalid.

E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called and the object is in a zombie state.


IChapteredRowset::ReleaseChapter

Releases a chapter.

HRESULT ReleaseChapter (
   HCHAPTER   hChapter,
      ULONG *      pcRefCount);

Parameters

hChapter

[in]
The chapter handle.

pcRefCount [out]

A pointer to memory in which to return the reference count of the chapter handle. If pcRefCount is a null pointer, no reference count is returned.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
The rowset was not chaptered.

DB_E_BADCHAPTER
hChapter was invalid.

DB_E_NOTREENTRANT
The provider called a method from IRowsetNotify in the consumer and the method has not yet returned.

Comments

If the chapter's reference count is reduced to zero and retrieved again later, the fetch position of the retrieved chapter is reset to the initial state.

This method can be called while the rowset is in a zombie state to allow the consumer to clean up after a transaction has been committed or aborted.

See Also

IRowset::GetNextRows, IRowsetLocate::GetRowsAt


IColumnsInfo

IColumnsInfo is the simpler of two interfaces that can be used to expose information about columns of a rowset or prepared command. It provides a limited set of information in an array.

When to Implement

All rowsets and commands implement IColumnsInfo. Service components can synthesize IColumnsRowset from this so that consumers and providers have independent choice of whether they want to code for simple limited information, or to work with flexible and open-ended column descriptions.

IColumnsInfo is required on both commands and rowsets.

When to Call

GetColumnInfo returns the most commonly used metadata: column IDs, data types, updatability, and so on. GetColumnInfo returns the metadata in an array of structures, which can be created and accessed quickly. The metadata returned, however, is limited.

Consumers that require more complete metadata can obtain it by calling IColumnsRowset::GetColumnsRowset.

Note For commands that expose ICommandPrepare, the methods on this interface can be called only after the command is prepared or the rowset is instantiated. If a command text is set but not prepared, any calls to methods on IColumnsInfo return DB_E_NOTPREPARED. For commands that do not expose ICommandPrepare, the methods on this interface can be called only after the command text has been set.

Method | Description
GetColumnInfo | Returns the column metadata needed by most consumers.
MapColumnIDs | Returns an array of ordinals of the columns in a rowset that are identified by the specified column IDs.


IColumnsInfo::GetColumnInfo

Returns the column metadata needed by most consumers.

HRESULT GetColumnInfo (
   ULONG *                  pcColumns,
   DBCOLUMNINFO **   prgInfo,
   OLECHAR **            ppStringsBuffer);

Parameters

pcColumns

[out]
A pointer to memory in which to return the number of columns in the rowset; this number includes the bookmark column, if there is one. If GetColumnInfo is called on a command that does not return rows, *pcColumns is set to zero. If this method terminates due to an error, *pcColumns is set to zero.

prgInfo

[out]
A pointer to memory in which to return an array of DBCOLUMNINFO structures. One structure is returned for each column in the rowset. The provider allocates memory for the structures and returns the address to this memory; the consumer releases this memory with IMalloc::Free when it no longer needs the column information. If *pcColumns is 0 on output or terminates due to an error, the provider does not allocate any memory and ensures that *prgInfo is a null pointer on output. For more information, see "DBCOLUMNINFO Structures" in the Comments section.

ppStringsBuffer

[out]
A pointer to memory in which to return a pointer to storage for all string values (names used either within columnid or for *pwszName) within a single allocation block. If no returned columns have either form of string name, or if this method terminates due to error, this parameter returns a null pointer. If there are any string names, then this will be a buffer containing all the values of those names. The consumer should free the buffer with IMalloc::Free when finished working with the names. If *pcColumns is zero on output, the provider does not allocate any memory and ensures that *ppStringsBuffer is a null pointer on output. Each of the individual string values stored in this buffer is terminated by a null-termination character. Therefore, the buffer may contain one or more strings, each with its own null-termination character, and may contain embedded null-termination characters.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
pcColumns, prgInfo, or ppStringsBuffer was a null pointer.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to return the column information structures.

E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called and the object is in a zombie state. This error can be returned only when the method is called on a rowset.

DB_E_ERRORSINCOMMAND
The command text contained one or more errors. Providers should use OLE DB error objects to return details about the errors.

DB_E_NOCOMMAND
No command text was set. This error can be returned only when this method is called from the Command object.

DB_E_NOTPREPARED
The command exposed ICommandPrepare and the command text was set, but the command was not prepared. This error can be returned only when this method is called from the Command object.

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 retrieve the column information.

Comments

This function makes no logical change to the state of the object.

GetColumnInfo returns a fixed set of column metadata in an array of DBCOLUMNINFO structures, one per column. The returned metadata is that most commonly used by consumers, such as the data type and column ID.

The order of the structures is the order in which the columns appear in the rowset (column ordinal order). This is the same order as they appear in IColumnsRowset, and is predictable from the ordering of requested columns in the command text.

Bookmark columns may be returned on any rowset, regardless of its source (for example, ICommand::Execute, IOpenRowset::OpenRowset, IColumnsRowset::GetColumnsRowset, or IDBSchemaRowset::GetRowset) or whether bookmarks were requested. When bookmarks are returned, they will always appear as the first element of *prgInfo with iOrdinal equal to zero. When processing the columns returned by GetColumnInfo, generic consumers should be prepared to receive and handle bookmark columns appropriately (that is, not display them to the user if not appropriate for the application).

GetColumnInfo provides a quick alternative to GetColumnsRowset. While GetColumnsRowset returns all available column metadata, it does so in a rowset. To get the metadata, the consumer must therefore create the column metadata rowset, create one or more accessors, fetch each row in the rowset, and get the data from the rowset.

Calling GetColumnInfo on a command before the command is executed may be an expensive operation.

DBCOLUMNINFO Structures

GetColumnInfo returns column metadata in DBCOLUMNINFO structures.

typedef struct tagDBCOLUMNINFO {
 LPOLESTR  pwszName;
 ITypeInfo * pTypeInfo;
 ULONG   iOrdinal;
 DBCOLUMNFLAGS dwFlags;
 ULONG   ulColumnSize;
 DBTYPE    wType;
 BYTE    bPrecision;
 BYTE    bScale;
 DBID    columnid;
} DBCOLUMNINFO;

The elements of this structure are used as follows.

Element | Description
pwszName Pointer to the name of the column; this might not be unique. If this cannot be determined, a null pointer is returned.

The name can be different from the string part of the column ID if the column has been renamed by the command text. This name always reflects the most recent renaming of the column in the current view or command text.
pTypeInfo | Reserved for future use. Providers should return a null pointer in pTypeInfo.
iOrdinal | The ordinal of the column. This is zero for the bookmark column of the row, if any. Other columns are numbered starting from one.
dwFlags | A bitmask that describes column characteristics. The DBCOLUMNFLAGS enumerated type specifies the bits in the bitmask. For more information, see the following section.
ulColumnSize The maximum possible length of a value in the column. For columns that use a fixed-length data type, this is the size of the data type. For columns that use a variable-length data type, this is one of the following:
  • The maximum length of the column in characters, for DBTYPE_STR and DBTYPE_WSTR, or bytes, for DBTYPE_BYTES, if one is defined. For example, a CHAR(5) column in an SQL table has a maximum length of 5.
  • The maximum length of the data type in characters, for DBTYPE_STR and DBTYPE_WSTR, or bytes, for DBTYPE_BYTES, if the column does not have a defined length.

    For data types that do not have a length, this is set to ~0 (bitwise, the value is not 0; that is, all bits are set to 1).

  • ~0 (bitwise, the value is not 0; that is, all bits are set to 1) if neither the column nor the data type has a defined maximum length.
wType | The indicator of the column's data type. If the data type of the column varies from row to row, this must be DBTYPE_VARIANT. For a list of valid type indicators, see "Type Indicators" in Appendix A.
bPrecision If wType is a numeric data type, this is the maximum precision of the column. The precision of columns with a data type of DBTYPE_DECIMAL or DBTYPE_NUMERIC depends on the definition of the column. For the precision of all other numeric data types, see "Precision of Numeric Data Types" in Appendix A.

For DBTYPE_DBTIMESTAMP data types, this is the length of the string representation (assuming the maximum allowed precision of the fractional seconds component).

If the column's data type is not numeric or datetime, this is ~0 (bitwise, the value is not 0; that is, all bits are set to 1).
bScale If wType is DBTYPE_DECIMAL or DBTYPE_NUMERIC, this is the number of digits to the right of the decimal point. Otherwise, this is ~0 (bitwise, the value is not 0; that is, all bits are set to 1).

For DBTYPE_DBTIMESTAMP data types, this is the length of the string representation of the fractional seconds component.
columnid The column ID of the column.

The column ID of a base table should be invariant under views.

For more information, see "Column IDs," Chapter 4.

DBCOLUMNFLAGS Enumerated Type

The dwFlags element of the DBCOLUMNINFO structure is a bitmask that describes column characteristics. The DBCOLUMNFLAGS enumerated type specifies the bits in the bitmask; the meanings of these values are as follows.

Value Description
DBCOLUMNFLAGS_
CACHEDEFERRED 		Set if, when a deferred column is first read, its value is cached by the provider. Later reads of the column are done from this cache. The contents of the cache can be overwritten by IRowsetChange::SetData or IRowsetRefresh::RefreshVisibleData. The cached value is released when the row handle is released. Otherwise, not set.

This flag can be set through the DBPROP_CACHEDEFERRED property in the Rowset property group.
DBCOLUMNFLAGS_
ISBOOKMARK 		Set if the column contains a bookmark. Otherwise, not set.
DBCOLUMNFLAGS_
ISCHAPTER 		Set if the column contains a chapter value. Otherwise, not set.
DBCOLUMNFLAGS_
ISFIXEDLENGTH 		Set if all data in the column has the same length. Otherwise, not set.

The flag is always set for fixed length data types except DBTYPE_BSTR. The flag is set for variable-length data types (DBTYPE_STR, DBTYPE_WSTR, DBTYPE_BSTR, and DBTYPE_BYTES) if all the values are the same length. The flag is set for DBTYPE_VECTOR or DBTYPE_ARRAY columns if all elements in each vector or array are of the same length and each vector or array has the same number of elements. Otherwise, the flag is not set.

The flag is not affected by DBTYPE_BYREF.
DBCOLUMNFLAGS_
ISLONG 		Set if the column contains a BLOB that contains very long data. The definition of very long data is provider-specific. The setting of this flag corresponds to the value of the IS_LONG column in the PROVIDER_TYPES schema rowset for the data type.

When this flag is set, the provider supports reading the value through a storage interface. Although such BLOBs can be retrieved in a single piece with IRowset::GetData, there may be provider-specific problems in doing so. For example, the BLOB might be truncated due to machine limits on memory or GetData might fail if called more than once for the BLOB. Furthermore, when this flag is set, the provider might not be able to accurately return the maximum length of the BLOB data in ulColumnSize in the DBCOLUMNINFO structure.

When this flag is not set, the BLOB can be accessed directly through GetData. It is provider-specific whether columns without this flag can be read through a storage interface.

For more information, see "Accessing BLOB Data" in Chapter 7.
DBCOLUMNFLAGS_
ISNULLABLE 		Set if the column can be set to NULL or if the provider cannot determine whether or not the column can be set to NULL. Otherwise, not set. This reflects only declarative rules. DBCOLUMNFLAGS_ISNULLABLE is generally used by consumers attempting to set data to determine whether or not a particular column might be able to hold a null value.
DBCOLUMNFLAGS_
ISROWID 		Set if the column contains a persistent row identifier that cannot be written to and has no meaningful value except to identify the row.
DBCOLUMNFLAGS_
ISROWVER 		Set if the column contains a timestamp or other versioning mechanism that cannot be written to directly and that is automatically updated to a new, increasing value when the row is updated and committed. The data type of a version column is provider-specific. How a version column is used—for example, how two version values are compared—is also provider-specific.
DBCOLUMNFLAGS_
MAYBENULL 		Set if NULL can be gotten from the column, or if the provider cannot guarantee that NULLs cannot be gotten from the column. Otherwise, not set. DBCOLUMNFLAGS_MAYBENULL is generally used by consumers reading data to determine whether or not they might encounter a null value. When DBCOLUMNINFO is returned in the COLUMNS rowset, DBCOLUMNFLAGS_MAYBENULL should not be set if a simple select over the single column of that table would return no null values.

DBCOLUMFLAGS_MAYBENULL can be set even if DBCOLUMNFLAGS_ISNULLABLE is not set. For example, in a left outer join, if there is no row on the right side that matches a row on the left side, the columns from the right side in the joined row contain NULLs, even if the underlying columns are non-nullable; that is, if DBCOLUMNFLAGS_ISNULLABLE is not set.
DBCOLUMNFLAGS_
MAYDEFER 		Set if the column is deferred. Otherwise, not set.

A deferred column is one for which the data is not fetched from the data source until the consumer attempts to get it. That is, the data is not fetched when the row is fetched, but when IRowset::GetData is called. This flag can be set through the DBPROP_DEFERRED property in the Rowset property group.

For more information, see "Deferred Columns" in Chapter 4.
DBCOLUMNFLAGS_
WRITE 		Set if IRowsetChange::SetData can be called for the column. Otherwise, not set.

This flag can be set through the DBPROP_MAYWRITECOLUMN property in the Rowset property group.

Providers never set both DBCOLUMNFLAGS_WRITE and DBCOLUMNFLAGS_WRITEUNKNOWN; they are mutually exclusive. Absence of these flags means the column is read-only.
DBCOLUMNFLAGS_
WRITEUNKNOWN 		Set if it is not known whether IRowsetChange::SetData can be called for the column.

Providers never set both DBCOLUMNFLAGS_WRITE and DBCOLUMNFLAGS_WRITEUNKNOWN; they are mutually exclusive. Absence of these flags means the column is read-only.

See Also

IColumnsRowset::GetColumnsRowset, IColumnsInfo::MapColumnIDs, IRowsetInfo::GetProperties


IColumnsInfo::MapColumnIDs

Returns an array of ordinals of the columns in a rowset that are identified by the specified column IDs.

HRESULT MapColumnIDs (
   ULONG      cColumnIDs,
   const DBID   rgColumnIDs[],
   ULONG      rgColumns[]);

Parameters

cColumnIDs

[in]
The number of column IDs to map. If cColumnIDs is 0, MapColumnIDs does nothing and returns S_OK.

rgColumnIDs

[in]
An array of IDs of the columns of which to determine the column ordinals. If rgColumnIDs contains a duplicate column ID, a column ordinal is returned once for each occurrence of the column ID. If the column ID is invalid, the corresponding element of rgColumns is set to DB_INVALIDCOLUMN.

rgColumns

[out]
An array of cColumnIDs ordinals of the columns identified by the elements of rgColumnIDs. The consumer allocates, but is not required to initialize, memory for this array and passes the address of this memory to the provider. The provider returns the column IDs in the array.

Return Code

S_OK
The method succeeded. All elements of rgColumns are set to values other than DB_INVALIDCOLUMN.

DB_S_ERRORSOCCURRED
An element of rgColumnIDs was invalid. If the column ID is invalid, the corresponding element of rgColumns is set to DB_INVALIDCOLUMN.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
cColumnIDs was not 0 and rgColumnIDs was a null pointer.

rgColumns was a null pointer.

E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called and the object is in a zombie state. This error can be returned only when the method is called on a rowset.

DB_E_ERRORSOCCURRED
All elements of rgColumnIDs were invalid. All elements of rgColumns are set to DB_INVALIDCOLUMN.

DB_E_NOCOMMAND
No command text was set. This error can be returned only when this method is called from the Command object.

DB_E_NOTPREPARED
The command exposed ICommandPrepare and the command text was set, but the command was not prepared. This error can be returned only when this method is called from the Command object.

DB_E_NOTREENTRANT
The provider called a method from IRowsetNotify in the consumer and the method has not yet returned.

Comments

This method makes no logical change to the state of the object.

MapColumnIDs returns the ordinals of the columns in the rowset that are identified by the elements of rgColumnIDs. These column ordinals do not change during the life of the rowset. Column ordinals may change between different instantiations of a rowset if the command text does not define an order, such as a SELECT * FROM MyTable.

Two column IDs that are the same—except that one contains a GUID and the other contains a pointer to a GUID—are equivalent if both use the same GUID. Both are mapped to the same column ordinal.

See Also

IColumnsInfo::GetColumnInfo, IColumnsRowset::GetColumnsRowset


IColumnsRowset

This interface supplies complete information about columns in a rowset. The methods in it can be called from a rowset or a command.

When to Implement

Advanced providers implement this interface.

A service component can be used to synthesize IColumnsRowset from the simpler IColumnsInfo (with defaults and NULL values to round out the information).

IColumnsRowset is optional on both commands and rowsets.

When to Call

Consumers use the methods in IColumnsRowset for detailed and flexible information about the columns of a rowset.

Note For commands that expose ICommandPrepare, the methods on this interface can be called only after the command is prepared or the rowset is instantiated. If a command text is set but not prepared, any calls to methods on IColumnsInfo return DB_E_NOTPREPARED. For commands that do not expose ICommandPrepare, the methods on this interface can be called only after the command text has been set.

Method | Description
GetAvailableColumns | Returns a list of optional metadata columns that can be supplied in a column's rowset.
GetColumnsRowset | Returns a rowset containing metadata about each column in the current rowset. The rowset is known as the column metadata rowset and is read-only.


IColumnsRowset::GetAvailableColumns

Returns a list of optional metadata columns that can be supplied in a column metadata rowset.

HRESULT GetAvailableColumns (
   ULONG *      pcOptColumns,
   DBID **      prgOptColumns);

Parameters

pcOptColumns

[out]
A pointer to memory in which to return the count of the elements in *prgOptColumns. If an error occurs, *pcOptColumns is set to zero.

prgOptColumns

[out]
A pointer to memory in which to return an array of the optional columns this provider can supply. In addition to the optional columns listed in GetColumnsRowset, the provider can return provider-specific columns. The rowset or command allocates memory for the structures and returns the address to this memory; the consumer releases this memory with IMalloc::Free when it no longer needs the list of columns. If *pcOptColumns is zero on output or an error occurs, the provider does not allocate any memory and ensures that *prgOptColumns is a null pointer on output.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
pcOptColumns or prgOptColumns was a null pointer.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to return the column IDs.

E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called and the object is in a zombie state. This error can be returned only when the method is called on a rowset.

DB_E_NOCOMMAND
No command text was set. This error can be returned only when this method is called from the command object.

DB_E_NOTPREPARED
The command exposed ICommandPrepare and the command text was set, but the command was not prepared. This error can be returned only when this method is called from the command object.

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 retrieve the available optional metadata columns.

Comments

This method makes no logical change to the state of the object.

Calling GetAvailableColumns on a command before the command is executed may be an expensive operation.

See Also

IColumnsRowset::GetColumnsRowset


IColumnsRowset::GetColumnsRowset

Returns a rowset containing metadata about each column in the current rowset. This rowset is known as the column metadata rowset and is read-only.

HRESULT GetColumnsRowset (
   IUnknown *      pUnkOuter,
   ULONG         cOptColumns,
   const DBID      rgOptColumns[],
   REFIID            riid,
   ULONG         cPropertySets,
   DBPROPSET   rgPropertySets[],
   IUnknown **      ppColRowset);

Parameters

pUnkOuter

[in]
A pointer to the controlling IUnknown interface if the column metadata rowset is being created as part of an aggregate. It is a null pointer if the rowset is not part of an aggregate.

cOptColumns

[in]
The number of the elements in rgOptColumns. If cOptColumns is zero, then rgOptColumns is ignored, and the provider returns all available columns in the columns rowset.

rgOptColumns

[in]
An array that specifies the optional columns to return. In addition to the optional columns listed below, the consumer can request provider-specific columns.

riid

[in]
The IID of the requested rowset interface. This interface is conceptually added to the list of required interfaces on the resulting rowset, and the method fails (E_NOINTERFACE) if that interface cannot be supported on the resulting rowset.

cPropertySets

[in]
The number of DBPROPSET structures in rgPropertySets. If this is zero, the provider ignores rgPropertySets.

rgPropertySets

[in/out]
An array of DBPROPSET structures containing properties and values to be set. The properties specified in these structures must belong to the Rowset property group. If the same property is specified more than once in rgPropertySets, then it is provider-specific which value is used. If cPropertySets is zero, this argument is ignored.

For information about the properties in the Rowset property group that are defined by OLE DB, see "Rowset Properties" in Appendix C. For information about the DBPROPSET and DBPROP structures, see "DBPROPSET Structure" and "DBPROP Structure" in Chapter 11.

ppColRowset

[out]
A pointer to memory in which to return the requested interface pointer on the column metadata rowset. If an error occurs, the returned pointer is null. If GetColumnsRowset is called on a command that does not return rows, then the column metadata rowset will be empty.

Return Code

S_OK
The method succeeded. In all DBPROP structures passed to the method, dwStatus is set to DBPROPSTATUS_OK.

DB_S_ASYNCHRONOUS
The method has initiated asynchronous creation of the rowset. The consumer can call IDBAsynchStatus to poll for status or IConnectionPointContainer to obtain the IID_IDBAsynchNotify connection point. Attempting to call any other interfaces may fail and the full set of interfaces may not be available on the object until asynchronous initialization of the rowset has completed.

DB_S_ERRORSOCCURRED
The rowset was opened but one or more properties—for which the dwOptions element of the DBPROP structure was DBPROPOPTIONS_OPTIONAL—were not set. The consumer checks dwStatus in the DBPROP structures to determine which properties were not set. The method can fail to set properties for a number of reasons, including:

  • The property was not supported by the provider.

  • The property was not in the Rowset property group.

  • The property set was not supported by the provider.

  • It was not possible to set the property.

  • colid in the DBPROP structure was invalid.

  • The data type in vValue in the DBPROP structure was not the data type of the property or was not VT_EMPTY.

  • The value in vValue in the DBPROP structure was invalid.

  • The property's value conflicted with an existing property.

  • A property was specified to be applied to all columns, but could not be applied to one or more columns.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
ppColRowset was a null pointer.

cPropertySets was greater than zero and rgPropertySets was a null pointer.

In an element of rgPropertySets, cProperties was not zero and rgProperties was a null pointer.

cOptColumns was greater than zero and rgOptColumns was a null pointer.

E_NOINTERFACE
The column metadata rowset did not support the interface specified in riid.

E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called and the object is in a zombie state. This error can be returned only when the method is called on a rowset.

DB_E_ABORTLIMITREACHED
The method failed because a resource limit has been reached. For example, a query used to implement the method timed out. No rowset is returned.

DB_E_BADCOLUMNID
An element of rgOptColumns was an invalid DBID.

DB_E_ERRORSOCCURRED
No rowset was returned because one or more properties—for which the dwOptions element of the DBPROP structure was DBPROPOPTIONS_REQUIRED or an invalid value—were not set. The consumer checks dwStatus in the DBPROP structures to determine which properties were not set. None of the satisfiable properties are remembered. The 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.

DB_E_NOAGGREGATION
pUnkOuter was not a null pointer and the column's rowset does not support aggregation.

pUnkOuter was non-null and riid was not IID_Unknown.

DB_E_NOCOMMAND
No command text was set. This error can be returned only when this method is called from the command object.

DB_E_NOTPREPARED
The command exposed ICommandPrepare and the command text was set, but the command was not prepared. This error can be returned only when this method is called from the command object.

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 create the column metadata rowset.

Comments

This method makes no logical change to the state of the object.

GetColumnsRowset creates a rowset containing metadata about a rowset. Unlike IColumnsInfo::GetColumnInfo, it provides all of the metadata, but it is more complex to implement and use.

The rows in the column metadata rowset describe the columns in the underlying rowset. The column metadata rowset contains one row for each column in the rowset. This includes the columns of the base table and any pseudo-columns generated by the provider or data source, such as bookmarks and row IDs.

The order of the rows is the order in which the columns appear in the rowset (column ordinal order). This is the same order as they appear in IColumnsInfo. The order is usually predictable from the ordering of requested columns in the command text; if the command text does not specify an order, such as SELECT * FROM MyTable, then the order is determined by the provider, such as when the command is prepared.

Each column in the column metadata rowset describes a single attribute, such as the name or data type, of a column in the original rowset. The order of the required columns is the same as the order in which they are listed below. The order of the optional columns is arbitrary, although they must be after the required columns. That is, the optional columns in the column metadata rowset can occur in any order after the required columns.

The column metadata rowset always includes the required columns. It contains only those optional columns which are requested.

GetColumnsRowset can be called for rowsets created by GetColumnsRowset.

Calling GetColumnsRowset on a command before the command is executed may be an expensive operation.

Required Metadata Columns

The column metadata rowset always contains the following columns; these columns return the same information as GetColumnInfo.

Column ID | Type indicator | Description
DBCOLUMN_IDNAME DBTYPE_WSTR Column name. This column, together with the DBCOLUMN_GUID and DBCOLUMN_PROPID columns, form the ID of the column. One or more of these columns will be NULL depending on which elements of the DBID structure the provider uses.

The column ID of a base table should be invariant under views.
DBCOLUMN_GUID | DBTYPE_GUID | Column GUID.
DBCOLUMN_PROPID | DBTYPE_UI4 | Column property ID.
DBCOLUMN_NAME DBTYPE_WSTR The name of the column; this might not be unique. If this cannot be determined, a NULL is returned.

The name can be different from the value returned in DBCOLUMN_IDNAME if the column has been renamed by the command text. This name always reflects the most recent renaming of the column in the current view or command text.

If GetColumnsRowset is called for a column metadata rowset (the rowset returned by GetColumnsRowset), the name of each column is the name of the column ID constant. For example, the name of the DBCOLUMN_SCALE column is "DBCOLUMN_SCALE".
DBCOLUMN_NUMBER | DBTYPE_UI4 | The ordinal of the column. This is zero for the bookmark column of the row, if any. Other columns are numbered starting with one. This column cannot contain a NULL value.
DBCOLUMN_TYPE | DBTYPE_UI2 | The indicator of the column's data type. If the data type of the column varies from row to row, this must be DBTYPE_VARIANT. This column cannot contain a NULL value. For a list of valid type indicators, see "Type Indicators" in Appendix A.
DBCOLUMN_TYPEINFO DBTYPE_
IUNKNOWN 		Reserved for future use. Providers should return a null pointer in pTypeInfo.
DBCOLUMN_
COLUMNSIZE 		DBTYPE_UI4 The maximum possible length of a value in the column. For columns that use a fixed-length data type, this is the size of the data type. For columns that use a variable-length data type, this is one of the following:
  • The maximum length of the column in characters, for DBTYPE_STR and DBTYPE_WSTR, or bytes, for DBTYPE_BYTES, if one is defined. For example, a CHAR(5) column in an SQL table has a maximum length of 5.

  • The maximum length of the data type in characters, for DBTYPE_STR and DBTYPE_WSTR, or bytes, for DBTYPE_BYTES, if the column does not have a defined length.

  • ~0 (bitwise, the value is not 0; that is, all bits are set to 1) if neither the column nor the data type has a defined maximum length.

For data types that do not have a length, this is set to ~0 (bitwise, the value is not 0; that is, all bits are set to 1).
DBCOLUMN_
PRECISION 		DBTYPE_UI2 If DBCOLUMN_TYPE is a numeric data type, this is the maximum precision of the column. The precision of columns with a data type of DBTYPE_DECIMAL or DBTYPE_NUMERIC depends on the definition of the column. For the precision of all other numeric data types, see "Precision of Numeric Data Types" in Appendix A.

If DBCOLUMN_TYPE is not a numeric data type, this is NULL.
DBCOLUMN_SCALE | DBTYPE_I2 | If DBCOLUMN_TYPE is DBTYPE_DECIMAL or DBTYPE_NUMERIC, this is the number of digits to the right of the decimal point. Otherwise, this is NULL.
DBCOLUMN_FLAGS | DBTYPE_UI4 | A bitmask that describes column characteristics. The DBCOLUMNFLAGS enumerated type specifies the bits in the bitmask. For more information, see IColumnsInfo::GetColumnInfo. This column cannot contain a NULL value.

Optional Metadata Columns

The following columns are optional; if the column metadata rowset does not contain one of them, the consumer can safely use the default value. The default value is the value the consumer should assume if the provider does not support that information. It is also the value the column metadata rowset returns when the provider does have support, but does not specify that information for a particular column. The provider can also have optional, provider-specific columns.

Column ID | Type indicator | Description
DBCOLUMN_ BASECATALOGNAME | DBTYPE_WSTR | The name of the catalog in the data source that contains the column. NULL if the base catalog name cannot be determined. The default of this column is NULL.
DBCOLUMN_ BASECOLUMNNAME | DBTYPE_WSTR | The name of the column in the data source. This might be different than the column name returned in the DBCOLUMN_NAME column if an alias was used. NULL if the base column name cannot be determined. The default of this column is NULL.
DBCOLUMN_ BASESCHEMANAME | DBTYPE_WSTR | The name of the schema in the data source that contains the column. NULL if the base schema name cannot be determined. The default of this column is NULL.
DBCOLUMN_ BASETABLENAME | DBTYPE_WSTR | The name of the table in the data source that contains the column. NULL if the base table name cannot be determined. The default of this column is NULL.
DBCOLUMN_CLSID | DBTYPE_GUID | If all objects in the column all have the same class ID, this is that class ID. If the column may contain objects with different class IDs, or if the column is not of DBTYPE_IUNKNOWN, this is set to NULL. The default of this column is NULL.
DBCOLUMN_ COLLATINGSEQUENCE | DBTYPE_I4 | The locale ID (LCID) that defines the collating sequence for the column. For more information, see the OLE documentation. The default of this column is the code page installed on the local machine.
DBCOLUMN_ COMPUTEMODE | DBTYPE_I4 | Whether a column is computed. One of the following: DBCOMPUTEMODE_COMPUTED: The column is computed, such as Salary/12. DBCOMPUTEMODE_DYNAMIC: The column is computed and IRowset::GetData returns the value of the column based on the current values of its component columns, which might have been changed with IRowsetChange::SetData or IRowsetChange::InsertRow. DBCOMPUTEDMODE_NOTCOMPUTED: The column is not computed. This is the default.
DBCOLUMN_ DATETIMEPRECISION | DBTYPE_UI4 | The datetime precision—number of digits in the fractional seconds portion—if the column is a datetime or interval type. The default of this column is derived from the value in column DATETIME_PRECISION in the COLUMNS schema rowset.
DBCOLUMN_ DEFAULTVALUE | DBTYPE_ VARIANT | The column default value if declared statically. Dynamic initialization is handled by notifications. NULL if the default value cannot be determined. For a list of possible values, see the COLUMN_DEFAULT in "COLUMNS Rowset" in Appendix B. The default of this column is NULL.
DBCOLUMN_ DOMAINCATALOG | DBTYPE_WSTR | The name of the catalog containing the column's domain. NULL if the domain catalog name cannot be determined. The default of this column is NULL.
DBCOLUMN_ DOMAINSCHEMA | DBTYPE_WSTR | The name of the schema containing the column's domain. NULL if the domain schema name cannot be determined. The default of this column is NULL.
DBCOLUMN_ DOMAINNAME | DBTYPE_WSTR | The name of the domain of which the column is a member. NULL if the domain name cannot be determined. The default of this column is NULL.
DBCOLUMN_ HASDEFAULT | DBTYPE_BOOL | VARIANT_TRUE: The column has a default value. VARIANT_FALSE: The column does not have a default value. NULL: The provider could not determine if the column has a default value or if a default value does not make sense for the column. For example, it is a computed, derived, or nonupdatable column. The default of this column is VARIANT_FALSE.
DBCOLUMN_ ISAUTOINCREMENT | DBTYPE_BOOL | VARIANT_TRUE: The column assigns values to new rows in fixed increments. VARIANT_FALSE: The column does not assign values to new rows in fixed increments. The default of this column is VARIANT_FALSE.
DBCOLUMN_ ISCASESENSITIVE | DBTYPE_BOOL | VARIANT_TRUE if the order of the column is case sensitive and if searches on the column are case sensitive. Otherwise, VARIANT_FALSE. The default of this column is VARIANT_TRUE.
DBCOLUMN_ ISSEARCHABLE | DBTYPE_UI4 | An integer indicating the searchability of a column. For more information, see the SEARCHABLE column in "PROVIDER_TYPES Rowset" in Appendix B. The default of this column is derived from the value of the SEARCHABLE column in the PROVIDER_TYPES schema rowset.
DBCOLUMN_ ISUNIQUE | DBTYPE_BOOL | VARIANT_TRUE: No two rows in the base table—the table returned in DBCOLUMN_BASETABLENAME—can have the same value in this column. DBCOLUMN_ISUNIQUE is guaranteed to be VARIANT_TRUE if the column constitutes a key by itself, or if there is a constraint of type UNIQUE that applies only to this column. VARIANT_FALSE: The column can contain duplicate values in the base table. The default of this column is VARIANT_FALSE.
DBCOLUMN_ MAYSORT | DBTYPE_BOOL | VARIANT_TRUE: The column can be sorted. VARIANT_FALSE: The column cannot be sorted.
DBCOLUMN_ OCTETLENGTH | DBTYPE_UI4 | The maximum length in octets (bytes) of the column, if the column is a character or binary type. A value of zero means the column has no maximum length. NULL for all other types of columns. The default of this column is derived from the value of the CHARACTER_OCTET_LENGTH column in the COLUMNS schema rowset.
DBCOLUMN_KEYCOLUMN | DBTYPE_BOOL | VARIANT_TRUE—The column is one of a set of columns required in order to uniquely identify the row. VARIANT_FALSE—The column is not required to uniquely identify the row. If the DBPROP_UNIQUEROW property is set to VARIANT_TRUE, then the set of columns with DBCOLUMN_KEYCOLUMN set to VARIANT_TRUE uniquely identifies a row in the rowset.
DBCOLUMN_BASETABLEVERSION | DBTYPE_UI8 | Version number of the table in the data source that contains the column. This number is assumed to change every time the table definition is modified. The way in which this number is generated is provider specific.

See Also

IColumnsInfo::GetColumnInfo, IColumnsRowset::GetAvailableColumns, IRowsetInfo::GetProperties


ICommand

ICommand contains methods to execute commands. A command can be executed many times and the parameter values can vary. This interface is mandatory on commands.

A command object contains a single text command, which is specified through ICommandText.

Method | Description
Cancel | Cancels the current command execution.
Execute | Executes the command.
GetDBSession | Returns an interface pointer to the session that created the command.


ICommand::Cancel

Cancels the current command execution.

HRESULT Cancel();

Parameters

None.

Return Code

S_OK
The method succeeded.

The command was not executing.

E_FAIL
A provider-specific error occurred.

DB_E_CANTCANCEL
The executing command cannot be canceled.

Comments

Cancel may be called on a separate thread after Execute has been called and before the method has returned. If the executing command can be canceled, the method returns S_OK, and Execute returns DB_E_CANCELED. If the executing command cannot be canceled, Cancel returns DB_E_CANTCANCEL and Execute continues. If the Execute method has not been called or has already returned, the method returns S_OK.

If the DBPROPVAL_ASYNCH_INITIALIZE bit in the DBPROP_ROWSET_ASYNCH property has been set prior to execution of the command, the Execute method may return with DB_S_ASYNCHRONOUS before the command has fully executed. Calling ICommand::Cancel after the Execute method has returned has no effect on any asynchronously executing commands. Once the Execute method has returned DB_S_ASYNCHRONOUS, the command can only be canceled by calling IDBAsynchStatus::Abort on the returned object.

See Also

ICommand::Execute


ICommand::Execute

Executes the command.

HRESULT Execute (
   IUnknown *      pUnkOuter,
   REFIID            riid,
   DBPARAMS *   pParams,
   LONG   *         pcRowsAffected,
   IUnknown **      ppRowset);

Parameters

pUnkOuter

[in]
A pointer to the controlling IUnknown interface if the rowset is being created as part of an aggregate; otherwise, it is null.

riid

[in]
The requested IID for the rowset returned in *ppRowset. This interface is conceptually added to the list of required interfaces on the resulting rowset, and the method fails (E_NOINTERFACE) if that interface cannot be supported on the resulting rowset.

If this is IID_NULL, then ppRowset is ignored and no rowset is returned, even if the command would otherwise generate a rowset. Specifying IID_NULL is useful in the case of text commands that do not generate rowsets, such as data definition commands, as a hint to the provider that no rowset properties need to be verified.

If riid is IID_IMultipleResults, the provider creates a multiple results object and returns a pointer to it in *ppRowset; it does this even if the command generates a single result. If the provider supports multiple results and the command generates multiple results but riid is not IID_IMultipleResults, the provider returns the first result and discards any remaining results. If riid is IID_IMultipleResults and the provider does not support multiple results, Execute returns E_NOINTERFACE.

pParams

[in/out]
A pointer to a DBPARAMS structure that specifies the values for one or more parameters. In text commands that use parameters, if no value is specified for a parameter through pParams, an error occurs.

struct DBPARAMS {
 void *  pData;
 ULONG   cParamSets;
 HACCESSOR hAccessor;
};

The elements of this structure are used as follows.

Element | Description
pData | Pointer to a buffer from which the provider retrieves input parameter data and to which the provider returns output parameter data, according to the bindings specified by hAccessor. This pointer must be a valid pointer to a contiguous block of consumer-owned memory for the input and output parameter values. For more information, see "Setting Data" and "Getting Data" in Chapter 6. When output parameter data is available to the consumer depends on the DBPROP_OUTPUTPARAMETERAVAILABILITY property.
cParamSets | The number of sets of parameters in *pData. If cParamSets is greater than one, then the bindings described by hAccessor define the offsets within *pData for each set of parameters, and cbRowSize (as specified in IAccessor::CreateAccessor) defines a single fixed offset between each of those values and the corresponding values for the next set of parameters. Sets of multiple parameters (cParamSets is greater than one) can be specified only if DBPROP_MULTIPLEPARAMSETS is VARIANT_TRUE and the command does not return any rowsets.
hAccessor | Handle of the accessor to use. If hAccessor is the handle of a null accessor (cBindings in CreateAccessor was 0), then Execute does not retrieve or return any parameter values.

If the command text does not include parameters, the provider ignores this argument.

pcRowsAffected

[out]
A pointer to memory in which to return the count of rows affected by a command that updates, deletes, or inserts rows. If cParamSets is greater than one, *pcRowsAffected is the total number of rows affected by all of the sets of parameters specified in the execution. If the number of affected rows is not available, *pcRowsAffected is set to DB_COUNTUNAVAILABLE on output. If riid is IID_IMultipleResults, the value returned in *pcRowsAffected is either DB_COUNTUNAVAILABLE or the total number of rows affected by the entire command; to retrieve individual row counts, the consumer calls IMultipleResults::GetResult. If the command does not update, delete, or insert rows, *pcRowsAffected is undefined on output. If pcRowsAffected is a null pointer, no count of rows is returned.

pcRowsAffected is undefined if ICommand::Execute returns DB_S_ASYNCHRONOUS. For asynchronously executed commands, the consumer should call IDBAsynchStatus::GetStatus to obtain the number of rows affected in pulProgress.

ppRowset

[in/out]
A pointer to the memory in which to return the rowset's pointer. If ppRowset is a null pointer, no rowset is created.

Return Code

S_OK
The method succeeded. In all DBPROP structures returned by the method, dwStatus is set to DBPROPSTATUS_OK, the status of all input parameters bound by the accessor is set to DBSTATUS_S_OK or DBSTATUS_S_ISNULL, and the status of all output parameters bound by the accessor is set to DBSTATUS_S_OK, DBSTATUS_S_ISNULL, or DBSTATUS_S_TRUNCATED or is unknown because the parameter value has not been returned yet.

DB_S_ASYNCHRONOUS
The method has initiated asynchronous creation of the rowset. The consumer can call IDBAsynchStatus to poll for status or IConnectionPointContainer to obtain the IID_IDBAsynchNotify connection point. Attempting to call any other interfaces may fail, and the full set of interfaces may not be available on the object until asynchronous initialization of the rowset has completed.

DB_S_ERRORSOCCURRED
This can be returned for any of the following reasons.

  • The command was executed but an error occurred while returning one or more output parameter values. To determine which output parameters were not returned, the consumer checks the status values. For a list of status values that can be returned by this method, see "Status Values Used When Getting Data" in "Status" in Chapter 6.

  • The rowset was opened but one or more properties—for which the dwOptions element of the DBPROP structure was DBPROPOPTIONS_OPTIONAL—were not set. The consumer calls IRowsetInfo::GetProperties to determine which properties were set.

DB_S_STOPLIMITREACHED
Execution has been stopped because a resource limit has been reached. The results obtained so far have been returned. Execution cannot be resumed.

Multiple sets of parameters were specified and one or more, but not all of the parameters have been processed prior to the command being canceled by ICommand::Cancel or IDBAsynchStatus::Abort.

This return code takes precedence over DB_S_ERRORSOCCURRED. That is, if the conditions described here and in those described in DB_S_ERRORSOCCURRED both occur, the provider returns this code. When the consumer receives this return code, it should also check for the conditions described in DB_S_ERRORSOCCURRED.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
pParams was not ignored, cParamSets in the DBPARAMS structure pointed to by pParams was greater than one, ppRowset was not a null pointer, and the provider does not support multiple results.

pParams was not ignored and, in the DBPARAMS structure pointed to by pParams, pData was a null pointer.

pParams was not ignored, was not a null pointer, and in the DBPARAMS structure pointed to by pParams, cParamSets was zero.

riid was not IID_NULL and ppRowset was a null pointer.

E_NOINTERFACE
The rowset did not support the interface specified in riid.

riid was IID_IMultipleResults and the provider did not support multiple results objects.

DB_E_ABORTLIMITREACHED
Execution has been aborted because a resource limit has been reached. For example, a query timed out. No results have been returned.

DB_E_BADACCESSORHANDLE
pParams was not ignored and hAccessor in the DBPARAMS structure pointed to by pParams was invalid.

DB_E_BADACCESSORTYPE
hAccessor in the DBPARAMS structure pointed to by pParams was not the handle of a parameter accessor.

DB_E_CANCELED
The command was canceled by a call to Cancel on another thread. No records were affected.

DB_E_CANTCONVERTVALUE
A literal value in the command text could not be converted to the type of the associated column for reasons other than data overflow.

DB_E_DATAOVERFLOW
A literal value in the command text overflowed the type specified by the associated column.

DB_E_ERRORSINCOMMAND
The command text contained one or more errors. Providers should use OLE DB error objects to return details about the errors.

DB_E_ERRORSOCCURRED
The method failed due to one or more invalid input parameter values. To determine which input parameter values were invalid, the consumer checks the status values. For a list of status values that can be returned by this method, see "Status Values Used When Setting Data" in "Status" in Chapter 6.

The command was not executed and no rowset was returned because one or more properties—for which the dwOptions element of the DBPROP structure was DBPROPOPTIONS_REQUIRED—were not set.

DB_E_INTEGRITYVIOLATION
A literal value in the command text violated the integrity constraints for the column.

DB_E_NOAGGREGATION
pUnkOuter was not a null pointer and the rowset being created does not support aggregation.

pUnkOuter was non-null and riid was not IID_Unknown.

DB_E_NOCOMMAND
No command text was currently set on the command object.

DB_E_NOTABLE
The specific table or view does not exist in the current data source.

DB_E_PARAMNOTOPTIONAL
A value was not supplied for a required parameter.

The command text used parameters and pParams was a null pointer.

DB_SEC_E_PERMISSIONDENIED
The consumer did not have sufficient permission to execute the command. For example, a rowset-returning command specified a column for which the consumer does not have read permission, or an update command specified a column for which the consumer does not have write permission.

DB_E_OBJECTOPEN
The provider would have to open a new connection to support the operation and DBPROP_MULTIPLECONNECTIONS is set to VARIANT_FALSE.

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 HRESULTs for the 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

If the command returns rows, such as an SQL SELECT statement, the result of this method is a rowset over the result rows. If no rows match the command, the rowset is still created. The resulting rowset is fully functional and can be used, for example, to insert new rows or determine column metadata.

If the command returns multiple results (rowsets or row counts), the consumer requests a multiple-results object by setting riid to IID_IMultipleResults. Execute creates the multiple-results object and returns an IMultipleResults interface pointer to it in *ppRowset. The consumer repeatedly calls IMultipleResults::GetResult to retrieve the results in order. For more information, see "Multiple Results" in Chapter 3.

If any or all parameters fail and the provider does not support errors within an array of parameters (that is, the command fails if any or all of the parameters fail), the provider returns DB_E_ERRORSOCCURRED and returns any error information for the failed parameters in their status bindings.

If any or all parameters fail and the provider supports errors within an array of parameters, the provider returns DB_S_ERRORSOCCURRED, sets pcRowsAffected to the number of successful parameters, and returns any error information for the failed parameters in their status bindings.

If Execute is called multiple times for a single command, with or without changes to the command text, the outcome may reflect changes in the underlying stored data, depending on the isolation level specified for the surrounding transaction.

Execute can be called when a rowset is already open on the command only if the only change between the calls is a change in the value of existing parameters (calls to ICommandWithParameters::SetParameterInfo will fail). Methods that modify the command (ICommandPrepare::Prepare, ICommandPrepare::Unprepare, ICommandProperties::SetProperties, and ICommandText::SetCommandText) while a rowset is open will fail and return DB_E_OBJECTOPEN. Each call to Execute creates a new rowset, which must be explicitly released by IRowset::Release.

Execute does not affect the prepared state of a command.

The consumer determines whether the command supports parameters by calling QueryInterface for ICommandWithParameters. If this interface is exposed, the command supports parameters; if it is not exposed, the command does not support parameters. If the command does not support parameters, Execute ignores pParams. However, if the command text includes parameters, Execute returns DB_E_ERRORSINCOMMAND.

If an input parameter value is not specified, Execute returns DB_E_PARAMNOTOPTIONAL. If the provider cannot describe parameters and the consumer has not called SetParameterInfo for all parameters, the behavior of Execute is undefined. For example, Execute might guess at the parameter information or it might fail completely. For more information, see SetParameterInfo.

If Execute returns DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED, the consumer can immediately call ICommandProperties::GetProperties with the DBPPROPSET_PROPERTIESINERROR property set to return the properties that could not be set. For more information, see "Property Sets" in Chapter 11.

Execute does not alter the value of any properties. That is, ICommandProperties::GetProperties returns the same value for a property regardless of whether it is called before or after Execute and whether Execute succeeded or failed. However, if a property value is not required, IRowsetInfo::GetProperties can return a different value for that property than ICommandProperties::GetProperties. For more information, see IRowsetInfo::GetProperties.

If several threads concurrently request execution of a given command, the corresponding executions are serialized, and each thread will block until its corresponding execution concludes.

Execute can fail even if ICommandPrepare::Prepare has succeeded; this may be the case if, for example, the underlying schema has changed between the Prepare and Execute calls and the command text had therefore become illegal.

See Also

ICommand::Cancel, ICommandPrepare::Prepare, ICommandText::SetCommandText, ICommandWithParameters::SetParameterInfo


ICommand::GetDBSession

Returns an interface pointer to the session that created the command.

HRESULT GetDBSession (
   REFIID         riid,
   IUnknown **   ppSession);

Parameters

riid

[in]
The IID of the interface on which to return a pointer.

ppSession

[out]
A pointer to memory in which to return the interface pointer. If the provider does not have an object that created the command, it sets *ppSession to a null pointer. If GetDBSession fails, it must attempt to set *ppSession to a null pointer.

Return Code

S_OK
The method succeeded.

S_FALSE
The provider did not have an object that created the command. Therefore, it set *ppSession to a null pointer.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
ppSession was a null pointer.

E_NOINTERFACE
The session did not support the interface specified in riid.


ICommandPersist

Command objects support ICommandPersist for persisting the state of a command object. Persisting a command object does not persist any active rowsets that may have resulted from the execution of the command object, nor does it persist accessors associated with the command object, current property settings, or specified parameter information.

Persisted commands can be enumerated through the PROCEDURES rowset. Persisted commands that can act as the source of a new command (that is, a table in an SQL FROM clause) can be enumerated through the VIEWS rowset.

When to Implement

ICommandPersist is implemented by providers that can persist a command object.

ICommandPersist methods

Method | Description
DeleteCommand | Deletes a persisted command.
GetCurrentCommand | Returns the DBID of the current command.
LoadCommand | Loads a persisted command.
SaveCommand | Saves the current command.


ICommandPersist:: DeleteCommand

Deletes a persisted command definition.

HRESULT   DeleteCommand(
   DBID *   pCommandID);

Parameters

pCommandID

[in]
The DBID of the command to be deleted.

Return Code

S_OK

The command was successfully deleted.

DB_E_BADCOMMANDID

The command specified in *pCommandID does not exist.

E_FAIL

A provider-specific error occurred.


ICommandPersist:: GetCurrentCommand

Returns the DBID of the current command.

HRESULT   GetCurrentCommand(
   DBID *   pCommandID);

Parameters

pCommandID

[out]
The DBID of the current command.

Return Code

S_OK

The DBID of the command was successfully returned.

E_OUTOFMEMORY

There is not enough memory to successfully return the command's DBID.

DB_E_COMMANDNOTPERSISTED

The current command does not yet have a DBID.

E_FAIL

A provider-specific error occurred.


ICommandPersist:: LoadCommand

Loads a persisted command definition.

HRESULT   LoadCommand(
   DBID *   pCommandID,
   DWORD   dwFlags);

Parameters

pCommandID

[in]
The DBID of the command to load.

dwFlags

[in]
Reserved for future use. Consumers should set this value to zero. Providers should ignore this argument.

Return Code

S_OK

The command was successfully loaded.

E_OUTOFMEMORY

There is not enough memory to successfully load the command.

DB_E_OBJECTOPEN

A rowset was open on the command.

DB_E_BADCOMMANDID

The command specified in *pCommandID does not exist.

E_FAIL

A provider-specific error occurred.

Comments

LoadCommand loads the persisted command definition into the current command object. Loading a persisted command definition does not change the command object's transaction scope and semantics, nor does it change the session scope. Set property values as well as specified parameter information are not affected by loading a persisted command definition. LoadCommand fails if the current command object has any outstanding rowsets (DB_E_OBJECTOPEN).


ICommandPersist:: SaveCommand

Persists the current command definition.

HRESULT   SaveCommand(
   DBID *   pCommandID,
   DWORD   dwFlags);

Parameters

pCommandID

[in]
The DBID to assign to the persisted command. If this is a null pointer and the command already has a DBID, the provider saves the current version with the same DBID. If this is a null pointer and the command does not already have a DBID, the provider assigns an ID to the command. The ID must be unique.

dwFlags

[in]
Options for persisting command definition. Providers return DB_E_INVALIDARG if a flag the provider does not know is specified. You can use DBCOMMANDPERSISTFLAG_NOSAVE to associate or obtain a new DBID for the command without actually persisting the definition.

Return Code

S_OK

The command was successfully saved.

DB_E_BADCOMMANDID

The DBID specified in *pCommandID was invalid.

E_INVALIDARG

The argument dwFlags specified a value unknown by the provider.

DB_E_DUPLICATEID

The specified DBID already exists. If the provider supports the use of persisted commands as a source in a new command definition, DBIDs must be unique across persisted commands, procedures, tables, and views.

E_FAIL

A provider-specific error occurred.

Comments

SaveCommand saves the current command definition. It does not persist any open rowsets, current property values, or specified parameter information.


ICommandPrepare

This optional interface encapsulates command optimization, a separation of compile time and run time, as found in traditional relational database systems. The result of this optimization is a command execution plan.

If the provider supports command preparation, by supporting this interface, commands must be in a prepared state prior to calling the following methods.

  • IColumnsInfo::GetColumnInfo

  • IColumnsInfo::MapColumnIDs

  • IColumnsRowset::GetAvailableColumns

  • IColumnsRowset::GetColumnsRowset
Method | Description
Prepare | Validates and optimizes the current command.
Unprepare | Discards the current command execution plan.


ICommandPrepare::Prepare

Validates and optimizes the current command.

HRESULT Prepare (
ULONG cExpectedRuns);

Parameters

cExpectedRuns

[in]
Using this parameter, the consumer can indicate how often the command execution plan, which is produced by Prepare, will be used; that is, how often the command is likely to be executed without renewed optimization. This guides the optimizer in determining tradeoffs between search effort and run-time processing effort. A value of zero indicates that the consumer is unable to provide an estimate, and leaves it to the optimizer to choose a default value.

Return Code

S_OK
The method succeeded.

DB_S_ERRORSOCCURRED
The command was prepared but one or more properties—for which the dwOptions element of the DBPROP structure was DBPROPOPTIONS_OPTIONAL—were not set. The consumer calls ICommandProperties::GetProperties to determine which properties were set.

E_FAIL
A provider-specific error occurred.

E_OUTOFMEMORY
The provider ran out of memory while preparing the command.

DB_E_ABORTLIMITREACHED
Preparation has been aborted because a resource limit has been reached. For example, the preparation timed out.

DB_E_ERRORSINCOMMAND
The command text contained one or more errors. Providers should use OLE DB error objects to return details about the errors.

DB_E_ERRORSOCCURRED
The command was not prepared because one or more properties—for which the dwOptions element of the DBPROP structure was DBPROPOPTIONS_REQUIRED—were not set. The consumer calls ICommandProperties::GetProperties to determine which properties were not set.

DB_E_NOCOMMAND
No command text was currently set on the Command object.

DB_E_OBJECTOPEN
A rowset was open on the command.

DB_SEC_E_PERMISSIONDENIED
The consumer did not have sufficient permission to prepare the command.

Comments

Although they are not required to do so, consumers should set any properties before calling Prepare, because these properties might be relevant to preparing the command.

If Prepare is called redundantly, the provider determines whether command optimization is reinvoked; the provider returns S_OK.

If Prepare returns DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED, the consumer can immediately call ICommandProperties::GetProperties with the DBPPROPSET_PROPERTIESINERROR property set to return the properties that could not be set. For more information, see "Property Sets" in Chapter 11.

See Also

ICommand::Execute, ICommandPrepare::Unprepare, ICommandWithParameters::SetParameterInfo


ICommandPrepare::Unprepare

Discards the current command execution plan.

HRESULT Unprepare();

Parameters

None.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

DB_E_OBJECTOPEN
A rowset was open on the command.

Comments

This method has no effect if the command was not prepared; the provider returns S_OK.

See Also

ICommandPrepare::Prepare


ICommandProperties

ICommandProperties specifies to the command the properties from the Rowset property group that must be supported by the rowsets returned by ICommand::Execute. A special case of these properties, and the ones most commonly requested, are the interfaces the rowset must support. In addition to interfaces, the consumer can request properties that modify the behavior of the rowset or interfaces.

All rowsets must support IRowset, IAccessor, IColumnsInfo, IRowsetInfo, and IConvertType. Providers may choose to return rowsets supporting other interfaces if doing so is possible and the support for the returned interfaces does not affect consumer code that is not expecting them. The riid parameter of ICommand::Execute should be one of the interfaces returned by IRowsetInfo::GetProperties.

This interface is mandatory on commands.

Method | Description
GetProperties | Returns the list of properties in the Rowset property group that are currently requested for the rowset.
SetProperties | Sets properties in the Rowset property group.


ICommandProperties::GetProperties

Returns the list of properties in the Rowset property group that are currently requested for the rowset.

HRESULT GetProperties (
   const ULONG            cPropertyIDSets,
   const DBPROPIDSET   rgPropertyIDSets[],
   ULONG *                  pcPropertySets,
   DBPROPSET **         prgPropertySets);

Parameters

cPropertyIDSets

[in]
The number of DBPROPIDSET structures in rgPropertyIDSets.

If cPropertySets is zero, the provider ignores rgPropertyIDSets and returns the values of all properties in the Rowset property group for which values have been set or defaults exist. It does not return the values of properties in the Rowset property group for which values have not been set and no defaults exist, nor does it return the values of properties for which no value has been set, nor default exists, and for which a value will be set automatically because a value for another property in the Rowset property group has been set.

If cPropertyIDSets is not zero, the provider returns the values of the requested properties. If a property is not supported, the returned value of dwStatus in the returned DBPROP structure for that property is DBPROPSTATUS_NOTSUPPORTED and the value of dwOptions is undefined.

rgPropertyIDSets

[in]
An array of cPropertyIDSets DBPROPIDSET structures. The properties specified in these structures must belong to the Rowset property group. The provider returns the values of the properties specified in these structures. If cPropertyIDSets is zero, then this parameter is ignored.

For information about the properties in the Rowset property group that are defined by OLE DB, see ""Rowset Properties"" in Appendix C. For information about the DBPROPIDSET structure, see "DBPROPIDSET Structure" in Chapter 11.

pcPropertySets

[out]
A pointer to memory in which to return the number of DBPROPSET structures returned in *prgPropertySets. If cPropertyIDSets is zero, *pcPropertySets is the total number of property sets for which the provider supports at least one property in the Rowset property group. If cPropertyIDSets is greater than zero, *pcPropertySets is set to cPropertyIDSets. If an error other than DB_E_ERRORSOCCURRED occurs, *pcPropertySets is set to zero.

prgPropertySets

[out]
A pointer to memory in which to return an array of DBPROPSET structures. If cPropertyIDSets is zero, then one structure is returned for each property set that contains at least one property belonging to the Rowset property group. If cPropertyIDSets is not zero, then one structure is returned for each property set specified in rgPropertyIDSets.

If cPropertyIDSets is not zero, the DBPROPSET structures in *prgPropertySets are returned in the same order as the DBPROPIDSET structures in rgPropertyIDSets; that is, for corresponding elements of each array, the guidPropertySet elements are the same. If cPropertyIDs, in an element of rgPropertyIDSets, is not zero, the DBPROP structures in the corresponding element of *prgPropertySets are returned in the same order as the DBPROPID values in rgPropertyIDs. Thus, in the case where no column properties are specified in rgPropertyIDSets, corresponding elements of the input rgPropertyIDs and the returned rgProperties have the same property ID. However, if a column property is requested in rgPropertyIDSets, multiple properties may be returned, one for each column, in rgProperties. In this case, corresponding elements of rgPropertyIDs and rgProperties will not have the same property ID, and rgProperties will contain more elements than rgPropertyIDs.

The provider allocates memory for the structures and returns the address to this memory; the consumer releases this memory with IMalloc::Free when it no longer needs the structures. Before calling IMalloc::Free for *prgPropertySets, the consumer should call IMalloc::Free for the rgProperties element within each element of *prgPropertySets. If *pcPropertySets is zero on output or an error other than DB_E_ERRORSOCCURRED occurs, the provider does not allocate any memory and ensures that *prgPropertySets is a null pointer on output.

For information about the DBPROPSET and DBPROP structures, see "DBPROPSET Structure" and "DBPROP Structure" in Chapter 11.

Return Code

S_OK
The method succeeded. In all DBPROP structures returned by the method, dwStatus is set to DBPROPSTATUS_OK.

DB_S_ERRORSOCCURRED
No value was returned for one or more properties. The consumer checks dwStatus in the DBPROP structure to determine the properties for which values were not returned. GetProperties can fail to return properties for a number of reasons, including:

  • The property was not supported by the provider.

  • The property was not in the Rowset property group.

  • The property set was not supported by the provider. If cPropertyIDs in the DBPROPIDSET structure for the property set was zero, the provider cannot set dwStatus in the DBPROP structure because it does not know the IDs of any properties in the property set. Instead, it sets cProperties to zero in the DBPROPSET structure returned for the property set.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
cPropertyIDSets was not equal to zero and rgPropertyIDSets was a null pointer.

pcPropertySets or prgPropertySets was a null pointer.

In an element of rgPropertyIDSets, cPropertyIDs was not zero and rgPropertyIDs was a null pointer.

In an element of rgPropertyIDSets, guidPropertySet was DBPROPSET_PROPERTIESINERROR and cPropertyIDs was not zero, or rgPropertyIDs was not a null pointer.

cPropertyIDSets was greater than one and, in an element of rgPropertyIDSets, guidPropertySet was DBPROPSET_PROPERTIESINERROR.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to return the DBPROPSET or DBPROP structures.

DB_E_ERRORSOCCURRED
No values were returned for any properties. The provider allocates memory for *prgPropertySets and the consumer checks dwStatus in the DBPROP structures to determine why properties were not returned. The consumer frees this memory when it no longer needs the information.

Comments

GetProperties returns the current values of properties that have been set by the consumer with ICommandProperties::SetProperties. For all values not set by the consumer, GetProperties returns the initial property value.

Even though IDBProperties::GetPropertyInfo lists a property as being supported by the provider, GetProperties will not return a value for it if it does not apply to the current circumstances. For example, the provider's ability to support the property might be affected by the current transaction or the current command text.

The property values returned by ICommandProperties::GetProperties are not affected by executing the command. However, IRowsetInfo::GetProperties might return a different value for a property than does ICommandProperties::GetProperties. For example, if a consumer requests ordered bookmarks if they are possible, it calls SetProperties to set the value of DBPROP_ORDEREDBOOKMARKS to VARIANT_TRUE and specifies a dwOptions value of DBPROPOPTIONS_OPTIONAL. If the provider cannot determine at this point in the definition of the command whether this is possible, ICommandProperties::GetProperties returns a value of VARIANT_TRUE and a dwOptions of DBPROPOPTIONS_OPTIONAL for this property. If the provider determines during optimization or execution that ordered bookmarks are not possible, IRowsetInfo::GetProperties returns a value of VARIANT_FALSE and a dwOptions of zero.

If ICommand::Execute returns DB_E_ERRORSOCCURRED, the consumer can immediately call GetProperties with the DBPROPSET_PROPERTIESINERROR property set to return all the properties that were in error. For more information, see "Property Sets" in Chapter 11.

See Also

ICommandProperties::SetProperties, IDBProperties::GetPropertyInfo, IRowsetInfo::GetProperties


ICommandProperties::SetProperties

Sets properties in the Rowset property group.

HRESULT SetProperties (
   ULONG         cPropertySets,
   DBPROPSET   rgPropertySets[]);

Parameters

cPropertySets

[in]
The number of DBPROPSET structures in rgPropertySets. If this is zero, the provider ignores rgPropertySets and the method does not do anything.

rgPropertySets

[in/out]
An array of DBPROPSET structures containing properties and values to be set. The properties specified in these structures must belong to the Rowset property group. If the same property is specified more than once in rgPropertySets, then which value is used is provider-specific. If cPropertySets is zero, this parameter is ignored.

For information about the properties in the Rowset property group that are defined by OLE DB, see "Rowset Properties" in Appendix C. For information about the DBPROPSET and DBPROP structures, see "DBPROPSET Structure" and "DBPROP Structure" in Chapter 11.

Return Code

S_OK
The method succeeded. In all DBPROP structures passed to the method, dwStatus is set to DBPROPSTATUS_OK.

DB_S_ERRORSOCCURRED
One or more properties were not set. Properties not in error remain set. The consumer checks dwStatus in the DBPROP structures to determine which properties were not set. SetProperties can fail to set properties for a number of reasons, including:

  • The property was not supported by the provider.

  • The property was not in the Rowset property group.

  • The property set was not supported by the provider.

  • The property is read-only and was not set to its default value.

  • It was not possible to set the property.

  • The value of dwOptions in a DBPROP structure was invalid.

  • colid in the DBPROP structure was not DB_NULLID and the property cannot be set on a column.

  • The data type in vValue in the DBPROP structure was not the data type of the property or was not VT_EMPTY.

  • The value in vValue in the DBPROP structure was invalid.

  • The property's value conflicted with an existing property.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
cPropertySets was not equal to zero and rgPropertySets was a null pointer.

In an element of rgPropertySets, cProperties was not zero and rgProperties was a null pointer.

DB_E_ERRORSOCCURRED
All property values were invalid and no properties were set. The consumer checks dwStatus in the DBPROP structures to determine why properties were not set. The 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.

DB_E_OBJECTOPEN
Properties cannot be set while there is an open rowset.

Comments

Consumers should first set properties for interfaces and then set other properties that modify those interfaces. The combination of the values of an interface property and a noninterface property might result in another interface being requested. However, the value of a noninterface property, by itself, never results in another interface being requested.

Setting property values is a cumulative operation. That is, each call to SetProperties attempts to change the values of the specified properties. If a new value is illegal or conflicts with the value of another property, the value of the property is not changed and SetProperties returns DBPROPSTATUS_BADVALUE or DBPROPSTATUS_CONFLICTING in the dwStatus element of the DBPROP structure for the property. Properties are processed from the beginning of the array to the end of the array. As they are processed, properties that conflict with previously set properties, including those set previously on the same call, are marked with DBPROPSTATUS_CONFLICTING, and processing continues through the array of properties.

SetProperties cannot always determine whether a property can be set to its requested value. For example, the provider often cannot determine whether the rowset is updatable until it creates the rowset. In this case, SetProperties appears to successfully set the property value and the provider delays the determination until ICommand::Execute is called.

If the value of dwOptions for a property is DBPROPOPTIONS_OPTIONAL, the provider attempts to determine whether it can set the property to the requested value. If it can make this determination, it sets the property and returns DBPROPSTATUS_OK or does not set the property and returns DBPROPSTATUS_NOTSET. If it cannot make this determination, it can delay setting the property until the command is executed; in this case, the value of dwOptions returned for the property by GetProperties is DBPROPOPTIONS_OPTIONAL.

Consumers should not attempt to unset mandatory rowset interfaces such as IRowset, IAccessor, IColumnsInfo, and IRowsetInfo. These interfaces are always supported, and SetProperties returns DBPROPSTATUS_NOTSETTABLE for them.

Even if IDBProperties::GetPropertyInfo lists a property as being supported by the provider, SetProperties can return DBPROPSTATUS_NOTSUPPORTED when the consumer attempts to set the property value if the property does not apply to the current circumstances. For example, the provider's ability to support the property might be affected by the current transaction or the current command text.

If an error occurs when setting a particular property, SetProperties flags the error in dwStatus in the DBPROP structure and continues processing.

Although providers allow changing properties at any time before command execution, consumers are encouraged to set all properties prior to preparing the command to avoid forcing the provider to reprepare the command at execution.

See Also

ICommandProperties::GetProperties, IDBProperties::GetPropertyInfo, IRowsetInfo::GetProperties


ICommandText : ICommand

This interface is mandatory on commands.

A command object can have only one text command. When the command text is specified through SetCommandText, it replaces the existing command text.

Method | Description
GetCommandText | Returns the text command set by the last call to SetCommandText.
SetCommandText | Sets the command text, replacing the existing command text.


ICommandText::GetCommandText

Returns the text command set by the last call to SetCommandText.

HRESULT GetCommandText (
   GUID *            pguidDialect,
   LPOLESTR *   ppwszCommand);

Parameters

pguidDialect

[in/out]
A pointer to memory containing a GUID that specifies the syntax and general rules for parsing the command text—that is, the dialect that will be used by the provider to interpret the statement. This is generally the dialect specified in SetCommandText. However, if DBGUID_DEFAULT is specified in SetCommandText, the provider returns the particular dialect that it will use to interpret the statement. If the provider determines between multiple dialects at execution time for a command that has been set with DBGUID_DEFAULT, or if the provider is returning some provider-specific syntax that may be different from the command set in SetCommandText, it returns DBGUID_DEFAULT. Providers can define GUIDs for their own dialects.

For more information about dialects, see "Using Commands" in Chapter 3, "Commands."

If GetCommandText returns an error, *pguidDialect is set to DB_NULLGUID.

ppwszCommand

[out]
A pointer to memory in which to return the command text. The command object allocates memory for the command text and returns the address to this memory. The consumer releases this memory with IMalloc::Free when it no longer needs the text. If GetCommandText returns an error, *ppwszCommand is set to a null pointer.

Return Code

S_OK
The method succeeded.

DB_S_DIALECTIGNORED
The method succeeded, but the input value of pguidDialect was ignored. The text is returned in the dialect specified in SetCommandText or the dialect that makes the most sense to the provider. The value returned in *pguidDialect represents that dialect.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
ppwszCommand was a null pointer.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to return the command text.

DB_E_NOCOMMAND
No command text was currently set on the command object.

See Also

ICommandText::SetCommandText

Comments

The GUID returned in GetCommandText is the dialect that will be used by the provider to interpret the statement. This is generally the dialect specified in SetCommandText. However, if DBGUID_DEFAULT is specified in SetCommandText, the provider should return the particular dialect that it will use to interpret the statement. If the provider determines between multiple dialects at execution time for a command that has been set with DBGUID_DEFAULT, or if the provider is returning some provider-specific syntax that may be different from the command set in SetCommandText, it returns DBGUID_DEFAULT.


ICommandText::SetCommandText

Sets the command text, replacing the existing command text.

HRESULT SetCommandText (
   REFGUID         rguidDialect,
   LPCOLESTR   pwszCommand);

Parameters

rguidDialect

[in]
A GUID that specifies the syntax and general rules for the provider to use in parsing the command text. For a complete description of dialects, see GetCommandText.

pwszCommand

[in]
A pointer to the text of the command.

If *pwszCommand is an empty string ("") or pwszCommand is a null pointer, the current command text is cleared and the command is put in an unprepared state. Any properties that have been set on the command are unaffected; that is, they retain their current values. Methods that require a command, such as ICommand::Execute, ICommandPrepare::Prepare, or IColumnsInfo::GetColumnsInfo, will return DB_E_NOCOMMAND until a new command text is set.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

DB_E_DIALECTNOTSUPPORTED
The provider did not support the dialect specified in rguidDialect.

DB_E_OBJECTOPEN
A rowset was open on the command.

Comments

A command object contains a single text command, usually an SQL statement. The new command text is copied into the command object; thus, the consumer can delete the original text without affecting the command object. All meaningful error checking, such as syntax checking and parsing, is deferred until ICommandPrepare::Prepare or ICommand::Execute is called. SetCommandText only verifies that the command text can be copied into the command object's space.

If the text of a prepared or unprepared command is overwritten with new command text, by calling SetCommandText, the command object is left in an unprepared state.

SetCommandText does not alter the value of any properties. That is, ICommandProperties::GetProperties returns the same value for a property regardless of whether it is called before or after SetCommandText and whether SetCommandText succeeded or failed. Furthermore, setting the command text does not reset parameter information set through SetParameterInfo.

See Also

ICommand::Execute, ICommandPrepare::Prepare, ICommandText::GetCommandText, ICommandWithParameters::SetParameterInfo


ICommandWithParameters

Providers that support parameters must support ICommandWithParameters. Any provider that returns DBPROPVAL_SQL_ANSI92_INTERMEDIATE or DBPROPVAL_SQL_ANSI92_FULL for the DBPROP_SQLSUPPORT property can support parameters.

This optional interface encapsulates parameters. Parameters are scalar values, or a vector of scalar values, typically expressed in predicates but possibly supported by many providers in any scalar expression.

For scalar parameters of prepared commands, there is a presumption that different parameter values do not require different plans. In other words, a single preparation and its resulting plan are satisfactory for all possible values of scalar parameters.

Parameter values are set when a command is executed. Methods are included here to offer a means for setting and obtaining a list of parameters and their types.

Method | Description
GetParameterInfo | Gets a list of the command’s parameters, their names, and their types.
MapParameterNames | Returns an array of parameter ordinals when given named parameters.
SetParameterInfo | Specifies the native data type of each parameter.


ICommandWithParameters:: GetParameterInfo

Gets a list of the command’s parameters, their names, and their types.

HRESULT GetParameterInfo (
   ULONG *               pcParams,
   DBPARAMINFO **   prgParamInfo,
   OLECHAR **         ppNamesBuffer);

Parameters

pcParams

[out]
A pointer to memory in which to return the number of parameters in the command. If an error occurs, *pcParams is set to zero.

prgParamInfo

[out]
A pointer to memory in which to return an array of parameter information structures. The command allocates memory for the array, as well as the strings, and returns the address to this memory. The consumer releases the array memory with IMalloc::Free when it no longer needs the parameter information. If *pcParams is zero on output or an error occurs, the provider does not allocate any memory and ensures that *prgParamInfo is a null pointer on output. Parameters are returned in ascending order according to the iOrdinal element of the PARAMINFO structure.

Here is the DBPARAMINFO structure:

typedef struct tagDBPARAMINFO {
 DBPARAMFLAGS dwFlags;
 ULONG    iOrdinal;
 LPOLESTR   pwszName;
 ITypeInfo *  pTypeInfo;
 ULONG    ulParamSize;
 DBTYPE   wType;
 BYTE     bPrecision;
 BYTE     bScale;
} DBPARAMINFO;

The elements of this structure are used as follows.

Element | Description
dwFlags A bitmask describing parameter characteristics; these values have the following meaning:
  • DBPARAMFLAGS_ISINPUT—Whether a parameter accepts values on input. Not set if this is unknown.

  • DBPARAMFLAGS_ISOUTPUT—Whether a parameter returns values on output. Not set if this is unknown. Providers support only those parameter types that make sense for their data source.
dwFlags
(continued)
  • DBPARAMFLAGS_ISSIGNED—Whether a parameter is signed. This is ignored if the type is inherently signed, such as DBTYPE_I2, or if the sign does not apply to the type, such as DBTYPE_BSTR. It is generally used in SetParameterInfo so the consumer can tell the provider if a provider-specific type name refers to a signed or unsigned type.

  • DBPARAMFLAGS_ISNULLABLE—Whether a parameter accepts NULLs. If nullability is unknown, this flag is set.

  • DBPARAMFLAGS_ISLONG—Whether a parameter contains a BLOB that contains very long data. The definition of very long data is provider specific. The flag setting corresponds to the value of the IS_LONG column in the PROVIDER_TYPES schema rowset for the data type.

    When this flag is set, the BLOB is best manipulated through one of the storage interfaces. Although such BLOBs can be sent in a single piece with ICommand::Execute, there may be provider-specific problems in doing so. For example, the BLOB might be truncated due to machine limits on memory. Furthermore, when this flag is set, the provider might not be able to accurately return the maximum length of the BLOB data in ulParamSize in GetParameterInfo.

    When this flag is not set, the BLOB can be accessed either through ICommand::Execute or through a storage interface.

    For more information, see ""Accessing BLOB Data"" in Chapter 7.
iOrdinal | The ordinal of the parameter. Parameters are numbered from left to right as they appear in the command, with the first parameter in the command having an iOrdinal value of 1.
pwszName | The name of the parameter; it is a null pointer if there is no name. Names are normal names. The colon prefix (where used within SQL text) is stripped.
pTypeInfo | ITypeInfo describes the type, if pTypeInfo is not a null pointer.
ulParamSize The maximum possible length of a value in the parameter. For parameters that use a fixed-length data type, this is the size of the data type. For parameters that use a variable-length data type, this is one of the following:
  • The maximum length of the parameters in characters, for DBTYPE_STR and DBTYPE_WSTR, or bytes, for DBTYPE_BYTES, if one is defined. For example, a parameter for a CHAR(5) column in an SQL table has a maximum length of 5.

  • The maximum length of the data type in characters, for DBTYPE_STR and DBTYPE_WSTR, or bytes, for DBTYPE_BYTES, if the parameter does not have a defined length.

  • ~0 (bitwise, the value is not 0; that is, all bits are set to 1) if neither the parameter nor the data type has a defined maximum length.

For data types that do not have a length, this is set to ~0 (bitwise, the value is not 0; that is, all bits are set to 1).
wType | The indicator of the parameter’s data type.
bPrecision | If wType is a numeric type, bPrecision is the maximum number of digits, expressed in base 10. Otherwise, this is ~0 (bitwise, the value is not 0; that is, all bits are set to 1).
bScale | If wType is a numeric type with a fixed scale, bScale is the number of digits to the right, if bScale is positive, or left, if bScale is negative, of the decimal point. Otherwise, this is ~0 (bitwise, the value is not 0; that is, all bits are set to 1).

ppNamesBuffer

[out]
A pointer to memory in which to store all string values (names used within the *pwszName element of the DBPARAMINFO structures) with a single, globally allocated buffer. Specifying a null pointer for ppNamesBuffer suspends the return of parameter names. The command allocates memory for the buffer and returns the address to this memory. The consumer releases the memory with IMalloc::Free when it no longer needs the parameter information. If *pcParams is zero on output or an error occurs, the provider does not allocate any memory and ensures that *ppNamesBuffer is a null pointer on output.

Each of the individual string values stored in this buffer is terminated by a null termination character. Therefore, the buffer may contain one or more strings, each with its own null termination character, and may contain embedded null termination characters.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
pcParams or prgParamInfo was a null pointer.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to return the parameter data array or parameter names.

DB_E_NOCOMMAND
The provider can derive parameter information, but it does not support command preparation. However, no command text was currently set on the Command object and no parameter information had been specified with SetParameterInfo.

DB_E_NOTPREPARED
The provider can derive parameter information, and it supports command preparation. However, the command was in an unprepared state and no parameter information was specified with SetParameterInfo.

DB_E_PARAMUNAVAILABLE
The provider cannot derive parameter information from the command and SetParameterInfo has not been called.

Comments

This method makes no logical change to the state of the object.

If SetParameterInfo has not been called for any parameters or SetParameterInfo has been called with cParams equal to zero, GetParameterInfo returns information about the parameters only if the provider can derive parameter information. If the provider cannot derive parameter information, GetParameterInfo returns DB_E_PARAMUNAVAILABLE.

If SetParameterInfo has been called for at least one parameter, GetParameterInfo returns the parameter information only for those parameters for which SetParameterInfo has been called. It does this even if the provider can derive information about the parameters for which SetParameterInfo was not called. The provider does not return a warning in this case because it often cannot determine the number of parameters and therefore cannot determine whether it has returned information for all parameters.

See Also

ICommandWithParameters::MapParameterNames, ICommandWithParameters::SetParameterInfo


ICommandWithParameters:: MapParameterNames

Returns an array of parameter ordinals when given named parameters.

HRESULT MapParameterNames (
   ULONG               cParamNames,
   const OLECHAR *      rgParamNames[],
   LONG                  rgParamOrdinals[]);

Parameters

cParamNames

[in]
The number of parameter names to map. If cParamNames is zero, MapParameterNames does nothing and returns S_OK.

rgParamNames

[in]
An array of parameter names of which to determine the parameter ordinals. If a parameter name is not found, the corresponding element of rgParamOrdinals is set to zero and the method returns DB_S_ERRORSOCCURRED.

rgParamOrdinals

[out]
An array of cParamNames ordinals of the parameters identified by the elements of rgParamNames. The consumer allocates (but is not required to initialize) memory for this array and passes the address of this memory to the provider. The provider returns the parameter ordinals in the array.

Return Code

S_OK
The method succeeded. Each element of rgParamOrdinals is set to a nonzero value.

DB_S_ERRORSOCCURRED
An element of rgParamNames was invalid. The corresponding element of rgParamOrdinals is set to zero.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
cParamNames was not zero and rgParamNames or rgParamOrdinals was a null pointer.

DB_E_ERRORSOCCURRED
All elements of rgParamNames were invalid. All elements of rgParamOrdinals are set to zero.

DB_E_NOCOMMAND
No command text was currently set on the Command object and no parameter information had been specified with SetParameterInfo.

DB_E_NOTPREPARED
The provider can derive parameter information and supports command preparation. However, the command was in an unprepared state and no parameter information had been specified with SetParameterInfo.

See Also

ICommandWithParameters::GetParameterInfo


ICommandWithParameters:: SetParameterInfo

Specifies the native data type of each parameter.

HRESULT SetParameterInfo (
   ULONG                        cParams,
   const ULONG                  rgParamOrdinals[],
   const DBPARAMBINDINFO   rgParamBindInfo[]);

Parameters

cParams

[in]
The number of parameters for which to set type information. If cParams is zero, the type information for all parameters is discarded, and rgParamOrdinals and rgParamBindInfo are ignored.

rgParamOrdinals

[in]
An array of cParams ordinals. These are the ordinals of the parameters for which to set type information. Type information for parameters whose ordinals are not specified is not affected.

rgParamBindInfo

[in]
An array of cParams DBPARAMBINDINFO structures. If rgParamBindInfo is a null pointer, then the type information for the parameters specified by the ordinals in rgParamOrdinals is discarded.

The DBPARAMBINDINFO structure is:

typedef struct tagDBPARAMBINDINFO {
 LPOLESTR   pwszDataSourceType;
 LPOLESTR   pwszName;
 ULONG    ulParamSize;
 DBPARAMFLAGS dwFlags;
 BYTE     bPrecision;
 BYTE     bScale;
} DBPARAMBINDINFO;

The elements of this structure are used as follows.

Element | Description
pwszDataSourceType A pointer to the provider-specific name of the parameter’s data type or a standard data type name. This name is not returned by GetParameterInfo; instead, the provider maps the data type specified by this name to an OLE DB type indicator and returns that type indicator. For a list of standard data type names, see ""Comments.""

If pwszDataSourceType is null, the provider attempts a default conversion from the data type specified in the binding for the parameter, or returns E_INVALIDARG if it cannot perform default parameter conversions.
pwszName The name of the parameter. This is a null pointer if the parameter does not have a name.

The consumer must specify a name for all or none of the parameters set at any time. If the provider does not support named parameters, this argument is ignored and the provider is not required to verify that all or none of the parameters are named.
ulParamSize The maximum possible length of a value in the parameter. For parameters that use a fixed-length data type, this is the size of the data type. For parameters that use a variable-length data type, this is one of the following:
  • The maximum length of the parameters in characters, for DBTYPE_STR and DBTYPE_WSTR, or bytes, for DBTYPE_BYTES, if one is defined. For example, a parameter for a CHAR(5) column in an SQL table has a maximum length of 5.

  • The maximum length of the data type in characters, for DBTYPE_STR and DBTYPE_WSTR, or bytes, for DBTYPE_BYTES, if the parameter does not have a defined length.

  • ~0 (bitwise, the value is not 0; that is, all bits are set to 1) if neither the parameter nor the data type has a defined maximum length.

For data types that do not have a length, this is set to ~0 (bitwise, the value is not 0; that is, all bits are set to 1).

This argument is ignored if pwszDataSourceType is null.
dwFlags | See the dwFlags element of the DBPARAMINFO structure in GetParameterInfo.
bPrecision If pwszDataSourceType is a numeric type, bPrecision is the maximum number of digits, expressed in base 10. Otherwise, it is ignored.

This argument is ignored if pwszDataSourceType is null.
bScale If pwszDataSourceType is a numeric type with a fixed scale, bScale is the number of digits to the right (if bScale is positive) or left (if bScale is negative) of the decimal point. Otherwise, it is ignored.

This argument is ignored if pwszDataSourceType is null.

Return Code

S_OK
The method succeeded.

DB_S_TYPEINFOOVERRIDDEN
The provider was capable of deriving the parameter type information and SetParameterInfo was called. The parameter type information specified in SetParameterInfo was used.

SetParameterInfo replaced parameter type information specified in a previous call to SetParameterInfo.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
cParams was not zero and rgParamOrdinals was a null pointer.

An element of rgParamOrdinals was zero.

In an element of rgParamBindInfo, the pwszDataSourceType element was a null pointer, and the provider does not support default parameter conversions.

In an element of rgParamBindInfo, the dwFlags element was invalid.

DB_E_BADPARAMETERNAME
In an element of rgParamBindInfo, the pwszName element specified an invalid parameter name. The provider does not check whether the name was correct for the specified parameter, just whether it was a valid parameter name.

In one or more elements of rgParamBindInfo, but not all, pwszName was null.

In one or more elements of rgParamBindInfo, pwszName was null and one or more parameters previously set and not overridden were specified with a non-null pwszName.

In one or more elements of rgParamBindInfo, pwszName was non-null and one or more parameters previously set and not overridden were specified with a null pwszName.

DB_E_BADTYPENAME
In an element of rgParamBindInfo, the pwszDataSourceType element specified an invalid data type name. The provider does not check whether the data type was correct for the specified parameter, just whether it was a data type name supported by the provider.

DB_E_OBJECTOPEN
A rowset was open on the command.

Comments

Providers generally derive parameter type information from the data source and return it to the consumer through GetParameterInfo. Consumers then use this information to build parameter accessors for use with ICommand::Execute.

Some providers—notably many SQL database providers—cannot derive parameter type information from the data source. For these providers, the consumer must supply the native parameter type information through SetParameterInfo. The provider uses the type information specified by SetParameterInfo to determine how to convert parameter data from the type supplied by the consumer (as indicated by the wType value in the binding structure) to the native type used by the data source. When the consumer specifies a data type with known precision, scale, and size values, any information supplied by the consumer for precision, scale, or size should be ignored by the provider.

The information that the consumer supplies must be correct and must be supplied for all parameters. Providers that cannot derive parameter type information cannot verify the supplied information against the parameter metadata, although they can determine that the specified values are legal values for the provider. Such providers sometimes cannot even determine the number of parameters in the command. The result of executing a command using incorrect parameter information or passing parameter information for the wrong number of parameters is undefined. For example, if the parameter type is LONG and the consumer specifies a type indicator of DBTYPE_STR in SetParameterInfo, the provider converts the data to a string before sending it to the data source. Because the data source is expecting a LONG, this will likely result in an error.

When the consumer calls SetParameterInfo, it specifies a provider-specific data type name (as derived from the PROVIDER_TYPES schema rowset) or a standard type name. If the consumer passes a standard type name, the provider maps it to a provider-specific type name. For example, a provider for an SQL DBMS might map ""DBTYPE_I2"" to ""SMALLINT"".

The following is a list of standard type names and their associated type indicators. This list contains many commonly known types. Individual providers may allow other, provider-specific names as well.

Standard type name | Type indicator
""DBTYPE_I1"" | DBTYPE_I1
"DBTYPE_I2" | DBTYPE_I2
"DBTYPE_I4" | DBTYPE_I4
"DBTYPE_I8" | DBTYPE_I8
"DBTYPE_UI1" | DBTYPE_UI1
"DBTYPE_UI2" | DBTYPE_UI2
"DBTYPE_UI4" | DBTYPE_UI4
"DBTYPE_UI8" | DBTYPE_UI8
"DBTYPE_R4" | DBTYPE_R4
"DBTYPE_R8" | DBTYPE_R8
"DBTYPE_CY" | DBTYPE_CY
"DBTYPE_DECIMAL" | DBTYPE_DECIMAL
"DBTYPE_NUMERIC" | DBTYPE_NUMERIC
"DBTYPE_BOOL" | DBTYPE_BOOL
"DBTYPE_ERROR" | DBTYPE_ERROR
"DBTYPE_UDT" | DBTYPE_UDT
"DBTYPE_VARIANT" | DBTYPE_VARIANT
"DBTYPE_IDISPATCH" | DBTYPE_IDISPATCH
"DBTYPE_IUNKNOWN" | DBTYPE_IUNKNOWN
"DBTYPE_GUID" | DBTYPE_GUID
"DBTYPE_DATE" | DBTYPE_DATE
"DBTYPE_DBDATE" | DBTYPE_DBDATE
"DBTYPE_DBTIME" | DBTYPE_DBTIME
"DBTYPE_DBTIMESTAMP" | DBTYPE_DBTIMESTAMP
"DBTYPE_BSTR" | DBTYPE_BSTR
"DBTYPE_CHAR" | DBTYPE_STR
"DBTYPE_VARCHAR" | DBTYPE_STR
"DBTYPE_LONGVARCHAR" | DBTYPE_STR
"DBTYPE_WCHAR" | DBTYPE_WSTR
"DBTYPE_WVARCHAR" | DBTYPE_WSTR
"DBTYPE_WLONGVARCHAR" | DBTYPE_WSTR
"DBTYPE_BINARY" | DBTYPE_BYTES
"DBTYPE_VARBINARY" | DBTYPE_BYTES
"DBTYPE_LONGVARBINARY" | DBTYPE_BYTES

After the consumer calls SetParameterInfo to specify the parameter type information, it can call GetParameterInfo to retrieve the type indicator for each parameter. These values—which are based on the information specified in SetParameterInfo—represent the best fit of OLE DB types to the native parameter types. The provider guarantees that, if the consumer uses these types in a parameter accessor, it will be able to convert the data from the OLE DB type to the native parameter type.

If the provider can derive parameter type information and the consumer calls SetParameterInfo, SetParameterInfo uses the specified type information and returns DB_S_TYPEINFOOVERRIDDEN. Because deriving type information can be an expensive operation, this may result in more efficient code.

See Also

ICommandWithParameters::GetParameterInfo


IConvertType

This interface is mandatory on commands, rowsets, and index rowsets.

This interface contains a single method that gives information about the availability of type conversions on a command or on a rowset.

Method | Description
CanConvert | Gives information about the availability of type conversions on a command or on a rowset.


IConvertType::CanConvert

Gives information about the availability of type conversions on a command or on a rowset.

HRESULT CanConvert (
   DBTYPE                  wFromType,
   DBTYPE                  wToType,
   DBCONVERTFLAGS   dwConvertFlags);

Parameters

wFromType

[in]
The source type of the conversion.

wToType

[in]
The target type of the conversion.

dwConvertFlags

[in]
Whether CanConvert is to determine if the conversion is supported on the rowset or on the command. These flags have the following meaning:

Value | Description
DBCONVERTFLAGS_ COLUMN | CanConvert is to determine if the conversion is supported when setting, getting, finding, filtering, or seeking on columns of the rowset. This flag is mutually exclusive with DBCONVERTFLAGS_PARAMETER.
DBCONVERTFLAGS_ISFIXEDLENGTH | CanConvert is to determine a fixed length value of the DBTYPE specified in the dwFromType can be converted to the DBTYPE specified in wToType. This flag can coexist with either DBCONVERTFLAGS_COLUMN or DBCONVERTFLAGS_PARAMETER. This flag is only supported by OLE DB version 2.0 or greater providers.
DBCONVERTFLAGS_ ISLONG | CanConvert is to determine if the long version of the DBTYPE specified in the dwFromType can be converted to the DBTYPE specified in wToType. This flag is only valid when used in conjunction with a variable-length DBTYPE. This flag can coexist with either DBCONVERTFLAGS_COLUMN or DBCONVERTFLAGS_PARAMETER. This flag is only supported by OLE DB version 2.0 or greater providers.
DBCONVERTFLAGS_ PARAMETER | CanConvert is to determine if the conversion is supported on the parameters of the command.
DBCONVERTFLAGS_ FROMVARIANT | The DBTYPE specified in the wFromType represents a type within a VARIANT. CanConvert returns whether the conversion from a VARIANT of that type to the type specified in wToType is allowed.

Return Code

S_OK

The requested conversion is available.

S_FALSE

The requested conversion is not available.

wFromType or wToType were not valid types for the provider, and the provider was a version 2.0 or greater provider.

E_FAIL

A provider-specific error occurred.

E_INVALIDARG

The provider was an OLE DB 1.x provider and wFromType or wToType was not a valid type indicator for OLE DB 1.x .

DB_E_BADCONVERTFLAG

dwConvert flags was invalid.

The method was called on a command and its flags inquired about a conversion on a rowset, but the property DBPROP_ROWSETCONVERSIONSONCOMMAND was VARIANT_FALSE.

The DBCONVERTFLAG_PARAMETER bit was set and CanConvert was called on a rowset.

The DBCONVERTFLAGS_PARAMETER bit was set and CanConvert was called on a command (whether from a provider or not) that does not support parameters.

DBCONVERTFLAGS_ISLONG was specified, and the DBTYPE specified in dwFromType was not a variable-length DBTYPE.

DBCONVERTFLAGS_ISLONG was specified, and the provider was an OLE DB 1.x provider.

DBCONVERTFLAGS_ISFIXEDLENGTH was specified, and the provider was an OLE DB 1.x provider.

DB_E_BADTYPE

DBCONVERTFLAGS_FROMVARIANT was specified and the type specified in wFromType was not a VARIANT type.

Comments

CanConvert can be called from a command or a rowset. The consumer can call it on a command to inquire about conversions supported by that command and on a rowset to inquire about conversions supported by that rowset. The consumer might be able to call it on a command to inquire about conversions supported on rowsets generated from that command; whether this is supported is specified by the DBPROP_ROWSETCONVERSIONSONCOMMAND property.

Note that converting a value to or from DBTYPE_IUNKNOWN is different than requesting a stream over the value, even though streams are bound using DBTYPE_IUNKNOWN. Providers typically don't support conversion of character or binary values to and from DBTYPE_IUNKOWN, even though they may support binding such objects as streams.

See Also

IAccessor::CreateAccessor


IDBAsynchNotify

IDBAsynchNotify is the callback interface that a consumer must support to get notified of the progress of asynchronous operations such as initializing a datasource, or opening or populating a rowset.

The notifications use the standard OLE connection point scheme for events. The object being created, initialized, or populated supports IConnectionPointContainer and the consumer calls FindConnectionPoint for IID_IDBAsynchNotify to obtain the correct IConnectionPoint interface. The consumer then advises that connection point to connect and supplies a pointer to the consumer's IDBAsynchNotify interface.

When to Implement

IDBAsynchNotify is implemented by consumers that require notification for the progress of asynchronous operations. If the consumer requests that an operation, such as opening or populating a rowset, or initializaing a datasource, be done asynchronously, the object being created or initialized must support a connection point for IDBAsynchNotify.

Consumers must implement one object supporting IDBAsynchNotify per asynchronous rowset they consume.

Providers should implement this connection point if they expect to work directly with general-purpose consumers.

Method | Description
OnLowResource | Indicates that resources are low.
OnProgress | Indicates the current progress of the operation.
OnStop | Indicates that the asynchronous processing has stopped.


IDBAsynchNotify::OnLowResource

The provider calls this method when it detects low resources.

HRESULT OnLowResource(
   DWORD   dwReserved);

Parameters

dwReserved

[in]
Reserved for future use; providers should set this to zero.

Return Code

S_OK
The method succeeded.

E_FAIL
A consumer-specific error occurred.

E_NOTIMPL
The consumer is not interested in receiving this notification. The provider can optimize by making no further calls to this method for this listener. Notifications for other methods are unaffected.

Comments

The consumer should free any resource it no longer needs when receiving this notification.

See Also

IDBAsynchNotify::OnStop , IDBAsynchNotify::OnProgress


IDBAsynchNotify::OnProgress

OnProgress returns the progress of an asynchronous method call. This method makes no logical change to the state of the object.

HRESULT OnProgress (
   HCHAPTER   hChapter,
   ULONG      ulOperation,
   ULONG      ulProgress,
   ULONG      ulProgressMax,
   ULONG      ulAsynchPhase,
   LPOLESTR   pwszStatusText);

Parameters

hChapter
[in]
The chapter handle. If the object calling this method is not a rowset, or the operation does not apply to chapters, this is DB_NULL_HCHAPTER.

ulOperation
[in]
The operation about which status is being returned. One of the following:

DBASYNCHOP_OPEN
The information applies to the asynchronous opening or population of a rowset, or the asynchronous initialization of a data source.

ulProgress
[in]
Current progress of the asynchronous execution relative to the expected maximum indicated in the ulProgressMax parameter.

ulProgressMax

[in]
The expected maximum value of the ulProgress parameter for the duration of calls to IDBAsynchStatus::OnProgress for this phase. This value may change across calls to this method.

ulAsynchPhase
[in]
Additional information regarding the progress of the asynchronous operation. Valid values include:

DBASYNCHPHASE_CANCELED
A consumer has requested that the operation be canceled by calling IDBAsynchStatus::Abort. The listener can attempt to deny the cancellation by returning S_FALSE from the notification. If no listeners deny the cancellation, the provider will call IDBAsynchNotify::OnStop for all registered listeners with an hrStatus of DB_E_CANCELED.

DBASYNCHPHASE_INITIALIZATION
The object is in an initialization phase. The arguments ulProgress and ulProgressMax indicate an estimated ratio of completion. The object is not yet fully materialized. Attempting to call any other interfaces may fail, and the full set of interfaces may not be available on the object.

If the asynchronous operation is a result of calling ICommand::Execute for a command that updates, deletes, or inserts rows, and cParamSets is greater than 1, ulProgress and ulProgressMax may indicate the progress for a single set of parameters or for the full array of parameter sets.

DBASYNCHPHASE_POPULATION
The object is in a population phase. Although the rowset is fully initializedand the full range of interfaces are available on the object, there may be additional rows not yet populated into the rowset.

While ulProgress and ulProgressMax can be based on the number of rows populated, they are generally based on the time or effort required to populate the rowset. A caller would therefore be better using this information as a rough guide to how long the process might take, not the eventual row count.

This phase is returned only during population of a rowset. It is never returned in the initialization of a data source or by the execution of a command that updates, deletes, or inserts rows.

DBASYNCHPHASE_COMPLETE
All asynchronous processing of the object has completed. If the asynchronous operation was a result of calling ICommand::Execute for a command that updates, deletes, or inserts rows, ulProgress and ulProgressMax are equal to the total number of rows affected by the command. If cParamSets is greater than 1, this is the total number of rows affected by all of the sets of parameters specified in the execution.

pwszStatusText

[in]
Textual information indicating additional information about the asynchronous operation, or NULL if no status text is available.

The provider owns the memory for pwszStatusText. The consumer can copy the text to its own memory, but must not attempt to free pwszStatusText, nor to reference it once it has returned from IDBAsynchNotify::OnProgress.

Return Code

S_OK
The method succeeded.

S_FALSE
The ulAsynchPhase was DBASYNCHPHASE_CANCELED, and the cancellation of the asynchronous operation was vetoed by the consumer.

The listener wants to cancel the asynchronous operation. The provider calls each listener's IDBAsynchNotify::OnProgress with a ulAsynchPhase of DBASYNCHPHASE_CANCELED. If none of the listeners deny the cancellation by returning S_FALSE to that notification, the asynchronous operation is canceled and IDBAsynchNotify::OnStop is called for each listener with an hrStatus of DB_E_CANCELED.

DB_S_UNWANTEDPHASE
The consumer is not interested in receiving this phase. The provider can optimize by making no further calls with this phase.

DB_S_UNWANTEDOPERATION
The consumer is not interested in receiving notifications for this operation. The provider can optimize by making no further calls to this method with this operation for this listener. Notifications for other operations are unaffected.

E_NOTIMPL
The consumer is not interested in receiving this notification. The provider can optimize by making no further calls to this method for this listener. Notifications for other methods are unaffected.

E_FAIL
A consumer-specific error occurred.

Comments

The provider calls this method repeatedly to indicate the current progress of the asynchronous operation, typically at reasonable intervals during a lengthy operation.

The consumer can use the progress notification to provide progress information to the end user from the ulProgress, ulProgressMax, and pwszStatusText parameters, or to make programmatic decisions based on the ulAsynchPhase parameter.

Consumers should be prepared to handle unknown values of ulOperation by returning S_OK or DB_S_UNWANTEDOPERATION, and unknown values of ulAsynchPhase by returning S_OK or DB_S_UNWANTEDPHASE.

See Also

ICommand::Execute, IDBAsynchStatus::GetStatus, IDBSchemaRowset::GetRowset, IOpenRowset::OpenRowset, ISourcesRowset::GetSourcesRowset


IDBAsynchNotify::OnStop

OnStop is called by the provider to indicate that asynchronous processing has stopped.

HRESULT OnStop (
   HCHAPTER   hChapter,
   ULONG      ulOperation,
   HRESULT      hrStatus,
   LPOLESTR   pwszStatusText);

Parameters

hChapter
[in]

The chapter for which the operation has stopped. If the object calling this method is not a rowset, or the operation does not apply to chapters, this is DB_NULL_HCHAPTER.

ulOperation
[in]
The operation that has stopped. One of the following:

DBASYNCHOP_OPEN
The completion information applies to the asynchronous opening or population of a rowset, or the asynchronous initialization of a data source.

hrStatus

[in]
Status code indicating the completion result of the asynchronous operation. This may be one of the following status codes, or some result specific to the asynchronous operation being executed:

S_OK
The asynchronous operation completed successfully.

DB_E_CANCELED
The asynchronous operation was canceled.

E_OUTOFMEMORY
The provider ran out of memory attempting to execute the asynchronous operation.

E_FAIL
The asynchronous operation failed for a provider-specific reason.

pwszStatusText

[in]
Additional information about the error, or the resource associated with the error. If no additional information is available, this string is empty.

The provider owns the memory for pwszStatusText. The consumer can copy the text to its own memory, but must not attempt to free pwszStatusText, nor to reference it once it has returned from IDBAsynchNotify::OnStop.

Return Code

S_OK
The method succeeded.

DB_S_UNWANTEDOPERATION
The consumer is not interested in receiving notifications for this operation. The provider can optimize by making no further calls to this method with this operation for this listener. Notifications for other operations are unaffected.

E_NOTIMPL
The consumer is not interested in receiving this notification. The provider can optimize by making no further calls to this method for this listener. Notifications for other methods are unaffected.

E_FAIL
A consumer-specific error occurred.

Comments

This method is called once per operation, when all asynchronous phases for that operation have completed. OnStop is always called, whether the asynchronous operation succeeded, failed, or was aborted by the consumer.

For operations that complete successfully, OnProgress is always called prior to OnStop with ulProgress equal to ulProgressMax. The only exception is if the listener has explicitly requested not to receive further OnProgress notifications by returning E_NOTIMPL or DB_S_UNWANTEDOPERATION, and the provider has optimized by not making additional calls to OnProgress.

Consumers should be prepared to handle new values of ulOperation by returning S_OK or DB_S_UNWANTEDOPERATION.

See Also

ICommand::Execute, IDBAsynchStatus::Abort, IDBAsynchStatus::GetStatus, IDBInitialize::Initialize, IDBSchemaRowset::GetRowset, IOpenRowset::OpenRowset, ISourcesRowset::GetSourcesRowset


IDBAsynchStatus

Consumers requiring asynchronous data source initialization or asynchronous rowset generation or population can poll for status or cancel the asynchronous operation by requesting IDBAsynchStatus on the object being initialized, generated, or populated.

When to Implement

Providers should implement IDBAsynchStatus if they expect to work directly with general-purpose consumers.

Method | Description
Abort | Cancels an asynchronously executing operation.
GetStatus | Returns the status of an asynchronously executing operation.


IDBAsynchStatus::Abort

Cancels an asynchronously executing operation.

HRESULT Abort(
   HCHAPTER      hChapter,
   ULONG         ulOperation);

Parameters

hChapter [in]

The handle of the chapter for which to abort the operation. If the object being called is not a rowset object, or the operation does not apply to a chapter, this should be set to DB_NULL_HCHAPTER, which is ignored by the provider.

ulOperation [in]

The operation to abort. This should be the following value:

DBASYNCHOP_OPEN
The request to cancel applies to the asynchronous opening or population of a rowset, or the asynchronous initialization of a data source.

Return Code

S_OK
The request to cancel the asynchronous operation was processed. This does not guarantee that the operation itself was canceled. To determine whether the operation was canceled, the consumer should call IDBAsynchStatus::GetStatus and check for DB_E_CANCELED; however, it may not be returned in the very next call.

DB_E_CANTCANCEL
The asynchronous operation cannot be canceled.

DB_E_CANCELED
The request to abort the asynchronous operation was canceled during notifications. The operation is still being executed asynchronously.

E_FAIL
A provider-specific error occurred.

E_UNEXPECTED
IDBAsynchStatus::Abort was called on a data source on which Initialize has not been called.

IDBAsynchStatus::Abort was called on a data source on which Initialize was called but subsequently canceled prior to initialization. The data source is still uninitialized.

IDBAsynchStatus::Abort was called on a rowset on which ITransaction::Commit or ITransaction::Abort was previously called, and the rowset did not survive the commit or abort and is in a zombie state.

IDBAsynchStatus::Abort was called on a rowset that was asynchronously canceled in its initialization phase. The rowset is in a zombie state.

Comments

After receiving a request to cancel, if the provider supports cancellation of the operation it calls IDBAsynchNotify::OnProgress with a ulAsynchPhase of DBASYNCHPHASE_CANCELED for all listeners. If no listeners attempt to deny the cancellation by returning S_FALSE, the operation is canceled and the provider calls IDBAsynchNotify::OnStop for all listeners with an hrStatus of DB_E_CANCELED. If any listeners deny the cancellation of the operation by returning S_FALSE from the OnProgress notification, the provider returns DB_E_CANCELED from the IDBAsynchStatus::Abort function and continues processing the operation.

See Also

IDBAsynchNotify::OnStop


IDBAsynchStatus::GetStatus

Returns the status of an asynchronously executing operation.

HRESULT GetStatus (
   HCHAPTER         hChapter,
   ULONG            ulOperation,
   ULONG*            pulProgress,
   ULONG*            pulProgressMax,
   ULONG*            pulAsynchPhase,
   LPOLESTR *      ppwszStatusText);

Parameters

hChapter
[in]

The chapter handle. If the object being polled is not a rowset object, or the operation does not apply to a chapter, this should be set to DB_NULL_HCHAPTER, which is ignored by the provider.

ulOperation
[in]

The operation for which the asynchronous status is being requested. This should be the following value:

DBASYNCHOP_OPEN
The consumer requests information about the asynchronous opening or population of a rowset, or the asynchronous initialization of a data source.

pulProgress
[out]
A pointer to memory in which to return the current progress of the asynchronous operation relative to the expected maximum indicated in the pulProgressMax parameter. For more information about the meaning of pulProgress, see the description of pulAsynchPhase.

If pulProgress is a null pointer, no progress is returned.

pulProgressMax

[out]
A pointer to memory in which to return the expected maximum value of the pulProgress parameter. This value may change across calls to this method. For more information about the meaning of pulProgressMax, see the description of pulAsynchPhase.

If pulProgressMax is a null pointer, no expected maximum value is returned.

pulAsynchPhase
[out]
A pointer to memory in which to return additional information regarding the progress of the asynchronous operation. Valid values include:

DBASYNCHPHASE_INITIALIZATION
The object is in an initialization phase. The arguments pulProgress and pulProgressMax indicate an estimated ratio of completion. The object is not yet fully materialized. Attempting to call any other interfaces may fail, and the full set of interfaces may not be available on the object.

If the asynchronous operation was a result of calling ICommand::Execute for a command that updates, deletes, or inserts rows, and cParamSets is greater than 1, pulProgress and pulProgressMax may indicate the progress for a single set of parameters or for the full array of parameter sets.

DBASYNCHPHASE_POPULATION
The object is in a population phase. Although the rowset is fully initialized, and the full range of interfaces are available on the object, there may be additional rows not yet populated into the rowset.

While pulProgress and pulProgressMax can be based on the number of rows populated, they are generally based on the time or effort required to populate the rowset. A caller should, therefore, use this information as a rough estimate of how long the process might take, not the eventual row count.

This phase is returned only during population of a rowset; it is never returned in the initialization of a data source or by the execution of a command that updates, deletes, or inserts rows.

DBASYNCHPHASE_COMPLETE
All asynchronous processing of the object has completed. IDBAsynchStatus::GetStatus returns an HRESULT indicating the outcome of the operation. Typically, this will be the HRESULT that would have been returned had the operation been called synchronously.

If the asynchronous operation was a result of calling ICommand::Execute for a command that updates, deletes, or inserts rows, pulProgress and pulProgressMax are equal to the total number of rows affected by the command. If cParamSets is greater than 1, this is the total number of rows affected by all of the sets of parameters specified in the execution.

If pulAsynchPhase is a null pointer, no status code is returned.

ppwszStatusText

[in/out]
A pointer to memory containing additional information about the operation. A provider may use this value to distinguish between different elements of an operation; for instance, different resources being accessed. This string is localized according to the DBPROP_INIT_LCID property on the data source object.

If *ppwszStatusText is non-null on input, the provider returns status associated with the particular element identified by *ppwszStatusText. If *ppwszStatusText does not indicate an element of ulOperation, the provider returns S_OK with pulProgress and pulProgressMax set to the same value. If the provider does not distinguish between elements based on a textual identifier, it sets *ppwszStatusText to NULL and returns information about the operation as a whole; otherwise, if *ppwszStatusText is non-null on input, the provider leaves *ppwszStatusText untouched.

If *ppwszStatusText is null on input, the provider sets *ppwszStatusText to a value indicating more information about the operation, or NULL if no such information is available or if GetStatus returns an error. When *ppwszStatusText is null on input, the provider allocates memory for the status string and returns the address to this memory. The consumer releases this memory with IMalloc::Free when it no longer needs the string.

If ppwszStatusString is NULL on input, no status string is returned and the provider returns information about any element of the operation, or the operation in general.

Return Code

S_OK
The method returned successfully.

  • If pulAsynchPhase is equal to DBASYNCHPHASE_INITIALIZATION, the object is not yet fully initialized; attempting to call any other interfaces may fail, and the full set of interfaces may not be available on the object.

  • If pulAsynchPhase is equal to DBASYNCHPHASE_POPULATION, the rowset is fully initialized, and the full range of interfaces are available on the object; however, there may be additional rows not yet populated into the rowset.

  • If pulAsynchPhase is equal to DBASYNCHPHASE_COMPLETE, all asynchronous processing of the object has completed. The object fully initialized and populated.

DB_E_CANCELED

  • If the method is being called on a rowset during the population phase, population stops but the rowset remains valid for the rows already populated.

  • If the method is being called on a data source in the initialization phase, that data source is in an uninitialized state.

E_UNEXPECTED
IDBAsynchStatus::GetStatus was called on a data source and Initialize has not been called on the data source.

IDBAsynchStatus::GetStatus was called on a rowset, ITransaction::Commit or ITransaction::Abort was called, and the object is in a zombie state.

IDBAsynchStatus::GetStatus was called on a rowset that was asynchronously canceled in its initialization phase. The rowset is in a zombie state.

E_FAIL
A provider-specific error occurred.

Comments

If the rowset is initialized or populated asynchronously, it must support this method.

In addition to the return values listed, IDBAsynchStatus::GetStatus can return any HRESULT that would have been returned by the method that initiated the asynchronous operation, indicating the success or failure of the operation.

Some asynchronous operations might not be able to return any states other than "finished" and "not finished". They should set pulProgressMax to a value of 1, indicating the all-or-nothing granularity of their estimate, so their answers would be either 0/1 or 1/1.

A provider may change pulProgressMax in successive calls, and even return a smaller ratio than previously, if this reflects an improving estimate of the degree of completion of the task.

The provider is not obliged to guarantee any further accuracy, but encouraged to do so in cases where reasonable estimates of completion are possible. Such efforts will improve user-interface quality, because the main use of this function is likely to be to give progress feedback to the user. User satisfaction increases with the quality of feedback on an invisible, long-running task.

Calling GetStatus on an initialized data source or a populated rowset, or passing a value for ulOperation other than DBASYNCHOP_OPEN, returns S_OK with pulProgress and pulProgressMax set to the same value. If GetStatus is called on an object created from the execution of a command that updates, deletes, or inserts rows, both pulProgress and pulProgressMax indicate the total number of rows affected by the command.

See Also

IDBAsynchNotify::OnProgress


IDBCreateCommand

Consumers call IDBCreateCommand::CreateCommand on a session to obtain a new command.

Method | Description
CreateCommand | Creates a new command.


IDBCreateCommand::CreateCommand

Creates a new command.

HRESULT CreateCommand(
   IUnknown *   pUnkOuter,
   REFIID         riid,
   IUnknown **   ppCommand);

Parameters

pUnkOuter

[in]
A pointer to the controlling IUnknown interface if the new command is being created as part of an aggregate. It is a null pointer if the command is not part of an aggregate.

riid

[in]
The IID of the interface requested on the command.

ppCommand

[out]
A pointer to memory in which to return the interface pointer on the newly created command.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
ppCommand was a null pointer.

E_NOINTERFACE
The command did not support the interface specified in riid.

E_OUTOFMEMORY
The provider did not have enough memory to create the command.

DB_E_NOAGGREGATION
pUnkOuter was not a null pointer and the command being created does not support aggregation.

pUnkOuter was non-null and riid was not IID_Unknown.

DB_E_OBJECTOPEN
The provider would have to open a new connection to support the operation and DBPROP_MULTIPLECONNECTIONS is set to VARIANT_FALSE.

Comments

If the session is transacted, the command and any actions performed as a result of executing that command are within the scope of the transaction.

See Also

ICommand


IDBCreateSession

Consumers call IDBCreateSession::CreateSession on a data source object to obtain a new session.

Method | Description
CreateSession | Creates a new session from the data source object and returns the requested interface on the newly created session.


IDBCreateSession::CreateSession

Creates a new session from the data source object and returns the requested interface on the newly created session.

HRESULT CreateSession (
   IUnknown *   pUnkOuter,
   REFIID         riid,
   IUnknown **   ppDBSession);

Parameters

pUnkOuter

[in]
A pointer to the controlling IUnknown interface if the new session is being created as part of an aggregate. It is a null pointer if the session is not part of an aggregate.

riid

[in]
The IID of the interface.

ppDBSession

[out]
A pointer to memory in which to return the interface pointer.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
ppDBSession was a null pointer.

E_NOINTERFACE
The session did not support the interface specified in riid.

E_OUTOFMEMORY
The provider did not have enough memory to create the session.

E_UNEXPECTED
The data source object was in an uninitialized state.

DB_E_NOAGGREGATION
pUnkOuter was not a null pointer and the session being created does not support aggregation.

pUnkOuter was non-null and riid was not IID_Unknown.

DB_E_OBJECTCREATIONLIMITREACHED
The maximum number of sessions supported by the provider has already been created. The consumer must release one or more currently held sessions before obtaining a new session object. This error is only returned by providers that have a fixed maximum number of sessions as returned by DBPROP_ACTIVESESSIONS. It is not returned due to other resource constraints, such as available memory (for which the provider returns E_OUTOFMEMORY).

DB_E_OBJECTOPEN
The provider would have to open a new connection to support the operation and DBPROP_MULTIPLECONNECTIONS is set to VARIANT_FALSE.

See Also

IDBProperties::GetPropertyInfo, ISessionProperties::SetProperties


IDBDataSourceAdmin

IDBDataSourceAdmin is an optional interface for creating, destroying, and modifying data sources.

It is important to distinguish between the data source object and the data source. The data source object is the object used by the consumer code. The data source is the actual source of data, such as a server database.

Method | Description
CreateDataSource | Creates a new data source and data source object, and initializes the data source object to the data source.
DestroyDataSource | Destroys the current data source and leaves the data source object in an uninitialized state.
GetCreationProperties | Returns information about the data source creation properties supported by the provider.
ModifyDataSource | Modifies the current data source.


IDBDataSourceAdmin::CreateDataSource

Creates a new data source and data source object, and initializes the data source object to the data source.

HRESULT CreateDataSource(
   ULONG         cPropertySets,
   DBPROPSET   rgPropertySets[],
   IUnknown*      pUnkOuter,
   REFIID            riid,
   IUnknown**      ppSession);

Parameters

cPropertySets

[in]
The number of DBPROPSET structures in rgPropertySets. If this is zero, the provider ignores rgPropertySets and the method does not do anything.

rgPropertySets

[in/out]
An array of DBPROPSET structures containing properties and values to be set. The properties specified in these structures must belong to the Data Source Creation or Initialization property groups; Initialization properties must be supported by the provider for use in data source creation. If ppSession is not a null pointer, the properties can also belong to the Session property group. If the same property is specified more than once in rgPropertySets, then which value is used is provider-specific. If a provider cannot support a property, then the property is ignored. If cPropertySets is zero, this argument is ignored.

For information about the properties in the Data Source Creation, Initialization, and Session property groups that are defined by OLE DB, see "Data Source Creation Properties," "Initialization Properties," and "Session Properties" in Appendix C. For information about the DBPROPSET and DBPROP structures, see "DBPROPSET Structure" and "DBPROP Structure" in Chapter 11.

pUnkOuter

[in]
A pointer to the controlling IUnknown interface if a session is desired and is to be created as part of an aggregate. Otherwise, it is a null pointer.

riid

[in]
The requested interface for the session returned in *ppSession. Ignored if ppSession is a null pointer.

ppSession

[in/out]
A pointer to memory in which to return the pointer to the session. If ppSession is a null pointer, no session is created.

Return Code

S_OK
The method succeeded. In all DBPROP structures passed to the method, dwStatus is set to DBPROPSTATUS_OK.

DB_S_ERRORSOCCURRED
The new data source was created but one or more properties—for which the dwOptions element of the DBPROP structure was DBPROPOPTIONS_OPTIONAL—were not set. The consumer checks dwStatus in the DBPROP structures to determine which properties were not set. The method can fail to set properties for a number of reasons, including:

  • The property was not in the Data Source Creation, Initialization, or Session property group.

  • The property was in the Initialization property group but was not supported for use in data source creation.

  • The property set was not supported by the provider.

  • It was not possible to set the property.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
cPropertySets was not zero and rgPropertySets was a null pointer.

In an element of rgPropertySets, cProperties was not zero and rgProperties was a null pointer.

E_NOINTERFACE
The session did not support the interface specified in riid.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory to create the new data source.

DB_E_ALREADYINITIALIZED
Initialize had already been called for the data source object and an intervening call to Uninitialize had not been made.

DB_E_DUPLICATEDATASOURCE
A data source with the same name already exists.

DB_E_ERRORSOCCURRED
The data source was not created because one or more properties—for which the dwOptions element of the DBPROP structure was DBPROPOPTIONS_REQUIRED or an invalid value—were not set. The consumer checks dwStatus in the DBPROP structures to determine which properties were not set. None of the satisfiable properties are remembered. The method can fail to set properties for a number of reasons, including:

  • The property was not in the Data Source Creation, Initialization, or Session property group.

  • The property was in the Initialization property group but was not supported for use in data source creation.

  • The property set was not supported by the provider.

DB_E_NOAGGREGATION
pUnkOuter was not a null pointer and the data source being created does not support aggregation.

pUnkOuter was non-null and riid was not IID_Unknown.

DB_SEC_E_AUTH_FAILED
The provider required initialization but an authentication failed. The data source was not created and the data source object remains in an uninitialized state.

DB_SEC_E_PERMISSIONDENIED
The consumer did not have permission to create a new data source.

Comments

CreateDataSource creates a data source and a data source object and initializes the data source object in one atomic operation.

The properties DBPROP_INIT_DATASOURCE and DBPROP_INIT_LOCATION are always required by CreateDataSource. If DBPROP_INIT_LOCATION is set to VT_EMPTY, the data source is created in the default location.

See Also

IDBDataSourceAdmin::DestroyDataSource, IDBDataSourceAdmin::ModifyDataSource


IDBDataSourceAdmin::DestroyDataSource

Destroys the current data source and leaves the data source object in an uninitialized state.

HRESULT DestroyDataSource ();

Parameters

None.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_UNEXPECTED
The data source object was in an uninitialized state.

DB_E_NOTSUPPORTED
The provider does not support this method.

DB_SEC_E_PERMISSIONDENIED
The consumer did not have permission to destroy the current data source.

Comments

DestroyDataSource destroys the current data source and leaves the data source object in an uninitialized state. It is an error to call DestroyDataSource when there are open sessions, commands, or rowsets on the data source object. The consumer must release all interface pointers on all sessions, commands, and rowsets on the data source object before calling DestroyDataSource.

See Also

IDBDataSourceAdmin::CreateDataSource


IDBDataSourceAdmin:: GetCreationProperties

Returns information about the data source creation properties supported by the data provider.

HRESULT GetCreationProperties (
   ULONG                  cPropertyIDSets,
   const DBPROPIDSET   rgPropertyIDSets[],
   ULONG *                  pcPropertyInfoSets,
   DBPROPINFOSET **   prgPropertyInfoSets,
   OLECHAR **            ppDescBuffer);

Parameters

cPropertyIDSets

[in]
The number of DBPROPIDSET structures in rgPropertyIDSets.

If cPropertySets is zero, the provider ignores rgPropertyIDSets and returns information about all of the properties in the Data Source Creation and Session property groups it supports and all of the properties in the Initialization property group it supports for use in creating data sources.

If cPropertyIDSets is not zero, the provider returns information about the requested properties. If a property is not supported, or if a property in the Initialization property group cannot be used to create data sources, the returned value of dwStatus in the returned DBPROPINFO structure for that property is DBPROPFLAGS_NOTSUPPORTED and the values of the pwszDescription, vtType, and vValues elements are undefined.

rgPropertyIDSets

[in]
An array of cPropertyIDSets DBPROPIDSET structures. The properties specified in these structures must belong to the Data Source Creation or Session property group or belong to the Initialization property group and be supported for use in creating data sources. The provider returns information about the properties specified in these structures. If cPropertyIDSets is zero, then this parameter is ignored.

For information about the properties in the Data Source Creation, Initialization, and Session property groups that are defined by OLE DB, see "Data Source Creation Properties," "Initialization Properties," and "Session Properties" in Appendix C. For information about the DBPROPIDSET structure, see "DBPROPIDSET Structure" in Chapter 11.

pcPropertyInfoSets

[out]
A pointer to memory in which to return the number of DBPROPINFOSET structures returned in *prgPropertyInfoSets. If cPropertyIDSets is zero, *pcPropertyInfoSets is the total number of property sets for which the provider supports at least one property in the Data Source Creation, Initialization, or Session property groups. If cPropertyIDSets is greater than zero, *pcPropertyInfoSets is set to cPropertyIDSets. If an error occurs, *pcPropertyInfoSets is set to zero.

prgPropertyInfoSets

[out]
A pointer to memory in which to return an array of DBPROPINFOSET structures. If cPropertyIDSets is zero, then one structure is returned for each property set that contains at least one property belonging to the Data Source Creation, Initialization, or Session property group. If cPropertyIDSets is not zero, then one structure is returned for each property set specified in rgPropertyIDSets.

If cPropertyIDSets is not zero, the DBPROPINFOSET structures in *prgPropertyInfoSets are returned in the same order as the DBPROPIDSET structures in rgPropertyIDSets; that is, for corresponding elements of each array, the guidPropertySet elements are the same. If cPropertyIDs, in an element of rgPropertyIDSets, is not zero, the DBPROPINFO structures in the corresponding element of *prgPropertyInfoSets are returned in the same order as the DBPROPID values in rgPropertyIDs; that is, for corresponding elements of each array, the property IDs are the same.

The provider allocates memory for the structures and returns the address to this memory; the consumer releases this memory with IMalloc::Free when it no longer needs the structures. Before calling IMalloc::Free for *prgPropertyInfoSets, the consumer should call IMalloc::Free for the rgPropertyInfos element in each element of *prgPropertyInfoSets. If *pcPropertyInfoSets is zero on output or an error occurs, then *prgPropertyInfoSets must be a null pointer on output.

For information about the DBPROPINFOSET and DBPROPINFO structures, see "DBPROPINFOSET Structure" and "DBPROPINFO Structure" in Chapter 11.

ppDescBuffer

[out]
A pointer to memory in which to return a pointer to storage for all string values returned in the *pwszDescription element of the DBPROPINFO structure. The provider allocates this memory with IMalloc and the consumer frees it with IMalloc::Free when it no longer needs the property descriptions. If ppDescBuffer is a null pointer on input, GetCreationProperties does not return the property descriptions. If *pcPropertyInfoSets is zero on output or an error occurs, the provider does not allocate any memory and ensures that *ppDescBuffer is a null pointer on output.

Return Code

S_OK
The method succeeded. In all DBPROPINFO structures returned by the method, dwFlags is set to a value other than DBPROPFLAGS_NOTSUPPORTED.

DB_S_ERRORSOCCURRED
One or more properties specified in rgPropertyIDSets were not supported by the provider. The dwFlags element of the DBPROPINFO structure for such properties is set to DBPROPFLAGS_NOTSUPPORTED.

One or more properties specified in rgPropertyIDSets were not in the Data Source Creation, Initialization, or Session property groups. The dwFlags element of the DBPROPINFO structure for such properties is set to DBPROPFLAGS_NOTSUPPORTED.

One or more properties specified in rgPropertyIDSets were in the Initialization property group but could not be used to create data sources. The dwFlags element of the DBPROPINFO structure for such properties is set to DBPROPFLAGS_NOTSUPPORTED.

One or more property sets specified in rgPropertyIDSets were not supported by the provider. The dwFlags element of the DBPROPINFO structure for all specified properties in these sets is set to DBPROPFLAGS_NOTSUPPORTED. If cPropertyIDs in the DBPROPIDSET structure for the property set was zero, the provider cannot set dwStatus in the DBPROP structure because it does not know the IDs of any properties in the property set. Instead, it sets cProperties to zero in the DBPROPSET structure returned for the property set.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
pcPropertyInfoSets or prgPropertyInfoSets was a null pointer.

cPropertyIDSets was not equal to zero and rgPropertyIDSets was a null pointer.

In an element of rgPropertyIDSets, cPropertyIDs was not zero and rgPropertyIDs was a null pointer.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to return the DBPROPINFOSET or DBPROPINFO structures or the property descriptions.

DB_E_ERRORSOCCURRED
Values were not returned for any properties. The provider allocates memory for *prgPropertySets and the consumer checks dwStatus in the DBPROP structures to determine why properties were not returned. The consumer frees this memory when it no longer needs the information.

Comments

GetCreationProperties returns properties in the Data Source Creation and Session property groups and properties in the Initialization property group that can be used in data source creation. It is possible that there are properties in the Initialization property group that cannot be used in data source creation. The properties for which values must be set to create a data source are indicated in the dwFlags element of the DBPROPINFO structure as DBPROPFLAGS_REQUIRED.

This method can be called before the data source object has been initialized.

See Also

IDBDataSourceAdmin::CreateDataSource


IDBDataSourceAdmin::ModifyDataSource

Modifies the current data source.

HRESULT ModifyDataSource (
   ULONG         cPropertySets,
   DBPROPSET   rgPropertySets[]);

Parameters

cPropertySets

[in]
The number of DBPROPSET structures in rgPropertySets. If this is zero, the provider ignores rgPropertySets and the method does not do anything.

rgPropertySets

[in/out]
An array of DBPROPSET structures containing properties and values to be set. The properties specified in these structures must belong to the Data Source Creation or Initialization property groups; Initialization properties must be supported by the provider for use in data source creation. If the same property is specified more than once in rgPropertySets, then which value is used is provider-specific. If a provider cannot support a property, then the property is ignored. If cPropertySets is zero, this argument is ignored.

For information about the properties in the Data Source Creation and Initialization property groups that are defined by OLE DB, see "Data Source Creation Properties" and "Initialization Properties" in Appendix C. For information about the DBPROPSET and DBPROP structures, see "DBPROPSET Structure" and "DBPROP Structure" in Chapter 11.

Return Code

S_OK
The method succeeded. In all DBPROP structures passed to the method, dwStatus is set to DBPROPSTATUS_OK.

DB_S_ERRORSOCCURRED
The data source was modified but one or more properties—for which the dwOptions element of the DBPROP structure was DBPROPOPTIONS_OPTIONAL—were not set. The consumer checks dwStatus in the DBPROP structures to determine which properties were not set. The method can fail to set properties for a number of reasons, including:

  • The property was not in the Data Source Creation or Initialization property group.

  • The property was in the Initialization property group but was not supported for use in data source creation.

  • The property set was not supported by the provider.

  • It was not possible to set the property.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
cPropertySets was not zero and rgPropertySets was a null pointer.

In an element of rgPropertySets, cProperties was not zero and rgProperties was a null pointer.

E_UNEXPECTED
The data source object was in an uninitialized state.

DB_E_ERRORSOCCURRED
The data source was not modified because one or more properties—for which the dwOptions element of the DBPROP structure was DBPROPOPTIONS_REQUIRED—were not set. The consumer checks dwStatus in the DBPROP structures to determine which properties were not set. None of the satisfiable properties are remembered. The method can fail to set properties for a number of reasons, including:

  • The property was not in the Data Source Creation or Initialization property group.

  • The property was in the Initialization property group but was not supported for use in data source creation.

  • The property set was not supported by the provider.

  • The value of dwOptions in a DBPROP structure was invalid.

DB_E_NOTSUPPORTED
The provider does not support this method.

DB_SEC_E_PERMISSIONDENIED
The consumer did not have permission to modify the current data source.

Comments

ModifyDataSource modifies the current data source and leaves the data source object in an initialized state. It is an error to call ModifyDataSource when there are open sessions, commands, or rowsets on the data source object; that is, the consumer must release all interface pointers on all sessions, commands, and rowsets on the data source object before calling ModifyDataSource.

See Also

IDBDataSourceAdmin::CreateDataSource, IDBDataSourceAdmin::DestroyDataSource


IDBInfo

IDBInfo returns information about the keywords and literals a provider supports. It is an optional interface on the data source objects.

Method | Description
GetKeywords | Returns a list of provider-specific keywords.
GetLiteralInfo | Returns information about literals used in text commands and in ITableDefinition and IIndexDefinition.


IDBInfo::GetKeywords

Returns a list of provider-specific keywords.

HRESULT GetKeywords(
   LPOLESTR *   ppwszKeywords);

Parameters

ppwszKeywords

[out]
A pointer to memory in which to return the address of a string. The string contains a comma-separated list of all keywords unique to this provider; that is, a comma-separated list of keywords that are not in the list in the Comments section. If there are no keywords unique to this provider, or if an error occurs, the provider sets *ppwszKeywords to a null pointer.

The provider allocates memory for the string and returns the address to this memory; the consumer releases this memory with IMalloc::Free when it no longer needs the string.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
ppwszKeywords was a null pointer.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to return the keywords.

E_UNEXPECTED
The data source object was in an uninitialized state.

Comments

The following table lists the keywords from OLE DB.

ABSOLUTE | INTO
ACTION | IS
ADD | ISOLATION
ALL | JOIN
ALLOCATE | KEY
ALTER | LANGUAGE
AND | LAST
ANY | LEADING
ARE | LEFT
AS | LEVEL
ASC | LIKE
ASSERTION | LOCAL
AT | LOWER
AUTHORIZATION | MATCH
AVG | MAX
BEGIN | MIN
BETWEEN | MINUTE
BIT | MODULE
BIT_LENGTH | MONTH
BOTH | NAMES
BY | NATIONAL
CASCADE | NATURAL
CASCADED | NCHAR
CASE | NEXT
CAST | NO
CATALOG | NOT
CHAR | NULL
CHAR_LENGTH | NULLIF
CHARACTER | NUMERIC
CHARACTER_LENGTH | OCTET_LENGTH
CHECK | OF
CLOSE | ON
COALESCE | ONLY
COLLATE | OPEN
COLLATION | OPTION
COLUMN | OR
COMMIT | ORDER
CONNECT | OUTER
CONNECTION | OUTPUT
CONSTRAINT | OVERLAPS
CONSTRAINTS | PARTIAL
CONTINUE | POSITION
CONVERT | PRECISION
CORRESPONDING | PREPARE
COUNT | PRESERVE
CREATE | PRIMARY
CROSS | PRIOR
CURRENT | PRIVILEGES
CURRENT_DATE | PROCEDURE
CURRENT_TIME | PUBLIC
CURRENT_TIMESTAMP | READ
CURRENT_USER | REAL
CURSOR | REFERENCES
DATE | RELATIVE
DAY | RESTRICT
DEALLOCATE | REVOKE
DEC | RIGHT
DECIMAL | ROLLBACK
DECLARE | ROWS
DEFAULT | SCHEMA
DEFERRABLE | SCROLL
DEFERRED | SECOND
DELETE | SECTION
DESC | SELECT
DESCRIBE | SESSION
DESCRIPTOR | SESSION_USER
DIAGNOSTICS | SET
DISCONNECT | SIZE
DISTINCT | SMALLINT
DISTINCTROW | SOME
DOMAIN | SQL
DOUBLE | SQLCODE
DROP | SQLERROR
ELSE | SQLSTATE
END | SUBSTRING
END-EXEC | SUM
ESCAPE | SYSTEM_USER
EXCEPT | TABLE
EXCEPTION | TEMPORARY
EXEC | THEN
EXECUTE | TIME
EXISTS | TIMESTAMP
EXTERNAL | TIMEZONE_HOUR
EXTRACT | TIMEZONE_MINUTE
FALSE | TO
FETCH | TRAILING
FIRST | TRANSACTION
FLOAT | TRANSLATE
FOR | TRANSLATION
FOREIGN | TRIGGER
FOUND | TRIM
FROM | TRUE
FULL | UNION
GET | UNIQUE
GLOBAL | UNKNOWN
GO | UPDATE
GOTO | UPPER
GRANT | USAGE
GROUP | USER
HAVING | USING
HOUR | VALUE
IDENTITY | VALUES
IMMEDIATE | VARCHAR
IN | VARYING
INDICATOR | VIEW
INITIALLY | WHEN
INNER | WHENEVER
INPUT | WHERE
INSENSITIVE | WITH
INSERT | WORK
INT | WRITE
INTEGER | YEAR
INTERSECT | ZONE
INTERVAL |

See Also

IDBInfo::GetLiteralInfo, IDBProperties::GetProperties


IDBInfo::GetLiteralInfo

Returns information about literals used in text commands, ITableDefinition, IIndexDefinition, and IOpenRowset, or any interface that takes DBIDs as arguments.

HRESULT GetLiteralInfo(
   ULONG                  cLiterals,
   const DBLITERAL         rgLiterals[],
   ULONG *                  pcLiteralInfo,
   DBLITERALINFO **   prgLiteralInfo,
   OLECHAR **            ppCharBuffer);

Parameters

cLiterals

[in]
The number of literals being asked about. If this is 0, the provider ignores rgLiterals and returns information about all of the literals it supports.

rgLiterals

[in]
An array of cLiterals literals about which to return information. If the consumer specifies an invalid DBLITERAL value in this array, GetLiteralInfo returns FALSE in fSupported in the corresponding element of the *prgLiteralInfo array.

If cLiterals is 0, this parameter is ignored.

pcLiteralInfo

[out]
A pointer to memory in which to return the number of literals for which information was returned. If cLiterals is 0, this is the total number of literals supported by the provider. If an error other than DB_E_ERRORSOCCURRED occurs, *pcLiteralInfo is set to 0.

prgLiteralInfo

[out]
A pointer to memory in which to return a pointer to an array of DBLITERALINFO structures. One structure is returned for each literal. The provider allocates memory for the structures and returns the address to this memory; the consumer releases this memory with IMalloc::Free when it no longer needs the structures. If *pcLiteralInfo is 0 on output or an error other than DB_E_ERRORSOCCURRED occurs, the provider does not allocate any memory and ensures that *prgLiteralInfo is a null pointer on output. For information about DBLITERALINFO structures, see the Comments section.

ppCharBuffer

[out]
A pointer to memory in which to return a pointer for all string values (pwszLiteralValue, pwszInvalidChars, and pwszInvalidStartingChars) within a single allocation block. The provider allocates this memory and the consumer releases it with IMalloc::Free when it no longer needs it. If *pcLiteralInfo is 0 on output or an error occurs, the provider does not allocate any memory and ensures that *ppCharBuffer is a null pointer on output. Each of the individual string values stored in this buffer is terminated by a null-termination character. Therefore, the buffer may contain one or more strings, each with its own null-termination character, and may contain embedded null-termination characters.

Return Code

S_OK
The method succeeded. In each structure returned in *prgLiteralInfo, the fSupported element is set to TRUE.

DB_S_ERRORSOCCURRED
rgLiterals contained at least one unsupported or invalid literal. In the structures returned in *prgLiteralInfo for unsupported or invalid literals, the fSupported element is set to FALSE.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
cLiterals was not equal to zero and rgLiterals was a null pointer.

pcLiteralInfo, prgLiteralInfo, or ppCharBuffer was a null pointer.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to return the DBLITERALINFO structures or the strings containing the valid and starting characters.

E_UNEXPECTED
The data source object was in an uninitialized state.

DB_E_ERRORSOCCURRED
All literals were either invalid or unsupported. The provider allocates memory for *prgLiteralInfo and sets the value of the fSupported element in all of the structures to FALSE. The consumer frees this memory when it no longer needs the information.

Comments

In the context of GetLiteralInfo, a literal is one of several things:

  • A special character or characters used by text commands, such as the character used to quote identifiers. GetLiteralInfo returns the character or characters.

  • A literal data value, such as a character literal in an SQL statement. For such literal data values, GetLiteralInfo returns the maximum length of the literal in characters, a list of the characters that cannot be used in the literal, and a list of the characters that cannot be used as the first character of the literal.

  • The name of a database object such as a column or table. For such names, GetLiteralInfo returns the maximum length of the name in characters, a list of the characters that cannot be used in the name, and a list of the characters that cannot be used as the first character of the name.

Information about literals is returned in the DBLITERALINFO structure:

typedef struct tagDBLITERALINFO {
 LPOLESTR  pwszLiteralValue;
 LPOLESTR  pwszInvalidChars;
 LPOLESTR  pwszInvalidStartingChars;
 DBLITERAL lt;
 BOOL    fSupported;
 ULONG   cchMaxLen;
} DBLITERALINFO;

The elements of this structure are used as follows.

Element | Description
pwszLiteralValue | A pointer to a string in the *ppCharBuffer buffer containing the actual literal value. For example, if lt is DBLITERAL_LIKE_PERCENT, and the percent character (%) is used to match zero or more characters in a LIKE clause, this would be "%". This is used for DBLITERAL_CATALOG_SEPARATOR, DBLITERAL_ESCAPE_PERCENT_PREFIX, DBLITERAL_ESCAPE_UNDERSCORE_PREFIX, DBLITERAL_LIKE_PERCENT, DBLITERAL_LIKE_UNDERSCORE, DBLITERAL_QUOTE_PREFIX, and DBLITERAL_SCHEMA_SEPARATOR. For all other DBLITERAL values, pwszLiteralValue is not used and is set to a null pointer.
pwszInvalidChars | A pointer to a string in the *ppCharBuffer buffer containing the characters that are not valid in the literal. For example, if table names can contain anything other than a numeric character, this would be "0123456789" when lt is DBLITERAL_TABLE_NAME. If the literal can contain any valid character, this is a null pointer. This is not used for DBLITERAL_BINARY_LITERAL, DBLITERAL_CATALOG_SEPARATOR, DBLITERAL_ESCAPE_PERCENT_PREFIX, DBLITERAL_ESCAPE_UNDERSCORE_PREFIX, DBLITERAL_LIKE_PERCENT, DBLITERAL_LIKE_UNDERSCORE, DBLITERAL_QUOTE_PREFIX, and DBLITERAL_SCHEMA_SEPARATOR; pwszInvalidChars is set to a null pointer for these DBLITERAL values.
pwszInvalidStartingChars | A pointer to a string in the *ppCharBuffer buffer containing the characters that are not valid as the first character of the literal. If the literal can start with any valid character, this is a null pointer. For example, if table names can begin with anything other than a numeric character, this would be "0123456789" when lt is DBLITERAL_TABLE_NAME. This is not used for DBLITERAL_BINARY_LITERAL, DBLITERAL_CATALOG_SEPARATOR, DBLITERAL_ESCAPE_PERCENT_PREFIX, DBLITERAL_ESCAPE_UNDERSCORE_PREFIX, DBLITERAL_LIKE_PERCENT, DBLITERAL_LIKE_UNDERSCORE, DBLITERAL_QUOTE_PREFIX, and DBLITERAL_SCHEMA_SEPARATOR; pwszInvalidStartingChars is set to a null pointer for these DBLITERAL values.
lt | The literal described in the structure. For more information, see the following section.
fSupported | TRUE if the provider supports the literal specified by lt. If cLiterals is 0, this is always TRUE, because GetLiteralInfo only returns information about literals it supports in this case. FALSE if the provider does not support the literal; or the value of the corresponding element of the rgLiterals array was not a valid value in the DBLITERAL enumerated type.
cchMaxLen | The maximum number of characters in the literal. If there is no maximum or the maximum is unknown, cchMaxLen is set to ~0 (bitwise, the value is not 0; that is, all bits are set to 1). For DBLITERAL_CATALOG_SEPARATOR, DBLITERAL_ESCAPE_PERCENT_PREFIX, DBLITERAL_ESCAPE_UNDERSCORE_PREFIX, DBLITERAL_LIKE_PERCENT, DBLITERAL_LIKE_UNDERSCORE, DBLITERAL_QUOTE_PREFIX, and DBLITERAL_SCHEMA_SEPARATOR, this is the actual number of characters in the literal.

The following values of DBLITERAL are supported.

Value | Description
DBLITERAL_INVALID | An invalid value.
DBLITERAL_ BINARY_LITERAL | A binary literal in a text command.
DBLITERAL_ CATALOG_NAME | A catalog name in a text command.
DBLITERAL_ CATALOG_SEPARATOR | The character that separates the catalog name from the rest of the identifier in a text command.
DBLITERAL_ CHAR_LITERAL | A character literal in a text command.
DBLITERAL_ COLUMN_ALIAS | A column alias in a text command.
DBLITERAL_ COLUMN_NAME | A column name used in a text command or in a data-definition interface.
DBLITERAL_ CORRELATION_NAME | A correlation name (table alias) in a text command.
DBLITERAL_ CURSOR_NAME | A cursor name in a text command.
DBLITERAL_ ESCAPE_PERCENT_PREFIX | The character used in a LIKE clause to escape the character returned for the DBLITERAL_LIKE_PERCENT literal. For example, if a percent sign (%) is used to match zero or more characters and this is a backslash (\), the characters "abc\%%" match all character values that start with "abc%".
 | Some SQL dialects support a clause (the ESCAPE clause) that can be used to override this value.
DBLITERAL_ESCAPE_PERCENT_SUFFIX | The escape character, if any, used to suffix the character returned for the DBLITERAL_LIKE_PERCENT literal. For example, if a percent sign (%) is used to match zero or more characters, and percent signs are escaped by enclosing in open and close square brackets, DBLITERAL_ESCAPE_PERCENT_PREFIX is "[", DBLITERAL_ESCAPE_PERCENT_SUFFIX is "]", and the characters "abc[%]%" match all character values that start with "abc%". Providers that do not use a suffix character to escape the DBLITERAL_ESCAPE_PERCENT character do not return this literal value, and set the lt member of the DBLITERAL structure to DBLITERAL_INVALID if requested.
DBLITERAL_ ESCAPE_UNDERSCORE_PREFIX | The character used in a LIKE clause to escape the character returned for the DBLITERAL_LIKE_UNDERSCORE literal. For example, if an underscore (_) is used to match exactly one character and this is a backslash (\), the characters "abc\_ _" match all character values that are five characters long and start with "abc_". Some SQL dialects support a clause (the ESCAPE clause) that can be used to override this value.
DBLITERAL_ESCAPE_UNDERSCORE_SUFFIX | The escape character, if any, used to suffix the character returned for the DBLITERAL_LIKE_UNDERSCORE literal. For example, if an underscore (_) is used to match exactly one character, and underscores are escaped by enclosing in open and close square brackets, DBLITERAL_ESCAPE_UNDERSCORE_PREFIX is "[", DBLITERAL_ESCAPE_UNDERSCORE_SUFFIX is "]", and the characters "abc[_]_" match all character values that are five characters long and start with "abc_". Providers that do not use a suffix character to escape the DBLITERAL_ESCAPE_UNDERSCORE character do not return this literal value, and set the lt member of the DBLITERAL structure to DBLITERAL_INVALID if requested.
DBLITERAL_ INDEX_NAME | An index name used in a text command or in a data-definition interface.
DBLITERAL_ LIKE_PERCENT | The character used in a LIKE clause to match zero or more characters. For example, if this is a percent sign (%), the characters "abc%" match all character values that start with "abc".
DBLITERAL_ LIKE_UNDERSCORE | The character used in a LIKE clause to match exactly one character. For example, if this is an underscore (_), the characters "abc_" match all character values that are four characters long and start with "abc".
DBLITERAL_ PROCEDURE_NAME | A procedure name in a text command.
DBLITERAL_ SCHEMA_NAME | A schema name in a text command.
DBLITERAL_ SCHEMA_SEPARATOR | The character that separates the schema name from the rest of the identifier in a text command.
DBLITERAL_ TABLE_NAME | A table name used in a text command or in a data-definition interface.
DBLITERAL_ TEXT_COMMAND | A text command, such as an SQL statement.
DBLITERAL_ USER_NAME | A user name in a text command.
DBLITERAL_ VIEW_NAME | A view name in a text command.
DBLITERAL_QUOTE_PREFIX | The character used in a text command as the opening quote for quoting identifiers that contain special characters.
DBLITERAL_QUOTE_SUFFIX | The character used in a text command as the closing quote for quoting identifiers that contain special characters. 1.x providers that use the same character as the prefix and suffix may not return this literal value, and set the lt member of the DBLITERAL structure to DBLITERAL_INVALID if requested.

Note If a provider returns non-NULL CATALOG or SCHEMA column values in schema rowsets, then it must support IDBInfo to describe how a fully qualified name is assembled. A provider that does not support IDBInfo must return NULL values for CATALOG and SCHEMA columns in all schema rowsets.

See Also

IDBInfo::GetKeywords, IDBProperties::GetProperties


IDBInitialize

IDBInitialize is used to initialize and uninitialize data source objects and enumerators. It is a mandatory interface on data source objects and an optional interface on enumerators.

Method | Description
Initialize | Initializes a data source object or enumerator.
Uninitialize | Returns the data source object or enumerator to an uninitialized state.


IDBInitialize::Initialize

Initializes a data source object or enumerator.

HRESULT Initialize();

Parameters

None.

Return Code

S_OK
The method succeeded.

DB_S_ASYNCHRONOUS
The method has initiated asynchronous initialization of the data source. The consumer can call IDBAsynchStatus::GetStatus to poll for status or can register for notifications of asynchronous processing. Until asynchronous processing completes, the data source remains in an uninitialized state.

DB_S_ERRORSOCCURRED
The data source object or enumerator was initialized but one or more properties—for which the dwOptions element of the DBPROP structure was DBPROPOPTIONS_OPTIONAL—were not set. In order to return properties in error, the provider uses DBPROPSET_PROPERTIESINERROR as described in Chapter 11, "Properties." The method can fail to set properties for a number of reasons, including:

  • It was not possible to set the property.

  • The value in vValue in the DBPROP structure was invalid.

  • The property's value conflicted with an existing property.

E_FAIL
A provider-specific error occurred.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in order to initialize the data source object or enumerator.

E_UNEXPECTED
The data source is in the process of being initialized asynchronously. To cancel asynchronous execution, call IDBAsynchStatus::Abort.

DB_E_ALREADYINITIALIZED
Initialize had already been called for the data source object or enumerator and an intervening call to Uninitialize had not been made.

DB_E_CANCELED
The provider prompted for additional information and the user selected Cancel.

DB_E_ERRORSOCCURRED
The data source object or enumerator was not initialized because one or more properties—for which the dwOptions element of the DBPROP structure was DBPROPOPTIONS_REQUIRED—were not set. The consumer checks dwStatus in the DBPROP structures to determine which properties were in error. The 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.

DB_SEC_E_AUTH_FAILED
Authentication of the consumer to the data source or enumerator failed. The data source object or enumerator remains in the uninitialized state.

Comments

Initialize initializes the data source object or enumerator. It uses the values of properties in the Initialization property group that have been set with IDBProperties::SetProperties. If the consumer has not set values for all required properties, Initialize can prompt for values.

If Initialize returns DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED, the consumer can immediately call IDBProperties::GetProperties with the DBPPROPSET_PROPERTIESINERROR property set to return the properties that could not be set. For more information, see "Property Sets" in Chapter 11.

For information about what the consumer can and cannot do with a data source object or enumerator before it is initialized, see "Data Source Object States" and "Enumerator States" in Chapter 2.

Initializing a Data Source Object Through a Network Connection

The following shows how to instantiate a data source object as an in-process object using CoCreateInstance.

#include <oledb.h>
extern CLSID CLSID_DSO;
int main() {
 HRESULT    hr;
 IDBInitialize *pIDBInitialize;

 // Create the data source object.
 hr = CoCreateInstance(CLSID_DSO, NULL, CLSCTX_INPROC_SERVER,
         IID_IDBInitialize, (void**) &pIDBInitialize);

 // Set the initialization properties.
 DBPROP rgProps[8];
 for (ULONG i = 0; i <= 7; i++) {
  VariantInit(&rgProps[i].vValue);
  rgProps[i].dwOptions = DBPROPOPTIONS_REQUIRED;
 };

 rgProps[0].dwPropertyID = DBPROP_INIT_LOCATION;
 V_VT(&(rgProps[0].vValue)) = VT_BSTR;
 V_BSTR(&(rgProps[0].vValue)) = SysAllocStringLen(OLESTR("server"),
                 wcslen(OLESTR("server")));

 rgProps[1].dwPropertyID = DBPROP_INIT_DATASOURCE;
 V_VT(&(rgProps[1].vValue)) = VT_BSTR;
 V_BSTR(&(rgProps[1].vValue)) = SysAllocStringLen(OLESTR("database"),
                 wcslen(OLESTR("database")));

 rgProps[2].dwPropertyID = DBPROP_AUTH_PASSWORD;
 V_VT(&(rgProps[2].vValue)) = VT_BSTR;
 V_BSTR(rgProps[2].vValue) = SysAllocStringLen(OLESTR("password"),
                 wcslen(OLESTR("password")));

 rgProps[3].dwPropertyID = DBPROP_AUTH_USERID;
 V_VT(&(rgProps[3].vValue)) = VT_BSTR;
 V_BSTR(&(rgProps[3].vValue)) = SysAllocStringLen(OLESTR("username"),
                 wcslen(OLESTR("username")));

 rgProps[4].dwPropertyID = DBPROP_AUTH_ENCRYPT_PASSWORD;
 V_VT(&(rgProps[4].vValue)) = VT_BOOL;
 V_BOOL(&(rgProps[4].vValue)) = VARIANT_TRUE;

 rgProps[5].dwPropertyID = DBPROP_AUTH_CACHE_AUTHINFO;
 V_VT(&(rgProps[5].vValue)) = VT_BOOL;
 V_BOOL(&(rgProps[5].vValue)) = VARIANT_TRUE;

 rgProps[6].dwPropertyID = DBPROP_AUTH_PERSIST_SENSITIVE_AUTHINFO;
 V_VT(&(rgProps[6].vValue)) = VT_BOOL;
 V_BOOL(&(rgProps[6].vValue)) = VARIANT_TRUE;

 rgProps[7].dwPropertyID = DBPROP_AUTH_PERSIST_ENCRYPTED;
 V_VT(&(rgProps[7].vValue)) = VT_BOOL;
 V_BOOL(&(rgProps[7].vValue)) = VARIANT_TRUE;

 // Create the structure containing the properties.
 DBPROPSET PropSet;
 PropSet.rgProperties  = rgProps;
 PropSet.cProperties   = 8;
 PropSet.guidPropertySet = DBPROPSET_DBINIT;

 // Get an IDBProperties pointer and set the initialization properties.
 IDBProperties *pIDBProperties;
 pIDBInitialize->QueryInterface(IID_IDBProperties, &pIDBProperties);
 pIDBProperties->SetProperties(1, &PropSet);
 pIDBProperties->Release();

 // Initialize the data source object.
 hr = pIDBInitialize->Initialize();
 return hr;
};

See Also

IDBInitialize::Uninitialize, IDBProperties::SetProperties


IDBInitialize::Uninitialize

Returns the data source object or enumerator to an uninitialized state.

HRESULT Uninitialize();

Parameters

None.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_UNEXPECTED
The data source is in the process of being initialized asynchronously. To cancel asynchronous initialization, call IDBAsynchStatus::Abort.

DB_E_OBJECTOPEN
There were open sessions, commands, or rowsets on the data source object.

Comments

The consumer is not required to uninitialize a data source object or enumerator before releasing it, but may use this method to release the state associated with the data source object or enumerator so that it can be reinitialized with Initialize or a new state loaded on a data source object, such as with IPersistFile::Load.

It is an error to call Uninitialize when there are open sessions, commands, or rowsets on the data source object; that is, the consumer must release all interface pointers on all sessions, commands, and rowsets on the data source object before calling Uninitialize. It is not an error to call Uninitialize if Initialize has not been called.

For information about what the consumer can and cannot do with a data source object or enumerator that is uninitialized, see "Data Source Object States" and "Enumerator States" in Chapter 2.

See Also

IDBInitialize::Initialize


IDBProperties

IDBProperties is used to set and get the values of properties on the data source object or enumerator and to get information about all properties supported by the provider.

Before the data source object or enumerator is initialized, the consumer can work only with properties in the Initialization property group. After the data source object or enumerator is initialized, the consumer can work with properties in the Initialization, Data Source, and Data Source Information property groups, although it cannot set the value of properties in the Initialization property group.

Only properties in the Initialization property group are guaranteed to survive uninitialization. That is, if the consumer uninitializes and reinitializes a data source object or enumerator, it might need to reset the values of properties in groups other than Initialization.

IDBProperties is a mandatory interface for data source objects and an optional interface for enumerators. However, if an enumerator exposes IDBInitialize, it must expose IDBProperties.

Method | Description
GetProperties | Returns the values of properties in the Data Source, Data Source Information, and Initialization property groups that are currently set on the data source object or the values of properties in the Initialization property group that are currently set on the enumerator.
GetPropertyInfo | Returns information about all properties supported by the provider.
SetProperties | Sets properties in the Data Source and Initialization property groups, for data source objects, or the Initialization property group, for enumerators.


IDBProperties::GetProperties

Returns the values of properties in the Data Source, Data Source Information, and Initialization property groups that are currently set on the data source object or the values of properties in the Initialization property group that are currently set on the enumerator.

HRESULT GetProperties (
   ULONG                  cPropertyIDSets,
   const DBPROPIDSET   rgPropertyIDSets[],
   ULONG *                  pcPropertySets,
   DBPROPSET **         prgPropertySets);

Parameters

cPropertyIDSets

[in]
The number of DBPROPIDSET structures in rgPropertyIDSets.

If cPropertyIDSets is zero, the provider ignores rgPropertyIDSets. If the data source object or enumerator has not been initialized, the provider returns the values of all properties in the Initialization property group for which values have been set or defaults exist. If the data source object or enumerator has been initialized, the provider returns the values of all properties in the Data Source, Data Source Information, and Initialization property groups, for data source objects, or the Initialization property group, for enumerators, for which values have been set or defaults exist. The provider does not return the values of properties in any of these property groups for which values have not been set and no defaults exist.

If cPropertyIDSets is not zero, the provider returns the values of the requested properties. If a property is not supported, or if the data source object or enumerator is not initialized and the value of a property in a group other than the Initialization property group is requested, the returned value of dwStatus in the returned DBPROP structure for that property is DBPROPSTATUS_NOTSUPPORTED and the value of dwOptions is undefined.

rgPropertyIDSets

[in]
An array of cPropertyIDSets DBPROPIDSET structures. If the data source object or enumerator has not been initialized, the properties specified in these structures must belong to the Initialization property group. If the data source object or enumerator has been initialized, the properties specified in these structures must belong to the Data Source, Data Source Information, or Initialization property group, for data source objects, or the Initialization property group, for enumerators. The provider returns the values of the properties specified in these structures. If cPropertyIDSets is zero, then this argument is ignored.

For information about the properties in the Data Source, Data Source Information, and Initialization property groups that are defined by OLE DB, see "Data Source Properties," "Data Source Information Properties," and "Initialization Properties" in Appendix C. For information about the DBPROPIDSET structure, see "DBPROPIDSET Structure" in Chapter 11.

pcPropertySets

[out]
A pointer to memory in which to return the number of DBPROPSET structures returned in *prgPropertySets. If cPropertyIDSets is zero, *pcPropertySets is the total number of property sets for which the provider supports at least one property in the Data Source, Data Source Information, or Initialization property group, for data source objects, or the Initialization property group, for enumerators. If cPropertyIDSets is greater than zero, *pcPropertySets is set to cPropertyIDSets. If an error other than DB_E_ERRORSOCCURRED occurs, *pcPropertySets is set to zero.

prgPropertySets

[out]
A pointer to memory in which to return an array of DBPROPSET structures. If cPropertyIDSets is zero, then one structure is returned for each property set that contains at least one property belonging to the Initialization property group (if the data source object or enumerator is not initialized), the Data Source, Data Source Information, or Initialization property group (if the data source object is initialized), or the Initialization property group (if the enumerator is initialized). If cPropertyIDSets is not zero, then one structure is returned for each property set specified in rgPropertyIDSets.

In the case of properties in the Initialization property group and for a previously persisted data source object, those properties related to sensitive authentication information such as password will be returned in an encrypted form if DBPROP_AUTH_PERSIST_ENCRYPTED is VARIANT_TRUE.

If cPropertyIDSets is not zero, the DBPROPSET structures in *prgPropertySets are returned in the same order as the DBPROPIDSET structures in rgPropertyIDSets; that is, for corresponding elements of each array, the guidPropertySet elements are the same. If cPropertyIDs, in an element of rgPropertyIDSets, is not zero, the DBPROP structures in the corresponding element of *prgPropertySets are returned in the same order as the DBPROPID values in rgPropertyIDs; that is, for corresponding elements of each array, the property IDs are the same.

The provider allocates memory for the structures and returns the address to this memory; the consumer releases this memory with IMalloc::Free when it no longer needs the structures. Before calling IMalloc::Free for *prgPropertySets, the consumer should call IMalloc::Free for the rgProperties element within each element of *prgPropertySets. If *pcPropertySets is zero on output or an error other than DB_E_ERRORSOCCURRED occurs, the provider does not allocate any memory and ensures that *prgPropertySets is a null pointer on output.

For information about the DBPROPSET and DBPROP structures, see "DBPROPSET Structure" and "DBPROP Structure" in Chapter 11.

Return Code

S_OK
The method succeeded. In all DBPROP structures returned by the method, dwStatus is set to DBPROPSTATUS_OK.

DB_S_ERRORSOCCURRED
No value was returned for one or more properties. The consumer checks dwStatus in the DBPROP structure to determine the properties for which values were not returned. GetProperties can fail to return properties for a number of reasons, including:

  • The property was not supported by the provider.

  • The data source object or enumerator was not initialized and the property was not in the Initialization property group.

  • The method was called on the data source object, the data source object was initialized, and the property was not in the Data Source, Data Source Information, or Initialization property groups.

  • The method was called on the enumerator, the enumerator was initialized, and the property was not in the Initialization property group.

  • The property set was not supported by the provider. If cPropertyIDs in the DBPROPIDSET structure for the property set was zero, the provider cannot set dwStatus in the DBPROP structure because it does not know the IDs of any properties in the property set. Instead, it sets cProperties to zero in the DBPROPSET structure returned for the property set.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
cPropertyIDSets was not equal to zero and rgPropertyIDSets was a null pointer.

pcPropertySets or prgPropertySets was a null pointer.

In an element of rgPropertyIDSets, cPropertyIDs was not zero and rgPropertyIDs was a null pointer.

In an element of rgPropertyIDSets, guidPropertySet was DBPROPSET_PROPERTIESINERROR and cPropertyIDs was not zero or rgPropertyIDs was not a null pointer.

cPropertyIDSets was greater than 1 and, in an element of rgPropertyIDSets, guidPropertySet was DBPROPSET_PROPERTIESINERROR.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to return the DBPROPSET or DBPROP structures.

DB_E_ERRORSOCCURRED
Values were not returned for any properties. The provider allocates memory for *prgPropertySets and the consumer checks dwStatus in the DBPROP structures to determine why properties were not returned. The consumer frees this memory when it no longer needs the information.

Comments

If IDBInitialize::Initialize returns DB_E_ERRORSOCCURRED, the consumer can immediately call GetProperties with the DBPROPSET_PROPERTIESINERROR property set to return all the properties that were in error. For more information, see "Property Sets" in Chapter 11.

See Also

IDBProperties::GetPropertyInfo, IDBProperties::SetProperties


IDBProperties::GetPropertyInfo

GetPropertyInfo returns information about all properties supported by the provider.

HRESULT GetPropertyInfo(
   ULONG                  cPropertyIDSets,
   const DBPROPIDSET   rgPropertyIDSets[],
   ULONG *                  pcPropertyInfoSets,
   DBPROPINFOSET **   prgPropertyInfoSets,
   OLECHAR **            ppDescBuffer);

Parameters

cPropertyIDSets

[in]
The number of DBPROPIDSET structures in rgPropertyIDSets.

If cPropertyIDSets is zero, the provider ignores rgPropertyIDSets. When called on the enumerator, the provider returns information about all properties in the Initialization property group. When called on the data source object, if the data source object has not been initialized, the provider returns information about all properties in the Initialization property group. If the data source object has been initialized, the provider returns information about all of the properties in all of the property sets it supports.

If cPropertyIDSets is not zero, the provider returns information about the requested properties. If a property is not supported, or if the method is called on an enumerator or an uninitialized data source object and the value of a property in a group other than the Initialization property group is requested, the returned value of dwStatus in the returned DBPROPINFO structure for that property is DBPROPFLAGS_NOTSUPPORTED and the values of the pwszDescription, vtType, and vValues elements are undefined.

rgPropertyIDSets

[in]
An array of cPropertyIDSets DBPROPIDSET structures. When called on the enumerator, the properties specified in these structures must belong to the Initialization property group. When called on the data source object, if the data source object has not been initialized, the properties must belong to the Initialization property group. If the data source object has been initialized, the properties can belong to any property group. The provider returns information about the properties specified in these structures. If cPropertyIDSets is zero, then this parameter is ignored. For information about the DBPROPIDSET structure, see "DBPROPIDSET Structure" in Chapter 11.

The following special GUIDs are defined for use with GetPropertyInfo. All of these GUIDs can be used on data source objects; only the DBPROPSET_DBINITALL GUID can be used on enumerators. If any of these GUIDs are specified in the guidPropertySet element of a DBPROPIDSET structure, the cPropertyIDs and rgPropertyIDs elements of that structure are ignored. However, the consumer should set these to zero and a null pointer, respectively, because the provider might attempt to verify that they are valid values. Consumers cannot pass special GUIDs and the GUIDs of other property sets in the same call to GetPropertyInfo. That is, if one element of rgPropertyIDSets contains a special GUID, all elements of rgPropertyIDSets must contain special GUIDs. These GUIDs are not returned in the guidPropertySet element of the DBPROPINFOSET structures returned in rgPropertyInfoSets. Instead, the GUID of the property set to which the property belongs is returned.

Property Set GUID | Description
DBPROPSET_DATASOURCEALL | Returns all properties in the Data Source property group, including provider-specific properties.
DBPROPSET_DATASOURCEINFOALL | Returns all properties in the Data Source Information property group, including provider-specific properties.
DBPROPSET_DBINITALL | Returns all properties in the Initialization property group, including provider-specific properties.
DBPROPSET_ROWSETALL | Returns all properties in the Rowset property group, including provider-specific properties.
DBPROPSET_SESSIONALL | Returns all properties in the Session property group, including provider-specific properties.

pcPropertyInfoSets

[out]
A pointer to memory in which to return the number of DBPROPINFOSET structures returned in *prgPropertyInfoSets. If cPropertyIDSets is zero, *pcPropertyInfoSets is the total number of property sets for which the provider supports at least one property. If *pcPropertyInfoSets is not zero and one of the special GUIDs listed in rgPropertyIDSets was used, *pcPropertyInfoSets may differ from cPropertyInfoSets. If an error other than DB_E_ERRORSOCCURRED occurs, *pcPropertyInfoSets is set to zero.

prgPropertyInfoSets

[out]
A pointer to memory in which to return an array of DBPROPINFOSET structures. If cPropertyIDSets is zero, then one structure is returned for each property set that contains at least one property supported by the provider. If cPropertyIDSets is not zero, then one structure is returned for each property set specified in rgPropertyIDSets.

If cPropertyIDSets is not zero, the DBPROPINFOSET structures in *prgPropertyInfoSets are returned in the same order as the DBPROPIDSET structures in rgPropertyIDSets; that is, for corresponding elements of each array, the guidPropertySet elements are the same. If cPropertyIDs, in an element of rgPropertyIDSets, is not zero, the DBPROPINFO structures in the corresponding element of *prgPropertyInfoSets are returned in the same order as the DBPROPID values in rgPropertyIDs; that is, for corresponding elements of each array, the property IDs are the same. The only exception to these rules is when one of the special GUIDs listed in rgPropertyIDSets was used. In this case, the order of the property information returned may differ from the order specified in rgPropertyIDSets.

The provider allocates memory for the structures and returns the address to this memory; the consumer releases this memory with IMalloc::Free when it no longer needs the structures. Before calling IMalloc::Free for *prgPropertyInfoSets, the consumer should call IMalloc::Free for the rgPropertyInfos element within each element of *prgPropertyInfoSets. If *pcPropertyInfoSets is zero on output or if an error other than DB_E_ERRORSOCCURRED occurs, then *prgPropertyInfoSets must be a null pointer on output.

For information about the DBPROPINFOSET and DBPROPINFO structures, see "DBPROPINFOSET Structure" and "DBPROPINFO Structure" in Chapter 11.

ppDescBuffer

[out]
A pointer to memory in which to return a pointer to storage for all string values returned in the *pwszDescription element of the DBPROPINFO structure. The provider allocates this memory with IMalloc and the consumer frees it with IMalloc::Free when it no longer needs the property descriptions. If ppDescBuffer is a null pointer on input, GetPropertyInfo does not return the property descriptions. If *pcPropertyInfoSets is zero on output or if an error occurs, the provider does not allocate any memory and ensures that *ppDescBuffer is a null pointer on output. Each of the individual string values stored in this buffer is terminated by a null-termination character. Therefore, the buffer may contain one or more strings, each with its own null-termination character, and may contain embedded null-termination characters.

Return Code

S_OK
The method succeeded. In all DBPROPINFO structures returned by the method, dwFlags is set to a value other than DBPROPFLAGS_NOTSUPPORTED.

DB_S_ERRORSOCCURRED
In an element of rgPropertyIDSets, a property specified in rgPropertyIDs was not supported by the provider; this includes the case in which the property set to which the property belongs is not supported by the provider. In the DBPROPINFO structures returned for the property, dwFlags is set to DBPROPFLAGS_NOTSUPPORTED.

In an element of rgPropertyIDSets, the property set specified in guidPropertySet was not supported by the provider. In the DBPROPINFOSET structure returned for the property set, cPropertyInfos is set to zero, and rgPropertyInfos is set to a null pointer.

The method was called on the enumerator and, in an element of rgPropertyIDSets, a property belonged to a group other than the Initialization property group.

The method was called on an uninitialized data source object and, in an element of rgPropertyIDSets, a property belonged to a group other than the Initialization property group.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
pcPropertyInfoSets or prgPropertyInfoSets was a null pointer.

cPropertyIDSets was not equal to zero and rgPropertyIDSets was a null pointer.

In an element of rgPropertyIDSets, cPropertyIDs was not zero and rgPropertyIDs was a null pointer.

In one element of rgPropertyIDSets, guidPropertySet specified one of the special GUIDs listed in the description of rgPropertyIDSets. In a different element of rgPropertyIDSets, guidPropertySet specified the GUID of a normal property set.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to return the DBPROPINFOSET or DBPROPINFO structures or the property descriptions.

DB_E_ERRORSOCCURRED
Errors occurred while attempting to return information about all properties. The provider allocates memory for *prgPropertyInfoSets and the consumer checks dwFlags in the DBPROPINFO structures to determine why information about each property was not returned. The consumer frees this memory when it no longer needs the information. The method can fail to return information about properties for any of the reasons specified in DB_S_ERRORSOCCURRED.

Comments

GetPropertyInfo returns information about supported properties, including the property set to which they belong, their ID, their data type, the object to which they apply, and whether they can be read or written. Properties in the Initialization and Data Source Creation property groups for which the consumer must supply a value before calling Initialize are marked with the DBPROPFLAGS_REQUIRED bit in the dwFlags element of the DBPROPINFO structure.

Whether a property is supported is different from its value. For example, if a provider supports IRowsetLocate, it supports the DBPROP_IRowsetLocate property. Whether a rowset actually exposes IRowsetLocate depends on whether the consumer sets the value of the corresponding property to VARIANT_TRUE or VARIANT_FALSE.

The rowset properties returned by GetPropertyInfo indicate the set of properties that the provider knows about. The properties that may actually be supported for any particular rowset may be a subset of these properties. For example, a schema rowset may not be able to support all of the same properties as a rowset resulting from calling OpenRowset, or the properties that can be supported on a rowset generated from a command may vary depending on the command text. In general, consumers should not make assumptions based on the information returned by GetPropertyInfo. When opening a rowset, consumers should specify DBPROPOPTIONS_REQUIRED for the minimum number of properties required to be able to consume the rowset, and should set dwOptions for properties that provide the consumer additional functionality to DBPROPTIONS_OPTIONAL.

See Also

IDBProperties::GetProperties, IDBProperties::SetProperties


IDBProperties::SetProperties

Sets properties in the Data Source and Initialization property groups, for data source objects, or the Initialization property group, for enumerators.

HRESULT SetProperties (
   ULONG         cPropertySets,
   DBPROPSET   rgPropertySets[]);

Parameters

cPropertySets

[in]
The number of DBPROPSET structures in rgPropertySets. If this is zero, the provider ignores rgPropertySets and the method does not do anything.

rgPropertySets

[in/out]
An array of DBPROPSET structures containing properties and values to be set. If the data source object or enumerator is uninitialized, the properties specified in these structures must belong to the Initialization property group. If the data source object is initialized, the properties must belong to the Data Source property group. If the enumerator is initialized, it is an error to call this method. If the same property is specified more than once in rgPropertySets, then which value is used is provider-specific. If cPropertySets is zero, this parameter is ignored.

For information about the properties in the Data Source and Initialization property groups that are defined by OLE DB, see "Data Source Properties" and "Initialization Properties" in Appendix C. For information about the DBPROPSET and DBPROP structures, see "DBPROPSET Structure" and "DBPROP Structure" in Chapter 11.

Return Code

S_OK
The method succeeded. In all DBPROP structures passed to the method, dwStatus is set to DBPROPSTATUS_OK.

DB_S_ERRORSOCCURRED
One or more properties were not set. Properties not in error remain set. The consumer checks dwStatus in the DBPROP structures to determine which properties were not set. SetProperties can fail to set properties for a number of reasons, including:

  • The property was not supported by the provider.

  • The data source object or enumerator was not initialized and the property was not in the Initialization property group.

  • The data source object was initialized and the property was not in the Data Source property group.

  • The property set was not supported by the provider.

  • The property is read-only and was not set to its default value.

  • It was not possible to set the property.

  • colid in the DBPROP structure was not DB_NULLID.

  • The value of dwOptions in the DBPROP structure was invalid.

  • The data type in vValue in the DBPROP structure was not the data type of the property or was not VT_EMPTY.

  • The value in vValue in the DBPROP structure was invalid.

  • The property's value conflicted with an existing property.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
cPropertySets was not equal to zero and rgPropertySets was a null pointer.

In an element of rgPropertySets, cProperties was not zero and rgProperties was a null pointer.

DB_E_ALREADYINITIALIZED
The method was called on the enumerator and the enumerator was already initialized.

DB_E_ERRORSOCCURRED
All property values were invalid and no properties were set. The consumer checks dwStatus in the DBPROP structures to determine why properties were not set. The 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 cheap to set the property.

See Also

IDBProperties::GetProperties, IDBProperties::GetPropertyInfo


IDBSchemaRowset

This is an optional interface on sessions. It is used to provide advanced schema information.

Consumers can get information about a data source without knowing its structure by using the IDBSchemaRowset methods. For example, the data source might be a Microsoft SQL Server database that organizes each database into a set of schemas that contain the tables and queries for each schema; the data source might be a Microsoft Access 2.0 database that has a container of tables and a container of queries; or the data source might be a Microsoft Access for a Windows® 95 database that enables users to define folders to group tables into an arbitrary hierarchy.

For the following schema rowsets, it is suggested that providers pay particular attention to rowset construction and data retrieval performance, because these methods will be used frequently by consumers.

This schema defines a minimum content of the system schema. It is assumed that consumers will precompile and store the specialized queries used for things such as command planning and execution-time plan validation, and storage engines may have specialized optimizations associated with those plans. Consumers can also look at the schema tables in the schema, and thus discover other schema tables and attributes beyond the minimum set.

For information about the schema rowsets, see Appendix B, "Schema Rowsets." Providers must return all of the columns in the rowsets they return. If they cannot return the information in a column, they must return an appropriate value. Generally, this is NULL. Providers can return provider-specific columns after the last column defined by OLE DB.

Schema rowsets are identified by GUIDs. The following table lists these GUIDs and the columns for which restrictions can be specified on the schema rowset. The number of restriction columns for each schema rowset are defined as constants prefixed with CRESTRICTIONS_ in the header files. Restriction values are treated as literals rather than as search patterns. For example, the restriction value "A_C" matches "A_C" but not "ABC".

GUID | Number of restrictions | Restriction columns
DBSCHEMA_ASSERTIONS | 3 | CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME
DBSCHEMA_CATALOGS | 1 | CATALOG_NAME
DBSCHEMA_CHARACTER_SETS | 3 | CHARACTER_SET_CATALOG CHARACTER_SET_SCHEMA CHARACTER_SET_NAME
DBSCHEMA_CHECK_CONSTRAINTS | 3 | CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME
DBSCHEMA_COLLATIONS | 3 | COLLATION_CATALOG COLLATION_SCHEMA COLLATION_NAME
DBSCHEMA_COLUMN_DOMAIN_ USAGE | 4 | DOMAIN_CATALOG DOMAIN_SCHEMA DOMAIN_NAME COLUMN_NAME
DBSCHEMA_COLUMN_PRIVILEGES | 6 | TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME GRANTOR GRANTEE
DBSCHEMA_COLUMNS | 4 | TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME
DBSCHEMA_CONSTRAINT_ COLUMN_USAGE | 7 | TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME
DBSCHEMA_CONSTRAINT_ TABLE_USAGE | 6 | TABLE_CATALOG TABLE_SCHEMA TABLE_NAME CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME
DBSCHEMA_FOREIGN_KEYS | 6 | PK_TABLE_CATALOG PK_TABLE_SCHEMA PK_TABLE_NAME FK_TABLE_CATALOG FK_TABLE_SCHEMA FK_TABLE_NAME
DBSCHEMA_INDEXES | 5 | TABLE_CATALOG TABLE_SCHEMA INDEX_NAME TYPE TABLE_NAME
DBSCHEMA_KEY_COLUMN_ USAGE | 7 | CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME
DBSCHEMA_PRIMARY_KEYS | 3 | TABLE_CATALOG TABLE_SCHEMA TABLE_NAME
DBSCHEMA_PROCEDURE_COLUMNS | 4 | PROCEDURE_CATALOG PROCEDURE_SCHEMA PROCEDURE_NAME COLUMN_NAME
DBSCHEMA_PROCEDURE_ PARAMETERS | 4 | PROCEDURE_CATALOG PROCEDURE_SCHEMA PROCEDURE_NAME PARAMETER_NAME
DBSCHEMA_PROCEDURES | 4 | PROCEDURE_CATALOG PROCEDURE_SCHEMA PROCEDURE_NAME PROCEDURE_TYPE
DBSCHEMA_PROVIDER_TYPES | 2 | DATA_TYPE BEST_MATCH
DBSCHEMA_REFERENTIAL_ CONSTRAINTS | 3 | CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME
DBSCHEMA_SCHEMATA | 3 | CATALOG_NAME SCHEMA_NAME SCHEMA_OWNER
DBSCHEMA_SQL_LANGUAGES | 0 | none
DBSCHEMA_STATISTICS | 3 | TABLE_CATALOG TABLE_SCHEMA TABLE_NAME
DBSCHEMA_TABLE_CONSTRAINTS | 7 | CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME TABLE_CATALOG TABLE_SCHEMA TABLE_NAME CONSTRAINT_TYPE
DBSCHEMA_TABLE_PRIVILEGES | 5 | TABLE_CATALOG TABLE_SCHEMA TABLE_NAME GRANTOR GRANTEE
DBSCHEMA_TABLES | 4 | TABLE_CATALOG TABLE_SCHEMA TABLE_NAME TABLE_TYPE
DBSCHEMA_TABLES_INFO | 4 | TABLE_CATALOG TABLE_SCHEMA TABLE_NAME TABLE_TYPE
DBSCHEMA_TRANSLATIONS | 3 | TRANSLATION_CATALOG TRANSLATION_SCHEMA TRANSLATION_NAME
DBSCHEMA_USAGE_PRIVILEGES | 6 | OBJECT_CATALOG OBJECT_SCHEMA OBJECT_NAME OBJECT_TYPE GRANTOR GRANTEE
DBSCHEMA_VIEW_COLUMN_ USAGE | 3 | VIEW_CATALOG VIEW_SCHEMA VIEW_NAME
DBSCHEMA_VIEW_TABLE_USAGE | 3 | VIEW_CATALOG VIEW_SCHEMA VIEW_NAME
DBSCHEMA_VIEWS | 3 | TABLE_CATALOG TABLE_SCHEMA TABLE_NAME

For IDBSchemaRowset, providers must support the following GUIDs:

  • DBSCHEMA_TABLES

  • DBSCHEMA_COLUMNS

  • DBSCHEMA_PROVIDER_TYPES
Method | Description
GetRowset | Returns a schema rowset.
GetSchemas | Returns a list of schema rowsets accessible by IDBSchemaRowset::GetRowset.


IDBSchemaRowset::GetRowset

Returns a schema rowset.

HRESULT GetRowset (
   IUnknown *         punkOuter,
   REFGUID            rguidSchema,
   ULONG            cRestrictions,
   const VARIANT      rgRestrictions[],
   REFIID               riid,
   ULONG            cPropertySets,
   DBPROPSET      rgPropertySets[],
   IUnknown **         ppRowset);

Parameters

pUnkOuter

[in]
A pointer to the controlling IUnknown interface if the rowset is being created as part of an aggregate. If the IDBSchemaRowset interface is not being aggregated, it is a null pointer.

rguidSchema

[in]
A GUID identifying the schema rowset. For more information, see the "Comments" section and IDBSchemaRowset.

cRestrictions

[in]
The count of restriction values.

rgRestrictions

[in]
An array of restriction values. These are applied in order to the restriction columns. That is, the first restriction value applies to the first restriction column, the second restriction value applies to the second restriction column, and so on. For more information, see the "Comments" section.

riid

[in]
The IID of the requested rowset interface. This interface is conceptually added to the list of required interfaces on the resulting rowset, and the method fails (E_NOINTERFACE) if that interface cannot be supported on the resulting rowset.

cPropertySets

[in]
The number of DBPROPSET structures in rgPropertySets. If this is 0, the provider ignores rgPropertySets.

rgPropertySets

[in/out]
An array of DBPROPSET structures containing properties and values to be set. The properties specified in these structures must belong to the Rowset property group. If the same property is specified more than once in rgPropertySets, then it is provider-specific which value is used. If cPropertySets is 0, this argument is ignored.

For information about the properties in the Rowset property group that are defined by OLE DB, see "Rowset Properties" in Appendix C. For information about the DBPROPSET and DBPROP structures, see "DBPROPSET Structure" and "DBPROP Structure" in Chapter 11.

ppRowset

[out]
A pointer to memory in which to return the requested interface pointer on the schema rowset. This rowset is read-only. If no applicable schema information exists, an empty rowset is returned.

Return Code

S_OK
The method succeeded. In all DBPROP structures passed to the method, dwStatus is set to DBPROPSTATUS_OK.

DB_S_ASYNCHRONOUS
The method has initiated asynchronous creation of the rowset. The consumer can call IDBAsynchStatus to poll for status or IConnectionPointContainer to obtain the IID_IDBAsynchNotify connection point. Attempting to call any other interfaces may fail and the full set of interfaces may not be available on the object until asynchronous initialization of the rowset has completed.

DB_S_ERRORSOCCURRED
The rowset was opened but one or more properties—for which the dwOptions element of the DBPROP structure was DBPROPOPTIONS_OPTIONAL—were not set. The consumer checks dwStatus in the DBPROP structures to determine which properties were not set. The method can fail to set properties for a number of reasons:

  • The property was not supported by the provider.

  • The property was not in the Rowset property group.

  • The property set was not supported by the provider.

  • It was not possible to set the property.

  • The colid in the DBPROP structure was invalid.

  • The data type in vValue in the DBPROP structure was not the data type of the property or was not VT_EMPTY.

  • The value in vValue in the DBPROP structure was invalid.

  • The property's value conflicted with an existing property.

  • A property was specified to be applied to all columns, but could not be applied to one or more columns.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
rguidSchema was invalid.

rguidSchema specified a schema rowset that was not supported by the provider.

cRestrictions was greater than the number of restriction columns for the schema rowset specified in rguidSchema.

In one or more restriction values specified in rgRestrictions, the vt element of the VARIANT was the incorrect type.

cRestrictions was greater than zero and rgRestrictions was a null pointer.

In an element of rgRestrictions, the vt element of the VARIANT was not VT_EMPTY and the provider did not support the corresponding restriction.

ppRowset was a null pointer.

In an element of rgPropertySets, cProperties was not 0 and rgProperties was a null pointer.

cPropertySets was greater than zero and rgPropertySets was a null pointer.

E_NOINTERFACE
The schema rowset did not support the interface specified in riid.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to create the rowset.

DB_E_ABORTLIMITREACHED
The method failed because a resource limit has been reached. For example, a query used to implement the method timed out. No rowset is returned.

DB_E_ERRORSOCCURRED
No rowset was returned because one or more properties—for which the dwOptions element of the DBPROP structure was DBPROPOPTIONS_REQUIRED or an invalid value—were not set. The consumer checks dwStatus in the DBPROP structures to determine which properties were not set. None of the satisfiable properties are remembered. The 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.

DB_E_NOAGGREGATION
pUnkOuter was not a null pointer and the rowset being created does not support aggregation.

pUnkOuter was non-null and riid was not IID_Unknown.

DB_E_NOTSUPPORTED
The provider does not support restrictions on one or more of the columns in the corresponding schema.

DB_SEC_E_PERMISSIONDENIED
The consumer did not have sufficient permission to create the schema rowset.

DB_E_OBJECTOPEN
The provider would have to open a new connection to support the operation and DBPROP_MULTIPLECONNECTIONS is set to VARIANT_FALSE.

Comments

GetRowset allows the consumer to specify restrictions that limit the set of rows returned by the schema rowset, but does not define the format for such restrictions. The restriction array contains single values that are compared for equality to the corresponding value in the schema rowset. Only rows that exactly match the restriction value in the corresponding column are returned in the rowset. Pattern matching is not supported.

When preparing an array of restriction values, consumers are not required to specify values for all of the restriction columns on a rowset. Any columns for which no restriction values are specified are not restricted.

There are two ways for a consumer to explicitly specify that there is no restriction value for a column. First, the consumer cannot specify a restriction value for the column. For example, if there are three restriction columns, the consumer passes only two restriction values; these apply to the first two restriction columns and no value is specified for the third restriction column.

Second, the consumer can set the vt element of the VARIANT specified for the column to VT_EMPTY, even if the provider does not support restrictions on the column. For example, suppose there are three restriction columns, the provider supports restrictions on the first and third columns, and the consumer wants to restrict values on these two columns. The consumer passes three restriction values. The first and third values are the desired restrictions and the second value is a VARIANT with a vt element of VT_EMPTY.

If the vt element of the VARIANT specified for a restriction column is VT_NULL, this means that the column must be NULL to satisfy the restriction. This is equivalent to the IS NULL predicate in SQL. If the vt element of the VARIANT specified for a restriction column is VT_BSTR and the bstrVal element is set to a null pointer, this means that the column must contain an empty string to satisfy the restriction.

See Also

IDBSchemaRowset::GetSchemas


IDBSchemaRowset::GetSchemas

Returns a list of schema rowsets accessible by IDBSchemaRowset::GetRowset.

HRESULT GetSchemas (
   ULONG *      pcSchemas,
   GUID **      prgSchemas,
   ULONG**      prgRestrictionSupport);

Parameters

pcSchemas

[out]
A pointer to memory in which to return the number of GUIDs in *prgSchemas. The returned count of schema GUIDs is always at least 3, because all providers must support the TABLES, COLUMNS, and PROVIDER_TYPES schema rowsets. If an error occurs, *pcSchemas is set to 0.

prgSchemas

[out]
A pointer to memory in which to return an array of GUIDs identifying supported schema rowsets. For more information, see IDBSchemaRowset. The rowset allocates memory for the GUIDs and returns the address to this memory. The consumer releases this memory with IMalloc::Free when it no longer needs the list of GUIDs. If an error occurs, *prgSchemas is set to a null pointer.

prgRestrictionSupport

[out]
A pointer to memory in which to return an array of ULONGs, one element for each supported schema rowset, describing the restriction columns supported for that schema rowset. For a given schema rowset, the array element corresponding to the schema rowset will have the appropriate bit set if the provider implements a restriction on that column. Bits that do not correspond to currently defined restriction columns should not be set.

For example, the specification lists three restriction columns for the schema DBSCHEMA_ASSERTIONS. The array element corresponding to this schema will represent the CONSTRAINT_CATALOG column in bit 0, the CONSTRAINT_SCHEMA column in bit 1, and the CONSTRAINT_NAME column in bit 2. The following code shows how the consumer might determine whether a restriction on the CONSTRAINT_CATALOG column is supported on the ASSERTIONS schema rowset.

ULONG  cSchemas;
GUID  *rgSchemas;
ULONG *rgRestSupport;

pIDBSchemaRowset->GetSchemas(&cSchemas, &rgSchemas, &rgRestSupport);

// Assume rgSchemas[0] is the GUID DBSCHEMA_ASSERTIONS. The following
// code checks whether the restriction on the CONSTRAINT_CATALOG
// column is supported.
if (rgRestSupport[0] & 0x1) {
 // Restriction is supported.
} else {
 // Restriction is not supported.
}

The rowset allocates memory for the restrictions and returns the address to this memory. The consumer releases this memory with IMalloc::Free when it no longer needs the list of restrictions. If an error occurs, *prgRestrictionSupport is set to a null pointer.

If prgRestrictionSupport is a null pointer, the rowset does not allocate any memory or return any restrictions.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
pcSchemas, prgSchemas, or prgRestrictionSupport was a null pointer.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to return the array of schema GUIDs or restriction support information.

See Also

IDBSchemaRowset::GetRowset


IErrorInfo

IErrorInfo is defined by Automation; the following describes how the interface is used in OLE DB. IErrorInfo returns information about an error in addition to the return code. It returns the error message, name of the component, and GUID of the interface in which the error occurred, and the name and topic of the Help file that applies to the error.

When to Implement

IErrorInfo is implemented by code in the OLE DB Software Development Kit (SDK). This code calls provider-specific code in IErrorLookup to retrieve error messages, sources, and Help file information.

OLE DB error objects expose IErrorInfo at two levels. First, it is exposed on the OLE DB error object itself, which enables Automation consumers to use OLE DB error objects. The interface pointer is returned by GetErrorInfo in the Automation DLL; it can also be returned by QueryInterface. IErrorInfo returns the information stored in record 0 of the OLE DB error object and uses the default locale ID.

IErrorInfo is also exposed on individual records in the OLE DB error object. These IErrorInfo interface pointers, returned by IErrorRecords::GetErrorInfo, are different from the IErrorInfo interface pointer exposed on the OLE DB error object itself, and cannot be returned by QueryInterface. The record to which the interface pointer applies and the locale ID it uses is specified by the consumer in IErrorRecords::GetErrorInfo.

When to Call

Automation consumers call GetErrorInfo in the Automation DLL to get an IErrorInfo interface pointer on the OLE DB error object.

OLE DB consumers also call GetErrorInfo in the Automation DLL. However, they do not generally use the returned IErrorInfo interface pointer. Instead, they call QueryInterface to get an IErrorRecords interface pointer and then call IErrorRecords::GetErrorInfo to get an IErrorInfo interface pointer on a particular record in the OLE DB error object. Then the OLE DB consumers use this interface pointer to retrieve the error information.

Method | Description
GetDescription | Returns a text description of the error.
GetGUID | Returns the GUID of the interface that defined the error.
GetHelpContext | Returns the Help context ID for the error.
GetHelpFile | Returns the path of the Help file that describes the error.
GetSource | Returns the name of the component that generated the error, such as "ODBC driver-name".


IErrorInfo::GetDescription

Returns a text description of the error.

HRESULT GetDescription (
   BSTR *   pbstrDescription);

Parameters

pbstrDescription

[out]
A pointer to memory in which to return a pointer to a string that describes the error. If there is no error description or an error occurs, the returned value (*pbstrDescription) is set to a null pointer. The memory for this string is allocated by the provider and must be freed by the consumer with a call to SysFreeString.

Return Code

S_OK
The method succeeded.

E_INVALIDARG
pbstrDescription was a null pointer.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to return the error description.

DB_E_NOLOCALE
The locale ID specified by the lcid parameter in IErrorRecords::GetErrorInfo was not supported by the provider.

Comments

If the lookup ID is IDENTIFIER_SDK_ERROR, the implementation of this method retrieves the error description from the error resource DLL shipped with the OLE DB SDK. Otherwise, GetDescription calls IErrorLookup::GetErrorDescription to retrieve the string. If the description is parameterized, such as "Cannot open table <param1>," the lookup service incorporates the parameter values before returning the string.

See Also

IErrorLookup::GetErrorDescription


IErrorInfo::GetGUID

Returns the GUID of interface that defined the error.

HRESULT GetGUID (
   GUID *   pguid);

Parameters

pguid

[out]
A pointer to memory in which to return the GUID of the interface that defined the error. If the error was defined by the operating system, the returned GUID is DB_NULLGUID. This is the same value as is returned in the iid element of the ERRORINFO structure returned by IErrorRecords::GetBasicErrorInfo.

Return Code

S_OK
The method succeeded.

E_INVALIDARG
pguid was a null pointer.

Comments

This GUID does not necessarily represent the source of the error; the source is the class or application that raised the error. Using the GUID, an application can handle errors in an interface independent of the class that implements the interface.


IErrorInfo::GetHelpContext

Returns the Help context ID for the error.

HRESULT GetHelpContext (
   DWORD *   pdwHelpContext);

Parameters

pdwHelpContext

[out]
A pointer to memory in which to return the Help context ID for the error. If there is no Help file (*pbstrHelpFile returned by GetHelpFile is a null pointer), then the returned value has no meaning.

Return Code

S_OK
The method succeeded.

E_INVALIDARG
pdwHelpContext was a null pointer.

DB_E_NOLOCALE
The locale ID specified by the lcid parameter in IErrorRecords::GetErrorInfo was not supported by the provider.

Comments

The implementation of this method shipped with the OLE DB SDK calls IErrorLookup::GetHelpInfo to retrieve the Help context ID.

See Also

IErrorInfo::GetHelpFile, IErrorLookup::GetHelpInfo


IErrorInfo::GetHelpFile

Returns the path of the Help file that describes the error.

HRESULT GetHelpFile (
   BSTR *   pbstrHelpFile);

Parameters

pbstrHelpFile

[out]
A pointer to memory in which to return a pointer to a string containing the fully qualified path of the Help file. If there is no Help file or an error occurs, the returned value (*pbstrHelpFile) is set to a null pointer. The memory for this string is allocated by the provider and must be freed by the consumer with a call to SysFreeString.

Return Code

S_OK
The method succeeded.

E_INVALIDARG
pbstrHelpFile was a null pointer.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to return the Help file path.

DB_E_NOLOCALE
The locale ID specified by the lcid parameter in IErrorRecords::GetErrorInfo was not supported by the provider.

Comments

The implementation of this method shipped with the OLE DB SDK calls IErrorLookup::GetHelpInfo to retrieve the path of the Help file.

See Also

IErrorInfo::GetHelpContext, IErrorLookup::GetHelpInfo


IErrorInfo::GetSource

Returns the name of the component that generated the error, such as "ODBC driver-name."

HRESULT GetSource (
   BSTR *   pbstrSource);

Parameters

pbstrSource

[out]
A pointer to memory in which to return a pointer to the name of the component that generated the error. If an error occurs, *pbstrSource is set to a null pointer. The memory for this string is allocated by the provider and must be freed by the consumer with a call to SysFreeString.

Return Code

S_OK
The method succeeded.

E_INVALIDARG
pbstrSource was a null pointer.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to return the component name.

DB_E_NOLOCALE
The locale ID specified by the lcid parameter in IErrorRecords::GetErrorInfo was not supported by the provider.

Comments

The implementation of this method shipped with the OLE DB SDK calls IErrorLookup::GetErrorDescription to retrieve the error source.

See Also

IErrorLookup::GetErrorDescription


IErrorLookup

IErrorLookup is used by OLE DB error objects to determine the values of the error message, source, Help file path, and context ID based on the return code and a provider-specific error number.

IErrorLookup is exposed by a provider-specific error lookup service that is mandatory for all providers that return OLE DB error objects.

When to Implement

All providers that return OLE DB error objects must implement IErrorLookup in a separate lookup service. For information about the registry entries used by error lookup services, see "Error Lookup Service Registry Entries" in Chapter 14.

When to Call

IErrorLookup is called by the code shipped in the OLE DB SDK that implements OLE DB error objects. It should not be called by general consumers.

When an error occurs, the following sequence of events takes place:

  1. The provider creates an OLE DB error object and adds a record to that object.

  2. The consumer retrieves the error object and gets an IErrorInfo interface on a particular record in that object. It then calls a method in IErrorInfo.

  3. Except for the IErrorInfo::GetGUID method, the OLE DB error object code, which is shipped with the OLE DB SDK, loads the error lookup service based on the class ID stored in the error record.

  4. The OLE DB error object code returns the hrError element of the ERRORINFO structure, the lookup ID, and the error parameters from the error record. It also returns the locale ID requested in IErrorRecords::GetErrorInfo. It passes all of this information to the appropriate IErrorLookup method.

  5. The IErrorLookup method returns the requested information, based on the hrError, dwLookupID, and lcid arguments, integrates the parameters, and returns this to the error object code.

  6. The OLE DB error object code returns the requested information to the consumer.

  7. The consumer releases the OLE DB error object.

  8. The OLE DB error object code calls IErrorLookup::ReleaseErrors for all error records with a nonzero dynamic error ID to release the error information associated with that record.
Method | Description
GetErrorDescription | Returns the error message and source, based on the return code and the provider-specific error number.
GetHelpInfo | Returns the path of the Help file and the context ID of the topic that explains the error.
ReleaseErrors | Releases any dynamic error information associated with a dynamic error ID.


IErrorLookup::GetErrorDescription

Returns the error message and source, based on the return code and the provider-specific error number.

HRESULT GetErrorDescription (
   HRESULT            hrError,
   DWORD            dwLookupID,
   DISPPARAMS *   pdispparams,
   LCID               lcid,
   BSTR *               pbstrSource,
   BSTR *               pbstrDescription);

Parameters

hrError

[in]
The code returned by the method that caused the error.

dwLookupID

[in]
The provider-specific number of the error.

pdispparams

[in]
The parameters of the error. If there are no error parameters, this is a null pointer.

lcid

[in]
The locale ID for which to return the description and source.

pbstrSource

[out]
A pointer to memory in which to return a pointer to the name of the component that generated the error. If an error occurs, *pbstrSource is set to a null pointer. The memory for this string is allocated by the provider and must be freed by the consumer with a call to SysFreeString.

pbstrDescription

[out]
A pointer to memory in which to return a pointer to a string that describes the error. If pdispparams was not a null pointer, then the error parameters are integrated into this description. If there is no error description or an error occurs, the returned value (*pbstrDescription) is a null pointer. The memory for this string is allocated by the provider and must be freed by the consumer with a call to SysFreeString.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
pbstrSource or pbstrDescription was a null pointer.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to return the error source or description.

DB_E_BADHRESULT
hrError was invalid.

DB_E_BADLOOKUPID
dwLookupID was invalid.

DB_E_NOLOCALE
The locale ID specified in lcid was not supported by the provider.

See Also

IErrorInfo::GetDescription, IErrorLookup::GetHelpInfo


IErrorLookup::GetHelpInfo

Returns the path of the Help file and the context ID of the topic that explains the error.

HRESULT GetHelpInfo (
   HRESULT      hrError,
   DWORD      dwLookupID,
   LCID         lcid,
   BSTR *         pbstrHelpFile,
   DWORD *      pdwHelpContext);

Parameters

hrError

[in]
The code returned by the method that caused the error.

dwLookupID

[in]
The provider-specific number of the error.

lcid

[in]
The locale ID for which to return the Help file path and context ID.

pbstrHelpFile

[out]
A pointer to memory in which to return a pointer to a string containing the fully qualified path of the Help file. If there is no Help file or an error occurs, the returned value (*pbstrHelpFile) is a null pointer. The memory for this string is allocated by the provider and must be freed by the consumer with a call to SysFreeString.

pdwHelpContext

[out]
A pointer to memory in which to return the Help context ID for the error. If there is no Help file (*pbstrHelpFile is a null pointer), then the returned value has no meaning.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
pbstrHelpFile or pdwHelpContext was a null pointer.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to return the Help file path.

DB_E_BADHRESULT
hrError was invalid.

DB_E_BADLOOKUPID
dwLookupID was invalid.

DB_E_NOLOCALE
The locale ID specified in lcid was not supported by the provider.

See Also

IErrorInfo::GetHelpContext, IErrorInfo::GetHelpFile, IErrorLookup::GetErrorDescription


IErrorLookup::ReleaseErrors

Releases any dynamic error information associated with a dynamic error ID.

HRESULT ReleaseErrors (
   const DWORD   dwDynamicErrorID);

Parameters

dwDynamicErrorID

[in]
The ID of the dynamic error information to release.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

DB_E_BADDYNAMICERRORID
dwDynamicErrorID was invalid.

Comments

Dynamic error information is created at run time. It is released when the OLE DB error object calls ReleaseErrors with the ID of the error information to release.

Although it is not required, it is more efficient for providers to use the same error ID for all records in a single error object. This allows ReleaseErrors to release all of this information in a single call.

For more information, see "Error Lookup Services" in Chapter 13.


IErrorRecords

IErrorRecords is defined by OLE DB. It is used to add and retrieve records in an OLE DB error object. Information is passed to and from OLE DB error objects in an ERRORINFO structure. For information about this structure, see "Error Records" in Chapter 13.

When to Implement

IErrorRecords is implemented by code in the OLE DB SDK.

When to Call

Consumers use this interface to retrieve information stored in the records of an OLE DB error object. They call QueryInterface to get a pointer to this interface after retrieving an OLE DB error object with GetErrorInfo in the Automation DLL.

Providers use this interface to add records to an OLE DB error object. If they get an existing OLE DB error object with GetErrorInfo in the Automation DLL, they call QueryInterface on that object to get a pointer to this interface. If they create a new OLE DB error object through a class factory or with CoCreateInstance, they request that a pointer to this interface be returned.

Method | Description
AddErrorRecord | Adds a record to an OLE DB error object.
GetBasicErrorInfo | Returns basic information about the error, such as the return code and provider-specific error number.
GetCustomErrorObject | Returns a pointer to an interface on the custom error object.
GetErrorInfo | Returns an IErrorInfo interface pointer on the specified record.
GetErrorParameters | Returns the error parameters.
GetRecordCount | Returns the count of records in the OLE DB error object.


IErrorRecords::AddErrorRecord

Adds a record to an OLE DB error object.

HRESULT AddErrorRecord (
   ERRORINFO *      pErrorInfo,
   DWORD            dwLookupID,
   DISPPARAMS *   pdispparams,
   IUnknown *         punkCustomError,
   DWORD            dwDynamicErrorID);

Parameters

pErrorInfo

[in]
A pointer to an ERRORINFO structure containing information about the error. This structure is allocated and freed by the consumer. For more information, see "Error Records" in Chapter 13.

dwLookupID

[in]
The value used by the provider's error lookup service in conjunction with the return code to identify the error description, Help file, and context ID for an error. This can be a provider-specific value, such as the dwMinor element of *pErrorInfo. It can also be a special value, IDENTIFIER_SDK_ERROR, that tells the implementation of IErrorInfo that is shipped with the OLE DB SDK to ignore the provider's lookup service and use the description supplied in the OLE DB SDK error resource DLL.

const DWORD IDENTIFIER_SDK_MASK  = 0xF000000;
const DWORD IDENTIFIER_SDK_ERROR = 0x1000000;

pdispparams

[in]
A pointer to the parameters for the error. This is a null pointer if there are no error parameters. The error parameters are inserted into the error text by the error lookup service. This structure is allocated and freed by the consumer. For more information, see "Error Parameters" in "Error Records" in Chapter 13.

punkCustomError

[in]
An interface pointer to the custom error object. This is a null pointer if there is no custom object for the error. For more information, see "OLE DB Error Objects" in Chapter 13.

dwDynamicErrorID

[in]
If the error lookup service uses static errors—that is, error information that is hard-coded in the lookup service—dwDynamicErrorID is zero.

If the error lookup service uses dynamic errors—that is, error information that is created at run time—dwDynamicErrorID is the ID of the error record. This ID is used to release the error information when the OLE DB error object is released. Although it is not required, it is more efficient for all error records in a single OLE DB error object to have the same dynamic error ID.

For more information, see "Error Lookup Services" in Chapter 13.

Return Code

S_OK
The method succeeded.

E_INVALIDARG
pErrorInfo was a null pointer.

E_OUTOFMEMORY
The OLE DB error object was unable to allocate sufficient memory with which to add a new record.

Comments

This method should be used only by providers; there are no reasons for consumers to use it.

Records are added to the top of the list. That is, the number of the newly added record is record 0 and the number of all other records is increased by 1.

AddErrorRecord adds a reference count on the custom error object. After adding a custom error object to a record in an OLE DB error object, the provider must call Release on all interface pointers it holds on that custom error object. This transfers ownership of the custom error object to the OLE DB error object. When it is released, the OLE DB error object will release all custom error objects.

See Also

IErrorRecords::GetBasicErrorInfo, IErrorRecords::GetCustomErrorObject, IErrorRecords::GetErrorParameters


IErrorRecords::GetBasicErrorInfo

Returns basic information about the error, such as the return code and provider-specific error number.

HRESULT GetBasicErrorInfo (
   ULONG         ulRecordNum,
   ERRORINFO *   pErrorInfo);

Parameters

ulRecordNum

[in]
The zero-based number of the record for which to return information.

pErrorInfo

[out]
A pointer to an ERRORINFO structure in which to return basic error information. This structure is allocated and freed by the consumer. For more information, see "Error Records" in Chapter 13.

Return Code

S_OK
The method succeeded.

E_INVALIDARG
pErrorInfo was a null pointer.

DB_E_BADRECORDNUM
ulRecordNum, which is zero-based, was greater than or equal to the count, which is one-based, of records returned by GetRecordCount.

Comments

This method should be used only by consumers; there are no reasons for providers to use it.

See Also

IErrorRecords::GetCustomErrorObject, IErrorRecords::GetErrorInfo, IErrorRecords::GetErrorParameters


IErrorRecords::GetCustomErrorObject

Returns a pointer to an interface on the custom error object.

HRESULT GetCustomErrorObject (
   ULONG      ulRecordNum,
   REFIID         riid,
   IUnknown **   ppObject);

Parameters

ulRecordNum

[in]
The zero-based number of the record for which to return a custom error object.

riid

[in]
The IID of the interface to return.

ppObject

[out]
A pointer to memory in which to return an interface pointer on the custom error object. If there is no custom error object, a null pointer is returned; that is, *ppObject is a null pointer.

Return Code

S_OK
The method succeeded.

E_INVALIDARG
ppObject was a null pointer.

E_NOINTERFACE
The custom error object did not support the interface specified in riid.

DB_E_BADRECORDNUM
ulRecordNum, which is zero-based, was greater than or equal to the count, which is one-based, of records returned by GetRecordCount.

Comments

This method should be used only by consumers; there are no reasons for providers to use it.

See Also

IErrorRecords::GetBasicErrorInfo, IErrorRecords::GetErrorParameters


IErrorRecords::GetErrorInfo

Returns an IErrorInfo interface pointer on the specified record.

HRESULT GetErrorInfo (
   ULONG         ulRecordNum,
   LCID            lcid,
   IErrorInfo **      ppErrorInfo);

Parameters

ulRecordNum

[in]
The zero-based number of the record for which to return an IErrorInfo interface pointer.

lcid

[in]
The locale ID for which to return error information. This parameter is checked when it is passed to methods in IErrorInfo.

ppErrorInfo

[out]
A pointer to memory in which to return a pointer to an IErrorInfo interface on the specified record. This IErrorInfo interface pointer is different from the IErrorInfo interface pointer exposed on the OLE DB error object with QueryInterface. For more information, see "OLE DB Error Objects" in Chapter 13.

Return Code

S_OK
The method succeeded.

E_INVALIDARG
ppErrorInfo was a null pointer.

DB_E_BADRECORDNUM
ulRecordNum, which is zero-based, was greater than or equal to the count, which is one-based, of records returned by GetRecordCount.

Comments

This method should be used only by consumers; there are no reasons for providers to use it.

See Also

IErrorRecords::GetBasicErrorInfo, IErrorRecords::GetCustomErrorObject, IErrorRecords::GetErrorParameters


IErrorRecords::GetErrorParameters

Returns the error parameters.

HRESULT GetErrorParameters (
   ULONG            ulRecordNum,
   DISPPARAMS *   pdispparams);

Parameters

ulRecordNum

[in]
The zero-based number of the record for which to return parameters.

pdispparams

[out]
A pointer to a DISPPARAMS structure in which to return the error parameters. The consumer allocates the memory for the DISPPARAMS structure itself, but the provider allocates the memory for any arrays pointed to by elements of the DISPPARAMS structure.

Return Code

S_OK
The method succeeded.

E_INVALIDARG
pdispparams was a null pointer.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to return the data pointed to by elements of *pdispparams.

DB_E_BADRECORDNUM
ulRecordNum, which is zero-based, was greater than or equal to the count, which is one-based, of records returned by GetRecordCount.

Comments

This method is used by consumers only when the meaning of the error parameters is known to the consumer; error parameters are generally passed to the error lookup service and incorporated into error messages by the provider through that lookup service. There is no reason for providers to use this method.

See Also

IErrorRecords::GetBasicErrorInfo, IErrorRecords::GetCustomErrorObject


IErrorRecords::GetRecordCount

Returns the count of records in the OLE DB error object.

HRESULT GetRecordCount (
   ULONG *   pcRecords);

Parameters

pcRecords

[out]
A pointer to memory in which to return the count of error records. This is a one-based count, although error records are zero-based. If *pcRecords is zero, there are no records in the OLE DB error object and calls to GetBasicErrorInfo, GetErrorInfo, GetErrorParameters, and GetCustomErrorObject will return DB_E_BADRECORDNUM.

Return Code

S_OK
The method succeeded.

E_INVALIDARG
pcRecords was a null pointer.

Comments

This method should be used only by consumers; there are no reasons for providers to use it.

See Also

IErrorRecords::GetBasicErrorInfo, IErrorRecords::GetCustomErrorObject, IErrorRecords::GetErrorInfo, IErrorRecords::GetErrorParameters


IGetDataSource

This is a mandatory interface on the session for obtaining an interface pointer to the data source object.

Method | Description
GetDataSource | Returns an interface pointer on the data source object that created this session.


IGetDataSource::GetDataSource

Returns an interface pointer on the data source object that created the session.

HRESULT GetDataSource (
   REFIID       riid,
   IUnknown **   ppDataSource);

Parameters

riid

[in]
The IID of the interface on which to return a pointer.

ppDataSource

[out]
A pointer to memory in which to return the interface pointer. If GetDataSource fails, it must attempt to set *ppDataSource to a null pointer.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
ppDataSource was a null pointer.

E_NOINTERFACE
The object that created this session did not support the interface specified in riid.

See Also

ICommand::GetDBSession, IRowsetInfo::GetSpecification


IIndexDefinition

IIndexDefinition exposes simple methods to create and drop indexes from the data source object.

When to Implement

IIndexDefinition is optional for providers that do not otherwise support index creation; it is mandatory for providers that support creating and dropping indexes through commands.

Method | Description
CreateIndex | Adds a new index to a base table.
DropIndex | Drops an index from a base table.


IIndexDefinition::CreateIndex

Adds a new index to a base table.

HRESULT CreateIndex(
   DBID *                                 pTableID,
   DBID *                                 pIndexID,
   ULONG                              cIndexColumnDescs,
   const DBINDEXCOLUMNDESC      rgIndexColumnDescs[],
   ULONG                              cPropertySets,
   DBPROPSET                        rgPropertySets[],
   DBID **                              ppIndexID);

Parameters

pTableID

[in]
A pointer to the DBID of the table for which to create an index.

pIndexID

[in]
A pointer to the ID of the new index to create. If this is a null pointer, the provider assigns an ID to the index. The ID must be unique.

cIndexColumnDescs

[in]
The count of DBINDEXCOLUMNSDESC structures in rgIndexColumnDescs.

rgIndexColumnDescs

[in]
An array of DBINDEXCOLUMNDESC structures that describe how to construct the index. The order of the DBINDEXCOLUMNDESC structures in rgIndexColumnDescs determines the order of the columns in the index key. That is, the column identified by the first element of this array is the most significant column in the index key and the column identified by the last element is the least significant column. When the index is opened as a rowset, the key columns occur in order of most significant column to least significant column.

typedef struct {
 DBID *    pColumnID;
 DBINDEX_COL_ORDER eIndexColOrder;
} DBINDEXCOLUMNDESC;

The elements of this structure are used as follows.

Element | Description
pColumnID | A pointer to the ID of the base table column.
eIndexColOrder Whether the index is ascending or descending in this column.
  • DBINDEX_COL_ORDER_ASC—Ascending

  • DBINDEX_COL_ORDER_DESC—Descending

cPropertySets

[in]
The number of DBPROPSET structures in rgPropertySets. If this is zero, the provider ignores rgPropertySets.

rgPropertySets

[in/out]
An array of DBPROPSET structures containing properties and values to be set. The properties specified in these structures must belong to the Index property group. If the same property is specified more than once in rgPropertySets, then it is provider-specific which value is used. If cPropertySets is zero, this argument is ignored.

For information about the properties in the Index property group that are defined by OLE DB, see "Index Properties" in Appendix C. For information about the DBPROPSET and DBPROP structures, see "DBPROPSET Structure" and "DBPROP Structure" in Chapter 11.

ppIndexID

[out]
A pointer to memory in which to return a pointer to the DBID of a newly created index. If ppIndexID is a null pointer, no DBID is returned.

Return Code

S_OK
The method succeeded and the new index has been created. In all DBPROP structures passed to the method, dwStatus is set to DBPROPSTATUS_OK.

DB_S_ERRORSOCCURRED
The index was created but one or more properties—for which the dwOptions element of the DBPROP structure was DBPROPOPTIONS_OPTIONAL—were not set. The consumer checks dwStatus in the DBPROP structures to determine which properties were not set. The method can fail to set properties for a number of reasons, including:

  • The property was not supported by the provider.

  • The property was not in the Index property group.

  • The property set was not supported by the provider.

  • It was not possible to set the property.

  • The colid in the DBPROP structure was not DB_NULLID.

  • The data type in vValue in the DBPROP structure was not the data type of the property or was not VT_EMPTY.

  • The value in vValue in the DBPROP structure was invalid.

  • The property's value conflicted with an existing property.

DB_E_TABLEINUSE
The specified table was in use and the provider could not create the index with the table open.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
pTableID was a null pointer.

pIndexID and ppIndexID were both null pointers.

cIndexColumnDescs was zero.

rgIndexColumnDescs was a null pointer.

eIndexColOrder in an element of rgIndexColumnDescs was not a valid value.

cPropertySets was not zero and rgPropertySets was a null pointer.

In an element of rgPropertySets, cProperties was not zero and rgProperties was a null pointer.

DB_E_BADCOLUMNID
A column specified in an element of rgIndexColumnDescs did not exist.

DB_E_BADINDEXID
*pIndexID was an invalid index ID.

DB_E_BADTABLEID
*pTableID was an invalid table ID.

DB_E_DUPLICATEINDEXID
The specified index already exists in the current data source object.

DB_E_ERRORSOCCURRED
No index was created because one or more properties—for which the dwOptions element of the DBPROP structure was DBPROPOPTIONS_REQUIRED or an invalid value—were not set. The consumer checks dwStatus in the DBPROP structures to determine which properties were not set. None of the satisfiable properties are remembered. The 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.

DB_E_NOTABLE
The specified table does not exist in the current data source object.

DB_SEC_E_PERMISSIONDENIED
The consumer did not have sufficient permission to create the index.

Comments

If CreateIndex returns any errors, the index is not created, and ppIndexID is set to NULL on output.

See Also

IIndexDefinition::DropIndex, IRowsetIndex, ITableDefinition


IIndexDefinition::DropIndex

Drops an index from the base table.

HRESULT DropIndex(
   DBID *   pTableID,
   DBID *   pIndexID);

Parameters

pTableID

[in]
A pointer to the DBID of the base table.

pIndexID

[in]
A pointer to the DBID of the index to drop. This must be an index on the table specified with pTableID. If pIndexId is a null pointer, all indexes for the table specified with pTableID are dropped.

Return Code

S_OK
The method succeeded and the index has been dropped from the base table.

DB_S_ERRORSOCCURRED
pIndexID was a null pointer, at least one index was successfully dropped, but one or more indexes for the specified table could not be dropped. The consumer can determine the set of indexes not dropped through the INDEXES schema rowset.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
pTableID was a null pointer.

DB_E_BADTABLEID
*pTableID was an invalid table ID.

DB_E_ERRORSOCCURRED
pIndexID was a null pointer, but none of the indexes for the specified table could be dropped.

DB_E_INDEXINUSE
The specified index was in use.

DB_E_NOINDEX
The specified index does not exist in the current data source or did not apply to the specified table.

DB_E_NOTABLE
The specified table does not exist in the current data source.

DB_E_TABLEINUSE
The specified table was in use and the provider could not drop the index with the table open.

DB_SEC_E_PERMISSIONDENIED
The consumer did not have sufficient permission to drop the index.

Comments

For partitioned indexes, a call to this method drops all partitions.

If DropIndex returns any errors, the index is not dropped.

If pIndexID is a null pointer, and one or more indexes for that table cannot be dropped, the processing continues to attempt to drop the remaining indexes for that table.

See Also

IIndexDefinition::CreateIndex


IMultipleResults

IMultipleResults is used to retrieve multiple results (rowsets or row counts) created by a command. For more information, see "Multiple Results" in Chapter 3.

When to Implement

IMultipleResults is a mandatory interface on multiple results objects. Providers specify whether they support multiple results objects and what restrictions they have on these objects through the DBPROP_MULTIPLERESULTS property in the Data Source Information property group.

When to Call

To create a multiple results object, a consumer creates a command, sets the command text, and calls ICommand::Execute with riid set to IID_IMultipleResults. The command text generally specifies multiple results, although this is not required. To retrieve each result in succession, the consumer repeatedly calls IMultipleResults::GetResult.

Method | Description
GetResult | Returns the next in a series of multiple results from the provider.


IMultipleResults::GetResult

Returns the next in a series of multiple results from the provider.

HRESULT GetResult(
   IUnknown *   pUnkOuter,
   LONG         lReserved,
   REFIID         riid,
   LONG *      pcRowsAffected,
   IUnknown **   ppRowset);

Parameters

pUnkOuter

[in]
A pointer to the controlling IUnknown interface if the object is being created as part of an aggregate; otherwise, it is a null pointer.

lReserved

[in]
Reserved. Must be set to zero.

riid

[in]
The requested interface to return in *ppRowset. This interface is conceptually added to the list of required interfaces on the resulting rowset, and the method fails (E_NOINTERFACE) if that interface cannot be supported on the resulting rowset.

If this is IID_NULL, then ppRowset is ignored and no rowset is returned, even if one exists.

pcRowsAffected

[out]
A pointer to memory in which to return the count of rows affected by an update, delete, or insert. If the value of cParamSets passed into ICommand::Execute was greater than 1, then *pcRowsAffected is the total number of rows affected by all of the sets of parameters represented by the current result. If the count of affected rows is not available, *pcRowsAffected is set to DB_COUNTUNAVAILABLE on output. If the result is not a count of rows affected by an update, delete, or insert, *pcRowsAffected is undefined on output. If an error occurs, *pcRowsAffected is set to DB_COUNTUNAVAILABLE. If pcRowsAffected is a null pointer, no count of affected rows is returned.

Some providers do not support returning individual counts of rows, but instead return an overall count of the total rows affected by the call to Execute, or do not return row counts at all. Such providers set *pcRowsAffected to DB_COUNTUNAVAILABLE when the count of affected rows is not available.

ppRowset

[out]
A pointer to memory in which to return the interface for the next result. If the next result is not a rowset (for instance, if it is the count of the rows affected by an update, delete, or insert), this is set to a null pointer. If an error occurs, *ppRowset is set to a null pointer.

If ppRowset is a null pointer, no rowset is created.

Return Code

S_OK
The method succeeded.

DB_S_ERRORSOCCURRED
This can be returned for either of the following reasons:

  • An error occurred, returning one or more output parameter values associated with the next result. To determine which output parameters were not returned, the consumer checks the status values. For a list of status values that can be returned by this method, see "Status Values Used When Getting Data" in "Status" in Chapter 6.

  • The rowset was opened but one or more properties—for which the dwOptions element of the DBPROP structure was DBPROPOPTIONS_OPTIONAL—were not set. The consumer calls IRowsetInfo::GetProperties to determine which properties were set.

DB_S_NORESULT
There are no more results. *ppRowset is set to a null pointer and *pcRowsAffected is set to –1.

DB_S_STOPLIMITREACHED
Execution has been stopped because a resource limit has been reached. The results obtained so far have been returned. Calling GetResult again returns information for the next result, or DB_S_NORESULT if no more results can be obtained, either because they do not exist or because the resource limit applies across multiple results.

This return code takes precedence over DB_S_ERRORSOCCURRED. That is, if the conditions described here and those described in DB_S_ERRORSOCCURRED both occur, the provider returns this code. When the consumer receives this return code, it should also check for the conditions described in DB_S_ERRORSOCCURRED.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
lReserved was not zero.

E_NOINTERFACE
The interface specified in riid was not supported on the rowset.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to create the rowset.

E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called and the object is in a zombie state.

DB_E_ABORTLIMITREACHED
Execution has been aborted because a resource limit has been reached. No results have been returned. Calling GetResult again returns information for the next result, or DB_S_NORESULT if no more results can be obtained, either because they do not exist or because the resource limit applies across multiple results.

DB_E_CANTCONVERTVALUE
A literal value in the command text associated with the next result could not be converted to the type of the associated column for reasons other than data overflow.

DB_E_DATAOVERFLOW
A literal value in the command text associated with the next result overflowed the type specified by the associated column.

DB_E_ERRORSINCOMMAND
The command text associated with the next result contained one or more errors. Providers should use OLE DB error objects to return details about the errors.

DB_E_ERRORSOCCURRED
The method failed due to one or more invalid input parameter values associated with the next result. To determine which input parameter values were invalid, the consumer checks the status values. For a list of status values that can be returned by this method, see "Status Values Used When Setting Data" in "Status" in Chapter 6.

The rowset was not returned because one or more properties—for which the dwOptions element of the DBPROP structure was DBPROPOPTIONS_REQUIRED or an invalid value—could not be satisfied.

DB_E_INTEGRITYVIOLATION
A literal value in the command text associated with the next result violated the integrity constraints for the column.

DB_E_NOAGGREGATION
pUnkOuter was not a null pointer, and the object being created does not support aggregation.

pUnkOuter was non-null and riid was not IID_Unknown.

DB_E_OBJECTOPEN
The previous rowset is still open, and the provider does not support multiple open results simultaneously (DBPROP_MULTIPLERESULTS is DBPROPVAL_MR_SUPPORTED).

DB_SEC_E_PERMISSIONDENIED
The consumer did not have sufficient permission to get the next result.

Comments

GetResult returns the next in a series of multiple results from the provider. If the requested result is a rowset, GetResult returns the requested interface on that rowset. If the requested result is a row count, GetResult returns the count of rows affected in *pcRowsAffected.

Providers will generally be able to process only one result at a time. For maximum interoperability, consumers should free any rowset obtained by a previous call to GetResult before requesting the next result.

Providers may check the entire command text for errors at execute time, or may check the command text associated with each result when that result is retrieved. Therefore, the syntax errors returned by ICommand::Execute can also be returned by GetResult. In this case, the next call to GetResult moves on to the next result, or returns DB_S_NORESULT if no more results are available.

When GetResult returns an error, its behavior depends on the error that occurred:

  • If GetResult returns E_INVALIDARG, DB_E_NOAGGREGATION or DB_E_OBJECTOPEN, the current result is still available. Assuming there are no other errors, the next call to GetResult returns the current result.

  • If GetResult returns any other error, the current result is lost. The next call to GetResult returns either the next result or DB_S_NORESULT if the error caused the provider to lose all remaining results. Providers that can recover the current result in this situation must discard it and return the next result.

The following example shows how a consumer might process multiple results:

hr = pICommandText->Execute(pUnkOuter, IID_IMultipleResults, pParams, &cRowsAffected,
          &pIMultipleResults);

if (pIMultipleResults) {
 while(hr != DB_S_NORESULT) {
  if(SUCCEEDED(hr = pIMultipleResults->GetResult(pUnkOuter, 0, IID_IRowset,
                   &cRowsAffected, &pIRowset))) {
   if(pIRowset) {
    // The next result is a rowset. Process the rowset.
    pIRowset->Release();
   } else {
    // The next result is not a rowset. Process the nonrowset result.
   }
  } else {
   // Process error from GetResult.
   break;
  }
 }
 pIMultipleResults->Release();
}

See Also

ICommand::Execute


IOpenRowset

IOpenRowset is a required interface on the session. It can be supported by providers that do not support creating rowsets through commands.

The IOpenRowset model enables consumers to open and work directly with individual tables or indexes in a data source by using IOpenRowset::OpenRowset, which generates a rowset of all rows in the table or index.

Method | Description
OpenRowset | Opens and returns a rowset that includes all rows from a single base table or index.


IOpenRowset::OpenRowset

Opens and returns a rowset that includes all rows from a single base table or index.

HRESULT OpenRowset(
   IUnknown *      pUnkOuter,
   DBID *            pTableID,
   DBID *            pIndexID,
   REFIID            riid,
   ULONG         cPropertySets,
   DBPROPSET   rgPropertySets[],
   IUnknown **      ppRowset);

Parameters

pUnkOuter

[in]
The controlling IUnknown if the rowset is to be aggregated, otherwise a null pointer.

pTableID

[in]
The DBID of the table to open. For more information, see the "Comments" section.

pIndexID

[in]
The DBID of the index to open. For more information, see the "Comments" section.

riid

[in]
The IID of the interface to return in *ppRowset. This interface is conceptually added to the list of required interfaces on the resulting rowset, and the method fails (E_NOINTERFACE) if that interface cannot be supported on the resulting rowset. This must be an interface that the rowset supports, even when ppRowset is set to a null pointer and no rowset is created.

cPropertySets

[in]
The number of DBPROPSET structures in rgPropertySets. If this is zero, the provider ignores rgPropertySets.

rgPropertySets

[in/out]
An array of DBPROPSET structures containing properties and values to be set. The properties specified in these structures must belong to the Rowset property group. If the same property is specified more than once in rgPropertySets, then it is provider-specific which value is used. If cPropertySets is zero, this argument is ignored.

For information about the properties in the Rowset property group that are defined by OLE DB, see "Rowset Properties" in Appendix C. For information about the DBPROPSET and DBPROP structures, see "DBPROPSET Structure" and "DBPROP Structure" in Chapter 11.

ppRowset

[in/out]
A pointer to memory in which to return the interface pointer to the created rowset. If ppRowset is a null pointer, no rowset is created; properties are verified and if a required property cannot be set, DB_E_ERRORSOCCURRED is returned. If OpenRowset fails, *ppRowset is set to a null pointer.

Return Code

S_OK
The method succeeded and the rowset is opened. In all DBPROP structures passed to the method, dwStatus is set to DBPROPSTATUS_OK.

DB_S_ASYNCHRONOUS
The method has initiated asynchronous creation of the rowset. The consumer can call IDBAsynchStatus to poll for status or IConnectionPointContainer to obtain the IID_IDBAsynchNotify connection point. Attempting to call any other interfaces may fail, and the full set of interfaces may not be available on the object until asynchronous initialization of the rowset has completed.

DB_S_ERRORSOCCURRED
The rowset was opened but one or more properties—for which the dwOptions element of the DBPROP structure was DBPROPOPTIONS_OPTIONAL—were not set. The consumer checks dwStatus in the DBPROP structures to determine which properties were not set. The method can fail to set properties for a number of reasons, including:

  • colid in the DBPROP structure was invalid.

  • The data type in vValue in the DBPROP structure was not the data type of the property or was not VT_EMPTY.

  • The value in vValue in the DBPROP structure was invalid.

  • The property's value conflicted with an existing property.

  • A property was specified to be applied to all columns, but could not be applied to one or more columns.

  • The property was not supported by the provider.

  • It was not possible to set the property.

DB_S_STOPLIMITREACHED
Execution has been stopped because a resource limit has been reached. The results obtained so far have been returned. This return code takes precedence over DB_S_ERRORSOCCURRED; that is, if the conditions descrbed here and in those described in DB_S_ERRORSOCCURRED both occur, the provider returns this code. When the consumer receives this return code, it should also check for the conditions described in DB_S_ERRORSOCCURRED.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
pTableID and pIndexID were both null pointers.

cPropertySets was not zero and prgPropertySets was a null pointer.

In an element of rgPropertySets, cProperties was not zero and rgProperties was a null pointer.

E_NOINTERFACE
The rowset did not support the interface specified in riid or riid was IID_NULL.

DB_E_ERRORSOCCURRED
No rowset was returned because one or more properties—for which the dwOptions element of the DBPROP structure was DBPROPOPTIONS_REQUIRED or an invalid value—were not set. The consumer checks dwStatus in the DBPROP structures to determine which properties were not set. None of the satisfiable properties are remembered. The 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.

DB_E_ABORTLIMITREACHED
The method failed because a resource limit has been reached. For example, a query used to implement the method timed out. No rowset is returned.

DB_E_NOAGGREGATION
pUnkOuter was not a null pointer and the rowset being created does not support aggregation.

pUnkOuter was non-null and riid was not IID_Unknown.

DB_E_NOINDEX
The specified index does not exist in the current data source or did not apply to the specified table.

The provider does not support opening indexes through IOpenRowset.

DB_E_NOTABLE
The specified table does not exist in the current data source.

DB_E_PARAMNOTOPTIONAL
The table specified by pTableID is a procedure that requires one or more parameters.

DB_SEC_E_PERMISSIONDENIED
The consumer did not have sufficient permission to open the rowset. For example, a rowset included a column for which the consumer does not have read permission.

DB_E_OBJECTOPEN
The provider would have to open a new connection to support the operation and DBPROP_MULTIPLECONNECTIONS is set to VARIANT_FALSE.

Comments

If the table or index has no rows, the rowset is still created. The resulting rowset is fully functional and can be used, for example, to insert new rows or determine column metadata.

pTableID and pIndexID are used in the following combinations:

  • If pTableID is not a null pointer and pIndexID is a null pointer, the table identified by *pTableID is opened.

  • If pTableID is a null pointer and pIndexID is not a null pointer, *pIndexID must uniquely and fully identify an index; this index is opened. If *pIndexID does not uniquely and fully identify an index, DB_E_NOINDEX is returned.

  • If neither pTableID nor pIndexID is a null pointer, *pIndexID must identify an index for the table identified by *pTableID; this index is opened. If *pIndexID does not identify an index for the table identified by *pTableID, DB_E_NOINDEX is returned.

  • If both pTableID and pIndexID are null pointers, E_INVALIDARG is returned.

Consumers must supply fully qualified names as pTableID on providers that support catalog or schema names. For more information, see "Fully Qualified Names," Chapter 4.

The threading model of the returned rowset is determined by the property DBPROP_ROWTHREADMODEL.

See Also

IDBCreateCommand::CreateCommand


IParentRowset

IParentRowset is used to retrieve child rowsets from a hierarchical rowset. For more information, see "Hierarchical Rowsets" in Chapter 4.

When to Implement

IParentRowset is a mandatory interface on a hierarchical rowset.

When to Call

The consumer calls IParentRowset::GetChildRowset in order to create an aggregated instance of the child rowset corresponding to a chapter-valued column in the parent rowset.

Method | Description
GetChildRowset | Returns the child rowset corresponding to a chapter-valued column in the parent rowset.

IParentRowset::GetChildRowset

Returns the child rowset corresponding to a chapter-valued column in the parent rowset.

HRESULT GetChildRowset(
   IUnknown *      pUnkOuter,
   ULONG            iOrdinal,
   REFIID         riid,
   IUnknown **   ppRowset);

Parameters

pUnkOuter

[in]
A pointer to the controlling IUnknown interface if the object is being created as part of an aggregate; otherwise, it is a null pointer.

iOrdinal

[in]
The ordinal of the chapter-valued column in the parent rowset.

riid

[in]
The requested interface to return in *ppRowset.

ppRowset

[out]
A pointer to memory in which to return the interface for the child rowset. If an error occurs, *ppRowset is set to a null pointer.

Return Value

S_OK
The method succeeded.

E_INVALIDARG
ppRowset was a null pointer.

E_NOINTERFACE
The interface specified in riid was not supported on the rowset.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to create the rowset.

E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called and the object is in a zombie state.

DB_E_NOAGGREGATION
pUnkOuter was not a null pointer, and riid was not IID_IUnknown.

DB_E_OBJECTOPEN

The referenced rowset has already been opened.

The provider is single-chaptered, and a child rowset is still open.

DB_E_BADORDINAL
The column specified by iOrdinal was not a chapter-valued column, or did not exist.

Comments

GetChildRowset can be called only once for each child rowset and, if called, must be called before any calls to GetReferencedRowset for that child. The pUnkOuter argument specified in GetChildRowset defines the controlling unknown for all references to that child rowset for the lifetime of the parent. The parent rowset must use addref on this controlling unknown to guarantee that it doesn’t go away before the parent. Even though the controlling unknown is constant for the lifetime of the parent, the consumer must not assume that rowset state, such as accessors, row position, and so on, are held when all references to the rowset are released. Furthermore, providers must ensure that the next fetch position within the child rowset is reset once all references to that child rowset have been released. Calling GetChildRowset for a child rowset that has already been obtained returns DB_E_OBJECTOPEN.

See Also

IRowsetInfo::GetReferencedRowset


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.


IRowset::AddRefRows

Adds a reference count to an existing row handle.

HRESULT AddRefRows(
   ULONG            cRows,
   const HROW         rghRows[],
   ULONG            rgRefCounts[],
   DBROWSTATUS   rgRowStatus[]);

Parameters

cRows

[in]
The number of rows for which to increment the reference count.

rghRows

[in]
An array of row handles for which to increment the reference count. The reference count of row handles is incremented by one for each time they appear in the array.

rgRefCounts

[out]
An array with cRows elements in which to return the new reference count for each row handle. The consumer allocates memory for this array. If rgRefCounts is a null pointer, no reference counts are returned.

rgRowStatus

[out]
An array with cRows elements in which to return values indicating the status of each row specified in rghRows. If no errors occur while incrementing the reference count of a row, the corresponding element of rgRowStatus is set to DBROWSTATUS_S_OK. If an error occurs while incrementing the reference count of a row, the corresponding element is set as specified in DB_S_ERRORSOCCURRED. The consumer allocates memory for this array. If rgRowStatus is a null pointer, no row statuses are returned. For information about the DBROWSTATUS enumerated type, see "Arrays of Errors" in Chapter 13.

Return Code

S_OK
The method succeeded. The reference count of all rows was successfully incremented. The following value can be returned in *prgRowStatus:

  • The reference count of the row was successfully incremented. The corresponding element of *prgRowStatus contains DBROWSTATUS_S_OK.

DB_S_ERRORSOCCURRED
An error occurred while incrementing the reference count of a row, but the reference count of at least one row was incremented. Successes can occur for the reason listed under S_OK. The following errors can occur:

  • A row handle was invalid. The reference count of the row was not incremented and the corresponding element of rgRowStatus contains DBROWSTATUS_E_INVALID.

  • A row handle referred to a row that had a reference count of zero. The reference count of the row was not incremented and the corresponding element of rgRowStatus contains DBROWSTATUS_E_INVALID.

  • A row handle referred to a row for which a deletion had been transmitted to the data source. The reference count of the row was not incremented and the corresponding element of rgRowStatus contains DBROWSTATUS_E_DELETED.

  • The consumer encountered a recoverable, provider-specific error, such as an RPC failure when transmitting the change to a remote server. The corresponding element of rgRowStatus contains DBROWSTATUS_E_FAIL.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
rghRows was a null pointer and cRows was not zero.

E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called and the object is in a zombie state.

DB_E_ERRORSOCCURRED
Errors occurred while incrementing the reference count of all of the rows. Errors can occur for the reasons listed under DB_S_ERRORSOCCURRED.

Comments

AddRefRows must be supported for implementing multiple references to the same row even if the rowset does not support IRowsetIdentity.

It is always possible for a consumer to call AddRefRows while it is processing a method in IRowsetNotify. That is, the rowset must be reentrant through AddRefRows during notification.

If AddRefRows encounters an error while incrementing the reference count of a row, it sets the corresponding element in rgRowStatus to the appropriate DBROWSTATUS value and continues processing.

If a row handle is duplicated in rghRows, the corresponding row will have its reference count incremented by 1 for each time it appears in the array.

See Also

IRowset::ReleaseRows


IRowset::GetData

Retrieves data from the rowset's copy of the row.

HRESULT GetData (
   HROW            hRow,
   HACCESSOR   hAccessor,
   void *            pData);

Parameters

hRow

[in]
The handle of the row from which to get the data.

Caution The consumer must ensure that hRow contains a valid row handle; the provider might not validate hRow before using it. The result of passing the handle of a deleted row is provider-specific, although the provider cannot terminate abnormally. For example, the provider might return DB_E_BADROWHANDLE, DB_E_DELETEDROW, or it might get data from a different row. The result of passing an invalid row handle in hRow is undefined.

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 GetData does not get any data values.

Caution The consumer must ensure that hAccessor contains a valid accessor handle; the provider might not validate hAccessor before using it. The result of passing an invalid accessor handle in hAccessor is undefined.

pData

[out]
A pointer to a buffer in which to return the data. The consumer allocates memory for this buffer. This pointer must be a valid pointer to a contiguous block of consumer-owned memory into which the data will be written.

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. To determine the columns for which data was returned, the consumer checks the status values. For a list of status values that can be returned by this method, see "Status Values Used When Getting Data" in "Status" in Chapter 6.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
pData was a null pointer and the accessor 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. Providers are not required to check for this condition, because doing so might slow the method significantly.

DB_E_BADACCESSORTYPE
The specified accessor was not a row accessor.

DB_E_BADROWHANDLE
hRow was invalid. Providers are not required to check for this condition, because doing so might slow the method significantly.

DB_E_DELETEDROW
hRow referred to a pending delete row or a row for which a deletion had been transmitted to the data source. Providers are not required to check for this condition, because doing so might slow the method significantly.

DB_E_ERRORSOCCURRED
Errors occurred while returning data for all columns. To determine what errors occurred, the consumer checks the status values. For a list of status values that can be returned by this method, see "Status Values Used When Getting Data" in "Status" in Chapter 6.

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 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 GetData to retrieve data from rows that have been fetched by prior calls to methods such as GetNextRows. For a complete description of how GetData retrieves data, see "Getting Data" in Chapter 6.

A consumer can call GetData any number of times. In each call, it can pass a different accessor and the address of a different buffer. This means that the consumer can get as many copies of the data as it wants, and it can get data in different types if alternate conversions are available.

GetData does not enforce any security restrictions. The provider must not create a rowset that includes columns for which the consumer does not have read privileges, so GetData never encounters problems accessing the data for a column. The rowset can contain columns to which the consumer does not have write permission if DBPROP_COLUMNRESTRICT is VARIANT_TRUE. The methods that fetch rows must not return the handles of rows for which the consumer does not have read privileges, so GetData never encounters problems accessing a row. Such rows might exist if the DBPROP_ROWRESTRICT property is VARIANT_TRUE.

If GetData fails, the memory to which pData points is not freed but its contents are undefined. If, before GetData failed, the provider allocated any memory for return to the consumer, the provider frees this memory and does not return it to the consumer.

GetData must be reentrant during notifications. If the provider calls a method from IRowsetNotify in the consumer, the consumer must be able to call GetData while processing the notification method.

The following example shows how a reference accessor is used.

#include <oledb.h>
#include <stddef.h>

IRowset *TheRowset;
IAccessor *TheRowsetAccessor;
HROW   hRow;

int main() {
 struct ExactlyTheSame {
  long l;
  double d;
  short  i;
 };
 HACCESSOR hRawAccess;

 static DBBINDING ExactBindings [3] = {
  {
   1,         // iOrdinal
   offsetof (ExactlyTheSame,l), // obValue
   0,         // No length binding
   0,         // No Status binding
   NULL,        // No TypeInfo
   NULL,        // No Object
   NULL,        // No Extensions
   DBPART_VALUE,
   DBMEMOWNER_PROVIDEROWNED,  // Ignored
   DBPARAMIO_NOTPARAM,
   sizeof (long),
   0,
   DBTYPE_I4,
   0,         // No Precision
   0          // No Scale
  },
  {
   2,         // iOrdinal
   offsetof (ExactlyTheSame, d), // obValue
   0,          // No length binding
   0,          // No Status binding
   NULL,         // No TypeInfo
   NULL,         // No Object
   NULL,         // No Extensions
   DBPART_VALUE,
   DBMEMOWNER_PROVIDEROWNED,   // Ignored
   DBPARAMIO_NOTPARAM,
   sizeof (double),
   0,
   DBTYPE_R8,
   0,          // No Precision
   0           // No Scale
  },
  {
   3,         // iOrdinal
   offsetof (ExactlyTheSame,i), // obValue
   0,         // No length binding
   0,         // No Status binding
   NULL,        // No TypeInfo
   NULL,        // No Object
   NULL,        // No Extensions
   DBPART_VALUE,
   DBMEMOWNER_PROVIDEROWNED,  // Ignored
   DBPARAMIO_NOTPARAM,
   sizeof (short),
   0,
   DBTYPE_I2,
   0,         // No Precision
   0          // No Scale
  }
 };

 TheRowsetAccessor->CreateAccessor (DBACCESSOR_PASSBYREF, 3, ExactBindings, 0,
              &hRawAccess, NULL);

To read the column i of some row, the consumer should do the following:

short     value;
ExactlyTheSame *pRow;

TheRowset->GetData(hRow, hRawAccess, &pRow);

value = pRow->i;

The following example shows how provider-owned memory is used.

#include <oledb.h>
#include <stddef.h>

IRowset *TheRowset;
IAccessor *TheRowsetAccessor;
HROW   hRow;

int main() {
 struct IndirectlySimilar {
  long * pl;
  double * pd;
  short * pi;
 };
 HACCESSOR hFastAccess;

 static DBBINDING IndirectBindings [3] = {
  {
   1,            // iOrdinal
   offsetof (IndirectlySimilar, pl), // obValue
   0,            // No length binding
   0,            // No Status binding
   NULL,           // No TypeInfo
   NULL,           // No Object
   NULL,           // No Extensions
   DBPART_VALUE,
   DBMEMOWNER_PROVIDEROWNED,
   DBPARAMIO_NOTPARAM,
   sizeof (long*),
   0,
   DBTYPE_BYREF|DBTYPE_I4,
   0,            // No Precision
   0           // No Scale
  },
  {
   2,            // iOrdinal
   offsetof (IndirectlySimilar, pd), // obValue
   0,            // No length binding
   0,            // No Status binding
   NULL,           // No TypeInfo
   NULL,           // No Object
   NULL,           // No Extensions
   DBPART_VALUE,
   DBMEMOWNER_PROVIDEROWNED,
   DBPARAMIO_NOTPARAM,
   sizeof (double*),
   0,
   DBTYPE_BYREF|DBTYPE_R8,
   0,            // No Precision
   0           // No Scale
  },
  {
   3,           // iOrdinal
   offsetof (IndirectlySimilar,pi), // obValue
   0,           // No length binding
   0,           // No Status binding
   NULL,          // No TypeInfo
   NULL,          // No Object
   NULL,          // No Extensions
   DBPART_VALUE,
   DBMEMOWNER_PROVIDEROWNED,
   DBPARAMIO_NOTPARAM,
   sizeof(short*),
   0,
   DBTYPE_BYREF|DBTYPE_I2,
   0,           // No Precision
   0            // No Scale
  }
 };

 TheRowsetAccessor->CreateAccessor (DBACCESSOR_ROWDATA, 3, IndirectBindings, 0,
              &hFastAccess, NULL );

To read the column i of some row, the consumer should do the following:

short        value;
IndirectlySimilar   rowPs;

TheRowset->GetData (hRow, hFastAccess, &rowPs);

if (rowPs.pi)    // Avoid null pointers
 value = *(rowPs.pi);

See Also

IRowset::GetNextRows, IRowsetChange::SetData


IRowset::GetNextRows

Fetches rows sequentially, remembering the previous position.

HRESULT GetNextRows (
   HCHAPTER   hChapter,
   LONG         lRowsOffset,
   LONG         cRows,
   ULONG *      pcRowsObtained,
   HROW **      prghRows);

Parameters

hChapter

[in]
The chapter handle. For nonchaptered rowsets, hChapter is ignored.

lRowsOffset

[in]
The signed count of rows to skip before fetching rows. Deleted rows that the provider has removed from the rowset are not counted in the skip. If this value is zero and cRows continues in the same direction as the previous call to either GetNextRows, or FindNextRow with a null pBookmark value, then the first row fetched will be the next row after the last one fetched in the previous call. If this value is zero and cRows reverses direction, then the first row fetched will be the last one fetched in the previous call.

lRowsOffset can be a negative number only if the value of the DBPROP_CANSCROLLBACKWARDS property is VARIANT_TRUE.

There is no guarantee that skipping rows is done efficiently on a sequential rowset. If the data source resides on a remote server, there may be remote support for skipping without transferring the intervening records across the network, but this is not guaranteed. For information about how the provider implements skipping, see the documentation for the provider.

cRows

[in]
The number of rows to fetch. A negative number means to fetch backward. cRows can be a negative number only if the value of the DBPROP_CANFETCHBACKWARDS property is VARIANT_TRUE.

If cRows is zero, no rows are fetched; the fetch direction and the next fetch position are unchanged, and the provider performs no processing, returning immediately from the method invocation. Specifically, lRowsOffset is ignored in this situation.

If the provider does not discover any other errors, the method returns S_OK; whether the provider checks for any other errors is provider-specific.

pcRowsObtained

[out]
A pointer to memory in which to return the actual number of fetched rows. If a warning condition occurs, this number may be less than the number of rows available or requested, and is the number of rows actually fetched before the warning condition occurred. If the consumer has insufficient permission to fetch all rows, GetNextRows fetches all rows for which the consumer has sufficient permission and skips all other rows. If the method fails, *pcRowsObtained is
set to zero.

prghRows

[out]
A pointer to memory in which to return an array of handles of the fetched rows. If *prghRows is not a null pointer on input, it must be a pointer to memory large enough to return the handles of the requested number of rows. If *prghRows is a null pointer on input, the rowset allocates memory for the row handles and returns the address to this memory; the consumer releases this memory with IMalloc::Free after it releases the row handles. If *prghRows is a null pointer on input and *pcRowsObtained is zero on output or if the method fails, the provider does not allocate any memory and ensures that *prghRows is a null pointer on output.

Return Code

S_OK
The method succeeded.

DB_S_ENDOFROWSET
GetNextRows reached the start or the end of the rowset or chapter or the start or end of the range on an index rowset and could not fetch all requested rows because the count extended beyond the end. The next fetch position is before the start or after the end of the rowset. The number of rows actually fetched is returned in *pcRowsObtained; this will be less than cRows.

The rowset is being populated asynchronously and no additional rows are available at this time. To determine whether additional rows may be available, the consumer should call IDBAsynchStatus::GetStatus or listen for the IDBAsynchNotify::OnStop notification.

lRowsOffset indicated a position either more than one row before the first row of the rowset or more than one row after the last row, and the provider was a version 2.0 or greater provider. *pcRowsObtained is set to zero and no rows are returned.

DB_S_ROWLIMITEXCEEDED
Fetching the number of rows specified in cRows would have exceeded the total number of active rows supported by the rowset. The number of rows that were actually fetched is returned in *pcRowsObtained. This condition can occur only when there are more rows available than can be handled by the rowset. Thus, this condition never conflicts with those described in DB_S_ENDOFROWSET and DB_S_STOPLIMITREACHED, both of which imply that no more rows were available.

DB_S_STOPLIMITREACHED
Fetching rows required further execution of the command, such as when the rowset uses a server-side cursor. Execution has been stopped because a resource limit has been reached. The number of rows that were actually fetched is returned in *pcRowsObtained.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
pcRowsObtained or prghRows was a null pointer.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory to complete the request.

E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called and the object is in a zombie state.

DB_E_BADCHAPTER
The rowset was chaptered and hChapter was invalid.

The rowset was single-chaptered and the specified chapter was not the currently open chapter. The consumer must use the currently open chapter or release the currently open chapter before specifying a new chapter.

DB_E_BADSTARTPOSITION
lRowsOffset indicated a position either more than one row before the first row of the rowset or more than one row after the last row, and the provider was a 1.x provider,

DB_E_CANCELED
Fetching rows was canceled during notification. No rows were fetched.

DB_E_CANTFETCHBACKWARDS
cRows was negative and the rowset cannot fetch backward.

DB_E_CANTSCROLLBACKWARDS
lRowsOffset was negative and the rowset cannot scroll backward.

DB_E_NOTREENTRANT
The consumer called this method while it was processing a notification, and it is an error to call this method while processing the specified DBREASON value.

DB_E_ROWSNOTRELEASED
The provider requires release of existing rows before new ones can be fetched. For more information, see DBPROP_CANHOLDROWS in "Rowset Properties" in Appendix C.

DB_SEC_E_PERMISSIONDENIED
The consumer did not have sufficient permission to fetch any of the rows; no rows were fetched.

Comments

GetNextRows fetches a sequence of rows. The provider maintains a next fetch position that is used in subsequent calls to either GetNextRows or FindNextRow. The next fetch position is changed either by calling GetNextRows, or by calling FindNextRow with a null pBookmark value. Calling FindNextRow with a non-null pBookmark value has no effect on the next fetch position. If the fetch direction is reversed from the previous call, then the next fetch position in the new direction is the last row that was fetched in the previous direction, otherwise the next fetch position is the row following the last row fetched by GetNextRows, or by FindNextRow with a null pBookmark value.

For a newly created rowset, or any time the next fetch position is prior to the first row of the rowset, the next fetch position is computed as follows, where N is the number of rows in the rowset. lRowsOffset can be less than zero only if DBPROP_CANSCROLLBACKWARDS is VARIANT_TRUE.

Value of lRowsOffset | Next fetch position
lRowsOffset > 0 | After lRowsOffset
lRowsOffset < 0 | After N – abs(lRowsOffset)
lRowsOffset = 0 | Before first row if cRows > 0, after last row if cRows < 0

For example:

GetNextRows call Row fetched New next fetch position
GetNextRows(hReserved, 2, 1,
    pcRowsObtained,
    prghRows);
3rd row After 3rd row
GetNextRows(hReserved, 2, -1,
    pcRowsObtained,
    prghRows);
2nd row After 1st row
GetNextRows(hReserved, -2, 1,
    pcRowsObtained,
    prghRows);
N – 1st row After N – 1st row
 GetNextRows(hReserved, -2, -1,
    pcRowsObtained,
    prghRows);
N – 2nd row After N – 3rd row

None of the other methods that fetch rows, except for IRowsetFind::FindNextRow with a null pBookmark value, has any effect on the next fetch position. However, IRowsetIndex::Seek sets the next fetch position to the row specified in the seek criteria, and RestartPosition resets the next fetch position to the same position as when the rowset is first created.

GetNextRows increments the reference count of each row for which it returns a handle by 1. Thus, if a handle is returned for a row that has already been fetched, the reference count of that row will be greater than 1. ReleaseRows must be called once for each time the handle to a row has been returned.

If the provider encounters a problem fetching a row—for example, data stored in a text file contains a letter in a numeric column—GetNextRows fetches the row normally, returns the row handle, and returns S_OK. However, when the consumer calls GetData for the row, the provider returns DBSTATUS_E_CANTCONVERTVALUE as the status for the offending column.

GetNextRows must always check for the conditions that cause E_INVALIDARG, E_UNEXPECTED, DB_E_CANTFETCHBACKWARDS, DB_E_CANTSCROLLBACKWARDS, DB_E_NOTREENTRANT, and DB_E_ROWSNOTRELEASED before changing the next fetch position. If it returns any other error besides these, the next fetch position is unknown. For example, the provider might have to perform actions that change the next fetch position in order to determine that the error DB_E_BADSTARTPOSITION occurred. When the next fetch position is unknown, the consumer generally calls RestartPosition to return it to a known position.

For information about what GetNextRows does when it fetches a row that it already has in its internal buffers, see "Uniqueness of Rows in the Rowset" in Chapter 4. For information about whether GetNextRows can detect changes made to rows in the rowset, see "Visibility of Changes" in Chapter 5.

See Also

IRowset::GetData, IRowsetLocate::GetRowsAt, IRowsetLocate::GetRowsByBookmark, IRowsetScroll::GetRowsAtRatio


IRowset::ReleaseRows

Releases rows.

HRESULT ReleaseRows (
   ULONG               cRows,
   const HROW            rghRows[],
   DBROWOPTIONS   rgRowOptions[]
   ULONG               rgRefCounts[],
   DBROWSTATUS      rgRowStatus[]);

Parameters

cRows

[in]
The number of rows to release. If cRows is zero, ReleaseRows does not do anything.

rghRows

[in]
An array of handles of the rows to be released. The row handles need not form a logical cluster; they may have been obtained at separate times and need not be for contiguous underlying rows. They must belong to the current thread. Row handles are decremented by one reference count for each time they appear in the array.

rgRowOptions

[in]
An array of cRows elements containing bitmasks indicating additional options to be specified when releasing a row. This parameter is reserved for future use and should be set to a null pointer.

typedef DWORD DBROWOPTIONS;

rgRefCounts

[out]
An array with cRows elements in which to return the new reference count of each row. If rgRefCounts is a null pointer, no counts are returned. The consumer allocates, but is not required to initialize, memory for this array and passes the address of this memory to the provider. The provider returns the reference counts in the array.

rgRowStatus

[out]
An array with cRows elements in which to return values indicating the status of each row specified in rghRows. If no errors or warnings occur while releasing a row, the corresponding element of rgRowStatus is set to DBROWSTATUS_S_OK. If an error or warning occurs while releasing a row, the corresponding element is set as specified in DB_S_ERRORSOCCURRED. The consumer allocates memory for this array. If rgRowStatus is a null pointer, no row statuses are returned. For information about the DBROWSTATUS enumerated type, see "Arrays of Errors" in Chapter 13.

Return Code

S_OK
The method succeeded. All rows were successfully released. The following values can be returned in *prgRowStatus:

  • The row was successfully released. The corresponding element of *prgRowStatus contains DBROWSTATUS_S_OK.

  • A row had a pending change. The row was released and the corresponding element of rgRowStatus contains DBROWSTATUS_S_PENDINGCHANGES.

DB_S_ERRORSOCCURRED
An error occurred while releasing a row, but at least one row was successfully released. Successes and warnings can occur for the reasons listed under S_OK. The following errors can occur:

  • A row handle was invalid. The row was not released and the corresponding element of rgRowStatus contains DBROWSTATUS_E_INVALID.

  • The consumer encountered a recoverable, provider-specific error, such as an RPC failure when transmitting the change to a remote server. The corresponding element of rgRowStatus contains DBROWSTATUS_E_FAIL.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
rghRows was a null pointer and cRows was not equal to zero.

DB_E_ERRORSOCCURRED
Errors occurred while releasing all of the rows. Errors can occur for the reasons listed under DB_S_ERRORSOCCURRED.

DB_E_NOTREENTRANT
The consumer called this method while it was processing a notification, and it is an error to call this method while processing the specified DBREASON value.

Comments

ReleaseRows decreases the reference count on the specified rows. It must be called once for each time that a row was fetched. For example, if the row was fetched three times, ReleaseRows must be called three times. Furthermore, if a row handle is duplicated in rghRows, the corresponding row will have its reference count decremented by one for each time it appears in the array.

If a provider doesn't support exact reference counts on rows, it should return a reference count of 1 while the row is active. Consumers should be aware of this behavior and should use the returned reference count for debugging purposes only; consumers should not rely on the returned reference count to indicate whether the row would survive another release. In this case, a provider may choose to return S_OK for any and all calls to ReleaseRows until the rowset itself is released.

If a consumer calls ReleaseRows on a row with pending changes, the row remains valid and ReleaseRows returns DBROWSTATUS_S_PENDINGCHANGES in rgRowStatus.

When the reference count for a row decreases to zero, the row is released:

  • Subject to the rules of the current transaction, the rowset is free to discard any resources used by a row that has a reference count of zero. For example, these might include memory, locks, and original values. When the rowset actually discards these resources is provider-specific.

  • If the row has pending changes, the row still remains valid even though its reference count is zero. Consumers should not use the handle of a row that has a reference count of zero, even though the handle might still be valid. If IRowsetUpdate::Update is called to transmit pending changes for a row with a reference count of zero to the data source, it transmits the changes of the row and releases the row and its resources if the update succeeds. If IRowsetUpdate::Undo is called to undo the pending changes for a row with a reference count of zero, it releases the row and its resources.

  • Pending changes are not lost when releasing a reference count obtained from GetRowsAt. A provider may take its own reference count on the row handle or store the row internally until pending changes are committed or dismissed (using Update or Undo).

After a row is released, methods called with the handle to that row return DB_E_BADROWHANDLE if the row has pending changes. After the pending changes are transmitted to the data source, methods might continue to return this error. However, the provider might have an implementation that recycles row handles and thereafter cannot detect the misuse. Because provider behavior varies, consumers should not use the handles of released rows.

If ReleaseRows encounters an error while decrementing the reference count of a row or releasing the row, it sets the corresponding element in rgRowStatus to the appropriate DBROWSTATUS value and continues processing.

This method can be called while the rowset is in a zombie state to allow the consumer to clean up after a transaction has been committed or aborted.

See Also

IRowset::AddRefRows


IRowset::RestartPosition

Repositions the next fetch position used by GetNextRows or FindNextRow to its initial position; that is, its position when the rowset was first created.

HRESULT RestartPosition (
   HCHAPTER   hChapter);

Parameters

hChapter

[in]
The chapter handle. For nonchaptered rowsets, hChapter is ignored.

Return Code

S_OK
The method succeeded. The provider did not have to reexecute the command, either because the rowset supports positioning on the first row without reexecuting the command or because the rowset is already positioned on the first row.

DB_S_COLUMNSCHANGED
The order of the columns was not specified in the object that created the rowset. The provider had to reexecute the command to reposition the next fetch position to its initial position, and the order of the columns changed.

The provider had to reexecute the command to reposition the next fetch position to its initial position, and columns were added or removed from the rowset. This is generally due to a change in the underlying schema and is extremely uncommon.

This return code takes precedence over DB_S_COMMANDREEXECUTED. That is, if the conditions described here and in those described in DB_S_COMMANDREEXECUTED both occur, the provider returns this code. A change to the columns generally implies that the command was reexecuted.

DB_S_COMMANDREEXECUTED
The command associated with this rowset was reexecuted. If the properties DBPROP_OWNINSERT and DBPROP_OWNUPDATEDELETE are VARIANT_TRUE, then the consumer will see its own changes. If the properties DBPROP_OWNINSERT or DBPROP_OWNUPDATEDELETE are VARIANT_FALSE, then the rowset may see its changes. The order of the columns remains unchanged.

E_FAIL
A provider-specific error occurred.

E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called and the object is in a zombie state.

DB_E_BADCHAPTER
The rowset was chaptered and hChapter was invalid.

The rowset was single-chaptered and the specified chapter was not the currently open chapter. The consumer must use the currently open chapter or release the currently open chapter before specifying a new chapter.

DB_E_CANCELED
RestartPosition was canceled during notification. The next fetch position remains unmodified.

DB_E_CANNOTRESTART
The rowset was built over a live data stream (for example, a stock feed) and the position cannot be restarted.

DB_E_NOTREENTRANT
The provider called a method from IRowsetNotify in the consumer and the method has not yet returned.

DB_E_ROWSNOTRELEASED
The provider requires release of existing rows before restarting because the Rowset will be regenerated. This may be required even if the provider supports a value of VARIANT_TRUE for DBPROP_CANHOLDROWS. For more information, see DBPROP_CANHOLDROWS and DPPROP_QUICKRESTART in "Rowset Properties" in Appendix C.

DB_SEC_E_PERMISSIONDENIED
The consumer did not have sufficient permission to reposition the next fetch position.

Comments

For information about the next fetch position when the rowset is first created, see GetNextRows.

If the underlying command contains output parameters, RestartPosition should not reset those parameters.

If the rowset was generated as a result of a procedure call, and the rowset is forward-only, the procedure may be reexecuted in order to satisfy the call to RestartPosition. This may cause other side effects. Additionally, if the stored procedure has been changed, the rowset may have a different schema. If the rowset is able to restart the next fetch position without reexecuting the procedure, RestartPosition should not reexecute it.

How expensive RestartPosition is depends on the provider, the rowset characteristics, and the tables underlying the rowset. If the rowset supports IRowsetLocate, then RestartPosition is always an inexpensive operation.

If the rowset is sequential, then RestartPosition might require reexecution of the underlying command. For some providers, this is always the case. For other providers, a rule of thumb is that rowsets built from a single table are not expensive to restart, but rowsets built by joining two or more tables are expensive to restart. If the provider reexecutes the command to restart the next fetch position, then the new rowset might return a different set of rows, differently ordered columns, and, in extreme cases, a different set of columns. Reexecution of a command by RestartPosition does not reinherit accessors.

A provider that reports DBPROP_QUICKRESTART as VARIANT_FALSE may require that all existing row handles are released prior to successfully processing a call to RestartPosition.

A consumer can determine whether a provider can quickly restart the next fetch position by attempting to set DBPROP_QUICKRESTART to VARIANT_TRUE. Setting this property to VARIANT_TRUE does not guarantee that the rowset can be quickly restarted because the provider is not required to honor the property. This behavior is necessary because the provider cannot evaluate the command at the time the property is set. For example, the consumer can set DBPROP_QUICKRESTART to VARIANT_TRUE and then change the command text.

In implementations that require reexecution of a command to reposition the next fetch position to its initial position, the provider is responsible for caching all parameters required by the command.

If RestartPosition returns DB_S_COLUMNSCHANGED and the consumer subsequently calls methods in IColumnsInfo or IColumnsRowset, these methods must reflect the new metadata. Existing rowset accessors are not updated to reflect the new metadata. That is, IAccessor::GetBindings returns exactly the same information it would have returned before RestartPosition was called. If such accessors are subsequently used, such as in a call to GetData, the provider must revalidate them. If none of the columns bound by the accessor have changed, the accessor can be used successfully. If any of the columns have changed, the appropriate error or warning is returned.

See Also

IRowset::GetNextRows, IRowsetFind::FindNextRow


IRowsetChange

The methods in IRowsetChange are used to update the values of columns in existing rows, delete existing rows, and insert new rows.

IRowsetChange requires IAccessor and IRowset.

When to Implement

Rowsets implement IRowsetChange if they support updating, deleting, or inserting rows. They are not required to support all three, but must support at least one of these operations to support IRowsetChange. The rowset reports which operations it supports through the DBPROP_UPDATABILITY property.

When to Call

The consumer calls methods in IRowsetChange to modify rows as follows.

  • SetData sets column data in an existing row.

  • DeleteRows deletes existing rows.

  • InsertRow creates a new row and sets initial values.

SetData and InsertRow require the use of an accessor. For more information about accessors, see "Accessors" in Chapter 6.

SetData and InsertRow can fail for a number of reasons. The most common of these is that new data values do not meet the schema or integrity constraints of the column. Furthermore, rowsets can have row-by-row and column-by-column access permissions that override the general permissions of the table or column. For more information, see the DBPROP_COLUMNRESTRICT and DBPROP_ROWRESTRICT properties "Rowset Properties" in Appendix C.

If IRowsetUpdate is exposed on the rowset, then changes made through IRowsetChange are buffered in the rowset and not transmitted to the data source until IRowsetUpdate::Update is called; this is known as delayed update mode. If IRowsetUpdate is not exposed on the rowset, then changes made through IRowsetChange are immediately transmitted to the data source; this is known as immediate update mode. For more information, see "Changing Data" in Chapter 5.

Method | Description
DeleteRows | Deletes rows.
InsertRow | Creates and initializes a new row.
SetData | Sets data in one or more columns in a row.


IRowsetChange::DeleteRows

Deletes rows.

HRESULT DeleteRows (
   HCHAPTER         hChapter,
   ULONG            cRows,
   const HROW         rghRows[],
   DBROWSTATUS   rgRowStatus[]);

Parameters

hChapter

[in]
The chapter handle. For nonchaptered rowsets, hChapter is ignored.

cRows

[in]
The number of rows to be deleted. If cRows is zero, DeleteRows does not do anything.

rghRows

[in]
An array of handles of the rows to be deleted.

If rghRows includes a duplicate row handle, DeleteRows behaves as follows. If the row handle is valid, it is provider-specific whether the returned row status information for each row or a single instance of the row is set to DBROWSTATUS_S_OK. If the row handle is invalid, the row status information for each occurrence of the row contains the appropriate error.

rgRowStatus

[out]
An array with cRows elements in which to return values indicating the status of each row specified in rghRows. If no errors or warnings occur while deleting a row, the corresponding element of rgRowStatus is set to DBROWSTATUS_S_OK. If a warning occurs while deleting a row, the corresponding element is set as specified in S_OK. If an error occurs while deleting a row, the corresponding element is set as specified in DB_S_ERRORSOCCURRED. The consumer allocates memory for this array. If rgRowStatus is a null pointer, no row statuses are returned. For information about the DBROWSTATUS enumerated type, see "Arrays of Errors" in Chapter 13.

Return Code

S_OK
The method succeeded. All rows were successfully deleted. The following values can be returned in rgRowStatus:

  • The row was successfully deleted and no warning conditions occurred. The corresponding element of rgRowStatus contains DBROWSTATUS_S_OK.

  • The rowset was in immediate update mode, and deleting a single row caused more than one row to be deleted in the data source. For more information, see the DBPROP_REPORTMULTIPLECHANGES property. The corresponding element of rgRowStatus contains DBROWSTATUS_S_MULTIPLECHANGES.

DB_S_ERRORSOCCURRED
An error occurred while deleting a row, but at least one row was successfully deleted. Successes and warnings can occur for the reasons listed under S_OK. The following errors can occur:

  • An element of rghRows was invalid or was a row handle to which the current thread does not have access rights. The corresponding element of rgRowStatus contains DBROWSTATUS_E_INVALID.

  • Deletion of a row was canceled during notification. The row was not deleted and the corresponding element of rgRowStatus contains DBROWSTATUS_E_CANCELED.

  • An element of rghRows referred to a row with a pending delete or for which a deletion had been transmitted to the data source. The corresponding element of rgRowStatus contains DBROWSTATUS_E_DELETED.

  • Deleting a row referred to by an element of rghRows violated the integrity constraints for the column or table. The corresponding element of rgRowStatus contains DBROWSTATUS_E_INTEGRITYVIOLATION.

  • The rowset was in immediate update mode and the row was not deleted due to reaching a limit on the server, such as a query execution timing out. The error in the corresponding element of rgRowStatus contains DBROWSTATUS_E_LIMITREACHED.

  • Deleting a row would exceed the limit for pending changes specified by the rowset property DBPROP_MAXPENDINGROWS. The corresponding element of rgRowStatus contains DBROWSTATUS_E_MAXPENDCHANGESEXCEEDED.

  • DBPROP_CHANGEINSERTEDROWS was VARIANT_FALSE and an element of rghRows referred to a row for which the insertion has been transmitted to the data source. The corresponding element of rgRowStatus contains DBROWSTATUS_E_NEWLYINSERTED.

  • The consumer did not have sufficient permission to delete a row. This error can be returned only if the value of the DBPROP_ROWRESTRICT property is VARIANT_TRUE. The corresponding element of rgRowStatus contains DBROWSTATUS_E_PERMISSIONDENIED. If the rowset is in delayed update mode, this error might not be returned until IRowsetUpdate::Update is called.

  • The consumer encountered a recoverable, provider-specific error, such as an RPC failure when transmitting the change to a remote server. The corresponding element of rgRowStatus contains DBROWSTATUS_E_FAIL.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
rghRows was a null pointer and cRows was greater than or equal to one.

E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called and the object is in a zombie state.

DB_E_BADCHAPTER
The rowset was chaptered and hChapter was invalid.

The rowset was single-chaptered and the specified chapter was not the currently open chapter. The consumer must use the currently open chapter or release the currently open chapter before specifying a new chapter.

DB_E_ERRORSOCCURRED
Errors occurred while deleting all of the rows. Errors can occur for the reasons listed under DB_S_ERRORSOCCURRED.

DB_E_NOTREENTRANT
The consumer called this method while it was processing a notification, and it is an error to call this method while processing the specified DBREASON value.

DB_E_NOTSUPPORTED
The provider does not support this method.

Comments

In delayed update mode, DeleteRows marks rows for deletion, rather than actually deleting them. Rows with pending deletes cannot be used in any methods except IRowsetRefresh::GetLastVisibleData, IRowsetUpdate::Undo, IRowsetUpdate::Update, IRowsetUpdate::GetOriginalData, and IRowset::ReleaseRows. The deletion is not transmitted to the data source until Update is called. In immediate update mode, DeleteRows transmits deletions to the data source immediately. For more information, see "Changing Data" in Chapter 5.

After a deletion has been transmitted to the data source, it cannot be undone. The row cannot be used with any method except ReleaseRows. Neither DeleteRows nor Update releases rows after transmitting deletions to the data source. The consumer must release the row with ReleaseRows.

If DeleteRows is called for a row with a pending insert, the row is placed in the same state as a row for which a deletion has been transmitted to the data source. That is, if a row is inserted and then deleted in delayed update mode, the deletion cannot be undone. The row cannot be used with any method except ReleaseRows, which must be called to release it.

If an error occurs while deleting a row, DeleteRows continues deleting the other rows in rghRows and returns DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED. It returns status information about each row in rgRowStatus.

If the DBPROP_ROWRESTRICT property is VARIANT_TRUE, the consumer may have permission to delete some rows but not other rows.

See Also

IRowsetUpdate::Undo, IRowsetUpdate::Update


IRowsetChange::InsertRow

Creates and initializes a new row.

HRESULT InsertRow (
   HCHAPTER      hChapter,
   HACCESSOR   hAccessor,
   void *            pData,
   HROW   *         phRow);

Parameters

hChapter

[in]
The chapter handle. For nonchaptered rowsets, hChapter is ignored.

hAccessor

[in]
The handle of the accessor to use.

If hAccessor is a null accessor (that is, an accessor for which cBindings in IAccessor::CreateAccessor was zero), then pData is ignored and the rows are initialized as specified in the Comments. Thus, the role of a null accessor is to construct a default row; it is a convenient way for a consumer to obtain a handle for a new row without having to set any values in that row initially.

pData

[in]
A pointer to memory containing the new data values, at offsets that correspond to the bindings in the accessor.

phRow

[out]
A pointer to memory in which to return the handle of the new row. If this is a null pointer, then no reference count is held on the row. Consumers should set this to null if they do not require the ability to make further changes to, or retrieve data from, the newly inserted row. Whether or not default or computed values from the server are available when calling GetData for this row handle depends on the setting of the DBPROP_SERVERDATAONINSERT. If InsertRow returns an error, and phRow is not a null pointer on input, *phRow is set to null on output and no row handle is returned.

Note Passing in a null pointer for phRow, or releasing the row handle returned in *phRow, does not release the row until the change is transmitted to the data source. If DBPROP_CANHOLDROWS is VARIANT_FALSE and the rowset is in deferred update mode, then in addition to freeing any reference counts on the hrow, the consumer must call IRowsetUpdate::Update in order to transmit the pending change to the data source before attempting to insert or retrieve any additional rows.

Return Code

S_OK
The method succeeded. The status of all columns bound by the accessor is set to DBSTATUS_S_OK or DBSTATUS_S_ISNULL.

DB_S_ERRORSOCCURRED
An error occurred while setting data for one or more columns, but data was successfully set for at least one column. To determine the columns for which values were invalid, the consumer checks the status values. For a list of status values that can be returned by this method, see "Status Values Used When Setting Data" in "Status" in Chapter 6.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
pData was a null pointer and hAccessor was not a null accessor.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to instantiate the row.

E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called and the object is in a zombie state.

DB_E_ABORTLIMITREACHED
The rowset was in immediate update mode and the row was not inserted due to reaching a limit on the server, such as a query execution timing out.

DB_E_BADACCESSORHANDLE
hAccessor was invalid.

DB_E_BADACCESSORTYPE
The specified accessor was not a row accessor or was a reference accessor.

DB_E_BADCHAPTER
The rowset was chaptered and hChapter was invalid.

The rowset was single-chaptered and the specified chapter was not the currently open chapter. The consumer must use the currently open chapter or release the currently open chapter before specifying a new chapter.

DB_E_CANCELED
The insertion was canceled during notification. The row was not inserted.

DB_E_CANTCONVERTVALUE
The data value for one or more columns couldn't be converted for reasons other than sign mismatch or data overflow, and the provider was unable to determine which columns couldn't be converted. Providers that can detect which columns could not be converted return DB_S_ERRORSOCCURRED and set the status flag for the columns that couldn't be converted to DBSTATUS_E_CANTCONVERTVALUE.

DB_E_DATAOVERFLOW
Conversion failed because the data value for one or more columns overflowed the type used by the provider and the provider was unable to determine which columns caused the overflow. Providers that can detect which columns caused the overflow return DB_S_ERRORSOCCURRED and set the status flag for the columns in violation to DBSTATUS_E_DATAOVERFLOW.

DB_E_ERRORSOCCURRED
An error occurred while setting data for one or more columns and data was not successfully set for any columns. To determine the columns for which values were invalid, the consumer checks the status values. For a list of status values that can be returned by this method, see "Status Values Used When Setting Data" in "Status" in Chapter 6.

DB_E_INTEGRITYVIOLATION
The data violated the integrity constraints for one or more columns of the rowset and the provider was unable to determine which columns violated the integrity constraints. Providers that can detect which columns violated the integrity constraints return DB_S_ERRORSOCCURRED and set the status flag for the columns in violation to DBSTATUS_E_INTEGRITYVIOLATION.

DB_E_MAXPENDCHANGESEXCEEDED
The number of rows that have pending changes has exceeded the limit specified by the DBPROP_MAXPENDINGROWS property.

DB_E_NOTREENTRANT
The provider called a method from IRowsetNotify in the consumer and the method has not yet returned.

DB_E_NOTSUPPORTED
The provider does not support this method.

DB_E_ROWLIMITEXCEEDED
Creating another row would have exceeded the total number of active rows supported by the rowset.

DB_E_ROWSNOTRELEASED
The consumer attempted to insert a new row before releasing previously retrieved row handles or transmitting pending changes to the data source, and DBPROP_CANHOLDROWS is VARIANT_FALSE.

DB_SEC_E_PERMISSIONDENIED
The consumer did not have sufficient permission to insert a new row. This error can be returned only if the value of the DBPROP_ROWRESTRICT property is VARIANT_TRUE. If the rowset is in delayed update mode, this error might not be returned until IRowsetUpdate::Update is called.

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

InsertRow creates a new row and initializes its columns. If phRow is not a null pointer, it then returns the handle of this row to the consumer and sets its reference count to one. In delayed update mode, the row is created locally to the rowset and is transmitted to the data source only when IRowsetUpdate::Update is called. In immediate update mode, the row is immediately transmitted to the data source. For more information, see "Changing Data" in Chapter 5.

To the consumer, newly inserted rows are almost indistinguishable from other rows. For example, they can be deleted with DeleteRows and updated with SetData. However, methods that fetch rows might not be able to return them. For more information, see "Visibility of Pending Changes" and "Visibility of Transmitted Changes" in Chapter 5. Furthermore, they might not contain the correct values for computed columns, including bookmark columns on some providers.

For information about where rows are inserted in the rowset, see "Position of Inserted Rows" in Chapter 5.

The DBPROP_COLUMNRESTRICT and DBPROP_ROWRESTRICT properties affect how security is enforced and how security errors are returned. If DBPROP_COLUMNRESTRICT is VARIANT_TRUE, the consumer might not have write permission on some columns. If the consumer attempts to write to these columns, InsertRows returns a column status of DBSTATUS_E_PERMISSIONDENIED and a return code of DB_S_ERRORSOCCURRED. If the DBPROP_ROWRESTRICT property is VARIANT_TRUE, the consumer might not have permission to insert some rows. If the consumer attempts to insert one of these rows, InsertRows returns a code of DB_SEC_E_PERMISSIONDENIED and no new row is created.

When a row is created, initialization proceeds in an orderly fashion:

  1. The provider sets all columns to their default values. If there is no default value and the column is nullable, it sets the column to NULL. If the column is non-nullable, it sets the column status to DBSTATUS_E_UNAVAILABLE. If the provider is unable or unwilling to determine the default value of a column or whether that column is nullable, it sets the column status to DBSTATUS_E_UNAVAILABLE; the provider might be unwilling to determine default values and nullability if doing so requires a call to the data source.

    If the column status is DBSTATUS_E_UNAVAILABLE, the consumer can still send this value to the data source to use the default. In this case, the default is available after the insertion is transmitted to the data source. To see the default, the consumer must call GetLastVisibleData or RefreshVisibleData. However, if there is no default for the column and it is non-nullable, this will cause a schema violation.

  2. The provider calls IRowsetNotify::OnRowChange with DBREASON_ROW_INSERT if any consumer of the rowset is using notifications. This serves as a hook allowing, among other things, more complex nondeclarative default values to be set in the row. For more information about notifications, see IRowsetNotify.

  3. InsertRow does not further modify the column values if the accessor is a null accessor; it returns the handle to the newly created row.

  4. The provider uses the accessor, if it is not a null accessor, to set columns with the values provided by the consumer in *pData. During this process, the provider does not generate notifications like it does when setting data in SetData. This prevents, for example, DBREASON_COLUMN_SET notifications from being generated for a row that is not yet properly constructed. For a complete description of how InsertRow sets data, see "Setting Data" in Chapter 6.

    The provider is not required to compute the value of computed columns. If the provider does not compute the value of these columns but lets the data source do so, then the computed value is not available until after the change is transmitted to the data source—that is, after InsertRow is called in immediate update mode or after IRowsetUpdate::Update if InsertRow is called in delayed update mode. To retrieve the computed value, the consumer calls RefreshVisibleData or GetLastVisibleData in IRowsetRefresh. Note that bookmark columns are often computed, such as when the bookmark is the primary key or is a ROWID assigned by the data source.

Domain and schema validation is enforced as it is with SetData.

If InsertRow returns an error, it does not create a new row.

It is not recommended to use null accessors because some providers may not be able to determine default values and may not be able to identify a row inserted with all default values.

Here is an example of how one might write a projection-join to a temporary file:

#include <oledb.h>
#include <stddef.h>
int main() {
 IRowset   *pLeftRowset;
 IRowset   *pRightRowset;
 IAccessor   *pLeftRowsetAcc;
 IAccessor   *pRightRowsetAcc;
 IAccessor   *pJoinRowsetAcc;
 IRowsetChange *pJoinRowsetNew;
 ULONG    cSortedRows;
 HROW     rghLeftRows [500];
 HROW     rghRightRows [500];
 //...
 //< sort and prepare the rows >
 //..

 struct join {
  long *pl;
  double *pd;
  short  *pi;
 };

 static DBBINDING LeftBindings [1] = {
  {
   1,
   offsetof (join, pl),
   0,        // No length binding
   0,        // No status binding
   NULL,       // No TypeInfo
   NULL,       // No object
   NULL,       // No extensions
   DBPART_VALUE,
   DBMEMOWNER_PROVIDEROWNED,
   DBPARAMIO_NOTPARAM,
   sizeof (void*),
   0,
   DBTYPE_I4 | DBTYPE_BYREF,
   0,        // No precision
   0         // No scale
  }
 };
 static DBBINDING RightBindings [2]= {
  {
   1,
   offsetof (join, pd),
   0,        // No length binding
   0,        // No status binding
   NULL,       // No TypeInfo
   NULL,       // No object
   NULL,       // No extensions
   DBPART_VALUE,
   DBMEMOWNER_PROVIDEROWNED,
   DBPARAMIO_NOTPARAM,
   sizeof (void*),
   0,
   DBTYPE_R8 | DBTYPE_BYREF,
   0,        // No precision
   0         // No scale
  },
  {
   2,
   offsetof (join, pi),
   0,        // No length binding
   0,        // No status binding
   NULL,       // No TypeInfo
   NULL,       // No object
   NULL,       // No extensions
   DBPART_VALUE,
   DBMEMOWNER_PROVIDEROWNED,
   DBPARAMIO_NOTPARAM,
   sizeof (void*),
   0,
   DBTYPE_I2 | DBTYPE_BYREF,
   0,        // No precision
   0         // No scale
  }
 };

 static DBBINDING JoinBindings [3] = {
  {
   1,
   offsetof (join, pl),
   0,        // No length binding
   0,        // No status binding
   NULL,       // No TypeInfo
   NULL,       // No object
   NULL,       // No extensions
   DBPART_VALUE,
   DBMEMOWNER_PROVIDEROWNED,
   DBPARAMIO_NOTPARAM,
   sizeof (void*),
   0,
   DBTYPE_I4 | DBTYPE_BYREF,
   0,        // No precision
   0         // No scale
  },
  {
   2,
   offsetof (join, pd),
   0,        // No length binding
   0,        // No status binding
   NULL,       // No TypeInfo
   NULL,       // No object
   NULL,       // No extensions
   DBPART_VALUE,
   DBMEMOWNER_PROVIDEROWNED,
   DBPARAMIO_NOTPARAM,
   sizeof (void*),
   0,
   DBTYPE_R8 | DBTYPE_BYREF,
   0,        // No precision
   0         // No scale
  },
  {
   3,
   offsetof (join, pi),
   0,        // No length binding
   0,        // No status binding
   NULL,       // No TypeInfo
   NULL,       // No object
   NULL,       // No extensions
   DBPART_VALUE,
   DBMEMOWNER_PROVIDEROWNED,
   DBPARAMIO_NOTPARAM,
   sizeof (void*),
   0,
   DBTYPE_I2 | DBTYPE_BYREF,
   0,        // No precision
   0         // No scale
  }
 };

 HACCESSOR hLeft;
 HACCESSOR hRight;
 HACCESSOR hJoin;
 join  theJoin;

 pLeftRowsetAcc->CreateAccessor(DBACCESSOR_ROWDATA, 1, LeftBindings, 0, &hLeft,
            NULL);

 pRightRowsetAcc->CreateAccessor(DBACCESSOR_ROWDATA, 2, RightBindings, 0, &hRight,
             NULL);

 pJoinRowsetAcc->CreateAccessor(DBACCESSOR_ROWDATA, 3, JoinBindings, 0, &hJoin,
            NULL);

 for (ULONG j = 0; j < cSortedRows;  j++) {
  pLeftRowset->GetData(rghLeftRows[j], hLeft, &theJoin);
  pRightRowset->GetData(rghRightRows[j], hRight, &theJoin);
  pJoinRowsetNew->InsertRow(NULL, hJoin, &theJoin, NULL);
 } ;
};

See Also

IRowset::GetData, IRowsetChange::SetData, IRowsetUpdate::Undo, IRowsetUpdate::Update


IRowsetChange::SetData

Sets data values in one or more columns in a row.

HRESULT SetData (
   HROW            hRow,
   HACCESSOR   hAccessor,
   void *            pData);

Parameters

hRow

[in]
The handle of the row in which to set data.

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 SetData does not set any data values.

pData

[in]
A pointer to memory containing the new data values, at offsets that correspond to the bindings in the accessor.

Return Code

S_OK
The method succeeded. The status of all columns bound by the accessor is set to DBSTATUS_S_OK or DBSTATUS_S_ISNULL

DB_S_ERRORSOCCURRED
An error occurred while setting data for one or more columns, but data was successfully set for at least one column. To determine the columns for which data was returned, the consumer checks the status values. For a list of status values that can be returned by this method, see "Status Values Used When Setting Data" in "Status" in Chapter 6.

DB_S_MULTIPLECHANGES
The rowset was in immediate update mode and updating the row caused more than one row to be updated in the data source. For more information, see DBPROP_REPORTMULTIPLECHANGES in "Rowset Properties" in Appendix C.

This return code takes precedence over DB_S_ERRORSOCCURRED. That is, if the conditions described here and in those described in DB_S_ERRORSOCCURRED both occur, the provider returns this code. When the consumer receives this return code, it should also check for the conditions described in DB_S_ERRORSOCCURRED.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
pData was a null pointer and the accessor was not a null accessor.

E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called and the object is in a zombie state.

DB_E_ABORTLIMITREACHED
The rowset was in immediate update mode and the row was not updated due to reaching a limit on the server, such as a query execution timing out.

DB_E_BADACCESSORHANDLE
hAccessor was invalid.

DB_E_BADACCESSORTYPE
The specified accessor was not a row accessor or was a reference accessor.

DB_E_BADROWHANDLE
hRow was invalid.

DB_E_CANCELED
The change was canceled during notification. No columns are changed.

DB_E_CANTCONVERTVALUE
The data value for one or more columns couldn't be converted for reasons other than sign mismatch or data overflow, and the provider was unable to determine which columns couldn't be converted. Providers that can detect which columns could not be converted return DB_S_ERRORSOCCURRED and set the status flag for the columns that couldn't be converted to DBSTATUS_E_CANTCONVERTVALUE.

DB_E_CONCURRENCYVIOLATION
The rowset was using optimistic concurrency and the value of a column has been changed since the containing row was last fetched or resynchronized. SetData returns this error only when the rowset is in immediate update mode.

DB_E_DATAOVERFLOW
Conversion failed because the data value for one or more columns overflowed the type used by the provider and the provider was unable to determine which columns caused the overflow. Providers that can detect which columns caused the overflow return DB_S_ERRORSOCCURRED and set the status flag for the columns in violation to DBSTATUS_E_DATAOVERFLOW.

DB_E_DELETEDROW
hRow referred to a row with a pending delete or for which a deletion had been transmitted to the data source.

DB_E_ERRORSOCCURRED
An error occurred while setting data for one or more columns and data was not successfully set for any columns. To determine the columns for which values were invalid, the consumer checks the status values. For a list of status values that can be returned by this method, see "Status Values Used When Setting Data" in "Status" in Chapter 6.

DB_E_INTEGRITYVIOLATION
The data violated the integrity constraints for one or more columns of the rowset and the provider was unable to determine which columns violated the integrity constraints. Providers that can detect which columns violated the integrity constraints return DB_S_ERRORSOCCURRED and set the status flag for the columns in violation to DBSTATUS_E_INTEGRITYVIOLATION.

DB_E_MAXPENDCHANGESEXCEEDED
The number of rows that have pending changes has exceeded the limit specified by the DBPROP_MAXPENDINGROWS property.

DB_E_NEWLYINSERTED
DBPROP_CHANGEINSERTEDROWS was VARIANT_FALSE and hRow referred to a row for which the insertion has been transmitted to the data source.

DB_E_NOTREENTRANT
The consumer called this method while it was processing a notification, and it is an error to call this method while processing the specified DBREASON value.

DB_E_NOTSUPPORTED
The provider does not support this method.

DB_SEC_E_PERMISSIONDENIED
The consumer did not have sufficient permission to update the row. This error can be returned only if the value of the DBPROP_ROWRESTRICT property is VARIANT_TRUE. If the rowset is in delayed update mode, this error might not be returned until IRowsetUpdate::Update is called.

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

SetData sets data values in one or more columns in a row. For a complete description of how SetData sets data, see "Setting Data" in Chapter 6.

In delayed update mode, these changes are buffered locally in the rowset and are transmitted to the data source only when IRowsetUpdate::Update is called. In immediate update mode, the changes are immediately transmitted to the data source. For more information, see "Changing Data" in Chapter 5.

If a computed column depends on a column that is changed with SetData, the provider is not required to compute the new value of the computed column. If the provider computes the new value, it sends a notification to the consumer. If the provider does not compute the new value but lets the data source do so, then the computed value is not available until after the change is transmitted to the data source—that is, after SetData is called in immediate update mode or after IRowsetUpdate::Update if SetData is called in delayed update mode. To retrieve the computed value in this case, the consumer calls RefreshVisibleData or GetLastVisibleData in IRowsetRefresh. Bookmark columns are often computed, such as when the bookmark is the primary key or is a ROWID assigned by the data source.

If SetData changes a column that is used to order the rowset, the DBPROP_IMMOBILEROWS property describes whether the row is moved based on its new value. If this property is VARIANT_TRUE, the row is not moved. If this property is VARIANT_FALSE, the row is moved. If the rowset is not ordered, then the position of updated rows is not changed. Note that, if the rowset is built on a set of key columns (typically a rowset for which DBPROP_OTHERUPDATEDELETE is VARIANT_TRUE but DBPROP_OTHERINSERT is VARIANT_FALSE), changing the value of key column is generally equivalent to deleting the current row and inserting a new one. Thus, the row may appear to move or even disappear from the rowset (if DBPROP_OWNINSERT is VARIANT_FALSE), even though the DBPROP_IMMOBILEROWS property is VARIANT_TRUE.

When the consumer passes a pointer to a storage object to SetData, SetData replaces the data in the column with the data in the new storage object. If the consumer wants only to delete the data in the column, it sets the column status to DBSTATUS_S_OK and passes a null pointer instead of a pointer to a storage object. For more information, see "Getting and Setting BLOB Data with Storage Objects" in Chapter 7. If the rowset is in immediate update mode, storage object data is always transmitted immediately to the data source. If it is in delayed update mode, whether it is transmitted immediately or delayed depends on the DBPROP_DELAYSTORAGEOBJECTS property.

Although SetData can detect domain constraint and some table constraint schema violations, it is not required to do so. Such validation can be delayed until the changes are transmitted to the data source with Update or the transaction is committed with ITransaction::Commit. This delay is often necessary because of dependencies on values in other columns or tables.

SetData cannot be called for rows with pending or transmitted deletes.

The DBPROP_COLUMNRESTRICT and DBPROP_ROWRESTRICT properties affect how security is enforced and how security errors are returned. If DBPROP_COLUMNRESTRICT is VARIANT_TRUE, the consumer might not have write permission on some columns. If the consumer attempts to write to these columns, SetData returns a column status of DBSTATUS_E_PERMISSIONDENIED and a return code of DB_S_ERRORSOCCURRED. If the DBPROP_ROWRESTRICT property is VARIANT_TRUE, the consumer might not have permission to update some rows. If the consumer attempts to update one of
these rows, SetData returns a code of DB_SEC_E_PERMISSIONDENIED and no data is set.

If any consumer of the rowset is using notifications, the provider sends notifications. These notifications can be vetoed, in which case the provider sends the DBEVENTPHASE_FAILEDTODO phase of the notification. When the consumer then calls Update, if the update is in delayed mode, the provider does not send additional DBREASON_COLUMN_SET notifications for the rows. However, if Update computes the value of computed columns, it sends DBREASON_COLUMN_RECALCULATED notifications. In this case, the provider must either be prepared to undo all pending changes for the row, and return DBREASON_ROW_UNDOCHANGE, or set the fCantDeny flag to TRUE.

The following sequence of notifications occurs for a rowset operating in delayed update mode:

  1. If the row is being changed for the first time since it was created, or changes were transmitted, the provider sends the DBEVENTPHASE_OKTODO and DBEVENTPHASE_ABOUTTODO phases of the DBREASON_ROW_FIRSTCHANGE notification.

  2. The provider sends the DBEVENTPHASE_OKTODO, DBEVENTPHASE_ABOUTTODO, and DBEVENTPHASE_SYNCHAFTER phases of the DBREASON_COLUMN_SET notification, in that order, provided none of the listeners veto any of the phases. The notification covers all the columns defined by the accessor used in the call to SetData.

  3. The provider sends the DBEVENTPHASE_SYNCHAFTER phase of the DBREASON_ROW_FIRSTCHANGE notification, assuming the provider sent the earlier phases of this notification.

  4. The provider sends the DBEVENTPHASE_DIDEVENT phase of the DBREASON_COLUMN_SET notification.

  5. The provider sends the DBEVENTPHASE_DIDEVENT phase of the ROW_FIRSTCHANGE notification.

See Also

IRowset::GetData, IRowsetChange::InsertRow, IRowsetUpdate::Undo, IRowsetUpdate::Update


IRowsetChapterMember

The IRowsetChapterMember interface detects whether or not a row is a member of a chapter.

When to Implement

IRowsetChapterMember is an optional interface on chaptered rowsets.

When to Call

Consumers call IsRowInChapter whenever they need to determine if a row handle is a member of a given chapter. For example, if a row handle is returned to the consumer during notification, the consumer can call this method on each of the chapters it already has to determine to which chapters the row belongs.

Method | Description
IsRowInChapter | Compares whether a row is a member of a chapter.


IRowsetChapterMember:: IsRowInChapter

Determines whether or not a row is a member of a chapter.

HRESULT IsRowInChapter (   HCHAPTER      hChapter,   HROW         hRow);

Parameters

hChapter

[in]
The chapter handle.

hRow

[in]
The row handle.

Return Code

S_FALSE

The method succeeded and the row handle is not a member of this chapter.

S_OK

The method succeeded and the row handle is a member of the chapter.

E_FAIL

A provider-specific error occurred.

E_UNEXPECTED

ITransaction::Commit or ITransaction::Abort was called and the object is in a zombie state.

DB_E_BADROWHANDLE

hRow is invalid.

DB_E_BADCHAPTER

hChapter is invalid.

The rowset was single-chaptered and the specified chapter was not the currently open chapter. The consumer must use the currently open chapter or release the currently open chapter before specifying a new chapter.

Comments

This method makes no logical change to the state of the object.

This method determines whether or not a row handle is a member of a chapter. This comparison could be used, for example, if the consumer receives a row handle through one of the methods in IRowsetNotify and needs to know if it is a member of a chapter that the consumer already holds.


IRowsetFind

IRowsetFind is the interface that allows consumers to find a row within the rowset matching a specified value.

When to Implement

IRowsetFind is required for all providers that intend to support general consumers.

When to Call

Consumers call IRowsetFind to locate the next row in the rowset matching a specified value.

Method | Description
FindNextRow | Returns the next row matching the criteria.


IRowsetFind::FindNextRow

Begin at the specified bookmark and find the next row matching the specified value.

HRESULT FindNextRow (
   HCHAPTER                  hChapter,
   HACCESSOR               hAccessor,
      void *                        pFindValue,
      DBCOMPAREOP            CompareOp,
      ULONG                     cbBookmark,
   const BYTE *                  pBookmark,
      LONG                        IRowsOffset,
   LONG                        cRows,
   ULONG *                     pcRowsObtained,
      HROW **                     prghRows);

Parameters

hChapter

[in]
The chapter handle. For nonchaptered rowsets, hChapter is ignored.

hAccessor

[in]
Accessor describing the value to be matched. This accessor must describe only a single column. If it describes more than one column, FindNextRow returns DB_E_BADBINDINFO.

Valid coercions for FindNextRow are the same as those for SetData.

pFindValue

[in]
Pointer to the value to be matched. If this indicates a null value (a variant of type VT_NULL, or a status value of DBSTATUS_ISNULL), it is compared to other values according to the DBPROP_NULLCOLLATION property returned in the Data Source Information property set. .

CompareOp

[in]
Operation to use in comparing the row values. The consumer should check DBPROP_FINDCOMPAREOPS to determine which comparison operators the provider supports.

Only DBCOMPAREOPS_EQ, DBCOMPAREOPS_NE, and DBCOMPAREOPS_IGNORE are valid for the following CompareOps:

  • DBTYPE_BOOL

  • DBTYPE_ERROR

  • DBTYPE_GUID

  • DBTYPE_IDISPATCH

  • DBTYPE_IUNKNOWN

  • DBTYPE_BTYES

The values in DBCOMPAREOPSENUM have the following meanings.

Value | Description
DBCOMPAREOPS_LT | Match the first value that is less than the search value.
DBCOMPAREOPS_LE | Match the first value that is less than or equal to the search value.
DBCOMPAREOPS_EQ | Match the first value that is equal to the search value.
DBCOMPAREOPS_GE | Match the first value that is greater than or equal to the search value.
DBCOMPAREOPS_GT | Match the first value that is greater than the search value.
DBCOMPAREOPS_BEGINSWITH | Match the first value that has the search value as its first characters. This is only valid for values bound as string data types.
DBCOMPAREOPS_NOTBEGINSWITH | Match the first value that does not have the search value as its first characters. This is only valid for values bound as string data types.
DBCOMPAREOPS_CONTAINS | Match the first value that contains the search value. This is only valid for values bound as string data types.
DBCOMPAREOPS_NOTCONTAINS | Match the first value that does not contain the search value. This is only valid for values bound as string data types.
DBCOMPAREOPS_NE | Match the first value that is not equal to the search value. NULLs evaluate as not equal to any other value, but equal to other NULLs.
DBCOMPAREOPS_IGNORE | Ignore the corresponding value. The provider ignores pFindValue and returns the next cRows rows starting from the row indicated by pBookmark, skipping the number of rows indicated by lRowsOffset.
DBCOMPAREOPS_CASESENSITIVE and DBCOMPAREOPS_CASEINSENSITIVE | Specify whether the search is case sensitive or case insensitive. You can join DBCOMPAREOPS_CASESENSITIVE or DBCOMPAREOPS_CASEINSENSITIVE with any of the other DBCOMPAREOPS values in a bitwise OR operation. If neither one is used, the search is handled according to the provider's implementation. Both DBCOMPAREOPS_CASESENSITIVE and DBCOMPAREOPS_CASEINSENSITIVE are ignored when searching for nonstring values.

cbBookmark

[in]
Length in bytes of the bookmark, or zero in order to start from the next fetch location (see the description of pBookmark).

pBookmark

[in]
Pointer to a bookmark that, in combination with IRowsetOffset, identifies the row from which to start searching for a match. The consumer is not required to have access rights to this row. However, if this row matches the seek criteria, the provider cannot return it unless the consumer has read permission for it.

The consumer can specify a cbBookmark value of zero, and set pBookmark to null, in order to request that the find occur starting with the next fetch location. The next fetch location is the same as the next fetch position used by GetNextRows.

If the rowset does not support IRowsetLocate, then only the special bookmarks DBBMK_FIRST and DBBMK_LAST, and the null value, may be used.

If the rowset does not support scrolling backward, then only the null bookmark value may be used.

If the rowset is chaptered, the identified row must fall within the specified chapter.

IRowsOffset

[in]
The signed count of rows from the origin bookmark to the row at which to start searching for a match. The first row checked is determined by the bookmark and this offset. For example, if IRowsOffset is 0, the first row checked is the bookmarked row; if IRowsOffset is 1, the first row checked is the row after the bookmarked row; if IRowsOffset is -1, the first row checked is the row before the bookmarked row. This can be a negative number only if the value of the DBPROP_CANSCROLLBACKWARDS property is VARIANT_TRUE.

cRows

[in]
The number of rows to fetch from the rowset, starting with the first row found. A negative number means to fetch backward. Check the DBPROP_CANFETCHBACKWARDS property returned in IRowsetInfo::GetProperties to determine whether this is supported.

The search direction is the same as the fetch direction, so a negative count searches backward from the starting position and returns successively earlier rows as subsequent obtained rows.

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. If no match is found, the fetch position is moved before the beginning of the rowset if the previous fetch direction was backward, or after the end of the rowset if the previous fetch direction was forward or if this is the first fetch.

pcRowsObtained

[out]
A pointer to memory in which to return the actual number of fetched rows.

prghRows

[out]
A pointer to memory in which to return an array of handles of the retrieved rows. If *prghRows is not a null pointer on input, it must be a pointer to memory large enough to return the handles of the requested number of rows. If *prghRows is a null pointer on input, the rowset allocates memory for the row handles and returns the address to this memory; the consumer releases this memory with IMalloc::Free after it releases the row handles. If *prghRows was a null pointer on input and *pcRowsObtained is 0 on output, the provider does not allocate any memory and ensures that *prghRows is a null pointer on output.

Return Code

S_OK
The method succeeded.

DB_S_ENDOFROWSET
FindNextRow reached the start or the end of the rowset or (in a hierarchical rowset) the start or the end of the chapter and could not fetch all requested rows because the count extended beyond the end. The next fetch position is before the start or after the end of the rowset. The number of rows actually returned is returned in *pcRowsObtained; this will be less than cRows.

DB_S_ROWLIMITEXCEEDED
Fetching the number of rows specified in cRows would have exceeded the total number of active rows supported by the rowset. The number of rows that were actually fetched is returned in *pcRowsObtained. This condition can occur only when there are more rows available than can be handled by the rowset. Thus, this condition never conflicts with those described in DB_S_ENDOFROWSET and DB_S_STOPLIMITREACHED, both of which imply that no more rows were available.

DB_S_STOPLIMITREACHED
Returning rows required further execution of the command, such as when the rowset uses a server-side cursor. Execution has been stopped because a resource limit has been reached. The number of rows that were actually fetched is returned in *pcRowsObtained. Execution cannot be resumed.

DB_S_BOOKMARKSKIPPED
The following behavior is only supported on rowsets that set the DBPROP_BOOKMARKSKIPPED property to VARIANT_TRUE. If this property is VARIANT_FALSE, this return code is never returned.

The row specified by *pBookmark was deleted, is no longer a member of the rowset, or is a row to which the consumer does not have access rights. FindNextRow skipped that row, and began searching with the next row in the direction indicated by cRows.

If this condition occurs along with another warning condition, the method returns the code for the other warning condition. Thus, whenever a consumer receives the return code for another warning condition, it should check to see whether this condition occurred.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
pcRowsObtained or prghRows was a null pointer.

cbBookmark was not 0 and pBookmark was a null pointer.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to instantiate the rows or return the row handles.

E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called and the object is in a zombie state.

DB_E_BADBINDINFO
The specified accessor specified binding information for more than one column.

DB_E_BADBOOKMARK
*pBookmark was invalid; for example, it was incorrectly formed.

*pBookmark did not match any rows in the rowset. This includes the case when the row corresponding to the bookmark has been deleted and DBPROP_BOOKMARKSKIPPED was VARIANT_FALSE.

*pBookmark was not a null value and the rowset does not support scrolling backward.

*pBookmark was not DBBMK_FIRST, DBBMK_LAST, or a null value, and the rowset does not support IRowsetLocate.

The rowset was chaptered and *pBookmark did not apply to the specified chapter.

Note Consumers should only attempt to use bookmarks that they have received from the provider. The provider is guaranteed only to handle bookmarks it gives out in a predictable manner. Attempting to use a random value as a bookmark is undefined; the provider may return DB_E_BADBOOKMARK, may return an unexpected row, or may terminate abnormally.

DB_E_BADCHAPTER
The rowset was chaptered and hChapter was invalid.

The rowset was single-chaptered and the specified chapter was not the currently open chapter. The consumer must use the currently open chapter or release the currently open chapter before specifying a new chapter.

DB_E_BADCOMPAREOP
CompareOp was an invalid value.

CompareOp was DBCOMPAREOPS_BEGINSWITH or DBCOMPAREOPS_CONTAINS and pFindValue was not bound as a string value.

Both DBCOMPAREOPS_CASESENSITIVE and DBCOMPAREOPS_CASEINSENSITIVE were used in the same operation.

DB_E_BADSTARTPOSITIONIRowsOffset would position the first row retrieved past either end of the rowset, regardless of the cRows value specified. cRowsObtained is 0.

DB_E_CANTFETCHBACKWARDS
cRows was negative and the rowset cannot fetch backward.

DB_E_CANTSCROLLBACKWARDS
A bookmark value of DBBMK_FIRST or DBBMK_LAST was specified and the rowset cannot scroll backward.

IRowsOffset was negative and the rowset cannot scroll backward.

DB_E_NOTREENTRANT
The consumer called this method while it was processing a notification, and it is an error to call this method while processing the specified DBREASON value.

DB_E_ROWSNOTRELEASED
The provider requires release of prior HROWs before new ones can be obtained (see IDBProperties::GetPropertyInfo, DBPROP_CANHOLDROWS).

DB_SEC_E_PERMISSIONDENIED
The consumer did not have sufficient permission to get the rows.

Comments

See IRowsetLocate::GetRowsAt for a complete description of the results for given values for a bookmark, offset, and number of rows to fetch.

FindNextRow searches the rowset in the direction indicated by cRows for a row that matches pFindValue according to the specified CompareOp. Once a match is found, if cRows is greater than 1, additional rows are returned in rowset order as if the consumer called GetNextRows starting with the first matching row. If no rows match the criteria, FindNextRow returns DB_S_ENDOFROWSET and pcRowsObtained is set to zero.

For a newly created rowset, if pBookmark is null, the search begins starting with the row computed according to the next fetch position as described in GetNextRows for newly created rowsets.

It is not required that the rowset be ordered on the column searched. FindNextRow always finds the next row according to the criteria, regardless of the sort order.

FindNextRow increments the reference count of each row for which it returns a handle by 1. Thus, if a handle is returned for a row that has already been fetched, the reference count of that row will be greater than 1. ReleaseRows must be called once for each time the handle to a row has been returned.

If the provider encounters a problem fetching a row—for example, data stored in a text file contains a letter in a numeric column—FindNextRow fetches the row normally, returns the row handle, and returns S_OK. However, when the consumer calls GetData for the row, the provider returns DBSTATUS_E_CANTCONVERTVALUE as the status for the offending column.

If an error occurs when calling FindNextRow, the fetch position is left unchanged.

If FindNextRow 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, and DB_E_UNSUPPORTEDCONVERSION.

For information about what FindNextRow does when it fetches a row that it already has in its internal buffers, see "Uniqueness of Rows in the Rowset" in Chapter 4. For information about whether FindNextRow can detect changes made to rows in the rowset, see "Visibility of Changes" in Chapter 5. For information about how FindNextRow interprets data from *pFindValue, see "Setting Data" in Chapter 6.

See Also

IRowset::GetData, IRowsetIndex::Seek, IRowsetLocate::GetRowsAt, IRowsetLocate::GetRowsByBookmark


IRowsetIdentity

IRowsetIdentity is the interface that indicates row instance identity is implemented on the rowset and enables testing for row identity. If a rowset supports this interface, any two row handles representing the same underlying row will always reflect the same data and state.

IRowsetIdentity depends on IRowset.

When to Implement

IRowsetIdentity is required for all providers that intend to support general consumers.

If the provider inherently supports row identity, this interface will be supported even if the command does not require it. For such providers, row identity is not disabled if the consumer does not request IRowsetIdentity.

If the DBPROP_LITERALIDENTITY property is set to VARIANT_TRUE, then the provider supports binary comparison of row handles.

This interface can be implemented even when row identity cannot be perfectly implemented; the rowset may reasonably take steps to be partially correct. For more information, see "Uniqueness of Rows in the Rowset" in Chapter 4.

When to Call

Consumers call IsSameRow whenever they need to determine if two row handles represent the same underlying row. For example, if a row handle is returned to the consumer during notification, the consumer can compare it to its own list of row handles to see if it is the handle of a row of interest.

Method | Description
IsSameRow | Compares two row handles to see if they refer to the same row instance.


IRowsetIdentity::IsSameRow

Compares two row handles to see if they refer to the same row instance.

HRESULT IsSameRow (
   HROW   hThisRow,
   HROW   hThatRow);

Parameters

hThisRow

[in]
The handle of an active row.

hThatRow

[in]
The handle of an active row.

Return Code

S_FALSE
The method succeeded and the row handles do not refer to the same row instance.

S_OK
The method succeeded and the row handles do refer to the same row instance.

E_FAIL
A provider-specific error occurred.

E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called and the object is in a zombie state.

DB_E_BADROWHANDLE
hRowThis or hRowThat was invalid.

DB_E_DELETEDROW
hRowThis or hRowThat referred to a row for which a deletion had been transmitted to the data source.

DB_E_NEWLYINSERTED
The provider is unable to determine identity for a row for which an insertion had been transmitted to the data source. This condition can occur when DBPROP_STRONGIDENTITY is set to VARIANT_FALSE.

Comments

This method makes no logical change to the state of the object.

The row handles refer to the same row instance if the value set in any column in the row by means of one row handle will be the value returned for that column when gotten through the other row handle.

Rowsets that support this interface must also implement the detection of row identity within their rowset. That is, if they fetch rows at different times, they can detect when the row handles are referring to the same logical row and arrange for both row handles to reflect the same data and state.

The value of the row handles—their bit patterns as handles—is not necessarily the same if the underlying row is the same. This depends on the value of the DBPROP_LITERALIDENTITY property.

The outcome of IsSameRow is not changed when one or both of its parameters correspond to a row with a pending delete.


IRowsetIndex

IRowsetIndex is the primary interface for exposing index functionality in OLE DB. For a complete description of indexes, see Chapter 8, "Indexes" and Chapter 16, "Integrated Indexes."

When to Implement

The IRowsetIndex interface is implemented by providers to expose the functionality of a file access method such as a B+-tree, or linear hash. For integrated indexes, IRowsetIndex is exposed on the base table rowset; otherwise, it is exposed on the index rowset.

A provider may also support other rowset interfaces on indexes, such as IRowsetScroll.

When to Call

The methods in IRowsetIndex are used to define a range of index entries to be read, to position at an index entry within the range, to fetch the index entry, and to access the contents of the index entry.

The following table shows how to perform various index operations.

Operation | Comments
Open an index | To get a handle to an IRowsetIndex object, the consumer calls IOpenRowset::OpenRowset, passing it the DBID of the index. The method returns a pointer to an index rowset.
Close an index | An index is closed by releasing all references to the index rowset.
Insert an index entry | New key entries are inserted into an index by using IRowsetChange.
Delete an index entry | Key entries are deleted from an index by using IRowsetChange.
Update an index entry | Key entries are updated in an index by first deleting the old index entry and then inserting a new index entry.
Traverse the index | To traverse an index, a user calls methods on IRowset. For more information, see "Using Index Rowsets" in Chapter 8.

Method | Description
GetIndexInfo | Returns information about the index rowset capabilities.
Seek | Allows direct positioning at a key value within the current range.
SetRange | Restricts the set of row entries visible through calls to IRowset::GetNextRows and IRowsetIndex::Seek.


IRowsetIndex::GetIndexInfo

Returns information about the index rowset capabilities.

HRESULT GetIndexInfo (
   ULONG *                        pcKeyColumns,
   DBINDEXCOLUMNDESC **   prgIndexColumnDesc,
   ULONG *                        pcIndexProperties,
   DBPROPSET **               prgIndexProperties);

Parameters

pcKeyColumns

[out]
A pointer to memory in which to return the number of key columns in the index.

prgIndexColumnDesc

[out]
A pointer to memory in which to return an array of DBINDEXCOLUMNDESC structures in key column order. For more information, see IIndexDefinition::CreateIndex. The size of the array is equal to *pcKeyColumns. If an error occurs, *prgIndexColumnDesc is set to a null pointer.

pcIndexProperties

[out]
A pointer to memory in which to return the number of DBPROPSET structures returned in *prgIndexProperties. *pcIndexProperties is the total number of property sets for which the provider supports at least one property in the Index property group. If an error occurs, *pcIndexProperties is set to zero.

prgIndexProperties

[out]
A pointer to memory in which to return an array of DBPROPSET structures. One structure is returned for each property set that contains at least one property belonging to the Index property group. For information about the properties in the Index property group that are defined by OLE DB, see "Index Properties" in Appendix C.

The provider allocates memory for the structures and returns the address to this memory; the consumer releases this memory with IMalloc::Free when it no longer needs the structures. Before calling IMalloc::Free for *prgPropertySets, the consumer should call IMalloc::Free for the rgProperties element within each element of *prgPropertySets. If *pcIndexProperties is zero on output, or if an error occurs, the provider does not allocate any memory and ensures that *prgIndexProperties is a null pointer on output.

For information about the DBPROPSET and DBPROP structures, see "DBPROPSET Structure" and "DBPROP Structure" in Chapter 11.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
pcKeyColumns, prgIndexColumnDesc, pcIndexProperties, or prgIndexProperties was a null pointer.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to return the column description structures or properties of the index.

DB_E_NOINDEX
The rowset uses integrated indexes and there is no current index.

See Also

IDBProperties::GetPropertyInfo, IIndexDefinition::CreateIndex


IRowsetIndex::Seek

Allows direct positioning at a key value within the current range established by the IRowsetIndex::SetRange method.

HRESULT Seek (
   HACCESSOR   hAccessor,
   ULONG         cKeyValues,
   void *            pData,
   DBSEEK         dwSeekOptions);

Parameters

hAccessor

[in]
The handle of the accessor to use. This accessor must meet the following criteria, which are illustrated with a key that consists of columns A, B, and C, where A is the most significant column and C is the least significant column:

  • For each key column this accessor binds, it must also bind all more significant key columns. For example, the accessor can bind column A, columns A and B, or columns A, B, and C.

  • Key columns must be bound in order from most significant key column to least significant key column. For example, if the accessor binds columns A and B, then the first binding must bind column A and the second binding must bind column B.

  • If the accessor binds any non-key columns, key columns must be bound first. For example, if the accessor binds columns A, B, and the bookmark column, then the first binding must bind column A, the second binding must bind column B, and the third binding must bind the bookmark column.

    If the accessor does not meet these criteria, the method returns DB_E_BADBINDINFO or a status of DBSTATUS_E_BADACCESSOR for the offending column.

    If hAccessor is the handle of a null accessor (cBindings in IAccessor::CreateAccessor was zero), then Seek does not change the next fetch position.

cKeyValues

[in]
The number of bindings in hAccessor for which *pData contains valid data. SetRange retrieves data from the first cKeyValues key columns from *pData. For example, suppose the accessor binds columns A, B, and C of the key in the previous example and cKeyValues is 2. SetRange retrieves data for columns A
and B.

pData

[in]
A pointer to a buffer containing the key values to which to seek, at offsets that correspond to the bindings in the accessor.

dwSeekOptions

[in]
A bitmask describing the options for the Seek method. The values in DBSEEKENUM have the following meanings:

Value | Description
DBSEEK_FIRSTEQ | First key with values equal to the values in *pData.
DBSEEK_LASTEQ | Last key with values equal to the values in *pData.
DBSEEK_GE | First key with values greater than or equal to the values in *pData.
DBSEEK_GT | First key with values greater than the values in *pData.
DBSEEK_LE | First key with values less than or equal to the values in *pData.
DBSEEK_LT | First key with values less than the values in *pData.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
dwSeekOptions was invalid.

hAccessor was the handle of a null accessor.

cKeyValues was zero.

pData was a null pointer.

DB_E_BADACCESSORHANDLE
hAccessor was invalid.

DB_E_BADACCESSORTYPE
The specified accessor was not a row accessor.

DB_E_ERRORSOCCURRED
An error occurred while transferring data for one or more key columns. To determine the columns for which values were invalid, the consumer checks the status values. For a list of status values that can be returned by this method, see "Status Values Used When Setting Data" in "Status" in Chapter 6.

DB_E_NOINDEX
The rowset uses integrated indexes and there is no current index.

DB_E_NOTFOUND
No key value matching the described characteristics could be found within the current range.

DB_E_NOTREENTRANT
The consumer called this method while it was processing a notification, and it is an error to call this method while processing the specified DBREASON value.

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 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

The Seek method provides the caller more control over the traversal of an index. Consider a relational query processor component implementing a merge join over inputs R1 and R2. R1, the outer input, is a rowset ordered by the joining column R1.X. R2, the inner input, is an indexed rowset on column R2.X. Suppose that R1.X has values {10, 20, 100, 110} and that R2.X has values {10, 20, ..., 30, ..., 40, ..., 50, ..., 100, ...}, then when searching R2.X, one could seek directly from 20 to 100 knowing the values of the input R1.X. In some cases, this strategy could be cost effective.

For information about how Seek transfers data from *pData, see "Setting Data" in Chapter 6.

See Also

IRowset::GetNextRows, IRowsetIndex::SetRange


IRowsetIndex::SetRange

Restricts the set of row entries visible through calls to IRowset::GetNextRows and IRowsetIndex::Seek.

HRESULT SetRange (
   HACCESSOR   hAccessor,
   ULONG         cStartKeyColumns,
   void *            pStartData,
   ULONG         cEndKeyColumns,
   void *            pEndData,
   DBRANGE      dwRangeOptions);

Parameters

hAccessor

[in]
The handle of the accessor to use for both *pStartData and *pEndData. This accessor must meet the following criteria, which are illustrated with a key that consists of columns A, B, and C, where A is the most significant column and C is the least significant column:

  • For each key column this accessor binds, it must also bind all more significant key columns. For example, the accessor can bind column A, columns A and B, or columns A, B, and C.

  • Key columns must be bound in order from most significant key column to least significant key column. For example, if the accessor binds columns A and B, then the first binding must bind column A and the second binding must bind column B.

  • If the accessor binds any nonkey columns, key columns must be bound first. For example, if the accessor binds columns A, B, and the bookmark column, then the first binding must bind column A, the second binding must bind column B, and the third binding must bind the bookmark column.

    If the accessor does not meet these criteria, the method returns DB_E_BADBINDINFO or a status of DBSTATUS_E_BADACCESSOR for the offending column.

    If hAccessor is the handle of a null accessor (cBindings in IAccessor::CreateAccessor was zero), then SetRange does not set a range.

cStartKeyColumns

[in]
The number of bindings in hAccessor for which *pStartData contains valid data. SetRange retrieves data from the first cStartKeyValues key columns from *pStartData. For example, suppose the accessor binds columns A, B, and C of the key in the previous example and cStartKeyValues is 2. SetRange retrieves data for columns A and B.

pStartData

[in]
A pointer to a buffer containing the starting key values of the range, at offsets that correspond to the bindings in the accessor.

cEndKeyColumns

[in]
The number of bindings in hAccessor for which *pEndData contains valid data. SetRange retrieves data from the first cEndKeyValues key columns from *pEndData. For example, suppose the accessor binds columns A, B, and C of the key in the previous example and cEndKeyValues is 2. SetRange retrieves data for columns A and B.

pEndData

[in]
A pointer to a buffer containing the ending key values of the range, at offsets that correspond to the bindings in the accessor.

dwRangeOptions

[in]
A bitmask describing the options of the range. The values in DBRANGEENUM have the following meanings:

Value | Description
DBRANGE_ INCLUSIVESTART | The start boundary is inclusive (the default).
DBRANGE_ EXCLUSIVESTART | The start boundary is exclusive.
DBRANGE_ INCLUSIVEEND | The end boundary is inclusive (the default).
DBRANGE_ EXCLUSIVEEND | The end boundary is exclusive.
DBRANGE_ EXCLUDENULLS | Exclude NULLs from the range.
DBRANGE_PREFIX | Use *pStartData as a prefix. *pEndData must be a null pointer. Prefix matching can be specified entirely using the inclusive and exclusive flags. However, because prefix matching is an important common case, this flag enables the consumer to specify only the *pStartData values, and enables the provider to interpret this request quickly.
DBRANGE_MATCH | Set the range to all keys that match *pStartData. *pStartData must specify a full key. *pEndData must be a null pointer. Used for fast equality match.
DBRANGE_MATCH_N_MASK | Equal to 0xFF000000.
DBRANGE_MATCH_N_SHIFT | Equal to 24 to indicate the number of bits to shift to get the number N.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
dwRangeOptions was invalid.

cStartKeyValues was not zero and pStartData was a null pointer.

cEndKeyValues was not zero and pEndData was a null pointer.

hAccessor was the handle of a null accessor.

DB_E_BADACCESSORHANDLE
hAccessor was invalid.

DB_E_BADACCESSORTYPE
The specified accessor was not a row accessor.

DB_E_ERRORSOCCURRED
An error occurred while transferring data for one or more key columns. To determine the columns for which values were invalid, the consumer checks the status values. For a list of status values that can be returned by this method, see "Status Values Used When Setting Data" in "Status" in Chapter 6.

DB_E_NOINDEX
The rowset uses integrated indexes and there is no current index.

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 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

A range defines a view in the index containing a contiguous set of key values. The *pStartData and *pEndData values always specify the starting and ending positions in the range, respectively. Thus, for an ascending index, *pStartData contains the smaller value and *pEndData contains the larger value; for a descending index, *pStartData contains the larger value and *pEndData contains the smaller value.

A range on the entire index is defined by calling SetRange (hAcc, 0, NULL, 0, NULL, 0). When a range is set, Seek can only position to rows in the current range.

For information about how SetRange transfers data from *pDataStart and *pDataEnd, see "Setting Data" in Chapter 6.

Inclusive and Exclusive Ranges with Ascending Indexes

The DBRANGE_INCLUSIVE* and DBRANGE_EXCLUSIVE* flags apply only to the last value in the *pStartData and *pEndData buffers. The other values are always inclusive.

Examples with full keys

a) open ranges (single column key)

Desired range SetRange call
X > 5
SetRange(hAcc, 1, {5}, 0, NULL,
DBRANGE_EXCLUSIVESTART);
X ≥ 5
SetRange(hAcc, 1, {5}, 0, NULL,
DBRANGE_INCLUSIVESTART);
X < 5
SetRange(hAcc, 0, NULL, 1, {5},
DBRANGE_EXCLUSIVEEND);
X ≤ 5
SetRange(hAcc, 0, NULL, 1, {5},
DBRANGE_INCLUSIVEEND);

b) closed ranges (single column key)

Desired range SetRange call
X ≥ 5 and
X ≤ 10
SetRange(hAcc, 1, {5}, 1, {10},
DBRANGE_INCLUSIVESTART |
DBRANGE_INCLUSIVEEND);
X ≥ 5 and
X < 10
SetRange(hAcc, 1, {5}, 1, {10},
DBRANGE_INCLUSIVESTART |
DBRANGE_EXCLUSIVEEND);
X > 5 and
X ≤ 10
SetRange(hAcc, 1, {5}, 1, {10},
DBRANGE_EXCLUSIVESTART |
DBRANGE_INCLUSIVEEND);
X > 5 and
X < 10
SetRange(hAcc, 1, {5}, 1, {10},
DBRANGE_EXCLUSIVESTART |
DBRANGE_EXCLUSIVEEND);

c) open ranges (multicolumn key [A, B])

The only way to specify open ranges with multicolumn indexes is by using partial keys. See the examples with partial keys below.

Notice that the range A > 5 and B = 1 cannot be specified because it does not correspond to a contiguous range.

d) closed ranges (multicolumn key [A, B])

Desired range SetRange call
A = 1 and
B ≥ 5 and
B ≤ 10
SetRange(hAcc, 2, {1, 5}, 2, {1, 10},
DBRANGE_INCLUSIVESTART |
DBRANGE_INCLUSIVEEND);
A = 1 and B > 5
SetRange(hAcc, 2, {1, 5}, 1, {1},
DBRANGE_EXCLUSIVESTART |
DBRANGE_INCLUSIVEEND);
A = 1 and B < 5
SetRange(hAcc, 1, {1}, 2, {1, 5},
DBRANGE_INCLUSIVESTART |
DBRANGE_EXCLUSIVEEND);

Examples with partial keys

e) open ranges (multicolumn key [A, B])

Desired range SetRange call
A > 5
SetRange(hAcc, 1, {5}, 0, NULL,
DBRANGE_EXCLUSIVESTART);
A ≥ 5
SetRange(hAcc, 1, {5}, 0, NULL,
DBRANGE_INCLUSIVESTART);
A = 5
SetRange(hAcc, 1, {5}, 1,{5},
DBRANGE_INCLUSIVESTART |
DBRANGE_INCLUSIVEEND);
A < 5
SetRange(hAcc, 0, NULL, 1, {5},
DBRANGE_EXCLUSIVEEND);
A ≤ 5
SetRange(hAcc, 0, NULL, 1, {5},
DBRANGE_INCLUSIVEEND);

f) closed ranges (multicolumn key [A,B])

Desired range SetRange call
A > 5 and A ≤ 10
SetRange(hAcc, 1, {5}, 1, {10},
DBRANGE_EXCLUSIVESTART |
DBRANGE_INCLUSIVEEND);

Prefix Matching

SetRange provides the DBRANGE_PREFIX flag to make it easier for a consumer to formulate ranges involving keys whose value represents a prefix. Consider the text command SELECT * FROM T WHERE T.A LIKE "abc*". This range can be formulated using only the inclusive and exclusive flags as follows for an ascending index:

SetRange (hAcc, 1, {"abc"}, 1, {"abd"}, DBRANGE_INCLUSIVESTART | DBRANGE_EXCLUSIVEEND);

This requires the consumer to know the collation order. The DBRANGE_PREFIX flag is provided to make it easier for the consumer to formulate this very common case as follows:

SetRange(hAcc, 1, {"abc"}, 0, NULL, DBRANGE_PREFIX);

Examples

Desired range SetRange call
A = 1 and
B like "abc*"
SetRange(hAcc, 2, {1, "abc"}, 0, NULL,
DBRANGE_PREFIX);

This call is the same regardless of whether DBRANGE_PREFIX is used with an ascending or descending index.

Equality Matching

To facilitate the formulation of ranges involving equality, the SetRange method offers the DBRANGE_MATCH option flag. Consider the example SELECT * FROM T WHERE T.X = 5. The corresponding range can be formulated in terms of the inclusive and exclusive flags as follows:

SetRange(hAcc, 1, {5}, 1, {5}, DBRANGE_INCLUSIVESTART | DBRANGE_INCLUSIVEEND);

The same range can be formulated using the match flags as follows:

SetRange(hAcc, 1, {5}, 0, NULL, DBRANGE_MATCH);

Example

Desired range SetRange call
A = 5 and B= 6
SetRange(hAcc, 2, {5, 6}, 2, {5, 6},
DBRANGE_INCLUSIVESTART |
DBRANGE_INCLUSIVEEND);
A = 5 and B= 6
SetRange(hAcc, 2, {5, 6}, 0, NULL,
DBRANGE_MATCH);

These calls are the same regardless of whether DBRANGE_MATCH is used with an ascending or descending index.

DBRANGE_MATCH_N is an extension of DBRANGE_MATCH, which indicates to the provider that the first N key values should be assumed to define comparison on equality. This is achieved by reserving the 8 high-order bits of dwRangeOptions to encode the value N. The defined values are DBRANGE_MATCH_N_SHIFT and DBRANGE_MATCH_N_MASK.

Example

a = 5, b = 6, c = 7, d = 8, e = 9, f >= 10 and f <= 20 becomes:

   SetRange (hAcc,
      6, {5,6,7,8,9,10},
      6, {5,6,7,8,9,20},
      (5 << DBRANGE_MATCH_N_SHIFT) | DB_RANGE_INCLUSIVESTART | DB_RANGE_INCLUSIVEEND);

Providers will check whether (dwRangeOptions and DBRANGE_MATCH_N_MASK) is non-zero. If so, then the key values will be interpreted by taking the first N keys encoded in the high-order bits as the keys on which to perform an equality match.

Ranges That Include NULLs

To save consumers from having to determine whether NULL values sort at the start or the end of an index, DBRANGE_EXCLUDENULLS excludes NULL values from the final column specified in *pStartData or *pEndData. This value of dwRangeOptions can only be used with partial keys. It is equivalent to one of the following:

  • If NULL values sort at the start of the index, it is equivalent to specifying an additional column in *pStartData, setting the value of the status of that column to DBSTATUS_S_ISNULL, and setting the DBRANGE_EXCLUSIVESTART value in dwRangeOptions. Thus, the range will start after the last NULL value in the column.

  • If NULL values sort at the end of the index, it is equivalent to specifying an additional column in *pEndData, setting the value of the status of that column to DBSTATUS_S_ISNULL, and setting the DBRANGE_EXCLUSIVEEND value in dwRangeOptions. Thus, the range will end before the first NULL value in the column.

Thus, the consumer does not need to know if NULL values sort at the start or end of the index.

The DBRANGE_EXCLUDENULLS flag is defined to allow consumers to easily obtain contiguous key values excluding NULLs at the start or end of the range. For multicolumn indexes (for example, [X, Y]), this means that key values for the prefix of the key must be an equality match. For example, it makes sense to set a range on X = 5 and DBRANGE_EXCLUDENULLS, but not on X BETWEEN 4 AND 6 and DBRANGE_EXCLUDENULLS because the range X BETWEEN 4 AND 6 and Y IS NOT NULL is not contiguous. Therefore, to use DBRANGE_EXCLUDENULLS a consumer must use an equality comparison or MATCH on the prefix of a multicolumn key.

To determine how an index treats NULLs, a consumer checks the information returned by GetIndexInfo method or the INDEXES schema rowset of IDBSchemaRowset.

Examples

Desired range SetRange call
X is NULL
SetRange(hAcc, 1, {NULL}, 1, {NULL},
DBRANGE_INCLUSIVESTART |
DBRANGE_INCLUSIVEEND);
X is not NULL
SetRange(hAcc, 0, NULL, 0, NULL,
DBRANGE_EXCLUDENULLS);
A = 4 and
B is not NULL
SetRange(hAcc, 1,{4}, 1, {4},
DBRANGE_INCLUSIVESTART |
DBRANGE_INCLUSIVEEND |
DBRANGE_EXCLUDENULLS);
A = 4 and
B is not NULL
SetRange(hAcc, 1, {4}, 0, NULL,
DBRANGE_MATCH | DBRANGE_EXCLUDENULLS );

Duplicate Keys

A SetRange request (with the exclusive flag on an ascending index allowing duplicate keys) sets the index position at the first index entry greater than the *pStartData value. If the index is descending, then SetRange sets the position at the first entry less than the *pStartData value.

End of Range

An index is traversed using IRowset::GetNextRows. This method returns DB_S_ENDOFROWSET when it reaches the end of the currently set range, or when it tries to read before the beginning or after the end of the index. This method also returns DB_S_ENDOFROWSET when the rowset is being populated asynchronously and no additional rows are available at this time. To determine whether additional rows may be available, the consumer should call IDBAsynchStatus::GetStatus or listen for the IDBAsynchNotify::OnStop notification.

Inclusive and Exclusive Ranges with Descending Indexes

As mentioned earlier, with descending indexes the user specifies the values that determine the start of the range using *pStartData and the values that determine the end of the range using *pEndData. The following are some examples.

Examples with full keys

a) open ranges (single column key)

Desired range SetRange call
X > 5
SetRange(hAcc, 0, NULL, 1, {5},
DBRANGE_EXCLUSIVEEND);
X ≥ 5
SetRange(hAcc, 0, NULL, 1, {5},
DBRANGE_INCLUSIVEEND);
X < 5
SetRange(hAcc, 1, {5}, 0, NULL,
DBRANGE_EXCLUSIVESTART);
X ≤ 5
SetRange(hAcc, 1, {5}, 0, NULL,
DBRANGE_INCLUSIVESTART);

b) closed ranges (single column key)

Desired range SetRange call
X ≥ 5 and
X ≤ 10
SetRange(hAcc, 1, {10}, 1, {5},
DBRANGE_INCLUSIVESTART |
DBRANGE_INCLUSIVEEND);
X ≥ 5 and
X < 10
SetRange(hAcc, 1, {10}, 1, {5},
DBRANGE_EXCLUSIVESTART |
DBRANGE_INCLUSIVEEND);
X > 5 and
X ≤ 10
SetRange(hAcc, 1, {10}, 1, {5},
DBRANGE_INCLUSIVESTART |
DBRANGE_EXCLUSIVEEND);
X > 5 and
X < 10
SetRange(hAcc, 1, {10}, 1, {5},
DBRANGE_EXCLUSIVESTART |
DBRANGE_EXCLUSIVEEND);

c) open ranges (multicolumn key [A, B])

The only way to specify open ranges with multicolumn indexes is by using partial keys. See examples with partial keys below.

The range A > 5 and B = 1 cannot be specified because it does not correspond to a contiguous range.

d) closed ranges (multicolumn key [A, B])

Desired range SetRange call
A = 1 and
B ≥ 5 and
B ≤ 10
SetRange(hAcc, 2, {1, 10}, 2, {1, 5},
DBRANGE_INCLUSIVESTART |
DBRANGE_INCLUSIVEEND);
A = 1 and B > 5
SetRange(hAcc, 1, {1}, 2, {1, 5},
DBRANGE_INCLUSIVESTART |
DBRANGE_EXCLUSIVEEND);
A = 1 and B < 5
SetRange(hAcc, 2, {1, 5}, 1, {1},
DBRANGE_EXCLUSIVESTART |
DBRANGE_INCLUSIVEEND);

Examples with partial keys

e) open ranges (multicolumn key [A, B])

Desired range SetRange call
A > 5
SetRange(hAcc, 0, NULL, 1, {5},
DBRANGE_EXCLUSIVEEND);
A ≥ 5
SetRange(hAcc, 0, NULL, 1, {5},
DBRANGE_INCLUSIVEEND);
A = 5
SetRange(hAcc, 1, {5}, 1, {5},
DBRANGE_INCLUSIVESTART |
DBRANGE_INCLUSIVEEND);
A < 5
SetRange(hAcc, 1, {5}, 0, NULL,
DBRANGE_EXCLUSIVESTART);
A ≤ 5
SetRange(hAcc, 1, {5}, 0, NULL,
DBRANGE_INCLUSIVESTART);

f) closed ranges (multicolumn key [A,B])

Desired range SetRange call
A > 5 and
A ≤ 10
SetRange(hAcc, 1, {10}, 1, {5},
DBRANGE_INCLUSIVESTART |
DBRANGE_EXCLUSIVEEND);

See Also

IRowset::GetNextRows, IRowsetIndex::Seek


IRowsetInfo

IRowsetInfo provides information about a rowset.

When to Implement

All rowsets must implement IRowsetInfo.

When to Call

When a consumer gets an interface pointer on a rowset, its first step usually is to determine the rowset's capabilities using IUnknown::QueryInterface. It may call GetProperties to learn the properties of the rowset that 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.

IRowsetInfo also provides methods for retrieving objects associated with the rowset. GetSpecification gets the object (command or session) that created the rowset. GetReferencedRowset gets the rowset that is referenced by a bookmark-valued column.

Method | Description
GetProperties | Returns the current setting of all properties supported by the rowset.
GetReferencedRowset | Returns an interface pointer to the rowset to which a bookmark applies.
GetSpecification | Returns an interface pointer on the object (command or session) that created the rowset.


IRowsetInfo::GetProperties

Returns the current settings of all properties supported by the rowset.

HRESULT GetProperties (
   const ULONG            cPropertyIDSets,
   const DBPROPIDSET   rgPropertyIDSets[],
   ULONG *                  pcPropertySets,
   DBPROPSET **         prgPropertySets);

Parameters

cPropertyIDSets

[in]
The number of DBPROPIDSET structures in rgPropertyIDSets.

If cPropertySets is zero, the provider ignores rgPropertyIDSets and returns the values of all properties in the Rowset property group for which values exist, including properties for which values were not set but for which defaults exist, and also including properties for which values were set automatically because values were set for other properties.

If cPropertyIDSets is not zero, the provider returns the values of the requested properties. If a property is not supported, the returned value of dwStatus in the returned DBPROP structure for that property is DBPROPSTATUS_NOTSUPPORTED and the value of dwOptions is undefined.

rgPropertyIDSets

[in]
An array of cPropertyIDSets DBPROPIDSET structures. The properties specified in these structures must belong to the Rowset property group. The provider returns the values of the properties specified in these structures. If cPropertyIDSets is zero, then this parameter is ignored.

For information about the properties in the Rowset property group that are defined by OLE DB, see "Rowset Properties" in Appendix C. For information about the DBPROPIDSET structure, see "DBPROPIDSET Structure" in Chapter 11.

pcPropertySets

[out]
A pointer to memory in which to return the number of DBPROPSET structures returned in *prgPropertySets. If cPropertyIDSets is zero, *pcPropertySets is the total number of property sets for which the provider supports at least one property in the Rowset property group. If cPropertyIDSets is greater than zero, *pcPropertySets is set to cPropertyIDSets. If an error other than DB_E_ERRORSOCCURRED occurs, *pcPropertySets is set to zero.

prgPropertySets

[out]
A pointer to memory in which to return an array of DBPROPSET structures. If cPropertyIDSets is zero, then one structure is returned for each property set that contains at least one property belonging to the Rowset property group. If cPropertyIDSets is not zero, then one structure is returned for each property set specified in rgPropertyIDSets.

If cPropertyIDSets is not zero, the DBPROPSET structures in *prgPropertySets are returned in the same order as the DBPROPIDSET structures in rgPropertyIDSets; that is, for corresponding elements of each array, the guidPropertySet elements are the same. If cPropertyIDs, in an element of rgPropertyIDSets, is not zero, the DBPROP structures in the corresponding element of *prgPropertySets are returned in the same order as the DBPROPID values in rgPropertyIDs. Thus, in the case where no column properties are specified in rgPropertyIDSets, corresponding elements of the input rgPropertyIDs and the returned rgProperties have the same property ID. However, if a column property is requested in rgPropertyIDSets, multiple properties may be returned, one for each column, in rgProperties. In this case, corresponding elements of rgPropertyIDs and rgProperties will not have the same property ID, and rgProperties will contain more elements than rgPropertyIDs.

The provider allocates memory for the structures and returns the address to this memory; the consumer releases this memory with IMalloc::Free when it no longer needs the structures. Before calling IMalloc::Free for *prgPropertySets, the consumer should call IMalloc::Free for the rgProperties element within each element of *prgPropertySets. If *pcPropertySets is zero on output or an if error other than DB_E_ERRORSOCCURRED occurs, the provider does not allocate any memory and ensures that *prgPropertySets is a null pointer on output.

For information about the DBPROPSET and DBPROP structures, see "DBPROPSET Structure" and "DBPROP Structure" in Chapter 11.

Return Code

S_OK
The method succeeded. In all DBPROP structures returned by the method, dwStatus is set to DBPROPSTATUS_OK.

DB_S_ERRORSOCCURRED
No value was returned for one or more properties. The consumer checks dwStatus in the DBPROP structure to determine the properties for which values were not returned. GetProperties can fail to return properties for a number of reasons, including:

  • The property was not supported by the provider.

  • The property was not in the Rowset property group.

  • The property set was not supported by the provider. If cPropertyIDs in the DBPROPIDSET structure for the property set was zero, the provider cannot set dwStatus in the DBPROP structure because it does not know the IDs of any properties in the property set. Instead, it sets cProperties to zero in the DBPROPSET structure returned for the property set.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
cPropertyIDSets was not equal to zero and rgPropertyIDSets was a null pointer.

pcPropertySets or prgPropertySets was a null pointer.

In an element of rgPropertyIDSets, cPropertyIDs was not zero and rgPropertyIDs was a null pointer.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to return the DBPROPSET or DBPROP structures.

E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called and the object is in a zombie state.

DB_E_ERRORSOCCURRED
Values were not returned for any properties. The provider allocates memory for *prgPropertySets and the consumer checks dwStatus in the DBPROP structures to determine why properties were not returned. The consumer frees this memory when it no longer needs the information.

Comments

This method makes no logical change to the state of the object.

Even though IDBProperties::GetPropertyInfo lists a property as being supported by the provider, GetProperties will not return a value for it if it does not apply to the current circumstances. For example, the provider's ability to support the property might be affected by the current transaction or the current command text.

IRowsetInfo::GetProperties might return a different value for a property than does ICommandProperties::GetProperties. For example, if a consumer requests ordered bookmarks if they are possible, it calls ICommandProperties::SetProperties to set the value of DBPROP_ORDEREDBOOKMARKS to VARIANT_TRUE and specifies a dwOptions value of DBPROPOPTIONS_OPTIONAL. If the provider cannot determine whether this is possible, ICommandProperties::GetProperties returns a value of VARIANT_TRUE and a dwOptions of DBPROPOPTIONS_OPTIONAL for this property. If the provider determines during execution that ordered bookmarks are not possible, IRowsetInfo::GetProperties returns a value of VARIANT_FALSE and a dwOptions of zero.

See Also

ICommandProperties::SetProperties, IDBProperties::GetPropertyInfo


IRowsetInfo::GetReferencedRowset

Returns an interface pointer to the rowset to which a bookmark or chapter applies.

HRESULT GetReferencedRowset (
   ULONG      iOrdinal,
   REFIID         riid,
   IUnknown **   ppReferencedRowset);

Parameters

iOrdinal

[in]
The bookmark or chapter column for which to get the related rowset.

riid

[in]
The IID of the interface pointer to return in *ppReferencedRowset. This interface is conceptually added to the list of required interfaces on the resulting rowset, and the method fails (E_NOINTERFACE) if that interface cannot be supported on the resulting rowset.

ppReferencedRowset

[out]
A pointer to memory in which to return an IUnknown interface pointer on the rowset that interprets values from this column. If this is not a reference column, *ppReferencedRowset is set to a null pointer.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
ppReferencedRowset was a null pointer.

E_NOINTERFACE
The interface specified in riid was not implicitly or explicitly specified as a rowset property of the rowset. The current rowset remains valid.

E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called and the object is in a zombie state.

DB_E_BADORDINAL
The column specified by iOrdinal did not exist.

DB_E_NOTAREFERENCECOLUMN
The column specified by iOrdinal did not contain bookmarks or chapter values.

DB_E_NOTREENTRANT
The provider called a method from IRowsetNotify in the consumer that had not yet returned, and the provider does not support reentrancy in this method.

DB_E_NOTSUPPORTED
The interface was exposed on an index rowset.

Comments

This method makes no logical change to the state of the current rowset.

All of the bookmarks or chapter values in a column reference a single rowset. The references can apply to the current rowset (an example would be genealogy relations on a People table), or they can apply to a different rowset (an example of which would be Customer Orders).

For more information, see "Bookmarks" or "Chapters" in Chapter 4.

See Also

IColumnsInfo, IColumnsRowset, IChapteredRowset


IRowsetInfo::GetSpecification

Returns an interface pointer on the object (command or session) that created this rowset.

HRESULT GetSpecification (
   REFIID       riid,
   IUnknown **   ppSpecification);

Parameters

riid

[in]
The IID of the interface on which to return a pointer.

ppSpecification

[out]
A pointer to memory in which to return the interface pointer. If the provider does not have an object that created the rowset, it sets *ppSpecification to a null pointer and returns S_FALSE. If GetSpecification fails, it must attempt to set *ppSpecification to a null pointer.

Return Code

S_OK
The method succeeded.

S_FALSE
The provider does not have an object that created the rowset.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
ppSpecification was a null pointer.

E_NOINTERFACE
The object that created this rowset did not support the interface specified in riid.

E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called and the object is in a zombie state.

DB_E_NOTREENTRANT
The provider called a method from IRowsetNotify in the consumer that had not yet returned, and the provider does not support reentrancy in this method.

Comments

This method makes no logical change to the state of the current rowset.

GetSpecification returns an interface pointer on the object that created the rowset. If the rowset was created by ICommand::Execute, this object is a command. If the rowset was created by IOpenRowset::OpenRowset, this object is a session.

If the object is not a command, then it must specify the contents of the rowset. That is, it must expose interfaces that can be used to modify the contents of the rowset before the rowset is created, or be used to gain additional information about the rowset. If the object cannot expose such interfaces, GetSpecification should return a null pointer in *ppSpecification. In a simple provider, such as a provider that creates a rowset over a fixed set of data, there might not be an object that created the rowset; in this case, GetSpecification returns a null pointer in *ppSpecification.

See Also

ICommand::GetDBSession, IGetDataSource::GetDataSource


IRowsetLocate : IRowset

IRowsetLocate is the interface for fetching arbitrary rows of a rowset. A rowset that does not implement this interface is a sequential rowset. IRowsetLocate is a prerequisite for IRowsetScroll.

When IRowsetLocate or one of its direct descendants is present on a rowset, then column 0 is the bookmark for the rows. Reading this column will obtain a bookmark value that can be used to reposition to the same row.

When to Implement

IRowsetLocate is required for all providers that intend to support general consumers.

Method | Description
Compare | Compares two bookmarks.
GetRowsAt | Fetches rows, starting with the row specified by an offset from a bookmark.
GetRowsByBookmark | Fetches the rows that match the specified bookmarks.
Hash | Returns hash values for the specified bookmarks.


IRowsetLocate::Compare

Compares two bookmarks.

HRESULT Compare (
   HCHAPTER         hChapter,
   ULONG            cbBookmark1,
   const BYTE *         pBookmark1,
   ULONG            cbBookmark2,
   const BYTE *         pBookmark2,
   DBCOMPARE *   pComparison);

Parameters

hChapter

[in]
The chapter handle. For nonchaptered rowsets, hChapter is ignored.

cbBookmark1

[in]
The length in bytes of the first bookmark.

pBookmark1

[in]
A pointer to the first bookmark. This can be a pointer to DBBMK_FIRST or DBBMK_LAST.

cbBookmark2

[in]
The length in bytes of the second bookmark.

pBookmark2

[in]
A pointer to the second bookmark. This can be a pointer to DBBMK_FIRST or DBBMK_LAST.

pComparison

[out]
A pointer to memory in which to return a flag that specifies the result of the comparison. The returned flag will be one of the following values.

Value | Description
DBCOMPARE_LT | The first bookmark is before the second.
DBCOMPARE_EQ | The two bookmarks are equal.
DBCOMPARE_GT | The first bookmark is after the second.
DBCOMPARE_NE | The bookmarks are not equal and not ordered.
DBCOMPARE_NOTCOMPARABLE | The two bookmarks cannot be compared.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
cbBookmark1 or cbBookmark2 was zero.

pBookmark1, pBookmark2, or pComparison was a null pointer.

E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called and the object is in a zombie state.

DB_E_BADBOOKMARK
*pBookmark1 or *pBookmark2 was invalid, incorrectly formed, or DBBMK_INVALID.

Note Consumers should only attempt to use bookmarks that they have received from the provider. The provider is guaranteed only to handle bookmarks it gives out in a predictable manner. Attempting to use a random value as a bookmark is undefined; the provider may return DB_E_BADBOOKMARK, may return an unexpected row, or may terminate abnormally.

DB_E_BADCHAPTER
The rowset was chaptered and hChapter was invalid.

The rowset was single-chaptered and the specified chapter was not the currently open chapter. The consumer must use the currently open chapter or release the currently open chapter before specifying a new chapter.

DB_E_NOTREENTRANT
The provider called a method from IRowsetNotify in the consumer that had not yet returned, and the provider does not support reentrancy in this method.

Comments

This method makes no logical change to the state of the object.

If bookmarks are ordered, they can be compared to determine the relative position of their associated rows in the rowset. The DBPROP_ORDEREDBOOKMARKS property indicates whether bookmarks are ordered. If bookmarks are not ordered, then the returned comparison value will be DBCOMPARE_EQ or DBCOMPARE_NE. If bookmarks are ordered, then the returned comparison value will be DBCOMPARE_LT, DBCOMPARE_EQ, or DBCOMPARE_GT. The command that creates the rowset does not have to have an ordered text command, such as an SQL statement containing an ORDER BY clause, to have ordered bookmarks.

Compare can compare any valid bookmarks. The consumer is not required to have permission to read the corresponding rows, nor are the rows even required to exist — for example, they might have been deleted.

If the DBPROP_LITERALBOOKMARKS property is VARIANT_TRUE, then consumers can directly compare values.

Specifying the bookmark DBBMK_FIRST or DBBMK_LAST returns DBCOMPARE_EQ when compared with itself and DBCOMPARE_NE when compared with any other bookmark.

See Also

IRowsetLocate::Hash, IRowsetIdentity::IsSameRow


IRowsetLocate::GetRowsAt

Fetches rows starting with the row specified by an offset from a bookmark.

HRESULT GetRowsAt (
   HWATCHREGION   hReserved1,
   HCHAPTER            hChapter
   ULONG               cbBookmark,
   const BYTE *            pBookmark,
   LONG                  lRowsOffset,
   LONG                  cRows,
   ULONG *               pcRowsObtained,
   HROW **               prghRows);

Parameters

hReserved1

[in]
Reserved for future use. Providers ignore this parameter.

hChapter

[in]
The chapter handle. For nonchaptered rowsets, hChapter is ignored.

cbBookmark

[in]
The length in bytes of the bookmark. This must not be zero.

pBookmark

[in]
A pointer to a bookmark that identifies the base row to be used. This can be a pointer to DBBMK_FIRST or DBBMK_LAST. If lRowsOffset is zero, then the provider fetches this row first; otherwise the provider skips this and subsequent rows up to the count specified in the offset, then fetches the following rows.

lRowsOffset

[in]
The signed count of rows from the origin bookmark to the target row. Deleted rows that the provider has removed from the rowset are not counted in the skip. The first row fetched is determined by the bookmark and this offset. For example, if lRowsOffset is zero, the first row fetched is the bookmarked row; if lRowsOffset is 1, the first row fetched is the row after the bookmarked row; if lRowsOffset is –1, the first row fetched is the row before the bookmarked row.

lRowsOffset can be a negative number only if the value of the DBPROP_CANSCROLLBACKWARDS property is VARIANT_TRUE.

cRows

[in]
The number of rows to fetch. A negative number means to fetch backward. cRows can be a negative number only if the value of the DBPROP_CANFETCHBACKWARDS property is VARIANT_TRUE.

If cRows is zero, no rows are fetched; the fetch direction and the next fetch position are unchanged, and the provider performs no processing, returning immediately from the method invocation. Specifically, lRowsOffset is ignored in this situation.

If the provider does not discover any other errors, the method returns S_OK; whether the provider checks for any other errors is provider-specific.

See the Comments section for a full description of the semantics of lRowsOffset and cRows parameters.

pcRowsObtained

[out]
A pointer to memory in which to return the actual number of fetched rows. If the consumer has insufficient permission to fetch all rows, GetRowsAt fetches all rows for which the consumer has sufficient permission and skips all other rows. If the method fails, *pcRowsObtained is set to zero.

prghRows

[in/out]
A pointer to memory in which to return an array of handles of the fetched rows. If *prghRows is not a null pointer on input, it must be a pointer to memory large enough to return the handles of the requested number of rows. If *prghRows is a null pointer on input, the rowset allocates memory for the row handles and returns the address to this memory. The consumer releases this memory with IMalloc::Free after it releases the row handles. If *prghRows was a null pointer on input and *pcRowsObtained is zero on output or if the method fails, the provider does not allocate any memory and ensures that *prghRows is a null pointer on output.

Return Code

S_OK
The method succeeded.

DB_S_BOOKMARKSKIPPED
The following behavior is only supported on rowsets that set the DBPROP_BOOKMARKSKIPPED property to VARIANT_TRUE. If this property is VARIANT_FALSE, this return code is never returned.

lRowsOffset was zero and the row specified by *pBookmark was deleted or is no longer a member of the rowset, or the row specified by the combination of *pBookmark and lRowsOffset is a row to which the consumer does not have access rights. GetRowsAt skipped that row. The full count of actual rows (cRows) will be met if there are enough rows available. The array of returned row handles does not have gaps for missing rows; the returned count is the number of rows actually fetched.

If a row is skipped, it is counted as one of the rows to be skipped for lRowsOffset. For example, if an offset of 1 is requested and the bookmark points to a row which is now missing, the offset is decremented by 1 and the provider begins by fetching the next row.

If this condition occurs along with another warning condition, the method returns the code for the other warning condition. Thus, whenever a consumer receives the return code for another warning condition, it should check to see whether this condition occurred.

DB_S_ENDOFROWSET
GetRowsAt reached the start or the end of the rowset or chapter and could not fetch all of the requested rows because the count extended beyond the end. The number of rows actually fetched is returned in *pcRowsObtained; this will be less than cRows.

The rowset is being populated asynchronously and no additional rows are available at this time. To determine whether additional rows may be available, the consumer should call IDBAsynchStatus::GetStatus or listen for the IDBAsynchNotify::OnStop notification.

lRowsOffset indicated a position either more than one row before the first row of the rowset or more than one row after the last row, and the provider was a version 2.0 or greater provider. *pcRowsObtained is set to zero and no rows are returned.

DB_S_ROWLIMITEXCEEDED
Fetching the number of rows specified in cRows would have exceeded the total number of active rows supported by the rowset. The number of rows that were actually fetched is returned in *pcRowsObtained. This condition can occur only when there are more rows available than can be handled by the rowset. Thus, this condition never conflicts with those described in DB_S_ENDOFROWSET and DB_S_STOPLIMITREACHED, both of which imply that no more rows were available.

DB_S_STOPLIMITREACHED
Fetching rows required further execution of the command, such as when the rowset uses a server-side cursor. Execution has been stopped because a resource limit has been reached. The number of rows that were actually fetched is returned in *pcRowsObtained.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
cbBookmark was zero or pBookmark was a null pointer.

pcRowsObtained or prghRows was a null pointer.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to instantiate the rows or return the row handles.

E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called and the object is in a zombie state.

DB_E_BADBOOKMARK
*pBookmark was invalid, incorrectly formed, or DBBMK_INVALID.

*pBookmark did not match any rows in the rowset. This includes the case when the row corresponding to the bookmark has been deleted and DBPROP_BOOKMARKSKIPPED was VARIANT_FALSE.

Note Consumers should only attempt to use bookmarks that they have received from the provider. The provider is guaranteed only to handle bookmarks it gives out in a predictable manner. Attempting to use a random value as a bookmark is undefined; the provider may return DB_E_BADBOOKMARK, may return an unexpected row, or may terminate abnormally.

DB_E_BADCHAPTER
The rowset was chaptered and hChapter was invalid.

The rowset was single-chaptered and the specified chapter was not the currently open chapter. The consumer must use the currently open chapter or release the currently open chapter before specifying a new chapter.

DB_E_BADSTARTPOSITION
lRowsOffset indicated a position either more than one row before the first row of the rowset or more than one row after the last row, and the provider was a 1.x provider,

DB_E_CANTFETCHBACKWARDS
cRows was negative and the rowset cannot fetch backward.

DB_E_CANTSCROLLBACKWARDS
lRowsOffset was negative and the rowset cannot scroll backward.

DB_E_NOTREENTRANT
The consumer called this method while it was processing a notification, and it is an error to call this method while processing the specified DBREASON value.

DB_E_ROWSNOTRELEASED
The provider requires release of existing rows before new ones can be fetched. For more information, see DBPROP_CANHOLDROWS in "Rowset Properties" in Appendix C.

DB_SEC_E_PERMISSIONDENIED
The consumer did not have sufficient permission to fetch any of the rows; no rows were fetched.

Comments

Given values for a bookmark, offset, and number of rows to fetch:

  • The provider determines a starting position for the fetch as the bookmark plus the offset.

  • If the starting position is off either end of the rowset, the method returns DB_E_BADSTARTPOSITION.

  • If the starting position is in the rowset, the provider fetches rows in the specified direction until it has fetched the requested number of rows or hits the end of the rowset.

  • If it fetches the requested number of rows, it returns S_OK. If it hits the end of the rowset, it stops fetching, returns DB_S_ENDOFROWSET, and returns the rows it has fetched.

  • The rows are returned in rowset traversal order; that is, the direction in which they were fetched. For example, if the consumer passes the bookmark for row 10 to GetRowsAt and specifies cRows equal to – 5 and lRowsOffset equal to 0, GetRowsAt returns rows 10 through 6. That is, *prghRows[0] is the handle for row 10 and *prghRows[4] is the handle for row 6.

The following table defines the behavior of GetRowsAt for all combinations of these parameters.

Variable | Description
N | Total rows in the rowset.
B | Row in the rowset specified by the bookmark parameters cbBookmark and pBookmark. Assume 1 ≤ B ≤ N, DBBMK_FIRST is equivalent to B = 1, and DBBMK_LAST is equivalent to B = N.
F | lRowsOffset
r | cRows

B + F | r | cRowsObtained | Return code
B + F < 1 or B + F > N | N/A | 0 | DB_E_BADSTARTPOSITION
1 ≤ B + F ≤ N | 0 | 0 | S_OK
1 ≤ B + F ≤ N | 1 ≤ r ≤ N – B – F + 1 | r | S_OK
1 ≤ B + F ≤ N | r > N – B – F + 1 | N – B – F+1 | DB_S_ENDOFROWSET
1 ≤ B + F ≤ N | r < 0 and abs(r) ≤ B + F | abs(r) | S_OK
1 ≤ B + F ≤ N | r < 0 and abs(r) > B + F | B + F | DB_S_ENDOFROWSET

Consumers that want to fetch forward, for example, 20 rows at a time, can use the call GetRowsAt( ..., B, 1, 20, ...) which allows for an easy check when reaching the end of the rowset. Similarly, consumers that want to fetch backward can use the call GetRowsAt(..., B, – 1, – 20, ...) to facilitate the detection of the beginning of the rowset. In the latter case, DBPROP_CANFETCHBACKWARDS must be set to VARIANT_TRUE.

The DBPROP_BOOKMARKSKIPPED property defines the behavior for cases in which there is no exact match for the bookmark pointed to by pBookmark. If this property is set to VARIANT_FALSE, then DB_E_BADBOOKMARK will be returned because the corresponding position is not well-defined. The behavior when this property is set to VARIANT_TRUE is as follows:

  • If lRowsOffset is zero, then the row is skipped, the requested number of rows are fetched from subsequent rows, and a DB_S_BOOKMARKSKIPPED warning is returned.

  • If lRowsOffset and cRows are any value except zero, the fetch succeeds, based on the position of the row before it became invalid. For example, suppose row 20 is deleted. If a consumer calls GetRowsAt with lRowsOffset set to 5 and a bookmark that points to row 20, then GetRowsAt moves to row 25.

If the provider encounters a problem fetching a row — for example, data stored in a text file contains a letter in a numeric column — GetRowsAt fetches the row normally, returns the row handle, and returns S_OK. However, when the consumer calls IRowset::GetData for the row, the provider returns DBSTATUS_E_CANTCONVERTVALUE as the status for the offending column.

For information about what GetRowsAt does when it fetches a row that it already has in its internal buffers, see "Uniqueness of Rows in the Rowset" in Chapter 4. For information about whether GetRowsAt can detect changes made to rows in the rowset, see "Visibility of Changes" in Chapter 5.

GetRowsAt increments the reference count of each row for which it returns a handle by 1. Thus, if a handle is returned for a row that has already been fetched, the reference count of that row will be greater than 1. ReleaseRows must be called once for each time the handle to a row has been returned.

In order to use GetRowsAt several times in sequence to obtain successive rows, the consumer should obtain the bookmark of the last row of the previous set and use that, with a skip of 1, to position for fetching the next rows.

See Also

IRowset::GetData, IRowset::GetNextRows, IRowsetLocate::GetRowsByBookmark, IRowsetScroll::GetRowsAtRatio


IRowsetLocate::GetRowsByBookmark

Fetches the rows that match the specified bookmarks.

HRESULT GetRowsByBookmark (
   HCHAPTER         hChapter,
   ULONG            cRows,
   const ULONG      rgcbBookmarks[],
   const BYTE *         rgpBookmarks[],
   HROW                rghRows[],
   DBROWSTATUS   rgRowStatus[]);

Parameters

hChapter

[in]
The chapter handle. For nonchaptered rowsets, hChapter is ignored.

cRows

[in]
The number of rows to fetch.

If cRows is zero, no rows are fetched; the fetch direction and the next fetch position are unchanged, and the provider performs no processing, returning immediately from the method invocation.

If the provider does not discover any other errors, the method returns S_OK; whether the provider checks for any other errors is provider-specific.

rgcbBookmarks

[in]
An array containing the length in bytes of each bookmark.

rgpBookmarks

[in]
An array containing a pointer to the bookmark of each row sought. These cannot be pointers to a standard bookmark (DBBMK_FIRST, DBBMK_LAST, DBBMK_INVALID). If rgpBookmarks contains a duplicate bookmark, the corresponding row is fetched and the reference count incremented once for each occurrence of the bookmark.

rghRows

[out]
An array with cRows elements in which to return the handles of the fetched rows. The consumer allocates this array but is not required to initialize the elements of it. In each element of this array, if the row was fetched, the provider returns the handle of the row identified by the bookmark in the corresponding element of rgpBookmarks. If the row was not fetched, the provider returns DB_NULL_HROW.

rgRowStatus

[out]
An array with cRows elements in which to return values indicating the status of each row specified in rgpBookmarks. If no errors or warnings occur while fetching a row, the corresponding element of rgRowStatus is set to DBROWSTATUS_S_OK. If an error occurs while fetching a row, the corresponding element is set as specified in DB_S_ERRORSOCCURRED. The consumer allocates memory for this array but is not required to initialize it. If rgRowStatus is a null pointer, no row statuses are returned. For information about the DBROWSTATUS enumerated type, see "Arrays of Errors" in Chapter 13.

Return Code

S_OK
The method succeeded. All rows were successfully fetched. The following value can be returned in rgRowStatus:

  • The row was successfully fetched. The corresponding element of rgRowStatus contains DBROWSTATUS_S_OK.

DB_S_ERRORSOCCURRED
An error occurred while fetching a row, but at least one row was successfully fetched. Successes can occur for the reasons listed under S_OK. The following errors can occur:

  • An element of rgpBookmarks pointed to an invalid or incorrectly formed bookmark. The corresponding element of rgRowStatus contains DBROWSTATUS_E_INVALID.

  • An element of rgpBookmarks pointed to a bookmark that did not match any rows in the rowset. This includes the case when the row corresponding to the bookmark has been deleted. The corresponding element of rgRowStatus contains DBROWSTATUS_E_INVALID.

  • An element of rgcbBookmarks was zero. The corresponding element of rgRowStatus contains DBROWSTATUS_E_INVALID.

  • An element of rgpBookmarks was a null pointer. The corresponding element of rgRowStatus contains DBROWSTATUS_E_INVALID.

  • An element of rgpBookmarks pointed to a standard bookmark (DBBMK_FIRST, DBBMK_LAST, DBBMK_INVALID). The corresponding element of rgRowStatus contains DBROWSTATUS_E_INVALID.

  • The row corresponding to the bookmark was not fetched because fetching it would have exceeded the total number of active rows supported by the rowset. The corresponding element of rgRowStatus contains DBROWSTATUS_E_LIMITREACHED.

  • The row corresponding to the bookmark was not fetched for the following reason. Fetching rows required further execution of the command, such as when the rowset uses a server-side cursor. Execution has been stopped because a resource limit has been reached. Execution cannot be resumed. The corresponding element of rgRowStatus contains DBROWSTATUS_E_LIMITREACHED.

  • The rowset ran out of memory and was unable to fetch the row. The corresponding element of rgRowStatus contains DBROWSTATUS_E_OUTOFMEMORY.

  • The consumer did not have sufficient permission to fetch a row. The corresponding element of rgRowStatus contains DBROWSTATUS_E_PERMISSIONDENIED.

  • The consumer encountered a recoverable, provider-specific error, such as an RPC failure when transmitting the change to a remote server. The corresponding element of rgRowStatus contains DBROWSTATUS_E_FAIL.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
rghRows was a null pointer.

rgcbBookmarks or rgpBookmarks was a null pointer.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to return the row handles.

E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called and the object is in a zombie state.

DB_E_BADCHAPTER
The rowset was chaptered and hChapter was invalid.

The rowset was single-chaptered and the specified chapter was not the currently open chapter. The consumer must use the currently open chapter or release the currently open chapter before specifying a new chapter.

DB_E_ERRORSOCCURRED
Errors occurred while fetching all of the rows. Errors can occur for the reasons listed under DB_S_ERRORSOCCURRED.

DB_E_NOTREENTRANT
The consumer called this method while it was processing a notification, and it is an error to call this method while processing the specified DBREASON value.

DB_E_ROWSNOTRELEASED
The provider requires release of existing rows before new ones can be fetched. For more information, see DBPROP_CANHOLDROWS in "Rowset Properties" in Appendix C.

Comments

GetRowsByBookmark increments the reference count of each row for which it returns a handle by 1. Thus, if a handle is returned for a row that has already been fetched, the reference count of that row will be greater than 1. ReleaseRows must be called once for each time the handle to a row has been returned.

If the provider encounters a problem fetching a row — for example, data stored in a text file contains a letter in a numeric column — GetRowsByBookmark fetches the row normally, returns the row handle, and returns S_OK. However, when the consumer calls IRowset::GetData for the row, the provider returns DBSTATUS_E_CANTCONVERTVALUE as the status for the offending column.

If GetRowsByBookmark encounters an error while attempting to fetch a row, such as a bad bookmark or adeleted row, it notes the error in the rgRowStatus array, continues processing, and returns DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED. Although the rows are fetched in undefined order, the ordering of the error array must match the order of the bookmark array, so the consumer can perform a side-by-side scan of each array to determine which rows were not fetched.

For information about what GetRowsByBookmark does when it fetches a row that it already has in its internal buffers, see "Uniqueness of Rows in the Rowset" in Chapter 4. For information about whether GetRowsByBookmark can detect changes made to rows in the rowset, see "Visibility of Changes" in Chapter 5.

See Also

IRowset::GetData, IRowset::GetNextRows, IRowsetLocate::GetRowsAt, IRowsetScroll::GetRowsAtRatio


IRowsetLocate::Hash

Returns hash values for the specified bookmarks.

HRESULT Hash (
   HCHAPTER         hChapter,
   ULONG            cBookmarks,
   const ULONG      rgcbBookmarks[],
   const BYTE *         rgpBookmarks[],
   DWORD            rgHashedValues[],
   DBROWSTATUS   rgBookmarkStatus[]);

Parameters

hChapter

[in]
The chapter handle. For nonchaptered rowsets, hChapter is ignored.

cBookmarks

The number of bookmarks to hash. If cBookmarks is zero, Hash does not do anything.

rgcbBookmarks

[in]
An array containing the length in bytes for each bookmark.

rgpBookmarks

[in]
An array of pointers to bookmarks. The bookmarks cannot be standard bookmarks (DBBMK_FIRST, DBBMK_LAST, DBBMK_INVALID). If rgpBookmarks contains a duplicate bookmark, a hash value is returned once for each occurrence of the bookmark.

Caution The consumer must ensure that all bookmarks in rgpBookmarks are valid. The provider is not required to validate bookmarks before hashing them. Thus, hash values might be returned for invalid bookmarks.

rgHashedValues

[out]
An array of cBookmarks hash values corresponding to the elements of rgpBookmarks. The consumer allocates, but is not required to initialize, memory for this array and passes the address of this memory to the provider. The provider returns the hash values in the array.

rgBookmarkStatus

[out]
An array with cBookmarks elements in which to return values indicating the status of each bookmark specified in rgpBookmarks. If no errors occur while hashing a bookmark, the corresponding element of rgBookmarkStatus is set to DBROWSTATUS_S_OK. If an error occurs while hashing a bookmark, the corresponding element is set as specified in DB_S_ERRORSOCCURRED. The consumer allocates memory for this array but is not required to initialize it. If rgBookmarkStatus is a null pointer, no bookmark statuses are returned. For information about the DBROWSTATUS enumerated type, see "Arrays of Errors" in Chapter 13.

Return Code

S_OK
The method succeeded. All bookmarks were successfully hashed. The following value can be returned in rgRowStatus:

  • The bookmark was successfully hashed. The corresponding element of rgRowStatus contains DBROWSTATUS_S_OK.

DB_S_ERRORSOCCURRED
An error occurred while hashing a bookmark, but at least one bookmark was successfully hashed. Successes can occur for the reason listed under S_OK. The following errors can occur:

  • An element of rgpBookmarks pointed to an invalid or incorrectly formed bookmark. The corresponding element of rgBookmarkStatus contains DBROWSTATUS_E_INVALID. Providers are not required to check for this condition, because doing so might slow the method significantly.

  • An element of rgcbBookmarks was zero. The corresponding element of rgBookmarkStatus contains DBROWSTATUS_E_INVALID.

  • An element of rgpBookmarks was a null pointer. The corresponding element of rgBookmarkStatus contains DBROWSTATUS_E_INVALID.

  • An element of rgpBookmarks pointed to a standard bookmark (DBBMK_FIRST, DBBMK_LAST, DBBMK_INVALID). The corresponding element of rgBookmarkStatus contains DBROWSTATUS_E_INVALID.

  • The consumer encountered a recoverable, provider-specific error, such as an RPC failure when transmitting the change to a remote server. The corresponding element of rgRowStatus contains DBROWSTATUS_E_FAIL.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
cBookmarks was not zero and rgcbBookmarks or rgpBookmarks was a null pointer.

rgHashedValues was a null pointer.

E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called and the object is in a zombie state.

DB_E_BADCHAPTER
The rowset was chaptered and hChapter was invalid.

The rowset was single-chaptered and the specified chapter was not the currently open chapter. The consumer must use the currently open chapter or release the currently open chapter before specifying a new chapter.

DB_E_ERRORSOCCURRED
Errors occurred while hashing all of the bookmarks. Errors can occur for the reasons listed under DB_S_ERRORSOCCURRED.

DB_E_NOTREENTRANT
The provider called a method from IRowsetNotify in the consumer that had not yet returned, and the provider does not support reentrancy in this method.

Comments

This method makes no logical change to the state of the object.

If two bookmarks point to the same underlying row, the same hash value is returned for both bookmarks. That is, if Compare returns DBCOMPARE_EQ for two bookmarks, Hash must return the same value for these two bookmarks.

If Hash encounters an error while attempting to hash a bookmark, such as a bad bookmark, it notes the error in the error array, continues processing, and returns DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED. Although the rows are hashed in undefined order, the ordering of the error array must match the order of the bookmark array, so the consumer can perform a side-by-side scan of each array to determine which rows were not hashed.

Hash can hash any valid bookmarks. The consumer is not required to have permission to read the corresponding row, nor is the row even required to exist — for example, it might have been deleted.

Standard bookmarks cannot be hashed.

See Also

IRowsetLocate::Compare


IRowsetNotify

IRowsetNotify is the callback interface that a consumer must support to connect to local notifications provided by a rowset. The notifications are intended to synchronize objects that are attached to the same rowset instance. The notifications do not reflect changes in underlying shared tables that occur through other programs or users.

The notifications use the standard OLE connection point scheme for events. A rowset supports IConnectionPointContainer and the consumer calls FindConnectionPoint for IID_IRowsetNotify to obtain the correct IConnectionPoint interface. The consumer then advises that connection point to connect and supplies a pointer to the consumer's IRowsetNotify interface.

For more information about notifications, see Chapter 9, "Notifications."

When to Implement

IRowsetNotify is implemented by consumers that require notification. If the command requests support for IConnectionPointContainer, then the rowset is required to support a connection point for IRowsetNotify. Providers should implement this connection point if they expect to work directly with general purpose consumers.

Notification about transactions is not handled through rowsets. There may be multiple rowsets for each transaction, leading to a flood of events. Consumers that need to be notified of transaction activity should connect to the transaction coordinator. For more information, see "Notification Events" in Chapter 12.

IRowset is a prerequisite for notification.

DBEVENTPHASE

Events may have phases. A phased event is a notification method that is called multiple times, each time with a different phase in the sequence. Some events do not have phases. The phases are similar to phases in two-phase commit protocol, because ensuring that all controls authorize and succeed in handling an event is a problem very similar to ensuring resource managers all agree to, and succeed in, committing a transaction.

In the following descriptions, all consumer objects that connect their IRowsetNotify interface to the rowset are the listeners.

Value | Description
DBEVENTPHASE_ OKTODO | Informs a listener of an impending event. All listeners must return S_OK from DBEVENTPHASE_OKTODO for the event to proceed. Any listener can cancel the event by returning S_FALSE. The listener can prepare for the event, but should do nothing irreversible or time consuming.
 | If a listener cancels the event, all listeners that have already been called will be called again with DBEVENTPHASE_FAILEDTODO.
DBEVENTPHASE_ ABOUTTODO | Informs a listener that DBEVENTPHASE_OKTODO has been approved and all listeners can proceed to final preparations which must be reversible, but which may be lengthy. The listener should cancel this phase only if it is stopped by an error; it should have cleared all logical objections at the earlier DBEVENTPHASE_OKTODO phase. If a listener cancels the event by returning S_FALSE, all listeners that have already been called will be called again with DBEVENTPHASE_FAILEDTODO.
DBEVENTPHASE_ SYNCHAFTER | Informs a listener that the event has occurred after the rowset's copy of the row has been modified, but before the data source has received the change. The listener can synchronize itself with the rowset and ensure that it has no physical reason not to agree to commit the event's changes. If a listener cancels the event by returning S_FALSE, all listeners that have already been called will be called again with DBEVENTPHASE_FAILEDTODO.
DBEVENTPHASE_ FAILEDTODO | Informs a listener that has previously been called for this event that the event has failed. The listener should reverse all changes and synchronize with the state of the rowset.
DBEVENTPHASE_ DIDEVENT | Informs a listener that all consumers have synchronized themselves and agreed to commit the event's changes. The listener should now commit its changes. It must comply.

The final phase of an event is always either DBEVENTPHASE_FAILEDTODO or DBEVENTPHASE_DIDEVENT. If the event has no phases, it is equivalent to DBEVENTPHASE_DIDEVENT.

All providers must support the DBEVENTPHASE_FAILEDTODO and DBEVENTPHASE_DIDEVENT phases. Whether providers support DBEVENTPHASE_OKTODO, DBEVENTPHASE_ABOUTTODO, and DBEVENTPHASE_SYNCHAFTER is provider specific, although all but the most simple providers support these phases. To determine which phases a provider supports, a consumer calls IDBProperties::GetProperties for the DBPROP_NOTIFICATIONPHASES property.

If a method changes multiple rows and generates a single-phased event, such as DBREASON_ROW_ACTIVATE or DBREASON_ROW_RELEASE, the provider makes a single call to IRowsetNotify::OnRowChange and passes an array containing the handles of all of the affected rows.

If a method changes multiple rows and generates a multiphased event, such as DBREASON_ROW_UPDATE or DBREASON_ROW_UNDOCHANGE, the number of calls to OnRowChange for each phase depends on the DBPROP_NOTIFICATIONGRANULARITY property.

It is provider-specific whether or not listeners who register after some but not all of the phases of an event have been fired are called for the remaining phases of that event. Listeners must be prepared to get only the final phases of an event upon registering for notifications on an object that is already firing notifications.

DBREASON

Reasons are fine tunings of events. The receiver of events is expected to care only about the general effect of the event, but there may be other effects that more specialized consumers need to know about. The DBEVENT is the basic event, and the DBREASON is a single level of derived subevent.

Important The DBREASON types may be expanded in later versions of OLE DB. IRowsetNotify methods must return S_OK or DB_S_UNWANTEDREASON when they receive a DBREASON value they do not recognize.

Providers are not expected to add new DBREASONs to the defined set. Providers defining new DBREASONs should do so through an entirely new notification interface with its own IID.

The following #define values are valid for DBREASON.

Value Description
DBREASON_ROW_
ASYNCHINSERT 		The rowset is being randomly asynchronously populated and one or more rows have been inserted. Only the DBEVENTPHASE_DIDEVENT phase occurs.
DBREASON_
ROWSET_
FETCHPOSITIONCHANGE 		The next fetch position changed as a result of a call to IRowset::GetNextRows or IRowset::RestartPosition. All phases can occur.
DBREASON_
ROWSET_RELEASE 		The rowset is being released; that is, its reference count is zero. Only the DBEVENTPHASE_DIDEVENT phase occurs. The rowset no longer exists when this event is sent out, so recipients must not make any calls upon it. Due to OLE rules, there is no way to deny permission to IUnknown::Release.
DBREASON_
ROWSET_CHANGED 		The rowset metadata has changed.

Pointers to interfaces on the rowset remain valid.

Providers must revalidate accessors against the metadata, and this might fail due to the changes in the metadata. For example, the accessor might now specify an unsupported conversion.

Consumers should free any held row handles when a notification with this DBREASON is generated, because handles to rows are likely to have become invalid. For example, IRowset::GetData might return DB_E_BADROWHANDLE, or IRowsetUpdate::Update might return DBROWSTATUS_E_INVALID. The result of passing such a row handle is provider-specific, although the provider cannot terminate abnormally.

Only the DBEVENTPHASE_DIDEVENT phase occurs.
DBREASON_
COLUMN_SET 		A column value is set. All phases can occur.
DBREASON_
COLUMN_RECALCULATED 		A calculated column takes a new value because its input columns change. Only the DBEVENTPHASE_DIDEVENT phase occurs.
DBREASON_
ROW_ACTIVATE 		A function, such as IRowset::GetNextRows or IRowsetLocate::GetRowsAt, caused a new set of rows to be fetched. Only the DBEVENTPHASE_DIDEVENT phase occurs.
DBREASON_
ROW_RELEASE 		IRowset::ReleaseRows was called and the row's reference count is zero. The array of row handles the provider passed to the listeners is the subset of the original array of row handles passed to ReleaseRows; that is, those row handles for which the reference count is zero. Only the DBEVENTPHASE_DIDEVENT phase occurs. The returned row handles might be invalid and, therefore, should not be used with any methods.
DBREASON_
ROW_DELETE 		IRowsetChange::DeleteRows has been called. All phases can occur.
DBREASON_
ROW_FIRSTCHANGE 		The first time any column in the row is set, this notification occurs. It precedes DBREASON_COLUMN_SET. All phases can occur. This event occurs only when the rowset is in delayed update mode. It occurs the first time a column in a row is modified after the row was fetched or after the last call to IRowsetUpdate::Update or IRowsetUpdate::Undo, whichever is more recent.
DBREASON_
ROW_INSERT 		IRowsetChange::InsertRow has been called. All phases can occur.
DBREASON_
ROW_RESYNCH 		IRowsetRefresh::RefreshVisibleData has been called. All phases can occur.
DBREASON_
ROW_UNDOCHANGE 		IRowsetUpdate::Undo has been called on a pending change row. All phases can occur.
DBREASON_
ROW_UNDOINSERT 		IRowsetUpdate::Undo has been called on a pending insert row. All phases can occur.
DBREASON_
ROW_UNDODELETE 		IRowsetUpdate::Undo has been called on a pending delete row. All phases can occur.
DBREASON_
ROW_UPDATE 		IRowsetUpdate::Update has been called on a row with a pending change. All phases can occur.

The following table lists all rowset methods, the DBREASON values they generate, and the phases for each reason. Nested notifications occur when the consumer calls another rowset method while processing a notification.

Method | DBREASON generated 1 | Phases
IUnknown::AddRef | None | N/A
IUnknown::QueryInterface | None | N/A
IUnknown::Release | _ROWSET_RELEASE | DIDEVENT
IRowset::AddRefRows | None | N/A
IRowset::GetData | None | N/A
IRowset::GetNextRows | _ROW_ACTIVATE | DIDEVENT
 | _ROWSET_FETCHPOSITION CHANGE | All Phases
IRowset::ReleaseRows | _ROW_RELEASE | DIDEVENT
IRowset::RestartPosition | _ROWSET_FETCHPOSITION CHANGE | All Phases
 | _ROWSET_CHANGED 2 | All Phases
IRowsetChange::DeleteRows | _ROW_DELETE | All Phases
IRowsetChange::InsertRow | _ROW_INSERT | All Phases
IRowsetChange::SetData | _ROW_FIRSTCHANGE 3 | All Phases
 | _COLUMN_SET | All Phases
 | _COLUMN_ RECALCULATED | DIDEVENT
IRowsetIndex::Seek | _ROWSET_FETCHPOSITION CHANGE | All Phases
IRowsetLocate::Compare | None | N/A
IRowsetLocate::GetRowsAt | _ROW_ACTIVATE | DIDEVENT
IRowsetLocate:: GetRowsByBookmark | _ROW_ACTIVATE | DIDEVENT
IRowsetLocate::Hash | None | N/A
IRowsetRefresh::GetLastVisibleData | None | N/A
IRowsetRefresh::RefreshVisibleData | _ROW_RESYNCH | All Phases
IRowsetScroll:: GetApproximatePosition | None | N/A
IRowsetScroll::GetRowsAtRatio | _ROW_ACTIVATE | DIDEVENT
IRowsetUpdate::GetOriginalData | None | N/A
IRowsetUpdate::GetPendingRows | None | N/A
IRowsetUpdate::Undo | _ROW_UNDOCHANGE | All Phases
 | _ROW_UNDOINSERT | All Phases
 | _ROW_UNDODELETE | All Phases
IRowsetUpdate::Update | _ROW_UPDATE | All Phases
All other rowset methods | None | N/A
1Methods generate only the listed DBREASON values, even though other reasons might seem appropriate. For example, InsertRow generates only DBREASON_ROW_INSERT even though DBREASON_COLUMN_SET, DBREASON_ROW_FIRSTCHANGE, and DBREASON_ROW_ACTIVATE might appear to be applicable. 2IRowset::RestartPosition generates this DBREASON only when the metadata for the columns has changed. 3DBREASON_ROW_FIRSTCHANGE is generated only the first time a column in the row is changed. For more information, see the table earlier in this section.

Method | Description
OnFieldChange | Notifies the consumer of any change to the value of a column.
OnRowChange | Notifies the consumer of the first change to a row, or any change that affects the entire row.
OnRowsetChange | Notifies the consumer of any change affecting the entire rowset.


IRowsetNotify::OnFieldChange

Notifies the consumer of any change to the value of a column.

HRESULT OnFieldChange (
   IRowset*            pRowset,
   HROW               hRow,
   ULONG            cColumns,
   ULONG            rgColumns[],
   DBREASON         eReason,
   DBEVENTPHASE   ePhase,
   BOOL               fCantDeny);

Parameters

pRowset

[in]
A pointer to the rowset, because the consumer may be receiving notifications from multiple rowsets and this identifies which one is calling.

hRow

[in]
The handle of the row in which the column value was changed. After this method returns, the reference count of this row will be unchanged unless the consumer explicitly changes it. This is different from other methods that return rows to the consumer, in which the provider explicitly increments the reference count. Therefore, if the consumer wants to guarantee that this row handle is valid after this method returns, it must call IRowset::AddRefRows for the row while it is processing this method.

cColumns

[in]
The count of columns in rgColumns.

rgColumns

[in]
An array of columns in the row for which the value was changed.

eReason

[in]
The reason of the event that caused this change. If this value is not recognized by the method, the method returns S_OK or DB_S_UNWANTEDREASON.

ePhase

[in]
The phase of this notification.

fCantDeny

[in]
When this flag is set to TRUE, the consumer cannot veto the event by returning S_FALSE because the provider cannot undo the event.

Return Code

S_OK
The method succeeded.

S_FALSE
The event/phase is vetoed by reason of logical objection or a failure to be able to implement.

DB_S_UNWANTEDPHASE
The consumer is not interested in receiving this phase for this reason. The provider can optimize by making no further calls with this reason and phase. The phases for other reasons are unaffected.

DB_S_UNWANTEDREASON
The consumer is not interested in receiving any phases for this reason. The provider can optimize by making no further calls with this reason.

E_FAIL
A consumer-specific error occurred. Unless the consumer also returns S_FALSE, the provider continues the operation.

Comments

Possible reasons are DBREASON_COLUMN_SET and DBREASON_COLUMN_RECALCULATED.

All phases can occur for a DBREASON_COLUMN_SET event. The new value is visible beginning with the DBEVENTPHASE_SYNCHAFTER phase. For a DBREASON_COLUMN_RECALCULATED event, only the DBEVENTPHASE_DIDEVENT phase occurs.

Setting a column to its current value may, but is not required to, generate a notification for that column.

Unless the consumer returns S_FALSE, the provider continues the operation. This is true even if the consumer returns E_FAIL or any other error code.

See Also

IRowsetNotify::OnRowChange, IRowsetNotify::OnRowsetChange


IRowsetNotify::OnRowChange

Notifies the consumer of the first change to a row, or any change that affects the entire row.

HRESULT OnRowChange (
   IRowset*            pRowset,
   ULONG            cRows,
   const HROW         rghRows[],
   DBREASON         eReason,
   DBEVENTPHASE   ePhase,
   BOOL               fCantDeny);

Parameters

pRowset

[in]
A pointer to the rowset, because the consumer may be receiving notifications from multiple rowsets and this identifies which one is calling.

cRows

[in]
The count of row handles in rghRows.

rghRows

[in]
An array of handles of rows that are changing. This array belongs to the caller (rowset) and must not be freed or used beyond the duration of the method call.

After this method returns, the reference count of these rows will be unchanged unless the consumer explicitly changes them. This is different from other methods that return rows to the consumer, in which the provider explicitly increments the reference counts. Therefore, if the consumer wants to guarantee that these row handles are valid after this method returns, it must call IRowset::AddRefRows for these rows while it is processing this method.

eReason

[in]
The reason of the event that caused this change. If this value is not recognized by the method, the method returns S_OK or DB_S_UNWANTEDREASON.

ePhase

[in]
The phase of this notification.

fCantDeny

[in]
When this flag is set to TRUE, the consumer cannot veto the event by returning S_FALSE because the provider cannot undo the event.

Return Code

S_OK
The method succeeded.

S_FALSE
The event/phase is vetoed by reason of logical objection or a failure to be able to implement, as permitted for the phase.

DB_S_UNWANTEDPHASE
The consumer is not interested in receiving this phase for this reason. The provider can optimize by making no further calls with this reason and phase. The phases for other reasons are unaffected.

DB_S_UNWANTEDREASON
The consumer is not interested in receiving any phases for this reason. The provider can optimize by making no further calls with this reason.

E_FAIL
A consumer-specific error occurred. Unless the consumer also returns S_FALSE, the provider continues the operation.

Comments

Possible reasons are any of the DBREASON_ROW_* reasons.

Phases occur as documented for the DBREASON enumeration. Prior values remain in the row up to and including the DBEVENTPHASE_ABOUTTODO phase. The new value is visible with the DBEVENTPHASE_SYNCHAFTER or later phases.

When a DBREASON_ROW_ACTIVATE notification is triggered, the array of row handles passed to OnRowChange is exactly the order in which the rows were fetched.

Unless the consumer returns S_FALSE, the provider continues the operation. This is true even if the consumer returns E_FAIL or any other error code.

See Also

IRowsetNotify::OnFieldChange, IRowsetNotify::OnRowsetChange


IRowsetNotify::OnRowsetChange

Notifies the consumer of any change affecting the entire rowset.

HRESULT OnRowsetChange (
   IRowset*            pRowset,
   DBREASON         eReason,
   DBEVENTPHASE   ePhase,
   BOOL               fCantDeny);

Parameters

pRowset

[in]
A pointer to the rowset, because the consumer may be receiving notifications from multiple rowsets and this identifies which one is calling.

eReason

[in]
The reason for the event that caused this change. If this value is not recognized by the method, the method returns S_OK or DB_S_UNWANTEDREASON.

ePhase

[in]
The phase of this notification.

fCantDeny

[in]
When this flag is set to TRUE, the consumer cannot veto the event by returning S_FALSE because the provider cannot undo the event.

Return Code

S_OK
The method succeeded.

S_FALSE
The event/phase is vetoed by reason of logical objection or a failure to be able to implement.

DB_S_UNWANTEDPHASE
The consumer is not interested in receiving this phase for this reason. The provider can optimize by making no further calls with this reason and phase. The phases for other reasons are unaffected.

DB_S_UNWANTEDREASON
The consumer is not interested in receiving any phases for this reason. The provider can optimize by making no further calls with this reason.

E_FAIL
A consumer-specific error occurred. Unless the consumer also returns S_FALSE, the provider continues the operation.

Comments

Possible reasons are any of the DBREASON_ROWSET_* reasons.

No phases occur; this method is equivalent to DBEVENTPHASE_DIDEVENT.

See Also

IRowsetNotify::OnFieldChange, IRowsetNotify::OnRowChange


IRowsetRefresh

IRowsetRefresh is used to retrieve the values for rows that are currently visible to the transaction.

When to Implement

IRowsetRefresh is optional. If it is exposed, the rowset must also expose IRowsetIdentity.

To support IRowsetRefresh, the provider must be able to get the latest value of the data that is visible to the transaction. If the value of the DBPROP_OTHERUPDATEDELETE property is VARIANT_TRUE, such as when the rowset is implemented on top of a dynamic or keyset-driven cursor in ODBC, the rowset can do this by the mechanism it uses to fetch rows from the server. However, if the value of DBPROP_OTHERUPDATEDELETE is VARIANT_FALSE, such as when the rowset is implemented on top of a static cursor in ODBC, the rowset must have another mechanism for retrieving data from the data source, such as executing a separate command to retrieve the currently visible values. If the rowset does not have such a mechanism, it cannot support this interface.

When to Call

IRowsetRefresh is used to synchronize rows in the rowset with those in the data source. The primary uses of IRowsetRefresh are:

  • To implement optimistic concurrency.

  • To perform row fix-up in a disconnected environment.
Method | Description
GetLastVisibleData | Gets the most recent data from either the visible data cache or the data source.
RefreshVisibleData | Retrieves the data values from the data source that are visible to the transaction for the specified rows.


IRowsetRefresh::GetLastVisibleData

Gets the most recent data from either the visible data cache or the data source.

HRESULT GetLastVisibleData (
HROW       hRow,
HACCESSOR    hAccessor,
void *         pData);

Parameters

hRow

[in]
The handle of the row with pending changes for which to get the latest 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 GetLastVisibleData 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. To determine the columns for which data was returned, the consumer checks the status values. For a list of status values that can be returned by this method, see “Status Values Used When Getting Data” in “Status” in Chapter 6.

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

hRow was invalid.

DB_E_ERRORSOCCURRED

Errors occurred while returning data for all columns. To determine what errors occurred, the consumer checks the status values. For a list of status values that can be returned by this method, see “Status Values Used When Getting Data” in “Status” in Chapter 6.

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 GetLastVisibleData to retrieve the latest visible data from the data source.

For providers that cache visible data, this is visible data obtained in the most recent call to IRowsetRefresh::RefreshVisibleData for this row. On a given row with pending changes, until there has been a successful call to refresh the visible data cache with IRowsetRefresh::RefreshVisibleData, this method will return the same data as a call to IRowsetUpdate::GetOriginalData for that row. On a given row with no pending changes, this method will return the same data as a call to IRowset::GetData for that row.

For providers that don't cache visible data, or for deferred columns, this is the data currently visible from the data source. For a complete description of how GetLastVisibleData retrieves data, see “Getting Data” in Chapter 6.

GetVisibleData is usually used in combination with IRowset::GetData and IRowsetUpdate::GetOriginalData to allow consumers to display collisions occurring with optimistic updates. After a row collision and a call to IRowsetRefresh::RefreshVisibleData to obtain the latest data source values for the conflicting rows, the three GetData-style functions collectively allow consumers access to all three sets of values needed to analyze the collision and decide on a resolution. The consumer can gain access to the pending-change values (GetData), the original values of the rowset before any changes were made (GetOriginalData), and the newly changed values at the data source that caused the collision (GetLastVisibleData).

If GetLastVisibleData fails, the memory to which pData points is not freed but its contents are undefined. If, before GetLastVisibleData failed, the provider allocated any memory for return to the consumer, the provider frees this memory and does not return it to the consumer.

There is a difference between calling GetOriginalData and calling GetLastVisibleData. In a delayed update mode, if data is changed by another consumer, different consumers may retrieve different data. For example, the value in Column 1 is X. In a delayed update mode, Consumer A changes the value in that column to Y but does not transmit this action to the data source. Consumer B then changes the value in Column 1 to Z. If Consumer A calls GetOriginalData, it gets X. However, if it calls GetLastVisibleData, using a dirty read, it will retrieve Z.

See Also

IRowset::GetData, IRowsetUpdate::GetOriginalData, IRowsetRefresh::RefreshVisibleData


IRowsetRefresh::RefreshVisibleData

Retrieves the data values from the data source that are visible to the transaction for the specified rows.

HRESULT RefreshVisibleData (
HCHAPTER          hChapter,
ULONG             cRows,
const HROW       rghRows[],
BOOL                fOverwrite,
ULONG*            pcRowsRefreshed,
HROW**            prghRowsRefreshed,
DBROWSTATUS**     prgRowStatus);

Parameters

hChapter

[in]
The chapter handle. For nonchaptered rowsets, hChapter is ignored.

cRows

[in]
The count of rows to refresh. If cRows is zero, RefreshVisibleData ignores rghRows and reads in current values for all active rows.

rghRows

[in]
An array of cRows row handles to be refreshed. If cRows is zero, this argument is ignored.

fOverwrite

[in]
TRUE if the provider should discard any pending changes to a given row and accept the visible values as the new current values; FALSE otherwise.

pcRowsRefreshed

[out]
A pointer to memory in which to return the number of rows the method attempted to refresh. If this is a null pointer, no count of rows is returned. If an error occurs, the provider sets *pcRowsRefreshed to zero.

prghRowsRefreshed

[out]
A pointer to memory in which to return the array of row handles the method attempted to refresh. 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 with pending changes in the chapter, or all active rows with pending changes in the rowset if hChapter is DB_NULL_HCHAPTER. When cRows is zero, RefreshVisibleData will add to the reference count of the rows whose handles are returned in prghRowsRefreshed.

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 pcRowsRefreshed is a null pointer and must not be a null pointer otherwise. If *pcRowsRefreshed is zero on output or the method fails, the provider does not allocate any memory and ensures that *prghRowsRefreshed 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 *prghRowsRefreshed. If no errors occur while refreshing a row, the corresponding element of *prgRowStatus is set to DBROWSTATUS_S_OK if the row is successfully resynchronized, or DBROWSTATUS_S_NOCHANGE if the provider can easily determine that there was no change to the value. If an error occurs while refreshing 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 pcRowsRefreshed is a null pointer. If *pcRowsRefreshed 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 processed. For each row, prgRowStatus contains one of the following:

  • DBROWSTATUS_S_NOCHANGE if the row in the rowset had no pending changes when RefreshVisibleData was called, or if the provider could easily detect that there were no changes made to the data source for this row.

  • DBROWSTATUS_S_OK in all other cases.

DB_S_ERRORSOCCURRED

An error occurred while refreshing a row, but at least one row was successfully refreshed. Successes can occur for the reason listed under S_OK. The following errors can occur:

  • An element of rghRows was invalid. The corresponding element of *prgRowStatus contains DBROWSTATUS_E_INVALID.

  • Refreshing a row was canceled during notification. The row was not refreshed 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 refreshed 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 refreshed 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.

pcRowsRefreshed was not a null pointer and prghRowsRefreshed 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 refreshing 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 refresh the rows.

Comments

RefreshVisibleData retrieves the data values from the data source that are visible to the transaction for the specified rows. Changes made to the row by the current transaction are always visible to RefreshVisibleData, including changes made by other rowsets in the same transaction. Whether changes made by other transactions are visible to RefreshVisibleData depends on the isolation level of the current transaction. Thus, RefreshVisibleData uses values as follows:

  • Read Uncommitted. The changes most recently made, committed or uncommitted, by any transaction are used. If no changes have been made by any transaction, the original row is used.

  • Read Committed. The changes most recently made by the current transaction or committed by other transactions are used. If no changes have been made by the current transaction or committed by other transactions, the original row is used.

  • Repeatable Read and higher. The changes most recently made by the current transaction are used. If no changes have been made by the current transaction, the original row is used.

For providers that keep a local copy of visible data, RefreshVisibleData updates the provider's local copy of the visible data for any indicated rows with pending changes.

When fOverwrite is TRUE, the provider discards any pending changes to a given row and accepts the visible values as the new current values that will be returned by calling GetData or GetOriginalData. This affects rows with or without pending changes. Any changes already transmitted to the data source are not lost; they will be committed or aborted when the transaction is committed or aborted. But all pending changes are lost because they exist only in the rowset’s copy of the row and the fOverwrite TRUE option overwrites the contents of this copy. The pending change status in this case is removed from the row.

When fOverwrite is FALSE, the provider makes no change to the pending status of the row. It does not affect the current values of the row (available through IRowset::GetData) or the original values of the row (available through IRowsetUpdate::GetOriginalData). If the provider keeps a local copy of the visible data values, it refreshes the values for the indicated rows. If the provider does not keep a local copy of the visible data values, the indicated row has no pending changes, or all columns in the row are deferred, the provider reports success without taking any action.

Providers that perform optimistic updates in a disconnected scenario usually use the cached visible values on a pending row as the source of concurrency conflict detection values. For example, the values used to identify the row to a SQL-based provider through a WHERE clause would come from this buffer. Thus, refreshing the visible values buffer for a row with pending changes via RefreshVisibleData potentially affects the behavior of a subsequent call to IRowsetUpdate::Update for that row because it may introduce new acceptable identity values into the conflict-detection mechanism of the provider.

If a specified row has been deleted from the data source and this deletion is visible, RefreshVisibleData returns DBROWSTATUS_E_DELETED in the error status array for the row. This row is subsequently treated as a deleted row for the purposes of IRowset::GetData (if fOverwrite is TRUE) or for GetLastVisibleData (if fOverwrite is FALSE).

If RefreshVisibleData encounters an error while attempting to refresh a row, such as a bad row handle, it notes the error in *prgRowStatus, continues processing, and returns DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED. Although the rows are refreshed in undefined order, the ordering of *prgRowStatus must match the order of the row handle array, so the consumer can perform a side-by-side scan of each array to determine which rows were not refreshed.

If cRows is zero and pcRowsRefreshed and prghRowsRefreshed are not null pointers, RefreshVisibleData adds to the reference count of the rows it returns in *prghRowsRefreshed to ensure that the consumer has these row handles.

See Also

IRowsetRefresh::GetLastVisibleData


IRowsetScroll : IRowsetLocate

IRowsetScroll enables consumers to fetch rows at approximate positions in the rowset.

When to Implement

IRowsetScroll is required for all providers that intend to support general consumers. It is implemented for rowsets intended to work smoothly with long lists browsed directly by the user interface.

IRowsetScroll implies IRowsetLocate.

When to Call

IRowsetScroll enables consumers to fetch rows located at approximate positions within a rowset. This method can be used for cases where precise positioning is not critical.

Method | Description
GetApproximatePosition | Gets the approximate position of a row corresponding to a specified bookmark.
GetRowsAtRatio | Fetches rows starting from a fractional position in the rowset.


IRowsetScroll::GetApproximatePosition

Gets the approximate position of a row corresponding to a specified bookmark.

HRESULT GetApproximatePosition (
   HCHAPTER      hChapter,
   ULONG         cbBookmark,
   const BYTE *      pBookmark,
   ULONG *         pulPosition,
   ULONG *         pcRows);

Parameters

hChapter

[in]
The chapter handle. For nonchaptered rowsets, hChapter is ignored.

cbBookmark

[in]
The length in bytes of the bookmark. If this is zero, then pBookmark is ignored, *pcRows is set to the count of rows, and no position is returned in *pulPosition.

pBookmark

[in]
A pointer to a bookmark that identifies the row of which to find the position. This can be a pointer to DBBMK_FIRST or DBBMK_LAST. The consumer is not required to have permission to read the row.

pulPosition

[out]
A pointer to memory in which to return the position of the row identified by the bookmark. The returned number is one-based; that is, the first row in the rowset is 1 and the last row is equal to *pcRows. If *pcRows is zero, the provider sets *pulPosition to zero also, regardless of the bookmark that was passed. If pulPosition is a null pointer, no position is returned. In case of error, *pulPosition is not changed.

pcRows

[out]
A pointer to memory in which to return the total number of rows. This number is zero if there are no rows. If pcRows is a null pointer, no count of rows is returned. In case of error, *pcRows is not changed.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
cbBookmark was not zero and pBookmark was a null pointer.

E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called and the object is in a zombie state.

DB_E_BADBOOKMARK
*pBookmark was invalid, incorrectly formed, or DBBMK_INVALID.

*pBookmark did not match any of the rows in the rowset. This includes the case when the row corresponding to the bookmark was deleted, and DBPROP_BOOKMARKSKIPPED was either VARIANT_FALSE or the provider is a 1.x provider that does not support getting the approximate position of a deleted row.

Note Consumers should only attempt to use bookmarks that they have received from the provider. The provider is guaranteed only to handle bookmarks it gives out in a predictable manner. Attempting to use a random value as a bookmark is undefined; the provider may return DB_E_BADBOOKMARK, may return an unexpected row, or may terminate abnormally.

DB_E_BADCHAPTER
The rowset was chaptered and hChapter was invalid.

The rowset was single-chaptered, and the specified chapter was not the currently open chapter. The consumer must use the currently open chapter or release the currently open chapter before specifying a new chapter.

DB_E_NOTREENTRANT
The provider called a method from IRowsetNotify in the consumer that had not yet returned, and the provider does not support reentrancy in this method.

Comments

This method makes no logical change to the state of the object.

There is no guarantee that the row position will be accurate. The provider is expected to choose the fastest possible estimation technique that is not completely in error. *pcRows must be greater than or equal to *pulPosition. The ratio is intended to be useful for scrolling and similar noncritical uses.

See Also

IRowsetScroll::GetRowsAtRatio


IRowsetScroll::GetRowsAtRatio

Fetches rows starting from a fractional position in the rowset.

HRESULT GetRowsAtRatio (
   HWATCHREGION   hReserved1,
   HCHAPTER            hChapter,
   ULONG               ulNumerator,
   ULONG               ulDenominator,
   LONG                  cRows,
   ULONG *               pcRowsObtained,
   HROW **               prghRows);

Parameters

hReserved1

[in]
Reserved for future use. Providers ignore this parameter.

hChapter

[in]
The chapter handle. For nonchaptered rowsets, hChapter is ignored.

ulNumerator

[in]
See ulDenominator below.

ulDenominator

[in]
The provider determines the first row to fetch from the ratio of ulNumerator to ulDenominator, roughly using the formula:

(ulNumerator x Number of Rows in Rowset) / ulDenominator

If the rowset is being populated asynchronously, ulNumerator and ulDenominator specify the relative position within the rows fetched so far.

How accurately the provider applies this ratio is provider-specific. For example, if ulNumerator is 1 and ulDenominator is 2, some providers will fetch rows starting exactly halfway through the rowset while other providers will fetch rows starting 40 percent of the way through the rowset.

However, all providers must handle the following conditions correctly.

Condition | GetRowsAtRatio action
(ulNumerator = 0) AND (cRows > 0) | Fetches rows starting with the first row in the rowset.
(ulNumerator = 0) AND (cRows < 0) | Returns DB_S_ENDOFROWSET.
(ulNumerator = ulDenominator) AND (cRows > 0) | Returns DB_S_ENDOFROWSET.
(ulNumerator = ulDenominator) AND (cRows < 0) | Fetches rows starting with the last row in the rowset.

cRows

[in]
The number of rows to fetch. A negative number means to fetch backward. cRows can be a negative number only if the value of the DBPROP_CANFETCHBACKWARDS property is VARIANT_TRUE. The rows are returned in rowset-traversal order; that is, the direction in which they were fetched.

If cRows is zero, no rows are fetched. If the provider does not discover any other errors, the method returns S_OK; whether the provider checks for any other errors is provider-specific.

pcRowsObtained

[out]
A pointer to memory in which to return the number of rows fetched. If the consumer has insufficient permissions to return all rows, GetRowsAtRatio fetches all rows for which the consumer has sufficient permission and skips all other rows. If the method fails, *pcRowsObtained is set to 0.

prghRows

[in/out]
A pointer to memory in which to return an array of handles of the fetched rows. If *prghRows is not a null pointer on input, it must be a pointer to memory large enough to return the handles of the requested number of rows. If *prghRows is a null pointer on input, the rowset allocates memory for the row handles and returns the address to this memory; the consumer releases this memory with IMalloc::Free after it releases the row handles. If *prghRows was a null pointer on input and *pcRowsObtained is zero on output or the method fails, the provider does not allocate any memory and ensures that *prghRows is a null pointer on output.

Return Code

S_OK
The method succeeded.

DB_S_ENDOFROWSET
GetRowsAtRatio reached the start or the end of the rowset or chapter and could not fetch all requested rows because the count extended beyond the end. The number of rows actually fetched is returned in *pcRowsObtained; this will be less than cRows.

The rowset is being populated asynchronously and no additional rows are available at this time. To determine whether additional rows may be available, the consumer should call IDBAsynchStatus::GetStatus or listen for the IDBAsynchNotify::OnStop notification.

DB_S_ROWLIMITEXCEEDED
Fetching the number of rows specified in cRows would have exceeded the total number of active rows supported by the rowset. The number of rows that were actually fetched is returned in *pcRowsObtained. This condition can occur only when there are more rows available than can be handled by the rowset. Thus, this condition never conflicts with those described in DB_S_ENDOFROWSET and DB_S_STOPLIMITREACHED, both of which imply that no more rows were available.

DB_S_STOPLIMITREACHED
Fetching rows required further execution of the command, such as when the rowset uses a server-side cursor. Execution was stopped because a resource limit was reached. The number of rows that were actually fetched is returned in *pcRowsObtained.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
pcRowsObtained or prghRows was a null pointer.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to instantiate the rows or return the row handles.

E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called and the object is in a zombie state.

DB_E_BADCHAPTER
The rowset was chaptered and hChapter was invalid.

The rowset was single-chaptered and the specified chapter was not the currently open chapter. The consumer must use the currently open chapter or release the currently open chapter before specifying a new chapter.

DB_E_BADRATIO
ulNumerator was greater than ulDenominator.

ulDenominator was zero.

DB_E_CANTFETCHBACKWARDS
cRows was negative and the rowset cannot fetch backward.

DB_E_NOTREENTRANT
The consumer called this method while it was processing a notification, and it is an error to call this method while processing the specified DBREASON value.

DB_E_ROWSNOTRELEASED
The provider requires release of existing rows before new ones can be fetched. For more information, see DBPROP_CANHOLDROWS in "Rowset Properties" in Appendix C.

DB_SEC_E_PERMISSIONDENIED
The consumer did not have sufficient permission to fetch any of the rows; no rows were fetched.

Comments

GetRowsAtRatio increments the reference count of each row for which it returns a handle by 1. Thus, if a handle is returned for a row that has already been fetched, the reference count of that row will be greater than 1. ReleaseRows must be called once for each time the handle to a row has been returned.

If the provider encounters a problem fetching a row—for example, data stored in a text file contains a letter in what is supposed to be a numeric column—GetRowsAtRatio fetches the row normally, returns the row handle, and returns S_OK. However, when the consumer calls IRowset::GetData for the row, the provider returns DBSTATUS_E_CANTCONVERTVALUE as the status for the offending column.

For information about what GetRowsAtRatio does when it fetches a row that it already has in its internal buffers, see "Uniqueness of Rows in the Rowset" in Chapter 4. For information about whether GetRowsAtRatio can detect changes made to rows in the rowset, see "Visibility of Changes" in Chapter 5.

See Also

IRowset::GetData, IRowset::GetNextRows, IRowsetLocate::GetRowsAt, IRowsetLocate::GetRowsByBookmark, IRowsetScroll::GetApproximatePosition


IRowsetUpdate : IRowsetChange

IRowsetUpdate enables consumers to delay the transmission of changes made with IRowsetChange to the data source. This interface also enables consumers to undo changes before transmission.

IRowsetUpdate is optional.

When to Implement

Rowsets that want to allow delayed transmission of changes and to support undo capabilities implement IRowsetUpdate. Generally, this requires the rowset to locally cache a copy of each row as it is changed with IRowsetChange::SetData.

If IRowsetUpdate is not requested as a property of the rowset, it must not be exposed by the rowset.

When to Call

If IRowsetUpdate is exposed on the rowset, then changes made through IRowsetChange are buffered in the rowset and not transmitted to the data source until IRowsetUpdate::Update is called; this is known as delayed update mode. If IRowsetUpdate is not exposed on the rowset, then changes made through IRowsetChange are immediately transmitted to the data source; this is known as immediate update mode. For more information, see "Changing Data" in Chapter 5.

The consumer calls the methods in IRowsetChange to update, delete, and insert rows. The rowset buffers these changes. When the consumer is ready to transmit these changes to the data source, it calls Update. Before calling Update, the consumer can back out any changes with Undo.

Method | Description
GetOriginalData | Gets the data most recently fetched from or transmitted to the data source; does not get values based on pending changes.
GetPendingRows | Returns a list of rows with pending changes.
GetRowStatus | Returns the status of rows.
Undo | Undoes any changes made to a row since it was last fetched or since Update was called for it.
Update | Transmits any changes made to a row since it was last fetched or since Update was called for it.


IRowsetUpdate::GetOriginalData

Gets the data most recently fetched from or transmitted to the data source; does not get values based on pending changes.

HRESULT GetOriginalData (
   HROW            hRow,
   HACCESSOR   hAccessor,
   void *            pData);

Parameters

hRow

[in]
The handle of the row for which to get the original data. This can be the handle of a row with a pending change or delete.

hAccessor

[in]
The handle of the accessor to use. If hAccessor is the handle of a null accessor (cBindingsin IAccessor::CreateAccessor was zero), then GetOriginalData 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
A row handle in rghRows referred to a row on which a storage object or OLE object was open. The corresponding element of *prgRowStatus contains DBROWSTATUS_E_OBJECTOPEN.

An error occurred while returning data for one or more columns, but data was successfully returned for at least one column. To determine the columns for which data was returned, the consumer checks the status values. For a list of status values that can be returned by this method, see "Status Values Used When Getting Data" in "Status" in Chapter 6.

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
hRow was invalid.

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. For a list of status values that can be returned by this method, see "Status Values Used When Getting Data" in "Status" in Chapter 6.

DB_E_NOTREENTRANT
The provider called a method from IRowsetNotify in the consumer that had not yet returned, and the provider does not support reentrancy in this method.

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.

GetOriginalData retrieves the values the row contained when it was last fetched or had changes transmitted to the data source. It does not retrieve any pending changes or changes made by other rowsets in the same transaction or other applications in other transactions. It also does not affect the current values for the row. For a complete description of how GetOriginalData retrieves data, see "Getting Data" in Chapter 6.

How GetOriginalData works is best illustrated in the following examples. In the first example, GetOriginalData fetches the values last fetched from the data source:

  1. The consumer fetches a row.

  2. The consumer calls IRowsetChange::SetData to update values in the row.

  3. The consumer calls GetOriginalData. The consumer retrieves the values it would have retrieved had it called IRowset::GetData after step 1 and before step 2.

In the second example, GetOriginalData fetches the values last transmitted to the data source:

  1. The consumer fetches a row.

  2. The consumer calls SetData to update values in the row.

  3. The consumer calls Update.

  4. The consumer calls SetData to update values in the row.

  5. The consumer calls GetOriginalData. The consumer retrieves the values it would have retrieved had it called GetData after step 3 and before step 4.

To implement GetOriginalData, the provider usually caches the original values just before making a change to a row and discards the cached values when Undo or Update is called for the row.

If hRow refers to a pending insert row, GetOriginalData returns the column defaults and, for columns without defaults or for which the provider was unable to determine the defaults, NULLs.

Whether GetOriginalData can retrieve the original value of an OLE object that is stored in a column, or a storage object that is created over a BLOB after changes have been made to that OLE object or BLOB, depends on the value of the DBPROP_DELAYSTORAGEOBJECTS rowset property.

There is a difference between calling GetOriginalData and calling GetLastVisibleData. In a delayed update mode, if data is changed by another consumer, different consumers may retrieve different data. For example, the value in Column 1 is X. In a delayed update mode, Consumer A changes the value in that column to Y but does not transmit this action to the data source. Consumer B then changes the value in Column 1 to Z. If Consumer A calls GetOriginalData, it gets X. However, if it calls GetLastVisibleData, using a dirty read, it will retrieve Z.

GetOriginalData does not enforce any security restrictions. The provider must not create a rowset that includes columns for which the consumer does not have read privileges, so GetOriginalData never encounters problems accessing the data for a column. The rowset can contain columns to which the consumer does not have write permission if DBPROP_COLUMNRESTRICT is VARIANT_TRUE. The methods that fetch rows must not return the handles of rows for which the consumer does not have read privileges, so GetOriginalData never encounters problems accessing a row. Such rows might exist if the DBPROP_ROWRESTRICT property is VARIANT_TRUE.

If GetOriginalData fails, the memory to which pData points is not freed but its contents are undefined. If, before GetOriginalData failed, the provider allocated any memory for return to the consumer, the provider frees this memory and does not return it to the consumer.

See Also

IRowset::GetData, IRowsetRefresh::GetLastVisibleData, IRowsetUpdate::Undo, IRowsetUpdate::Update


IRowsetUpdate::GetPendingRows

Returns a list of rows with pending changes.

HRESULT GetPendingRows (
   HCHAPTER                  hChapter,
   DBPENDINGSTATUS      dwRowStatus,
   ULONG *                     pcPendingRows,
   HROW **                     prgPendingRows,
   DBPENDINGSTATUS **   prgPendingStatus);

Parameters

hChapter

[in]
The chapter handle. For nonchaptered rowsets, hChapter is ignored.

dwRowStatus

[in]
Indicates whether consumers want rows with pending updates, deletes, or inserts. The following DBPENDINGSTATUS values are valid and can be combined:

DBPENDINGSTATUS_NEW
DBPENDINGSTATUS_CHANGED
DBPENDINGSTATUS_DELETED

For information about the DBPENDINGSTATUS type, see "Row States" in Chapter 5.

pcPendingRows

[out]
A pointer to memory in which to return the number of rows with pending changes. If this is a null pointer, prgPendingRows and prgPendingStatus are ignored. This is useful when the consumer wants to check the returned return code to determine whether there are any pending changes. If an error occurs, *pcPendingRows is set to zero.

prgPendingRows

[out]
A pointer to memory in which to return an array of handles of rows with pending changes. If this is a null pointer, no row handles are returned. The rowset allocates memory for the row handles and returns the address to this memory; the consumer releases this memory with IMalloc::Free when it no longer needs the row handles. This argument is ignored if pcPendingRows is a null pointer. If *pcPendingRows is zero on output or an error occurs, the provider does not allocate any memory and ensures that *prgPendingRows is a null pointer on output.

prgPendingStatus

[out]
A pointer to memory in which to return an array of DBPENDINGSTATUS values. These values are in one-to-one correspondence with the row handles returned in *prgPendingRows and indicate the type of pending change. For information about the DBPENDINGSTATUS type, see "Row States" in Chapter 5. If this is a null pointer, no status information is returned.

The rowset allocates memory for the row statuses and returns the address to this memory; the consumer releases this memory with IMalloc::Free when it no longer needs the row statuses. This argument is ignored if pcPendingRows is a null pointer. If *pcPendingRows is zero on output or an error occurs, the provider does not allocate any memory and ensures that *prgPendingStatus is a null pointer on output.

Return Code

S_OK
The method succeeded and changes were pending.

S_FALSE
The method succeeded and no changes were pending.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
dwRowStatus was DBPENDINGSTATUS_INVALIDROW, DBPENDINGSTATUS_UNCHANGED, or any other invalid value.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to return the handles of rows with pending changes or the array of DBPENDINGSTATUS values.

E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called and the object is in a zombie state.

DB_E_BADCHAPTER
The rowset was chaptered and hChapter was invalid.

The rowset was single-chaptered and the specified chapter was not the currently open chapter. The consumer must use the currently open chapter or release the currently open chapter before specifying a new chapter.

Comments

GetPendingRows increments the reference of each row handle it returns in *prgPendingRows. The consumer must call ReleaseRows for these rows. If multiple changes are made to a single row, GetPendingRows returns the status as follows.

  • If IRowsetChange::SetData is called for a pending insert row, the row is still considered a pending insert row.

  • If IRowsetChange::DeleteRows is called for a pending update row, the row is considered a pending delete row.

  • If IRowsetChange::DeleteRows is called for a pending insert row, the row is considered a transmitted delete row; such rows are not returned by GetPendingRows.

  • If IRowsetRefresh::RefreshVisibleData is called for a pending insert row, the row is still considered a pending insert row.

For a complete description of pending change states, see "Row States" in Chapter 5.

See Also

IRowsetUpdate::GetRowStatus


IRowsetUpdate::GetRowStatus

Returns the status of rows.

HRESULT GetRowStatus(
   HCHAPTER               hChapter,
   ULONG                  cRows,
   const HROW               rghRows[],
   DBPENDINGSTATUS   rgPendingStatus[]);

Parameters

hChapter

[in]
The chapter handle. For nonchaptered rowsets, hChapter is ignored.

cRows

[in]
The count of elements in rghRows and rgPendingStatus. If this value is zero , GetRowStatus ignores rghRows and rgPendingStatus and does not return any status.

rghRows

[in]
An array of handles of rows for which to return the status. This array is allocated by the consumer and must not be freed by the provider.

rgPendingStatus

[out]
An array of DBPENDINGSTATUS values. GetRowStatus returns the DBPENDINGSTATUS values for all rows specified in the rghRows array. The DBPENDINGSTATUS_INVALIDROW value is used to indicate an invalid row handle. For information about the DBPENDINGSTATUS type, see "Row States" in Chapter 5.

The rgPendingStatus array is allocated, but not necessarily initialized, by the caller and must not be freed by the provider.

Return Code

S_OK
The method succeeded. Status values were successfully retrieved for all rows and each element of rgPendingStatus is set to DBPENDINGSTATUS_NEW, DBPENDINGSTATUS_CHANGED, DBPENDINGSTATUS_DELETED, or DBPENDINGSTATUS_UNCHANGED.

DB_S_ERRORSOCCURRED
An error occurred while getting the status of a row, but the status of at least one row was successfully retrieved. Successes can occur for the reasons listed under S_OK. The following error can occur:

  • A row handle in rghRows was invalid. The corresponding element of rgRowStatus contains DBPENDINGSTATUS_INVALIDROW.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
cRows was greater than zero and either rghRows was a null pointer or rgPendingStatus was a null pointer.

E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called and the object is in a zombie state.

DB_E_BADCHAPTER
The rowset was chaptered and hChapter was invalid.

The rowset was single-chaptered and the specified chapter was not the currently open chapter. The consumer must use the currently open chapter or release the currently open chapter before specifying a new chapter.

DB_E_ERRORSOCCURRED
Errors occurred getting the status of all of the rows. Errors can occur for the reason listed under DB_S_ERRORSOCCURRED.

Comments

If multiple changes are made to a single row, GetRowStatus returns the status as described in GetPendingRows. If IRowsetChange::DeleteRows is called for a pending insert row, a status of DBPENDINGSTATUS_INVALIDROW is returned for the row.

For a complete description of row states, see "Row States" in Chapter 5.

See Also

IRowsetUpdate::GetPendingRows


IRowsetUpdate::Undo

Undoes any changes made to a row since it was last fetched or Update was called for it.

HRESULT Undo (
   HCHAPTER            hChapter,
   ULONG               cRows,
   const HROW            rghRows[],
   ULONG *               pcRows,
   HROW **               prgRows,
   DBROWSTATUS **   prgRowStatus);

Parameters

hChapter

[in]
The chapter handle. For nonchaptered rowsets, hChapter is ignored.

cRows

[in]
The count of rows to undo. If cRows is nonzero, Undo undoes all pending changes in the rows specified in rghRows. If cRows is zero, Undo ignores rghRows and undoes all pending changes to all rows in the rowset.

rghRows

[in]
An array of handles of the rows to undo. Elements of this array can refer to rows with pending deletes.

If rghRows includes a row that does not have any pending changes, Undo does not return an error. Instead, the row remains unchanged from its original state—which is the intention of Undo—and its row status is set to DBROWSTATUS_S_OK.

If rghRows includes a duplicate row, Undo treats the occurrences as if the row were passed to the method two times sequentially. Thus, on the first occurrence, Undo undoes any pending changes. On the second occurrence, Undo treats the row as a row with no pending changes and leaves it in its current (now original) state.

pcRows

[out]
A pointer to memory in which to return the number of rows Undo attempted to undo. If this is a null pointer, no count of rows is returned. If the method fails with an error other than DB_E_ERRORSOCCURRED, *pcRows is set to zero.

prgRows

[out]
A pointer to memory in which to return an array containing the handles of all the rows Undo attempted to undo. If rghRows is not a null pointer, then the elements of this array are in one-to-one correspondence with those in rghRows. For example, if a row appears twice in rghRows, it appears twice in *prgRows. When rghRows is not a null pointer, Undo does not add to the reference count of the rows it returns in *prgRows; the reason is that the consumer already has these row handles.

If rghRows is a null pointer, the elements of this array are the handles of all the rows that had pending changes, regardless of whether Undo was successful at undoing those changes. The consumer checks *prgRowStatus to determine which rows were undone. When rghRows is a null pointer, Undo adds to the reference count of the rows it returns in *prgRows; the reason is that the consumer is not guaranteed to already have these row handles. A side effect of this is that rows with a reference count of zero but with pending changes at the time Undo is called are brought back into existence; that is, their reference count is increased to 1 and they must be rereleased.

The rowset allocates memory for the array of handles and returns the address to this memory; the consumer releases this memory with IMalloc::Free when it no longer needs the handles. This argument is ignored if pcRows is a null pointer, and must not be a null pointer otherwise. If *pcRows is zero on output or the method fails with an error other than DB_E_ERRORSOCCURRED, the provider does not allocate any memory and ensures that *prgRows 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 rghRows (if rghRows is not a null pointer) or *prgRows (if rghRows is a null pointer). If no errors occur while undoing a row, the corresponding element of *prgRowStatus is set to DBROWSTATUS_S_OK. If an error occurs while undoing 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. For information about the DBROWSTATUS enumerated type, see "Arrays of Errors" in Chapter 13.

The rowset allocates memory for the row status values and returns the address to this memory; the consumer releases this memory with IMalloc::Free when it no longer needs the row status values. This argument is ignored if cRows is zero and pcRows is a null pointer. If Undo does not attempt to undo any rows or the method fails with an error other than DB_E_ERRORSOCCURRED, the provider does not allocate any memory and ensures that *prgRowStatus is a null pointer on output.

Return Code

S_OK
The method succeeded. The changes in all rows were successfully undone. The following value can be returned in *prgRowStatus:

  • The changes in the row were successfully undone. The corresponding element of *prgRowStatus contains DBROWSTATUS_S_OK.

DB_S_ERRORSOCCURRED
An error occurred while undoing the changes in a row, but the changes in at least one row were successfully undone. Successes can occur for the reasons listed under S_OK. The following errors can occur:

  • Undoing the changes in a row was canceled during notification. Changes made to the row were not undone and the corresponding element of rgRowStatus contains DBROWSTATUS_E_CANCELED.

  • A row handle in rghRows referred to a row for which a delete had been transmitted to the data source. The corresponding element of *prgRowStatus contains DBROWSTATUS_E_DELETED.

  • A row handle in rghRows was invalid. The corresponding element of *prgRowStatus contains DBROWSTATUS_E_INVALID.

  • A row handle in rghRows referred to a row on which a storage object or OLE object was open. The corresponding element of *prgRowStatus contains DBROWSTATUS_E_OBJECTOPEN.

  • The consumer encountered a recoverable, provider-specific error, such as an RPC failure when transmitting the change to a remote server. The corresponding element of *prgRowStatus contains DBROWSTATUS_E_FAIL.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
cRows was not 0 and rghRows was a null pointer.

pcRows was not a null pointer and prgRows was a null pointer.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to return either the handles of the rows Undo attempted to undo or the array of row status values.

E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called and the object is in a zombie state.

DB_E_BADCHAPTER
The rowset was chaptered and hChapter was invalid.

The rowset was single-chaptered and the specified chapter was not the currently open chapter. The consumer must use the currently open chapter or release the currently open chapter before specifying a new chapter.

DB_E_ERRORSOCCURRED
Errors occurred while undoing all of the rows. The provider allocates memory for *prgRows and *prgRowStatus and the consumer checks the values in *prgRowStatus to determine why the pending changes were not undone. The consumer frees this memory when it no longer needs the information. Errors can occur for the reasons listed under DB_S_ERRORSOCCURRED.

DB_E_NOTREENTRANT
The consumer called this method while it was processing a notification, and it is an error to call this method while processing the specified DBREASON value.

Comments

Undo backs any pending changes out of the specified rows and clears their pending change status. That is, it undoes any changes made to the row since it was last fetched or Update was called for the row. If multiple changes were made to a row, Undo undoes all of these changes; the provider does not remember intermediate steps. If Update is called for a row immediately after Undo is called for the row, Update does not transmit any changes to the data source for the row. For more information about pending changes, see "Changing Data" in Chapter 5.

How Undo works is best illustrated in the following examples. In the first example, Undo backs out any changes made since the row was last fetched from the data source:

  1. The consumer fetches a row.

  2. The consumer deletes the row with IRowsetChange::DeleteRows or updates value in it with IRowsetChange::SetData.

  3. The consumer repeats step 2 as often as it likes for the row, except that after it calls DeleteRows for the row it cannot call either SetData or DeleteRows for the row.

  4. The consumer calls Undo for the row. The values in the row are changed to those it had after completing step 1.

In the second example, Undo backs out any changes made since values were last transmitted to the data source:

  1. The consumer fetches a row.

  2. The consumer updates the value in a row with SetData.

  3. The consumer repeats step 2 as often as it likes for the row.

  4. The consumer calls Update for the row.

  5. The consumer deletes the row with DeleteRows or updates the value in it with SetData.

  6. The consumer repeats step 5 as often as it likes for the row, except that after it calls DeleteRows for the row it cannot call either SetData or DeleteRows for the row.

  7. The consumer calls Undo for the row. The values in the row are changed to those it had after completing step 4.

To implement Undo, the provider generally caches the original values just before making a change to a row and discards the cached values when Undo or Update is called for the row. This same cache can be used by GetOriginalData to retrieve the original data for the row.

If Undo is called for a row with a pending insert, the row is deleted from the rowset. That is, calls to IRowset::GetData or SetData for the row fail with DB_E_DELETEDROW. The consumer must still call IRowset::ReleaseRows to release the row.

Whether Undo can undo changes made to an OLE object stored in a column or to a storage object created over a BLOB depends on the value of the DBPROP_DELAYSTORAGEOBJECTS rowset property.

If Undo is called for a row that has a reference count of zero and exists only because the row has a pending change, Undo releases the row and all its resources. The only exception to this is when the handle to the row is returned in *prgRows, in which case the reference count is set to one.

The order in which Undo processes rows is provider-specific. If Undo encounters an error, it continues processing rows until it has attempted to undo all specified rows, then returns the appropriate warning. Because Undo is generally implemented by copying data from a cache of original data, such errors should be extremely rare and generally represent a consumer programming error, such as passing an invalid row handle.

If any consumer of the rowset is using notifications, the provider sends notifications that pending changes for the specified rows are being undone.

See Also

IRowsetChange, IRowsetUpdate::GetOriginalData, IRowsetUpdate::Update


IRowsetUpdate::Update

Transmits any changes made to a row since it was last fetched or Update was called for it.

HRESULT Update (
   HCHAPTER            hChapter,
   ULONG               cRows,
   const HROW            rghRows[],
   ULONG *               pcRows,
   HROW **               prgRows,
   DBROWSTATUS**   prgRowStatus);

Parameters

hChapter

[in]
The chapter handle. For nonchaptered rowsets, hChapter is ignored.

cRows

[in]
The count of rows to update. If cRows is nonzero, Update updates all pending changes in the rows specified in rghRows. If cRows is zero, Update ignores rghRows and updates all pending changes to all rows in the rowset.

rghRows

[in]
An array of handles of the rows to update.

If rghRows includes a row that does not have any pending changes, Update does not return an error. Instead, the row remains unchanged and hence has no pending changes after Update returns—which is the intention of Update—and the row status value associated with that row is DBROWSTATUS_S_OK. Furthermore, Update guarantees not to transmit any value for the row to the data source.

If rghRows includes a duplicate row, Update behaves as follows. If the row handle is valid, no errors occur and *prgRowStatus contains DBROWSTATUS_S_OK for each occurrence. If the row handle is invalid, *prgRowStatus contains the appropriate error for each occurrence.

pcRows

[out]
A pointer to memory in which to return the number of rows Update attempted to update. If this is a null pointer, no count of rows is returned. If the method fails with an error other than DB_E_ERRORSOCCURRED, *pcRows is set to zero.

prgRows

[out]
A pointer to memory in which to return an array containing the handles of all the rows Update attempted to update. If rghRows is not a null pointer, then the elements of this array are in one-to-one correspondence with those in rghRows. For example, if a row appears twice in rghRows, it appears twice in *prgRows. When rghRows is not a null pointer, Update does not add to the reference count of the rows it returns in *prgRows; the reason is that the consumer already has these row handles.

If rghRows is a null pointer, the elements of this array are handles of all the rows that had pending changes, regardless of whether Update was successful at transmitting those changes to the data source. The consumer checks *prgRowStatus to determine which rows were updated. When rghRows is a null pointer, Update adds to the reference count of the rows it returns in *prgRows; the reason is that the consumer is not guaranteed to already have these row handles. A side effect of this is that rows with a reference count of zero, but with pending changes at the time Update is called, are brought back into existence; that is, their reference count is increased to 1 and they must be rereleased.

The rowset allocates memory for the array of handles and returns the address to this memory; the consumer releases this memory with IMalloc::Free when it no longer needs the handles. This argument is ignored if pcRows is a null pointer, and must not be a null pointer otherwise. If *pcRows is zero on output or the method fails with an error other than DB_E_ERRORSOCCURRED, the provider does not allocate any memory and ensures that *prgRows 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 rghRows (if rghRows is not a null pointer) or *prgRows (if rghRows is a null pointer). If no errors or warnings occur while updating a row, the corresponding element of *prgRowStatus is set to DBROWSTATUS_S_OK. If an error or warning occurs while updating 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. For information about the DBROWSTATUS enumerated type, see "Arrays of Errors" in Chapter 13.

The rowset allocates memory for the row status values and returns the address to this memory; the consumer releases this memory with IMalloc::Free when it no longer needs the row status values. This argument is ignored if cRows is zero and pcRows is a null pointer. If Update does not attempt to update any rows or the method fails with an error other than DB_E_ERRORSOCCURRED, the provider does not allocate any memory and ensures that *prgRowStatus is a null pointer on output.

Return Code

S_OK
The method succeeded. The changes in all rows were successfully updated. The following values can be returned in *prgRowStatus:

  • The changes in the row were successfully updated. The corresponding element of *prgRowStatus contains DBROWSTATUS_S_OK.

  • Updating or deleting a single row caused more than one row to be updated or deleted in the data source. For more information, see DBPROP_REPORTMULTIPLECHANGES in "Rowset Properties" in Appendix C. The corresponding element of *prgRowStatus contains DBROWSTATUS_S_MULTIPLECHANGES.

DB_S_ERRORSOCCURRED
An error occurred while updating a row, but at least one row was successfully updated. Successes can occur for the reasons listed under S_OK. The following errors can occur:

  • Updating a row was canceled during notification. The row was not updated and the corresponding element of rgRowStatus contains DBROWSTATUS_E_CANCELED.

  • The rowset was using optimistic concurrency, a row was being updated or deleted, and the value of a column in that row has been changed since it was last fetched. The error in the corresponding element of *prgRowStatus contains DBROWSTATUS_E_CONCURRENCYVIOLATION.

  • An element of rghRows referred to a row for which a deletion had been transmitted to the data source. The error in the corresponding element of *prgRowStatus contains DBROWSTATUS_E_DELETED.

  • A row was being inserted or updated and a value specified for that row violated the integrity constraints for the column or table. The error in the corresponding element of *prgRowStatus contains DBROWSTATUS_E_INTEGRITYVIOLATION.

  • A row was being deleted, and doing so violated the integrity constraints for the column or table. The error in the corresponding element of *prgRowStatus contains DBROWSTATUS_E_INTEGRITYVIOLATION.

  • An element of rghRows was invalid. The error in the corresponding element of *prgRowStatus contains DBROWSTATUS_E_INVALID.

  • The consumer did not have sufficient permission to update, delete, or insert a row. This error can be returned only if the value of the DBPROP_ROWRESTRICT property is VARIANT_TRUE. The error in the corresponding element of *prgRowStatus contains DBROWSTATUS_E_PERMISSIONDENIED.

  • The update, delete, or insert failed 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.

  • A column value did not meet the schema requirements for the column. The error in the corresponding element of *prgRowStatus contains DBROWSTATUS_E_SCHEMAVIOLATION.

  • The values for two or more columns did not meet the multiple-column schema requirements for those columns. The error in the corresponding element of *prgRowStatus contains DBROWSTATUS_E_SCHEMAVIOLATION.

  • A row was being inserted, no value was specified for a column, the column does not have a default, and the column is non-nullable. The error in the corresponding element of *prgRowStatus contains DBROWSTATUS_E_SCHEMAVIOLATION.

  • The consumer encountered a recoverable, provider-specific error, such as an RPC failure when transmitting the change to a remote server. The corresponding element of *prgRowStatus contains DBROWSTATUS_E_FAIL.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
cRows was not zero and rghRows was a null pointer.

pcRows was not a null pointer and prgRows was a null pointer on input.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to return either the handles of the rows Update attempted to update or the array of row status values.

E_UNEXPECTED
ITransaction::Commit or ITransaction::Abort was called and the object is in a zombie state.

DB_E_BADCHAPTER
The rowset was chaptered and hChapter was invalid.

The rowset was single-chaptered and the specified chapter was not the currently open chapter. The consumer must use the currently open chapter or release the currently open chapter before specifying a new chapter.

DB_E_ERRORSOCCURRED
Errors occurred while updating all of the rows. The provider allocates memory for *prgRows and *prgRowStatus and the consumer checks the values in *prgRowStatus to determine why the pending changes were not updated. The consumer frees this memory when it no longer needs the information. 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.

Comments

Update transmits pending changes for the specified rows to the data source and clears their pending change status. That is, it transmits any changes made to the row since it was last fetched or Update was called for the row. For more information about pending changes, see "Changing Data" in Chapter 5.

The order in which Update processes rows is provider-specific. If Update encounters an error, it continues processing rows until it has attempted to update all specified rows, then it returns the appropriate warning. This might leave the rowset in a state where changes have been transmitted for some rows but not others. When the consumer repairs the cause of the error, it can call Update again and the provider guarantees it will not transmit data to the data source for those rows for which data was already successfully transmitted.

If any consumer of the rowset is using notifications, the provider sends notifications that pending changes for the specified rows are being transmitted to the data source.

If Update is called for a row that has a reference count of zero and exists only because the row has a pending change, Update releases the row and all its resources. The only exception to this is when the handle to the row is returned in *prgRows, in which case the reference count is set to 1.

If the provider cached original values to implement GetOriginalData and Undo, it discards them as part of Update.

If the rowset is released with IUnknown::Release before Update is called, all pending changes are lost.

See Also

IRowsetChange, IRowsetUpdate::Undo


IRowsetView

IRowsetView enables the consumer to create or apply a view to an existing rowset.

When to Implement

Providers that can do simple post-processing of rowsets, such as applying filters or re-sorting rowsets, support IRowsetView.

When to Call

Consumers call IRowsetView in order to obtain a view object from a rowset on which to apply post-processing operations to an existing rowset. The consumer calls CreateView in order to create a view object and calls methods on the view in order to apply post-processing operations to that view object. The consumer then calls OpenViewChapter to retrieve a chapter handle that reflects the specified view conditions on the original rowset, or OpenViewRowset in order to create a new rowset that reflects the specified view conditions. Any resources associated with the view opened as a chapter of an existing rowset can be cleaned up with a call to IChapteredRowset::ReleaseChapter.

Method | Description
CreateView | Creates a view object.
GetView | Returns a view object reflecting the conditions applied to the specified chapter.


IRowsetView::CreateView

Creates a view.

HRESULT CreateView (
   IUnknown *      pUnkOuter,
   REFIID            riid,
   IUnknown **      ppView);

Parameters

pUnkOuter

[in]
A pointer to the controlling IUnknown interface if the view is being created as part of an aggregate. It is a null pointer if the view is not part of an aggregate.

riid

[in]
The IID of the interface requested on the view.

ppView

[out]
A pointer to memory in which to return the interface pointer on the newly created view.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
ppView was a null pointer.

E_NOINTERFACE
The view did not support the interface specified in riid.

E_OUTOFMEMORY
The provider did not have enough memory to create the view.

DB_E_NOAGGREGATION
pUnkOuter was not a null pointer and the view being created does not support aggregation.

pUnkOuter was non-null and riid was not IID_Unknown.

E_UNEXPECTED
ITransaction::Commit or ITransactionAbort was called and the object is in a zombie state.

Comments

None.


IRowsetView::GetView

Returns a new view describing conditions applied to the specified chapter.

HRESULT GetView (
   HCHAPTER      hChapter,
   REFIID            riid,
   HCHAPTER *   phChapterSource,
   IUnknown **      ppView);

Parameters

hChapter

[in]
The chapter from which to return the view conditions.

riid

[in]
The IID of the interface on which to return a pointer.

phChapterSource

[out]
The chapter handle to which the view was applied to create the new chapter.

ppView

[out]
A pointer to memory in which to return the interface pointer. If GetView fails, it must attempt to set *ppView to a null pointer.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_NOINTERFACE
The view did not support the interface specified in riid.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to return the view information.

DB_E_BADCHAPTER
hChapter was invalid.

Comments

GetView returns a new view on which the consumer representing the view conditions applied to the specified chapter. The consumer can call methods such as GetFilter and GetSort in order to determine the filter and sort conditions applied to the chapter when created.

The view returned is not the same view as was used when the view was created; it simply returns the same view conditions. Therefore, if the consumer has changed or released the original view after creating the chapter, it has no effect on the view returned by this method.

The consumer must call Release in order to release the returned view when it is no longer needed.


ISequentialStream

ISequentialStream is the preferred minimal interface for reading and writing Binary Large Object (BLOB) data in OLE DB. ISequentialStream is a subset of the OLE IStream interface, and provides forward-only reading and writing of data. The IStream interface inherits its Read and Write methods from ISequentialStream.

When to Implement

You can implement ISequentialStream on an object if you require simple sequential access to a stream object. If you implement IStream, you must provide an implementation of the Read and Write methods from ISequentialStream.

Note Most applications do not implement ISequentialStream as a separate interface, and you are not required to provide it separately even if you provide an IStream implementation. For example, the compound file implementation of structured storage does not succeed on a QueryInterface for ISequentialStream but it includes the Read and Write methods through the IStream interface pointer. The same is true for the NTFS Native Structured Storage implementation.

When to Call

Call the methods of the ISequentialStream interface from a container or application to perform sequential reads and writes of data. Most applications call the Read and Write methods through the IStream interface.

Methods in Vtable Order

IUnknown Methods | Description
QueryInterface | Returns pointers to supported interfaces.
AddRef | Increments the reference count.
 | Decrements the reference count.

ISequentialStream Methods | Description
Read | Reads a specified number of bytes from the stream object into memory, starting at the current seek pointer.
Write | Writes a specified number of bytes to the stream object, starting at the current seek pointer.


ISequentialStream : IUnknown

ISequentialStream is a simplified version of IStream that provides strictly sequential stream access to BLOB data. This class behaves as if it were the strict ancestor of IStream. In particular, any object that implements IStream can choose to identify itself as ISequentialStream, because the first two slots in the IStream vtable and the definition of those methods exactly matches the vtable and methods of ISequentialStream.

Method | Description
Read | Reads a specified number of bytes from the stream.
Write | Writes a specified number of bytes to the stream.


ISequentialStream::Read

Reads a specified number of bytes from the stream object into memory starting at the current read/write location within the stream.

HRESULT Read(
   void *    pv,         //Pointer to buffer into which the stream is read
   ULONG    cb,         //Specifies the number of bytes to read
   ULONG *  pcbRead);   //Pointer to location that contains actual number                            of bytes read

Parameters

pv

[in]
Points to the buffer into which the stream is read. If an error occurs, this value is NULL.

cb

[in]
Specifies the number of bytes of data to attempt to read from the stream object.

pcbRead

[out]
Pointer to a location where this method writes the actual number of bytes read from the stream object. You can set this pointer to NULL to indicate that you are not interested in this value. In this case, this method does not provide the actual number of bytes read.

Return Code

S_OK

Data was successfully read from the stream object.

S_FALSE

The data could not be read from the stream object.

E_PENDING

Asynchronous Storage only: Part or all of the data to be read is currently unavailable. For more information, see IFillLockBytes and Asynchronous Storage.

STG_E_ACCESSDENIED

The caller does not have sufficient permissions for reading this stream object.

STG_E_INVALIDPOINTER

One of the pointer values is invalid.

STG_E_REVERTED

The object has been invalidated by a revert operation above it in the transaction tree.

Comments

This method reads bytes from this stream object into memory. The stream object must be opened in STGM_READ mode. This method adjusts the current read/write location within the stream by the actual number of bytes read.

The number of bytes actually read is returned in the pcbRead parameter.

The actual number of bytes read can be fewer than the number of bytes requested if an error occurs or if the end of the stream is reached during the read operation.

Some implementations might return an error if the end of the stream is reached during the read. You must be prepared to deal with the error return or S_OK return values on the end of stream reads.


ISequentialStream::Write

Writes a specified number of bytes into the stream object starting at the current read/write location within the stream.

HRESULT Write(
   Void const *    pv,      //Pointer to buffer into which the stream
                              is written
   ULONG          cb,      //Specifies the number of bytes to write
   ULONG *  pcbWritten);   // Specifies the actual number of
                              bytes written

Parameters

pv

[in]
Points to the buffer from which the stream should be written.

cb

[in]
The number of bytes of data to attempt to write into the stream.

pcbWritten

[out]
Pointer to a location where this method writes the actual number of bytes written to the stream object. The caller can set this pointer to NULL, in which case this method does not provide the actual number of bytes written.

Return Code

S_OK

The data was successfully written into the stream object.

E_PENDING

Asynchronous Storage only: Part or all of the data to be written is currently unavailable. For more information, see IFillLockBytes and Asynchronous Storage.

STG_E_MEDIUMFULL

The write operation was not completed because there is no space left on the storage device.

STG_E_ACCESSDENIED

The caller does not have sufficient permissions for writing this stream object.

STG_E_CANTSAVE

Data cannot be written for reasons other than lack of access or space.

STG_E_INVALIDPOINTER

One of the pointer values is invalid.

STG_E_REVERTED

The object has been invalidated by a revert operation above it in the transaction tree.

STG_E_WRITEFAULT

The write operation was not completed due to a disk error.

Comments

ISequentialStream::Write writes the specified data to a stream object. The current read/write location within the stream is adjusted for the number of bytes actually written. The number of bytes actually written is returned in the pcbWrite parameter. If the byte count is zero bytes, the write operation has no effect.

The pcbWrite parameter can have a value even if an error occurs.


ISessionProperties

ISessionProperties returns information about the properties a session supports and the current settings of those properties. It is a mandatory interface on sessions.

Method | Description
GetProperties | Returns the list of properties in the Session property group that are currently set on the session.
SetProperties | Sets properties in the Session property group.


ISessionProperties::GetProperties

Returns the list of properties in the Session property group that are currently set on the session.

HRESULT GetProperties (
   ULONG                  cPropertyIDSets,
   const DBPROPIDSET   rgPropertyIDSets[],
   ULONG *                  pcPropertySets,
   DBPROPSET **         prgPropertySets);

Parameters

cPropertyIDSets

[in]
The number of DBPROPIDSET structures in rgPropertyIDSets.

If cPropertySets is zero, the provider ignores rgPropertyIDSets and returns the values of all properties in the Session property group for which values have been set or defaults exist. It does not return the values of properties in the Session property group for which values have not been set and no defaults exist.

If cPropertyIDSets is not zero, the provider returns the values of the requested properties. If a property is not supported, the returned value of dwStatus in the returned DBPROP structure for that property is DBPROPSTATUS_NOTSUPPORTED and the value of dwOptions is undefined.

rgPropertyIDSets

[in]
An array of cPropertyIDSets DBPROPIDSET structures. The properties specified in these structures must belong to the Session property group. The provider returns the values of information about the properties specified in these structures. If cPropertyIDSets is zero, then this parameter is ignored.

For information about the properties in the Session property group that are defined by OLE DB, see "Session Properties" in Appendix C. For information about the DBPROPIDSET structure, see "DBPROPIDSET Structure" in Chapter 11.

pcPropertySets

[out]
A pointer to memory in which to return the number of DBPROPSET structures returned in *prgPropertySets. If cPropertyIDSets is zero, *pcPropertySets is the total number of property sets for which the provider supports at least one property in the Session property group. If cPropertyIDSets is greater than zero, *pcPropertySets is set to cPropertyIDSets. If an error other than DB_E_ERRORSOCCURRED occurs, *pcPropertySets is set to zero.

prgPropertySets

[out]
A pointer to memory in which to return an array of DBPROPSET structures. If cPropertyIDSets is zero, then one structure is returned for each property set that contains at least one property belonging to the Session property group. If cPropertyIDSets is not zero, then one structure is returned for each property set specified in rgPropertyIDSets.

If cPropertyIDSets is not zero, the DBPROPSET structures in *prgPropertySets are returned in the same order as the DBPROPIDSET structures in rgPropertyIDSets; that is, for corresponding elements of each array, the guidPropertySet elements are the same. If cPropertyIDs, in an element of rgPropertyIDSets, is not zero, the DBPROP structures in the corresponding element of *prgPropertySets are returned in the same order as the DBPROPID values in rgPropertyIDs; that is, for corresponding elements of each array, the property IDs are the same.

The provider allocates memory for the structures and returns the address to this memory; the consumer releases this memory with IMalloc::Free when it no longer needs the structures. Before calling IMalloc::Free for *prgPropertySets, the consumer should call IMalloc::Free for the rgProperties element within each element of *prgPropertySets. If *pcPropertySets is zero on output or if an error other than DB_E_ERRORSOCCURRED occurs, the provider does not allocate any memory and ensures that *prgPropertySets is a null pointer on output.

For information about the DBPROPSET and DBPROP structures, see "DBPROPSET Structure" and "DBPROP Structure" in Chapter 11.

Return Code

S_OK
The method succeeded. In all DBPROP structures returned by the method, dwStatus is set to DBPROPSTATUS_OK.

DB_S_ERRORSOCCURRED
No value was returned for one or more properties. The consumer checks dwStatus in the DBPROP structure to determine the properties for which values were not returned. GetProperties can fail to return properties for a number of reasons, including:

  • The property was not supported by the provider.

  • The property was not in the Session property group.

  • The property set was not supported by the provider. If cPropertyIDs in the DBPROPIDSET structure for the property set was zero, the provider cannot set dwStatus in the DBPROP structure because it does not know the IDs of any properties in the property set. Instead, it sets cProperties to zero in the DBPROPSET structure returned for the property set.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
cPropertyIDSets was not equal to zero and rgPropertyIDSets was a null pointer.

pcPropertySets or prgPropertySets was a null pointer.

In an element of rgPropertyIDSets, cPropertyIDs was not zero and rgPropertyIDs was a null pointer.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to return the DBPROPSET or DBPROP structures.

DB_E_ERRORSOCCURRED
Values were not returned for any properties. The provider allocates memory for *prgPropertySets and the consumer checks dwStatus in the DBPROP structures to determine why properties were not returned. The consumer frees this memory when it no longer needs the information.

See Also

IDBProperties::GetPropertyInfo, ISessionProperties::SetProperties


ISessionProperties::SetProperties

Sets properties in the Session property group.

HRESULT SetProperties (
   ULONG         cPropertySets,
   DBPROPSET   rgPropertySets[]);

Parameters

cPropertySets

[in]
The number of DBPROPSET structures in rgPropertySets. If this is zero, the provider ignores rgPropertySets and the method does not do anything.

rgPropertySets

[in/out]
An array of DBPROPSET structures containing properties and values to be set. The properties specified in these structures must belong to the Session property group. If the same property is specified more than once in rgPropertySets, then which value is used is provider-specific. If cPropertySets is zero, this parameter is ignored.

For information about the properties in the Session property group that are defined by OLE DB, see "Session Properties" in Appendix C. For information about the DBPROPSET and DBPROP structures, see "DBPROPSET Structure" and "DBPROP Structure" in Chapter 11.

Return Code

S_OK
The method succeeded. In all DBPROP structures passed to the method, dwStatus is set to DBPROPSTATUS_OK.

DB_S_ERRORSOCCURRED
One or more properties were not set. Properties not in error remain set. The consumer checks dwStatus in the DBPROP structures to determine which properties were not set. SetProperties can fail to set properties for a number of reasons, including:

  • The property was not supported by the provider.

  • The property was not in the Session property group.

  • The property set was not supported by the provider.

  • The property is read-only and was not set to its default value.

  • It was not possible to set the property.

  • colid in the DBPROP structure was not DB_NULLID.

  • The value of dwOptions in the DBPROP structure was invalid.

  • The data type in vValue in the DBPROP structure was not the data type of the property or was not VT_EMPTY.

  • The value in vValue in the DBPROP structure was invalid.

  • The property's value conflicted with an existing property.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
cPropertySets was not equal to zero and rgPropertySets was a null pointer.

In an element of rgPropertySets, cProperties was not zero and rgProperties was a null pointer.

DB_E_ERRORSOCCURRED
All property values were invalid and no properties were set. The consumer checks dwStatus in the DBPROP structures to determine why properties were not set.

See Also

IDBProperties::GetPropertyInfo, ISessionProperties::GetProperties


ISourcesRowset

ISourcesRowset returns a rowset of data sources and enumerators visible from the current enumerator. For more information about enumerators, see "Enumerators" in Chapter 2.

Method | Description
GetSourcesRowset | Returns a rowset of the data sources and enumerators visible from the current enumerator.


ISourcesRowset::GetSourcesRowset

Returns a rowset of the data sources and enumerators visible from the current enumerator.

HRESULT GetSourcesRowset(
   IUnknown         pUnkOuter,
   REFIID            riid,
   ULONG         cPropertySets,
   DBPROPSET   rgPropertySets[],
   IUnknown **      ppSourcesRowset);

Parameters

pUnkOuter

A pointer to the controlling IUnknown interface if the sources rowset is being created as part of an aggregate. If the rowset is not part of an aggregate, this must be set to a null pointer.

riid

The IID of the interface on which to return a pointer. This interface is conceptually added to the list of required interfaces on the resulting rowset, and the method fails (E_NOINTERFACE) if that interface cannot be supported on the resulting rowset.

cPropertySets

[in]
The number of DBPROPSET structures in rgPropertySets. If this is zero, the provider ignores rgPropertySets.

rgPropertySets

[in/out]
An array of DBPROPSET structures containing properties and values to be set. The properties specified in these structures must belong to the Rowset property group. If the same property is specified more than once in rgPropertySets, then it is provider-specific which value is used. If cPropertySets is 0, this argument is ignored.

For information about the properties in the Rowset property group that are defined by OLE DB, see "Rowset Properties" in Appendix C. For information about the DBPROPSET and DBPROP structures, see "DBPROPSET Structure" and "DBPROP Structure" in Chapter 11.

ppSourcesRowset

A pointer to memory in which to return the requested interface pointer on the rowset. If an error occurs, the returned pointer is null.

Return Code

S_OK
The method succeeded. In all DBPROP structures passed to the method, dwStatus is set to DBPROPSTATUS_OK.

DB_S_ERRORSOCCURRED
The rowset was opened but one or more properties—for which the dwOptions element of the DBPROP structure was DBPROPOPTIONS_OPTIONAL—were not set. The consumer checks dwStatus in the DBPROP structures to determine which properties were not set. The method can fail to set properties for a number of reasons, including:

  • The property was not supported by the provider.

  • The property was not in the Rowset property group.

  • The property set was not supported by the provider.

  • It was not possible to set the property.

  • The colid in the DBPROP structure was invalid.

  • The data type in vValue in the DBPROP structure was not the data type of the property or was not VT_EMPTY.

  • The value in vValue in the DBPROP structure was invalid.

  • The property's value conflicted with an existing property.

  • A property was specified to be applied to all columns, but could not be applied to one or more columns.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
ppSourcesRowset was a null pointer.

cPropertySets was not 0 and rgPropertySets was a null pointer.

In an element of rgPropertySets, cProperties was not 0 and rgProperties was a null pointer.

E_NOINTERFACE
The rowset did not support the interface specified in riid.

E_OUTOFMEMORY
The provider did not have enough memory to create the Rowset object.

E_UNEXPECTED
The Enumerator object was in an uninitialized state.

DB_E_ABORTLIMITREACHED
The method failed because a resource limit has been reached. For example, a query used to implement the method timed out. No rowset is returned.

DB_E_ERRORSOCCURRED
No rowset was returned because one or more properties—for which the dwOptions element of the DBPROP structure was DBPROPOPTIONS_REQUIRED or an invalid value—were not set. The consumer checks dwStatus in the DBPROP structures to determine which properties were not set. None of the satisfiable properties are remembered. The 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.

DB_E_NOAGGREGATION
pUnkOuter was not a null pointer and the rowset did not support aggregation.

pUnkOuter was non-null and riid was not IID_Unknown.

Comments

GetSourcesRowset returns the following rowset. The rowset is read-only. The columns are returned in the order shown.

Column name | Type indicator | Description
SOURCES_NAME | DBTYPE_WSTR | The name of the data source or enumerator.
SOURCES_PARSENAME | DBTYPE_WSTR | String to pass to IParseDisplayName to obtain a moniker for the data source or enumerator.
SOURCES_DESCRIPTION | DBTYPE_WSTR | The description of the data source or enumerator.
SOURCES_TYPE DBTYPE_UI2 Whether the row describes a data source or an enumerator:
  • DBSOURCETYPE_DATASOURCE

  • DBSOURCETYPE_ENUMERATOR

If a single piece of code is capable of being used both as a data source and as an enumerator, it is listed in the rowset twice, once in each role.
SOURCES_ISPARENT DBTYPE_BOOL If the row describes an enumerator, SOURCES_ISPARENT is VARIANT_TRUE if the enumerator is the parent enumerator; that is, the enumerator whose enumeration contains the enumerator on which ISourcesRowset::GetSourcesRowset was just called. This allows the consumer to go backward through the enumeration. Whether an enumerator is able to enumerate its parent is provider-specific. Otherwise, SOURCES_ISPARENT is VARIANT_FALSE.

If the row describes a data source, SOURCES_ISPARENT is ignored by the consumer.

See Also

IDBProperties::GetPropertyInfo


ISQLErrorInfo

ISQLErrorInfo is used to return the SQLSTATE and native error code.

When to Implement

Providers for SQL databases can implement ISQLErrorInfo on custom error objects returned with the OLE DB error object.

When to Call

Consumers call the methods on ISQLErrorInfo to retrieve the SQLSTATE and native error code. To retrieve a custom error object, a consumer calls IErrorRecords::GetCustomErrorObject.

Method | Description
GetSQLInfo | Returns the SQLSTATE and native error code associated with an error.


ISQLErrorInfo::GetSQLInfo

Returns the SQLSTATE and native error code associated with an error.

HRESULT GetSQLInfo (
   BSTR *      pbstrSQLState,
   LONG *   plNativeError);

Parameters

pbstrSQLState

[out]
A pointer to memory in which to return a pointer to a string that contains the SQLSTATE. An SQLSTATE is a five-character string defined by the ANSI SQL standard. The memory for this string is allocated by the provider and must be freed by the consumer with a call to SysFreeString. If an error occurs, *pbstrSQLState is set to a null pointer.

plNativeError

[out]
A pointer to memory in which to return a provider-specific, native error code. *plNativeError is not necessarily the same as the dwMinor element in the ERRORINFO structure returned by IErrorRecords::GetErrorInfo. The combination of the hrError and dwMinor elements of the ERRORINFO structure is used to identify an error to the error lookup service, whereas *plNativeError has no such restrictions.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
pbstrSQLState or plNativeError was a null pointer.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to return the SQLSTATE.


ISupportErrorInfo

ISupportErrorInfo is defined by Automation; the following describes how the interface is used in OLE DB. ISupportErrorInfo indicates whether a specific interface can return Automation error objects. Because OLE DB error objects are returned through the same mechanism as Automation error objects, support for them is also indicated through this interface.

When to Implement

ISupportErrorInfo must be implemented by the provider on any object that exposes an interface that can return OLE DB error objects.

When to Call

A consumer uses this interface to determine whether a particular OLE DB interface can return an OLE DB error object.

Method | Description
InterfaceSupportsErrorInfo | Indicates whether a specific OLE DB interface can return OLE DB error objects.


ISupportErrorInfo:: InterfaceSupportsErrorInfo

Indicates whether a specific OLE DB interface on the object can return OLE DB error objects.

HRESULT InterfaceSupportsErrorInfo (
   REFIID   riid);

Parameters

riid

[in]
The IID of the interface in question.

Return Code

S_OK
The interface can return Automation error objects; therefore, it can return OLE DB error objects.

S_FALSE
The interface cannot return Automation error objects; therefore, it cannot return OLE DB error objects.

Comments

InterfaceSupportsErrorInfo is implemented on each OLE DB object that may return an error object, and identifies the interfaces on that object that may generate an OLE DB error object.

To retrieve an OLE DB error object, the consumer takes the following steps:

  1. The consumer calls QueryInterface to retrieve a pointer to ISupportErrorInfo.

  2. The consumer calls ISupportErrorInfo::InterfaceSupportsErrorInfo and passes it the IID of the OLE DB interface that returned the error.

  3. The consumer calls GetErrorInfo in the Automation DLL if InterfaceSupportsErrorInfo returns S_OK. GetErrorInfo returns an IErrorInfo interface pointer on the OLE DB error object. If InterfaceSupportsErrorInfo returns S_FALSE, the consumer should discard any error object returned by GetErrorInfo because it applies to a different method and interface.


ITableDefinition

The ITableDefinition interface exposes simple methods to create, drop, and alter tables on the data source.

When to Implement

ITableDefinition is optional for providers that do not otherwise support table creation; it is mandatory for providers that support table creation through commands.

Method | Description
AddColumn | Adds a new column to a base table.
CreateTable | Creates a new base table in the data source.
DropColumn | Drops a column from a base table.
DropTable | Drops a base table in the data source.


ITableDefinition::AddColumn

Adds a new column to a base table.HRESULT AddColumn(
   DBID *                     pTableID,
   DBCOLUMNDESC *      pColumnDesc,
   DBID **                  ppColumnID);

Parameters

pTableID

[in]
A pointer to the DBID of the table to which the column is to be added.

pColumnDesc

[in/out]
A pointer to the DBCOLUMNDESC structure that describes the new column.

ppColumnID

[out]
A pointer to memory in which to return the returned DBID of newly created column. If this is a null pointer, no DBID is returned. If ppColumnID is non-null, the provider allocates memory for the DBID and overwrites *ppColumnID with a pointer to this new DBID without regard for its current value.

Return Code

S_OK
The method succeeded.

DB_S_ERRORSOCCURRED
The column was added, but one or more properties—for which the dwOptions element of the DBPROP structure was DBPROPOPTIONS_OPTIONAL—were not set. The consumer checks dwStatus in the DBPROP structures to determine which properties were not set. The method can fail to set properties for a number of reasons, including:

  • The property was not supported by the provider.

  • The property was not in the Column, Rowset, or Table property group.

  • The property set was not supported by the provider.

  • It was not possible to set the property.

  • colid in the DBPROP structure was not DB_NULLID.

  • The data type in vValue in the DBPROP structure was not the data type of the property or was not VT_EMPTY.

  • The value in vValue in the DBPROP structure was invalid.

  • The property's value conflicted with an existing property.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
pTableID or pColumnDesc was a null pointer.

DB_E_BADCOLUMNID
dbcid in *pColumnDesc was an invalid column ID.

DB_E_BADPRECISION
The precision in *pColumnDesc was invalid.

DB_E_BADSCALE
The scale in *pColumnDesc was invalid.

DB_E_BADTABLEID
*pTableID was an invalid table ID.

DB_E_BADTYPE
One or more of the wType, pwszTypeName, and pTypeInfo elements in an element of rgColumnDescs was invalid.

DB_E_DUPLICATECOLUMNID
dbcid in *pColumnDesc was the same as an existing column ID.

DB_E_ERRORSOCCURRED
The column was not added because one or more properties—for which the dwOptions element of the DBPROP structure was DBPROPOPTIONS_REQUIRED or an invalid value—could not be set. The consumer checks dwStatus in the DBPROP structures to determine which properties were not set. The method can fail to set properties for any of the reasons specified in DB_S_ERRORSOCCURRED.

DB_E_NOTABLE
The specified table does not exist in the current data source.

DB_E_TABLEINUSE
The specified table was in use.

DB_SEC_E_PERMISSIONDENIED
The consumer did not have sufficient permission to add a column.

Comments

If AddColumn returns any errors, the column is not created.

The placement of the new column, whether inserted or added after the last column, is provider-specific.

See Also

IDBProperties::GetPropertyInfo, ITableDefinition::CreateTable, ITableDefinition::DropColumn


ITableDefinition::CreateTable

Creates a new base table in the data source.

HRESULT CreateTable(
   IUnknown *            pUnkOuter,
   DBID *                  pTableID,
   ULONG               cColumnDescs,
   DBCOLUMNDESC   rgColumnDescs[],
   REFIID                  riid,
   ULONG               cPropertySets,
   DBPROPSET         rgPropertySets[],
   DBID **               ppTableID,
   IUnknown **            ppRowset);

Parameters

pUnkOuter

[in]
The controlling unknown if the rowset is to be aggregated, otherwise a null pointer.

pTableID

[in]
A pointer to the ID of the table to create. If this is a null pointer, the provider must assign a unique ID to the table.

cColumnDescs

[in]
The number of DBCOLUMNDESC structures in the rgColumnDescs array. This can be zero if the provider allows the creation of tables with no columns.

rgColumnDescs

[in/out]
An array of DBCOLUMNDESC structures that describe the columns of the table.

typedef struct tagDBCOLUMNDESC {
 LPOLESTR  pwszTypeName;
 ITypeInfo * pTypeInfo;
 DBPROPSET * rgPropertySets;
 CLSID *   pclsid;
 ULONG   cPropertySets;
 ULONG   ulColumnSize;
 DBID    dbcid;
 DBTYPE    wType;
 BYTE    bPrecision;
 BYTE    bScale;
} DBCOLUMNDESC;

The elements of this structure are used as follows. The consumer generally decides the values to use in the non-properties elements of this structure based on values from the PROVIDER_TYPES schema rowset.

Element | Description
pwszTypeName | The provider-specific name of the data type of the column. This name corresponds to a value in the TYPE_NAME column in the PROVIDER_TYPES schema rowset. In most cases, there is no reason for a consumer to specify a value for pwszTypeName that is different from the values listed in the PROVIDER_TYPES schema rowset.
pTypeInfo | If pTypeInfo is not a null pointer, then the data type of the column is an abstract data type (ADT) and values in this column are actually instances of the type described by the type library. wType may be either DBTYPE_BYTES with a length of at least 4, or it may be DBTYPE_IUNKNOWN. The instance values are required to be COM objects derived from IUnknown.
rgPropertySets | An array of DBPROPSET structures containing properties and values to be set. The properties specified in these structures must belong to the Column property group. All properties specified in rgPropertySets for this element of rgColumnDescs apply to the column specified by dbcid; the colid element in the DBPROP structure for specified properties is ignored. If the same property is specified more than once in rgPropertySets, then it is provider-specific which value is used. If cPropertySets is zero, this argument is ignored. For information about the properties in the Column property group that are defined by OLE DB, see "Column Properties" in Appendix C. For information about the DBPROPSET and DBPROP structures, see "DBPROPSET Structure" and "DBPROP Structure" in Chapter 11.
pclsid | If the column contains OLE objects, a pointer to the class ID of those objects. If more than one class of objects can reside in the column, *pclsid is set to IID_NULL.
cPropertySets | The number of DBPROPSET structures in rgPropertySets. If this is zero, the provider ignores rgPropertySets.
ulColumnSize | If wType is DBTYPE_STR or DBTYPE_WSTR, this is the maximum length in characters for values in this column. If wType is DBTYPE_BYTES, this is the maximum length in bytes for values in this column. For all other values of wType, this is ignored. Providers that do not require a length argument in the specification of the provider type (pwszTypeName) ignore this value.
dbcid | The column ID of the column.
wType | The type indicator for the data type of the column. This name corresponds to a value in the DATA_TYPE column in the PROVIDER_TYPES schema rowset. In most cases, there is no reason for a consumer to specify a value for wType that is different from the values listed in the PROVIDER_TYPES schema rowset.
bPrecision | The maximum precision of data values in the column when wType is the indicator for a numeric type; it is ignored for all other data types. This must be within the limits specified for the type in the COLUMN_SIZE column in the PROVIDER_TYPES schema rowset. For information about the precision of numeric data types, see "Precision of Numeric Data Types" in Appendix A.
bScale | The scale of data values in the column when wType is DBTYPE_NUMERIC or DBTYPE_DECIMAL; it is ignored for all other data types. This must be within the limits specified for the type in the MINIMUM_SCALE and MAXIMUM_SCALE columns in the PROVIDER_TYPES schema rowset.

riid

[in]
The IID of the interface to be returned for the resulting rowset; this is ignored if ppRowset is a null pointer.

cPropertySets

[in]
The number of DBPROPSET structures in rgPropertySets. If this is zero, the provider ignores rgPropertySets.

rgPropertySets

[in/out]
An array of DBPROPSET structures containing properties and values to be set. The properties specified in these structures must belong to the Rowset property group (for properties that apply to the rowset returned in *ppRowset) or the Table property group (for properties that apply to the table). If the same property is specified more than once in rgPropertySets, then it is provider-specific which value is used. If cPropertySets is zero, this argument is ignored.

For information about the properties in the Rowset and Tables property groups that are defined by OLE DB, see "Rowset Properties" and "Table Properties" in Appendix C. For information about the DBPROPSET and DBPROP structures, see "DBPROPSET Structure" and "DBPROP Structure" in Chapter 11.

ppTableID

[out]
A pointer to memory in which to return the DBID of the newly created table. If ppTableID is a null pointer, no DBID is returned.

ppRowset

[out]
A pointer to memory in which to return the requested interface pointer on an empty rowset opened on the newly created table. If ppRowset is a null pointer, no rowset is created.

Return Code

S_OK
The method succeeded and the table is created and opened. In all DBPROP structures passed to the method, dwStatus is set to DBPROPSTATUS_OK.

DB_S_ERRORSOCCURRED
The table was created and the rowset was opened, but one or more properties—for which the dwOptions element of the DBPROP structure was DBPROPOPTIONS_OPTIONAL—were not set. The consumer checks dwStatus in the DBPROP structures to determine which properties were not set. The method can fail to set properties for a number of reasons, including:

  • The property was not supported by the provider.

  • The property was not in the Column, Rowset, or Table property group.

  • The property set was not supported by the provider.

  • It was not possible to set the property.

  • colid in the DBPROP structure was not DB_NULLID.

  • The data type in vValue in the DBPROP structure was not the data type of the property or was not VT_EMPTY.

  • The value in vValue in the DBPROP structure was invalid.

  • The property's value conflicted with an existing property.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
pTableID and ppTableID were both null pointers.

cColumnDesc was not zero or rgColumnDescs was a null pointer. cColumnDesc was zero and the provider does not support creating tables with no columns.

cPropertySets was not zero and rgPropertySets was a null pointer.

In an element of rgPropertySets, cProperties was not zero and rgProperties was a null pointer.

In an element of rgColumnDescs, cPropertySets was not zero and rgPropertySets was a null pointer.

DB_E_BADCOLUMNID
dbcid in an element of rgColumnDescs was an invalid column ID.

DB_E_BADTABLEID
*pTableID was an invalid table ID.

DB_E_BADPRECISION
The precision in an element of rgColumnDescs was invalid.

DB_E_BADSCALE
The scale in an element of rgColumnDescs was invalid.

DB_E_BADTYPE
One or more of the wType, pwszTypeName, and pTypeInfo elements in an element of rgColumnDescs was invalid.

DB_E_DUPLICATECOLUMNID
dbcid was the same in two or more elements of rgColumnDescs.

DB_E_DUPLICATETABLEID
The specified table already exists in the current data source.

DB_E_ERRORSOCCURRED
The table was not created and no rowset was returned because one or more properties—for which the dwOptions element of the DBPROP structure was DBPROPOPTIONS_REQUIRED or an invalid value—could not be set. The consumer checks dwStatus in the DBPROP structures to determine which properties were not set. The method can fail to set properties for any of the reasons specified in DB_S_ERRORSOCCURRED.

DB_E_NOAGGREGATION
pUnkOuter was not a null pointer and the rowset being created does not support aggregation.

pUnkOuter was non-null and riid was not IID_Unknown.

DB_SEC_E_PERMISSIONDENIED
The consumer did not have sufficient permission to create the table.

DB_E_OBJECTOPEN
The provider would have to open a new connection to support the operation and DBPROP_MULTIPLECONNECTIONS is set to VARIANT_FALSE.

Comments

If ppRowset is not a null pointer, an empty rowset is opened on the newly created table. If CreateTable returns any errors, the table is not created, and ppTableID is set to NULL on output.

See Also

IDBProperties::GetPropertyInfo, ITableDefinition::AddColumn, ITableDefinition::DropColumn, ITableDefinition::DropTable


ITableDefinition::DropColumn

This method drops a column from the base table.

HRESULT DropColumn(
   DBID*   pTableID,
   DBID*   pColumnID);

Parameters

pTableID

[in]
A pointer to the DBID of the table from which to drop the column.

pColumnID

[in]
A pointer to the DBID of the column to drop.

Return Code

S_OK
The method succeeded and the column was dropped from the base table.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
pTableID or pColumnID was a null pointer.

DB_E_BADCOLUMNID
The column specified in *pColumnID does not exist in the specified table.

DB_E_BADTABLEID
*pTableID was an invalid table ID.

DB_E_NOTABLE
The specified table does not exist in the current data source.

DB_E_TABLEINUSE
The specified table was in use.

DB_SEC_E_PERMISSIONDENIED
The consumer did not have sufficient permission to drop the column.

DB_E_BADTABLEID
*pTableID was an invalid table ID.

Comments

If DropColumn returns any errors, the column is not dropped.

See Also

ITableDefinition::AddColumn


ITableDefinition::DropTable

Drops a base table in the data source.

HRESULT DropTable (
   DBID *   pTableID);

Parameters

pTableID

[in]
A pointer to the DBID of the base table to drop.

Return Code

S_OK
The method succeeded and the table is dropped.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
pTableID was a null pointer.

DB_E_NOTABLE
The specified table does not exist in the current data source.

DB_E_TABLEINUSE
The specified table was in use and could not be dropped.

DB_SEC_E_PERMISSIONDENIED
The consumer did not have sufficient permission to drop the table.

DB_E_BADTABLEID
*pTableID was an invalid table ID.

See Also

ITableDefinition::CreateTable


ITransaction

The ITransaction interface is used to commit, abort, and obtain status information about transactions. For more information about transactions, see Chapter 12, "Transactions."

Method | Description
Abort | Aborts a transaction.
Commit | Commits a transaction.
GetTransactionInfo | Returns information about a transaction.


ITransaction::Abort

Aborts a transaction.

HRESULT Abort(
   BOID *   pboidReason,
   BOOL   fRetaining,
   BOOL   fAsync);

Parameters

pboidReason

[in]
A pointer to a BOID that indicates why the transaction is being aborted. If this is a null pointer, no reason is provided.

fRetaining

[in]
Whether the abort is retaining or nonretaining.

fAsync

[in]
When fAsync is TRUE, an asynchronous abort is performed and the caller must use ITransactionOutcomeEvents to learn the outcome of the transaction.

Return Code

S_OK
The transaction was successfully aborted.

XACT_S_ABORTING
An abort operation was already in progress. This call was ignored.

XACT_S_ASYNC
An asynchronous abort was specified. The abort operation has begun but its outcome is not yet known. When the transaction is complete, notification will be sent by ITransactionOutcomeEvents.

E_FAIL
The transaction failed to abort for an unspecified reason.

E_UNEXPECTED
An unexpected error occurred. The transaction status is unknown.

XACT_E_ALREADYINPROGRESS
A commit operation was already in progress. This call was ignored.

XACT_E_CANTRETAIN
A retaining abort is not supported or a new unit of work could not be created. The abort succeeded and the session is in autocommit mode.

XACT_E_CONNECTION_DOWN
The connection to the transaction manager failed. The transaction state is unknown.

XACT_E_INDOUBT
The transaction status is in doubt. A communication failure occurred or a transaction manager or resource manager has failed.

XACT_E_NOTRANSACTION
The transaction cannot be aborted because it already had been implicitly or explicitly committed or aborted. This call was ignored.

XACT_E_NOTSUPPORTED
fAsync was TRUE on input and asynchronous abort operations are not supported.

Comments

The following table shows how the values of fRetaining and DBPROP_ABORTPRESERVE affect the rowset state and transaction mode.

DBPROP_ ABORT PRESERVE | fRetaining | Rowset state after abort | Resulting transaction mode of session
FALSE | FALSE | zombie | implicit / autocommit
FALSE | TRUE | zombie | explicit / manual
TRUE | FALSE | preserved | implicit / autocommit
TRUE | TRUE | preserved | explicit / manual

See Also

ITransaction::Commit


ITransaction::Commit

Commits a transaction.

HRESULT Commit(
   BOOL      fRetaining,
   DWORD   grfTC,
   DWORD   grfRM);

Parameters

fRetaining

[in]
Whether the commit is retaining or nonretaining.

grfTC

[in]
Values taken from the enumeration XACTTC. Values that may be specified in grfTC are as follows. These values are mutually exclusive.

Flag | Description
XACTTC_ ASYNC_PHASEONE | When this flag is specified, an asynchronous commit is performed.
XACTTC_ SYNC_ PHASEONE | When this flag is specified, the call to Commit returns after phase one of the two-phase commit protocol.
XACTTC_ SYNC_ PHASETWO | When this flag is specified, the call to Commit returns after phase two of the two-phase commit protocol.
XACTTC_ SYNC | Synonym for XACTTC_SYNC_PHASETWO.

grfRM

[in]
Must be zero.

Return Code

S_OK
The transaction was successfully committed.

XACT_S_ASYNC
An asynchronous commit was specified. The commit operation has begun but its outcome is not yet known. When the transaction is complete, notification will be sent by ITransactionOutcomeEvents.

E_FAIL
A provider-specific error occurred. The transaction was aborted.

E_UNEXPECTED
An unexpected error occurred. The transaction status is unknown.

XACT_E_ABORTED
The transaction was aborted before Commit was called.

XACT_E_ALREADYINPROGRESS
A commit or abort operation was already in progress. This call was ignored.

XACT_E_CANTRETAIN
Retaining commit is not supported or a new unit of work could not be created. The commit succeeded and the session is in autocommit mode.

XACT_E_COMMITFAILED
The transaction failed to commit for an unknown reason. The transaction was aborted.

XACT_E_CONNECTION_DOWN
The connection to the transaction manager failed. The transaction was aborted.

XACT_E_INDOUBT
The transaction status is in doubt. A communication failure occurred or a transaction manager or resource manager has failed.

XACT_E_NOTRANSACTION
Unable to commit the transaction because it had already been implicitly or explicitly committed or aborted. This call was ignored.

XACT_E_NOTSUPPORTED
An invalid combination of commit flags was specified or grfRM was not equal to zero. This call was ignored.

Comments

The following table shows how values of the fRetaining parameter and DBPROP_COMMITPRESERVE affect the rowset state and transaction mode.

DBPROP_ COMMIT PRESERVE | fRetaining | Rowset state after commit | Resulting transaction mode of session
FALSE | FALSE | zombie | implicit / autocommit
FALSE | TRUE | zombie | explicit / manual
TRUE | FALSE | preserved | implicit / autocommit
TRUE | TRUE | preserved | explicit / manual

If Commit fails for any reason that results in an aborted transaction, the session is left in autocommit mode.

See Also

ITransaction::Abort


ITransaction::GetTransactionInfo

Returns information regarding a transaction.

HRESULT GetTransactionInfo(
   XACTTRANSINFO *   pInfo);

Parameters

pInfo

[out]
A pointer to the caller-allocated XACTTRANSINFO structure in which the method returns information about the transaction. pInfo must not be a null pointer.

typedef struct XACTTRANSINFO {
 XACTUOW  uow;
     ISOLEVEL isoLevel;
     ULONG  isoFlags;
     DWORD  grfTCSupported;
     DWORD  grfRMSupported;
     DWORD  grfTCSupportedRetaining;
     DWORD  grfRMSupportedRetaining;
} XACTTRANSINFO;

The elements of this structure are used as follows.

Element | Description
uow | The unit of work associated with this transaction.
isoLevel | The isolation level associated with this transaction. ISOLATIONLEVEL_UNSPECIFIED indicates that no isolation level was specified. For more information, see ITransactionLocal::StartTransaction.
isoFlags | Will be zero.
grfTCSupported | This bitmask indicates the XACTTC flags that this transaction implementation supports.
grfRMSupported | Will be zero.
grfTCSupportedRetaining | Will be zero.
grfRMSupportedRetaining | Will be zero.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
pInfo was a null pointer.

E_UNEXPECTED
An unknown error occurred. No information is returned.

XACT_E_NOTRANSACTION
Unable to retrieve information for the transaction because it was already completed. No information is returned.


ITransactionJoin

ITransactionJoin is exposed only by providers that support distributed transactions. The consumer calls QueryInterface for ITransactionJoin to determine whether the provider supports coordinated transactions. For more information, see "Coordinated Transactions" in Chapter 12.

Method | Description
GetOptionsObject | Returns an object that can be used to specify configuration options for a subsequent call to JoinTransaction.
JoinTransaction | Requests that the session enlist in a coordinated transaction.


ITransactionJoin::GetOptionsObject

Returns an object that can be used to specify configuration options for a subsequent call to JoinTransaction.

HRESULT GetOptionsObject (
   ITransactionOptions **   ppOptions);

Parameters

ppOptions

[out]
A pointer to memory in which to return a pointer to the object that can be used to set extended transaction options.

Return Code

S_OK
The method succeeded.

E_FAIL
An unknown error occurred.

E_INVALIDARG
ppOptions was a null pointer.

E_OUTOFMEMORY
Unable to allocate memory.

See Also

ITransactionOptions::GetOptions


ITransactionJoin::JoinTransaction

Requests that the session enlist in a coordinated transaction.

HRESULT JoinTransaction (
   IUnknown *               punkTransactionCoord,
   ISOLEVEL               isoLevel,
   ULONG                  isoFlags,
   ITransactionOptions *   pOtherOptions);

Parameters

punkTransactionCoord

[in]
A pointer to the controlling IUnknown of the transaction coordinator, or NULL in order to unenlist from the coordinated transaction. If non-null, QueryInterface can be called for ITransaction on the transaction coordinator.

isoLevel

[in]
The isolation level to be used with this transaction. For more information, see ITransactionLocal::StartTransaction.

isoFlags

[in]
Must be zero.

pOtherOptions

[in]
Optionally a null pointer. If this is not a null pointer, then it is a pointer to an object previously returned from GetOptionsObject called on this session.

Return Code

S_OK
The method succeeded.

E_FAIL
An unknown error occurred.

E_INVALIDARG
punkTransactionCoord was a null pointer and the provider doesn’t support unenlisting from a coordinated transaction.

E_UNEXPECTED
An unknown provider-specific error occurred.

XACT_E_CONNECTION_DOWN
The connection to the transaction manager failed.

XACT_E_CONNECTION_REQUEST_DENIED
The transaction manager did not accept a connection request.

XACT_E_ISOLATIONLEVEL
Neither the requested isolation level, nor a strengthening of it, can be supported by this transaction implementation or isoLevel was not valid.

XACT_E_LOGFULL
Unable to begin a new transaction because the log file is full.

XACT_E_NOENLIST
A transaction coordinator was specified, but the new transaction was unable to enlist therein.

XACT_E_NOISORETAIN
The requested semantics of retention of isolation across retaining commit and abort boundaries cannot be supported by this transaction implementation or isoFlags was not equal to zero.

XACT_E_NOTIMEOUT
A time-out was specified, but time-outs are not supported.

XACT_E_TMNOTAVAILABLE
Unable to connect to the transaction manager or the transaction manager is unavailable.

XACT_E_XTIONEXISTS
The enlistment request failed for one of the following reasons:

  • This session can handle only one extant transaction at a time, and there is presently such a transaction.

  • punkTransactionCoord was NULL, and the session is enlisted in a coordinated transaction with uncommitted work.

Comments

The provider of the session will generally first register its ITransactionOutcomeEvents notification sink with the transaction coordinator, so that it can be notified of the outcome of the coordinated events, and will then enlist with the transaction coordinator using the interfaces defined in OLE Transactions. It is important that the provider first register with the outcome events so that it can be advised of any failures which may occur between enlistment registration of the outcome events.

See Also

ITransactionLocal::StartTransaction


ITransactionLocal : ITransaction

ITransactionLocal is an optional interface on sessions. It is used to start, commit, and abort transactions on the session. For more information, see "Simple Transactions" in Chapter 12.

Method | Description
GetOptionsObject | Returns an object that can be used to specify configuration options for a subsequent call to StartTransaction.
StartTransaction | Begins a new transaction.


ITransactionLocal::GetOptionsObject

Returns an object that can be used to specify configuration options for a subsequent call to StartTransaction.

HRESULT GetOptionsObject (
   ITransactionOptions **   ppOptions);

Parameters

ppOptions

[out]
A pointer to memory in which to return a pointer to the object that can be used to set extended transaction options.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
ppOptions was a null pointer.

E_OUTOFMEMORY
Unable to allocate memory.

E_UNEXPECTED
An unknown error occurred and the method failed.

See Also

ITransactionLocal::StartTransaction, ITransactionOptions::SetOptions


ITransactionLocal::StartTransaction

Begins a new transaction.

HRESULT StartTransaction (
   ISOLEVEL               isoLevel,
   ULONG                  isoFlags,
   ITransactionOptions *   pOtherOptions,
   ULONG *                  pulTransactionLevel);

Parameters

isoLevel

[in]
The isolation level to be used with this transaction. For more information, see "Isolation Levels" in Chapter 12.

Value | Description
ISOLATIONLEVEL_ UNSPECIFIED | Applicable only to ITransactionJoin::JoinTransaction. Invalid for ITransactionLocal or for setting isolation level while in autocommit mode.
ISOLATIONLEVEL_ CHAOS | Cannot overwrite the dirty data of other transactions at higher isolation levels.
ISOLATIONLEVEL_ READUNCOMMITTED | Read Uncommitted.
ISOLATIONLEVEL_ BROWSE | Synonym for ISOLATIONLEVEL_READUNCOMMITTED.
ISOLATIONLEVEL_ READCOMMITTED | Read Committed.
ISOLATIONLEVEL_ CURSORSTABILITY | Synonym for ISOLATIONLEVEL_READCOMMITTED.
ISOLATIONLEVEL_ REPEATABLEREAD | Repeatable Read.
ISOLATIONLEVEL_ SERIALIZABLE | Serializable.
ISOLATIONLEVEL_ ISOLATED | Synonym for ISOLATIONLEVEL_SERIALIZABLE.

isoFlags

[in]
Must be zero.

pOtherOptions

[in]
Optionally a null pointer. If this is not a null pointer, it is a pointer to an object previously returned from GetOptionsObject called on this session instance.

pulTransactionLevel

[out]
A pointer to memory in which to return the level of the new transaction. The value of the top-level transaction is 1. If pulTransactionLevel is a null pointer, the level is not returned.

Return Code

S_OK
The method succeeded.

DB_E_OBJECTOPEN

A rowset object was open and the provider does not support starting a new transaction with existing open rowset objects.

E_FAIL
A provider-specific error occurred.

E_UNEXPECTED
An unknown error occurred and the method failed.

XACT_E_CONNECTION_DENIED
This session could not create a new transaction at the present time due to unspecified capacity issues.

XACT_E_CONNECTION_DOWN
This session is having communication difficulties with its internal implementation.

XACT_E_ISOLATIONLEVEL
Neither the requested isolation level nor a strengthening of it can be supported by this transaction implementation or isoLevel was not valid.

XACT_E_LOGFULL
A transaction could not be created because this session uses logging to a device that lacks available space.

XACT_E_NOISORETAIN
The requested semantics of retention of isolation across retaining commit and abort boundaries cannot be supported by this transaction implementation or isoFlags was not equal to zero.

XACT_E_NOTIMEOUT
A noninfinite time-out value was requested, but time-outs are not supported by this transaction.

XACT_E_XTIONEXISTS
This session can handle only one extant transaction at a time, and there is presently such a transaction.

The session supports a limited number of nested transactions, and that limit has been reached.

Comments

ITransactionLocal starts a new local transaction. If there is already an active transaction on the session (that is, StartTransaction has been called with no matching Commit or Abort), then the new transaction is started as a nested transaction below the current transaction. Calling Commit or Abort always commits or aborts the transaction at the lowest level, respectively. If the provider does not support nested transactions, calling StartTransaction when there is already an active transaction on the session returns XACT_E_XTIONEXISTS.

See Also

ITransactionOptions::SetOptions


ITransactionObject

ITransactionObject enables consumers to obtain the transaction object associated with a particular transaction level. For more information, see "Transaction Objects" in Chapter 12.

Method | Description
GetTransactionObject | Returns an interface pointer on the transaction object.


ITransactionObject::GetTransactionObject

Returns an interface pointer on the transaction object.

HRESULT GetTransactionObject (
   ULONG         ulTransactionLevel,
   ITransaction **   ppTransactionObject);

Parameters

ulTransactionLevel

[in]
The level of the transaction.

ppTransactionObject

[out]
A pointer to memory in which to return a pointer to the returned transaction object.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
ulTransactionLevel was zero or ppTransactionObject was a null pointer.

E_UNEXPECTED
An unknown error occurred and the method failed.


ITransactionOptions

ITransactionOptions gets and sets a suite of options associated with a transaction.

Method | Description
GetOptions | Gets a suite of options associated with a transaction.
SetOptions | Sets a suite of options associated with a transaction.


ITransactionOptions::GetOptions

Gets a suite of options associated with a transaction.

HRESULT GetOptions(
   XACTOPT *   pOptions);

Parameters

pOptions

[in/out]
A pointer to an XACTOPT structure in which to return the options for this transaction. The consumer allocates this structure. For more information, see SetOptions.

Return Code

S_OK
Success.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
pOptions was a null pointer.

E_UNEXPECTED
An unknown error occurred; the method failed.

Comments

This method can be called at any time. GetOptions does not make any logical changes to the state of any open transactions.

See Also

ITransactionOptions::SetOptions, ITransactionJoin::GetOptionsObject, ITransactionLocal::GetOptionsObject


ITransactionOptions::SetOptions

Sets a suite of options associated with a transaction.

HRESULT SetOptions(
   XACTOPT *   pOptions);

Parameters

pOptions

[in]
A pointer to an XACTOPT structure containing the options to be set in this transaction. This cannot be a null pointer.

typedef struct XACTOPT {
 ULONG   ulTimeout;
 unsigned char szDescription[MAX_TRAN_DESC];
} XACTOPT

The elements of this structure are used as follows.

Element | Description
ulTimeout | The amount of real time in milliseconds before the transaction is to be aborted automatically. Zero indicates an infinite timeout. If no options have been previously set, ulTimeout is zero.
szDescription | A pointer to a textual description associated with this transaction. This string is appropriate for display in various end-user administration tools that might monitor or log the transaction. If no options have been previously set, szDescription is an empty string.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
pOptions was a null pointer.

E_UNEXPECTED
An unknown error occurred; the method failed.

See Also

ITransactionOptions::GetOptions, ITransactionLocal::StartTransaction


IViewChapter

IViewChapter enables the consumer to create or apply a view to an existing rowset.

When to Implement

Providers that can do simple post-processing of rowsets, such as applying filters or re-sorting rowsets, support IViewChapter.

When to Call

Consumers call IViewChapter in order to apply post-processing operations to an existing rowset. The consumer calls CreateView on the rowset in order to create a view object, applies post-processing operations to that view object, then calls OpenViewChapter to retrieve a chapter handle that reflects the specified view conditions. Any resources associated with the view can be released with a call to IChapteredRowset::ReleaseChapter.

Method | Description
GetSpecification | Returns the rowset from which the view was created.
OpenViewChapter | Returns a chapter reflecting the specified view.


IViewChapter::GetSpecification

Returns the rowset from which the view was created.

HRESULT GetSpecification (
   REFIID            riid,
   IUnknown **      ppRowset);

Parameters

riid

[in]
The IID of the interface on which to return a pointer.

ppRowset

[out]
A pointer to memory in which to return the interface pointer. If GetSpecification fails, it must attempt to set *ppRowset to a null pointer.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_NOINTERFACE
The view did not support the interface specified in riid.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to return the rowset information.

Comments

GetSpecification adds a reference count to the rowset. The consumer must call Release in order to release the reference count when it is no longer needed.


IViewChapter::OpenViewChapter

Returns a chapter for the rows meeting the specified view conditions.

HRESULT OpenViewChapter (
   HCHAPTER         hSource,
   HCHAPTER *      phViewChapter);

Parameters

hSource

[in]
A chapter defining the set of rows on which to apply the view. In order to apply a view to the root rowset, the consumer must use DB_NULL_HCHAPTER.

phViewChapter

[out]
A chapter reflecting the specified view conditions, or NULL to indicate that the view conditions should be applied to hSource.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

DB_E_BADCHAPTER
hSource was invalid.

DB_E_CANTFILTER
The described filter could not be opened. The provider may have limitations on the columns used in a filter or a limitation on the complexity of the filter.

DB_E_CANTORDER
The described order could not be opened. The provider may have limitations on the columns used in an order or a limitation on the complexity of the order.

DB_E_BADCOMPAREOP
A specified comparison operator was invalid.

Comments

None.


IViewFilter

IViewFilter enables consumers to restrict the contents of a rowset to rows matching a set of conditions.

When to Implement

Providers that can define a subset of rows based on a consumer-defined set of conditions expose IViewFilter. A provider may restrict functionality to filters based on existing indexes, or filters may require a specific sort order also to be applied.

When to Call

Consumers needing a subset of rows can call IViewFilter::SetFilter to apply a filter condition to a view. Rowsets or chapters opening using this view expose only those rows that match the criteria. Each view can have only a single filter applied, but a view containing a filter can be applied to an existing filtered chapter to further filter its contents.

Filter criteria are expressed as an array of rows and columns, with an associated set of comparison operators.

Within a row of the criteria array, each column corresponds to a column in the table. The mapping of the columns in the criteria array to the columns in the table is done through an accessor passed into SetFilter or GetFilter. The same column from the table may appear once, more than once, or not at all in the criteria array. Each column in the criteria array has a corresponding comparison operation that defines how the value passed in the criteria array is to be compared to the value of the corresponding column in the rowset. Each column of the criteria array is joined in a logical AND with the other columns in that row of the criteria array.

The criteria array can contain multiple rows. In this case, the array of comparison operations is a two-dimensional array stored in row-major format, such that the comparison operators for each of the columns of the first row in the criteria array are followed by the comparison operators for each column of the second row in the criteria array, and so forth. Multiple rows in the criteria array are joined together in a logical OR.

For example, assume the following rowset:

Name | Children | State
Joe | 0 | WA
Jane | 2 | WA
Fred | 3 | OR
Judy | 1 | OR
Jane | 2 | ID

The following table shows the results for various combinations of accessor bindings, criteria array, and comparison operators.

Accessor | Criteria Array | Comparison Ops | Result
rgBindings[0].iOrdinal = 3 | {DBCOMPAREOPS_EQ} | {"WA"} | "Joe"0 "WA" "Jane"2 "WA"
rgBindings[0].iOrdinal=3 | {DBCOMPAREOPS_EQ, DBCOMPAREOPS_EQ} | {"WA","ID"} | "Joe"0 "WA" "Jane"2 "WA" "Jane"2 "ID"
rgBindings[0].iOrdinal=3 rgBindings[1].iOrdinal=2 | {DBCOMPAREOPS_EQ, DBCOMPAREOPS_GT} | {"OR",1} | "Fred"3 "OR"
rgBindings[0].iOrdinal=3 rgBindings[1].iOrdinal=2 | {DBCOMPAREOPS_EQ, DBCOMPAREOPS_IGNORE, DBCOMPAREOPS_IGNORE, DBCOMPAREOPS_GT} | {"OR",NULL, NULL,1} | "Jane"2 "WA" "Fred"3 "OR" "Judy"1 "OR" "Jane"2 "ID"

The following code example shows defining a filter for which the individual is from Oregon or has more than one child:

DBBINDING    rgBindings[2];
IAccessor     *pIAccessor;
HACCESSOR    hAccessor;
DBCOMPAREOP    rgCompareOps[] = { DBCOMPAREOP_EQ, DBCOMPAREOP_IGNORE,
                          DBCOMPAREOP_IGNORE, DBCOMPAREOP_GT };

typedef struct FILTER_CRITERIA {
   TCHAR    rcState[4];
   ULONG    ulStateStatus;
   ULONG    ulStateLength;
   ULONG    cChildren;
   ULONG    ulChildrenStatus;
} WHERE_CLAUSE;

WHERE_CLAUSE rgCriteriaData[] =
{
   _T("OR"), DBSTATUS_S_OK,   2 * sizeof(TCHAR), NULL, DBSTATUS_S_ISNULL,
   NULL,   DBSTATUS_S_ISNULL, 0,       1,  DBSTATUS_S_OK
};

//initialize common binding values
for (cCol = 0; cCol < 2; cCol++)
{
   rgBindings[cCol].pTypeInfo   = NULL;
   rgBindings[cCol].pObject   = NULL;
   rgBindings[cCol].pBindExp   = NULL;
   rgBindings[cCol].dwMemOwner   = DBMEMOWNER_CLIENTOWNED;
   rgBindings[cCol].eParamIO   = DBPARAMIO_NOTPARAM;
   rgBindings[cCol].dwFlags   = 0;
   rgBindings[cCol].bPrecision   = 0;
   rgBindings[cCol].bScale      = 0;
}

//initial bindings for the "State" column
   rgBindings[0].dwPart   = DBPART_VALUE | DBPART_LENGTH | DBPART_STATUS;
   rgBindings[0].iOrdinal   = 3;
   rgBindings[0].wType      = DBTYPE_STR;
   rgBindings[0].obValue   = offsetof(WHERE_CLAUSE, rcState);
   rgBindings[0].obStatus   = offsetof(WHERE_CLAUSE, ulStateStatus);
   rgBindings[0].obLength   = offsetof(WHERE_CLAUSE, ulStateLength);
   rgBindings[0].cbMaxLen   = sizeof(WHERE_CLAUSE.rcState);

//initial bindings for the "Children" column
   rgBindings[1].dwPart   =
   rgBindings[1].iOrdinal   = 2
   rgBindings[1].wType      = DBTYPE_I4;
   rgBindings[1].obValue   = offsetof(WHERE_CLAUSE, cChildren);
   rgBindings[1].obStatus   = offsetof(WHERE_CLAUSE, ulChildrenStatus);
   rgBindings[0].obLength   = 0;
   rgBindings[1].cbMaxLen   = sizeof(WHERE_CLAUSE.cChildren);

pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 2, rgBindings, sizeof(WHERE_CLAUSE), &hAccessor, NULL);

//set filter condition
pIViewFilter->SetFilter(hAccessor, 2, rgCompareOps, rgCriteriaData);

Method | Description
GetFilter | Returns the filter condition associated with a view.
GetFilterBindings | Returns the bindings used to describe the filter conditions associated with a view.
SetFilter | Sets a filter condition on a view.


IViewFilter::GetFilter

Retrieves the filter applied to a view.

HRESULT GetFilter (
   HACCESSOR         hAccessor,
   ULONG *               pcRows,
   DBCOMPAREOP *   pCompareOps[],
   void *                  pCriteriaData);

Parameters

hAccessor

[in]
A row data accessor describing the data to be written to ppCriteriaData. The same column may appear more than once in the criteria.

pcRows

[out]
A pointer to memory in which to write the number of rows in the criteria table, where the criteria described by each row is joined in a logical OR with the other rows. If pcRows is greater than one, then the cbRowSize argument used in the call to CreateAccessor is used to specify the offset between each set of row values.

pCompareOps

[out]
A pointer to memory in which to write a two-dimensional array of comparisons operators. The two-dimensional array contains pcRows by cBindings comparison operators in pcRows-major format, where cBindings is the number of columns represented in hAccessor, and is written in row-major format. Each comparison operator in the cBindings dimension refers to a column in pCriteriaData, and each set of columns in the pcRows dimension refers to a row in pCriteriaData. Columns within a row are joined together in a logical AND, and each row is joined in a logical OR with another row. For information about the DBCOMPAREOP enumerated type, see IRowsetFind::FindNextRow.

pCriteriaData

[out]
A pointer to a buffer in which to return the criteria data.

Return Code

S_OK
The method succeeded.

DB_S_ERRORSOCCURRED
An error occurred while returning data for one or more columns, but data was successfully returned for at least one column. To determine the columns for which data was returned, the consumer checks the status values. For a list of status values that can be returned by this method, see "Status Values Used When Getting Data" in "Status" in Chapter 6.

E_FAIL
A provider-specific error occurred.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to return the filter information.

DB_E_BADACCESSORHANDLE
hAccessor was invalid. Providers are required to check for this condition in this method.

DB_E_BADACCESSORTYPE
The specified accessor was not a row accessor.

DB_E_BADCOMPAREOP
In an element of pCompareOps, both DBCOMPAREOPS_CASESENSITIVE and DBCOMPAREOPS_CASEINSENSITIVE were specified.

The provider was asked for an option that it does not support.DB_E_ERRORSOCCURRED
Errors occurred while returning data for all columns. To determine what errors occurred, the consumer checks the status values. For a list of status values that can be returned by this method, see "Status Values Used When Getting Data" in "Status" in Chapter 6.

Comments

None.


IViewFilter::GetFilterBindings

Retrieves the bindings used to describe the filter conditions associated with a view.

HRESULT GetFilterBindings(
   ULONG *            pcBindings,
   DBBINDING **      prgBindings);

Parameters

cBindings

[out]
A pointer to memory in which to return the number of bindings used to describe the filter criteria. If this method fails or if no filter has been applied to the view, *pcBindings is set to zero.

rgBindings

[out]
A pointer to memory in which to return an array of DBBINDING structures. One structure is returned for each binding used to describe the filter criteria. The provider allocates memory for the structures and returns the address to this memory; the consumer releases this memory with IMalloc::Free when it no longer needs the bindings. If *pcBindings is zero on output or the method fails, the provider does not allocate any memory and ensures that *prgBindings is a null pointer on output. For information about bindings, see "DBBINDING Structures" in Chapter 6.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
pcBindings or prgBindings was a null pointer.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to return the binding structures.

Comments

The consumer calls IViewFilter::GetFilterBindings in order to retrieve information about the columns bound in the current filter. The consumer can use this information in order to create an accessor to be passed into IViewFilter::GetFilter in order to retrieve the information describing the filter conditions.


IViewFilter::SetFilter

Specifies a filter condition for a view.

HRESULT SetFilter (
   HACCESSOR      hAccessor,
   ULONG            cRows,
   DBCOMPAREOP   CompareOps[],
   void *               pCriteriaData);

Parameters

hAccessor

[in]
The handle of the accessor which describes the data in pCriteriaData. The same column may appear more than once in the criteria.

cRows

[in]
The number of rows in the criteria table, where the criteria described by each row is joined in a logical OR with the other rows. Some providers may have limits on the number of rows (OR conditions) that can be expressed in the criteria.

CompareOps

[in]
A two-dimensional array containing cRows by cBindings comparison operators in cRows-major format, where cBindings is the number of columns represented in hAccessor. Each comparison operator in the cBindings dimension refers to a column in pCriteriaData, and each set of columns in the pcRows dimension refers to a row in pCriteriaData. Columns within a row are joined together in a logical AND, and each row is joined in a logical OR with another row. The consumer should check DBPROP_FINDCOMPAREOPS to determine which comparison operators the provider supports. For information about the DBCOMPAREOP enumerated type, see IRowsetFind::FindNextRow.

pCriteriaData

[in]
A pointer to memory containing the data values, at offsets that correspond to the bindings in the accessor which, in conjunction with the array of comparison operators, define the criteria.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

DB_E_BADCOMPAREOP
In an element of CompareOps, both DBCOMPAREOPS_CASESENSITIVE and DBCOMPAREOPS_CASEINSENSITIVE were specified.

The provider was asked for an option that it does not support.

DB_E_CANTFILTER
The described filter could not be applied. The provider may have limitations on the columns used in a filter or a limitation on the complexity of the filter.

Comments

A provider may restrict filter columns to use only existing indexes, or may restrict a filter to work only with an order chapter as a source.

If there is already a filter condition applied to a view, SetFilter overrides that previous filter condition.

Changing the filter condition of a view does not change the filtering of any rowsets or chapters opened using that view.


IViewRowset

IViewRowset enables the consumer to create or apply view operations when opening a rowset.

When to Implement

Providers that can do simple operations, such as filtering and sorting when opening rowsets, support IViewRowset.

When to Call

Consumers call IViewRowset in order to open a rowset with simple operations such as filtering and sorting applied. The consumer requests IID_IViewRowset when calling a method that returns a rowset in order to create a view object, applies operations to that view, then calls OpenViewRowset to open the rowset with those operations applied.

Method | Description
GetSpecification | Returns the object that created the view.
OpenViewRowset | Opens the requested rowset using the specified view.


IViewRowset::GetSpecification

Returns the object from which the view was created.

HRESULT GetSpecification (
   REFIID            riid,
   IUnknown **      ppObject);

Parameters

riid

[in]
The IID of the interface to be returned. This interface is conceptually added to the list of required interfaces on the resulting rowset, and the method fails (E_NOINTERFACE) if that interface cannot be supported on the resulting rowset.

ppObject

[out]
A pointer to memory in which to return the interface pointer. If GetSpecification fails, it must attempt to set *ppObject to a null pointer.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_NOINTERFACE
The view did not support the interface specified in riid.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to return the object information.

Comments

GetSpecification adds a reference count to the object. The consumer must call Release in order to release the reference count when it is no longer needed.


IViewRowset::OpenViewRowset

Creates a rowset from a view.

HRESULT OpenViewRowset (
   IUnknown *      pUnkOuter,
   REFIID            riid,
   IUnknown **      ppRowset);

Parameters

pUnkOuter

[in]
A pointer to the controlling IUnknown interface if the new rowset is being created as part of an aggregate. It is a null pointer if the rowset is not part of an aggregate.

riid

[in]
The IID of the interface requested on the rowset.

ppRowset

[out]
A pointer to memory in which to return the interface pointer on the newly created rowset. If OpenViewRowset fails, *ppRowset is set to a null pointer.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
ppRowset was a null pointer.

E_NOINTERFACE
The rowset did not support the interface specified in riid.

E_OUTOFMEMORY
The provider did not have enough memory to create the rowset.

DB_E_NOAGGREGATION
pUnkOuter was not a null pointer and the rowset being created does not support aggregation.

pUnkOuter was non-null and riid was not IID_Unknown.

Comments

None.


IViewSort

IViewSort enables the consumer to apply a sort order to a view.

When to Implement

Providers that can present rows in a sorted order as specified by the consumer support IViewSort. A provider may restrict functionality to orders based on existing indexes, or may limit the complexity of the order.

When to Call

Consumers needing a sorted view of rows call IViewSort::SetSort to apply a sort order to a view.

When a view with a specified sort order is applied to a rowset by calling OpenViewChapter, a chapter handle is returned, and methods that return HROWs such as IRowset::GetNextRows or IRowsetScroll::GetRowsAtRatio using that chapter handle return the rows in sorted order. Any resources associated with the order can be freed with a call to IChapteredRowset::ReleaseChapter.

Method | Description
GetSortOrder | Retrieves a sort order from a view.
SetSortOrder | Assigns a sort order to a view.


IViewSort::GetSortOrder

Retrieves the sort order applied to a view.

HRESULT GetSortOrder (
   ULONG *         pcColumns,
   ULONG *         prgColumns[],
   DBSORT *      prgOrders[]);

Parameters

pcColumns

[out]
Count of the number of columns in the sort order.

prgColumns

[out]
The ordinals of the columns used to describe the sort. The order of the columns in the list defines the precedence of the columns in the sort.

prgOrders

[out]
The sort order for the corresponding column in the prgColumns list.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
pcColumns, prgColumns, or prgOrders was NULL.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory in which to return the sort information.

Comments

GetSortOrder returns the latest sort order successfully set on the view.

If GetSortOrder returns an error and pcColumns, prgColumns, and prgOrders are non-null, then *pcColumns, *prgColumns, and *prgOrders are set to NULL, respectively.


IViewSort::SetSortOrder

Specifies a sort order to be applied to a view.

HRESULT SetSortOrder (
   ULONG            cColumns,
   const ULONG      rgColumns[],
   const DBSORT      rgOrders[]);

Parameters

cColumns

[in]
Count of the number of columns to be used in specifying the order, or zero in order to clear any sort order previously set on the view.

rgColumns

[in]
The ordinals of the columns used to describe the sort. The order of the columns in the list defines the precedence of the columns in the sort. This argument is ignored in cColumns is zero.

rgOrders

[in]
The sort order for the corresponding column in the rgColumns list. This argument is ignored in cColumns is zero.

The DBSORT structure is

typedef DWORD DBSORT;

enum DBSORTENUM {
DBSORT_ASCENDING = 0,
DBSORT_DESCENDING
};

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
rgColumns was NULL and cColumns was not equal to zero.

rgOrders was NULL and cColumns was not equal to zero.

DB_E_CANTORDER
The described order could not be opened. The provider may have limitations on the columns used in an order or a limitation on the complexity of the order.

The same column ordinal appeared multiple times in rgColumns.

Comments

A provider may impose constraints on the columns that can be used for sorting. For example, a provider may only support ordering on columns for which there exists an index. If the provider does not support sorting for the specified columns, it returns DB_E_CANTORDER. If there is already a sort order applied to a view, SetSortOrder overrides the previous sort order.

The setting of sort columns is atomic. If any columns cannot be set, then SetSortOrder returns DB_E_CANTORDER and the sort order for the view remains unchanged.

Changing the sort order of a view does not change the sort order of any rowsets or chapters previously opened using that view.


Data Types

This appendix describes data types in OLE DB. For information about how consumers and providers use data types, see Chapter 10, "Data Types in OLE DB."


Type Indicators

A type indicator indicates a data type. For more information, see Chapter 10, "Data Types in OLE DB."

typedef WORD DBTYPE;

enum DBTYPEENUM {
 // The following values exactly match VARENUM
 // in Automation and may be used in VARIANT
 DBTYPE_EMPTY = 0,
 DBTYPE_NULL,
 DBTYPE_I2,
 DBTYPE_I4,
 DBTYPE_R4,
 DBTYPE_R8,
 DBTYPE_CY,
 DBTYPE_DATE,
 DBTYPE_BSTR,
 DBTYPE_IDISPATCH,
 DBTYPE_ERROR,
 DBTYPE_BOOL,
 DBTYPE_VARIANT,
 DBTYPE_IUNKNOWN,
 DBTYPE_DECIMAL,
 DBTYPE_UI1 = 17,
 DBTYPE_ARRAY = 0x2000,
 DBTYPE_BYREF = 0x4000,

 // The following values exactly match VARENUM
 // in Automation but cannot be used in VARIANT
 DBTYPE_I1 = 16,
 DBTYPE_UI2 = 18,
 DBTYPE_UI4,
 DBTYPE_I8,
 DBTYPE_UI8,
 DBTYPE_GUID = 72,
 DBTYPE_VECTOR = 0x1000,
 DBTYPE_FILETIME = 64,
 DBTYPE_RESERVED = 0x8000,

 // The following values are not in VARENUM in OLE
 DBTYPE_BYTES = 128,
 DBTYPE_STR = 129,
 DBTYPE_WSTR,
 DBTYPE_NUMERIC,
 DBTYPE_UDT,
 DBTYPE_DBDATE,
 DBTYPE_DBTIME,
 DBTYPE_DBTIMESTAMP,
DBTYPE_HCHAPTER = 136,
DBTYPE_PROPVARIANT = 138,
DBTYPE_VARNUMERIC = 139
};

The following table describes each type indicator, including the C data type it corresponds to.

Type indicator | Description
DBTYPE_EMPTY | No value was specified. This indicator is valid only in a VARIANT structure and exists only to match the VT_EMPTY value in the VARENUM enumerated type in OLE. It does not correspond to any C data type.
DBTYPE_NULL | A NULL value. This indicator is valid only in a VARIANT structure and exists only to match the VT_NULL value in the VARENUM enumerated type in OLE. It does not correspond to any C data type.
DBTYPE_
RESERVED 		Reserved for future use by OLE. This indicator does not correspond to any C data type.
DBTYPE_I1 A one-byte, signed integer:
signed char
DBTYPE_I2 A two-byte, signed integer:
SHORT
DBTYPE_I4 A four-byte, signed integer:
LONG
DBTYPE_I8 An eight-byte, signed integer:
LARGE_INTEGER
DBTYPE_UI1 A one-byte, unsigned integer:
BYTE
DBTYPE_UI2 A two-byte, unsigned integer:
unsigned short
DBTYPE_UI4 A four-byte, unsigned integer:
unsigned int
DBTYPE_UI8 An eight-byte, unsigned integer:
ULARGE_INTEGER
DBTYPE_R4 A single-precision floating point value:
float
DBTYPE_R8 A double-precision floating point value:
double
DBTYPE_CY A currency value:
LARGE_INTEGER

Currency is a fixed-point number with four digits to the right of the decimal point. It is stored in an eight-byte signed integer, scaled by 10,000.
DBTYPE_
DECIMAL 		An exact numeric value with a fixed precision and fixed scale, stored in the same way as in Automation:
typedef struct tagDEC {
 USHORT wReserved;
 union {
  struct {
   BYTE scale;
   BYTE sign;
  };
  USHORT signscale;
 };
 ULONG Hi32;
 union {
  struct {
   ULONG Lo32;
   ULONG Mid32;
  };
  ULONGLONG Lo64;
 };
} DBTYPE_DECIMAL;
The elements of this structure are used as follows:
  • wReserved—Reserved. Should be 0.

  • scale—The scale specifies the number of digits to the right of the decimal point and ranges from 0 to 28.

  • sign—The sign: 0 if positive, 0x80 if negative.

  • Hi32—The high part of the integer (32-bit aligned).

  • Mid32—The middle part of the integer (32-bit aligned).

  • Lo32—The low part of the integer (32-bit aligned).

For example, to specify the number 12.345, the scale is 3, the sign is 0, and the number stored in the 12-byte integer is 12345.

For information about what precision and scale the provider uses when accessing DBTYPE_DECIMAL structures, see "Conversions Involving DBTYPE_NUMERIC or DBTYPE_DECIMAL" later in this appendix.
DBTYPE_
NUMERIC 		An exact numeric value with a fixed precision and scale:
typedef struct tagDB_NUMERIC {
 BYTE precision;
 BYTE scale;
 BYTE sign;
 BYTE val[16];
} DBTYPE_NUMERIC;

The elements of this structure are used as follows:

  • precision—The maximum number of digits in base 10.

  • scale—The number of digits to the right of the decimal point.

  • sign—The sign: 1 for positive numbers, 0 for negative numbers.

  • val—A number stored as a 16-byte scaled integer, with the least-significant byte on the left.

For example, to specify the base 10 number 20.003 with a scale of 4, the number is scaled to an integer of 200030 (20.003 shifted by four tens digits), which is 30D5E in hexadecimal. The value stored in the 16-byte integer is 5E 0D 03 00 00 00 00 00 00 00 00 00 00 00 00 00, the precision is the maximum precision, the scale is 4, and the sign is 1.

For information about what precision and scale the provider uses when accessing DBTYPE_NUMERIC structures, see "Conversions Involving DBTYPE_NUMERIC or DBTYPE_DECIMAL" later in this appendix.
DBTYPE_DATE A date stored in the same way as in Automation:
DATE

A DATE is a double, the whole part of which is the number of days since December 30, 1899, and the fractional part of which is the fraction of a day. For example, the number 2.25 represents the datetime January 1, 1900 6:00 AM.
DBTYPE_BOOL A Boolean value stored in the same way as in Automation:
VARIANT_BOOL

0 means false and ~0 (bitwise, the value is not 0; that is, all bits are set to 1) means true.
DBTYPE_BYTES A binary data value. That is, an array of bytes:
BYTE[length]

The length of the array is specified by cbMaxLen in the DBBINDING structure if DBTYPE_BYTES is used by itself. It is specified by the bound length value if DBTYPE_BYTES is combined with DBTYPE_BYREF.

For columns containing binary data, the provider reports the data type as DBTYPE_BYTES and the maximum length as the true maximum length of the binary data (assuming there is one). For small, fixed-length binary data, such as a column in an SQL database of type BINARY(1000), the consumer generally binds the column as DBTYPE_BYTES. For small, variable-length binary data, such as a column in an SQL database of type VARBINARY(1000), the consumer generally binds the column as DBTYPE_BYTES | DBTYPE_BYREF, so no space is wasted and no data is truncated. For long binary data, the consumer generally binds the column as DBTYPE_IUNKNOWN and uses a storage interface such as ISequentialStream to manipulate the data. For more information, see "Accessing BLOB Data" in Chapter 7.

When null-terminated string data is converted to or from DBTYPE_BYTES, the null termination character is not included in the length count or the data transferred.
DBTYPE_BSTR A pointer to a BSTR, as in Automation:
typedef WCHAR * BSTR;

A BSTR is a pointer to a null-terminated character string in which the string length is stored with the string. Because the length is stored with the string, BSTR variables can contain embedded null characters. To determine the length of the BSTR in characters, call SysStringLen. To determine the length of the BSTR in bytes, call SysStringByteLen. In both cases, the length does not include the null termination character.

In Win32 (the environment for OLE DB) a BSTR contains Unicode.

Consumers are responsible for freeing the memory used by BSTRs. Failure to free such memory is a common cause of memory leaks. For example, if a consumer calls IRowset::GetData to retrieve a BSTR, the provider allocates memory for the BSTR and returns a pointer to it in the memory pointed to by pData. A memory leak occurs when the consumer calls GetData again with the same value of pData without first freeing the memory for the BSTR. In this case, the provider allocates new memory for the BSTR and overwrites the pointer to the old BSTR with the pointer to the new BSTR. The old BSTR is still allocated, but the pointer to it is lost. For more information, see "Memory Management" in Chapter 6 and the OLE Programmer's Reference, Volume 2.
DBTYPE_STR A null-terminated ANSI character string:
char[length]

If DBTYPE_STR is used by itself, the number of bytes allocated for the string, including the null termination character, is specified by cbMaxLen in the DBBINDING structure. If DBTYPE_STR is combined with DBTYPE_BYREF, the number of bytes allocated for the string, including the null termination character, is at least the length of the string plus one. In either case, the actual length of the string is determined from the bound length value.

If a locale is applicable, it is indicated first by the column metadata, or if none is available then by table metadata, or if none is available then by the database locale.
DBTYPE_WSTR A null-terminated Unicode character string:
wchar_t[length]

If DBTYPE_WSTR is used by itself, the number of bytes allocated for the string, including the null termination character, is specified by cbMaxLen in the DBBINDING structure. If DBTYPE_WSTR is combined with DBTYPE_BYREF, the number of bytes allocated for the string, including the null termination character, is at least the length of the string plus two. In either case, the actual length of the string is determined from the bound length value. Note that the maximum length of the string is the number of allocated bytes divided by sizeof(wchar_t) and truncated to the nearest integer.
DBTYPE_
VARIANT 		An Automation VARIANT:
VARIANT

The DBTYPE values that do not match Automation VARENUM values represent data types that cannot be stored in a VARIANT. If the VARIANT structure contains a pointer to the data, then the buffer for that data is allocated separately.
DBTYPE_
IDISPATCH 		A pointer to an IDispatch interface on an OLE object:
IDispatch *

For more information, see the OLE Programmer's Reference, Volume 2.
DBTYPE_
IUNKNOWN 		A pointer to an IUnknown interface on an OLE object:
IUnknown *

For columns containing OLE objects, the provider reports the data type as DBTYPE_IUNKNOWN. The consumer specifies the interface to use through the pObject element of the DBBINDING structure. For more information, see "IPersist* Objects" in Chapter 7.
DBTYPE_GUID A globally unique identifier (GUID):
GUID

GUIDs are also known as universally unique identifiers (UUIDs) and are used as class identifiers (CLSIDs) and interface identifiers (IIDs). For more information, see the OLE documentation.
DBTYPE_ERROR A 32-bit error code:
SCODE

For more information, see the OLE documentation.
DBTYPE_BYREF A pointer to data:
void *

DBTYPE_BYREF must be combined with another type indicator. This indicates that the data in the consumer's buffer is a pointer to the other type. For example, DBTYPE_STR | DBTYPE_BYREF means that the data is a pointer to an ANSI string and DBTYPE_I2 | DBTYPE_BYREF means that the data is a pointer to a two-byte integer. DBTYPE_BYREF is commonly used to return pointers to variable-length data. It is used with provider-owned bindings to return pointers to the data in the rowset's copy of a row.

When DBTYPE_BYREF is used, the data in the consumer's buffer can be a null pointer only when the corresponding status value is DBSTATUS_S_ISNULL. When setting a column value, if the data in the consumer's buffer is a null pointer and the status is not DBSTATUS_S_ISNULL, the provider returns a conversion error.

DBTYPE_BYREF is mutually exclusive with the DBTYPE_ARRAY and DBTYPE_VECTOR modifiers and cannot be combined with the following values:

DBTYPE_EMPTY
DBTYPE_NULL
DBTYPE_RESERVED

For information about how DBTYPE_BYREF values are allocated, see "Memory Management" in Chapter 6.
DBTYPE_ARRAY A pointer to a SAFEARRAY:
SAFEARRAY *

DBTYPE_ARRAY must be combined with another type indicator. This indicates that the data in the consumer's buffer is a pointer to a SAFEARRAY of the other type. For example, DBTYPE_I2 | DBTYPE_ARRAY means that the data is a pointer to a SAFEARRAY of two-byte integers.

When DBTYPE_ARRAY is used, the data in the consumer's buffer can be a null pointer only when the corresponding status value is DBSTATUS_S_ISNULL. When setting a column value, if the data in the consumer's buffer is a null pointer and the status is not DBSTATUS_S_ISNULL, the provider returns a conversion error.

DBTYPE_ARRAY is mutually exclusive with the DBTYPE_BYREF and DBTYPE_VECTOR modifiers and cannot be combined with indicators for variable-length data types because there is no way to determine the length of each element of the array. DBTYPE_ARRAY can be combined with DBTYPE_BSTR because DBTYPE_BSTR is a fixed-length data type: A BSTR is a pointer to a separately allocated string that contains its own length.

For information about how SAFEARRAYs are allocated, see "Memory Management" in Chapter 6. For more information about SAFEARRAYs, see the OLE documentation.
DBTYPE_
VECTOR 		A DBVECTOR structure:
typedef struct tagDBVECTOR {
 void * ptr;
 ULONG  size;
} DBVECTOR;

DBTYPE_VECTOR must be combined with another type indicator. This indicates that the data in the consumer's buffer is a DBVECTOR structure, which contains a pointer to an array of the other type. For example, DBTYPE_I2 | DBTYPE_VECTOR means that the data is a DBVECTOR structure that contains a pointer to an array of two-byte integers.

When DBTYPE_VECTOR is used, the pointer in the DBVECTOR structure can be a null pointer only when the corresponding status value is DBSTATUS_S_ISNULL. When setting a column value, if the pointer is a null pointer and the status is not DBSTATUS_S_ISNULL, the provider returns a conversion error.

DBTYPE_VECTOR is mutually exclusive with the DBTYPE_BYREF and DBTYPE_ARRAY modifiers and cannot be combined with indicators for variable-length data types because there is no way to determine the length of each element of the array.

For information about how the memory pointed to by DBVECTOR structures is allocated, see "Memory Management" in Chapter 6.
DBTYPE_UDT | A user-defined data type of variable length. In future versions, OLE DB may define the use of type libraries to support user-defined data types. This indicator does not correspond to any C data type.
DBTYPE_
DBDATE 		A date structure:
typedef struct tagDBDATE {
 SHORT  year;
 USHORT month;
 USHORT day;
} DBDATE;

The elements of this structure are used as follows:

  • year—The year: 0-9999 measured from 0 A.D.

  • month—The month: 1 – 12.

  • day—1 – n where n is the number of days in the month.
DBTYPE_
DBTIME 		A time structure:
typedef struct tagDBTIME {
 USHORT hour;
 USHORT minute;
 USHORT second;
} DBTIME;

The elements of this structure are used as follows:

  • hour—The hour: 0–23.

  • minute—The minute: 0–59.

  • second—The second: 0–61.

The range of seconds allows as many as two leap seconds to maintain synchronization with sidereal time.
DBTYPE_
DBTIMESTAMP 		A timestamp structure:
typedef struct tagDBTIMESTAMP {
 SHORT  year;
 USHORT month;
 USHORT day;
 USHORT hour;
 USHORT minute;
 USHORT second;
 ULONG  fraction;
} DBTIMESTAMP;
The elements of this structure are used as follows:
  • year—The year: 0-9999 measured from 0 A.D.

  • month—The month: 1–12.

  • day—1 – n where n is the number of days in the month.

  • hour—The hour: 0–23.

  • minute—The minute: 0–59.

  • second—The second: 0–61.

  • fraction—Billionths of a second: 0–999,999,999.

The range of seconds allows as many as two leap seconds to maintain synchronization with sidereal time.
DBTYPE_FILETIME A 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601. This type is equivalent to the FILETIME type used in the time-conversion functions between DOS and Win32.
typedef struct tagFILETIME {
 DWORD  dwLowDateTime;
 DWORD  dwHighDateTime;
} FILETIME;

The elements of the structure are used as follows:

  • dwLowDateTime—The lower 32 bits of the Win32 date/time value.

  • dwHighDateTime—The upper 32 bits of the Win32 date/time value.
DBTYPE_PROPVARIANT An Automation PROPVARIANT:
PROPVARIANT

If the VARIANT structure contains a pointer to the data, then the buffer for the data is allocated separately.

DBTYPE_PROPVARIANT should support the same set of conversions as DBTYPE_VARIANT (including conversions to/from DBTYPE_VARIANT).
DBTYPE_HCHAPTER | A 4-byte chapter value that can be used to identify rows in a child rowset.
DBTYPE_VARNUMERIC A variable-length, exact numeric value with a signed scale value:
typedef struct  tagDB_VARNUMERIC {
 BYTE    precision;
 SBYTE    scale;
 BYTE    sign;
 BYTE    val[ 1 ];
} DB_VARNUMERIC;


Precision of Numeric Data Types

The following OLE DB data types are used to represent numeric values. Each type has a corresponding maximum precision value that indicates the maximum number of base 10 digits that it can store. The radix of the precision for all numeric types is 10. The maximum precision returned by the provider in the COLUMN_SIZE column of the PROVIDER_TYPES schema rowset is the maximum possible precision for the data type that is supported by the data source. For example, if a data source can only support a maximum precision of 28 for a DBTYPE_NUMERIC column, the provider returns 28, not 39, as the maximum precision.

Type indicator | Maximum precision
DBTYPE_I1 | 3
DBTYPE_I2 | 5
DBTYPE_I4 | 10
DBTYPE_I8 | 19
DBTYPE_UI1 | 3
DBTYPE_UI2 | 5
DBTYPE_UI4 | 10
DBTYPE_UI8 | 20
DBTYPE_R4 | 7
DBTYPE_R8 | 15
DBTYPE_CY | 19
DBTYPE_DECIMAL | 29
DBTYPE_NUMERIC | 39
DBTYPE_VARNUMERIC | 255


Fixed-Length Data Types

The following type indicators are for fixed-length data types. For more information, see "Fixed- and Variable-Length Data Types" in Chapter 10.

DBTYPE_I1 | DBTYPE_IDISPATCH
DBTYPE_I2 | DBTYPE_ERROR
DBTYPE_I4 | DBTYPE_BOOL
DBTYPE_I8 | DBTYPE_VARIANT
DBTYPE_UI1 | DBTYPE_IUNKNOWN
DBTYPE_UI2 | DBTYPE_GUID
DBTYPE_UI4 | DBTYPE_NUMERIC
DBTYPE_UI8 | DBTYPE_DECIMAL
DBTYPE_R4 | DBTYPE_DBDATE
DBTYPE_R8 | DBTYPE_DBTIME
DBTYPE_CY | DBTYPE_DBTIMESTAMP
DBTYPE_DATE | DBTYPE_FILETIME
DBTYPE_BSTR | DBTYPE_HCHAPTER
DBTYPE_PROPVARIANT |


Variable-Length Data Types

The following type indicators are for variable-length data types. For more information, see "Fixed- and Variable-Length Data Types" in Chapter 10.

DBTYPE_STR
DBTYPE_BYTES
DBTYPE_WSTR
DBTYPE_VARNUMERIC


Data Type Conversion Rules

For information about the conversions providers are required to support and how consumers determine what these conversions are, see "Data Type Conversion" in Chapter 10. Providers that support optional conversions should follow the guidelines listed in this section.


Conversion Tables

The tables in this section describe most additional conversions. They use the following symbols:

  • S = A safe conversion is possible.

  • T = Conversion with truncation of nonsignificant digits is possible.

  • E = An error due to truncation of significant digits is possible.

  • – = The conversion is not generally supported.
To From | I1 | I2 | I4 | I8 | UI1
I1 | S | S | S | S | S, E
I2 | S, E | S | S | S | S, E
I4 | S, E | S, E | S | S | S, E
I8 | - | - | - | S | -
UI1 | S, E | S | S | S | S
UI2 | S, E | S, E | S | S | S, E
UI4 | S, E | S, E | S, E | S | S, E
UI8 | - | - | - | S, E | -
R4 | S, T, E | S, T, E | S, T | S, T | S, T, E
R8 | S, T, E | S, T, E | S, T, E | S, T | S, T, E
CY | S, T, E | S, T, E | S, T, E | S, T | S, T, E
DEC | S, T, E | S, T, E | S, T, E | S, T, E | S, T, E
NUM | S, T, E | S, T, E | S, T, E | S, T, E | S, T, E
BOOL | S | S | S | S | S
DATE | S | S | S | S | S
DBDATE | – | – | – | – | –
DBTIME | – | – | – | – | –
DBTIMESTAMP | – | – | – | – | –
FILETIME | – | – | – | S, E | –
BYTES | S, E | S, E | S, E | S, E | S, E
BSTR | S, T, E | S, T, E | S, T, E | S, T, E | S, T, E
STR | S, T, E | S, T, E | S, T, E | S, T, E | S, T, E
WSTR | S, T, E | S, T, E | S, T, E | S, T, E | S, T, E
VARIANT | S, T, E | S | S | S, T, E | S, T, E
DISP | – | – | – | – | –
UNK | – | – | – | – | –
GUID | – | – | – | – | –
ERROR | – | – | – | – | –
PROPVARIANT | S, T, E | S | S | S | S

To From | UI2 | UI4 | UI8 | R4 | R8
I1 | S, E | S, E | S, E | S | S
I2 | S, E | S, E | S, E | S | S
I4 | S, E | S, E | S, E | S | S
I8 | - | - | S, E | - | -
UI1 | S | S | S | S | S
UI2 | S | S | S | S | S
UI4 | S, E | S | S | S, E | S
UI8 | - | - | S | - | -
R4 | S, T, E | S, T, E | S, T, E | S | S
R8 | S, T, E | S, T, E | S, T, E | S, E | S
CY | S, T, E | S, T, E | S, T, E | S, E | S, E
DEC | S, T, E | S, T, E | S, T, E | S, E | S, E
NUM | S, T, E | S, T, E | S, T, E | S, E | S, E
BOOL | S | S | S | S | S
DATE | S | S | S | S | S
DBDATE | – | – | – | – | –
DBTIME | – | – | – | – | –
DBTIMESTAMP | – | – | – | – | –
FILETIME | – | – | S, E | – | –
BYTES | S, E | S, E | S, E | S, E | S, E
BSTR | S, T, E | S, T, E | S, T, E | S, T, E | S, T, E
STR | S, T, E | S, T, E | S, T, E | S, T, E | S, T, E
WSTR | S, T, E | S, T, E | S, T, E | S, T, E | S, T, E
VARIANT | S, T, E | S, T, E | S, T, E | S | S
DISP | – | – | – | – | –
UNK | – | – | – | – | –
GUID | – | – | – | – | –
ERROR | – | – | – | – | –
PROPVARIANT | S | S | S | S | S

To From | CY | DEC | NUM | BOOL | DATE
I1 | S | S | S | S | S
I2 | S | S | S | S | S
I4 | S | S | S | S | S
I8 | - | - | - | - | -
UI1 | S | S | S | S | S
UI2 | S | S | S | S | S
UI4 | S | S | S | S | S
UI8 | - | - | - | - | -
R4 | S, T | S | S | S | S
R8 | S, T | S | S | S | S
CY | S | S | S | S | -
DEC | S, E | S | S | S | -
NUM | S, E | S, T, E | S | S | -
BOOL | S | S | S | S | S
DATE | S | S | S | S | S
DBDATE | – | – | – | – | S
DBTIME | – | – | – | – | S
DBTIMESTAMP | – | – | – | – | S, T
FILETIME | – | – | – | – | S, T
BYTES | S, E | S, E | S, E | S, E | -
BSTR | S, T, E | S, T, E | S, T, E | S, E | S, T, E
STR | S, T, E | S, T, E | S, T, E | S, E | S, T, E
WSTR | S, T, E | S, T, E | S, T, E | S, E | S, T, E
VARIANT | S | S | S, T, E | S | S
DISP | – | – | – | – | –
UNK | – | – | – | – | –
GUID | – | – | – | – | –
ERROR | – | – | – | – | –
PROPVARIANT | S | S | S, T, E | S | S

To From | DBDATE | DBTIME | DBTIME STAMP | FILE TIME | BYTES
I1 | – | – | – | – | -
I2 | – | – | – | – | S, E
I4 | – | – | – | – | S, E
I8 | – | – | – | S, E | -
UI1 | – | – | – | – | -
UI2 | – | – | – | – | S, E
UI4 | – | – | – | – | S, E
UI8 | – | – | – | S, E | -
R4 | – | – | – | – | S, E
R8 | – | – | – | – | S, E
CY | – | – | – | – | S, E
DEC | – | – | – | – | S, E
NUM | – | – | – | – | S, E
BOOL | – | – | – | – | S
DATE | S, T, E | S, T | S, T, E | S, T, E | S, E
DBDATE | S | S | S | S | S, E
DBTIME | S | S | S | S | S, E
DBTIMESTAMP | S, T | S, T | S | S, T | S, E
FILETIME | S, T, E | S, T, E | S, T, E | S | S, E
BYTES | S, E | – | S | S, E | S, E
BSTR | S, T, E | S, T, E | S, T, E | S, T, E | S, E
STR | S, T, E | S, T, E | S, T, E | S, T, E | S, E
WSTR | S, T, E | S, T, E | S, T, E | S, T, E | S, E
VARIANT | S, T, E | S, T, E | S, T, E | S, T, E | S, T, E
DISP | – | – | – | – | S, E
UNK | – | – | – | – | S, E
GUID | – | – | – | – | -
ERROR | – | – | – | – | –
PROPVARIANT | S, T, E | S, T, E | S, T, E | S, T, E | S

To From | BSTR | STR | WSTR | VARIANT | DISP
I1 | S, E | S, E | S, E | S | –
I2 | S, E | S, E | S, E | S | –
I4 | S, E | S, E | S, E | S | –
I8 | S, E | S, E | S, E | S | –
UI1 | S, E | S, E | S, E | S | –
UI2 | S, E | S, E | S, E | – | –
UI4 | S, E | S, E | S, E | S | –
UI8 | S, E | S, E | S, E | S | –
R4 | S, E | S, T, E | S, T, E | S | –
R8 | S, E | S, T, E | S, T, E | S | –
CY | S, E | S, T, E | S, T, E | S | –
DEC | S, E | S, T, E | S, T, E | S | –
NUM | S, E | S, T, E | S, T, E | – | –
BOOL | S, E | S, E | S, E | S | –
DATE | S, E | S, T, E | S, T, E | S | –
DBDATE | S, E | S, T, E | S, T, E | S | –
DBTIME | S, E | S, T, E | S, T, E | S | –
DBTIMESTAMP | S, E | S, T, E | S, T, E | S | –
FILETIME | S, E | S, T, E | S, T, E | S | –
BYTES | S, E | S, E | S, E | – | S, E
BSTR | S | S, T | S, T | S |
STR | S | S, T | S, T | S | –
WSTR | S | S, T | S, T | S | –
VARIANT | S, T, E | S, T, E | S, T, E | S | S
DISP | – | – | – | S | S
UNK | – | – | – | S | –
GUID | S, E | S, E | S, E | S | –
ERROR | – | – | – | S | –
PROPVARIANT | S, T, E | S, T, E | S, T, E | S | S

To From | UNK | GUID | ERROR | PROPVARIANT
I1 | – | – | – | -
I2 | – | – | – | S
I4 | – | – | – | S
I8 | – | – | – | S
UI1 | – | – | – | S
UI2 | – | – | – | S
UI4 | – | – | – | S
UI8 | – | – | – | S
R4 | – | – | – | S
R8 | – | – | – | S
CY | – | – | – | S
DEC | – | – | – | -
NUM | – | – | – | -
BOOL | – | – | – | S
DATE | – | – | – | S
DBDATE | – | – | – | -
DBTIME | – | – | – | -
DBTIMESTAMP | – | – | – | -
FILETIME | – | – | – | S
BYTES | S, E | S, E | – | S
BSTR | – | S, E | – | S
STR | S, E | S, E | – | S
WSTR | S, E | S, E | – | S
VARIANT | S | S, T, E | S | S
DISP | S | – | – | S
UNK | S | – | – | -
GUID | – | S | – | S
ERROR | – | – | S | -
PROPVARIANT | S | S | S | S


Conversions Involving Strings

The following formats are used for conversions to and from strings. Only nontrivial formats are listed. Converting a string to a nonstring type may fail for some providers if the string contains trailing blanks.

Type indicator | Format
DBTYPE_BYTES | When strings are converted to and from binary data, each byte of binary data is represented as two ASCII characters. These characters are the ASCII character representation of the number in its hexadecimal form. For example, a binary 00000001 is converted to "01" and a binary 11111111 is converted to "FF".
DBTYPE_CY | "...cccc.cccc"
DBTYPE_BOOL | "True" and "False"
DBTYPE_DATE As in Automation, according to the regional settings of the machine. For example, for the "English(United States)" default setting, this would typically be:

"mm/dd/yy hh:mm:ss {AM|PM}"
DBTYPE_DBDATE As specified by ISO:

"yyyy-mm-dd"
DBTYPE_FILETIME | "yyyyy-mm-dd hh:mm:ss.fff" where fff is from 0-999. Up to 3 digits may be used. The format is the same as SYSTEMTIME. Valid filetime value is 0x0000000000000000 - 0x7FFFFFFFFFFFFFFF, which is 01601-01-01 00:00:000 - 30828-09-14 02:24:05.477.
DBTYPE_DBTIME As specified by ISO:

"hh:mm:ss"
DBTYPE_
DBTIMESTAMP 		As specified by ISO:

"yyyy-mm-dd hh:mm:ss.f..."

where "f..." is fractions of a second (up to nine digits may be used).
DBTYPE_GUID As represented in the registry:

"{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"

where x is a hexadecimal digit.
DBTYPE_STR
DBTYPE_WSTR
DBTYPE_BSTR 		Conversion between strings of different locale IDs (LCIDs) is not supported. Conversion between ANSI and Unicode strings of the same LCID are required.

In converting a Unicode string to ANSI, information may be lost because the Unicode range is greater. Lost characters are represented in the ANSI string by a special character and the conversion is considered to be a truncation.

When mixing character sets, conversion to Unicode is the preferred solution. If conversion to ANSI is needed to use ANSI strings with the user interface, and the LCID of the user interface is different from the LCID of some columns, then the consumer is responsible for the conversion. The LCID of a column is returned in the DBCOLUMN_COLLATING_SEQUENCE column returned by IColumnsRowset::GetColumnsRowset.


Conversions Involving Numeric Values

Conversions involving numeric values use the following general guidelines:

  • Except for the most significant digit, all digits in floating point numbers are considered to be less significant. Thus, floating point numbers can always be converted (possibly with truncation) to integers, numerics, or strings as long as they don't overflow the integer, numeric, or string.

    For example, 1.234567E2 is truncated to 123 when converted to an integer, to 123.4 when converted to a NUMERIC(4, 1), to 120 when converted to a NUMERIC(4, – 1), assuming the provider supports negative scale (many do not), to "123\0" when converted to a four-byte ANSI string, and to "123.4\0" when converted to a six byte ANSI string. It cannot be converted to an ANSI string of three or fewer bytes because such a conversion would overflow the string.

  • In DBTYPE_NUMERIC and DBTYPE_DECIMAL, digits to the right of the decimal point are considered less significant and can be truncated; digits to the left of the decimal point are considered significant and cannot be truncated. For example, 123.4567 is truncated to 123 when converted to an integer and to "123.4\0" when converted to a six-byte ANSI string.

  • In strings that represent numbers, digits to the right of the decimal point are considered less significant and can be truncated; digits to the left of the decimal point are considered significant and cannot be truncated. For example, "123.45\0" is truncated to 123 when converted to an integer and 123.4 when converted to a NUMERIC(4, 1). "1234.5\0" cannot be converted to a one-byte integer.

  • It is provider-specific whether negative numbers are truncated up or down.
    Thus, – 1.99 might be truncated to – 1 or to – 2 when converted to an integer.


Conversions Involving DBTYPE_NUMERIC or DBTYPE_DECIMAL

When retrieving data from a DBTYPE_NUMERIC or DBTYPE_DECIMAL structure, the provider uses the maximum precision and scale stored in that structure. When storing data in a DBTYPE_NUMERIC or DBTYPE_DECIMAL structure, the provider uses the maximum precision and scale in the metadata for that structure. For consumer structures, these are in the binding structure. For provider structures, these are in the DBCOLUMNINFO structure returned by IColumnsInfo::GetColumnInfo or the DBPARAMINFO structure returned by ICommandWithParameters::GetParameterInfo.

The following table shows the maximum precision and scale used by the provider when accessing data in a DBTYPE_NUMERIC or DBTYPE_DECIMAL structure in the consumer's buffer.

Consumer structure | Getting or setting data | Maximum precision | Scale
DBTYPE_NUMERIC | getting | precision element of DBTYPE_NUMERIC structure | scale element of DBTYPE_NUMERIC structure
DBTYPE_NUMERIC | setting | bPrecision element of DBBINDING structure | bScale element of DBBINDING structure
DBTYPE_VARNUMERIC | getting | precision element of DB_VARNUMERIC structure | scale element of DB_VARNUMERIC structure
DBTYPE_VARNUMERIC | setting | bPrecision element of DBBINDING structure | bScale element of DBBINDING structure
DBTYPE_DECIMAL | getting | 28 | scale element of DBTYPE_DECIMAL structure
DBTYPE_DECIMAL | setting | 28 | bScale element of DBBINDING structure

The following table shows the maximum precision and scale used by the provider when accessing data in a DBTYPE_NUMERIC or DBTYPE_DECIMAL structure in the provider.

Provider structure | Getting or setting data | Maximum precision | Scale
DBTYPE_NUMERIC | getting | precision element of DBTYPE_NUMERIC structure | scale element of DBTYPE_NUMERIC structure
DBTYPE_NUMERIC | setting | bPrecision element of DBCOLUMNINFO or DBPARAMINFO structure | bScale element of DBCOLUMNINFO or DBPARAMINFO structure
DBTYPE_VARNUMERIC | getting | precision element of DB_VARNUMERIC structure | scale element of DB_VARNUMERIC structure
DBTYPE_VARNUMERIC | setting | bPrecision element of DBCOLUMNINFO or DBPARAMINFO structure | bScale element of DBCOLUMNINFO or DBPARAMINFO structure
DBTYPE_DECIMAL | getting | 28 | scale element of DBTYPE_DECIMAL structure
DBTYPE_DECIMAL | setting | 28 | bScale element of DBCOLUMNINFO or DBPARAMINFO structure


Conversions Involving VARIANTs

The following table describes conversions to and from VARIANTs. Type indicator X can be modified by DBTYPE_ARRAY or DBTYPE_BYREF. No conversions are supported when it is modified by DBTYPE_VECTOR.

Source type indicator | Target type indicator | Notes
X | DBTYPE_VARIANT | If X is a valid VARENUM value (that is, it is a type indicator that can be used in a VARIANT), the provider sets vt in the target VARIANT to X and transfers the data to the corresponding element of the union. If X is not a valid VARENUM value, it is provider-specific whether the provider supports the conversion and what type it uses in the VARIANT. Any such conversions must follow the rules in this appendix.
DBTYPE_VARIANT | X | If the type indicator specified in the source VARIANT is X, the provider transfers the data. If the type indicator specified in the VARIANT is not X, it is provider-specific whether the provider supports the conversion. Any such conversions must follow the rules in this appendix.
DBTYPE_VARIANT | DBTYPE_VARIANT | The provider sets vt in the target VARIANT to the same value as vt in the source VARIANT. It transfers the data from the union element in the source VARIANT to the same element in the target VARIANT.


Conversions Involving DBTYPE_BYREF

The following table describes conversions to and from types modified by DBTYPE_BYREF. Type indicators X and Y are unmodified by DBTYPE_BYREF.

Source type indicator | Target type indicator | Notes
X Y | DBTYPE_BYREF If Y is a variable-length data type and the provider supports a conversion from X to Y, the provider must support this conversion.

If Y is not a variable-length data type, it is provider-specific whether this conversion is supported. Any such conversions must follow the rules in this appendix.

For information about further restrictions when memory is provider-owned, see the description of dwMemOwner in "DBBINDING Structures" in Chapter 6.
X | DBTYPE_BYREF | Y | If X equals Y, the provider must support this conversion.
If X is not equal to Y, the provider supports this if and only if it supports a conversion from X to Y. Any such conversions must follow the rules in this appendix.
X | DBTYPE_BYREF | Y | DBTYPE_BYREF | The provider must support this conversion if it supports a conversion from X to Y | DBTYPE_BYREF. Any such conversions must follow the rules in this appendix.


Conversions Involving DBTYPE_ARRAY

The following table describes conversions to and from types modified by DBTYPE_ARRAY. Type indicators X and Y are unmodified; when combined with DBTYPE_ARRAY, they cannot be DBTYPE_EMPTY or DBTYPE_NULL.

Source type indicator | Target type indicator | Notes
X | Y | DBTYPE_ARRAY | It is provider-specific whether this is supported.
X | DBTYPE_ARRAY | Y | It is provider-specific whether this is supported.
X | DBTYPE_ARRAY | Y | DBTYPE_ARRAY | If the provider supports a conversion from X to Y, and the provider supports both X | DBTYPE_ARRAY and Y | DBTYPE_ARRAY, it must support this conversion. Any such conversions must follow the rules in this appendix.
X | DBTYPE_VECTOR | Y | DBTYPE_ARRAY | Not supported.
X | DBTYPE_ARRAY | Y | DBTYPE_VECTOR | Not supported.


Conversions Involving DBTYPE_VECTOR

The following table describes conversions to and from types modified by DBTYPE_VECTOR. Type indicators X and Y are unmodified.

Source type indicator | Target type indicator | Notes
X | Y | DBTYPE_VECTOR | It is provider-specific whether this is supported.
X | DBTYPE_VECTOR | Y | It is provider-specific whether this is supported.
X | DBTYPE_VECTOR | Y | DBTYPE_VECTOR | If the provider supports a conversion from X to Y, and the provider supports both X | DBTYPE_VECTOR and Y | DBTYPE_VECTOR, it must support this conversion. Any such conversions must follow the rules in this appendix.
X | DBTYPE_ARRAY | Y | DBTYPE_VECTOR | Not supported.
X | DBTYPE_VECTOR | Y | DBTYPE_ARRAY | Not supported.


Schema Rowsets

The schema information specified in OLE DB is based on the assumption that providers support the concepts of a catalog and a schema. The ANSI SQL92 specification defines them as follows:

  • A catalog contains one or more schemas, but always contains a schema named INFORMATION_SCHEMA, which contains the views and domains of the information schema. In Microsoft SQL Server and Microsoft Access terms, a catalog is a database; in ODBC 2.x terms, a catalog is a qualifier.

  • A schema is a collection of database objects that are owned or have been created by a particular user. In Microsoft SQL Server and ODBC 2.x terms, a schema is an owner; there is no equivalent to a schema in a Microsoft Access database.

Schema information in OLE DB is retrieved using predefined schema rowsets; this appendix lists the contents of each schema rowset.

Schema rowsets are retrieved with IDBSchemaRowset::GetRowset. GetRowset allows consumers to specify simple restrictions, such as returning all the columns in a particular table. Each schema rowset is guaranteed to return all of the columns in the order shown in the section detailing that rowset. In addition, providers can return provider-specific columns after the last column defined by OLE DB.

In implementing IDBSchemaRowset, it is suggested that providers pay particular attention to rowset construction and data retrieval performance, because these commands will be frequently used by consumers.

This information schema also defines a minimum content of the system schema. It is assumed that consumers will prepare and store the specialized commands used for things such as command planning and execution-time plan validation, and storage engines may have specialized optimizations associated with those plans. Consumers can also look at the schema tables in the schema, and thus discover other schema tables and attributes beyond the minimum set.

Note If a provider returns non-NULL CATALOG or SCHEMA column values in schema rowsets, then it must support IDBInfo to describe how a fully qualified name is assembled. A provider that does not support IDBInfo must return NULL values for CATALOG and SCHEMA columns in all schema rowsets.

For schema rowset columns of type DBTYPE_WSTR, the provider returns, as the column size in IColumnsInfo or IColumnsRowset, the maximum number of characters that can appear in this column of the Schema Rowset. The provider guarantees that no value in the column will exceed this length, but does not guarantee that an element of the column could actually contain that many characters. If the provider always returns NULL for the column, it may return zero as the maximum length of the column in IColumnsInfo and IColumnsRowset.


Logical Information Schema Rowsets

This section details each schema rowset in the logical information schema. These schema rowsets are primarily concerned with the logical database metadata and are a superset of the information presented by the INFORMATION_SCHEMA views defined in ANSI SQL92, with the exception of the COLUMNS and DOMAINS rowsets, which contain subsets of this information. The INFORMATION_SCHEMA schema exists in all ANSI SQL92 catalogs.

Although the ANSI SQL92 views define some columns as NOT NULLABLE, OLE DB does not enforce this restriction. Therefore, consumers should check the metadata for a particular column with IColumnsInfo::GetColumnInfo or IColumnsRowset::GetColumnRowset to determine if it is nullable.

The following rowsets are in the logical information schema. All providers that support IDBSchemaRowset must support the TABLES and COLUMNS schema rowsets. Support for other schema rowsets in the logical information schema is optional.

ASSERTIONS | PROCEDURE_PARAMETERS
CHARACTER_SETS | PROCEDURES
CHECK_CONSTRAINTS | REFERENTIAL_CONSTRAINTS
COLLATIONS | SCHEMATA
COLUMN_DOMAIN_USAGE | SQL_LANGUAGES
COLUMN_PRIVILEGES | TABLE_CONSTRAINTS
COLUMNS | TABLE_PRIVILEGES
CONSTRAINT_COLUMN_USAGE | TABLES
CONSTRAINT_TABLE_USAGE | TABLES_INFO
FOREIGN_KEYS | TRANSLATIONS
KEY_COLUMN_USAGE | USAGE_PRIVILEGES
PRIMARY_KEYS | VIEW_COLUMN_USAGE
PROCEDURE_COLUMNS | VIEW_TABLE_USAGE
 | VIEWS


Physical Information Schema Rowsets

This section details the contents of each schema rowset in the physical information schema. These schema rowsets are primarily concerned with the physical database metadata. The contents of such metadata are not defined in ANSI SQL92; all definitions in this section are OLE DB extensions to ANSI SQL92.

The following rowsets are in the physical information schema. All providers that support IDBSchemaRowset must support the PROVIDER_TYPES schema rowset. Support for other schema rowsets in the physical information schema is optional.

CATALOGS | PROVIDER_TYPES
INDEXES | STATISTICS


ASSERTIONS Rowset

The ASSERTIONS rowset identifies the assertions defined in the catalog that are owned by a given user.

The ASSERTIONS rowset contains the following columns.

Column name | Type indicator | Description
CONSTRAINT_CATALOG | DBTYPE_WSTR | Catalog name. NULL if the provider does not support catalogs.
CONSTRAINT_SCHEMA | DBTYPE_WSTR | Unqualified schema name. NULL if the provider does not support schemas.
CONSTRAINT_NAME | DBTYPE_WSTR | Constraint name.
IS_DEFERRABLE | DBTYPE_BOOL | VARIANT_TRUE—The assertion is deferrable. VARIANT_FALSE—The assertion is not deferrable.
INITIALLY_DEFERRED | DBTYPE_BOOL | VARIANT_TRUE—The assertion is initially deferred. VARIANT_FALSE—The assertion is initially immediate.
DESCRIPTION | DBTYPE_WSTR | Human-readable description of the column.

Default Sort Order: CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME


CATALOGS Rowset

The CATALOGS rowset identifies the physical attributes associated with catalogs accessible from the DBMS. For some systems, such as Microsoft Access, there may be only one catalog. For Microsoft SQL Server, this rowset would enumerate all catalogs (databases) defined in the system database.

The CATALOGS rowset contains the following columns.

Column name | Type indicator | Description
CATALOG_NAME | DBTYPE_WSTR | Catalog name.
DESCRIPTION | DBTYPE_WSTR | Human-readable description of the table.

Default Sort Order: CATALOG_NAME


CHARACTER_SETS Rowset

The CHARACTER_SETS rowset identifies the character sets defined in the catalog that are accessible to a given user.

The CHARACTER_SETS rowset contains the following columns.

Column name | Type indicator | Description
CHARACTER_SET_ CATALOG | DBTYPE_WSTR | Catalog name. NULL if the provider does not support catalogs.
CHARACTER_SET_ SCHEMA | DBTYPE_WSTR | Unqualified schema name. NULL if the provider does not support schemas.
CHARACTER_SET_NAME | DBTYPE_WSTR | Character set name.
FORM_OF_USE | DBTYPE_WSTR | Name of form-of-use of the character set.
NUMBER_OF_ CHARACTERS | DBTYPE_I8 | Number of characters in the character repertoire.
DEFAULT_COLLATE_ CATALOG | DBTYPE_WSTR | Catalog name containing the default collation. NULL if the provider does not support catalogs or different collations.
DEFAULT_COLLATE_ SCHEMA | DBTYPE_WSTR | Unqualified schema name containing the default collation. NULL if the provider does not support schemas or different collations.
DEFAULT_COLLATE_ NAME | DBTYPE_WSTR | Default collation. NULL if the provider does not support different collations.

Default Sort Order: CHARACTER_SET_CATALOG, CHARACTER_SET_SCHEMA, CHARACTER_SET_NAME


CHECK_CONSTRAINTS Rowset

The CHECK_CONSTRAINTS rowset identifies the check constraints defined in the catalog that are owned by a given user.

The CHECK_CONSTRAINTS rowset contains the following columns.

Column name | Type indicator | Description
CONSTRAINT_ CATALOG | DBTYPE_WSTR | Catalog name. NULL if the provider does not support catalogs.
CONSTRAINT_SCHEMA | DBTYPE_WSTR | Unqualified schema name. NULL if the provider does not support schemas.
CONSTRAINT_NAME | DBTYPE_WSTR | Constraint name.
CHECK_CLAUSE | DBTYPE_WSTR | The WHERE clause specified in the CHECK clause.
DESCRIPTION | DBTYPE_WSTR | Human-readable description of the column.

Default Sort Order: CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME


COLLATIONS Rowset

The COLLATIONS rowset identifies the character collations defined in the catalog that are accessible to a given user.

The COLLATIONS rowset contains the following columns.

Column name | Type indicator | Description
COLLATION_CATALOG | DBTYPE_WSTR | Catalog name. NULL if the provider does not support catalogs.
COLLATION_SCHEMA | DBTYPE_WSTR | Unqualified schema name. NULL if the provider does not support schemas.
COLLATION_NAME | DBTYPE_WSTR | Collation name.
CHARACTER_SET_ CATALOG | DBTYPE_WSTR | Catalog name containing the character set on which the collation is defined. NULL if the provider does not support catalogs or different character sets.
CHARACTER_SET_ SCHEMA | DBTYPE_WSTR | Unqualified schema name containing the character set on which the collation is defined. NULL if the provider does not support schema or different character sets.
CHARACTER_SET_NAME | DBTYPE_WSTR | Character set name on which the collation is defined. NULL if the provider does not support different character sets.
PAD_ATTRIBUTE | DBTYPE_WSTR | "NO PAD"—The collation being described has the NO PAD attribute. "PAD SPACE"—The collation being described has the PAD SPACE attribute.

Default Sort Order: COLLATION_CATALOG, COLLATION_SCHEMA, COLLATION_CATALOG, CHARACTER_SET_CATALOG, CHARACTER_SET_SCHEMA, CHARACTER_SET_NAME


COLUMN_DOMAIN_USAGE Rowset

The COLUMN_DOMAIN_USAGE rowset identifies the columns defined in the catalog that are dependent on a domain defined in the catalog and owned by a given user.

The COLUMN_DOMAIN_USAGE rowset contains the following columns.

Column name | Type indicator | Description
DOMAIN_CATALOG | DBTYPE_WSTR | Catalog name. NULL if the provider does not support catalogs.
DOMAIN_SCHEMA | DBTYPE_WSTR | Unqualified schema name. NULL if the provider does not support schemas.
DOMAIN_NAME | DBTYPE_WSTR | View name.
TABLE_CATALOG | DBTYPE_WSTR | Catalog name in which the table is defined. NULL if the provider does not support catalogs.
TABLE_SCHEMA | DBTYPE_WSTR | Unqualified schema name in which the table is defined. NULL if the provider does not support schemas.
TABLE_NAME | DBTYPE_WSTR | Table name.
COLUMN_NAME | DBTYPE_WSTR | Column name. This column, together with the COLUMN_GUID and COLUMN_PROPID columns, forms the column ID. One or more of these columns will be NULL depending on which elements of the DBID structure the provider uses.
COLUMN_GUID | DBTYPE_GUID | Column GUID.
COLUMN_PROPID | DBTYPE_UI4 | Column property ID.

Default Sort Order: DOMAIN_CATALOG, DOMAIN_SCHEMA, DOMAIN_NAME, TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_GUID, COLUMN_PROPID


COLUMN_PRIVILEGES Rowset

The COLUMN_PRIVILEGES rowset identifies the privileges on columns of tables defined in the catalog that are available to or granted by a given user.

The COLUMN_PRIVILEGES rowset contains the following columns.

Column name | Type indicator | Description
GRANTOR | DBTYPE_WSTR | User who granted the privileges on the table in TABLE_NAME.
GRANTEE | DBTYPE_WSTR | User name (or "PUBLIC") to whom the privilege has been granted.
TABLE_CATALOG | DBTYPE_WSTR | Catalog name in which the table is defined. NULL if the provider does not support catalogs.
TABLE_SCHEMA | DBTYPE_WSTR | Unqualified schema name in which the table is defined. NULL if the provider does not support schemas.
TABLE_NAME | DBTYPE_WSTR | Table name.
COLUMN_NAME | DBTYPE_WSTR | Column name. This column, together with the COLUMN_GUID and COLUMN_PROPID columns, forms the column ID. One or more of these columns will be NULL depending on which elements of the DBID structure the provider uses.
COLUMN_GUID | DBTYPE_GUID | Column GUID.
COLUMN_PROPID | DBTYPE_UI4 | Column property ID.
PRIVILEGE_TYPE | DBTYPE_WSTR | Privilege type. One of the following: "SELECT" "DELETE" "INSERT" "UPDATE" "REFERENCES"
IS_GRANTABLE | DBTYPE_BOOL | VARIANT_TRUE—The privilege being described was granted with the WITH GRANT OPTION clause. VARIANT_FALSE—The privilege being described was not granted with the WITH GRANT OPTION clause.

Default Sort Order: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_GUID, COLUMN_PROPID, PRIVILEGE_TYPE


COLUMNS Rowset

The COLUMNS rowset identifies the columns of tables defined in the catalog that are accessible to a given user.

The COLUMNS rowset contains the following columns.

Column name | Type indicator | Description
TABLE_CATALOG | DBTYPE_WSTR | Catalog name. NULL if the provider does not support catalogs.
TABLE_SCHEMA | DBTYPE_WSTR | Unqualified schema name. NULL if the provider does not support schemas.
TABLE_NAME | DBTYPE_WSTR | Table name. This column cannot contain a NULL.
COLUMN_NAME DBTYPE_WSTR The name of the column; this might not be unique. If this cannot be determined, a NULL is returned.

This column, together with the COLUMN_GUID and COLUMN_PROPID columns, forms the column ID. One or more of these columns will be NULL depending on which elements of the DBID structure the provider uses.

If possible, the resulting column ID should be persistent. However, some providers do not support persistent identifiers for columns.

The column ID of a base table should be invariant under views.
COLUMN_GUID | DBTYPE_GUID | Column GUID. Providers that do not use GUIDs to identify columns should return NULL in this column.
COLUMN_PROPID | DBTYPE_UI4 | Column property ID. Providers that do not associate PROPIDs with columns should return NULL in this column.
ORDINAL_POSITION | DBTYPE_UI4 | The ordinal of the column. Columns are numbered starting from one. NULL if there is no stable ordinal value for the column.
COLUMN_HASDEFAULT DBTYPE_BOOL VARIANT_TRUE—The column has a default value.

VARIANT_FALSE—The column does not have a default value or it is unknown whether the column has a default value.
COLUMN_DEFAULT DBTYPE_WSTR Default value of the column. A provider may expose DBCOLUMN_DEFAULTVALUE but not DBCOLUMN_HASDEFAULT (for SQL92 tables) in the rowset returned by IColumnsRowset::GetColumnsRowset.

If the default value is the NULL value, COLUMN_HASDEFAULT is VARIANT_TRUE, and the COLUMN_DEFAULT column is a NULL value.
COLUMN_FLAGS | DBTYPE_UI4 | A bitmask that describes column characteristics. The DBCOLUMNFLAGS enumerated type specifies the bits in the bitmask. For information about DBCOLUMNFLAGS, see IColumnsInfo::GetColumnInfo. This column cannot contain a NULL value.
IS_NULLABLE DBTYPE_BOOL VARIANT_TRUE—The column might be nullable.

VARIANT_FALSE—The column is known not to be nullable.
DATA_TYPE | DBTYPE_UI2 | The indicator of the column's data type. If the data type of the column varies from row to row, this must be DBTYPE_VARIANT. This column cannot contain NULL. For a list of valid type indicators, see "Type Indicators" in Appendix A.
TYPE_GUID | DBTYPE_GUID | The GUID of the column's data type. Providers that do not use GUIDs to identify data types should return NULL in this column.
CHARACTER_
MAXIMUM_LENGTH 		DBTYPE_UI4 The maximum possible length of a value in the column. For character, binary, or bit columns, this is one of the following:
  • The maximum length of the column in characters, bytes, or bits, respectively, if one is defined. For example, a CHAR(5) column in an SQL table has a maximum length of 5.

  • The maximum length of the data type in characters, bytes, or bits, respectively, if the column does not have a defined length.

  • Zero (0) if neither the column nor the data type has a defined maximum length.

NULL for all other types of columns.
CHARACTER_OCTET_
LENGTH 		DBTYPE_UI4 Maximum length in octets (bytes) of the column, if the type of the column is character or binary. A value of zero means the column has no maximum length. NULL for all other types of columns.
NUMERIC_PRECISION DBTYPE_UI2 If the column's data type is numeric, this is the maximum precision of the column. The precision of columns with a data type of DBTYPE_DECIMAL or DBTYPE_NUMERIC depends on the definition of the column. For the precision of all other numeric data types, see "Precision of Numeric Data Types" in Appendix A.

If the column's data type is not numeric, this is NULL.
NUMERIC_SCALE | DBTYPE_I2 | If the column's type indicator is DBTYPE_DECIMAL or DBTYPE_NUMERIC, this is the number of digits to the right of the decimal point. Otherwise, this is NULL.
DATETIME_PRECISION | DBTYPE_UI4 | Datetime precision (number of digits in the fractional seconds portion) of the column if the column is a datetime or interval type. If the column's data type is not datetime, this is NULL.
CHARACTER_SET_
CATALOG 		DBTYPE_WSTR Catalog name in which the character set is defined. NULL if the provider does not support catalogs or different character sets.
CHARACTER_SET_
SCHEMA 		DBTYPE_WSTR Unqualified schema name in which the character set is defined. NULL if the provider does not support schemas or different character sets.
CHARACTER_SET_NAME | DBTYPE_WSTR | Character set name. NULL if the provider does not support different character sets.
COLLATION_CATALOG | DBTYPE_WSTR | Catalog name in which the collation is defined. NULL if the provider does not support catalogs or different collations.
COLLATION_SCHEMA | DBTYPE_WSTR | Unqualified schema name in which the collation is defined. NULL if the provider does not support schemas or different collations.
COLLATION_NAME | DBTYPE_WSTR | Collation name. NULL if the provider does not support different collations.
DOMAIN_CATALOG | DBTYPE_WSTR | Catalog name in which the domain is defined. NULL if the provider does not support catalogs or domains.
DOMAIN_SCHEMA | DBTYPE_WSTR | Unqualified schema name in which the domain is defined. NULL if the provider does not support schemas or domains.
DOMAIN_NAME | DBTYPE_WSTR | Domain name. NULL if the provider does not support domains.
DESCRIPTION | DBTYPE_WSTR | Human-readable description of the column. For example, the description for a column named Name in the Employee table might be "Employee name." Null if there is no description associated with the column.

Default Sort Order: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME


CONSTRAINT_COLUMN_USAGE Rowset

The CONSTRAINT_COLUMN_USAGE rowset identifies the columns used by referential constraints, unique constraints, check constraints, and assertions, defined in the catalog and owned by a given user.

The CONSTRAINT_COLUMN_USAGE rowset contains the following columns.

Column name | Type indicator | Description
TABLE_CATALOG | DBTYPE_WSTR | Catalog name in which the table is defined. NULL if the provider does not support catalogs.
TABLE_SCHEMA | DBTYPE_WSTR | Unqualified schema name in which the table is defined. NULL if the provider does not support schemas.
TABLE_NAME | DBTYPE_WSTR | Table name.
COLUMN_NAME | DBTYPE_WSTR | Column name. This column, together with the COLUMN_GUID and COLUMN_PROPID columns, forms the column ID. One or more of these columns will be NULL depending on which elements of the DBID structure the provider uses.
COLUMN_GUID | DBTYPE_GUID | Column GUID.
COLUMN_PROPID | DBTYPE_UI4 | Column property ID.
CONSTRAINT_CATALOG | DBTYPE_WSTR | Catalog name. NULL if the provider does not support catalogs.
CONSTRAINT_SCHEMA | DBTYPE_WSTR | Unqualified schema name. NULL if the provider does not support schemas.
CONSTRAINT_NAME | DBTYPE_WSTR | Constraint name.

Default Sort Order: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_GUID, COLUMN_PROPID, CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME


CONSTRAINT_TABLE_USAGE Rowset

The CONSTRAINT_TABLE_USAGE rowset identifies the tables that are used by referential constraints, unique constraints, check constraints, and assertions defined in the catalog and owned by a given user.

The CONSTRAINT_TABLE_USAGE rowset contains the following columns.

Column name | Type indicator | Description
TABLE_CATALOG | DBTYPE_WSTR | Catalog name in which the table is defined. NULL if the provider does not support catalogs.
TABLE_SCHEMA | DBTYPE_WSTR | Unqualified schema name in which the table is defined. NULL if the provider does not support schemas.
TABLE_NAME | DBTYPE_WSTR | Table name.
CONSTRAINT_CATALOG | DBTYPE_WSTR | Catalog name. NULL if the provider does not support catalogs.
CONSTRAINT_SCHEMA | DBTYPE_WSTR | Unqualified schema name. NULL if the provider does not support schemas.
CONSTRAINT_NAME | DBTYPE_WSTR | Constraint name.

Default Sort Order: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME


FOREIGN_KEYS Rowset

The FOREIGN_KEYS rowset identifies the foreign key columns defined in the catalog by a given user. This schema rowset is built upon several SQL92 schema views as a convenience to the non-SQL programmer, and, if supported, this must be synchronized with the related SQL92 views (REFERENTIAL_CONSTRAINTS and CONSTRAINT_COLUMN_USAGE).

The FOREIGN_KEYS rowset contains the following columns

Column name | Type indicator | Description
PK_NAME | DBTYPE_WSTR | Primary key name. NULL if the provider does not support named primary key constraints. This column is not returned by 1.x providers.
PK_TABLE_CATALOG | DBTYPE_WSTR | Catalog name in which the primary key table is defined. NULL if the provider does not support catalogs.
PK_TABLE_SCHEMA | DBTYPE_WSTR | Unqualified schema name in which the primary key table is defined. NULL if the provider does not support schemas.
PK_TABLE_NAME | DBTYPE_WSTR | Primary key table name.
PK_COLUMN_NAME | DBTYPE_WSTR | Primary key column name. This column, together with the PK_COLUMN_GUID and PK_COLUMN_PROPID columns, forms the column ID. One or more of these columns will be NULL depending on which elements of the DBID structure the provider uses.
PK_COLUMN_GUID | DBTYPE_GUID | Primary key column GUID.
PK_COLUMN_PROPID | DBTYPE_UI4 | Primary key column property ID.
FK_NAME | DBTYPE_WSTR | Foreign key name. NULL if the provider does not support named foreign key constraints. This column is not returned by 1.x providers.
FK_TABLE_CATALOG | DBTYPE_WSTR | Catalog name in which the foreign key table is defined. NULL if the provider does not support catalogs.
FK_TABLE_SCHEMA | DBTYPE_WSTR | Unqualified schema name in which the foreign key table is defined. NULL if the provider does not support schemas.
FK_TABLE_NAME | DBTYPE_WSTR | Foreign key table name.
FK_COLUMN_NAME | DBTYPE_WSTR | Foreign key column name. This column, together with the FK_COLUMN_GUID and FK_COLUMN_PROPID columns, forms the column ID. One or more of these columns will be NULL depending on which elements of the DBID structure the provider uses.
FK_COLUMN_GUID | DBTYPE_GUID | Foreign key column GUID.
FK_COLUMN_PROPID | DBTYPE_UI4 | Foreign key column property ID.
ORDINAL | DBTYPE_UI4 | The order of the column names (and GUIDs and property IDs) in the key. For example, a table might contain several foreign key references to another table. The ordinal starts over for each reference; for example, two references to a three-column key would return 1, 2, 3, 1, 2, 3.
UPDATE_RULE | DBTYPE_WSTR | If an <update rule> was specified, then the UPDATE_RULE value is one of the following: "CASCADE"—A <referential action> of CASCADE was specified. "SET NULL"—A <referential action> of SET NULL was specified. "SET DEFAULT"—A <referential action> of SET DEFAULT was specified. "NO ACTION"—A <referential action> of NO ACTION was specified. Providers should return NULL only if they cannot determine the UPDATE_RULE. In most cases, this implies a default of NO ACTION.
DEFERRABILITY | DBTYPE_12 | Deferrability of the foreign key. Value is one of the following: DBPROPVAL_DF_INITIALLY_DEFERRED DBPROPVAL_DF_INITIALLY_IMMEDIATE DBPROPVAL_DF_NOT_DEFERRABLE This column is not returned by 1.x providers.
DELETE_RULE | DBTYPE_WSTR | If a <delete rule> was specified then the DELETE_RULE value is one of the following: "CASCADE"—A <referential action> of CASCADE was specified. "SET NULL"—A <referential action> of SET NULL was specified. "SET DEFAULT"—A <referential action> of SET DEFAULT was specified. "NO ACTION"—A <referential action> of NO ACTION was specified. Providers should return NULL only if they cannot determine the DELETE_RULE. In most cases, this implies a default of NO ACTION.

Default Sort Order: FK_TABLE_CATALOG, FK_TABLE_SCHEMA, FK_TABLE_NAME


INDEXES Rowset

The INDEXES rowset identifies the indexes defined in the catalog that are owned by a given user.

The INDEXES rowset contains the following columns

Column name | Type indicator | Description
TABLE_CATALOG | DBTYPE_WSTR | Catalog name. NULL if the provider does not support catalogs.
TABLE_SCHEMA | DBTYPE_WSTR | Unqualified schema name. NULL if the provider does not support schemas.
TABLE_NAME | DBTYPE_WSTR | Table name.
INDEX_CATALOG | DBTYPE_WSTR | Catalog name. NULL if the provider does not support catalogs.
INDEX_SCHEMA | DBTYPE_WSTR | Unqualified schema name. NULL if the provider does not support schemas.
INDEX_NAME | DBTYPE_WSTR | Index name.
PRIMARY_KEY | DBTYPE_BOOL | Whether the index represents the primary key on the table. NULL if this is not known.
UNIQUE DBTYPE_BOOL Whether index keys must be unique. One of the following:

VARIANT_TRUE—The index keys must be unique.

VARIANT_FALSE—Duplicate keys are allowed.
CLUSTERED DBTYPE_BOOL Whether an index is clustered. One of the following:

VARIANT_TRUE—The leaf nodes of the index contain full rows, not bookmarks. This is a way to represent a table clustered by key value.

VARIANT_FALSE—The leaf nodes of the index contain bookmarks of the base table rows whose key value matches the key value of the index entry.
INTEGRATED DBTYPE_BOOL Whether the index is integrated (all base table columns are available from the index). One of the following:

VARIANT_TRUE—The index is integrated. For clustered indexes, this value must always be VARIANT_TRUE.

VARIANT_FALSE—The index is not integrated.
TYPE DBTYPE_UI2 The type of the index. One of the following:

DBPROPVAL_IT_BTREE—The index is a B+-tree.

DBPROPVAL_IT_HASH—The index is a hash file using, for example, linear or extensible hashing.

DBPROPVAL_IT_CONTENT—The index is a content index.

DBPROPVAL_IT_OTHER—The index is some other type of index.
FILL_FACTOR | DBTYPE_I4 | For a B+-tree index, this property represents the storage utilization factor of page nodes during the creation of the index. The value is an integer from 1 to 100 representing the percentage of use of an index node. For a linear hash index, this property represents the storage utilization of the entire hash structure (the ratio of used area to total allocated area) before a file structure expansion occurs.
INITIAL_SIZE | DBTYPE_I4 | The total amount of bytes allocated to this structure at creation time.
NULLS DBTYPE_I4 Whether null keys are allowed. One of the following:

DBPROPVAL_IN_DISALLOWNULL—The index does not allow entries where the key columns are NULL. If the consumer attempts to insert an index entry with a NULL key, then the provider returns an error.

DBPROPVAL_IN_IGNORENULL—The index does not insert entries containing NULL keys. If the consumer attempts to insert an index entry with a NULL key, then the provider ignores that entry and no error code is returned.

DBPROPVAL_IN_IGNOREANYNULL—The index does not insert entries where some column key has a NULL value. For an index having a multi-column search key, if the consumer inserts an index entry with NULL value in some column of the search key, then the provider ignores that entry and no error code is returned.
SORT_BOOKMARKS DBTYPE_BOOL How the index treats repeated keys. One of the following:

VARIANT_TRUE—The index sorts repeated keys by bookmark.

VARIANT_FALSE—The index does not sort repeated keys by bookmark.
AUTO_UPDATE DBTYPE_BOOL Whether the index is maintained automatically when changes are made to the corresponding base table. One of the following:

VARIANT_TRUE—The index is automatically maintained.

VARIANT_FALSE—The index must be maintained by the consumer through explicit calls to IRowsetChange. Ensuring consistency of the index as a result of updates to the associated base table is the responsibility of the consumer.
NULL_COLLATION DBTYPE_I4 How NULLs are collated in the index. One of the following:

DBPROPVAL_NC_END—NULLs are collated at the end of the list, regardless of the collation order.

DBPROPVAL_NC_START—NULLs are collated at the start of the list, regardless of the collation order.

DBPROPVAL_NC_HIGH—NULLs are collated at the high end of the list.

DBPROPVAL_NC_LOW—NULLs are collated at the low end of the list.
ORDINAL_POSITION | DBTYPE_UI4 | Ordinal position of the column in the index, starting with one.
COLUMN_NAME | DBTYPE_WSTR | Column name. This column, together with the COLUMN_GUID and COLUMN_PROPID columns, forms the column ID. One or more of these columns will be NULL depending on which elements of the DBID structure the provider uses.
COLUMN_GUID | DBTYPE_GUID | Column GUID.
COLUMN_PROPID | DBTYPE_UI4 | Column property ID.
COLLATION DBTYPE_I2 One of the following:

DB_COLLATION_ASC—The sort sequence for the column is ascending.

DB_COLLATION_DESC—The sort sequence for the column is descending.

NULL—A column sort sequence is not supported.
CARDINALITY | DBTYPE_UI8 | Number of unique values in the index.
PAGES | DBTYPE_I4 | Number of pages used to store the index.
FILTER_CONDITION | DBTYPE_WSTR | The WHERE clause identifying the filtering restriction.
INTEGRATED DBTYPE_BOOL Whether the index is integrated. That is, all base table columns are available from the index. One of the following:

VARIANT_TRUE—The index is integrated. For clustered indexes this value must always be VARIANT_TRUE.

VARIANT_FALSE—The index is not integrated.

This column is not returned by 1.x providers.

Default Sort Order: UNIQUE, TYPE, INDEX_CATALOG, INDEX_SCHEMA, INDEX_NAME, ORDINAL_POSITION


KEY_COLUMN_USAGE Rowset

The KEY_COLUMN_USAGE rowset identifies the columns defined in the catalog that are constrained as keys by a given user.

The KEY_COLUMN_USAGE rowset contains the following columns.

Column name | Type indicator | Description
CONSTRAINT_CATALOG | DBTYPE_WSTR | Catalog name. NULL if the provider does not support catalogs.
CONSTRAINT_SCHEMA | DBTYPE_WSTR | Unqualified schema name. NULL if the provider does not support schemas.
CONSTRAINT_NAME | DBTYPE_WSTR | Constraint name.
TABLE_CATALOG | DBTYPE_WSTR | Catalog name in which the table containing the key column is defined. NULL if the provider does not support catalogs.
TABLE_SCHEMA | DBTYPE_WSTR | Unqualified schema name in which the table containing the key column is defined. NULL if the provider does not support schemas.
TABLE_NAME | DBTYPE_WSTR | Table name containing the key column.
COLUMN_NAME | DBTYPE_WSTR | Name of the column participating in the unique, primary, or foreign key. This column, together with the COLUMN_GUID and COLUMN_PROPID columns, forms the column ID. One or more of these columns will be NULL depending on which elements of the DBID structure the provider uses.
COLUMN_GUID | DBTYPE_GUID | Column GUID.
COLUMN_PROPID | DBTYPE_UI4 | Column property ID.
ORDINAL_POSITION | DBTYPE_UI4 | Ordinal position of the column in the constraint being described. If the constraint being described is a key of cardinality one, the value of ORDINAL_POSITION is always one.

Default Sort Order: CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, ORDINAL_POSITION


PRIMARY_KEYS Rowset

The PRIMARY_KEYS rowset identifies the primary key columns defined in the catalog by a given user. This schema rowset is built upon an SQL92 schema view as a convenience to the non-SQL programmer, and, if supported, must be synchronized with the related SQL92 view (CONSTRAINT_COLUMN_USAGE).

The PRIMARY_KEYS rowset contains the following columns.

Column name | Type indicator | Description
PK_NAME | DB_WSTR | Primary key name. NULL if the provider does not support primary key constraints. This column is not returned by 1.x providers.
TABLE_CATALOG | DBTYPE_WSTR | Catalog name in which the table is defined. NULL if the provider does not support catalogs.
TABLE_SCHEMA | DBTYPE_WSTR | Unqualified schema name in which the table is defined. NULL if the provider does not support schemas.
TABLE_NAME | DBTYPE_WSTR | Table name.
COLUMN_NAME | DBTYPE_WSTR | Primary key column name. This column, together with the COLUMN_GUID and COLUMN_PROPID columns, forms the column ID. One or more of these columns will be NULL depending on which elements of the DBID structure the provider uses.
COLUMN_GUID | DBTYPE_GUID | Primary key column GUID.
COLUMN_PROPID | DBTYPE_UI4 | Primary key column property ID.
ORDINAL | DBTYPE_UI4 | The order of the column names (and GUIDs and property IDs) in the key.

Default Sort Order: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME


PROCEDURE_COLUMNS Rowset

The PROCEDURE_COLUMNS rowset returns information about the columns of rowsets returned by procedures.

The PROCEDURE_COLUMNS rowset contains the following columns.

Column name | Type indicator | Description
PROCEDURE_CATALOG | DBTYPE_WSTR | Catalog name. NULL if the provider does not support catalogs.
PROCEDURE_SCHEMA | DBTYPE_WSTR | Unqualified schema name. NULL if the provider does not support schemas.
PROCEDURE_NAME | DBTYPE_WSTR | Table name.
COLUMN_NAME DBTYPE_WSTR The name of the column; this might not be unique. If this cannot be determined, a NULL is returned.

This column, together with the COLUMN_GUID and COLUMN_PROPID columns, forms the column ID. One or more of these columns will be NULL depending on which elements of the DBID structure the provider uses.
COLUMN_GUID | DBTYPE_GUID | Column GUID.
COLUMN_PROPID | DBTYPE_UI4 | Column property ID.
ROWSET_NUMBER | DBTYPE_UI4 | Number of the rowset containing the column. This is greater than one only if the procedure returns multiple rowsets.
ORDINAL_POSITION | DBTYPE_UI4 | The ordinal of the column. Columns are numbered starting from one. NULL if there is no stable ordinal value for the column.
IS_NULLABLE DBTYPE_BOOL VARIANT_TRUE—The column might be nullable.

VARIANT_FALSE—The column is known not to be nullable.
DATA_TYPE | DBTYPE_UI2 | The indicator of the column's data type. If the data type of the column varies from row to row, this must be DBTYPE_VARIANT. For a list of valid type indicators, see "Type Indicators" in Appendix A.
TYPE_GUID | DBTYPE_GUID | The GUID of the column's data type.
CHARACTER_MAXIMUM_
LENGTH 		DBTYPE_UI4 The maximum possible length of a value in the column. For character, binary, or bit columns, this is one of the following:
  • The maximum length of the column in characters, bytes, or bits, respectively, if one is defined. For example, a CHAR(5) column in an SQL table has a maximum length of five (5).

  • The maximum length of the data type in characters, bytes, or bits, respectively, if the column does not have a defined length.

  • Zero (0) if neither the column nor the data type has a defined maximum length.

NULL for all other types of columns.
CHARACTER_OCTET_
LENGTH 		DBTYPE_UI4 Maximum length in octets (bytes) of the column, if the type of the column is character or binary. A value of zero means the column has no maximum length. NULL for all other types of columns.
NUMERIC_PRECISION DBTYPE_UI2 If the column's data type is numeric, this is the maximum precision of the column. The precision of columns with a data type of DBTYPE_DECIMAL or DBTYPE_NUMERIC depends on the definition of the column. For the precision of all other numeric data types, see "Precision of Numeric Data Types" in Appendix A.

If the column's data type is not numeric, this is NULL.
NUMERIC_SCALE | DBTYPE_I2 | If the column's type indicator is DBTYPE_DECIMAL or DBTYPE_NUMERIC, this is the number of digits to the right of the decimal point. Otherwise, this is NULL.
DESCRIPTION | DBTYPE_WSTR | Human-readable description of the column. For example, the description for a column named Name in the Employee table might be "Employee name."

Default Sort Order: PROCEDURE_CATALOG, PROCEDURE_SCHEMA, PROCEDURE_NAME


PROCEDURE_PARAMETERS Rowset

The PROCEDURE_PARAMETERS rowset returns information about the parameters and return codes of procedures.

The PROCEDURE_PARAMETERS rowset contains the following columns.

Column name | Type indicator | Description
PROCEDURE_CATALOG | DBTYPE_WSTR | Catalog name. NULL if the provider does not support catalogs.
PROCEDURE_SCHEMA | DBTYPE_WSTR | Schema name. NULL if the provider does not support catalogs.
PROCEDURE_NAME | DBTYPE_WSTR | Procedure name.
PARAMETER_NAME | DBTYPE_WSTR | Parameter name. NULL if the parameter is not named.
ORDINAL_POSITION DBTYPE_UI2 If the parameter is an input, input/output, or output parameter, this is the one-based ordinal position of the parameter in the procedure call.

If the parameter is the return value, this is zero.
PARAMETER_TYPE DBTYPE_UI2 One of the following:

DBPARAMTYPE_INPUT—The parameter is an input parameter.

DBPARAMTYPE_INPUTOUTPUT—The parameter is an input/output parameter.

DBPARAMTYPE_OUTPUT—The parameter is an output parameter.

DBPARAMTYPE_RETURNVALUE—The parameter is a procedure return value. For example, in the following ODBC SQL statement to call a procedure, the question mark marks a procedure return value:

{? = call GetNextOrderID}

If the provider cannot determine the parameter type, this is NULL.
PARAMETER_
HASDEFAULT 		DBTYPE_BOOL VARIANT_TRUE—The parameter has a default value.

VARIANT_FALSE—The parameter does not have a default value or it is unknown whether the parameter has a default value.
PARAMETER_DEFAULT DBTYPE_WSTR Default value of parameter.

If the default value is the NULL value, COLUMN_HASDEFAULT is VARIANT_TRUE, and the COLUMN_DEFAULT column is a NULL value.
IS_NULLABLE DBTYPE_BOOL VARIANT_TRUE—The parameter might be nullable.

VARIANT_FALSE—The parameter is not nullable.
DATA_TYPE | DBTYPE_UI2 | The indicator of the parameter's data type. For a list of valid type indicators, see "Type Indicators" in Appendix A.
CHARACTER_MAXIMUM_
LENGTH 		DBTYPE_UI4 The maximum possible length of a value in the parameter. For character, binary, or bit parameters, this is one of the following:
  • The maximum length of the parameter in characters, bytes, or bits, respectively, if one is defined. For example, a CHAR(5) parameter has a maximum length of five (5).

  • The maximum length of the data type in characters, bytes, or bits, respectively, if the parameter does not have a defined length.
  • Zero (0) if neither the parameter nor the data type has a defined maximum length.

    NULL for all other types of parameters.

CHARACTER_OCTET_
LENGTH 		DBTYPE_UI4 Maximum length in octets (bytes) of the parameter, if the type of the parameter is character or binary. A value of zero means the parameter has no maximum length. NULL for all other types of parameters.
NUMERIC_PRECISION DBTYPE_UI2 If the parameter's data type is numeric, this is the maximum precision of the parameter. The precision of parameters with a data type of DBTYPE_DECIMAL or DBTYPE_NUMERIC depends on the definition of the parameters. For the precision of all other numeric data types, see "Precision of Numeric Data Types" in Appendix A.

If the parameter's data type is not numeric, this is NULL.
NUMERIC_SCALE | DBTYPE_I2 | If the column's type indicator is DBTYPE_DECIMAL or DBTYPE_NUMERIC, this is the number of digits to the right of the decimal point. Otherwise, this is NULL.
DESCRIPTION | DBTYPE_WSTR | Human-readable description of the parameter. For example, the description of a parameter named Name in a procedure that adds a new employee might be "Employee name."
TYPE_NAME | DBTYPE_WSTR | Provider-specific data type name. This column is not returned by 1.x providers.
LOCAL_TYPE_NAME | DBTYPE_WSTR | Localized version of TYPE_NAME. NULL is returned if a localized name is not supported by the data provider. This column is not returned by 1.x providers.

Default Sort Order: PROCEDURE_CATALOG, PROCEDURE_SCHEMA, PROCEDURE_NAME


PROCEDURES Rowset

The PROCEDURES rowset is an OLE DB extension, based on SQL3. It identifies the procedures defined in the catalog that are owned by a given user.

The PROCEDURES rowset contains the following columns.

Column name | Type indicator | Description
PROCEDURE_CATALOG | DBTYPE_WSTR | Catalog name. NULL if the provider does not support catalogs.
PROCEDURE_SCHEMA | DBTYPE_WSTR | Unqualified schema name. NULL if the provider does not support schemas.
PROCEDURE_NAME | DBTYPE_WSTR | Procedure name.
PROCEDURE_TYPE | DBTYPE_I2 | DB_PT_UNKNOWN—It is not known whether there is a returned value. DB_PT_PROCEDURE—Procedure; there is no returned value. DB_PT_FUNCTION—Function; there is a returned value.
PROCEDURE_ DEFINITION | DBTYPE_WSTR | Procedure definition.
DESCRIPTION | DBTYPE_WSTR | Human-readable description of the procedure.
DATE_CREATED | DBTYPE_DATE | Date when the procedure was created or NULL if the provider does not have this information. Note that 1.x providers do not return this column.
DATE_MODIFIED | DBTYPE_DATE | Date when the procedure definition was last modified or NULL if the provider does not have this information. Note that 1.x providers do not return this column.

Default Sort Order: PROCEDURE_CATALOG, PROCEDURE_SCHEMA, PROCEDURE_NAME


PROVIDER_TYPES Rowset

The PROVIDER_TYPES rowset identifies the (base) data types supported by the data provider.

The PROVIDER_TYPES rowset contains the following columns.

Column name | Type indicator | Description
TYPE_NAME | DBTYPE_WSTR | Provider-specific data type name.
DATA_TYPE | DBTYPE_UI2 | The indicator of the data type.
COLUMN_SIZE DBTYPE_UI4 The length of a non-numeric column or parameter refers to either the maximum or the defined length for this type by the provider. For character data, this is the maximum or defined length in characters. For datetime data types, this is the length of the string representation (assuming the maximum allowed precision of the fractional seconds component).

If the data type is numeric, this is the upper bound on the maximum precision of the data type. For the maximum precision of all numeric data types, see "Precision of Numeric Data Types" in Appendix A.
LITERAL_PREFIX | DBTYPE_WSTR | Character or characters used to prefix a literal of this type in a text command.
LITERAL_SUFFIX | DBTYPE_WSTR | Character or characters used to suffix a literal of this type in a text command.
CREATE_PARAMS DBTYPE_WSTR The creation parameters are specified by the consumer when creating a column of this data type. For example, the SQL data type DECIMAL needs a precision and a scale. In this case, the creation parameters might be the string "precision,scale". In a text command to create a DECIMAL column with a precision of 10 and a scale of 2, the value of the TYPE_NAME column might be DECIMAL() and the complete type specification would be DECIMAL(10,2).

The creation parameters appear as a comma-separated list of values, in the order they are to be supplied, with no surrounding parentheses. If a creation parameter is length, maximum length, precision, or scale, "length", "max length", "precision", and "scale" should be used, respectively. If the creation parameters are some other value, it is provider-specific what text is used to describe the creation parameter.

If the data type requires creation parameters, "()" generally appears in the type name. This indicates the position at which to insert the creation parameters. If the type name does not include "()", the creation parameters are enclosed in parentheses and appended to the end of the data type name.
IS_NULLABLE DBTYPE_BOOL VARIANT_TRUE—The data type is nullable.

VARIANT_FALSE—The data type is not nullable.

NULL—It is not known whether the data type is nullable.
CASE_SENSITIVE DBTYPE_BOOL VARIANT_TRUE—The data type is a character type and is case sensitive.

VARIANT_FALSE—The data type is not a character type or is not case sensitive.
SEARCHABLE DBTYPE_UI4 If the provider supports ICommandText, then this column is an integer indicating the searchability of a data type; otherwise, this column is NULL. One of the following:

DB_UNSEARCHABLE—The data type cannot be used in a WHERE clause.

DB_LIKE_ONLY—The data type can be used in a WHERE clause only with the LIKE predicate.

DB_ALL_EXCEPT_LIKE—The data type can be used in a WHERE clause with all comparison operators except LIKE.
DB_SEARCHABLE—The data type can be used in a WHERE clause with any comparison operator.
UNSIGNED_ATTRIBUTE DBTYPE_BOOL VARIANT_TRUE—The data type is unsigned.

VARIANT_FALSE—The data type is signed.

NULL—Not applicable to data type.
FIXED_PREC_SCALE DBTYPE_BOOL VARIANT_TRUE—The data type has a fixed precision and scale.

VARIANT_FALSE—The data type does not have a fixed precision and scale.
AUTO_UNIQUE_VALUE DBTYPE_BOOL VARIANT_TRUE—Values of this type can be autoincrementing.

VARIANT_FALSE—Values of this type cannot be autoincrementing.

If this value is VARIANT_TRUE, then whether a column of this type is always autoincrementing or not depends on the provider's DBPROP_COL_AUTOINCREMENT column property. If the DBPROP_COL_AUTOINCREMENT property is read/write, then whether or not a column of this type is autoincrementing depends on the setting of the DBPROP_COL_AUTOINCREMENT property. If DBPROP_COL_AUTOINCREMENT is a read-only property, then either all columns of this type are autoincrementing or they are all not.
LOCAL_TYPE_NAME | DBTYPE_WSTR | Localized version of TYPE_NAME. NULL is returned if a localized name is not supported by the data provider.
MINIMUM_SCALE | DBTYPE_I2 | If the type indicator is DBTYPE_DECIMAL or DBTYPE_NUMERIC, this is the minimum number of digits allowed to the right of the decimal point. Otherwise, this is NULL.
MAXIMUM_SCALE | DBTYPE_I2 | If the type indicator is DBTYPE_DECIMAL or DBTYPE_NUMERIC, this is the maximum number of digits allowed to the right of the decimal point. Otherwise, this is NULL.
GUID | DBTYPE_GUID | The GUID of the type. All types supported by a provider are described in a type library, so each type has a corresponding GUID.
TYPELIB | DBTYPE_WSTR | The type library containing the description of this type. All types supported by a provider, including those in Appendix A, are described in one or more type libraries.
VERSION | DBTYPE_WSTR | The version of the type definition. Providers may wish to version type definitions. Different providers may use different version schemes, such as a timestamp or number (integer or float). NULL if not supported.
IS_LONG DBTYPE_BOOL VARIANT_TRUE—The data type is a BLOB that contains very long data; the definition of very long data is provider-specific.

VARIANT_FALSE—The data type is a BLOB that does not contain very long data or is not a BLOB.

This value determines the setting of the DBCOLUMNFLAGS_ISLONG flag returned by GetColumnInfo in IColumnsInfo and GetParameterInfo in ICommandWithParameters. For more information, see GetColumnInfo, GetParameterInfo, and "Accessing BLOB Data" in Chapter 7.
BEST_MATCH DBTYPE_BOOL VARIANT_TRUE—The data type is the best match between all data types in the data source and the OLE DB data type indicated by the value in the DATA_TYPE column.

VARIANT_FALSE—The data type is not the best match.

For each set of rows in which the value of the DATA_TYPE column is the same, the BEST_MATCH column is set to VARIANT_TRUE in only one row.
IS_FIXEDLENGTH DBTYPE_BOOL VARIANT_TRUE—Columns of this type created by the DDL will be of fixed length.

VARIANT_FALSE—Columns of this type created by the DDL will be of variable length.

If the field is NULL, it is not known whether the provider will map this field with a fixed or variable length.

Default Sort Order: DATA_TYPE


REFERENTIAL_CONSTRAINTS Rowset

The REFERENTIAL_CONSTRAINTS rowset identifies the referential constraints defined in the catalog that are owned by a given user.

The REFERENTIAL_CONSTRAINTS rowset contains the following columns.

Column name | Type indicator | Description
CONSTRAINT_CATALOG | DBTYPE_WSTR | Catalog name. NULL if the provider does not support catalogs.
CONSTRAINT_SCHEMA | DBTYPE_WSTR | Unqualified schema name. NULL if the provider does not support schemas.
CONSTRAINT_NAME | DBTYPE_WSTR | Constraint name.
UNIQUE_CONSTRAINT_ CATALOG | DBTYPE_WSTR | Catalog name in which the unique or primary key constraint is defined. NULL if the provider does not support catalogs.
UNIQUE_CONSTRAINT_ SCHEMA | DBTYPE_WSTR | Unqualified schema name in which the unique or primary key constraint is defined. NULL if the provider does not support schemas.
UNIQUE_CONSTRAINT_ NAME | DBTYPE_WSTR | Unique or primary key constraint name.
MATCH_OPTION | DBTYPE_WSTR | "NONE"—No <match type> was specified. "PARTIAL"—A <match type> of PARTIAL was specified. "FULL"—A <match type> of FULL was specified.
UPDATE_RULE | DBTYPE_WSTR | If an <update rule> was specified then the UPDATE_RULE value is one of the following: "CASCADE"—A <referential action> of CASCADE was specified. "SET NULL"—A <referential action> of SET NULL was specified. "SET DEFAULT"—A <referential action> of SET DEFAULT was specified. "NO ACTION"—A <referential action> of NO ACTION was specified.
DELETE_RULE | DBTYPE_WSTR | If a <delete rule> was specified, then the DELETE_RULE value is one of the following: "CASCADE"—A <referential action> of CASCADE was specified. "SET NULL"—A <referential action> of SET NULL was specified. "SET DEFAULT"—A <referential action> of SET DEFAULT was specified. "NO ACTION"—A <referential action> of NO ACTION was specified.
DESCRIPTION | DBTYPE_WSTR | Human-readable description of the constraint.

Default Sort Order: CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, UNIQUE_CONSTRAINT_CATALOG, UNIQUE_CONSTRAINT_SCHEMA, UNIQUE_CONSTRAINT_NAME


SCHEMATA Rowset

The SCHEMATA rowset identifies the schemas that are owned by a given user.

The SCHEMATA rowset contains the following columns.

Column name | Type indicator | Description
CATALOG_NAME | DBTYPE_WSTR | Catalog name. NULL if the provider does not support catalogs.
SCHEMA_NAME | DBTYPE_WSTR | Unqualified schema name.
SCHEMA_OWNER | DBTYPE_WSTR | User that owns the schemas.
DEFAULT_CHARACTER_ SET_CATALOG | DBTYPE_WSTR | Catalog name of the default character set for columns and domains in the schemas. NULL if the provider does not support catalogs or different character sets.
DEFAULT_CHARACTER_ SET_SCHEMA | DBTYPE_WSTR | Unqualified schema name of the default character set for columns and domains in the schemas. NULL if the provider does not support different character sets.
DEFAULT_CHARACTER_ SET_NAME | DBTYPE_WSTR | Default character set name. NULL if the provider does not support different character sets.

Default Sort Order: CATALOG_NAME, SCHEMA_NAME, SCHEMA_OWNER


SQL_LANGUAGES Rowset

The SQL_LANGUAGES rowset identifies the conformance levels, options, and dialects supported by the SQL-implementation processing data defined in the catalog.

The SQL_LANGUAGES rowset contains the following columns.

Column name | Type indicator | Description
SQL_LANGUAGE_ SOURCE | DBTYPE_WSTR | Should be "ISO 9075" for standard SQL.
SQL_LANGUAGE_YEAR | DBTYPE_WSTR | Should be "1992" for ANSI SQL92-compliant SQL.
SQL_LANGUAGE_ CONFORMANCE | DBTYPE_WSTR | One of the following: "ENTRY" "INTERMEDIATE" "FULL"
SQL_LANGUAGE_ INTEGRITY | DBTYPE_WSTR | "YES"—Optional integrity feature is supported. "NO"—Optional integrity feature is not supported.
SQL_LANGUAGE_ IMPLEMENTATION | DBTYPE_WSTR | NULL for "ISO 9075" implementation.
SQL_LANGUAGE_ BINDING_STYLE | DBTYPE_WSTR | "DIRECT" for C/C++ callable direct execution of SQL.
SQL_LANGUAGE_ PROGRAMMING_ LANGUAGE | DBTYPE_WSTR | NULL.

Default Sort Order: SQL_LANGUAGE_SOURCE


STATISTICS Rowset

The STATISTICS rowset identifies the statistics defined in the catalog that are owned by a given user.

The STATISTICS rowset contains the following columns.

Column name | Type indicator | Description
TABLE_CATALOG | DBTYPE_WSTR | Catalog name. NULL if the provider does not support catalogs.
TABLE_SCHEMA | DBTYPE_WSTR | Unqualified schema name. NULL if the provider does not support schemas.
TABLE_NAME | DBTYPE_WSTR | Table name.
CARDINALITY | DBTYPE_UI8 | Cardinality (number of rows) of the table.

Default Sort Order: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME


TABLE_CONSTRAINTS Rowset

The TABLE_CONSTRAINTS rowset identifies the table constraints defined in the catalog that are owned by a given user.

The TABLE_CONSTRAINTS rowset contains the following columns.

Column name | Type indicator | Description
CONSTRAINT_CATALOG | DBTYPE_WSTR | Catalog name. NULL if the provider does not support catalogs.
CONSTRAINT_SCHEMA | DBTYPE_WSTR | Unqualified schema name. NULL if the provider does not support schemas.
CONSTRAINT_NAME | DBTYPE_WSTR | Constraint name.
TABLE_CATALOG | DBTYPE_WSTR | Catalog name in which the table is defined. NULL if the provider does not support catalogs.
TABLE_SCHEMA | DBTYPE_WSTR | Unqualified schema name in which the table is defined. NULL if the provider does not support schemas.
TABLE_NAME | DBTYPE_WSTR | Table name.
CONSTRAINT_TYPE | DBTYPE_WSTR | Constraint type. One of the following: "UNIQUE" "PRIMARY KEY" "FOREIGN KEY" "CHECK"
IS_DEFERRABLE | DBTYPE_BOOL | VARIANT_TRUE—The table constraint is deferrable. VARIANT_FALSE—The table constraint is not deferrable.
INITIALLY_DEFERRED | DBTYPE_BOOL | VARIANT_TRUE—The table constraint is initially deferred. VARIANT_FALSE—The table constraint is initially immediate.
DESCRIPTION | DBTYPE_WSTR | Human-readable description of the column

Default Sort Order: CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, CONSTRAINT_TYPE


TABLE_PRIVILEGES Rowset

The TABLE_PRIVILEGES rowset identifies the privileges on tables defined in the catalog that are available to or granted by a given user.

The TABLE_PRIVILEGES rowset contains the following columns.

Column name | Type indicator | Description
GRANTOR | DBTYPE_WSTR | User who granted the privileges on the table in TABLE_NAME.
GRANTEE | DBTYPE_WSTR | User name (or "PUBLIC") to whom the privilege has been granted.
TABLE_CATALOG | DBTYPE_WSTR | Catalog name in which the table is defined. NULL if the provider does not support catalogs.
TABLE_SCHEMA | DBTYPE_WSTR | Unqualified schema name in which the table is defined. NULL if the provider does not support schemas.
TABLE_NAME | DBTYPE_WSTR | Table name.
PRIVILEGE_TYPE | DBTYPE_WSTR | Privilege type. One of the following: "SELECT" "DELETE" "INSERT" "UPDATE" "REFERENCES"
IS_GRANTABLE | DBTYPE_BOOL | VARIANT_TRUE—The privilege being described was granted with the WITH GRANT OPTION clause. VARIANT_FALSE—The privilege being described was not granted with the WITH GRANT OPTION clause.

Default Sort Order: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, PRIVILEGE_TYPE


TABLES Rowset

The TABLES rowset identifies the tables defined in the catalog that are accessible to a given user.

The TABLES rowset contains the following columns.

Column name | Type indicator | Description
TABLE_CATALOG | DBTYPE_WSTR | Catalog name. NULL if the provider does not support catalogs.
TABLE_SCHEMA | DBTYPE_WSTR | Unqualified schema name. NULL if the provider does not support schemas.
TABLE_NAME | DBTYPE_WSTR | Table name. This column cannot contain a NULL.
TABLE_TYPE | DBTYPE_WSTR | Table type. One of the following or a provider-specific value. "ALIAS" "TABLE" "SYNONYM" "SYSTEM TABLE" "VIEW" "GLOBAL TEMPORARY" "LOCAL TEMPORARY" This column cannot contain a Null.
TABLE_GUID | DBTYPE_GUID | GUID that uniquely identifies the table. Providers that do not use GUIDs to identify tables should return NULL in this column.
DESCRIPTION | DBTYPE_WSTR | Human-readable description of the table. Null if there is no description associated with the column.
TABLE_PROPID | DBTYPE_UI4 | Property ID of the table. Providers which do not use PROPIDs to identify columns should return NULL in this column.
DATE_CREATED | DBTYPE_DATE | Date when the table was created or NULL if the provider does not have this information. Note that 1.x providers do not return this column.
DATE_MODIFIED | DBTYPE_DATE | Date when the table definition was last modified or NULL if the provider does not have this information. Note that 1.x providers do not return this column.

Default Sort Order: TABLE_TYPE, TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME


TABLES_INFO Rowset

The TABLES_INFO rowset identifies the tables defined in the catalog that are accessible to a given user.

The TABLES_INFO rowset contains the following columns.

Column name | Type indicator | Description
TABLE_CATALOG | DBTYPE_WSTR | Catalog name. NULL if the provider does not support catalogs.
TABLE_SCHEMA | DBTYPE_WSTR | Unqualified schema name. NULL if the provider does not support schemas.
TABLE_NAME | DBTYPE_WSTR | Table name.
TABLE_TYPE | DBTYPE_WSTR | Table type. One of the following or a provider-specific value. “ALIAS” “TABLE” “SYNONYM” “SYSTEM TABLE” “VIEW” “GLOBAL TEMPORARY” “LOCAL TEMPORARY” “EXTERNAL TABLE”
TABLE_GUID | DBTYPE_GUID | GUID that uniquely identifies the table. Providers that do not use GUIDs to identify tables should return NULL in this column.
BOOKMARKS | DBTYPE_BOOL | Whether this table supports bookmarks when opening this table with IOpenRowset::OpenRowset or SELECT * FROM <table-name>. VARIANT_TRUE—The table supports bookmarks. VARIANT_FALSE—The table does not support bookmarks.
BOOKMARK_TYPE | DBTYPE_I4 | Default bookmark type supported on this table. Contains one of the following values: DBPROPVAL_BMK_NUMERIC—The bookmark type is numeric. DBPROPVAL_BMK_KEY—The bookmark type is key. For more information, see “Bookmark Types” in Chapter 4. This column is not NULL only if the BOOKMARKS column is VARIANT_TRUE.
BOOKMARK_DATATYPE | DBTYPE_UI2 | The indicator of the bookmark’s native data type. For a list of valid type indicators, see “Type Indicators” in Appendix A. This column is not NULL only if the BOOKMARKS column is VARIANT_TRUE.
BOOKMARK_ MAXIMUM_LENGTH | DBTYPE_UI4 | Maximum length of the bookmark in bytes. This column is not NULL only if the BOOKMARKS column is VARIANT_TRUE.
BOOKMARK_ INFORMATION | DBTYPE_UI4 | A bitmask specifying additional information about bookmarks over the rowset. A combination of zero or more of the following: DBPROPVAL_BI_CROSSROWSET—If set, bookmark values are valid across rowsets generated by calling OpenRowset on this table. If not set, bookmark values, if supported, are valid only within a single rowset over this table.
TABLE_VERSION | DBTYPE_I8 | Version number for this table, or NULL if the provider does not support returning table version information. This number is assumed to change every time the table is modified through ALTER TABLE, CREATE INDEX, DROP INDEX, or similar statements. The way in which this number is generated is provider specific.
CARDINALITY | DBTYPE_UI8 | Cardinality (number of rows) of the table.
DESCRIPTION | DBTYPE_WSTR | Human-readable description of the table.
TABLE_PROPID | DBTYPE_UI4 | Property ID of the table. Providers which do not use PROPIDs to identify columns should return NULL in this column.

Default Sort Order: TABLE_TYPE, TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME


TRANSLATIONS Rowset

The TRANSLATIONS rowset identifies the character translations defined in the catalog that are accessible to a given user.

The TRANSLATIONS rowset contains the following columns.

Column name | Type indicator | Description
TRANSLATION_ CATALOG | DBTYPE_WSTR | Catalog name. NULL if the provider does not support catalogs.
TRANSLATION_SCHEMA | DBTYPE_WSTR | Unqualified schema name. NULL if the provider does not support schemas.
TRANSLATION_NAME | DBTYPE_WSTR | Translation name.
SOURCE_CHARACTER_ SET_CATALOG | DBTYPE_WSTR | Catalog name containing the source character set on which the translation is defined. NULL if the provider does not support catalogs.
SOURCE_CHARACTER_ SET_SCHEMA | DBTYPE_WSTR | Unqualified schema name containing the source character set on which the translation is defined. NULL if the provider does not support schemas.
SOURCE_CHARACTER_ SET_NAME | DBTYPE_WSTR | Source character set name on which the translation is defined.
TARGET_CHARACTER_ SET_CATALOG | DBTYPE_WSTR | Catalog name containing the target character set on which the translation is defined. NULL if the provider does not support catalogs.
TARGET_CHARACTER_ SET_SCHEMA | DBTYPE_WSTR | Unqualified schema name containing the target character set on which the translation is defined. NULL if the provider does not support schemas.
TARGET_CHARACTER_ SET_NAME | DBTYPE_WSTR | Target character set name on which the translation is defined.

Default Sort Order: TRANSLATION_CATALOG, TRANSLATION_SCHEMA, TRANSLATION_NAME


USAGE_PRIVILEGES Rowset

The USAGE_PRIVILEGES rowset identifies the USAGE privileges on objects defined in the catalog that are available to or granted by a given user.

The USAGE_PRIVILEGES rowset contains the following columns.

Column name | Type indicator | Description
GRANTOR | DBTYPE_WSTR | User who granted the privileges on the object in OBJECT_NAME.
GRANTEE | DBTYPE_WSTR | User name (or "PUBLIC") to whom the privilege has been granted.
OBJECT_CATALOG | DBTYPE_WSTR | Catalog name in which the object is defined. NULL if the provider does not support catalogs.
OBJECT_SCHEMA | DBTYPE_WSTR | Unqualified schema name in which the object is defined. NULL if the provider does not support schemas.
OBJECT_NAME | DBTYPE_WSTR | Object name.
OBJECT_TYPE | DBTYPE_WSTR | Object type. One of the following: "DOMAIN" "CHARACTER SET" "COLLATION" "TRANSLATION"
PRIVILEGE_TYPE | DBTYPE_WSTR | Privilege type. One of the following: "USAGE"
IS_GRANTABLE | DBTYPE_BOOL | VARIANT_TRUE—The privilege being described was granted with the WITH GRANT OPTION clause. VARIANT_FALSE—The privilege being described was not granted with the WITH GRANT OPTION clause.

Default Sort Order: OBJECT_CATALOG, OBJECT_SCHEMA, OBJECT_NAME, OBJECT_TYPE, PRIVILEGE_TYPE


VIEW_COLUMN_USAGE Rowset

The VIEW_COLUMN_USAGE rowset identifies the columns on which viewed tables, defined in the catalog and owned by a given user, are dependent.

The VIEW_COLUMN_USAGE rowset contains the following columns.

Column name | Type indicator | Description
VIEW_CATALOG | DBTYPE_WSTR | Catalog name. NULL if the provider does not support catalogs.
VIEW_SCHEMA | DBTYPE_WSTR | Unqualified schema name. NULL if the provider does not support schemas.
VIEW_NAME | DBTYPE_WSTR | View name.
TABLE_CATALOG | DBTYPE_WSTR | Catalog name in which the table is defined. NULL if the provider does not support catalogs.
TABLE_SCHEMA | DBTYPE_WSTR | Unqualified schema name in which the table is defined. NULL if the provider does not support schemas.
TABLE_NAME | DBTYPE_WSTR | Table name.
COLUMN_NAME | DBTYPE_WSTR | Column name. This column, together with the COLUMN_GUID and COLUMN_PROPID columns, forms the column ID. One or more of these columns will be NULL depending on which elements of the DBID structure the provider uses.
COLUMN_GUID | DBTYPE_GUID | Column GUID.
COLUMN_PROPID | DBTYPE_UI4 | Column property ID.

Default Sort Order: VIEW_CATALOG, VIEW_SCHEMA, VIEW_NAME, TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_GUID, COLUMN_PROPID


VIEW_TABLE_USAGE Rowset

The VIEW_TABLE_USAGE rowset identifies the tables on which viewed tables, defined in the catalog and owned by a given user, are dependent.

The VIEW_TABLE_USAGE rowset contains the following columns.

Column name | Type indicator | Description
VIEW_CATALOG | DBTYPE_WSTR | Catalog name. NULL if the provider does not support catalogs.
VIEW_SCHEMA | DBTYPE_WSTR | Unqualified schema name. NULL if the provider does not support schemas.
VIEW_NAME | DBTYPE_WSTR | View name.
TABLE_CATALOG | DBTYPE_WSTR | Catalog name in which the table is defined. NULL if the provider does not support catalogs.
TABLE_SCHEMA | DBTYPE_WSTR | Unqualified schema name in which the table is defined. NULL if the provider does not support schemas.
TABLE_NAME | DBTYPE_WSTR | Table name.

Default Sort Order: VIEW_CATALOG, VIEW_SCHEMA, VIEW_NAME, TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME


VIEWS Rowset

The VIEWS rowset identifies the viewed tables defined in the catalog that are accessible to a given user.

The VIEWS rowset contains the following columns.

Column name | Type indicator | Description
TABLE_CATALOG | DBTYPE_WSTR | Catalog name. NULL if the provider does not support catalogs.
TABLE_SCHEMA | DBTYPE_WSTR | Unqualified schema name. NULL if the provider does not support schemas.
TABLE_NAME | DBTYPE_WSTR | View name.
VIEW_DEFINITION | DBTYPE_WSTR | View definition. This is a query expression.
CHECK_OPTION | DBTYPE_BOOL | Check option. One of the following: VARIANT_TRUE—Local update checking only. VARIANT_FALSE—Cascaded update checking (same as no CHECK OPTION specified on view definition).
IS_UPDATABLE | DBTYPE_BOOL | VARIANT_TRUE—The view is updatable. VARIANT_FALSE—The view is not updatable.
DESCRIPTION | DBTYPE_WSTR | Human-readable description of the view.
DATE_CREATED | DBTYPE_DATE | Date when the view was created or NULL if the provider does not have this information. Note that 1.x providers do not return this column.
DATE_MODIFIED | DBTYPE_DATE | Date when the view definition was last modified or NULL if the provider does not have this information. Note that 1.x providers do not return this column.

Default Sort Order: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME


Overview of Property Tables

This appendix lists the properties in all property groups that are defined by OLE DB. The properties table by property group is followed by an alphabetical listing of all properties, which gives information about them such as type, description, effects and characteristics, and return values.


Properties Table (by Property Groups)

This table lists all properties contained within each property group. For more information about a specific property, click its link below.

Property Group | Property Set | Properties
Column DBPROPSET_COLUMN DBPROP_COL_AUTOINCREMENT
DBPROP_COL_DEFAULT
DBPROP_COL_DESCRIPTION
DBPROP_COL_FIXEDLENGTH
DBPROP_COL_NULLABLE
DBPROP_COL_PRIMARYKEY
DBPROP_COL_UNIQUE
Data Source | DBPROPSET_DATASOURCE | DBPROP_CURRENTCATALOG
Data Source Creation | None. OLE DB does not define any data source creation properties. Providers can define their own data source creation properties.
Data Source Information DBPROPSET_DATASOURCEINFO DBPROP_ACTIVESESSIONS
DBPROP_ALTERCOLUMN
DBPROP_ASYNCTXNABORT
DBPROP_ASYNCTXNCOMMIT

DBPROP_BYREFACCESSORS
DBPROP_CATALOGLOCATION
DBPROP_CATALOGTERM
DBPROP_CATALOGUSAGE
DBPROP_COLUMNDEFINITION
DBPROP_CONCATNULLBEHAVIOR
DBPROP_DATASOURCENAME
DBPROP_DATASOURCEREADONLY
DBPROP_DBMSNAME
DBPROP_DBMSVER
DBPROP_DSOTHREADMODEL
DBPROP_GROUPBY
DBPROP_HETEROGENEOUSTABLES
DBPROP_IDENTIFIERCASE
DBPROP_MAXINDEXSIZE
DBPROP_MAXOPENCHAPTERS

DBPROP_MAXORSINFILTER
DBPROP_MAXROWSIZE
DBPROP_MAXROWSIZEINCLUDESBLOB
DBPROP_MAXSORTCOLUMNS
DBPROP_MAXTABLESINSELECT
DBPROP_MULTIPLEPARAMSETS
DBPROP_MULTIPLERESULTS
DBPROP_MULTIPLESTORAGEOBJECTS
DBPROP_MULTITABLEUPDATE
DBPROP_NULLCOLLATION
DBPROP_OLEOBJECTS
DBPROP_ORDERBYCOLUMNSINSELECT
DBPROP_OUTPUTPARAMETERAVAILABILITY
DBPROP_PERSISTENTIDTYPE
DBPROP_PREPAREABORTBEHAVIOR
DBPROP_PREPARECOMMITBEHAVIOR
DBPROP_PROCEDURETERM
DBPROP_PROVIDERFRIENDLYNAME
DBPROP_PROVIDERNAME
DBPROP_PROVIDEROLEDBVER
DBPROP_PROVIDERVER
DBPROP_QUOTEDIDENTIFIERCASE
DBPROP_ROWSETCONVERSIONSONCOMMAND
DBPROP_SCHEMATERM
DBPROP_SCHEMAUSAGE
DBPROP_SERVERNAME
DBPROP_SORTONINDEX
DBPROP_SQLSUPPORT
DBPROP_STRUCTUREDSTORAGE
DBPROP_SUBQUERIES
DBPROP_SUPPORTEDTXNDDL
DBPROP_SUPPORTEDTXNISOLEVELS
DBPROP_SUPPORTEDTXNISORETAIN
DBPROP_USERNAME
Index DBPROPSET_INDEX DBPROP_INDEX_AUTOUPDATE
DBPROP_INDEX_CLUSTERED
DBPROP_INDEX_FILLFACTOR
DBPROP_INDEX_INITIALSIZE
DBPROP_INDEX_NULLCOLLATION
DBPROP_INDEX_NULLS
DBPROP_INDEX_PRIMARYKEY
DBPROP_INDEX_SORTBOOKMARKS
DBPROP_INDEX_TEMPINDEX
DBPROP_INDEX_TYPE
DBPROP_INDEX_UNIQUE
Initialization DBPROPSET_DBINIT DBPROP_AUTH_CACHE_AUTHINFO
DBPROP_AUTH_ENCRYPT_PASSWORD
DBPROP_AUTH_INTEGRATED
DBPROP_AUTH_MASK_PASSWORD
DBPROP_AUTH_PASSWORD
DBPROP_AUTH_PERSIST_ENCRYPTED
DBPROP_AUTH_PERSIST_SENSITIVE_AUTHINFO
DBPROP_AUTH_USERID
DBPROP_INIT_ASYNCH
DBPROP_INIT_CATALOG
DBPROP_INIT_DATASOURCE
DBPROP_INIT_HWND
DBPROP_INIT_IMPERSONATION_LEVEL
DBPROP_INIT_LCID
DBPROP_INIT_LOCATION
DBPROP_INIT_MODE
DBPROP_INIT_PROMPT
DBPROP_INIT_PROTECTION_LEVEL
DBPROP_INIT_PROVIDERSTRING
DBPROP_INIT_TIMEOUT
Rowset DBPROPSET_ROWSET DBPROP_ABORTPRESERVE
DBPROP_APPENDONLY
DBPROP_BLOCKINGSTORAGEOBJECTS
DBPROP_BOOKMARKINFO
DBPROP_BOOKMARKS
DBPROP_BOOKMARKSKIPPED
DBPROP_BOOKMARKTYPE
DBPROP_CACHEDEFERRED
DBPROP_CANFETCHBACKWARDS
DBPROP_CANHOLDROWS
DBPROP_CANSCROLLBACKWARDS
DBPROP_CHANGEINSERTEDROWS
DBPROP_COLUMNRESTRICT
DBPROP_COMMANDTIMEOUT
DBPROP_COMMITPRESERVE
DBPROP_DEFERRED
DBPROP_DELAYSTORAGEOBJECTS
DBPROP_FILTERCOMPAREOPS
DBPROP_FINDCOMPAREOPS
DBPROP_IACCESSOR
DBPROP_ICHAPTEREDROWSET
DBPROP_ICOLUMNSINFO
DBPROP_ICOLUMNSROWSET
DBPROP_ICONNECTIONPOINTCONTAINER
DBPROP_ICONVERTTYPE
DBPROP_IDBASYNCHSTATUS
DBPROP_IMULTIPLERESULTS
DBPROP_IROWSET
DBPROP_IROWSETCHANGE
DBPROP_IROWSETFIND
DBPROP_IROWSETIDENTITY
DBPROP_IROWSETINDEX
DBPROP_IROWSETINFO
DBPROP_IROWSETLOCATE
DBPROP_IROWSETREFRESH
DBPROP_IROWSETSCROLL
DBPROP_IROWSETUPDATE
DBPROP_IROWSETVIEW
DBPROP_ISUPPORTERRORINFO
DBPROP_ILOCKBYTES
DBPROP_ISEQUENTIALSTREAM
DBPROP_ISTORAGE
DBPROP_ISTREAM
DBPROP_IMMOBILEROWS
DBPROP_LITERALBOOKMARKS
DBPROP_LITERALIDENTITY
DBPROP_LOCKMODE
DBPROP_MAXOPENROWS
DBPROP_MAXPENDINGROWS
DBPROP_MAXROWS
DBPROP_MAYWRITECOLUMN
DBPROP_MEMORYUSAGE
DBPROP_NOTIFICATIONGRANULARITY
DBPROP_NOTIFICATIONPHASES
DBPROP_NOTIFYCOLUMNSET
DBPROP_NOTIFYROWDELETE
DBPROP_NOTIFYROWFIRSTCHANGE
DBPROP_NOTIFYROWINSERT
DBPROP_NOTIFYROWRESYNCH
DBPROP_NOTIFYROWSETRELEASE
DBPROP_NOTIFYROWSETFETCHPOSITIONCHANGE
DBPROP_NOTIFYROWUNDOCHANGE
DBPROP_NOTIFYROWUNDODELETE
DBPROP_NOTIFYROWUNDOINSERT
DBPROP_NOTIFYROWUPDATE
DBPROP_ORDEREDBOOKMARKS
DBPROP_OTHERINSERT
DBPROP_OTHERUPDATEDELETE
DBPROP_OWNINSERT
DBPROP_OWNUPDATEDELETE
DBPROP_QUICKRESTART
DBPROP_REENTRANTEVENTS
DBPROP_REMOVEDELETED
DBPROP_REPORTMULTIPLECHANGES
DBPROP_RETURNPENDINGINSERTS
DBPROP_ROWRESTRICT
DBPROP_ROWSET_ASYNCH
DBPROP_ROWTHREADMODEL
DBPROP_SERVERCURSOR
DBPROP_STRONGIDENTITY
DBPROP_TRANSACTEDOBJECT
DBPROP_UPDATABILITY
Session | DBPROPSET_SESSION | DBPROP_SESS_AUTOCOMMITISOLEVELS
Table | DBPROPSET_TABLE | DBPROP_TBL_TEMPTABLE
View DBPROPSET_VIEW DBPROP_IVIEWCHAPTER
DBPROP_IVIEWFILTER
DBPROP_IVIEWROWSET
DBPROP_IVIEWSORT
DBPROP_IACCESSOR
DBPROP_ICOLUMNSINFO
DBPROP_ISUPPORTERRORINFO


Property Requirements Table

Property Group | Description | Other Definitions
Column | Properties used to create columns. |
Data Source | Properties that apply to data sources. These properties can be set for some providers. |
Data Source Creation | Properties used to create data sources. |
Data Source Information | Properties that describe data sources. These properties are read-only for all providers and constitute a set of static information about the provider and data source. | These properties are read-only in all providers and constitute a set of static information about the provider and data source.
Index | Properties used to create and describe indexes. |
Initialization | Properties used to initialize the data source object or enumerator. |
Rowset | Properties that apply to rowsets. |
Session | Properties that apply to sessions. |
Table | Properties used to create tables. |
View | Properties used to create views. |


Properties Table (Alphabetical)

The properties in this table are contained in the designated property sets and property groups. (For a listing of properties organized by property group, see the Property Groups table.) The “Column ?” designation for Rowset properties states whether the property can be set on individual columns or only on the rowset as a whole.

Important The “Typical R/W” value for each property specifies whether that property can typically be read or written. Whether a particular property can actually be read or written is provider-specific and is returned in the dwPropFlags element of the DBPROPINFO structure.

Property ID Description
DBPROP_ABORTPRESERVE Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_BOOL
Typical R/W: R/W
Description: Preserve on Abort

VARIANT_TRUE—After aborting a transaction, the rowset remains active. That is, it is possible to fetch new rows, update, delete, and insert rows, and so on.

VARIANT_FALSE—After aborting a transaction, the only operations allowed on a rowset are to release row and accessor handles and to release the rowset.
DBPROP_ACCESSORDER Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_I4
Typical R/W: R/W
Description: Access Order

The order in which columns must be accessed on the rowset. One of the following values:

DBPROPVAL_AO_RANDOM—Columns can be accessed in any order.

DBPROPVAL_AO_SEQUENTIALSTORAGEOBJECTS—Columns bound as storage objects can only be accessed in sequential order as determined by the column ordinal. Further, storage objects from one row must be retrieved before calling GetData on any columns in any subsequent row. Calling GetData on a column bound as a storage object returns DBSTATUS_E_UNAVAILABLE for any columns bound as storage objects if any of the following are true:

  • Columns beyond the column bound as a storage object are specified in the accessor.

  • Columns beyond the column bound as a storage object have been accessed in a previous call to GetData for that row.

  • GetData has been called for any columns on a row returned after the specified row.

Providers that never impose restrictions on column access ordering return DBPROPSTATUS_S_OK when this value is set. However, they upgrade the property to DBPROPVAL_AO_RANDOM such that calling GetProperty continues to return DBPROPVAL_AO_RANDOM for this property.

DBPROPVAL_AO_SEQUENTIAL—All columns must be accessed in sequential order determined by the column ordinal. Further, all columns from one row must be retrieved before calling GetData on any columns in any subsequent row. Calling GetData returns DBSTATUS_E_UNAVAILABLE for any columns for which any of the following are true:

  • The column is bound as a storage object and columns beyond it are specified in the accessor.

  • Columns beyond the bound column have been accessed in a previous call to GetData for that row.

  • GetData has been called for any columns on a row returned after the specified row.

Providers that never impose restrictions on column access ordering return DBPROPSTATUS_S_OK when this value is set. However, they upgrade the property to DBPROPVAL_AO_RANDOM such that calling GetProperty continues to return DBPROPVAL_AO_RANDOM for this property.

Providers may be able to optimize how data is retrieved if they know it will be read in column order. For instance, the provider may be able to read directly from a stream over the data if it knows the columns will be read in strictly sequential order, but may not be able to do so efficiently if the columns contain BLOBs that may be read in a random order.
DBPROP_ACTIVESESSIONS Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_I4
Typical R/W: R
Description: Active Sessions

The maximum number of sessions that can exist at the same time. If this property is set to zero, there is no limit on the number of sessions that can exist at one time.
DBPROP_ALTERCOLUMN Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_I4
Typical R/W: R
Description: Alter Column Support

A bitmask describing which portions of the DBCOLUMNDESC structure can be used in a call to IAlterTable::AlterColumn.

A combination of one or more of the following:

DBCOLUMNDESCFLAG_TYPENAME
DBCOLUMNDESCFLAG_ITYPEINFO
DBCOLUMNDESCFLAG_PROPERTIES
DBCOLUMNDESCFLAG_CLSID
DBCOLUMNDESCFLAG_COLSIZE
DBCOLUMNDESCFLAG_DBCID
DBCOLUMNDESCFLAG_WTYPE
DBCOLUMNDESCFLAG_PRECISION
DBCOLUMNDESCFLAG_SCALE

For more information about the mapping of these values to fields in the DBCOLUMNDESC structure, refer to the specification for IAlterTable::AlterColumn.
DBPROP_APPENDONLY Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_BOOL
Typical R/W: R/W
Description: Append-Only Rowset

A rowset opened with this property set to VARIANT_TRUE will be initially empty. If the rowset was obtained by IOpenRowset::OpenRowset, this is equivalent to positioning the start of the rowset at the end of the table; if it was obtained by executing a command, it is equivalent to placing the start of the rowset at the end of the command's results. A rowset opened with DBPROP_APPENDONLY set to VARIANT_TRUE will be populated only by those rows inserted in it.

DBPROP_APPENDONLY set to VARIANT_TRUE implies:

  • DBPROP_IRowsetChange is VARIANT_TRUE.

  • DBPROP_OWNINSERT is VARIANT_TRUE.

  • DBPROP_UPDATABILITY has the flag DBPROPVAL_UP_INSERT set.

  • DBPROP_OTHERINSERT is VARIANT_FALSE.
DBPROP_ASYNCTXNABORT Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_BOOL
Typical R/W: R
Description: Asynchable Abort

Whether transactions can be aborted asynchronously.
DBPROP_ASYNCTXNCOMMIT Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_BOOL
Typical R/W: R
Description: Asynchable Commit

Whether transactions can be committed asynchronously.
DBPROP_AUTH_CACHE_AUTHINFO Property group: Initialization
Property set: DBPROPSET_DBINIT
Type: VT_BOOL
Typical R/W: R/W
Description: Cache Authentication

VARIANT_TRUE—The data source object or enumerator is allowed to cache sensitive authentication information such as a password in an internal cache.

VARIANT_FALSE—The data source object or enumerator is not allowed to cache sensitive authentication information.
DBPROP_AUTH_ENCRYPT_PASSWORD Property group: Initialization
Property set: DBPROPSET_DBINIT
Type: VT_BOOL
Typical R/W: R/W
Description: Encrypt Password

VARIANT_TRUE—The consumer requires that the password be sent to the data source or enumerator in an encrypted form. This property specifies a stronger form of masking than DBPROP_AUTH_MASKPASSWORD; it uses cryptographic techniques.

VARIANT_FALSE—The password can be sent in an unencrypted form.
DBPROP_AUTH_INTEGRATED Property group: Initialization
Property set: DBPROPSET_DBINIT
Type: VT_BSTR
Typical R/W: R/W
Description: Integrated Security

A string containing the name of the authentication service used by the server to identify the user using the identity provided by an authentication domain. For example, for Microsoft® NT® Integrated Security, this is "SSPI" (for Security Support Provider Interface). If the BSTR is a null pointer, the default authentication service should be used. When this property is used, no other DBPROP_AUTH* properties are needed and, if provided, their values are ignored.
DBPROP_AUTH_MASK_PASSWORD Property group: Initialization
Property set: DBPROPSET_DBINIT
Type: VT_BOOL
Typical R/W: R/W
Description: Mask Password

VARIANT_TRUE—The consumer requires that the password be sent to the data source or enumerator in a masked form.

VARIANT_FALSE—The password can be sent in an unmasked form.
DBPROP_AUTH_PASSWORD Property group: Initialization
Property set: DBPROPSET_DBINIT
Type: VT_BSTR
Typical R/W: R/W
Description: Password

The password to be used when connecting to the data source or enumerator. When the value of this property is retrieved with IDBProperties::GetProperties, the provider might return a mask such as "******" or an empty string instead of the actual password. Note that the password is still set internally and is used when IDBInitialize::Initialize is called.
DBPROP_AUTH_PERSIST_ENCRYPTED Property group: Initialization
Property set: DBPROPSET_DBINIT
Type: VT_BOOL
Typical R/W: R/W
Description: Persist Encrypted

VARIANT_TRUE—The consumer requires that the data source object persist sensitive authentication information such as a password in encrypted form.

VARIANT_FALSE—The data source object can persist sensitive authentication information in unencrypted form.
DBPROP_AUTH_PERSIST_SENSITIVE_AUTHINFO Property group: Initialization
Property set: DBPROPSET_DBINIT
Type: VT_BOOL
Typical R/W: R/W
Description: Persist Security Info

VARIANT_TRUE—The data source object is allowed to persist sensitive authentication information such as a password along with other authentication information.

VARIANT_FALSE—The data source object cannot persist sensitive authentication information.

If DBPROP_AUTH_PERSIST_SENSITIVE_AUTHINFO is set to VARIANT_FALSE at the time the data source is initialized, the data source object cannot persist sensitive authentication information. Furthermore, requesting properties that contain sensitive authentication information, such as password, returns some default value instead of the actual password. Once the data source has been uninitialized, sensitive information that was set when the data source was initialized with the DBPROP_AUTH_PERSIST_SENSITIVE_AUTHINFO property set to VARIANT_FALSE still cannot be obtained through IDBProperties::GetProperties or by persisting the uninitialized data source, but new properties set after the data source has been uninitialized can be persisted or obtained as long as DBPROP_AUTH_PERSIST_SENSITIVE_AUTHINFO is VARIANT_TRUE.
DBPROP_AUTH_USERID Property group: Initialization
Property set: DBPROPSET_DBINIT
Type: VT_BSTR
Typical R/W: R/W
Description: User ID

The user ID to be used when connecting to the data source or enumerator.
DBPROP_BLOCKINGSTORAGE-OBJECTS Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_BOOL
Typical R/W: R/W
Description: Blocking Storage Objects

Whether storage objects might prevent use of other methods on the rowset.

VARIANT_TRUE—Instantiated storage objects might prevent the use of other methods on the rowset. That is, after a storage object is created and before it is released, methods other than those on the storage object might return E_UNEXPECTED.

VARIANT_FALSE—Instantiated storage objects do not prevent the use of other methods.
DBPROP_BOOKMARKINFO Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_I4
Typical R/W: R
Description: Bookmark Information

A bitmask specifying additional information about bookmarks over the rowset. A combination of zero or more of the following:

DBPROPVAL_BI_CROSSROWSET—If set, bookmark values returned by this rowset are valid across rowsets with the same metadata. If not set, bookmark values are specific to this rowset and are not guaranteed to return the same values in other rowsets, even those resulting from the same specification.
DBPROP_BOOKMARKS Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_BOOL
Typical R/W: R/W
Description: Use Bookmarks

Whether the rowset supports bookmarks. One of the following:

VARIANT_TRUE—The rowset supports bookmarks. Column 0 is the bookmark for the rows. Getting this column obtains a bookmark value, which can be used to reposition to the row.

VARIANT_FALSE—The rowset does not support bookmarks. The rowset is sequential and the values of the DBPROP_LITERALBOOKMARKS and DBPROP_ORDEREDBOOKMARKS properties are ignored.

The value of this property is automatically set to VARIANT_TRUE if the value of DBPROP_IRowsetLocate, DBPROP_LITERALBOOKMARKS, or DBPROP_ORDEREDBOOKMARKS is set to VARIANT_TRUE.
DBPROP_BOOKMARKSKIPPED Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_BOOL
Typical R/W: R/W
Description: Skip Deleted Bookmarks

Whether the rowset allows IRowsetLocate::GetRowsAt, IRowsetScroll::GetApproximatePosition, or IRowsetFind::FindNextRow to continue if a bookmark row was deleted, is a row to which the consumer does not have access rights, or is no longer a member of the rowset. One of the following:

VARIANT_TRUE—GetRowsAt, GetApproximatePosition, or FindNextRow skips the bookmark row and continues with the next row.

VARIANT_FALSE—GetRowsAt, GetApproximatePosition, or FindNextRow returns DB_E_BADBOOKMARK.
DBPROP_BOOKMARKTYPE Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_I4
Typical R/W: R/W
Description: Bookmark Type

The bookmark type supported by the rowset. One of the following:

DBPROPVAL_BMK_NUMERIC—The bookmark type is numeric. Numeric bookmarks are based upon a row property that is not dependent on the values of the row's columns. For instance, they can be based on the absolute position of the row within a rowset, or on a row ID that the storage engine assigned to a tuple at its creation. The validity of numeric bookmarks is not changed by modifying the row's columns.

DBPROPVAL_BMK_KEY—The bookmark type is key. Key bookmarks are based on the values of one or more of the row's columns; these values form a unique key for each row. A key bookmark may be left dangling if the key values of the corresponding row are changed.

For more information, see "Bookmark Types" in Chapter 4.
DBPROP_BYREFACCESSORS Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_BOOL
Typical R/W: R
Description: Pass By Ref Accessors

Whether the provider supports the DBACCESSOR_PASSBYREF flag in IAccessor::CreateAccessor. This applies to both row and parameter accessors.
DBPROP_CACHEDEFERRED Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? Y
Type: VT_BOOL
Typical R/W: R/W
Description: Cache Deferred Columns

VARIANT_TRUE—The provider caches the value of a deferred column when the consumer first gets a value from that column. When the consumer later gets a value from the column, the provider returns the value in the cache. The contents of the cache can be overwritten by IRowsetChange::SetData or IRowsetRefresh::RefreshVisibleData. The cached value is released when the row is released.

Consumers should set the value of this property to VARIANT_TRUE rather sparingly, because it might require substantial memory use in the provider. Such use might limit the number of rows that can be held at one time. Setting the value of this property to VARIANT_TRUE automatically sets the value of the DBPROP_DEFERRED property to VARIANT_TRUE.

VARIANT_FALSE—The provider does not cache the value of a deferred column, and multiple calls to IRowset::GetData for the column can return different values.
DBPROP_CANFETCHBACKWARDS Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_BOOL
Typical R/W: R/W
Description: Fetch Backward

Whether the rowset can fetch backward.

VARIANT_TRUE—cRows in IRowset::GetNextRows, IRowsetLocate::GetRowsAt, and IRowsetScroll::GetRowsAtRatio can be negative. When it is negative, these methods fetch rows backward from the specified row.

VARIANT_FALSE—cRows must be non-negative.
DBPROP_CANHOLDROWS Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_BOOL
Typical R/W: R/W
Description: Hold Rows

VARIANT_TRUE—The rowset allows the consumer to retrieve more rows or change the next fetch position while holding previously fetched rows with pending changes.

VARIANT_FALSE—The rowset requires pending changes to be transmitted to the data source and all rows to be released before fetching additional rows, inserting new rows, or changing the next fetch position.
DBPROP_CANSCROLLBACKWARDS Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_BOOL
Typical R/W: R/W
Description: Scroll Backward

Whether the rowset can scroll backward.

VARIANT_TRUE—lRowsOffset in IRowset::GetNextRows or IRowsetLocate::GetRowsAt can be negative.

VARIANT_FALSE—lRowsOffset must be non-negative.

If the rowset supports IRowsetLocate, then the value of this property is VARIANT_TRUE, because this method supports backward scrolling by definition.
DBPROP_CATALOGLOCATION Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_I4
Typical R/W: R
Description: Catalog Location

The position of the catalog name in a qualified table name in a text command. One of the following:

DBPROPVAL_CL_START—The catalog name is at the start of the fully qualified name. For example, a dBASE® provider returns DBPROPVAL_CL_START because the directory (catalog name) is at the start of the table name, as in \EMPDATA\EMP.DBF.

DBPROPVAL_CL_END—The catalog name is at the end of the fully qualified name. For example, an ORACLE server provider returns DBPROPVAL_CL_END because the catalog name is at the end of the table name, as in ADMIN.EMP@EMPDATA.
DBPROP_CATALOGTERM Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_BSTR
Typical R/W: R
Description: Catalog Term

The name the data source uses for a catalog; for example, "catalog", "database", or "directory". This is used for building user interfaces.
DBPROP_CATALOGUSAGE Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_I4
Typical R/W: R
Description: Catalog Usage

A bitmask specifying how catalog names can be used in text commands. A combination of zero or more of the following:

DBPROPVAL_CU_DML_STATEMENTS—Catalog names are supported in all Data Manipulation Language statements.

DBPROPVAL_CU_TABLE_DEFINITION—Catalog names are supported in all table definition statements.

DBPROPVAL_CU_INDEX_DEFINITION—Catalog names are supported in all index definition statements.

DBPROPVAL_CU_PRIVILEGE_DEFINITION—Catalog names are supported in all privilege definition statements.
DBPROP_CHANGEINSERTEDROWS Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_BOOL
Typical R/W: R/W
Description: Change Inserted Rows

VARIANT_TRUE—The consumer can call IRowsetChange::DeleteRows or IRowsetChange::SetData for newly inserted rows.

VARIANT_FALSE—If the consumer calls DeleteRows or SetData for newly inserted rows, DeleteRows returns a status of DBROWSTATUS_E_NEWLYINSERTED for the row and SetData returns DB_E_NEWLYINSERTED.

A newly inserted row is defined to be a row for which the insertion has been transmitted to the data source, as opposed to a pending insert row. For some providers this property may imply other properties.
DBPROP_COL_AUTOINCREMENT Property group: Column
Property set: DBPROPSET_COLUMN
Type: VT_BOOL
Typical R/W: R/W
Description: Autoincrement

Whether the values of the column are autoincrementing. One of the following:

VARIANT_TRUE—The values of the column are autoincrementing.

VARIANT_FALSE—The values of the column are not autoincrementing.

If this property is read-only, then whether or not a column is autoincrementing is a property of the datatype. If this property is read/write, then DBPROP_COL_AUTOINCREMENT can be specified for the column provided the AUTO_UNIQUE_VALUE property of the underlying datatype is VARIANT_TRUE in the PROVIDER_TYPES schema rowset.
DBPROP_COL_DEFAULT Property group: Column
Property set: DBPROPSET_COLUMN
Type: Any
Typical R/W: R/W
Description: Default

A VARIANT specifying the default value for an object—typically a domain or column. If the default value is a string, the string must be quoted so it can be distinguished from an object of the same name. For example, 'Salary' is a string, but Salary is an object, such as a column.
DBPROP_COL_DESCRIPTION Property group: Column
Property set: DBPROPSET_COLUMN
Type: VT_BSTR
Typical R/W: R/W
Description: Description

A string specifying a human-readable description of the specified column.
DBPROP_COL_FIXEDLENGTH Property group: Column
Property set: DBPROPSET_COLUMN
Type: VT_BOOL
Typical R/W: R/W
Description: Fixed Length

Whether a column is fixed- or variable-length.

If the value of this property is VARIANT_TRUE, the column is fixed length and ulColumnSize in the DBCOLUMNDESC structure contains the fixed-length value. If the value of this property is VARIANT_FALSE or is not specified, the column is variable length and ulColumnSize represents the maximum size of the column. For information about the DBCOLUMNDESC structure, see ITableDefinition::CreateTable.
DBPROP_COL_NULLABLE Property group: Column
Property set: DBPROPSET_COLUMN
Type: VT_BOOL
Typical R/W: R/W
Description: Nullable

Whether a column can contain a NULL value. One of the following:

VARIANT_TRUE—The column can contain NULL values.

VARIANT_FALSE—The column cannot contain NULL values.
DBPROP_COL_PRIMARYKEY Property group: Column
Property set: DBPROPSET_COLUMN
Type: VT_BOOL
Typical R/W: R/W
Description: Primary Key

Whether the column is part of the primary key. One of the following:

VARIANT_TRUE—The column is part of the primary key of the table.

VARIANT_FALSE—The column is not part of the primary key of the table.
DBPROP_COL_UNIQUE Property group: Column
Property set: DBPROPSET_COLUMN
Type: VT_BOOL
Typical R/W: R/W
Description: Unique

Whether values of the column must be unique in the table. One of the following:

VARIANT_TRUE—The values of the column must be unique within the table.

VARIANT_FALSE—The values of the column can be repeated within the table.
DBPROP_COLUMNDEFINITION Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_I4
Typical R/W: R
Description: Column Definition

A bitmask defining the valid clauses for the definition of a column. A combination of zero or more of the following:

DBPROPVAL_CD_NOTNULL—Columns can be created non-nullable.
DBPROP_COLUMNLCID Property group: Column
Property set: DBPROPSET_COLUMN
Type: VT_I4
Typical R/W: R
Description: Column LCID

The locale ID of the column.
DBPROP_COLUMNRESTRICT Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_BOOL
Typical R/W: R
Description: Column Privileges

VARIANT_TRUE—Access rights are restricted on a column-by-column basis. If the rowset exposes IRowsetChange, IRowsetChange::SetData cannot be called for at least one column. A provider must not execute a query that would specify a column for which the consumer has no read access rights.

VARIANT_FALSE—Access rights are not restricted on a column-by-column basis. If the rowset exposes IRowsetChange, SetData can be called for any column in the rowset.

If access is restricted both by row and by column, then individual columns of particular rows might have their own stricter access rights: the consumer might not even be permitted to read such columns. The column values will be NULL. If a NULL value is contrary to schema rules (NULLs not permitted) then the rowset should not count or return any rows that would have this condition. For more information, see DBPROP_ROWRESTRICT.
DBPROP_COMMANDTIMEOUT Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_I4
Typical R/W: R/W
Description: Command Time Out

The number of seconds before a command times out. A value of 0 indicates an infinite timeout.
DBPROP_COMMITPRESERVE Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_BOOL
Typical R/W: R/W
Description: Preserve on Commit

VARIANT_TRUE—After committing a transaction, the rowset remains active. That is, it is possible to fetch new rows, update, delete, and insert rows, and so on.

VARIANT_FALSE—After committing a transaction, the only operations allowed on a rowset are to release row and accessor handles and to release the rowset.
DBPROP_CONCATNULLBEHAVIOR Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_I4
Typical R/W: R
Description: NULL Concatenation Behavior

How the data source handles the concatenation of NULL-valued character data type columns with non–NULL-valued character data type columns. One of the following:

DBPROPVAL_CB_NULL—The result is NULL valued.

DBPROPVAL_CB_NON_NULL—The result is the concatenation of the non–NULL-valued column or columns.
DBPROP_CONNECTIONSTATUS Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_I4
Typical R/W: R
Description: Connection Status

The status of the current connection. One of the following:

DBPROPVAL_CS_UNINITIALIZED—The DSO is in an uninitialized state.

DBPROPVAL_CS_INITIALIZED—The DSO is in an initialized state and able to communicate with the data store.

DBPROPVAL_CS_COMMUNICATIONFAILURE—The DSO is unable to communicate with the data store.

Some providers may have to silently spawn multiple connections to the database in order to support multiple concurrent Commands, Sessions, and Rowsets, according to DBPROP_MULTIPLECONNECTIONS. For such providers, DBPROP_CONNECTIONSTATUS refers to the status of the primary connection, which was created when the user called Initialize.
DBPROP_CURRENTCATALOG Property group: Data Source
Property set: DBPROPSET_DATASOURCE
Type: VT_BSTR
Typical R/W: R/W
Description: Current Catalog

The name of the current catalog. The consumer can use the CATALOGS schema rowset to enumerate catalogs.
DBPROP_DATASOURCENAME Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_BSTR
Typical R/W: R
Description: Data Source Name

The name of the data source. This might be used during the connection process.
DBPROP_DATASOURCEREADONLY Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_BOOL
Typical R/W: R
Description: Read-Only Data Source

VARIANT_TRUE—The data source is read-only.

VARIANT_FALSE—The data source is updatable.
DBPROP_DBMSNAME Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_BSTR
Typical R/W: R
Description: DBMS Name

The name of the product accessed by the provider. For example, "ORACLE Server" or, for Microsoft® Excel®, "Excel".
DBPROP_DBMSVER Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_BSTR
Typical R/W: R
Description: DBMS Version

The version of the product accessed by the provider. The version is of the form ##.##.####, where the first two digits are the major version, the next two digits are the minor version, and the last four digits are the release version. The provider must render the product version in this form but can also append the product-specific version. For example, "04.01.0000 Rdb 4.1".
DBPROP_DEFERRED Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? Y
Type: VT_BOOL
Typical R/W: R/W
Description: Defer Column

VARIANT_TRUE—The data in the column is not fetched until an accessor is used on the column.

VARIANT_FALSE—The data in the column is fetched when the row containing it is fetched.

The value of this property is automatically set to VARIANT_TRUE if the value of the DBPROP_CACHEDEFERRED property is set to VARIANT_TRUE.
DBPROP_DELAYSTORAGEOBJECTS Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_BOOL
Typical R/W: R/W
Description: Delay Storage Object Updates

In delayed update mode, if the value of this property is VARIANT_TRUE then storage objects are also used in delayed update mode. In particular:

  • Changes to the object are not transmitted to the data source until IRowsetUpdate::Update is called,

  • IRowsetUpdate::Undo undoes any pending changes, and

  • IRowsetUpdate::GetOriginalData retrieves the original value of the object; that is, the object's value when the row was last fetched or updated and excluding any changes made since then.

In delayed update mode, if the value of this property is VARIANT_FALSE, then storage objects are used in immediate update mode. In particular:

  • Changes to the object are immediately transmitted to the data source.

  • Update has no effect on the object.

  • Undo does not undo changes made to the object since the row was last fetched or updated.

  • GetOriginalData retrieves the current value of the object, including changes made since the row was last fetched or updated.

  • In immediate update mode, this property has no effect on storage objects.
DBPROP_DSOTHREADMODEL Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_I4
Typical R/W: R
Description: Data Source Object Threading Model

A bitmask specifying the threading models supported by the data source object. A combination of one or more of the following:

DBPROPVAL_RT_FREETHREAD
DBPROPVAL_RT_APTMTTHREAD
DBPROPVAL_RT_SINGLETHREAD

Note that the threading model returned in this property may be stricter than the thread model registered under the provider's CLSID in the case where additional threading limitations are imposed by underlying components.
DBPROP_FILTERCOMPAREOPS Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? Y
Type: VT_I4
Typical R/W: R
Description: Filter Operations

A bitmask describing the comparison operations supported by IViewFilter for a particular column. If no column is specified, this is the full set of comparison operators that may be supported:

DBPROPVAL_CO_EQUALITY — Provider supports the following comparison operators:
DBCOMPAREOPS_LT
DBCOMPAREOPS_LE
DBCOMPAREOPS_EQ
DBCOMPAREOPS_GE
DBCOMPAREOPS_GT
DBCOMPAREOPS_NE

DBPROPVAL_CO_STRING — Provider supports the following comparison operator:
DBCOMPAREOPS_BEGINSWITH

DBPROPVAL_CO_CONTAINS—Provider supports the following comparison operators:
DBCOMPAREOPS_CONTAINS
DBCOMPAREOPS_NOTCONTAINS

DBPROPVAL_CO_BEGINSWITH—Provider supports the following comparison operators:
DBCOMPAREOPS_BEGINSWITH
DBCOMPAREOPS_NOTBEGINSWITH

DBPROPVAL_CO_CASESENSITIVE — Provider supports the DBCOMPAREOPS_CASESENSITIVE modifier.

DBPROPVAL_CO_CASEINSENSITIVE — Provider supports the DBCOMPAREOPS_CASEINSENSITIVE modifier.

  • Providers may support one, or the other, or both of DBPROPVAL_CO_CASESENSITIVE and DBPROPVAL_CO_CASEINSENSITIVE.
DBPROP_FINDCOMPAREOPS Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? Y
Type: VT_I4
Typical R/W: R
Description: Find Operations

A bitmask describing the comparison operations supported by IRowsetFind for a particular column. If no column is specified, this is the full set of comparison operators that may be supported:

DBPROPVAL_CO_EQUALITY — Provider supports the following comparison operators:
DBCOMPAREOPS_LT
DBCOMPAREOPS_LE
DBCOMPAREOPS_EQ
DBCOMPAREOPS_GE
DBCOMPAREOPS_GT
DBCOMPAREOPS_NE

DBPROPVAL_CO_STRING — Provider supports the following comparison operators:
DBCOMPAREOPS_BEGINSWITH

DBPROPVAL_CO_CONTAINS—Provider supports the following comparison operators:
DBCOMPAREOPS_CONTAINS
DBCOMPAREOPS_NOTCONTAINS

DBPROPVAL_CO_BEGINSWITH—Provider supports the following comparison operators:
DBCOMPAREOPS_BEGINSWITH
DBCOMPAREOPS_NOTBEGINSWITH

DBPROPVAL_CO_CASESENSITIVE — Provider supports the DBCOMPAREOPS_CASESENSITIVE modifier.

DBPROPVAL_CO_CASEINSENSITIVE — Provider supports the DBCOMPAREOPS_CASEINSENSITIVE modifier.

All providers that support IRowsetFind must support DBPROPVAL_CO_EQUALITY and DBPROPVAL_CO_STRING.

Providers may support one, or the other, or both of DBPROPVAL_CO_CASESENSITIVE and DBPROPVAL_CO_CASEINSENSITIVE.

DBPROP_GROUPBY Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_I4
Typical R/W: R
Description: GROUP BY Support

The relationship between the columns in a GROUP BY clause and the nonaggregated columns in the select list. One of the following:

DBPROPVAL_GB_EQUALS_SELECT—The GROUP BY clause must contain all nonaggregated columns in the select list. It cannot contain any other columns. For example, SELECT DEPT, MAX(SALARY) FROM EMPLOYEE GROUP BY DEPT.

DBPROPVAL_GB_COLLATE—A COLLATE clause can be specified at the end of each grouping column.

DBPROPVAL_GB_CONTAINS_SELECT—The GROUP BY clause must contain all nonaggregated columns in the select list. It can contain columns that are not in the select list. For example, SELECT DEPT, MAX(SALARY) FROM EMPLOYEE GROUP BY DEPT, AGE.

DBPROPVAL_GB_NO_RELATION—The columns in the GROUP BY clause and the select list are not related. The meaning of nongrouped, nonaggregated columns in the select list is data source–dependent. For example, SELECT DEPT, SALARY FROM EMPLOYEE GROUP BY DEPT, AGE.
DBPROP_HETEROGENEOUSTABLES Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_I4
Typical R/W: R
Description: Heterogeneous Table Support

A bitmask specifying whether the provider can join tables from different catalogs or providers. A combination of zero or more of the following:

DBPROPVAL_HT_DIFFERENT_CATALOGS
DBPROPVAL_HT_DIFFERENT_PROVIDERS
DBPROP_IACCESSOR
DBPROP_ICHAPTEREDROWSET
DBPROP_ICOLUMNSINFO
DBPROP_ICOLUMNSROWSET
DBPROP_ICONNECTIONPOINTCONTAINER
DBPROP_ICONVERTTYPE
DBPROP_IDBASYNCHSTATUS
DBPROP_IMULTIPLERESULTS
DBPROP_IROWSET
DBPROP_IROWSETCHANGE
DBPROP_IROWSETFIND
DBPROP_IROWSETIDENTITY
DBPROP_IROWSETINDEX
DBPROP_IROWSETINFO
DBPROP_IROWSETLOCATE
DBPROP_IROWSETREFRESH
DBPROP_IROWSETSCROLL
DBPROP_IROWSETUPDATE
DBPROP_IROWSETVIEW
DBPROP_ISUPPORTERRORINFO 		Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_BOOL
Typical R/W: R/W (except as noted below)
Description:IAccessor
IChapteredRowset
IColumnsInfo
IColumnsRowset
IConnectionPointContainer
IConvertType
IDBAsynchStatus
IMultipleResults
IRowset
IRowsetChange
IRowsetFind
IRowsetIdentity
IRowsetIndex
IRowsetInfo
IRowsetLocate
IRowsetRefresh
IRowsetScroll
IRowsetUpdate
IRowsetView
ISupportErrorInfo

If the value of any of these properties is set to VARIANT_TRUE, the rowset supports the specified interface. Providers that support an interface must support the property associated with that interface with a value of VARIANT_TRUE. These properties are primarily used to request interfaces through ICommandProperties::SetProperties.

The values of the DBPROP_IRowset, DBPROP_IAccessor, DBPROP_IColumnsInfo, DBPROP_IConvertType, and DBPROP_IRowsetInfo properties are read-only and are always VARIANT_TRUE. They cannot be set to VARIANT_FALSE. If the consumer does not set the value of any of these properties to true, the resulting rowset supports IRowset, IAccessor, IColumnsInfo, IConvertType, and IRowsetInfo.

Setting DBPROP_IRowsetLocate to VARIANT_TRUE implicitly causes the created rowset to support bookmarks and IRowsetInfo::GetProperties will return VARIANT_TRUE for the property DBPROP_BOOKMARKS. Setting DBPROP_IRowsetUpdate to VARIANT_TRUE automatically sets DBPROP_IRowsetChange to VARIANT_TRUE.
DBPROP_IDENTIFIERCASE Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_I4
Typical R/W: R
Description: Identifier Case Sensitivity

How identifiers treat case. One of the following:

DBPROPVAL_IC_UPPER—Identifiers in SQL are case insensitive and are stored in uppercase in system catalog.

DBPROPVAL_IC_LOWER—Identifiers in SQL are case insensitive and are stored in lowercase in system catalog.

DBPROPVAL_IC_SENSITIVE—Identifiers in SQL are case sensitive and are stored in mixed case in system catalog.

DBPROPVAL_IC_MIXED—Identifiers in SQL are case insensitive and are stored in mixed case in system catalog.
DBPROP_ILOCKBYTES
DBPROP_ISEQUENTIALSTREAM
DBPROP_ISTORAGE
DBPROP_ISTREAM 		Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? Y
Type: VT_BOOL
Typical R/W: R/W
Description:ILockBytes
ISequentialStream
IStorage
IStream

If the value of this property is set to VARIANT_TRUE, then the rowset is capable of manipulating the contents of columns as a storage object supporting the specified interface. The provider reports its ability to enable this property on a per-column basis by setting the flag DBPROPFLAGS_COLUMNOK. A provider that does not have the ability to turn the property on/off on a per-column basis, does not set DBPROPFLAGS_COLUMNOK. Regardless of whether the property is supported in the rowset as a whole or on a per-column basis, the ability to manipulate a column value as a storage object depends on whether the provider supports the coercion from the column's native type (BLOB or non-BLOB) to the particular storage interface.
DBPROP_IMMOBILEROWS Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_BOOL
Typical R/W: R/W
Description: Immobile Rows

VARIANT_TRUE—The rowset will not reorder inserted or updated rows. For IRowsetChange::InsertRow, rows will appear at the end of the rowset.

VARIANT_FALSE—If the rowset is ordered, then inserted rows and updated rows (where one or more of the columns in the ordering criteria are updated) obey the ordering criteria of the rowset. If the rowset is not ordered, then inserted rows are not guaranteed to appear in a determinate position, and the position of updated rows is not changed.

This property is meaningful only if DBPROP_OWNINSERT is VARIANT_TRUE.
DBPROP_INDEX_AUTOUPDATE Property group: Index
Property set: DBPROPSET_INDEX
Type: VT_BOOL
Typical R/W: R/W
Description: Auto-Update

Whether the index is maintained automatically when changes are made to the corresponding base table. One of the following:

VARIANT_TRUE—The index is automatically maintained.

VARIANT_FALSE—The index must be maintained by the consumer through explicit calls to IRowsetChange.

Ensuring consistency of the index as a result of updates to the associated base table is the responsibility of the consumer.

VARIANT_TRUE is always returned when using integrated indexes.
DBPROP_INDEX_CLUSTERED Property group: Index
Property set: DBPROPSET_INDEX
Type: VT_BOOL
Typical R/W: R/W
Description: Clustered

Whether an index is clustered.

VARIANT_TRUE—The leaf nodes of the index contain full rows, not bookmarks. This is a way to represent a table clustered by key value.

VARIANT_FALSE—The leaf nodes of the index contain bookmarks of the base table rows whose key value matches the key value of the index entry.
DBPROP_INDEX_FILLFACTOR Property group: Index
Property set: DBPROPSET_INDEX
Type: VT_I4
Typical R/W: R/W
Description: Fill Factor

For a B+-tree index, this property represents the storage utilization factor of page nodes during the creation of the index. The value is an integer from 1 to 100 representing the percentage of use of an index node. For a linear hash index, this property represents the storage utilization of the entire hash structure (the ratio of the used area to the total allocated area) before a file structure expansion occurs.
DBPROP_INDEX_INITIALSIZE Property group: Index
Property set: DBPROPSET_INDEX
Type: VT_I4
Typical R/W: R/W
Description: Initial Size

The total number of bytes allocated to this structure at creation time.
DBPROP_INDEX_NULLCOLLATION Property group: Index
Property set: DBPROPSET_INDEX
Type: VT_I4
Typical R/W: R/W
Description: NULL Collation

How NULLs are collated in the index. One of the following:

DBPROPVAL_NC_END—NULLs are collated at the end of the list, regardless of the collation order.

DBPROPVAL_NC_START—NULLs are collated at the start of the list, regardless of the collation order.

DBPROPVAL_NC_HIGH—NULLs are collated at the high end of the list.

DBPROPVAL_NC_LOW—NULLs are collated at the low end of the list.
DBPROP_INDEX_NULLS Property group: Index
Property set: DBPROPSET_INDEX
Type: VT_I4
Typical R/W: R/W
Description: NULL Keys

Whether NULL keys are allowed. One of the following values:

DBPROPVAL_IN_DISALLOWNULL—The index does not allow entries where the key columns are NULL. If the consumer attempts to insert an index entry with a NULL key, then the provider returns an error.

DBPROPVAL_IN_IGNORENULL—The index does not insert entries containing NULL keys. If the consumer attempts to insert an index entry with a NULL key, then the provider ignores that entry and no error code is returned.

DBPROPVAL_IN_IGNOREANYNULL—The index does not insert entries where some column key has a NULL value. For an index having a multi-column search key, if the consumer inserts an index entry with NULL value in some column of the search key, then the provider ignores that entry and no error code is returned.
DBPROP_INDEX_PRIMARYKEY Property group: Index
Property set: DBPROPSET_INDEX
Type: VT_BOOL
Typical R/W: R/W
Description: Primary Key

Whether the index represents the primary key on the table.
DBPROP_INDEX_SORTBOOKMARKS Property group: Index
Property set: DBPROPSET_INDEX
Type: VT_BOOL
Typical R/W: R/W
Description: Sort Bookmarks

How the index treats repeated keys. One of the following:

VARIANT_TRUE—The index sorts repeated keys by bookmark.

VARIANT_FALSE—The index does not sort repeated keys by bookmark.
DBPROP_INDEX_TEMPINDEX Property group: Index
Property set: DBPROPSET_INDEX
Type: VT_BOOL
Typical R/W: R/W
Description: Temporary Index

Whether the index is temporary. One of the following:

VARIANT_TRUE—The index is destroyed when the session is released.

VARIANT_FALSE—The index is created permanently.
DBPROP_INDEX_TYPE Property group: Index
Property set: DBPROPSET_INDEX
Type: VT_I4
Typical R/W: R/W
Description: Index Type

The type of the index. One of the following:

DBPROPVAL_IT_BTREE—The index is a B+-tree.

DBPROPVAL_IT_HASH—The index is a hash file using linear or extensible hashing.

DBPROPVAL_IT_CONTENT—The index is a content index.

DBPROPVAL_IT_OTHER—The index is some other type of index.
DBPROP_INDEX_UNIQUE Property group: Index
Property set: DBPROPSET_INDEX
Type: VT_BOOL
Typical R/W: R/W
Description: Unique

Whether index keys must be unique.

VARIANT_TRUE: The index keys must be unique.

VARIANT_FALSE: Duplicate keys are allowed.
DBPROP_INIT_ASYNCH Property group: Initialization
Property set: DBPROPSET_DBINIT
Type: VT_I4
Typical R/W: R/W
Description: Asynchronous Processing

A bitmask specifying the asynchronous processing performed on the data source. A combination of zero or more of the following

DBPROPVAL_ASYNCH_INITIALIZE: IDBInitialize::Initialize returns immediately, but the actual initialization of the data source is done asynchronously. The data source behaves as an unititialized data source prior to completing the initialization process, except that any call to IDBInitialize returns E_UNEXPECTED.

If no bits are set (the default), IDBInitialize::Initialize does not return until the data source is completely initialized.
DBPROP_INIT_CATALOG Property group: Initialization
Property set: DBPROPSET_DBINIT
Typical R/W: R/W
Description: Initial Catalog

The name of the initial, or default, catalog to use when connecting to the data source. If the provider supports changing the catalog for an initialized data source, the consumer can specify a different catalog name through the DBPROP_CURRENCATALOG property in the DBPROPSET_DATASOURCE property set after initialization.
DBPROP_INIT_DATASOURCE Property group: Initialization
Property set: DBPROPSET_DBINIT
Type: VT_BSTR
Typical R/W: R/W
Description: Data Source

The name of the database or enumerator to connect to. DBPROP_INIT_DATASOURCE is used to identify the data source to connect to, for instance a relational database server or a local file. If the provider uses two-part naming to identify the data source, then the data source name is qualified with the location specified in DBPROP_INIT_DATASOURCE.
DBPROP_INIT_HWND Property group: Initialization
Property set: DBPROPSET_DBINIT
Type: VT_I4
Typical R/W: R/W
Description: Window Handle

The window handle to be used if the data source object or enumerator needs to prompt for additional information.
DBPROP_INIT_IMPERSONATION_LEVEL Property group: Initialization
Property set: DBPROPSET_DBINIT
Type: VT_I4
Typical R/W: R/W
Description: Impersonation Level

The level of impersonation that the server is allowed to use when impersonating the client. This property applies only to network connections other than Remote Procedure Call (RPC) connections; these impersonation levels are similar to those provided by RPC. The values of this property correspond directly to the levels of impersonation that can be specified for authenticated RPC connections, but can be applied to connections other than authenticated RPC. One of the following:

DB_IMP_LEVEL_ANONYMOUS—The client is anonymous to the server. The server process cannot obtain identification information about the client and cannot impersonate the client.

DB_IMP_LEVEL_IDENTIFY—The server can obtain the client's identity. The server can impersonate the client for ACL checking but cannot access system objects as the client.

DB_IMP_LEVEL_IMPERSONATE—The server process can impersonate the client's security context while acting on behalf of the client. This information is obtained when the connection is established, not on every call.

DB_IMP_LEVEL_DELEGATE—The process can impersonate the client's security context while acting on behalf of the client. The server process can also make outgoing calls to other servers while acting on behalf of the client.
DBPROP_INIT_LCID Property group: Initialization
Property set: DBPROPSET_DBINIT
Type: VT_I4
Typical R/W: R/W
Description: Locale Identifier

The locale ID of preference for the consumer. Consumers specify the LCID at initialization. This provides a method for the server to determine the consumer's LCID of choice in cases where it can use this information. This property does not guarantee that all text returned to the consumer will be translated according to the LCID.

Providers may wish to set the dwOptions of the DBPROP structure for this property to DBPROPOPTIONS_OPTIONAL.
DBPROP_INIT_LOCATION Property group: Initialization
Property set: DBPROPSET_DBINIT
Type: VT_BSTR
Typical R/W: R/W
Description: Location

The location of the data source or enumerator to connect to. Typically, this will be a server name. DBPROP_INIT_LOCATION is used as the first part of a two-part name to qualify the data source specified in the DBPROP_INIT_DATASOURCE property. For example, if the data source is defined on a different machine, this might be the machine name on which to look for the data source definition. This is typically not used if the provider can identify the data source using a single name, such as the name of an RDBMS server, that the consumer can use to identify the data source directly.
DBPROP_INIT_MODE Property group: Initialization
Property set: DBPROPSET_DBINIT
Type: VT_I4
Typical R/W: R/W
Description: Mode

A bitmask specifying access permissions. A combination of zero or more of the following:

DB_MODE_READ—Read-only.

DB_MODE_WRITE—Write-only.

DB_MODE_READWRITE—Read/write (DB_MODE_READ | DB_MODE_WRITE).

DB_MODE_SHARE_DENY_READ—Prevents others from opening in read mode.

DB_MODE_SHARE_DENY_WRITE—Prevents others from opening in write mode.

DB_MODE_SHARE_EXCLUSIVE—Prevents others from opening in read/write mode (DB_MODE_SHARE_DENY_READ | DB_MODE_SHARE_DENY_WRITE).

DB_MODE_SHARE_DENY_NONE—Neither read nor write access can be denied to others.
DBPROP_INIT_OLEDBSERVICES Property group: Initialization
Property set: DBPROPSET_DBINIT
Type: VT_I4
Typical R/W: R/W
Description: OLE DB Services

A bitmask specifying OLE DB services to enable. A combination of zero or more of the following:

DBPROPVAL_OS_RESOURCEPOOLING—Resources should be pooled.

DBPROPVAL_OS_TXNENLISTMENT—Sessions in an MTS environment should automatically be enlisted in a global transaction where required (implies DBPROPVAL_OS_RESOURCEPOOLING).

DBPROPVAL_OS_ENABLEALL (default)—All services should be invoked. By default, all services are enabled and invoked as requested. Individual services can be deselected by specifying the bitwise-OR 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 an MTS environment.
DBPROP_INIT_PROMPT Property group: Initialization
Property set: DBPROPSET_DBINIT
Type: VT_I2
Typical R/W: R/W
Description: Prompt

Whether to prompt the user during initialization. One of the following values:

DBPROMPT_PROMPT—Always prompt the user for initialization information.

DBPROMPT_COMPLETE—Prompt the user only if more information is needed.

DBPROMPT_COMPLETEREQUIRED—Prompt the user only if more information is needed. Do not allow the user to enter optional information.

DBPROMPT_NOPROMPT—Do not prompt the user.

Information obtained from the user during prompting is available following initialization by calling GetProperties for the appropriate initialization properties.
DBPROP_INIT_PROTECTION_LEVEL Property group: Initialization
Property set: DBPROPSET_DBINIT
Type: VT_I4
Typical R/W: R/W
Description: Protection Level

The level of protection of data sent between client and server. This property applies only to network connections other than RPC connections; these protection levels are similar to those provided by RPC. The values of this property correspond directly to the levels of protection that can be specified for authenticated RPC connections, but can be applied to connections other than authenticated RPC. One of the following:

DB_PROT_LEVEL_NONE—Performs no authentication of data sent to the server.

DB_PROT_LEVEL_CONNECT—Authenticates only when the client establishes the connection with the server.

DB_PROT_LEVEL_CALL—Authenticates the source of the data at the beginning of each request from the client to the server.

DB_PROT_LEVEL_PKT—Authenticates that all data received is from the client.

DB_PROT_LEVEL_PKT_INTEGRITY—Authenticates that all data received is from the client and that it has not been changed in transit.

DB_PROT_LEVEL_PKT_PRIVACY—Authenticates that all data received is from the client, that it has not been changed in transit, and protects the privacy of the data by encrypting it.
DBPROP_INIT_PROVIDERSTRING Property group: Initialization
Property set: DBPROPSET_DBINIT
Type: VT_BSTR
Typical R/W: R/W
Description: Extended Properties

A string containing provider-specific, extended connection information. Use of this property implies that the consumer knows how this string will be interpreted and used by the provider. Consumers should use this property only for provider-specific connection information that cannot be explicitly described through the property mechanism.
DBPROP_INIT_TIMEOUT Property group: Initialization
Property set: DBPROPSET_DBINIT
Type: VT_I4
Typical R/W: R/W
Description: Connect Timeout

The amount of time (in seconds) to wait for initialization to complete.

For maximum user concurrency and component interoperability, providers for whom initialization is a potentially lengthy operation, such as connection across a network, are strongly encouraged to support this property.
DBPROP_IVIEWCHAPTER
DBPROP_IVIEWFILTER
DBPROP_IVIEWROWSET
DBPROP_IVIEWSORT
DBPROP_IACCESSOR
DBPROP_ICOLUMNSINFO
DBPROP_ISUPPORTERRORINFO
 Property group: View
Property set: DBPROPSET_VIEW
Column? N
Type: VT_BOOL
Typical R/W: R/W
Description:IViewChapter
IViewFilter
IViewRowset
IViewSort
IAccessor
IColumnsInfo
ISupportErrorInfo

For all methods returning a Rowset or View object, in addition to Rowset properties, DBPROPSET_VIEW should be set before creating a view.
DBPROP_LITERALBOOKMARKS Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_BOOL
Typical R/W: R/W
Description: Literal Bookmarks

VARIANT_TRUE—Bookmarks can be compared literally. That is, they can be compared as a sequence of bytes. Furthermore, if the bookmarks are ordered (as specified by the DBPROP_ORDEREDBOOKMARKS property), the bytes are guaranteed to be ordered so that an arithmetic comparison as their scalar type yields the same result as a call to IRowsetLocate::Compare. Setting the value of this property to VARIANT_TRUE automatically sets the value of DBPROP_BOOKMARKS to VARIANT_TRUE.

VARIANT_FALSE—Bookmarks can only be compared with IRowsetLocate::Compare.
DBPROP_LITERALIDENTITY Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_BOOL
Typical R/W: R
Description: Literal Row Identity

VARIANT_TRUE—The consumer can perform a binary comparison of two row handles to determine whether they point to the same row.

VARIANT_FALSE—The consumer must call IRowsetIdentity::IsSameRow to determine whether two row handles point to the same row.

Whether the handle of a newly inserted row can be successfully compared to another handle is specified by the DBPROP_STRONGIDENTITY property.

For more information about row identity, see "Uniqueness of Rows in the Rowset" in Chapter 4.
DBPROP_LOCKMODE Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_I4
Typical R/W: R/W
Description: Lock Mode

The level of locking peformed by the rowset. One of the following:

DBPROPVAL_LM_NONE—The provider is not required to lock rows at any time to ensure successful updates. Updates may fail when sent to the server for reasons of concurrency (for example, if someone else has updated the row).

DBPROPVAL_LM_SINGLEROW—The provider uses the minimum level of locking necessary to ensure that changes successfully written to a single row returned by the most recent fetch will not fail due to a concurrency violation if Update is called before any additional rows are retrieved. Typically this means that the provider takes a lock on the row when SetData is first called on the row, but the provider may lock the row as early as when it is read in order to guarantee that calling Update will succeed.

Lock Mode and Isolation Level are closely related, but distinct. A consumer's isolation level specifies the isolation of that consumer from changes made by other users to the underlying data. Lock mode defines when underlying data is locked in order to ensure updates succeed. The provider may use locking in order to enforce higher levels of isolation, in which case a higher level of locking may occur than required to enforce the specified lock mode. Lock mode specifies the minimum level of locking.
DBPROP_MAXINDEXSIZE Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_I4
Typical R/W: R
Description: Maximum Index Size

The maximum number of bytes allowed in the combined columns of an index. If there is no specified limit or the limit is unknown, this value is set to zero.
DBPROP_MAXOPENCHAPTERS Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_I4
Typical R/W: R
Description: Maximum Open Chapters

The maximum number of of chapters that can be open at any time.

If a chapter must be released before a new chapter can be opened, this value is one; if the provider does not support chapters, this value is zero.
DBPROP_MAXOPENROWS Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_I4
Typical R/W: R/W
Description: Maximum Open Rows

The maximum number of rows that can be active at the same time. This limit does not reflect resource limitations such as RAM, but does apply if the rowset implementation uses some strategy that results in a limit. If there is no limit, the value of this property is zero. The provider is free to support a greater number of active rows than the maximum specified by the consumer. In this case, the provider will return its actual maximum number of active rows instead of the value specified by the consumer.
DBPROP_MAXORSINFILTER Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_I4
Typical R/W: R
Description: Maximum OR Conditions

The maximum number of disjunct conditions that can be supported in a view filter. Multiple conditions (rows) of a view filter are joined in a logical OR. Providers that do not support joining multiple conditions return a value of 1. Providers that do not support view filters return a value of 0.

This value applies only to the conditions that can be joined in a logical OR in a call to SetFilter; it does not imply the maximum number of OR conditions that may exist in a command.
DBPROP_MAXPENDINGROWS Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_I4
Typical R/W: R/W
Description: Maximum Pending Rows

The maximum number of rows that can have pending changes at the same time. This limit does not reflect resource limitations such as Random Access Memory (RAM), but does apply if the rowset implementation uses some strategy that results in a limit. If there is no limit, this value is zero. The provider is free to support a greater number of pending rows than the maximum specified by the consumer. In this case, the provider will return its actual maximum number of pending rows instead of the value specified by the consumer.
DBPROP_MAXROWS Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_I4
Typical R/W: R/W
Description: Maximum Rows

The maximum number of rows that can be returned in a rowset. If there is no limit, this value is zero. If the provider supports setting DBPROP_MAXROWS the provider must ensure that the rowset never contains more than the specified number of rows.
DBPROP_MAXROWSIZE Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_I4
Typical R/W: R
Description: Maximum Row Size

The maximum length of a single row in a table. If there is no specified limit or the limit is unknown, this value is set to zero.
DBPROP_MAXROWSIZEINCLUDESBLOB Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_BOOL
Typical R/W: R
Description: Maximum Row Size Includes BLOB

VARIANT_TRUE—The maximum row size returned for the DBPROP_MAXROWSIZE property includes the length of all BLOB data.

VARIANT_FALSE—The maximum row size does not include the length of all BLOB data.
DBPROP_MAXSORTCOLUMNS Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_I4
Typical R/W: R
Description: Maximum Sort Columns

The maximum number of columns that can be supported in a View Sort. If there is no specified limit or the limit is unknown, this value is set to zero.

This value applies only to the number of sort columns that can be specified in a call to SetSortOrder; it does not imply the maximum number of columns that can be used to sort in a command.
DBPROP_MAXTABLESINSELECT Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_I4
Typical R/W: R
Description: Maximum Tables in SELECT

The maximum number of tables allowed in the FROM clause of a SELECT statement. If there is no specified limit or the limit is unknown, this value is set to zero.
DBPROP_MAYWRITECOLUMN Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? Y
Type: VT_BOOL
Typical R/W: R/W
Description: Column Writable

Whether a particular column is writable or not. This property can be set implicitly through the command used to create the rowset. For example, if the rowset is created by the SQL statement SELECT A, B FROM MyTable FOR UPDATE OF A, then this property is VARIANT_TRUE for column A and VARIANT_FALSE for column B.
DBPROP_MEMORYUSAGE Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_I4
Typical R/W: R/W
Description: Memory Usage

This property estimates the amount of memory that can be used by the rowset. If it is 0, the rowset can use unlimited memory. If it is between 1 and 99 inclusive, the rowset can use the specified percentage of total available virtual memory (physical and page file). If it is greater than or equal to 100, the rowset can use up to the specified number of kilobytes of memory.
DBPROP_MULTIPLECONNECTIONS Property group: Data Source
Property set: DBPROPSET_DATASOURCE
Type: VT_BOOL
Typical R/W: R/W
Description: Multiple Connections

Some providers may have to spawn multiple connections to the database in order to support multiple concurrent Commands, Sessions, and Rowsets. Such providers may expose DBPROP_MULTIPLECONNECTIONS in order to let the consumer disable making additional connections under the covers. Providers that can support multiple concurrent Commands, Sessions, and Rowsets without spawning multiple connections do not support this property.

VARIANT_TRUE (default)—The provider silently creates additional connections as required in order to support concurrent Command, Session, and Rowset objects.

VARIANT_FALSE—The provider returns DB_E_OBJECTOPEN if a method would require spawning an additional connection to the database.
DBPROP_MULTIPLEPARAMSETS Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_BOOL
Typical R/W: R
Description: Multiple Parameter Sets

VARIANT_TRUE—The provider supports multiple parameter sets.

VARIANT_FALSE—The provider supports only a single set of parameters per execution.

All providers that support parameters must support this property. Providers that do not support parameters must not support this property.
DBPROP_MULTIPLERESULTS Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_I4
Typical R/W: R
Description: Multiple Results

A bitmask specifying whether the provider supports multiple results objects and what restrictions it places on these objects. A combination of zero or more of the following:

DBPROPVAL_MR_SUPPORTED—The provider supports multiple results objects.

DBPROPVAL_MR_CONCURRENT—More than one rowset created by the same multiple results object can exist concurrently. If this bit is not set, the consumer must release the current rowset before calling IMultipleResults::GetResult to get the next result.

If multiple results objects are not supported, DBPROPVAL_MR_NOTSUPPORTED is returned. For more information about multiple results, see "Multiple Results" in Chapter 3.
DBPROP_MULTIPLESTORAGEOBJECTS Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_BOOL
Typical R/W: R
Description: Multiple Storage Objects

VARIANT_TRUE—The provider supports multiple, open storage objects at the same time.

VARIANT_FALSE—The provider supports only one open storage object at a time. Any method that attempts to open a second storage object returns a status of DBSTATUS_E_CANTCREATE for the column on which it attempted to open the second storage object, regardless of whether the objects are constructed over the same column, different columns in the same row, or different rows.
DBPROP_MULTITABLEUPDATE Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_BOOL
Typical R/W: R
Description: Multi-Table Update

VARIANT_TRUE—The provider can update rowsets derived from multiple tables.

VARIANT_FALSE—The provider cannot update rowsets derived from multiple tables.
DBPROP_NOTIFICATIONGRANULARITY Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_I4
Typical R/W: R/W
Description: Notification Granularity

DBPROPVAL_NT_SINGLEROW—For methods that operate on multiple rows, the provider calls IRowsetNotify::OnRowChange separately for each phase for each row. A cancellation affects a single row; it does not affect the other rows, and notifications are still sent for these rows.

DBPROPVAL_NT_MULTIPLEROWS—For methods that operate on multiple rows, then for each phase, the provider calls OnRowChange once for all rows that succeed and once for all rows that fail. This separation can occur at each phase where a change can fail. For example, if IRowsetChange::DeleteRows deletes some rows and fails to delete others during the Preliminary Work phase, it calls OnRowChange twice: once with DBEVENTPHASE_SYNCHAFTER and the array of handles of rows that it deleted, and once with DBEVENTPHASE_FAILEDTODO and the array of handles of rows it failed to delete. A cancellation affects all rows with handles that were passed to OnRowChange.

DBPROP_NOTIFICATIONGRANULARITY does not affect how providers return notifications about events that affect columns or the entire rowset.
DBPROP_NOTIFICATIONPHASES Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_I4
Typical R/W: R
Description: Notification Phases

A bitmask specifying the notification phases supported by the provider. A combination of two or more of the following:

DBPROPVAL_NP_OKTODO
DBPROPVAL_NP_ABOUTTODO
DBPROPVAL_NP_SYNCHAFTER
DBPROPVAL_NP_FAILEDTODO
DBPROPVAL_NP_DIDEVENT

The DBPROPVAL_NP_FAILEDTODO and DBPROPVAL_NP_DIDEVENT bits must be returned by all providers that support notifications.
DBPROP_NOTIFYCOLUMNSET
DBPROP_NOTIFYROWDELETE
DBPROP_NOTIFYROWFIRSTCHANGE
DBPROP_NOTIFYROWINSERT
DBPROP_NOTIFYROWRESYNCH
DBPROP_NOTIFYROWSETRELEASE
DBPROP_NOTIFYROWSETFETCH-POSITIONCHANGE
DBPROP_NOTIFYROWUNDOCHANGE
DBPROP_NOTIFYROWUNDODELETE
DBPROP_NOTIFYROWUNDOINSERT
DBPROP_NOTIFYROWUPDATE 		Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_I4
Typical R/W: R
Description:Column Set Notification
Row Delete Notification
Row First Change Notification
Row Insert Notification
Row Resynchronization Notification
Rowset Release Notification
Rowset Fetch Position Change Notification
Row Undo Change Notification
Row Undo Delete Notification
Row Undo Insert Notification
Row Update Notification

A bitmask specifying whether the notification phase is cancelable. A combination of zero or more of the following:

DBPROPVAL_NP_OKTODO
DBPROPVAL_NP_ABOUTTODO
DBPROPVAL_NP_SYNCHAFTER
DBPROP_NULLCOLLATION Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_I4
Typical R/W: R
Description: NULL Collation Order

Where NULLs are sorted in a list. One of the following:

DBPROPVAL_NC_END—NULLs are sorted at the end of the list, regardless of the sort order.

DBPROPVAL_NC_HIGH—NULLs are sorted at the high end of the list.

DBPROPVAL_NC_LOW—NULLs are sorted at the low end of the list.

DBPROPVAL_NC_START—NULLs are sorted at the start of the list, regardless of the sort order.
DBPROP_OLEOBJECTS Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_I4
Typical R/W: R
Description: OLE Object Support

A bitmask specifying the ways in which the provider supports access to BLOBs and OLE objects stored in columns. A combination of zero or more of the following:

DBPROPVAL_OO_BLOB—The provider supports access to BLOBs as structured storage objects. A consumer determines what interfaces are supported through DBPROP_STRUCTUREDSTORAGE.

DBPROPVAL_OO_IPERSIST—The provider supports access to OLE objects through IPersistStream, IPersistStreamInit, or IPersistStorage.
DBPROP_ORDERBYCOLUMNSINSELECT Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_BOOL
Typical R/W: R
Description: ORDER BY Columns in Select List

VARIANT_TRUE—Columns in an ORDER BY clause must be in the select list.

VARIANT_FALSE—Columns in an ORDER BY clause are not required to be in the select list.
DBPROP_ORDEREDBOOKMARKS Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_BOOL
Typical R/W: R/W
Description: Bookmarks Ordered

VARIANT_TRUE—Bookmarks can be compared to determine the relative position of their associated rows in the rowset. Setting the value of this property to VARIANT_TRUE automatically sets the value of DBPROP_BOOKMARKS to VARIANT_TRUE.

VARIANT_FALSE—Bookmarks can only be compared for equality.

Whether bookmarks can be compared byte-by-byte or must be compared with IRowsetLocate::Compare depends on the value of the DBPROP_LITERALBOOKMARKS property.
DBPROP_OTHERINSERT Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_BOOL
Typical R/W: R/W
Description: Others' Inserts Visible

VARIANT_TRUE—The rowset can see rows inserted by someone other than a consumer of the rowset. That is, if someone other than a consumer of the rowset inserts a row, any consumer of the rowset can see that row the next time it fetches a set of rows containing it. This includes rows inserted by other parties in the same transaction, as well as rows inserted by parties outside the transaction.

The transaction isolation level does not affect the ability of the rowset to see rows inserted by other parties in the same transaction, such as other rowsets in the same session. However, it does restrict the ability of the rowset to see rows inserted by parties outside the transaction.

VARIANT_FALSE—The rowset cannot see rows inserted by others.

For programmers accustomed to the cursor model in ODBC, the DBPROP_OTHERUPDATEDELETE and DBPROP_OTHERINSERT properties correspond to ODBC cursors as follows:

Static cursor:
DBPROP_OTHERINSERT = VARIANT_FALSE
DBPROP_OTHERUPDATEDELETE =
VARIANT_FALSE

Keyset-driven cursor:
DBPROP_OTHERINSERT = VARIANT_FALSE
DBPROP_OTHERUPDATEDELETE =
VARIANT_TRUE

Dynamic cursor:
DBPROP_OTHERINSERT = VARIANT_TRUE
DBPROP_OTHERUPDATEDELETE =
VARIANT_TRUE

Furthermore, the DBPROP_OWNUPDATEDELETE and DBPROP_OWNINSERT properties correspond to the values returned by the SQL_STATIC_SENSITIVITY information type in SQLGetInfo in ODBC.

For a description of how these properties relate to transaction isolation level, see "Visibility of Other Changes" in Chapter 5.
DBPROP_OTHERUPDATEDELETE Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_BOOL
Typical R/W: R/W
Description: Others' Changes Visible

VARIANT_TRUE—The rowset can see updates and deletes made by someone other than a consumer of the rowset. That is, suppose someone other than a consumer of the rowset updates the data underlying a row or deletes the row. If the row is released completely, any consumer of the rowset will see that change the next time it fetches the row. This includes updates and deletes made by other parties in the same transaction, as well as updates and deletes made by parties outside the transaction.

The transaction isolation level does not affect the ability of the rowset to see updates or deletes made by other parties in the same transaction, such as other rowsets in the same session. However, it does restrict the ability of the rowset to see updates or deletes made by parties outside the transaction.

VARIANT_FALSE—The rowset cannot see updates and deletes made by others.

For information about how this relates to the cursor types in ODBC, see the DBPROP_OTHERINSERT property.
DBPROP_OUTPUTPARAMETERAVAILABILITY Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_I4
Typical R/W: R
Description: Output Parameter Availability

The time at which output parameter values become available. One of the following:

DBPROPVAL_OA_NOTSUPPORTED—Output parameters are not supported.

DBPROPVAL_OA_ATEXECUTE—Output parameter data is available immediately after ICommand::Execute returns.

DBPROPVAL_OA_ATROWRELEASE—If a command returns a single result that is a rowset, output parameter data is available at the time the rowset is completely released. If a command returns multiple results, output parameter data is available when IMultipleResults::GetResult returns DB_S_NORESULT or the multiple results object is completely released, whichever occurs first. Before the output parameter data is available, the consumer's bound memory is in an indeterminate state. For more information about multiple results, see "Multiple Results" in Chapter 3.
DBPROP_OWNINSERT Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_BOOL
Typical R/W: R/W
Description: Own Inserts Visible

VARIANT_TRUE—The rowset can see its own inserts. That is, if a consumer of a rowset inserts a row, any consumer of the rowset can see that row the next time it fetches a set of rows containing it.

This ability is independent of the transaction isolation level, because all consumers of the rowset share the same transaction.

VARIANT_FALSE—The rowset cannot see rows inserted by consumers of the rowset unless the command is re-executed.

For information about how this relates to the SQL_STATIC_SENSITIVITY information type in ODBC, see the DBPROP_OTHERINSERT property.
DBPROP_OWNUPDATEDELETE Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_BOOL
Typical R/W: R/W
Description: Own Changes Visible

VARIANT_TRUE—The rowset can see its own updates and deletes. That is, suppose a consumer of the rowset updates or deletes a row. If the row is released completely, any consumer of the rowset will see the update or delete the next time it fetches that row.

This ability is independent of the transaction isolation level because all consumers of the rowset share the same transaction.

VARIANT_FALSE—The rowset cannot see updates and deletes made by consumers of the rowset unless the command is re-executed.

For information about how this relates to the SQL_STATIC_SENSITIVITY information type in ODBC, see the DBPROP_OTHERINSERT property.
DBPROP_PERSISTENTIDTYPE Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_I4
Typical R/W: R
Description: Persistent ID Type

An integer specifying the type of DBID that the provider uses when persisting DBIDs for tables, indexes, and columns. This is generally the type of DBID that the provider considers to be the most permanent under schema changes and physical data reorganizations. One of the following:

DBPROPVAL_PT_NAME
DBPROPVAL_PT_PROPID
DBPROPVAL_PT_GUID
DBPROPVAL_PT_GUID_NAME
DBPROPVAL_PT_GUID_PROPID
DBPROPVAL_PT_PGUID_NAME
DBPROPVAL_PT_PGUID_PROPID
DBPROP_PREPAREABORTBEHAVIOR Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_I4
Typical R/W: R
Description: Prepare Abort Behavior

How aborting a transaction affects prepared commands. One of the following:

DBPROPVAL_CB_DELETE—Aborting a transaction deletes prepared commands. The application must reprepare commands before executing them.

DBPROPAL_CB_PRESERVE—Aborting a transaction preserves prepared commands. The application can re-execute commands without repreparing them.
DBPROP_PREPARECOMMITBEHAVIOR Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_I4
Typical R/W: R
Description: Prepare Commit Behavior

How committing a transaction affects prepared commands. One of the following:

DBPROPVAL_CB_DELETE—Committing a transaction deletes prepared commands. The application must reprepare commands before executing them.

DBPROPAL_CB_PRESERVE—Committing a transaction preserves prepared commands. The application can re-execute commands without repreparing them.
DBPROP_PROCEDURETERM Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_BSTR
Typical R/W: R
Description: Procedure Term

A character string with the data source vendor's name for a procedure; for example, "database procedure", "stored procedure", or "procedure". This is used for building user interfaces.
DBPROP_PROVIDERFRIENDLYNAME Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_BSTR
Typical R/W: R
Description: Provider Friendly Name

The friendly name of the provider, for example "Microsoft OLE DB Provider for ODBC Drivers".
DBPROP_PROVIDERNAME Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_BSTR
Typical R/W: R
Description: Provider Name

The filename of the provider; for example, "MYPRVDR.DLL".
DBPROP_PROVIDEROLEDBVER Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_BSTR
Typical R/W: R
Description: OLE DB Version

The version of OLE DB supported by the provider. The version is of the form ##.##, where the first two digits are the major version and the next two digits are the minor version. For example, OLE DB providers conforming to the 1.5 specification would return "01.50".
DBPROP_PROVIDERVER Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_BSTR
Typical R/W: R
Description: Provider Version

The version of the provider. The version is of the form ##.##.####, where the first two digits are the major version, the next two digits are the minor version, and the last four digits are the release version. The provider can append a description of the provider.

This is the same as DBPROP_DBMSVER if the DBMS is the same as the provider; that is, if the DBMS supports OLE DB interfaces directly. It is different if the provider is separate from the DBMS, such as when the provider accesses the DBMS through ODBC.
DBPROP_QUICKRESTART Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_BOOL
Typical R/W: R/W
Description: Quick Restart

VARIANT_TRUE—IRowset::RestartPosition is relatively quick to execute. In particular, it does not reexecute the command that created the rowset.

VARIANT_FALSE—RestartPosition is expensive to execute and requires reexecuting the command that created the rowset.

Although the value of this property can be set to VARIANT_TRUE, the provider is not required to honor it. The reason for this is that the provider does not know what the command is at the time the property is set; in particular, the consumer can set this property and then change the command text. However, the provider can fail this property if it is never able to quickly restart the next fetch position. Thus, if a consumer successfully sets this property, it must still check this flag on the rowset to determine if the next fetch position can be quickly set.
DBPROP_QUOTEDIDENTIFIERCASE Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_I4
Typical R/W R
Description: Quoted Identifier Sensitivity

How quoted identifiers treat case. One of the following:

DBPROPVAL_IC_UPPER—Quoted identifiers in SQL are case insensitive and are stored in uppercase in system catalog.

DBPROPVAL_IC_LOWER—Quoted identifiers in SQL are case insensitive and are stored in lowercase in system catalog.

DBPROPVAL_IC_SENSITIVE—Quoted identifiers in SQL are case sensitive and are stored in mixed case in system catalog.

DBPROPVAL_IC_MIXED—Quoted identifiers in SQL are case insensitive and are stored in mixed case in system catalog.
DBPROP_REENTRANTEVENTS Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_BOOL
Typical R/W: R
Description: Reentrant Events

VARIANT_TRUE—The provider supports reentrancy during callbacks to the IRowsetNotify interface. The provider might not support reentrancy on all rowset methods. These methods return DB_E_NOTREENTRANT.

VARIANT_FALSE—The provider does not support such reentrancy. The provider returns DB_E_NOTREENTRANT on methods called during the notification.

Regardless of this flag, all providers must support IRowset::GetData and IRowset::ReleaseRows calls during notifications, so long as the columns being accessed do not include deferred columns.
DBPROP_REMOVEDELETED Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_BOOL
Typical R/W: R/W
Description: Remove Deleted Rows

If the value of this property is VARIANT_TRUE, the provider removes rows it detects as having been deleted from the rowset. That is, fetching a block of rows that formerly included a deleted row does not return a handle to that row.

Which rows the rowset detects as having been deleted is determined by the DBPROP_OWNUPDATEDELETE and DBPROP_OTHERUPDATEDELETE properties; whether the rowset removes these rows is determined by this property.

This property is independent of the transaction isolation level. While the transaction isolation level in some cases determines whether the rowset can detect a row as having been deleted, it has no effect on whether or not the rowset removes that row.

For programmers accustomed to the cursor model in ODBC, the value of this property is always VARIANT_TRUE for rowsets implemented through dynamic cursors; that is, dynamic cursors always remove deleted rows. Whether static and keyset-driven cursors remove deleted rows depends on the value of this property.
DBPROP_REPORTMULTIPLECHANGES Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_BOOL
Typical R/W: R
Description: Report Multiple Changes

VARIANT_TRUE—An update or delete can affect multiple rows and the provider can detect that multiple rows have been updated or deleted. This happens when a provider cannot uniquely identify a row. For example, the provider might use the values of all the columns in the row to identify the row; if these columns do not include a unique key, an update or delete might affect more than one row.

VARIANT_FALSE—An update or delete always affects a single row or the provider cannot detect whether it affects multiple rows.
DBPROP_RESETDATASOURCE Property group: Data Source
Property set: DBPROPSET_DATASOURCE
Type: VT_I4
Typical R/W: W
Description: Reset Datasource

A bitmask specifying the data source state to be reset. A combination of zero or more of the following:

DBPROPVAL_RD_RESETALL—The provider should reset all states associated with the data source, such that the DSO appears as if it were newly initialized, with the exception that any open object is not released.

DBPROP_RESETDATASOURCE is generally used by a consumer in order to reuse an existing data source object without paying the cost of uninitializing and reinitializing the DSO.
DBPROP_RETURNPENDINGINSERTS Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_BOOL
Typical R/W: R
Description: Return Pending Inserts

VARIANT_TRUE—The methods that fetch rows, such as IRowset::GetNextRows, can return pending insert rows; that is, rows that have been inserted in delayed update mode but for which IRowsetUpdate::Update has not yet been called.

VARIANT_FALSE—The methods that fetch rows cannot return pending insert rows.
DBPROP_ROW_BULKOPS Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_14
Description: Bulk Operations

A bitmask describing optimizations that a provider may take for updates to the rowset. These optimizations are usually used for things like bulk loading of a table. The following values can be specified, and are usually set as OPTIONAL properties because they are hints to the provider. Additional bits may be defined in the future; providers should be prepared to handle new bits in this bitmask by ignoring them if the property is set as optional, or returning an error if the property is set as required.

DBPROPVAL_BO_NOLOG—The provider is not required to log inserts or changes to the rowset.

DBPROPVAL_BO_NOINDEXUPDATE—The provider is not required to update indexes based on inserts or changes to the rowset. Any indexes need to be re-created following changes made through the rowset.

DBPROPVAL_BO_REFINTEGRITY—Referential Integrity constraints do not need to be checked or enforced for changes made through the rowset.
DBPROP_ROWRESTRICT Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_BOOL
Typical R/W: R
Description: Row Privileges

VARIANT_TRUE—Access rights are restricted on a row-by-row basis. If the rowset supports IRowsetChange, IRowsetChange::SetData can be called for some but not all rows. A rowset must never count or return a handle for a row for which the consumer does not have read access rights.

VARIANT_FALSE—Access rights are not restricted on a row-by-row basis. If the rowset supports IRowsetChange, SetData can be called for any row.

For more information, see DBPROP_COLUMNRESTRICT.
DBPROP_ROWSET_ASYNCH Property group: Rowset
Property set: DBPROPSET_ROWSET
Type: VT_I4
Typical R/W: R/W
Description: Asynchronous Rowset Processing

A bitmask specifying the asynchronous processing performed on the rowset. A combination of zero or more of the following:

DBPROPVAL_ASNYCH_INITIALIZE: The rowset is initialized asynchronously. The method requesting the rowset returns immediately, but attempting to call any interface other than IConnectionPointContainer to obtain the IID_IDBAsynchNotify connection point may fail and the full set of interfaces may not be available on the rowset until asynchronous initialization has completed.

DBPROPVAL_ASNYCH_SEQUENTIALPOPULATION: The rowset is sequentially asynchronously populated; requests for rows may return DB_S_ENDOFROWSET before the end of the rowset is actually reached. Asynchronously populated rows are always added to the end of the rowset.

DBPROPVAL_ASNYCH_RANDOMPOPULATION: The rowset is randomly asynchronously populated; requests for rows may return DB_S_ENDOFROWSET before the end of the rowset is actually reached. Asynchronously populated rows may be inserted anywhere in the rowset.

The consumer may set both DBPROPVAL_ASNYCH_SEQUENTIALPOPULATION and DBPROPVAL_ASNYCH_RANDOMPOPULATION bits to request that the rowset be asynchronously populated either sequentially or randomly. The consumer is prepared for asynchronous notifications in OnRowChange as well as from IDBAsynchStatus. Only one property is returned by the rowset; if the rowset is asynchronously populated it returns either DBPROPVAL_ASNYCH_RANDOM or DBPROPVAL_ASNYCH_SEQUENTIAL.

If no bits are set (the default) the rowset is initialized and populated synchronously. All requested interfaces are available when the method requesting the rowset returns and requesting rows block until the requested number of hRows are obtained or the end of the rowset is reached.

DBPROPVAL_ASYNCH_BACKGROUNDPOPULATION: The rowset is to be populated asynchronously in the background. The rowset supports IDBAsynchStatus in order to get information about the population of the rowset or abort background population, and may support the connection point for IDBAsynchNotify to give status of the background population. DBPROPVAL_ASYNCH_BACKGROUNDPOPULATION is implied by DBPROPVAL_ASYNCH_SEQUENTIALPOPLUATION and DBPROPVAL_ASYNCH_RANDOMPOPULATION, however, if DBPROPVAL_ASYNCH_SEQUENTIALPOPLUATION or DBPROPVAL_ASYNCH_RANDOMPOPLUATION are not also set, the rowset appears to the consumer as if it were being populated synchronously in that requesting rows will always block until the requested number of hRows are obtained or the end of the rowset is reached.

DBPROPVAL_ASYNCH_PREPOPULATE: The consumer prefers to optimize for retrieving all data when the rowset is materialized. This is a hint to the provider to fetch all of the data up-front. DBPROPVAL_ASYNCH_PREPOPULATE is only a hint to the provider, the provider should never fail opening the rowset based on the setting of this flag, and need not return it to the consumer, even if the rowset is prepopulated.

DBPROPVAL_ASYNCH_POPULATEONDEMAND: The consumer prefers to optimize for getting each individual request for data returned as quickly as possible. This is a hint to the provider to populate the rowset as the data is fetched. DBPROPVAL_ASYNCH_POPULATEONDEMAND is only a hint to the provider, the provider should never fail opening the rowset based on the setting of this flag, and need not return it to the consumer, even if the rowset is populated on demand.

Since DBPROPVAL_ASYNCH_PREPOPULATE and DBPROPVAL_ANSYCH_POPULATEONDEMAND are just hints to the provider, if the consumer sets one or both of these properties in addition to the asychronous population properties DBPROPVAL_ASYNCH_SEQUENTIALPOPULATION or DBPROPVAL_ASYNCH_RANDOMPOPULATION, then the provider should attempt to populate asynchronously according to DBPROPVAL_ASYNCH_SEQUENTIALPOPULATION and DBPROPVAL_ASYNCH_RANDOMPOPULATION. If the asynchronous population specified by DBPROPVAL_ASYNCH_SEQUENTIALPOPULATION or DBPROPVAL_ASYNCH_RANDOMPOPULATION can not be supported, the provider should fail if DBPROPOPTIONS_REQUIRED was specified for the property, or attempt to populate synchronously according to DBPROPVAL_ASYNCH_PREPOPULATE or DBPROPVAL_ASYNCH_POPULATEONDEMAND if the property was set with DBPROPOPTIONS_OPTIONAL.
DBPROP_ROWSETCONVERSIONSONCOMMAND Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_BOOL
Typical R/W: R
Description: Rowset Conversions on Command

VARIANT_TRUE—Callers to IConvertType::CanConvert can inquire on a command about conversions supported on rowsets generated by the command.

VARIANT_FALSE—Callers can inquire on a command only about conversions supported by the command.
DBPROP_ROWTHREADMODEL Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_I4
Typical R/W: R/W
Description: Row Threading Model

A bitmask specifying the threading models supported by the rowset. A combination of one or more of the following:

DBPROPVAL_RT_FREETHREAD
DBPROPVAL_RT_APTMTTHREAD
DBPROPVAL_RT_SINGLETHREAD
DBPROP_SCHEMATERM Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_BSTR
Typical R/W: R
Description: Schema Term

The name the data source uses for a schema; for example, "schema" or "owner". This is used for building user interfaces.
DBPROP_SCHEMAUSAGE Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_I4
Typical R/W: R
Description: Schema Usage

A bitmask specifying how schema names can be used in text commands. A combination of zero or more of the following:

DBPROPVAL_SU_DML_STATEMENTS—Schema names are supported in all Data Manipulation Language statements.

DBPROPVAL_SU_TABLE_DEFINITION—Schema names are supported in all table definition statements.

DBPROPVAL_SU_INDEX_DEFINITION—Schema names are supported in all index definition statements.

DBPROPVAL_SU_PRIVILEGE_DEFINITION—Schema names are supported in all privilege definition statements.
DBPROP_SERVERCURSOR Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_BOOL
Typical R/W: R/W
Description: Server Cursor

If the value of this property is set to VARIANT_TRUE with ICommandProperties::SetProperties, the cursor underlying the rowset (if any) must be materialized on the server.

If the value of this property is not set to VARIANT_TRUE with ICommandProperties::SetProperties, it is up to the provider to decide where to materialize the cursor.

The consumer can determine where the cursor was materialized by checking the value of this property on the rowset.
DBPROP_SERVERDATAONINSERT Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_BOOL
Typical R/W: R/W
Description: Server Data on Insert

VARIANT_TRUE—At the time an insert is transmitted to the server (when InsertRow is called in immediate mode, or when Update is called for an inserted row in deferred update mode), the provider retrieves data from the server to update the local row cache.

VARIANT_FALSE—The provider does not retrieve server values for newly inserted rows. The consumer can only retrieve data values explicitly set in the call to InsertRow, or by calls to SetData for the hRow returned by InsertRow.

Consumers should be aware that setting DBPROP_SERVERDATAONINSERT is potentially expensive, and may not be supported for certain types of rowsets.
DBPROP_SERVERNAME Property group: Datasource Information
Property set: DBPROPSET_DATASOURCEINFO
Type:VT_BSTR
Typical R/W:R
Description: Server Name

The name of the server. This may be the same as the DBPROP_INIT_DATASOURCE property if the server name is used to define the data source that the user specifies when connecting, or may be the actual name of the server if the provider connects through "friendly" data source names.
DBPROP_SESS_AUTOCOMMITISOLEVELS Property group: Session
Property set: DBPROPSET_SESSION
Type: VT_I4
Typical R/W: R/W
Description: Autocommit Isolation Levels

A bitmask specifying the transaction isolation level while in auto-commit mode. The values that can be set in this bitmask are the same as those that can be set for DBPROP_SUPPORTEDTXNISOLEVELS.
DBPROP_SORTONINDEX Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_BOOL
Typical R/W: R
Description: Sort on Index

VARIANT_TRUE: The provider supports SetSortOrder only for columns contained in an index.

VARIANT_FALSE: The provider does not require columns to be indexed in order to be specified in SetSortOrder, or the provider does not support SetSortOrder.

This value applies only to the ability to specify a column in SetSortOrder; it does not imply whether or not nonindexed columns can be used in the order clause of a command.
DBPROP_SQLSUPPORT Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_I4
Typical R/W: R
Description: SQL Support

A bitmask specifying the level of support for SQL. A combination of zero or more of the following:

DBPROPVAL_SQL_NONE—SQL is not supported.

DBPROPVAL_SQL_ODBC_MINIMUM
DBPROPVAL_SQL_ODBC_CORE
DBPROPVAL_SQL_ODBC_EXTENDED—These levels correspond to the levels of SQL conformance defined in ODBC version 2.5. These levels are cumulative. That is, if the provider supports one level, it also sets the bits for all lower levels. For example, if the provider sets the DBPROPVAL_SQL_ODBC_CORE bit, it also sets the DBPROPVAL_SQL_ODBC_MINIMUM bit.

DBPROPVAL_SQL_ESCAPECLAUSES—The provider supports the ODBC escape clause syntax.

DBPROPVAL_SQL_ANSI92_ENTRY
DBPROPVAL_SQL_FIPS_TRANSITIONAL
DBPROPVAL_SQL_ANSI92_INTERMEDIATE
DBPROPVAL_SQL_ANSI92_FULL—These levels correspond to the levels in ANSI SQL92. These levels are cumulative. That is, if the provider supports one level, it also sets the bits for all lower levels.

DBPROPVAL_SQL_ANSI89_IEF—The provider supports the ANSI89 Integrity Enhancement Facility.

DBPROPVAL_SQL_SUBMINIMUM—The provider supports the DBGUID_SQL dialect and parses the command text according to SQL rules, but does not support either the minimum ODBC level nor the ANSI SQL92 Entry level. This level is not accumulative; providers that support at least the minimal ODBC Level or ANSI SQL92 Entry Level do not set this bit. OLE DB consumers can determine whether or not the provider supports the DBGUID_SQL dialect by verifying that the DBPROPVAL_SQL_NONE bit is not set.
DBPROP_STRONGIDENTITY Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_BOOL
Typical R/W: R
Description: Strong Row Identity

VARIANT_TRUE—The handles of newly inserted rows can be compared as specified by DBPROP_LITERALIDENTITY.

VARIANT_FALSE—There is no guarantee that the handles of newly inserted rows can be compared successfully. In this case, IRowsetIdentity::IsSameRow might return DB_E_NEWLYINSERTED.

A newly inserted row is defined as a row for which an insertion has been transmitted to the data source, as opposed to a pending insert row. For more information, see "Uniqueness of Rows in the Rowset" in Chapter 4.
DBPROP_STRUCTUREDSTORAGE Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_I4
Typical R/W: R
Description: Structured Storage

A bitmask specifying what interfaces the rowset supports on storage objects. If a provider can support any of these interfaces it is also required to support ISequentialStream. A combination of zero or more of the following:

DBPROPVAL_SS_ISEQUENTIALSTREAM
DBPROPVAL_SS_ISTREAM
DBPROPVAL_SS_ISTORAGE
DBPROPVAL_SS_ILOCKBYTES
DBPROP_SUBQUERIES Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_I4
Typical R/W: R
Description: Subquery Support

A bitmask specifying the predicates in text commands that support subqueries. A combination of zero or more of the following:

DBPROPVAL_SQ_CORRELATEDSUBQUERIES
DBPROPVAL_SQ_COMPARISON
DBPROPVAL_SQ_EXISTS
DBPROPVAL_SQ_IN
DBPROPVAL_SQ_QUANTIFIED

The DBPROPVAL_SQ_CORRELATEDSUBQUERIES bit indicates that all predicates that support subqueries support correlated subqueries.
DBPROP_SUPPORTEDTXNDDL Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_I4
Typical R/W: R
Description: Transaction DDL

Whether Data Definition Language (DDL) statements are supported in transactions. One of the following:

DBPROPVAL_TC_NONE—Transactions are not supported.

DBPROPVAL_TC_DML—Transactions can only contain Data Manipulation Language (DML) statements. DDL statements within a transaction cause an error.

DBPROPVAL_TC_DDL_COMMIT—Transactions can only contain DML statements. DDL statements within a transaction cause the transaction to be committed.

DBPROPVAL_TC_DDL_IGNORE—Transactions can only contain DML statements. DDL statements within a transaction are ignored.

DBPROPVAL_TC_ALL—Transactions can contain DDL and DML statements in any order.
DBPROP_SUPPORTEDTXNISOLEVELS Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_I4
Typical R/W: R
Description: Isolation Levels

A bitmask specifying the supported transaction isolation levels. A combination of zero or more of the following:

DBPROPVAL_TI_CHAOS
DBPROPVAL_TI_READUNCOMMITTED
DBPROPVAL_TI_BROWSE
DBPROPVAL_TI_CURSORSTABILITY
DBPROPVAL_TI_READCOMMITTED
DBPROPVAL_TI_REPEATABLEREAD
DBPROPVAL_TI_SERIALIZABLE
DBPROPVAL_TI_ISOLATED

For more information, see "Isolation Levels" in Chapter 12.
DBPROP_SUPPORTEDTXNISORETAIN Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_I4
Typical R/W: R
Description: Isolation Retention

A bitmask specifying the supported transaction isolation retention levels. A combination of zero or more of the following:

DBPROPVAL_TR_COMMIT_DC—The transaction may either preserve or dispose of isolation context across a retaining commit.

DBPROPVAL_TR_COMMIT—The transaction preserves its isolation context (that is, it preserves its locks, if that is how isolation is implemented) across a retaining commit.

DBPROPVAL_TR_COMMIT_NO—The transaction is explicitly not to preserve isolation across a retaining commit.

DBPROPVAL_TR_ABORT_DC—The transaction may either preserve or dispose of isolation context across a retaining abort.

DBPROPVAL_TR_ABORT—The transaction preserves its isolation context across a retaining abort.

DBPROPVAL_TR_ABORT_NO—The transaction is explicitly not to preserve isolation across a retaining abort.

DBPROPVAL_TR_DONTCARE—The transaction may preserve or dispose of isolation context across a retaining commit or abort. This is the default.

DBPROPVAL_TR_BOTH—Isolation is preserved across both a retaining commit and a retaining abort.

DBPROPVAL_TR_NONE—Isolation is explicitly not to be retained across either a retaining commit or abort.

DBPROPVAL_TR_OPTIMISTIC—Optimistic concurrency control is to be used. If DBPROPVAL_TR_OPTIMISTIC is specified, then whatever isolation technology is in place (such as locking), it must be the case that other transactions' ability to make changes to the data and resources manipulated by this transaction is not in any way affected by the data read or updated by this transaction. That is, optimistic control is to be used for all data in the transaction.

For more information, see ITransactionLocal::StartTransaction.
DBPROP_TABLETERM Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_BSTR
Typical R/W: R
Description: Table Term

The name the data source uses for a table; for example, "table" or "file". This is used for building user interfaces.
DBPROP_TBL_TEMPTABLE Property group: Table
Property set: DBPROPSET_TABLE
Type: VT_BOOL
Typical R/W: R/W
Description: Temporary Table

Whether the table is temporary. One of the following:

VARIANT_TRUE—The table is destroyed when the session is released.

VARIANT_FALSE—The table is created permanently.
DBPROP_TRANSACTEDOBJECT Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? Y
Type: VT_BOOL
Typical R/W: R/W
Description: Objects Transacted

VARIANT_TRUE—Any object created on the specified column is transacted. That is, data made visible to the data source through the object can be committed with ITransaction::Commit or aborted with ITransaction::Abort.

VARIANT_FALSE—Any object created on the specified column is not transacted. That is, all changes to the object are permanent once they are made visible to the data source.

If this property is set on a column that does not contain an object, it is ignored.
DBPROP_UNIQUEROWS Property group: Rowset
Property set: DBPROPSET_ROWSET
Type: VT_BOOL
Typical R/W: R/W
Description: UniqueRows

VARIANT_TRUE—Each row is uniquely identified by its column values.

VARIANT_FALSE—Rows in the rowset may or may not be uniquely identified by their column values.

If this property is set to VARIANT_TRUE when opening the rowset, the provider adds additional columns, if necessary, in order to ensure that each row is uniquely identified by its values. These additional columns appear at the end of the rowset, have a DBID of type DBKIND_GUID_PROPID, DBKIND_PGUID_PROPID, DBKIND_GUID_NAME, or DBKIND_PGUID_NAME, and the guid (or pguid) element is (or points to) DBCOL_SPECIALCOL. These columns are typically not displayed to the user, but are used by components such as update services to uniquely identify a row.

The provider may, but is not required to, duplicate existing columns in the rowset in order to make sure key columns are included.

If this property is set to VARIANT_TRUE, and the provider supports the optional IColumnsRowset metadata column DBCOLUMN_KEYCOLUMN, then the set of columns which uniquely identify the row have a value of VARIANT_TRUE in the DBCOLUMN_KEYCOLUMN column returned by IColumnsRowset. This may be a subset of the columns in the row, or all of the columns if the provider cannot determine a proper subset that uniquely identifies the row. If this property is not VARIANT_TRUE, then DBCOLUMN_KEYCOLUMN may or may not be set to VARIANT_TRUE for key columns, but there is no guarantee that the set of columns that are flagged with this value are sufficient in order to uniquely identify the row.
DBPROP_UPDATABILITY Property group: Rowset
Property set: DBPROPSET_ROWSET
Column? N
Type: VT_I4
Typical R/W: R/W
Description: Updatability

A bitmask specifying the supported methods on IRowsetChange. A combination of zero or more of the following:

DBPROPVAL_UP_CHANGE—SetData is supported.

DBPROPVAL_UP_DELETE—DeleteRows is supported.

DBPROPVAL_UP_INSERT—InsertRow is supported.

DBPROP_UPDATABILITY should be used in conjunction with DBPROP_IRowsetChange. If DBPROP_IRowsetChange is VARIANT_TRUE and DBPROP_UPDATABILITY is not set, then it is provider-specific what methods are supported on IRowsetChange.

If DBPROP_UPDATABILITY is specified, then the provider must not support any methods whose bits are not set.
DBPROP_USERNAME Property group: Data Source Information
Property set: DBPROPSET_DATASOURCEINFO
Type: VT_BSTR
Typical R/W: R
Description: User Name

A character string with the name used in a particular database, which can be different than a login name.


CoTypes, Structures, and Enumerated Types

This appendix lists the CoTypes, structures, and enumerated types defined by OLE DB.


OLE DB CoTypes

Enumerators:

   CoType TEnumerator {
      [mandatory] IParseDisplayName;
      [mandatory] ISourcesRowset;
      [optional]  IDBInitialize;
      [optional]  IDBProperties;
      [optional]  ISupportErrorInfo;
   }

Data Source Objects:

   CoType TDataSource {
      [mandatory] interface IDBCreateSession;
      [mandatory] interface IDBInitialize;
      [mandatory] interface IDBProperties;
      [mandatory] interface IPersist;
      [optional]  interface IConnectionPointContainer;
      [optional]  interface IDBAsynchStatus;
      [optional]  interface IDBDataSourceAdmin;
      [optional]  interface IDBInfo;
      [optional]  interface IPersistFile;
      [optional]  interface ISupportErrorInfo;
   }

Sessions:

   CoType TSession {
      [mandatory] interface IGetDataSource;
      [mandatory] interface IOpenRowset;
      [mandatory] interface ISessionProperties;
      [optional]  interface IDBCreateCommand;
      [optional]  interface IDBSchemaRowset;
      [optional]  interface IIndexDefinition;
      [optional]  interface ISupportErrorInfo;
      [optional]  interface ITableDefinition;
      [optional]  interface ITransactionJoin;
      [optional]  interface ITransactionLocal;
      [optional]  interface ITransaction;
      [optional]  interface ITransactionObject;
   }

Commands:

   CoType TCommand {
      [mandatory] interface IAccessor;
      [mandatory] interface IColumnsInfo;
      [mandatory] interface ICommand;
      [mandatory] interface ICommandProperties;
      [mandatory] interface ICommandText;
      [mandatory] interface IConvertType;
      [optional]  interface IColumnsRowset;
      [optional]  interface ICommandPrepare;
      [optional]  interface ICommandWithParameters;
      [optional]  interface ISupportErrorInfo;
   }

Multiple Results Objects:

   CoType TMultipleResults {
      [mandatory] interface IMultipleResults;
      [optional] interface ISupportErrorInfo;
   }

Rowsets:

   CoType TRowset {
      [mandatory] interface IAccessor;
      [mandatory] interface IColumnsInfo;
      [mandatory] interface IConvertType;
      [mandatory] interface IRowset;
      [mandatory] interface IRowsetInfo;
      [optional]  interface IChapteredRowset;
      [optional]  interface IColumnsRowset;
      [optional]  interface IConnectionPointContainer;
      [optional]  interface IDBAsynchStatus;
      [optional]  interface IRowsetChange;
      [optional]  interface IRowsetFind;
      [optional]  interface IRowsetIdentity;
      [optional]  interface IRowsetIndex;
      [optional]  interface IRowsetLocate;
      [optional]  interface IRowsetRefresh;
      [optional]  interface IRowsetScroll;
      [optional]  interface IRowsetUpdate;
      [optional]  interface IRowsetView;
      [optional]  interface ISupportErrorInfo;
   }

Views:

   CoType TView {
      [mandatory] interface IColumnsInfo;
      [optional]  interface IAccessor;
      [optional]  interface ISupportErrorInfo;
      [optional]  interface IViewChapter;
      [optional]  interface IViewFilter;
      [optional]  interface IViewRowset;
      [optional]  interface IViewSort;
   };

Indexes:

   CoType TIndex {
      [mandatory] interface IAccessor;
      [mandatory] interface IColumnsInfo;
      [mandatory] interface IConvertType;
      [mandatory] interface IRowset;
      [mandatory] interface IRowsetIndex;
      [mandatory] interface IRowsetInfo;
      [optional]  interface IRowsetChange;
      [optional]  interface IRowsetFind;
      [optional]  interface IRowsetIdentity;
      [optional]  interface IRowsetLocate;
      [optional]  interface IRowsetRefresh;
      [optional]  interface IRowsetScroll;
      [optional]  interface IRowsetUpdate;
      [optional]  interface IRowsetView;
      [optional]  interface ISupportErrorInfo;
   };

Transactions:

   CoType TTransaction {
      [mandatory] interface IConnectionPointContainer;
      [mandatory] interface ITransaction;
      [optional]  interface ISupportErrorInfo;
   };

Transaction Options:

   CoType TTransactionOptions {
      [mandatory] interface ITransactionOptions;
      [optional]  interface ISupportErrorInfo;
   };

Errors:

   CoType TErrorObject {
      [mandatory] interface IErrorRecords;
   }

Custom Errors:

   CoType TCustomErrorObject {
      [optional]  interface IErrorLookup;
      [optional]  interface ISQLErrorInfo;
   }

Error Records:

   CoType TErrorRecord {
      [mandatory] interface IErrorInfo;
   }


OLE DB Structures and Enumerated Types

Type Defined in Used in
typedef DWORD DBACCESSORFLAGS;
enum DBACCESSORFLAGSENUM {
  DBACCESSOR_INVALID,
  DBACCESSOR_PASSBYREF,
  DBACCESSOR_ROWDATA,
  DBACCESSOR_PARAMETERDATA,
  DBACCESSOR_OPTIMIZED
};
IAccessor::
CreateAccessor 		IAccessor::CreateAccessor

IAccessor::GetBindings

typedef struct tagDBBINDEXT {
  BYTE * pExtension;
  ULONG  ulExtension;
} DBBINDEXT;

"DBBINDING

Structures" in Chapter 6
DBBINDING

typedef struct tagDBBINDING {
  ULONG   iOrdinal;
  ULONG   obValue;
  ULONG   obLength;
  ULONG   obStatus;
  ITypeInfo * pTypeInfo;
  DBOBJECT *  pObject;
  DBBINDEXT * pBindExt;
  DBPART  dwPart;
  DBMEMOWNER  dwMemOwner;
  DBPARAMIO eParamIO;
  ULONG   cbMaxLen;
  DWORD   dwFlags;
  DBTYPE  wType;
  BYTE    bPrecision;
  BYTE    bScale;
} DBBINDING;

"DBBINDING

Structures" in Chapter 6
IAccessor::CreateAccessor

IAccessor::GetBindings

typedef DWORD DBBINDFLAG;
enum DBBINDFLAGENUM {
  DBBINDFLAG_HTML
};

IAccessor::

CreateAccessor
IAccessor::CreateAccessor

typedef DWORD DBBINDSTATUS;
enum DBBINDSTATUSENUM {
  DBBINDSTATUS_OK
  DBBINDSTATUS_BADORDINAL
  DBBINDSTATUS_UNSUPPORTEDCONVERSION
  DBBINDSTATUS_BADBINDINFO
  DBBINDSTATUS_BADSTORAGEFLAGS
  DBBINDSTATUS_NOINTERFACE
};

IAccessor::

CreateAccessor
IAccessor::CreateAccessor

typedef enum tagDBBOOKMARK {
  DBBMK_INVALID,
  DBBMK_FIRST,
  DBBMK_LAST
} DBBOOKMARK;

"Standard Bookmarks" in Chapter 4

typedef struct tagDBCOLUMNDESC {
  LPOLESTR  pwszTypeName;
  ITypeInfo * pTypeInfo;
  DBPROPSET * rgPropertySets;
  CLSID *   pclsid;
  ULONG   cPropertySets;
  ULONG   ulColumnSize;
  DBID    dbcid;
  DBTYPE  wType;
  BYTE    bPrecision;
  BYTE    bScale;
} DBCOLUMNDESC;

ITableDefinition::

CreateTable
ITableDefinition::

CreateAddColumn

ITableDefinition::

CreateTable

typedef DWORD DBCOLUMNFLAGS;
enum DBCOLUMNFLAGSENUM {
  DBCOLUMNFLAGS_ISBOOKMARK,
  DBCOLUMNFLAGS_MAYDEFER,
  DBCOLUMNFLAGS_WRITE,
  DBCOLUMNFLAGS_WRITEUNKNOWN,
  DBCOLUMNFLAGS_ISFIXEDLENGTH,
  DBCOLUMNFLAGS_ISNULLABLE,
  DBCOLUMNFLAGS_MAYBENULL,
  DBCOLUMNFLAGS_ISLONG,
  DBCOLUMNFLAGS_ISROWID,
  DBCOLUMNFLAGS_ISROWVER,
  DBCOLUMNFLAGS_CACHEDEFERRED
};

IColumnsInfo::

GetColumnInfo
DBCOLUMNINFO

typedef struct tagDBCOLUMNINFO {
  LPOLESTR  pwszName;
  ITypeInfo * pTypeInfo;
  ULONG   iOrdinal;
  DBCOLUMNFLAGS dwFlags;
  ULONG   ulColumnSize;
  DBTYPE    wType;
  BYTE    bPrecision;
  BYTE    bScale;
  DBID    columnid;
} DBCOLUMNINFO;

IColumnsInfo::

GetColumnInfo
IColumnsInfo::

GetColumnInfo

typedef DWORD DBCOMPARE;
enum DBCOMPAREENUM {
  DBCOMPARE_LT,
  DBCOMPARE_EQ,
  DBCOMPARE_GT,
  DBCOMPARE_NE,
  DBCOMPARE_NOTCOMPARABLE
};

IRowsetLocate::

Compare
IRowsetLocate::Compare

typedef DWORD DBCOMPAREOP;
enum DBCOMPAREOPSENUM {
  DBCOMPAREOPS_LT,
  DBCOMPAREOPS_LE,
  DBCOMPAREOPS_EQ,
  DBCOMPAREOPS_GE,
  DBCOMPAREOPS_GT,
  DBCOMPAREOPS_BEGINSWITH,
  DBCOMPAREOPS_NOTBEGINSWITH,
  DBCOMPAREOPS_CONTAINS,
  DBCOMPAREOPS_NOTCONTAINS,
  DBCOMPAREOPS_NE,
  DBCOMPAREOPS_IGNORE,
  DBCOMPAREOPS_CASESENSITIVE,
  DBCOMPAREOPS_CASEINSENSITIVE
};

IRowsetFind::

FindNextRow
IRowsetFind::FindNextRow

IViewFilter::GetFilter

IViewFilter::SetFilter

typedef DWORD DBCONVERTFLAGS;
enum DBCONVERTFLAGSENUM {
  DBCONVERTFLAGS_COLUMN,
  DBCONVERTFLAGS_ISFIXEDLENGTH,
  DBCONVERTFLAGS_ISLONG,
  DBCONVERTFLAGS_PARAMETER,
  DBCONVERTFLAGS_FROMVARIANT
};

IConvertType::

CanConvert
IConvertType::CanConvert

typedef struct tagDBDATE {
  SHORT  year;
  USHORT month;
  USHORT day;
} DBDATE;

"Type Indicators" in Appendix A

typedef DWORD DBEVENTPHASE;
enum DBEVENTPHASEENUM {
  DBEVENTPHASE_OKTODO,
  DBEVENTPHASE_ABOUTTODO,
  DBEVENTPHASE_SYNCHAFTER,
  DBEVENTPHASE_FAILEDTODO,
  DBEVENTPHASE_DIDEVENT
};

IRowsetNotify
IRowsetNotify::

OnFieldChange

IRowsetNotify::

OnRowChange

IRowsetNotify::

OnRowsetChange

typedef struct tagDBID {
  union {
  GUID guid;
  GUID * pguid;
  } uGuid;
  DBKIND eKind;
  union {
  LPOLESTR pwszName;
  ULONG  ulPropid;
  } uName;
} DBID;

"Column IDs" in

Chapter 4
DBCOLUMNDESC

DBCOLUMNINFO

DBINDEXCOLUMNDESC

DBPROP

IColumnsInfo::

MapColumnIDs

IColumnsRowset::

GetAvailableColumns

IColumnsRowset::

GetColumnsRowset

IIndexDefinition::

CreateIndex

IIndexDefinition::

DropIndex

IOpenRowset::

OpenRowset

ITableDefinition::

AddColumn

ITableDefinition::

CreateTable

ITableDefinition::

DropColumn

ITableDefinition::DropTable

typedef DWORD DBINDEX_COL_ORDER;
enum DBINDEX_COL_ORDERENUM {
  DBINDEX_COL_ORDER_ASC,
  DBINDEX_COL_ORDER_DESC
};

IIndexDefinition::

CreateIndex
DBINDEXCOLUMNDESC

typedef struct tagDBINDEXCOLUMNDESC {
  DBID  *   pColumnID;
  DBINDEX_COL_ORDER eIndexColOrder;
} DBINDEXCOLUMNDESC;

IIndexDefinition::

CreateIndex
IIndexDefinition::

CreateIndex

IRowsetIndex::

GetIndexInfo

typedef DWORD DBKIND;
enum DBKINDENUM {
  DBKIND_GUID_NAME,
  DBKIND_GUID_PROPID,
  DBKIND_NAME,
  DBKIND_PGUID_NAME,
  DBKIND_PGUID_PROPID,
  DBKIND_PROPID,
  DBKIND_GUID
};

"Column IDs" in

Chapter 4
DBID

typedef DWORD DBLITERAL;
enum DBLITERALENUM {
  DBLITERAL_INVALID,
  DBLITERAL_BINARY_LITERAL,
  DBLITERAL_CATALOG_NAME,
  DBLITERAL_CATALOG_SEPARATOR,
  DBLITERAL_CHAR_LITERAL,
  DBLITERAL_COLUMN_ALIAS,
  DBLITERAL_COLUMN_NAME,
  DBLITERAL_CORRELATION_NAME,
  DBLITERAL_CURSOR_NAME,
  DBLITERAL_ESCAPE_PERCENT_PREFIX,
  DBLITERAL_ESCAPE_PERCENT_SUFFIX,
  DBLITERAL_ESCAPE_UNDERSCORE_PREFIX,
  DBLITERAL_ESCAPE_UNDERSCORE_SUFFIX,
  DBLITERAL_INDEX_NAME,
  DBLITERAL_LIKE_PERCENT,
  DBLITERAL_LIKE_UNDERSCORE,
  DBLITERAL_PROCEDURE_NAME,
  DBLITERAL_QUOTE_PREFIX,
  DBLITERAL_QUOTE_SUFFIX,
  DBLITERAL_SCHEMA_NAME,
  DBLITERAL_SCHEMA_SEPARATOR,
  DBLITERAL_TABLE_NAME,
  DBLITERAL_TEXT_COMMAND,
  DBLITERAL_USER_NAME,
  DBLITERAL_VIEW_NAME
};

IDBInfo::

GetLiteralInfo
IDBInfo::GetLiteralInfo

typedef DWORD DBMEMOWNER;
enum DBMEMOWNERENUM {
  DBMEMOWNER_CLIENTOWNED,
  DBMEMOWNER_PROVIDEROWNED
};

"DBBINDING

Structures" in Chapter 6
DBBINDING

typedef struct tagDB_NUMERIC {
  BYTE precision;
  BYTE scale;
  BYTE sign;
  BYTE val[16];
} DB_NUMERIC;

"Type Indicators" in Appendix A

typedef struct tagDBOBJECT {
  DWORD dwFlags;
  IID iid;
} DBOBJECT;

"DBBINDING

Structures" in Chapter 6
DBBINDING

typedef struct tagDBPARAMBINDINFO {
  LPOLESTR   pwszDataSourceType;
  LPOLESTR   pwszName;
  ULONG    ulParamSize;
  DBPARAMFLAGS dwFlags;
  BYTE   bPrecision;
  BYTE   bScale;
} DBPARAMBINDINFO;

ICommandWith-

Parameters::

SetParameterInfo
ICommandWithParameters::SetParameterInfo

typedef DWORD DBPARAMFLAGS;
enum DBPARAMFLAGSENUM {
  DBPARAMFLAGS_ISINPUT,
  DBPARAMFLAGS_ISOUTPUT,
  DBPARAMFLAGS_ISSIGNED,
  DBPARAMFLAGS_ISNULLABLE,
  DBPARAMFLAGS_ISLONG
};

ICommandWith-

Parameters::

GetParameterInfo
DBPARAMBINDINFO

DBPARAMINFO

typedef struct tagDBPARAMINFO {
  DBPARAMFLAGS dwFlags;
  ULONG    iOrdinal;
  LPOLESTR   pwszName;
  ITypeInfo *  pTypeInfo;
  ULONG    ulParamSize;
  DBTYPE   wType;
  BYTE   bPrecision;
  BYTE   bScale;
} DBPARAMINFO;

ICommandWith-

Parameters::

GetParameterInfo
ICommandWithParameters::GetParameterInfo

typedef DWORD DBPARAMIO;
enum DBPARAMIOENUM {
  DBPARAMIO_NOTPARAM,
  DBPARAMIO_INPUT,
  DBPARAMIO_OUTPUT
};

"DBBINDING

Structures" in Chapter 6
DBBINDING

typedef struct tagDBPARAMS {
  void *  pData;
  ULONG   cParamSets;
  HACCESSOR hAccessor;
} DBPARAMS;

ICommand::Execute
ICommand::Execute

typedef DWORD DBPART;
enum DBPARTENUM {
  DBPART_INVALID,
  DBPART_VALUE,
  DBPART_LENGTH,
  DBPART_STATUS
};

"DBBINDING

Structures" in Chapter 6
DBBINDING

typedef DWORD DBPENDINGSTATUS;
enum DBPENDINGSTATUSENUM {
  DBPENDINGSTATUS_NEW,
  DBPENDINGSTATUS_CHANGED,
  DBPENDINGSTATUS_DELETED,
  DBPENDINGSTATUS_UNCHANGED,
  DBPENDINGSTATUS_INVALIDROW
};

"Row States" in

Chapter 5
IRowsetUpdate::

GetPendingRows

IRowsetUpdate::

GetRowStatus

typedef struct tagDBPROP {
  DBPROPID  dwPropertyID;
  DBPROPOPTIONS dwOptions;
  DBPROPSTATUS  dwStatus;
  DBID    colid;
  VARIANT   vValue;
} DBPROP;

"DBPROP Structure" in Chapter 11
DBPROPSET

typedef DWORD DBPROPFLAGS;
enum DBPROPFLAGSENUM {
  DBPROPFLAGS_NOTSUPPORTED,
  DBPROPFLAGS_COLUMN,
  DBPROPFLAGS_DATASOURCE,
  DBPROPFLAGS_DATASOURCECREATE,
  DBPROPFLAGS_DATASOURCEINFO,
  DBPROPFLAGS_DBINIT,
  DBPROPFLAGS_INDEX,
  DBPROPFLAGS_ROWSET,
  DBPROPFLAGS_TABLE,
  DBPROPFLAGS_COLUMNOK,
  DBPROPFLAGS_READ,
  DBPROPFLAGS_WRITE,
  DBPROPFLAGS_REQUIRED,
  DBPROPFLAGS_SESSION
};

"DBPROPFLAGS Enumerated Type" in Chapter 11
DBPROPINFO

typedef struct tagDBPROPIDSET {
  DBPROPID * rgPropertyIDs;
  ULONG  cPropertyIDs;
  GUID   guidPropertySet;
} DBPROPIDSET;

"DBPROPIDSET Structure" in Chapter 11
ICommandProperties::

GetProperties

IDBDataSourceAdmin::

GetCreationProperties

IDBProperties::

GetProperties

IDBProperties::

GetPropertyInfo

IRowsetInfo::

GetProperties

ISessionProperties::

GetProperties

typedef struct tagDBPROPINFO {
  LPOLESTR  pwszDescription;
  DBPROPID  dwPropertyID;
  DBPROPFLAGS dwFlags;
  VARTYPE   vtType;
  VARIANT   vValues;
} DBPROPINFO;

"DBPROPINFO Structure" in Chapter 11
DBPROPINFOSET

typedef struct tagDBPROPINFOSET {
  DBPROPINFO * rgPropertyInfos;
  ULONG    cPropertyInfos;
  GUID   guidPropertySet;
} DBPROPINFOSET;

"DBPROPINFOSET Structure" in Chapter 11
IDBDataSourceAdmin::

GetCreationProperties

IDBProperties::

GetPropertyInfo

typedef DWORD DBPROPOPTIONS;
enum DBPROPOPTIONSENUM {
  DBPROPOPTIONS_REQUIRED,
  DBPROPOPTIONS_OPTIONAL
};

"DBPROPOPTIONS Enumerated Type" in Chapter 11
DBPROP

typedef struct tagDBPROPSET {
  DBPROP * rgProperties;
  ULONG  cProperties;
  GUID   guidPropertySet;
} DBPROPSET;

"DBPROPSET

Structure" in Chapter 11
DBCOLUMNDESC

IColumnsRowset::

GetColumnsRowset

ICommandProperties::

GetProperties

ICommandProperties::

SetProperties

IDBDataSourceAdmin::

CreateDataSource

IDBDataSourceAdmin::

ModifyDataSource

IDBProperties::

GetProperties

IDBProperties::

SetProperties

IDBSchemaRowset::

GetRowset

IIndexDefinition::

CreateIndex

IOpenRowset::

OpenRowset

IRowsetIndex::

GetIndexInfo

IRowsetInfo::GetProperties

ISessionProperties::

GetProperties

ISessionProperties::

SetProperties

ISourcesRowset::

GetSourcesRowset

ITableDefinition::

CreateTable

typedef DWORD DBPROPSTATUS;
enum DBPROPSTATUSENUM {
  DBPROPSTATUS_OK,
  DBPROPSTATUS_NOTSUPPORTED,
  DBPROPSTATUS_BADVALUE,
  DBPROPSTATUS_BADOPTION,
  DBPROPSTATUS_BADCOLUMN,
  DBPROPSTATUS_NOTALLSETTABLE,
  DBPROPSTATUS_NOTSETTABLE,
  DBPROPSTATUS_NOTSET,
  DBPROPSTATUS_CONFLICTING
};

"DBPROPSTATUS Enumerated Type" in Chapter 11
DBPROP

typedef DWORD DBRANGE;
enum DBRANGEENUM {
  DBRANGE_INCLUSIVESTART,
  DBRANGE_INCLUSIVEEND,
  DBRANGE_EXCLUSIVESTART,
  DBRANGE_EXCLUSIVEEND,
  DBRANGE_EXCLUDENULLS,
  DBRANGE_PREFIX,
  DBRANGE_MATCH
};

IRowsetIndex::

SetRange
IRowsetIndex::SetRange

typedef DWORD DBREASON;
enum DBREASONENUM {
  DBREASON_ROWSET_FETCHPOSITIONCHANGE,
  DBREASON_ROWSET_RELEASE,
  DBREASON_ROWSET_CHANGED,
  DBREASON_COLUMN_SET,
  DBREASON_COLUMN_RECALCULATED,
  DBREASON_ROW_ACTIVATE,
  DBREASON_ROW_RELEASE,
  DBREASON_ROW_DELETE,
  DBREASON_ROW_FIRSTCHANGE,
  DBREASON_ROW_INSERT,
  DBREASON_ROW_RESYNCH,
  DBREASON_ROW_UNDOCHANGE,
  DBREASON_ROW_UNDOINSERT,
  DBREASON_ROW_UNDODELETE,
  DBREASON_ROW_UPDATE
};

IRowsetNotify
IRowsetNotify::

OnFieldChange

IRowsetNotify::

OnRowChange

IRowsetNotify::

OnRowsetChange

typedef DWORD DBROWSTATUS;
enum DBROWSTATUSENUM {
  DBROWSTATUS_S_OK,
  DBROWSTATUS_S_MULTIPLECHANGES,
  DBROWSTATUS_S_PENDINGCHANGES,
  DBROWSTATUS_S_NOCHANGE,
  DBROWSTATUS_E_CANCELED,
  DBROWSTATUS_E_CANTRELEASE,
  DBROWSTATUS_E_CONCURRENCYVIOLATION,
  DBROWSTATUS_E_DELETED,
  DBROWSTATUS_E_FAIL,
  DBROWSTATUS_E_PENDINGINSERT,
  DBROWSTATUS_E_NEWLYINSERTED,
  DBROWSTATUS_E_INTEGRITYVIOLATION,
  DBROWSTATUS_E_INVALID,
  DBROWSTATUS_E_MAXPENDCHANGESEXCEEDED,
  DBROWSTATUS_E_OBJECTOPEN,
  DBROWSTATUS_E_OUTOFMEMORY,
  DBROWSTATUS_E_PERMISSIONDENIED,
  DBROWSTATUS_E_LIMITREACHED,
  DBROWSTATUS_E_SCHEMAVIOLATION
};

"Arrays of Errors" in Chapter 13
IRowset::AddRefRows

IRowset::ReleaseRows

IRowsetChange::

DeleteRows

IRowsetLocate::

GetRowsByBookmark

IRowsetLocate::Hash

IRowsetRefresh::

RefreshVisibleData

IRowsetUpdate::Undo

IRowsetUpdate::Update

typedef DWORD DBSEEK;
enum DBSEEKENUM {
  DBSEEK_INVALID,
  DBSEEK_FIRSTEQ,
  DBSEEK_LASTEQ,
  DBSEEK_GE,
  DBSEEK_GT,
  DBSEEK_LE,
  DBSEEK_LT
};

IRowsetIndex::Seek
IRowsetIndex::Seek

typedef DWORD DBSORT;
enum DBSORTENUM {
  DBSORT_ASCENDING = 0,
  DBSORT_DESCENDING
};

IViewSort::

SetSortOrder
IViewSort::SetSortOrder

typedef DWORD DBSOURCETYPE;
enum DBSOURCETYPEENUM {
  DBSOURCETYPE_DATASOURCE,
  DBSOURCETYPE_ENUMERATOR
};

ISourcesRowset::

GetSourcesRowset
ISourcesRowset::

GetSourcesRowset

typedef DWORD DBSTATUS;
enum DBSTATUSENUM {
  DBSTATUS_S_OK,
  DBSTATUS_E_BADACCESSOR,
  DBSTATUS_E_CANTCONVERTVALUE,
  DBSTATUS_S_ISNULL,
  DBSTATUS_S_TRUNCATED,
  DBSTATUS_E_SIGNMISMATCH,
  DBSTATUS_E_DATAOVERFLOW,
  DBSTATUS_E_CANTCREATE,
  DBSTATUS_E_UNAVAILABLE,
  DBSTATUS_E_PERMISSIONDENIED,
  DBSTATUS_S_DEFAULT,DBSTATUS_E_INTEGRITYVIOLATION,
  DBSTATUS_E_SCHEMAVIOLATION,
  DBSTATUS_E_BADSTATUS,
  DBSTATUS_S_DEFAULT
  };

"Status" in Chapter 6

typedef struct tagDBTIME {
  USHORT hour;
  USHORT minute;
  USHORT second;
} DBTIME;

"Type Indicators" in Appendix A

typedef struct tagDBTIMESTAMP {
  SHORT  year;
  USHORT month;
  USHORT day;
  USHORT hour;
  USHORT minute;
  USHORT second;
  ULONG  fraction;
} DBTIMESTAMP;

"Type Indicators" in Appendix A

typedef struct tagFILETIME {
  DWORD  dwLowDateTime;
  DWORD  dwHighDateTime;
} DBFILETIME;

"Type Indicators" in Appendix A

typedef WORD DBTYPE;
enum DBTYPEENUM {
  DBTYPE_EMPTY,
  DBTYPE_NULL,
  DBTYPE_I2,
  DBTYPE_I4,
  DBTYPE_R4,
  DBTYPE_R8,
  DBTYPE_CY,
  DBTYPE_DATE,
  DBTYPE_BSTR,
  DBTYPE_IDISPATCH,
  DBTYPE_ERROR,
  DBTYPE_BOOL,
  DBTYPE_VARIANT,
  DBTYPE_IUNKNOWN,
  DBTYPE_DECIMAL,
  DBTYPE_UI1,
  DBTYPE_ARRAY,
  DBTYPE_BYREF,
  DBTYPE_I1,
  DBTYPE_UI2,
  DBTYPE_UI4,
  DBTYPE_I8,
  DBTYPE_UI8,
  DBTYPE_GUID,
  DBTYPE_VECTOR,
  DBTYPE_RESERVED,
  DBTYPE_BYTES,
  DBTYPE_STR,
  DBTYPE_WSTR,
  DBTYPE_NUMERIC,
  DBTYPE_UDT,
  DBTYPE_DBDATE,
  DBTYPE_DBTIME,
  DBTYPE_DBTIMESTAMP,
  DBTYPE_FILETIME,
DBTYPE_HCHAPTER,
DBTYPE_PROPVARIANT,
DBTYPE_VARNUMERIC
};

"Type Indicators" in Appendix A
DBBINDING

DBCOLUMNINFO

DBPARAMINFO

DBCOLUMNDESC

IConvertType::CanConvert

typedef struct tagDBVECTOR {
  ULONG  size;
  void * ptr;
} DBVECTOR;

"Type Indicators" in Appendix A

typedef struct tagDEC {
 USHORT wReserved;
 union {
  struct {
   BYTE scale;
   BYTE sign;
  };
  USHORT signscale;
 };
 ULONG Hi32;
 union {
  struct {
   ULONG Lo32;
   ULONG Mid32;
  };
  ULONGLONG Lo64;
 };
} DECIMAL;

"Type Indicators" in Appendix A

typedef struct tagERRORINFO {
  HRESULT hrError;
  DWORD dwMinor;
  CLSID clsid;
  IID   iid;
  DISPID  dispid;
} ERRORINFO;

"Error Records" in

Chapter 13
IErrorRecords::

AddErrorRecord

IErrorRecords::

GetBasicErrorInfo

typedef struct XACTTRANSINFO {
  XACTUOW  uow;
  ISOLEVEL isoLevel;
  ULONG  isoFlags;
  DWORD  grfTCSupported;
  DWORD  grfRMSupported;
  DWORD  grfTCSupportedRetaining;
  DWORD  grfRMSupportedRetaining;
} XACTTRANSINFO;

ITransaction::

GetTransactionInfo
ITransaction::

GetTransactionInfo

typedef struct XACTOPT {
  ULONG   ulTimeout;
  unsigned char
   szDescription[MAX_TRAN_DESC];
} XACTOPT

ITransactionOptions::

SetOptions
ITransactionOptions::

GetOptions

ITransactionOptions::

SetOptions


Interface Summary

This appendix lists the prototypes of all interfaces in OLE DB.

interface IAccessor {
HRESULT AddRefAccessor (
HACCESSOR      hAccessor,
ULONG *      pcRefCount);

HRESULT CreateAccessor (
DBACCESSORFLAGS      dwAccessorFlags,
ULONG               cBindings,
const DBBINDING      rgBindings[],
ULONG               cbRowSize,
HACCESSOR *         phAccessor,
DBBINDSTATUS         rgStatus[]);

HRESULT GetBindings (
HACCESSOR            hAccessor,
DBACCESSORFLAGS *   pdwAccessorFlags,
ULONG *            pcBindings,
DBBINDING **         prgBindings);

HRESULT ReleaseAccessor (
HACCESSOR      hAccessor,
ULONG *      pcRefCount);
};

interface IAlterIndex {
HRESULT AlterIndex (
DBID *      pTableID,
DBID *      pIndexID,
DBID *      pNewIndexID,
ULONG         cPropertySets,
DBPROPSET   rgPropertySet[]);
};

interface IAlterTable {
HRESULT AlterColumn (
DBID *            pTableID,
DBID *            pColumnID,
DBCOLUMNDESCFLAGS   ColumnDescFlags,
DBCOLUMNDESC*      pColumnDesc);

DBID *      pTableID,
DBID *      pNewTableID,
ULONG         cPropertySets,
DBPROPSET   rgPropertySet[]);
};

interface IChapteredRowset {
HRESULT AddRefChapter (
HCHAPTER      hChapter,
ULONG *      pcRefCount);

HRESULT ReleaseChapter (
HCHAPTER      hChapter,
ULONG *      pcRefCount);
};

interface IColumnsInfo {
HRESULT GetColumnInfo (
ULONG *            pcColumns,
DBCOLUMNINFO **      prgInfo,
OLECHAR **         ppStringsBuffer);

HRESULT MapColumnIDs (
ULONG         cColumnIDs,
const DBID   rgColumnIDs[],
ULONG         rgColumns[]);
};

interface IColumnsRowset {
HRESULT GetAvailableColumns (
ULONG *   pcOptColumns,
DBID **   prgOptColumns);

HRESULT GetColumnsRowset (
IUnknown *   pUnkOuter,
ULONG         cOptColumns,
const DBID   rgOptColumns[],
REFIID         riid,
ULONG         cPropertySets,
DBPROPSET      rgPropertySets[],
IUnknown **   ppColRowset);
};

interface ICommand {
HRESULT Cancel();

HRESULT Execute (
IUnknown *   pUnkOuter,
REFIID         riid,
DBPARAMS *   pParams,
LONG   *      pcRowsAffected,
IUnknown **   ppRowset);

HRESULT GetDBSession (
REFIID         riid,
IUnknown **   ppSession);
};

interface ICommandPersist {
HRESULT   DeleteCommand(
DBID *   pCommandID);

HRESULT   GetCurrentCommand(
DBID *   pCommandID);

HRESULT   LoadCommand(
DBID *   pCommandID,
DWORD      dwFlags);

HRESULT   SaveCommand(
DBID *   pCommandID,
DWORD   dwFlags);
};

interface ICommandPrepare {
HRESULT Prepare (
ULONG   cExpectedRuns);

HRESULT Unprepare();
};

interface ICommandProperties {
HRESULT GetProperties (
const ULONG         cPropertyIDSets,
const DBPROPIDSET   rgPropertyIDSets[],
ULONG *            pcPropertySets,
DBPROPSET **         prgPropertySets);

HRESULT SetProperties (
ULONG         cPropertySets,
DBPROPSET      rgPropertySets[]);
};

interface ICommandText : ICommand {
HRESULT GetCommandText (
GUID *         pguidDialect,
LPOLESTR *   ppwszCommand);

HRESULT SetCommandText (
REFGUID      rguidDialect,
LPCOLESTR      pwszCommand);
};

interface ICommandWithParameters {
HRESULT GetParameterInfo (
ULONG *         pcParams,
DBPARAMINFO **   prgParamInfo,
OLECHAR **      ppNamesBuffer);

HRESULT MapParameterNames (
ULONG               cParamNames,
const OLECHAR *      rgParamNames[],
LONG               rgParamOrdinals[]);

HRESULT SetParameterInfo (
ULONG                     cParams,
const ULONG               rgParamOrdinals[],
const DBPARAMBINDINFO      rgParamBindInfo[]);
};

interface IConvertType {
HRESULT CanConvert (
DBTYPE            wFromType,
DBTYPE            wToType,
DBCONVERTFLAGS   dwConvertFlags);
};

interface IDBAsynchNotify {
HRESULT OnLowResource (
DWORD      dwReserved);

HRESULT OnProgress (
HCHAPTER      hChapter,
ULONG         ulOperation,
ULONG         ulProgress,
ULONG         ulProgressMax,
ULONG         ulAsynchPhase,
LPOLESTR      pwszStatusText);

HRESULT OnStop (
HCHAPTER      hChapter,
ULONG         ulOperation,
HRESULT      hrStatus,
LPOLESTR      pwszStatusText);
};

interface IDBAsynchStatus {
HRESULT Abort (
HCHAPTER      hChapter,
ULONG         ulOperation);

HRESULT GetStatus (
HCHAPTER      hChapter,
ULONG         ulOperation,
ULONG *      pulProgress,
ULONG *      pulProgressMax,
ULONG *      pulAsynchPhase,
LPOLESTR *   ppwszStatusText);
};

interface IDBCreateCommand {
HRESULT CreateCommand(
IUnknown *   pUnkOuter,
REFIID         riid,
IUnknown **   ppCommand);
};

interface IDBCreateSession {
HRESULT CreateSession (
IUnknown *   pUnkOuter,
REFIID         riid,
IUnknown **   ppDBSession);
};

interface IDBDataSourceAdmin {
HRESULT CreateDataSource(
ULONG         cPropertySets,
DBPROPSET      rgPropertySets[],
IUnknown *   pUnkOuter,
REFIID         riid,
IUnknown **   ppSession);

HRESULT DestroyDataSource ();

HRESULT GetCreationProperties (
ULONG               cPropertyIDSets,
const DBPROPIDSET   rgPropertyIDSets[],
ULONG *            pcPropertyInfoSets,
DBPROPINFOSET **   prgPropertyInfoSets,
OLECHAR **         ppDescBuffer);

HRESULT ModifyDataSource (
ULONG         cPropertySets,
DBPROPSET      rgPropertySets[]);
};

interface IDBInfo {
HRESULT GetKeywords(
LPOLESTR *   ppwszKeywords);

HRESULT GetLiteralInfo(
ULONG               cLiterals,
const DBLITERAL      rgLiterals[],
ULONG *            pcLiteralInfo,
DBLITERALINFO **   prgLiteralInfo,
OLECHAR **         ppCharBuffer);
};

interface IDBInitialize {
HRESULT Initialize();

HRESULT Uninitialize();
};

interface IDBProperties {
HRESULT GetProperties (
ULONG               cPropertyIDSets,
const DBPROPIDSET   rgPropertyIDSets[],
ULONG *            pcPropertySets,
DBPROPSET **         prgPropertySets);

HRESULT GetPropertyInfo(
ULONG               cPropertyIDSets,
const DBPROPIDSET   rgPropertyIDSets[],
ULONG *            pcPropertyInfoSets,
DBPROPINFOSET **   prgPropertyInfoSets,
OLECHAR **         ppDescBuffer);

HRESULT SetProperties (
ULONG         cPropertySets,
DBPROPSET      rgPropertySets[]);
};

interface IDBSchemaRowset {
HRESULT GetRowset (
IUnknown *      punkOuter,
REFGUID         rguidSchema,
ULONG            cRestrictions,
const VARIANT   rgRestrictions[],
REFIID            riid,
ULONG            cPropertySets,
DBPROPSET         rgPropertySets[],
IUnknown **      ppRowset);

HRESULT GetSchemas (
ULONG *   pcSchemas,
GUID **   prgSchemas,
ULONG**   prgRestrictionSupport);
};

interface IErrorInfo {
HRESULT GetDescription (
BSTR *   pbstrDescription);

HRESULT GetGUID (
GUID *   pguid);

HRESULT GetHelpContext (
DWORD *   pdwHelpContext);

HRESULT GetHelpFile (
BSTR *   pbstrHelpFile);

HRESULT GetSource (
BSTR *   pbstrSource);
};

interface IErrorLookup {
HRESULT GetErrorDescription (
HRESULT         hrError,
DWORD            dwLookupID,
DISPPARAMS *      pdispparams,
LCID            lcid,
BSTR *            pbstrSource,
BSTR *            pbstrDescription);

HRESULT GetHelpInfo (
HRESULT   hrError,
DWORD      dwLookupID,
LCID      lcid,
BSTR *      pbstrHelpFile,
DWORD *   pdwHelpContext);

HRESULT ReleaseErrors (
const DWORD   dwDynamicErrorID);
};

interface IErrorRecords {
HRESULT AddErrorRecord (
ERRORINFO *      pErrorInfo,
DWORD            dwLookupID,
DISPPARAMS *      pdispparams,
IUnknown *      punkCustomError,
DWORD            dwDynamicErrorID);

HRESULT GetBasicErrorInfo (
ULONG         ulRecordNum,
ERRORINFO *   pErrorInfo);

HRESULT GetCustomErrorObject (
ULONG         ulRecordNum,
REFIID         riid,
IUnknown **   ppObject);

HRESULT GetErrorInfo (
ULONG            ulRecordNum,
LCID            lcid,
IErrorInfo **   ppErrorInfo);

HRESULT GetErrorParameters (
ULONG            ulRecordNum,
DISPPARAMS *      pdispparams);

HRESULT GetRecordCount (
ULONG *   pcRecords);
};

interface IGetDataSource {
HRESULT GetDataSource (
REFIID       riid,
IUnknown **   ppDataSource);
};

interface IIndexDefinition {
HRESULT CreateIndex(
DBID *                        pTableID,
DBID *                        pIndexID,
ULONG                        cIndexColumnDescs,
const DBINDEXCOLUMNDESC   rgIndexColumnDescs[],
ULONG                        cPropertySets,
DBPROPSET                     rgPropertySets[],
DBID **                     ppIndexID);

HRESULT DropIndex(
DBID *   pTableID,
DBID *   pIndexID);
};

interface IMultipleResults {
HRESULT GetResult(
IUnknown *   pUnkOuter,
LONG         lReserved,
REFIID         riid,
LONG *         pcRowsAffected,
IUnknown **   ppRowset);
};

interface IOpenRowset {
HRESULT OpenRowset(
IUnknown *   pUnkOuter,
DBID *         pTableID,
DBID *         pIndexID,
REFIID         riid,
ULONG         cPropertySets,
DBPROPSET      rgPropertySets[],
IUnknown **   ppRowset);
};

interface IRowset {
HRESULT AddRefRows(
ULONG         cRows,
const HROW   rghRows[],
ULONG         rgRefCounts[],
DBROWSTATUS   rgRowStatus[]);

HRESULT GetData (
HROW         hRow,
HACCESSOR      hAccessor,
void *         pData);

HRESULT GetNextRows (
HCHAPTER   hChapter,
LONG      lRowsOffset,
LONG      cRows,
ULONG *   pcRowsObtained,
HROW **   prghRows);

HRESULT ReleaseRows (
ULONG            cRows,
const HROW      rghRows[],
DBROWOPTIONS      rgRowOptions[]
ULONG            rgRefCounts[],
DBROWSTATUS      rgRowStatus[]);

HRESULT RestartPosition (
HCHAPTER   hChapter);
};

interface IRowsetChange {
HRESULT DeleteRows (
HCHAPTER      hChapter,
ULONG         cRows,
const HROW   rghRows[],
DBROWSTATUS   rgRowStatus[]);

HRESULT InsertRow (
HCHAPTER      hChapter,
HACCESSOR      hAccessor,
void *         pData,
HROW   *      phRow);

HRESULT SetData (
HROW         hRow,
HACCESSOR      hAccessor,
void *         pData);
};

interface IRowsetChapterMember {
HRESULT IsRowInChapter (
HCHAPTER      hChapter,
HROW         hRow);
};

interface IRowsetFind {
HRESULT FindNextRow (
HCHAPTER         hChapter,
HACCESSOR      hAccessor,
void *         pFindValue,
DBCOMPAREOP      CompareOp,
ULONG            cbBookmark,
const BYTE *   pBookmark,
BOOL            fSkipStartRow,
LONG            cRows,
ULONG *         pcRowsObtained,
HROW **         prghRows);
};

interface IRowsetIdentity {
HRESULT IsSameRow (
HROW   hThisRow,
HROW   hThatRow);
};

interface IRowsetIndex {
HRESULT GetIndexInfo (
ULONG *                  pcKeyColumns,
DBINDEXCOLUMNDESC **      prgIndexColumnDesc,
ULONG *                  pcIndexProperties,
DBPROPSET **               prgIndexProperties);

HRESULT Seek (
HACCESSOR   hAccessor,
ULONG         cKeyValues,
void *         pData,
DBSEEK         dwSeekOptions);

HRESULT SetRange (
HACCESSOR   hAccessor,
ULONG         cStartKeyColumns,
void *         pStartData,
ULONG         cEndKeyColumns,
void *         pEndData,
DBRANGE      dwRangeOptions);
};

interface IRowsetInfo {
HRESULT GetProperties (
const ULONG         cPropertyIDSets,
const DBPROPIDSET   rgPropertyIDSets[],
ULONG *            pcPropertySets,
DBPROPSET **         prgPropertySets);

HRESULT GetReferencedRowset (
ULONG         iOrdinal,
REFIID         riid,
IUnknown **   ppReferencedRowset);

HRESULT GetSpecification (
REFIID       riid,
IUnknown **   ppSpecification);
};

interface IRowsetLocate : IRowset {
HRESULT Compare (
HCHAPTER         hChapter,
ULONG            cbBookmark1,
const BYTE *      pBookmark1,
ULONG            cbBookmark2,
const BYTE *      pBookmark2,
DBCOMPARE *      pComparison);

HRESULT GetRowsAt (
HWATCHREGION      hReserved1,
HCHAPTER         hChapter
ULONG            cbBookmark,
const BYTE *      pBookmark,
LONG            lRowsOffset,
LONG            cRows,
ULONG *         pcRowsObtained,
HROW **         prghRows);

HRESULT GetRowsByBookmark (
HCHAPTER         hChapter,
ULONG            cRows,
const ULONG      rgcbBookmarks[],
const BYTE *      rgpBookmarks[],
HROW            rghRows[],
DBROWSTATUS      rgRowStatus[]);

HRESULT Hash (
HCHAPTER         hChapter,
ULONG            cBookmarks,
const ULONG      rgcbBookmarks[],
const BYTE *      rgpBookmarks[],
DWORD            rgHashedValues[],
DBROWSTATUS      rgBookmarkStatus[]);
};

interface IRowsetNotify {
HRESULT OnFieldChange (
IRowset*         pRowset,
HROW            hRow,
ULONG            cColumns,
ULONG            rgColumns[],
DBREASON         eReason,
DBEVENTPHASE      ePhase,
BOOL            fCantDeny);

HRESULT OnRowChange (
IRowset*         pRowset,
ULONG            cRows,
const HROW      rghRows[],
DBREASON         eReason,
DBEVENTPHASE      ePhase,
BOOL            fCantDeny);

HRESULT OnRowsetChange (
IRowset*         pRowset,
DBREASON         eReason,
DBEVENTPHASE      ePhase,
BOOL            fCantDeny);
};

interface IRowsetRefresh {
HRESULT GetLastVisibleData (
HROW         hRow,
HACCESSOR      hAccessor,
void *         pData);

HRESULT RefreshVisibleData (
HCHAPTER          hChapter,
ULONG            cRows,
const HROW      rghRows[],
BOOL                fOverWrite,
ULONG*            pcRowsRefreshed,
HROW**            prghRowsRefreshed,
DBROWSTATUS**   prgRowStatus);
};

interface IRowsetScroll : IRowsetLocate {
HRESULT GetApproximatePosition (
HCHAPTER         hChapter,
ULONG            cbBookmark,
const BYTE *      pBookmark,
ULONG *         pulPosition,
ULONG *         pcRows);

HRESULT GetRowsAtRatio (
HWATCHREGION      hReserved1,
HCHAPTER         hChapter,
ULONG            ulNumerator,
ULONG            ulDenominator,
LONG            cRows,
ULONG *         pcRowsObtained,
HROW **         prghRows);
};

interface IRowsetUpdate : IRowsetChange {
HRESULT GetOriginalData (
HROW         hRow,
HACCESSOR      hAccessor,
void *         pData);

HRESULT GetPendingRows (
HCHAPTER               hChapter,
DBPENDINGSTATUS         dwRowStatus,
ULONG *               pcPendingRows,
HROW **               prgPendingRows,
DBPENDINGSTATUS **      prgPendingStatus);

HRESULT GetRowStatus(
HCHAPTER            hChapter,
ULONG               cRows,
const HROW         rghRows[],
DBPENDINGSTATUS      rgPendingStatus[]);

HRESULT Undo (
HCHAPTER         hChapter,
ULONG            cRows,
const HROW      rghRows[],
ULONG *         pcRows,
HROW **         prgRows,
DBROWSTATUS **   prgRowStatus);

HRESULT Update (
HCHAPTER         hChapter,
ULONG            cRows,
const HROW      rghRows[],
ULONG *         pcRows,
HROW **         prgRows,
DBROWSTATUS**   prgRowStatus);
};

interface IRowsetView {
HRESULT CreateView (
IUnknown *         pUnkOuter,
REFIID            riid,
IUnknown **         ppView);

HRESULT GetView (
HCHAPTER            hChapter,
REFIID            riid,
HCHAPTER *         phChapterSource,
IUnknown **         ppView);
};

interface ISessionProperties {
HRESULT GetProperties (
ULONG               cPropertyIDSets,
const DBPROPIDSET   rgPropertyIDSets[],
ULONG *            pcPropertySets,
DBPROPSET **         prgPropertySets);

HRESULT SetProperties (
ULONG         cPropertySets,
DBPROPSET      rgPropertySets[]);
};

interface ISourcesRowset {
HRESULT GetSourcesRowset(
IUnknown      pUnkOuter,
REFIID         riid,
ULONG         cPropertySets,
DBPROPSET      rgPropertySets[],
IUnknown **   ppSourcesRowset);
};

interface ISQLErrorInfo {
HRESULT GetSQLInfo (
BSTR *   pbstrSQLState,
LONG *   plNativeError);
};

interface ISupportErrorInfo {
HRESULT InterfaceSupportsErrorInfo (
REFIID      riid);
};

interface ITableDefinition {
HRESULT AddColumn(
DBID *            pTableID,
DBCOLUMNDESC *   pColumnDesc,
DBID **         ppColumnID);

HRESULT CreateTable(
IUnknown *      pUnkOuter,
DBID *            pTableID,
ULONG            cColumnDescs,
DBCOLUMNDESC      rgColumnDescs[],
REFIID            riid,
ULONG            cPropertySets,
DBPROPSET         rgPropertySets[],
DBID **         ppTableID,
IUnknown **      ppRowset);

HRESULT DropColumn(
DBID*   pTableID,
DBID*   pColumnID);

HRESULT DropTable (
DBID *   pTableID);
};

interface ITransaction {
HRESULT Abort(
BOID *      pboidReason,
BOOL      fRetaining,
BOOL      fAsync);

HRESULT Commit(
BOOL   fRetaining,
DWORD   grfTC,
DWORD   grfRM);

HRESULT GetTransactionInfo(
XACTTRANSINFO *      pInfo);
};

interface ITransactionJoin {
HRESULT GetOptionsObject (
ITransactionOptions **   ppOptions);

HRESULT JoinTransaction (
IUnknown *               punkTransactionCoord,
ISOLEVEL                  isoLevel,
ULONG                     isoFlags,
ITransactionOptions *      pOtherOptions);
};

interface ITransactionLocal : ITransaction {
HRESULT GetOptionsObject (
ITransactionOptions **   ppOptions);

HRESULT StartTransaction (
ISOLEVEL                  isoLevel,
ULONG                     isoFlags,
ITransactionOptions *      pOtherOptions,
ULONG *                  pulTransactionLevel);
};

interface ITransactionObject {
HRESULT GetTransactionObject (
ULONG               ulTransactionLevel,
ITransaction **      ppTransactionObject);
};

interface ITransactionOptions {
HRESULT GetOptions(
XACTOPT *      pOptions);

HRESULT SetOptions(
XACTOPT *      pOptions);
};

interface IViewChapter {
HRESULT GetSpecification (
REFIID         riid,
IUnknown **      ppRowset);

HRESULT OpenViewChapter (
HCHAPTER         hSource,
HCHAPTER *      phViewChapter);
};

interface IViewFilter {
HRESULT GetFilter (
HACCESSOR      hAccessor,
ULONG *         pcRows,
DBCOMPAREOP *   pCompareOps[],
void *         pCriteriaData);

HRESULT GetFilterBindings(
ULONG *         pcBindings,
DBBINDING **      prgBindings);

HRESULT SetFilter (
HACCESSOR      hAccessor,
ULONG            cRows,
DBCOMPAREOP      CompareOps [],
void *         pCriteriaData);
};

interface IViewRowset {
HRESULT GetSpecification (
REFIID         riid,
IUnknown **      ppObject);

HRESULT OpenViewRowset (
IUnknown *      pUnkOuter,
REFIID         riid,
IUnknown **   ppRowset);
};

interface IViewSort {
HRESULT GetSort (
ULONG *         pcColumns,
ULONG *         prgColumns[],
DBSORT *         prgOrders[]);

HRESULT SetSort (
ULONG            cColumns,
const ULONG      rgColumns[],
const DBSORT      rgOrders[]);
};


Glossary

A

Abort To return the values changed by a transaction to their original state.

Accessor A collection of information that describes how data is stored in the consumer's buffer. The provider uses this information to determine how to transfer data to and from this buffer. See also reference accessor.

Accessor handle A handle that identifies an accessor.

Auto-commit mode A transaction commit mode in which all actions taken in a transaction are committed immediately after they are performed.

B

Binary large object (BLOB) Any binary or character data over a certain number of bytes, such as 255. Typically much longer. Such data is generally sent to and retrieved from the data source in parts. Also known as long data.

Binding As a verb, the act of associating a column in a rowset or a parameter in a text command with a consumer variable. As a noun, the association or the DBBINDING structure that describes the association.

Bookmark A value that identifies a row in a rowset. Bookmarks are saved by the consumer and used later in the life of the rowset to retrieve a particular row.

BSTR A pointer to a null-terminated character string in which the string length is stored with the string.

B-tree A tree structure for storing database indexes.

Buffer A piece of memory used to pass information, usually data, between the consumer and provider.

C

Catalog A database object that contains one or more schemas.

Change To update, delete, or insert a row of data.

Class ID A globally unique identifier (GUID) associated with an OLE class object. If a class object will be used to create more than one instance of an object, the associated server application should register its CLSID in the system registry so that clients can locate and load the executable code associated with the object(s). Every OLE server or container that allows linking to its embedded objects must register a CLSID for each supported object definition.

Client/server A database access strategy in which one or more clients access data through a server. The clients generally implement the user interface while the server controls database access.

Column The container for a single item of information in a row. Also known as field.

Column ID A structure used to identify a column, primarily in a command where there are no stable ordinals or column names.

COM object An object that conforms to the OLE Component Object Model (COM). A COM object is an instance of an object definition, which specifies the object's data and one or more implementations of interfaces on the object. Clients interact with a COM object only through its interfaces. See also Component Object Model and interface.

Command An OLE DB object that encapsulates a command.

Command text The text command associated with a command object. Although it is not required to be, this is usually an SQL statement.

Commit To make the changes in a transaction permanent.

Component An object that encapsulates both data and code, and provides a well-specified set of publicly available services.

Component Object Model (COM) The OLE object-oriented programming model that defines how objects interact within a single process or between processes. In COM, clients have access to an object through interfaces implemented on the object. See also interface.

Concurrency The ability of more than one transaction to access the same data at the same time.

Consumer Software that calls OLE DB methods and interfaces.

Container A file containing linked or embedded objects.

Convert To change data from one type to another, such as from an integer to a string.

Coordinated transaction A transaction composed of multiple, subordinate transactions. All of the subordinate transactions must succeed for the coordinated transaction to succeed. Also known as a distributed transaction.

CoType A way to define a group of COM objects, such as rowsets or commands, that have similar characteristics. All COM objects that belong to a particular CoType must expose the mandatory interfaces in that CoType. In addition, they can expose the optional interfaces in the CoType and any interfaces not in the CoType.

D

Data The data for a parameter in a text command or a column in a row.

Data Definition Language (DDL) Those text commands that define, as opposed to manipulate, data. For example, the SQL statements CREATE TABLE, CREATE INDEX, GRANT, and REVOKE.

Data Manipulation Language (DML) Those text commands that manipulate, as opposed to define, data. For example, the SQL statements INSERT, UPDATE, DELETE, and SELECT.

Data part Data has three parts: the data value, the length of the data value, and the status of the data value.

Data provider A provider that directly exposes data, as opposed to a service component.

Data source The data the user wants to access, such as the data in a database, file, or array. This is distinct from a data source object.

Data source object An OLE DB object that connects to a data source. This is distinct from the data source.

Data type The type of a piece of data. See also type indicator.

Database A discrete collection of data in a DBMS. Also a DBMS.

Database Management System (DBMS) A layer of software between the physical database and the user. The DBMS manages all access to the database. An OLE DB provider can be built directly on top of a DBMS or as a layer between the DBMS and the consumer.

Deferred column A column for which the data is not instantiated in the rowset until the consumer actually attempts to access that data.

Delayed update mode An update mode in which changes made through IRowsetChange are cached in the rowset and transmitted to the data source only when IRowsetUpdate::Update is called. See also immediate update mode.

Delete To remove an existing row of data from the data source.

Distributed transaction See coordinated transaction.

Dynamic error An error message that cannot be predicted because it is not provider specific.Dynamic error ID An identifier that is used by a lookup service and is associated with an error object.

E

Empty string A zero-length string. See also null pointer and NULL value.

Endogenous change A change to data originating from a process other than your own.

Enumerator An OLE DB object that searches for data sources and other enumerators. See also root enumerator.

Error A condition in which a fatal error occurred. Also used to refer to any error, regardless of whether it is fatal.

Error code A class of return code, of the type HRESULT, that begins with E_ or DB_E_ and indicates that the method failed completely and was unable to do any useful work.

Error lookup service A method used by a provider to handle and interpret OLE DB error objects.

Error object An object that contains detailed information about an error. See also OLE Automation error object and OLE DB error object.

Event An action taken by a provider, such as changing a column value or deleting a row, of which the provider notifies the consumer.

Execution plan A plan generated by a query engine to execute a command. Equivalent to executable code compiled from a third-generation language such as C.

F

Fetch To retrieve one or more rows from the data source and instantiate them in a rowset.

Field See column.

Fixed-length data type A data type that is always stored in the same number of bytes, such as a two-byte integer. See also variable-length data type.

Foreign key A column or columns in a table that match the primary key in another table.

G

Getting data Transferring data from the provider to the consumer, as in getting column or output parameter data.

Globally Unique Identifier (GUID) A 16-byte value that uniquely identifies something, usually the software that implements one or more COM objects or an interface on one of those objects. Also known as a UUID (Universally Unique Identifier).

H

Handle A value that uniquely identifies something such as a row or an accessor. Handles are meaningful only to the software that creates and uses them, but are passed by other software to identify things.

HRESULT An opaque result handle defined to be zero for a successful return from a function and nonzero if error or status information is returned.

I

Interface Identifier (IID) A globally unique identifier (GUID) associated with an interface. Some functions take IIDs as parameters to allow the caller to specify which interface pointer should be returned.

Immediate update mode An update mode in which changes made through IRowsetChange are immediately transmitted to the data source. See also delayed update mode.

Index rowset A rowset built over an index. Each row in an index rowset contains a bookmark that points to a row in a rowset built over the corresponding table.

Initialize To change the state of an enumerator or data source object so it can be used to access data. For example, initializing a data source object might require the provider to open a data file or connect to a database.

Input parameter A parameter in a text command for which the consumer supplies a value to the provider.

Input/output parameter A parameter in a text command for which the consumer supplies a value to the provider and the provider returns a value to the consumer.

Insert To add a new row of data to the data source.

Instantiate To create an instance of a COM object.

Interface A group of semantically related functions that provide access to a COM object. Each OLE interface defines a contract that allows objects to interact according to the Component Object Model (COM). Although OLE provides many interface implementations, most interfaces can also be implemented by developers designing OLE applications. See also Component Object Model and COM object.

IPersist* object An OLE object that supports IPersistStream, IPersistStreamInit, or IPersistStorage.

Isolation level See transaction isolation level.

J

Join An operation in a relational database that links the rows in two or more tables by matching values in specified columns.

K

Key A column or columns whose values identify a row. See also primary key and foreign key.

Key value bookmark A bookmark that uses a unique key to identify a row. See also numeric bookmark.

L

Length The length of a data value. See also data part, value, and status.

Listener A consumer that has requested that a provider send it notifications of various events.

Long data See BLOB.

M

Manual-commit mode A transaction commit mode in which transactions must be explicitly committed or aborted by calling ITransaction::Commit or ITransaction::Abort.

Maximum precision The maximum number of base 10 digits that can be stored in a particular data type. See also precision.

Metadata Data that describes a parameter in a text command or a column in a rowset. For example, the data type, length, and updatability of a column.

Method A function in an interface.

Moniker A name for a specific object instance.

Multiple results object An OLE DB object created by executing a command and through which multiple results (rowsets or row counts) can be retrieved.

N

Next fetch position The position of the next row that will be fetched by a call to IRowset::GetNextRows.

Notification A call from a provider to a consumer, in which the provider notifies the consumer that a particular event is occurring.

Notification sink An actual object that implements the notification interface.

Null pointer A pointer with a value of zero. It is an error to dereference a null pointer. See also empty string and NULL value.

NULL value Having no explicitly assigned value. In particular, a NULL value is different from a zero or a blank. See also empty string and null pointer.

Numeric bookmark A bookmark that uses a unique number to identify a row. See also key value bookmark.

O

Object In OLE, a programming structure encapsulating both data and functionality that are defined and allocated as a single unit and for which the only public access is through the programming structure's interfaces. A COM object must support, at a minimum, the IUnknown interface, which maintains the object's existence while it is being used and provides access to the object's other interfaces. See also Component Object Model and interface.

OLE Microsoft's object-based technology for sharing information and services across process and machine boundaries.

OLE Automation error object An error object that conforms to the standards specified for such objects by OLE Automation. See also OLE DB error object.

OLE DB A set of interfaces that expose data from a variety of data sources using COM.

OLE DB error object An error object used by OLE DB objects to return an error. OLE DB error objects are an extension of Automation error objects.

OLE DB object A COM object defined by OLE DB. The COM objects defined by OLE DB are enumerators, data source objects, sessions, commands, rowsets, multiple results objects, OLE DB error objects, transaction objects, and transaction options objects.

OLE DB Software Development Kit (SDK) A collection of redistributable software, header files, tools, sample code, and documentation to be used by developers of OLE DB consumers and providers.

OLE object In OLE DB, a COM object that is stored in a column. In OLE, a COM object.

Optimistic concurrency A strategy to increase concurrency in which rows are not locked. Instead, before they are updated or deleted, a rowset checks to see if they have been changed since they were last read. If so, the update or delete fails. See also pessimistic concurrency.

Ordinary bookmark A bookmark whose value is defined by the provider. See also standard bookmark.

Output parameter A parameter in a text command for which the provider returns a value to the consumer.

P

Parallel processing A method of processing that can run only on a computer that contains two or more processors running simultaneously.

Parameter A variable in a text command. A parameter can be an input, input/output, or output parameter.

Pending change A change that has been cached in a rowset and not yet transmitted to the data source. See also delayed update mode and immediate update mode.

Persist To save the current state of a COM object, such as to a file. Currently, only data source objects can be persisted.

Pessimistic concurrency A strategy for implementing serializability in which rows are locked so that other transactions cannot change them. See also optimistic concurrency.

Phase A step in a sequence of notifications caused by a single event. The sequence of notifications is similar to the phases in a two-phase commit protocol.

Precision The number of base 10 digits in a number. See also maximum precision.

Prepare To compile a command. An execution plan is created by preparing a command.

Preserved rowset A rowset in which all previously defined functionality is retained after a commit or abort.

Primary key A column or columns that uniquely identifies a row in a table.

Procedure A group of one or more precompiled commands (generally SQL statements) that are stored as a named object in a database.

Property Attributes of an OLE DB object. For example, the maximum number of rows in a rowset that can be active at one time.

Property group The set of all properties that apply to a particular OLE DB object.

Property set A property is identified by a GUID and an integer (the property ID). A property set is the set of all properties that share the same GUID.

Provider Software that implements OLE DB methods and interfaces.

Q

Query A text command. Sometimes used to mean a text command that creates a rowset.

R

Reason The specific event that occurred, such as changing a row value or deleting a row.

Record See row.

Reference counting Keeping a count of each interface pointer held on an object to ensure that the object is not destroyed before all references to it are released. In OLE DB, rows and accessors are also reference counted.

Reference accessor An accessor that enables a consumer to get rowset data directly from the provider's buffer. Support for reference accessors is optional.

Release To decrease the reference count on a row, accessor, or COM object. When the reference count reaches zero, the provider generally releases the resources used by the row, accessor, or COM object.

Result A row count or rowset created by executing a command. See also multiple results object.

Resynchronize To update the data in a rowset with the data in the data source that is visible to the current transaction according to its isolation level.

Return code The value returned by an OLE DB method.

Root enumerator An enumerator shipped in the OLE DB SDK that enumerates the data sources and enumerators listed in the registry. See also enumerator.

Row A set of related columns that describe a specific entity. Also known as a record.

Row handle A handle used to identify a row.

Rowset An OLE DB object that contains a set of rows, each of which has columns of data.

S

Scale The number of digits in a number that are to the right of the decimal point.

Schema A database object that contains one or more tables, often created by a single user.

Schema rowset A predefined rowset that provides information about the structure of a database.

Self bookmark A bookmark, stored in column 0 of a row, that is used to return to that row.

Serializability Whether two transactions executing simultaneously produce a result that is the same as the serial (or sequential) execution of those transactions. Serializable transactions are required to maintain database integrity.

Service component A provider that does not directly expose data, but instead provides a service, such as query processing. Used in conjunction with data providers.

Session An OLE DB object that serves as the context for a transaction.

Setting data Transferring data from the consumer to the provider, as in setting column or input parameter data.

SQL Structured Query Language. A language used by relational databases to query, update, and manage data. Text commands often use SQL.

Standard bookmark A bookmark whose value is defined by OLE DB. See also ordinary bookmark.

Static error An error (text) that is stored by a specific provider lookup service, and most commomly tied to a single data store.

Status The status of a data value. See also data part, value, and length.

Storage interface An interface used to access data in a storage object: ISequentialStream, IStream, IStorage, or ILockBytes.

Storage object A COM object that implements a storage interface. Storage objects are used to access BLOB data and OLE objects stored in a column.

Success A condition in which no errors occurred.

Success code A class of return code, of the type HRESULT, that begins with S_ or DB_S_ and indicates success of the method.

T

Table A collection of rows in the data source.

Text command A text string, usually an SQL statement, that defines a command.

Transaction An atomic unit of work. The work in a transaction must be completed as a whole; if any part of the transaction fails, the entire transaction fails.

Transaction isolation The act of isolating one transaction from the effects of all other transactions.

Transaction isolation level A measure of how well a transaction is isolated.

Transaction object An OLE DB object used to support transactions.

Transaction options object An OLE DB object used to define various options for a transaction.

Transfer To move data between the consumer's and provider's buffers. The provider, not the consumer, transfers data. See also getting data and setting data.

Transmit To send changes made through IRowsetChange to the data source. See also delayed update mode and immediate update mode.

Transmitted change A change that has been sent to the data source. See also pending change.

Truncate To discard one or more bytes of variable-length data or non-significant digits of numeric data. Truncation results in a warning condition when getting data and an error condition when setting data.

Type indicator An integer value passed to or returned from an OLE DB method to indicate the data type of a consumer variable, a parameter, or a column.

U

Uninitialize To change the state of an enumerator or data source object so it cannot be used to access data. For example, uninitializing a data source object might require the provider to close a data file or disconnect from a database.

Unprepare To discard the current execution plan.

Update To change an existing row of data in the data source. Also, to transmit pending changes to the data source.

User The end user, which is a generally a person, as opposed to the consumer, which is a piece of software.

V

Value A data value. See also data part, length, and status.

Variable-length data type A data type for which the length of the data can vary, such as a string. See also fixed-length data type.

Visibility Whether data values can be detected by a rowset. Refers both to the visibility of data in a data source and data cached in a rowset.

W

Warning A condition in which a nonfatal error occurred.

Warning code A class of return code, of the type HRESULT, that begins with S_ or DB_S_ and indicates success of the method but with a warning.

Z

Zombie A state in which the only valid consumer action on a COM object is generally to release that object.


Microsoft OLE DB Providers Overview

An OLE DB data provider is an application that responds to OLE DB calls and provides data in a usable tabular form. The provider is a COM component with an OLE DB-compliant interface.

An OLE DB provider is not required to support the complete OLE DB interface, which makes it easier for an application to become an OLE DB provider.

For information on support for Microsoft data access components, see the Getting Help from Microsoft Technical Support page

© 1998 Microsoft Corporation. All rights reserved.

The following OLE DB providers are currently available:

  • Microsoft OLE DB ODBC Provider for accessing ODBC data.

  • Microsoft OLE DB Oracle Provider for accessing Oracle database data.

  • Microsoft OLE DB SQL Server Provider for accessing data on one or more SQL servers.

  • Microsoft OLE DB Jet Provider for accessing Microsoft Jet database data.

  • Other available OLE DB providers are:

  • Microsoft OLE DB Provider for Microsoft Active Directory Service (ADS) that provides access and management services for network resources and access to directory name space.

  • Microsoft OLE DB Provider for Microsoft Index Server that provides access to indexed file system and web data using SQL queries.

  • There are also many commercial OLE DB providers available.


Introduction

The OLE DB SDK contains an OLE DB provider for ODBC data. This document describes the ODBC Provider, explains the procedures for installing, configuring, and testing the provider, and lists the limitations of the current implementation.

OLE DB Specification Compliance

The ODBC Provider is compliant with version 1.5 and above of the OLE DB Specification, which is included in the OLE DB SDK.

ODBC Version

Make sure that you use the latest version of ODBC (ODBC 3.51) with the ODBC Provider.


What's New

Summary of New Features

The ODBC Provider supports the following new features:

  • Named parameters. You no longer have to specify ordinal numbers of parameters in stored procedures.

  • Approximate scrolling. IRowsetScroll is now completely supported with ODBC 3.x drivers that support the appropriate ODBC diagnostic fields. The "Interface Implementation Notes" contain information on what an ODBC 3.x driver needs to enable this functionality.

  • GUID data type. The ODBC 3.5 GUID data type is fully supported.

  • Bookmarks on newly added rows. The ODBC Provider supports the ability to retrieve the bookmark associated with a newly added row.

  • Driver-specific errors. The ODBC Provider includes new extensions that support driver-specific error fields.

  • Microsoft Transaction Server (see "ITransactionJoin").

  • Expanded data conversion.

  • ISequentialStream support for parameter binding.


Named Parameters

Named Parameter Support

ODBC 3.x supports named parameters in stored procedures. When you use a named parameter, you do not have to specify its ordinal position in the procedure definition, which was required in previous versions. Therefore, not only can you simply name parameters in an application, but you can also skip parameters with default values in binding.

If the data consumer wants to use named parameters, it needs to set pwszName in the DBPARAMBINDINFO structure to a name in ICommandWithParameters::SetParameterInfo. Otherwise, pwszName should be set to NULL.

Named and unnamed parameters should not be mixed. If they are, the result is undefined.


Error Support

A feature introduced in ODBC 3.0 is that of diagnostic fields. Following is information on how to use the ODBC Provider to access error information from those diagnostic fields.

To retrieve diagnostic information, the consumer registers the diagnostic fields of interest beforehand. The ODBC Provider includes a new interface, ISQLRequestDiagFields, to facilitate this process.

When an OLE DB consumer needs to retrieve certain diagnostic fields after a method call, it first calls ISQLRequestDiagFields::RequestDiagFields on the OLE DB object before calling the method to notify the ODBC Provider that it may request the diagnostic information. The ODBC Provider then caches those diagnostic fields. If the consumer does not call ISQLRequestDiagFields::RequestDiagFields, the ODBC Provider does not cache any extra diagnostic data except the information needed in the existing OLE DB error model.

ISQLRequestDiagFields (defined in the header file Msdasql.h) is an optional interface of any OLE DB object implemented in the ODBC Provider. Any object that supports diagnostic fields exposes this interface. It has one method, RequestDiagFields. Here is its calling convention:

enum KAGREQDIAGFLAGSENUM
 {   KAGREQDIAGFLAGS_HEADER  = 0x1,
    KAGREQDIAGFLAGS_RECORD  = 0x2
  };

typedef struct  tagKAGREQDIAG
{
 ULONG ulDiagFlags;
 VARTYPE vt;
 SHORT sDiagField;
} KAGREQDIAG;

ISQLRequestDiagFields : public IUnknown
{
public:
  virtual HRESULT STDMETHODCALLTYPE RequestDiagFields(
  ULONG cDiagFields,
  KAGREQDIAG rgDiagFields[  ]) = 0;

};

cDiagFields is the number of diagnostic fields to be requested; rgDiagFields is an array of KAGREQDIAG. KAGREQDIAG describes the diagnostic identifier, type, and flags of a diagnostic field. ulDiagFlags can be KAGREQDIAGFLAGS_HEADER or KAGREQDIAGFLAGS_RECORD. They tell whether the diagnostic field is a header field or record field. vt is data type of the diagnostic information and it must be VT_I2, VT_UI2, VT_I4, VT_UI4, VT_BSTR, or VT_UI1 | VT_ARRAY depending on the type of diagnostic field. For example, if the diagnostic field type is SQLINTEGER in ODBC, it must be VT_I4; if the type is string in ODBC, it must be VT_BSTR here. Otherwise, the result is undefined. This function clears the previous request of the diagnostic fields in the object. Setting cDiagFields to 0 means that no diagnostic fields should be cached by the provider on this object.

Although the ODBC Provider will support diagnostic fields for both 2.X and 3.X ODBC drivers, it should be noted that the functionality is limited for ODBC 2.X drivers because no 2.X driver supports diagnostics.

The diagnostic information is retrieved through IGetDiagField::GetDiagField. IGetDiagField is a new optional interface of TCustomErrorObject.

typedef struct  tagKAGGETDIAG
{
  ULONG ulSize;
  VARIANTARG vDiagInfo;
  SHORT sDiagField;
} KAGGETDIAG;

ISQLGetDiagField : public IUnknown
{
public:
 virtual HRESULT STDMETHODCALLTYPE GetDiagField(
     KAGGETDIAG *pDiagInfo) = 0;
};

pDiagInfo is an input/output argument.

On input, pDiagInfo->ulSize should be set the size of KAGGETDIAG; pDiagInfo->vDiagInfo should be initialized with VariantInit; and pDiagInfo->sDiagField is the diagnostic field identifier.

At return, pDiagInfo->vDiagInfo has the data for the diagnostic information. vDiagInfo.vt can be any of the values listed for argument vt in ISQLRequestDiagFields::RequestDiagFields.

If the diagnostic data is a string or binary, the data is stored as BSTR or VT_UI1 | VT_ARRAY. When the diagnostic information is no longer needed, pDiagInfo->vDiagInfo should be freed with VariantClear by the consumer.


Installation Notes

The Setup program installs the following files to the hard disk of your workstation or server. Provider writers need to build an installation program that will copy the below files (and any other desired OLE DB Core Components, such as the root enumerator) and register the DLLs (using resvr32 dllname).

\Program Files\Common Files\System\OLE DB or
<Install Dir>\REDIST

File name | Description | Required by
MSDADC.DLL | OLE DB data conversion object for the Data Conversion Library | ODBC provider OLE DB Core Component
MSDAENUM.DLL | OLE DB enumerator object | OLE DB Core Component
MSDAER.DLL | OLE DB error object | ODBC provider
MSDAERR.DLL | Resource file for OLE DB Error Object Resource Library | OLE DB error object
MSDASQL.DLL | ODBC provider |
MSDASQLR.DLL | Resource file for ODBC Provider Resource Library | ODBC provider
MSDATL.DLL | Memory/Array manager | Temp table service provider, error object, ODBC provider
MSDATL2.DLL | Memory/Array Manager version 2 | ODBC provider
MSDATT.DLL | Temporary table service provider | ODBC provider

<Install Dir>\INCLUDE

File name | Description | Required by
MSDASQL.H | Provider-specific defined | ODBC provider
MSDADC.H | Data conversion defined | ODBC provider


Predefined ODBC Data Sources

As part of the ODBC Provider installation, the following 32-bit ODBC data sources are created:

  • OLE_DB_NWind_Jet. This data source points to the NorthWind Access database shipped with the OLE DB SDK. This is \msdasdk\bin\oledb\nwind.mdb.

  • OLE_DB_NWind_SQL. This data source points to the Microsoft SQL Server "pubs" database. The server is set to "." and the database name is set to "pubs". You will need to modify this data source to use it on your system.


Supported Interfaces

The ODBC Provider supports the following standard OLE DB interfaces, as well as the nonstandard interfaces, ISQLRequestDiagFields and ISQLGetDiagFields. For implementation details and known limitations, see the "Interface Implementation Notes" and "Known Limitations" sections in this Help file.

IAccessor
IColumnsInfo
IColumnsRowset
ICommand
ICommandPrepare
ICommandProperties
ICommandText
ICommandWithParameters
IConnectionPoint
IConnectionPointContainer
IConvertType
IDBCreateCommand
IDBCreateSession
IDBInfo
IDBInitialize
IDBProperties
IDBSchemaRowset
IErrorLookup
IGetDataSource
IMultipleResults
IOpenRowset
IPersistFile (on data source object only)
IRowset
IRowsetChange
IRowsetIdentity
IRowsetInfo
IRowsetLocate
IRowsetResynch
IRowsetScroll
IRowsetUpdate
ISessionProperties
ISourcesRowset
ISupportErrorInfo
ITransaction
ITransactionJoin
ITransactionLocal
ITransactionOptions


Obtaining Data Source Objects

The ODBC Provider supports creating an initialized data source object through a file moniker:

  • Create a file moniker (.dsn file). A .dsn file contains connection information.

  • Bind to this file moniker, asking for IID_IDBInitialize. This returns an uninitialized data source object.

A data source object can also be obtained through CoCreateInstance using the CLSID MSDASQL.


Initialization Properties and .dsn Files

Initialization Properties

When the consumer calls IDBInitialize::Initialize, the ODBC Provider calls the ODBC functions SQLSetConnectAtt, to set various connection options, and SQLDriverConnect, to connect to an ODBC data source. The values passed to SQLSetConnectAtt 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 VT_EMPTY. If the consumer then calls IPeristFile::Load to load a .dsn file, the ODBC Provider reads the values of the properties stored in the .dsn file and sets initialization properties accordingly. When the consumer calls Initialize, it can pass values for initialization properties; these overwrite the values read from the .dsn file. When the consumer calls IDBInitialize::Uninitialize, the value of each initialization property is set back to VT_EMPTY.

.dsn Files

The consumer stores the initialization properties used to connect to a particular ODBC data source in a .dsn file. The consumer can then reload the values from a .dsn file at a later time and reconnect to the same data source. The format of a .dsn file is the same as that of an .ini file; the ODBC Provider reads and writes information from it with SQLWriteFileDSN and SQLReadFileDSN.

To save properties in a .dsn file, the consumer calls IPersistFile::Save on the data source object. To load properties from a .dsn file, the consumer calls IPersistFile::Load on an uninitialized data source object.

You can also use a .dsn file to create a moniker to obtain a data source object. The consumer calls CreateFileMoniker with the path to the .dsn file. This creates a moniker which can then be bound with BindMoniker, thus creating an uninitialized data source object.

Initialization Properties Used

The following table lists each initialization property the ODBC Provider uses, the corresponding keyword used in the .dsn file, and how it is used. Any properties not listed are not used by the ODBC Provider.

Property | .dsn file keyword | Description
DBPROP_AUTH_ PASSWORD | AUTH_PASSWORD | Passed as the value of the PWD keyword in SQLDriverConnect.
DBPROP_AUTH_ USERID | AUTH_USERID | Passed as the value of the UID keyword in SQLDriverConnect.
DBPROP_INIT_ DATASOURCE | INIT_DATASOURCE | Passed as the value of the DSN keyword in SQLDriverConnect.
DBPROP_INIT_ HWND | INIT_HWND | Passed as the value of the hwnd argument in SQLDriverConnect.
DBPROP_INIT_ LOCATION | INIT_LOCATION | Passed as the value of the Server keyword in SQLDriverConnect (Microsoft SQL Server driver only).
DBPROP_INIT_ MODE | INIT_MODE | Passed to SQLSetConnectAtt as the value of the SQL_ACCESS_MODE connection option. Only DB_MODE_READ and DB_MODE_READWRITE are supported.
DBPROP_INIT_ PROMPT | INIT_PROMPT | Passed as the value of the fDriverCompletion parameter in SQLDriverConnect.
DBPROP_INIT_ PROVIDERSTRING | INIT_ PROVIDERSTRING | Passed as the complete ODBC connection string (identical to the szConnStr argument in SQLDriverConnect).
DBPROP_INIT_ TIMEOUT | INIT_TIMEOUT | Passed to SQLSetConnectAtt as the value of the SQL_ATTR_LOGIN_TIMEOUT connection option.


ODBC Provider Properties

The following property sets are specific to the ODBC Provider.

The DBPROPSET_PROVIDERDATASOURCEINFO Property Set

The DBPROPSET_PROVIDERDATASOURCEINFO property set contains the following properties.

Property name | Variant type | Description
KAGPROP_ACCESSIBLEPROCEDURES | VT_BOOL | Accessible procedures
KAGPROP_ACCESSIBLETABLES | VT_BOOL | Accessible tables
KAGPROP_ ACTIVESTATEMENTS | VT_I4 | Active statements
KAGPROP_OJCAPABILITY | VT_I4 | Outer join capabilities
KAGPROP_PROCEDURES | VT_BOOL | Stored procedures available
KAGPROP_DRIVERNAME | VT_BSTR | ODBC Driver name
KAGPROP_DRIVERVER | VT_BSTR | ODBC Driver version
KAGPROP_DRIVERODBCVER | VT_BSTR | Driver ODBC version
KAGPROP_FILEUSAGE | VT_I4 | File usage
KAGPROP_LIKEESCAPECLAUSE | VT_BSTR | Like escape clause
KAGPROP_SPECIALCHARACTERS | VT_BSTR | Special characters
KAGPROP_MAXCOLUMNSINGROUPBY | VT_I4 | Maximum columns in Group By clause
KAGPROP_MAXCOLUMNSININDEX | VT_I4 | Maximum columns in index
KAGPROP_MAXCOLUMNSINORDERBY | VT_I4 | Maximum columns in Order By clause
KAGPROP_MAXCOLUMNSINSELECT | VT_I4 | Maximum columns in Select statement
KAGPROP_MAXCOLUMNSINTABLE | VT_I4 | Maximum columns in table
KAGPROP_NUMERICFUNCTIONS | VT_I4 | Numeric functions
KAGPROP_OUTERJOINS | VT_BSTR | Outer joins
KAGPROP_STRINGFUNCTIONS | VT_I4 | String functions
KAGPROP_SYSTEMFUNCTIONS | VT_I4 | System functions
KAGPROP_TIMEDATEFUNCTIONS | VT_I4 | Time and date functions
KAGPROP_ODBCSQLOPTIEF | VT_BOOL | Indicates whether the data source supports the optional Integrity Enhancement Facility
KAGPROP_ODBCSQLCONFORMANCE | VT_I4 | SQL grammar supported by the ODBC driver

The DBPROPSET_PROVIDERROWSET Property Set

The DBPROPSET_PROVIDERROWSET property set contains the following properties.

Property name | Variant type | Description
KAGPROP_QUERYBASEDUPDATES | VT_BOOL | Query-based updates, deletes, and inserts
KAGPROP_CONCURRENCY | VT_UI4 | Mask of ODBC concurrency to support. The following bitmask values apply to this option: KAGPROPVAL_CONCUR_READ_ONLY KAGPROPVAL_CONCUR_ROWVER KAGPROPVAL_CONCUR_VALUES KAGPROPVAL_CONCUR_LOCK
KAGPROP_BLOBSONFOCURSOR | VT_BOOL | BLOB accessibility on forward-only cursors
KAGPROP_INCLUDENONEXACT | VT_BOOL | Include SQL_FLOAT, SQL_DOUBLE, and SQL_REAL in QBU WHERE clauses
KAGPROP_MARSHALLABLE | VT_BOOL | Generate a rowset that can be marshaled
KAGPROP_POSITIONONNEWROW | VT_BOOL | Position on the last row after the insert operation completes
KAGPROP_IROWSETCHANGEEXTINFO | VT_BOOL | IRowsetChange interface extended information support
KAGPROP_CURSOR | VT_I4 | ODBC cursor type

The DBPROPSET_PROVIDERDBINIT Property Set

The DBPROPSET_PROVIDERDBINIT property set contains the following properties.

Property name | Variant type | Description
KAGPROP_AUTHTRUSTEDCONNECTION | VT_BOOL | Trusted connection. Sets SQL_COPT_SS_SERV_RAC to the value of the BSTR. Sets SQL_COPT_SS_INTEGRATED_SECURITY to SQL_IS_ON.
KAGPROP_AUTHSERVERINTEGRATED | VT_BOOL | Force integrated security when you log on. Sets SQL_COPT_SS_SERV_RAC to the value of the BSTR.


Supported Platforms

The ODBC Provider is supported on Windows NT version 4.0, Intel, and Alpha platforms. The ODBC Provider is also supported on the Windows 95 platform for x86 machines.


General Implementation Notes

The following notes discuss how the ODBC Provider is implemented.

  • When the consumer calls IUnknown::Release for a session and the reference count of the session drops to zero (that is, when the session is actually released), any outstanding transactions are aborted.

  • All support objects (conversion object, temporary table service object, and error collection object) must be registered for the ODBC Provider to be instantiated. In addition, the memory and array manager must be in the ODBC Provider directory or in the path.

  • Although the ODBC Provider advertises support for both threading models, only the free-threaded model is supported.

  • Although the OLE DB Programmer’s Reference specifies that methods capable of returning DB_S_ERRORSOCCURRED will do so only when one or more errors occur, the ODBC Provider returns DB_S_ERRORSOCCURRED when a warning or an error occurs in one or more items. The OLE DB Programmer’s Reference specifies that if only warnings occur, providers should return S_OK.

  • The ODBC Provider does not support the binding of output parameters more than once in a single accessor. This is due to the lack of support for binding an output parameter to different addresses in ODBC.

  • If a command times out, one of the following errors is returned:
    • ICommand::Execute returns DB_E_ABORTLIMITREACHED.

    • IOpenRowset::OpenRowset returns E_FAIL.

    • IDBSchemaRowset::GetRowset returns E_FAIL.

    • IMultipleResults::GetResult returns DB_E_ABORTLIMITREACHED.

    • IColumnsRowset::GetColumnsRowset returns E_FAIL.


Interface Implementation Notes

The following sections discuss how the ODBC Provider implements some interfaces.

IAccessor

  • 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.

  • The ODBC Provider supports DBACCESSOR_PASSBYREF accessors. These accessors return a single pointer to the ODBC Provider’s internal row buffer. This provides a significant speed improvement over copy or IMalloc bindings. The pointer is only valid while the row handle is active (until ReleaseRows is called).

  • The OLE DB specification does not enable a consumer to query for the layout of the internal buffer. The ODBC Provider will return DB_E_BADBINDINFO if the type does not match exactly, or if the location of the status or length is incorrect.

  • The ODBC Provider does not support the DBACCESSOR_OPTIMIZED flag. The flag is ignored (for example, there is no special processing) if it is specified on an accessor.

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. The ODBC Provider does not instantiate an object for these columns. Instead these settings signify that the bytes stored in the column can be retrieved or stored by requesting an ISequentialStream interface, through which the bytes may be read from or written to.

  • In some ODBC drivers, duplicate columns in an unprepared command cause the ODBC Provider to receive unexpected column names. If the command contains duplicate columns and the consumer prepares the command, unprepares the command, then executes the command, these drivers do not generate column names for the duplicate columns. This causes a warning, DB_S_ERRORSOCCURRED, in IColumnsInfo::MapColumnsID.

  • The ODBC Provider does not support the following DBCOLUMNFLAGS in IColumnsInfo::GetColumnInfo:

    DBCOLUMNFLAGS_MAYDEFER

    DBCOLUMNFLAGS_WRITE

    DBCOLUMNFLAGS_CACHEDEFERRED

IColumnsRowset

  • The following columns return NULL values:

    DBCOLUMN_COLLATINGSEQUENCE

    DBCOLUMN_COMPUTEMODE

    DBCOLUMN_DEFAULTVALUE

    DBCOLUMN_DOMAIN

    DBCOLUMN_HASDEFAULT

    DBCOLUMN_ISUNIQUE.

  • Driver-specific columns can be requested. There is no way to return the list of what these may be for the driver in use. Consumers should specify a COLUMNID of type DBCOLKIND_GUID_PROPID, with guid equal to GUID_NULL, and ulPropid equal to value of the ODBC driver-specific option (the fDescType parameter in SQLColAttributes). A heuristic is used to determine the data type of the option from the ODBC driver.

ICommandProperties

  • When used with the Microsoft SQL Server ODBC driver, the ODBC Provider cannot support both DBPROP_CANHOLDROWS and DBPROP_OTHERINSERT if each is set to VARIANT_TRUE.

ICommandText

  • If SQLNativeSQL is supported in the underlying ODBC driver, the ODBC Provider will initiate a call to this ODBC function when the consumer calls ICommandText::GetCommandText with pguidDialect set to NULL. The string returned from this call is not cached; users must call ICommandText::SetCommandText with the string returned by the ODBC driver to use the native SQL statement.

  • If pguidDialect is set to NULL on input, the ODBC Provider returns the command in ppwszCommand in the dialect used to set the command text. The ODBC Provider also returns S_OK (barring any other errors) rather than DB_S_DIALECTIGNORED.

ICommandWithParameters

  • The data type returned in ICommandWithParameters::GetParameterInfo should be considered without regard to sign. For example, if a database server is expected to return DBTYPE_I2, the ODBC Provider can return DBTYPE_UI2; this ambiguity occurs because the underlying ODBC function SQLDescribeParam does not return the sign of the data type. In addition, the following flags are not set because the ODBC Provider has no mechanism to obtain the status from the ODBC driver with SQLDescribeParam:

    DBPARAMFLAGS_ISSIGNED

    DBPARAMFLAGS_ISINPUT

    DBPARAMFLAGS_ISOUTPUT

The last two of the above flags can be set for stored procedures against SQL Server.

  • If the ODBC driver returns SQL_NULLABLE_UNKNOWN for a parameter, the ODBC Provider will return DBPARAMFLAGS_ISNULLABLE for that parameter.

IDBSchemaRowset

  • The ODBC Provider cannot support restrictions on schema columns if the restrictions are not supported in the underlying ODBC driver.

  • NULL values are returned in many columns that ANSI SQL 92 specifies as NOT NULL. These are cases in which the information is not available by means of ODBC.

  • Schema rowsets do not expose IColumnsRowset or IRowsetLocate.

  • The COLUMNS schema rowset is dependent on the information returned in the ODBC driver’s implementation of SQLColumns. Since ODBC does not return ordinal information, the ODBC Provider always sets the value of ORDINAL_POSITION to NULL.

    The ODBC Provider returns any driver-specific columns that the ODBC driver returns in SQLColumns. This information is returned in columns beyond those described in the OLE DB specification.

    These additional columns include:

    ORDINAL, which is returned by the Microsoft Access ODBC driver.

    ss_data_type and colid, which are returned by the Microsoft SQL Server 6.0 ODBC driver.

    "COLUMN_DEF", "SQL_DATA_TYPE", "SQL_DATETIME_SUB", "CHAR_OCTET_LENGTH", "ORDINAL_POSITION", "IS_NULLABLE", and "SS_DATA_TYPE", which are returned by the Microsoft SQL Server 6.5 ODBC driver.

  • In the INDEXES schema rowset, the value of the TYPE column is set to DBPROPVAL_IT_BTREE if the value of the TYPE column returned by SQLStatistics is SQL_INDEX_CLUSTERED. The value of the NULL_COLLATION column is set to the value returned by SQLGetInfo for the SQL_NULL_COLLATION option.

  • In the PROVIDER_TYPES schema rowset, the value of the DATA_TYPE column is set to DBTYPE_BYTES or DBTYPE_IUNKNOWN if the value of the DATA_TYPE column returned by SQLGetTypeInfo is a driver-specific SQL data type indicator.

  • The PROVIDER_TYPES schema rowset does not support any restrictions.

IRowset

  • GetNextRows may skip over rows if the underlying ODBC driver compacts data and some rows of the current rowset have been deleted.

IRowsetChange

  • IRowsetChange is implemented using the ODBC function SQLSetPos. It therefore can be exposed only when the underlying ODBC driver supports SQLSetPos. If the underlying driver does not support SQLSetPos, query-based updating will be used based on an optimistic, by-value scheme.

  • Newly inserted rows can be updated or deleted only through query-based updates; if query-based updating is unavailable, newly-inserted rows cannot be updated. This is not the case for ODBC 3.x drivers, which return bookmarks for newly inserted rows and allow updates.

  • You need to have an index on the table or else IRowsetChange::Insert Row will fail with any ISLONG column, returning an E_FAIL HRESULT.

IRowsetScroll

  • If the ODBC driver supports the SQL_DIAG_CURSOR_ROW_COUNT diagnostic identifier, the two methods GetApproximatePosition and GetRowsAtRatio (which are methods of IRowsetScroll), are implemented. Depending on the type of cursor, GetInfo methods can be called to determine whether the row count is exact or approximate.

ISequentialStream

In deferred update mode, when a row's update fails, this provider does not release any of the ISequentialStream pointers due to the lack of knowledge which pointers have and have not been read. The row is in an unknown state. If the consumer attempts to update again, failure may occur. Because responsibility for a reference on these failed interfaces still exists, this provider releases them in the following cases:

  • If the consumer calls SetData for a given ISS-bound BLOB object, the old interface will be released and replaced with the new one in the deferred-update buffer

  • If the consumer calls Undo for the row

  • If the consumer resynchs the row

  • If the consumer releases the rowset

Interface pointers are always released if the back-end updates succeed.

ITransaction

  • Consumers that use the ODBC Provider should be aware of the ITransaction::Abort and ITransaction::Commit error XACT_E_CANTRETAIN, which specifies that retaining abort and commit operations are not supported and that the specified abort or commit succeeded, leaving the session in autocommit mode. After receiving this error, consumers should check for extended error information. If extended error information is available, consumers should call ITransaction::GetTransactionInfo to determine the current transaction mode (manual commit or autocommit); there may be some cases in which the ODBC Provider cannot put the session in autocommit mode despite the information implied in the XACT_E_CANTRETAIN error.

ITransactionJoin

  • The ODBC Provider now supports Microsoft Transaction Server and its coordinated transaction capabilities by implementing ITransactionJoin.

  • Enabling support for Microsoft Transaction Server requires:
    • ODBC driver support for the connection attribute SQL_ATT_ENLIST_IN_DTC, which must be set.

    • That the consumer expose an IUnknown pointer from ITransactionCoordinate.


Data Types

This section describes data types in the ODBC Provider.

Default Data Type Mappings

The ODBC Provider binds to the ODBC 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.

Note that the GUID mapping is for an OBDC 3.x driver only.

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_GUID | SQL_C_GUID[1] | DBTYPE_GUID
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 [2] SQL_C_BIGINT [3] | 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
1For ODBC 3.5 drivers only. 2This is due to lack of support in the ODBC specification for a SQL_C type for 8-byte integers. For ODBC 3.x drivers, the indicator is SQL_C_BIGINT. 3 For ODBC 3.x drivers only.

Length Binding for Numerics

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.

String Truncation

Strings are truncated without warning when the destination length is smaller than the source length.

Support for Long Data Types

Consumer applications can bind to long data columns by binding to IUnknown and querying for the ISequentialStream interface on the data or by binding to _BYTES or _STR (depending on the underlying data type) to get the data in line. If the consumer chooses to use the ISequentialStream approach, support is limited to one BLOB per accessor.

The ODBC Provider supports STGM_READ on IRowset::GetData, IRowsetChange::InsertRow or IRowsetChange::SetData; STGM_WRITE is not supported for any one of these methods.

To write long data using IRowsetChange::InsertRow or IRowsetChange::SetData, follow these steps:

  1. Set pObject->dwFlags == STGM_READ;
    set pObject->iid==IID_ISequentialStream.
    (Only IID_ISequentialStream is supported; IStream, IStorage, and ILockBytes are not.)

  2. Bind the storage columns. Both IRowsetChange::InsertRow and IRowsetChange::SetData allow binding more than one storage column at a time.

  3. Pass the instantiated ISequentialStream pointer in the pData buffer at the correct offset (specified by obValue).

IRowsetChange::SetData reads the data by calling ISequentialStream::Read on the object until it reaches the end of the stream.

To read long data using IRowset::GetData follow these steps:

  1. Set pObject->dwFlags == STGM_READ;
    set pObject->iid==IID_ISequentialStream.
    (Only IID_ISequentialStream is supported; IStream, IStorage, and ILockBytes are not.)

  2. Bind the storage columns. IRowset::GetData permits only one storage column to be bound at a time. If there is a storage object bound, no other storage objects or long BLOBs can be bound.

  3. The pData buffer would then contain ISequentialStream pointer.

To support long data types, the consumer application must meet at least one of the following conditions:

  • The OLE DB IRowsetLocate interface is available.

  • SQLExtendedFetch and SQLSetPos(SQL_POSITION) are available in the underlying ODBC driver and DBPROP_CANHOLDROWS is set to VARIANT_FALSE.

  • If the underlying ODBC cursor is forward-only, BLOB data is available if there are no non-BLOB columns following a BLOB column or, BLOB data is available regardless of position, if the KAGPROP_BLOBSONFOCURSOR property is set to VARIANT_TRUE.

Long data in a newly inserted row that contains BLOB data columns cannot be read immediately after the row is inserted if the insertion occurred in immediate update mode or if the BLOB data was provided through an ISequentialStream object. The consumer must use IRowset::GetNextRows, IRowsetLocate::GetRowsAt, or IRowsetLocate::GetRowsByBookmark to reposition on the newly inserted row and gain access to the long data columns.

Note If you are using Query-By-Example, you cannot retrieve BLOB data.

Note Attempting to use a storage object on a non-BLOB column may result in a crash or memory corruption; no error will be returned.

Using the KAGPROP_BLOBSONFOCURSOR Property

The KAGPROP_BLOBSONFOCURSOR property enables users to access BLOB data in a forward-only rowset regardless of the position of the column containing the BLOB data in the column list. If this property is set to VARIANT_TRUE, users can access BLOB data in columns other than the last column in forward-only rowsets; if it set to VARIANT_FALSE, users can access BLOB data only if there are no additional BLOB columns following a BLOB column in the forward-only rowset. If any non-BLOB columns follow a BLOB column in this case, those columns will be inaccessible.

BLOBs can be accessed only from the most recent row. Users must access the BLOB data columns, as well as all other columns, in the ascending column order; data in any columns skipped in the process of traversing the row will be lost. There is no change to the behavior of the columns preceding the first BLOB column; these columns are buffered in the usual manner.

Data Type Conversion

The ODBC Provider supports limited conversions. Only the default types returned in IColumnsInfo are supported, and DBTYPE_BYREF is supported only on variable length types. Also, some types returned by IColumnsInfo currently cannot be converted to or from DBTYPE_WSTR, which should be valid for all types.

Note Supported data conversions can be determined through the IConvertType interface.

In the following table, click on the data type you want to convert from to view the supported conversions. In the conversion tables, an X indicates that the conversion is supported in the ODBC Provider.

Data types to convert from

I1 | R4 | DBTIMESTAMP | IUNKNOWN
I2 | R8 | BOOL | NULL
I4 | CY | BSTR | STR
I8 | DECIMAL | BYTES | VARIANT
UI1 | NUMERIC | Empty | WSTR
UI2 | DATE | ERROR |
UI4 | DBDATE | GUID |
UI8 | DBTIME | IDISPATCH |

Note When converting DBTYPE_NUMERIC types that correspond to ODBC types SQL_NUMERIC and SQL_DECIMAL, the ODBC Provider exposes additional conversions that are not listed as supported. This occurs because these types are internally bound to SQL_CHAR. Therefore, any conversions supported for CHAR data types in ODBC can be attempted for DBTYPE_NUMERIC types. There are some conversions that will fail, due to incompatibility, and some that will succeed, depending on the data.


Query-Based Updates

This section describes query-based updates and explains how rows are identified through the WHERE clause. It also describes the conditions under which rowsets can be updated based on query-based updates.

Query-Based Update Definition

Query-based updates use the SQL INSERT, UPDATE and DELETE commands to issue updates to the ODBC database. These commands are not positioned and are independent of, but in the same transaction context as, the rowset cursor. Query-based updates are used when the ODBC driver does not support the features required to use positioned updates, or when you specifically set some property to request a query-based update.

Row Identification

When updating or deleting an existing row, query-based update must identify the row through the WHERE clause of the UPDATE or DELETE command. Because it does not have any primary key information, a query-based update constructs a hopeful key that may be able to identify the row. The hopeful key consists of all searchable columns in the rowset. By default, all REAL, FLOAT, and DOUBLE numeric values are excluded from the WHERE clause; to include these values, users must set the property KAGPROP_INCLUDENONEXACT to VARIANT_TRUE. If the key does not uniquely identify a row, then severe update anomalies are possible.

For example if a rowset contains columns A, B, C, and D where columns A and D are searchable, the DELETE command that would be issued by a query-based update would be:

DELETE FROM table-name WHERE (A = value-a AND D = value-d)

Value-x is the value of column x for the row being deleted.

Criteria for Updatable Rowsets

To be updatable using query-based updates, a rowset must satisfy the following criteria. First, a rowset must contain at least one searchable column. Second, the query used to create the rowset must contain only a single table or view name in its FROM clauses.

Ensuring Changes are Visible

To ensure that changes are visible, the consumer should set DBPROP_OTHERUPDATEDELETE or DBPROP_OTHERINSERT. This enables the ODBC Provider to expose changes to the data source using query-based updates.

Setting the QBUTimeout Value

If your query-based updates are timing out (for example, because of a heavy load on a server), you may want to try changing the QBUTimeout value in the system registry.

Using the registry editor, go to the following key location: HKEY_CLASSES_ROOT\CLSID\{c8b522cb-5cf3-11ce-ade5-00aa0044773d}\Flags. Under this registry key you can change the DWORD value of QBUTimeout to the desired number. If no value is given, QBUTimeout will default to 30 seconds.

Note If the key "Flags" does not exist under {c8b522cb-5cf3-11ce-ade5-00aa0044773d}, you have to add it before adding the named value QBUTimeout.


Known Limitations

The following are known limitations with the ODBC Provider:

General

  • The ODBC Provider does not return precision correctly for any numeric data types except DBTYPE_NUMERIC and DBTYPE_DECIMAL. For example, the COLUMNS schema rowset, IColumnsInfo::GetColumnInfo, and IColumnsRowset::GetColumnsRowset return a precision of zero for integer data types.

  • The ODBC Desktop Database Driver cannot start a transaction when it has a rowset open. The ODBC Provider returns E_FAIL if ITransactionLocal::StartTransaction is called when any rowset is held open by this driver. This limitation does not necessarily affect other ODBC drivers.

  • The ODBC Provider does not quote table names passed through DBIDs; for instance, in IOpenRowset. Therefore, when trying to open a table against the ODBC Provider that requires quoting the name (such as when the table name contains extended characters), the consumer must add the quotes to the table name manually or simply execute "Select * from <quoted table name>".

  • Query-based updates cannot be used to update BLOB data or other long data.

  • The query-based update scheme implemented in the ODBC Provider may not allow for correct repositioning in the rowset after performing an update involving the key columns which were used to generate a keyset cursor-based rowset.

  • The ODBC Provider does not support output or input/output parameters as storage objects.

  • Using the ODBC Provider, if the underlying ODBC driver does not support SQLDescribeParam, and the OLE DB consumer does not specify the type of the parameter, the ODBC Provider will convert Boolean parameter values to "-1" and "0" when the data type of the parameter is string. This is in contrast to the OLE DB Programmer's Reference, which calls for Booleans converted to string data types to appear as the strings "True" or "False". In order to ensure proper conversion against ODBC drivers that do not support describing parameters, the OLE DB consumer should always call SetParameterInfo to specify the types of parameters.

  • If DBMEMOWNER_PROVIDEROWNED was set in dwMemOwner in a binding structure and the binding structure binds to a long data column, the ODBC Provider returns DB_E_BADBINDINFO.

  • If DBPROP_REMOVEDELETED is set to VARIANT_TRUE on the rowset and a dynamic ODBC cursor is requested and the ODBC Provider cannot provide this type of cursor, any deleted rows scheduled to be removed from the rowset are removed at fetch time. In this situation, it is possible for row-fetching methods (such as GetNextRows or GetRowsAt) to return both S_OK and fewer rows than requested. This irregular behavior is caused by the ODBC Provider requirement to maintain a one-to-one correspondence between OLE DB and ODBC rowsets. Note that this behavior does not signify the DB_S_ENDOFROWSET condition and that the number of rows returned by the fetching method will never be zero if there are any remaining rows (although the number of rows fetched may be smaller than the number requested).

  • With non-ODBC 3.x drivers, the ODBC Provider cannot return bookmarks on newly inserted rows. The ODBC Provider marks the status of the bookmark column of the newly inserted row as DBSTATUS_E_UNAVAILABLE. Since ODBC 3.x drivers retrieve bookmarks, updates can be performed on newly inserted rows.

  • The ODBC Provider does not allow BLOB data to be set via ISequentialStream on newly inserted rows.

  • In a deferred update mode, the ODBC Provider does not support retrieving data set into blob columns via ISequentialStream, unless that data has been updated on the provider using IRowsetUpdate::Update.

  • When performing deferred updates with the DBCOLUMNFLAGS_ISLONG flag, the following two limitations exist:
    • If you call SetData for a BLOB column bound as DBTYPE_IUNKNOWN, you cannot retrieve the interface pointer in subsequent calls to GetData. After calling IRowsetUpdate::Update, you can retrieve the value as usual.

    • If you call IRowsetUpdate::SetData for a BLOB column not bound in the accessor as DBTYPE_IUNKNOWN, you may retrieve that value as any legal binding other than DBTYPE_IUNKNOWN. After calling IRowsetUpdate::Update, you may retrieve the value in the usual manner.

Microsoft ODBC Driver for Oracle

There are currently no known limitations to using this driver with version 3.0 of the ODBC Provider.

Microsoft SQL Server ODBC Driver

  • When using a Microsoft SQL Server database as the data source, if the consumer specifies a long column in the WHERE clause of a SELECT statement, the long column cannot contain more than 255 characters.

  • Using a Microsoft SQL Server database as the data source, if the consumer uses SetData to update a BLOB column through an ISequentialStream interface, the length part of the binding must contain the total length of the BLOB data. This differs from other data sources, which require the length part of the binding to contain only the length of the pointer to the ISequentialStream object.

  • Using a Microsoft SQL Server database as the data source, if the consumer opens a forward-only cursor using a query-based update, any method of the IRowsetChange interface may cause a deadlock situation.

The following schema and schema restrictions are supported through the Microsoft SQL Server ODBC driver.

Supported schema | Supported restrictions
CATALOGS | None
COLUMNS | All
TABLES | All
PROVIDER_TYPES | None
INDEXES | 1, 2, and 5
PROCEDURES | All
PROCEDURE_PARAMETERS | All
COLUMN_PRIVILEGES | 1, 2, 3, and 4
FOREIGN_KEYS | All
PRIMARY_KEYS | All
SCHEMATA | None

Microsoft Access ODBC Driver

  • When a consumer specifies a value to be inserted or updated in a command that causes a data overflow, but has not called ICommandWithParameters::SetParameterInfo, and the provider cannot determine the column type used by the data source, the ODBC Provider returns DB_E_DATAOVERFLOW (rather than DB_E_ERRORSOCCURRED with the offending parameter marked with the DBSTATUS_E_DATAOVERFLOW flag). This occurs because the ODBC Provider cannot detect which parameter caused the overflow error.

  • In delayed-update mode, consumers who attempt to delete or update rows in a read-only rowset receive the status DBROWSTATUS_DELETED. This differs from the expected status, which is DBROWSTATUS_INTEGRITYVIOLATION.

  • The rowset property DBPROP_MAXROWS does not affect the number of rows returned by the Microsoft Access ODBC driver.

  • When run against the Access driver, the ODBC Provider reports output parameter availability as DBPROPVAL_OA_ATROWRELEASE, rather than the expected value of DBPROPVAL_OA_NOTSUPPORTED.

The following schema and schema restrictions are supported through the Microsoft Access ODBC driver.

Supported schema | Supported restrictions
CATALOGS | None
COLUMNS | All
TABLES | All
PROVIDER_TYPES | None
INDEXES | 1, 2, and 5
PROCEDURE_PARAMETERS | All
PROCEDURE_COLUMNS | All

Notifications

  • On the first change to a row from SetData, the ODBC Provider does not interleave the SynchAfter and DidEvent notifications. The ordering of OKTODO and ABOUTTODO is correct.

Rowset Properties

  • The rowset properties DBPROP_OTHERINSERT and DBPROP_CANHOLDROWS cannot be used together. If you attempt to create a rowset with this combination of properties, the ODBC Provider returns DB_E_ERRORSOCCURRED and returns _NOTSUPPORTED for both properties.

Setting Read-Only Properties to Their Default Values

  • The ODBC Provider does not support the OLE DB version 1.1 behavior of allowing consumers to set the value of a read-only property to its default. Although the OLE DB version 1.1 specification clearly indicates that a status of DBPROPSTATUS_OK is returned in "the case where the value of a read-only property was set to its current value," the ODBC Provider returns DBPROPSTATUS_NOTSETTABLE.

Threading

  • The ODBC Provider supports the Free Threaded multithreading model; there is no support for Apartment model or mixed threading.

ICommandWithParameters

  • The ODBC Provider returns zero for precision and scale when these values do not apply. These values are incorrect and should be ~0 (bitwise NOT 0).

  • If the consumer doesn't allocate enough space, a stored procedure that returns a variable length parameter will not detect the truncation that might have occurred. Instead of returning S_OK and setting the status to DBSTATUS_S_TRUNCATED, the ODBC Provider will return DB_S_ERRORSOCCURRED, and set the status to DBSTATUS_S_OK.

IConvertType

  • IConvertType::CanConvert returns E_INVALIDARG when a bad dwConvert flag is passed in. This is incorrect; the ODBC Provider should return DB_E_BADCONVERTFLAG.

IDBSchemaRowset

  • The ODBC Provider may not support restrictions on the CATALOGS and SCHEMATA schema rowsets.

  • IDBSchemaRowset::GetSchema may not support the schema restriction on any schema on any ODBC driver. The ODBC Provider will incorrectly report this as a supported restriction because ODBC drivers cannot export information about their support of restrictions to the ODBC Provider.

  • IDBSchemaRowset::GetRowset features limited support for the following schema rowsets with most data sources:
    FOREIGN_KEYS
    PRIMARY_KEYS
    STATISTICS
    Consumers must restrict their calls by table name or the ODBC Provider will return an empty result set. This limitation does not occur on Microsoft SQL Server 4.21.

  • On Microsoft SQL Server data sources, IDBSchemaRowset::GetRowset( DBSCHEMA_PROCEDURE_COLUMNS) is unsupported. Consumers who need rowset information about stored procedures should prepare a query that includes the stored procedure and call IColumnsInfo::GetColumnInfo.

  • A restriction on table name is required when calling IDBSchemaRowset::GetRowset(DBSCHEMA_INDEXES). This restriction must be used because ODBC function SQLStatistics returns information about a single table and the ODBC Provider uses this function to return information from the data source. If the restriction is not applied, the ODBC Provider returns an empty result set.

  • The PROVIDER_TYPES schema rowset is not sorted by the ODBC Provider.

  • SQL Server will return names similar but not matching when restrictions are placed on the PROCEDURE_NAMES column. When GetRowset is called for PROCEDURES and PROCEDURE_PARAMETERS schema rowsets, and a name restriction is specified, then the PROCEDURE NAMES returned in the result set may not exactly match the restriction value, but may have ";N" appended, where N is a number. Also, if the ";N" is used in the PROCEDURE NAME restriction, then the provider will return an empty result set even if a procedure name is appended with that number.

IRowset

  • The ODBC Provider does not support execution of RestartPosition when the command text contains parameters; the underlying ODBC driver must, however, support SQLExtendedFetch.

  • With IRowset::RestartPosition, the ODBC Provider does not return DB_S_COLUMNSCHANGED when it is supposed to. Instead, it returns E_FAIL.

  • If the row on which the rowset is currently positioned is deleted, the consumer must first execute IRowset::RestartPosition, before retrieving additional row handles with IRowset::GetNextRows.

  • If the columns in the underlying data source change and the consumer requests additional rows with IRowset::GetNextRows, the ODBC Provider will fail with the return code E_FAIL.

  • If the columns in the underlying data source change, the consumer calls IRowset::RestartPosition, and the reposition operation causes the command to be re-executed, the ODBC Provider will fail with the return code E_FAIL. The rowset is now invalid and any method other than ReleaseRows or ReleaseAccessor will fail. If this occurs, the consumer must release all accessors, row handles, and the rowset and must execute the command that created the rowset again.

  • When using IRowset::GetData, you can bind to only one long data column based on ISequentialStream; the ODBC Provider can bind to any number of long data columns not based on ISequentialStream.

IRowsetLocate

  • When the consumer fetches a row with IRowsetLocate::GetRowsAt, saves the bookmark value corresponding to the row, releases the row, and attempts to fetch the row again using IRowsetLocate::GetRowsAt with the saved bookmark, the second fetch operation may fail. The ODBC Provider returns DB_E_BADBOOKMARK if the underlying ODBC call to SQLExtendedFetch returns S1111 (invalid bookmark value); otherwise, the ODBC Provider returns E_FAIL.

IRowsetResynch

  • To enable the ODBC Provider to expose the IRowsetResynch interface, at least one of the following conditions must be met:
    • The IRowsetLocate interface is implemented.

    • The transaction isolation level is set to SQL_TXN_REPEATABLE_READ.

    • The transaction isolation level is set to SQL_TXN_SERIALIZABLE.

    • The transaction isolation level is set to SQL_TXN_VERSIONING.

    • DBPROP_CANHOLDROWS is set to VARIANT_FALSE, SQLExtendedFetch and SQLSetPos with REFRESH are available from the underlying ODBC driver, and the cursor is not a forward-only cursor.

IRowsetScroll

IRowsetScroll is supported on ODBC 3.x drivers. If a driver cannot determine the current row position, a value of 1 is returned for the row number in GetApproximatePosition.


Overview of the OLE DB Provider for Oracle

The OLE DB Provider for Oracle allows high performance and functional access to Oracle data for Microsoft® Visual Basic® applications.

The provider exposes interfaces to consumers wanting access to data on one or more Oracle servers. You can use it to develop an optimized OLE DB consumer for Oracle databases.

The OLE DB Provider for Oracle is an OLE DB version 2.0-compliant provider.

The provider supports Microsoft® Transaction Server functionality. The Microsoft Transaction Server enables OLE DB session object pooling and enlistment of automatic transactions. Integration enables this and satisfies pure performance requirements for ADO/Oracle ASP applications.

For more information on | See
Consumers and providers | "Consumers and Providers" in Chapter 1, "Overview," of the OLE DB Programmer's Reference


System Requirements

The Microsoft OLE DB Provider for Oracle requires a system running:

  • The Windows 95 operating system on Intel computers

    – or –

    The Windows NT operating system (version 4.0 with Service Pack 3 or later) on Intel or Alpha computers.

  • Oracle client software (appropriately installed and configured).

  • An operational Oracle server running in your enterprise.

Note Oracle networking components installed with an Oracle client, and Oracle7 Release 7.3.3 or later (including Oracle8.x) are also required. Oracle products are available from the Oracle Corporation.

Consumer Development

A C or C++ compiler supporting OLE/COM component construction is required to develop a consumer.


Data Source Objects

OLE DB uses the term data source for the set of OLE DB interfaces used to establish a link to a data store, such as Oracle. Creating an instance of the data source object of the provider is the first task of an OLE DB consumer. For more information about data source objects, see "Data Source Objects" in Chapter 2, "Enumerators, Data Source Objects, and Sessions," of the OLE DB Programmer's Reference.

Every OLE DB provider declares a class identifier for itself. The class identifier for the OLE DB Provider for Oracle is the C/C++ macro CLSID_MSDAORA, which can be found in the header file Msdaora.h. With the class identifier, the consumer uses the OLE CoCreateInstance function to manufacture an instance of the data source object.

The Oracle provider is an in-process server. Instances of the Oracle provider objects are created using the CLSCTX_INPROC_SERVER macro to indicate the executable context.

The data source object exposes the OLE DB initialization interfaces that allow the consumer to connect to existing Oracle databases.

Example

This example uses the class identifier macro to create a data source object and get a reference to its IDBInitialize interface.

#include "msdaora.h"
IDBInitialize* pIDBInitialize;
HRESULT    hr;

hr = CoCreateInstance(CLSID_MSDAORA, NULL, CLSCTX_INPROC_SERVER,
  IID_IDBInitialize, (void**) &pIDBInitialize);

if (FAILED(hr))
{
  // Display error
…
}

With successful creation of an instance of a data source object, the consumer application can continue by initializing the data source and creating sessions. OLE DB sessions expose the interfaces that allow data access and manipulation.

The provider makes its first connection to a specified Oracle server as part of successful data source initialization. The connection is maintained as long as a reference is maintained on any data source initialization interface, or until IDBInitialize::Uninitialize is called.

Note The OLE DB Provider for Oracle looks for the file Ociw32.dll when the client application tries to initialize. If this file is not an Oracle version 7.3.3 or later file, or if the file is not found, the provider returns a message stating that IID_IDBInitialize failed: “The Oracle client and networking components were not found. These components are supplied by the Oracle Corporation and are part of the Oracle Version 7.3.3 (or greater) client software installation. You will be unable to use this provider until these components have been installed.”


Initialization and Authorization Properties

The Microsoft Oracle OLE DB Provider interprets OLE DB initialization and authorization properties as follows. Only the properties supported are listed. For those not supported, an error is returned. For the full description, see Appendix C, "Properties," in the OLE DB Programmer's Reference.

Property ID | Short Description
DBPROP_AUTH_PASSWORD | The password assigned to an Oracle user.
DBPROP_AUTH_USERID | An Oracle user name.
DBPROP_INIT_DATASOURCE | An existing Oracle server.
DBPROP_INIT_HWND | A window handle from the calling application. A valid window handle is required for the initialization dialog box displayed when prompting for initialization properties is allowed.
DBPROP_INIT_LCID | This provider validates the locale ID and returns an error if the locale ID is not supported or is not installed on the client machine.
DBPROP_INIT_PROMPT | This provider supports all prompting modes for data source initialization. This provider uses DBPROMPT_NOPROMPT as its default setting for the property.


Sessions

A provider session represents a single connection to an Oracle Server.

OLE DB requires that sessions delimit transaction space for a data source. All command objects created from a specific session object will participate in the local or distributed transaction of the session object.

The first session object created on the initialized data source receives the Oracle connection established at initialization. When all references on the interfaces of the session object are released, the Oracle connection becomes available to another session object created on the data source.

An additional session object created on the data source establishes its own connection to the Oracle provider as specified by the data source. The Oracle connection is dropped when the application releases all references to objects created that session.

For more information on | Go to
Sessions | "Sessions" in Chapter 2, "Enumerators, Data Source Objects, and Sessions," of the OLE DB Programmer's Reference
Transactions | "Transaction Management" in Chapter 1, "Overview of OLE DB," of the OLE DB Programmer's Reference
Data sources | "Data Source Objects" in Chapter 2, "Enumerators, Data Source Objects, and Sessions," of the OLE DB Programmer's Reference

Creating new provider session objects can generate significant application overhead for applications that continually create and release session objects. The overhead can be minimized by managing the provider session objects efficiently. OLE DB Provider for Oracle applications can keep the Oracle connection of a session object active by maintaining a reference on at least one interface of the session object.


Session Properties

The OLE DB Provider for Oracle interprets OLE DB session properties as follows. For the full description, see Appendix C, "Properties," in the OLE DB Programmer's Reference.

Property ID | Short Description
DBPROP_SESS_ AUTOCOMMITISOLEVELS | This provider supports DBPROP_TI_READCOMMITED and DBPROP_TI_SERIALIZABLE.


Commands

The OLE DB Provider for Oracle exposes the ICommand interface and command objects.

For more information on | Go to
The ICommand interface | ICommand in the OLE DB Programmer's Reference
Commands | Commands in the OLE DB Programmer's Reference
Command parameters | "Using Parameters" in Chapter 3, "Commands," of the OLE DB Programmer's Reference
Multiple execution of a single command: command preparation | ICommandPrepare in the OLE DB Programmer's Reference


Command Syntax

The OLE DB Provider for Oracle recognizes command syntax specified by the DBGUID_DBSQL macro. This provider does not support the {result set} escape in the ODBC Driver.

Stored Procedures

When executing an Oracle stored procedure using a command, you can use the ODBC procedure call escape sequence in the command text.

ODBC SQL Syntax

{call CREDIT_TO_ACCOUNT(123456, 100.00)}

Oracle PL/SQL Syntax

BEGIN
   CREDIT_TO_ACCCOUNT(123456, 100.00);
END

Note CURSOR REF is not supported.


Command Parameters

Parameters are marked in command text with the ODBC-specified question mark (?) character. For example, the following ODBC SQL statement is marked for a single input parameter:

{call CREDIT_TO_ACCOUNT(123456,?)}

The OLE DB Provider for Oracle supports input parameters in SQL statement commands. On procedure-call commands, this provider supports input, output, and input/output parameters. Output parameter values are returned to the application on execution.

The names of stored procedure parameters need not be specified in a DBPARAMBINDINFO structure. Use NULL for the value of the pwszName member to indicate that the provider should ignore the parameter name and use only the ordinal specified in the rgParamOrdinals member of ICommandWithParameters::SetParameterInfo.

If the name of a stored procedure parameter is specified, the OLE DB Provider for Oracle checks the name to ensure that it is valid. The OLE DB Provider for Oracle returns an error when it receives an erroneous parameter name from the consumer.


Rowsets

The OLE DB Provider for Oracle exposes the IRowset interface and rowset objects.

For more information on | Go to
The IRowset interface | IRowset inthe OLE DB Programmer's Reference
Rowsets | The overview of "Rowsets" in the OLE DB Programmer's Reference


Creating a Rowset with IOpenRowset

The OLE DB Provider for Oracle supports IOpenRowset::OpenRowset with the following restrictions:

  • A base table or view must be specified in a DBID structure that the pTableID parameter points to.

  • The DBID eKind member must indicate DBKIND_NAME.

  • The DBID uName member must specify the base table or view name as a Unicode character string.

  • The pIndexID parameter of OpenRowset must be NULL.

The result set of IOpenRowset::OpenRowset contains a single rowset.


Rowset Properties and Behaviors

The OLE DB Provider for Oracle implements the supported rowset properties as follows. For the full description, see Appendix C, "Properties," in the OLE DB Programmer's Reference.

Property ID | Short Description
DBPROP_ABORTPRESERVE | R/O: Read-only Default: VARIANT_FALSE Description: The OLE DB Provider for Oracle does not invalidate rowsets after an abort.
DBPROP_ BLOCKINGSTORAGEOBJECTS | R/W: Read-only Default: VARIANT_FALSE Description: This provider's storage objects do not block the use of other rowset methods.
DBPROP_BOOKMARKS | R/W: Read-only Default: VARIANT_FALSE Description: The OLE DB Provider for Oracle does not support bookmarks for rowset row identification.
DBPROP_CACHEDEFERRED | R/W: Read-only Default: VARIANT_FALSE Description: Cannot defer column fetching.
DBPROP_CANFETCHBACKWARDS | R/W: Read-only Default: VARIANT_FALSE Description: The OLE DB Provider for Oracle does not support fetching backward.
DBPROP_CANHOLDROWS | R/W: Read-only Default: VARIANT_TRUE Description: The OLE DB Provider for Oracle rowsets are read-only. Rowsets may hold rows across the fetcher, as long as RestartPosition is not called.
DBPROP_CANSCROLLBACKWARDS | R/W: Read-only Default: VARIANT_FALSE Description: The OLE DB Provider for Oracle does not support backward scrolling.
DBPROP_COMMITPRESERVE | R/W: Read-only Default: VARIANT_TRUE Description: The OLE DB Provider for Oracle maintains a valid rowset if the property is VARIANT_TRUE.
DBPROP_DEFERRED | R/W: Read-only Default: VARIANT_FALSE Description: The OLE DB Provider for Oracle fetches data in the column when the row containing the data is fetched.
DBPROP_IACCESSOR DBPROP_ICOLUMNSINFO DBPROP_ICONVERTTYPE DBPROP_IROWSET DBPROP_IROWSETINFO | R/W: Read-only Default: VARIANT_TRUE Description: The OLE DB Provider for Oracle supports these interfaces on all rowsets.
DBPROP_ICONNECTIONPOINTCONTAINER | R/W: Read-only Default: VARIANT_TRUE
DBPROP_IROWSETIDENTITY | R/W: Read-only Default: VARIANT_TRUE Description: The OLE DB Provider for Oracle exposes IRowsetIdentity on rowsets.
DBPROP_ISUPPORTERRORINFO | R/W: Read-only Default: VARIANT_TRUE Description: The OLE DB Provider for Oracle exposes the ISupportErrorInfo interface on rowsets.
DBPROP_ISEQUENTIALSTREAM | R/W: Read-only Default: VARIANT_TRUE Description: The OLE DB Provider for Oracle exposes the ISequentialStream interface to support long, variable-length data stored in Oracle.
DBPROP_LITERALIDENTITY | R/W: Read-only Default: VARIANT_TRUE Description: See the description in the OLE DB Programmer's Reference.
DBPROP_MAXOPENROWS | R/W: Read-only Default: 0 Description: The OLE DB Provider for Oracle does not limit the number of rows that can be active in rowsets.
DBPROP_MAXROWS | R/W: Read/Write Default: 0 Description: No limit.
DBPROP_NOTIFICATIONGRANULARITY | R/W: Read-only Default: NT_MULTIPLE_ROWS Description: See the description in the OLE DB Programmer's Reference.
DBPROP_NOTIFICATIONPHASES | R/W: Read-only Default: DBPROPVAL_NP_OKTODO DBPROPVAL_NP_ABOUTTODO DBPROPVAL_NP_SYNCHAFTER DBPROPVAL_NP_FAILEDTODO BPROPVAL_NP_DIDEVENT Description: The OLE DB Provider for Oracle supports all notification phases.
DBPROP_NOTIFYROWSETRELEASE DBPROP_NOTIFYROWSETFETCHPOSITIONCHANGE | R/W: Read-only Default: DBPROPVAL_NP_OKTODO DBPROPVAL_NP_ABOUTTODO Description: The OLE DB Provider for Oracle notification phases are cancelable prior to an attempt to perform the rowset modification indicated. This provider does not support phase cancellation after the attempt has completed.
DBPROP_OTHERINSERT DBPROP_OTHERUPDATEDELETE DBPROP_OWNINSERT DBPROP_OWNUPDATEDELETE | R/W: Read/only Default: VARIANT_FALSE Description: See the description in the OLE DB Programmer's Reference.
DBPROP_QUICKRESTART | R/W: Read/only Default: VARIANT_FALSE Description: See the description in the OLE DB Programmer's Reference.
DBPROP_REENTRANTEVENTS | R/W: Read-only Default: VARIANT_FALSE Description: The OLE DB Provider for Oracle rowsets are re-entrant and can return DB_E_NOTREENTRANT if a consumer attempts to access a non–re-entrant rowset method from a notification callback.
DBPROP_REMOVEDELETED | R/W: Read/only Default: Description: See the description in the OLE DB Programmer's Reference.
DBPROP_ROWTHREADMODEL | R/W: Read-only Default: DBPROPVAL_RT_FREETHREAD Description: The OLE DB Provider for Oracle supports access to its objects from multiple execution threads of a single consumer.
DBPROP_SERVERCURSOR | R/W: Read/only Default: VARIANT_FALSE Description: The OLE DB Provider for Oracle does not support client-side cursors.
DBPROP_STRONGIDENTITY | R/W: Read-only Default: VARIANT_TRUE Description: See the description in the OLE DB Programmer's Reference.
DBPROP_TRANSACTEDOBJECT | R/W: Read-only Default: VARIANT_FALSE Description: The OLE DB Provider for Oracle supports only transacted objects. For more information, see “Transactions,” later in this file.


BLOBs and OLE Objects

The OLE DB Provider for Oracle exposes the ISequentialStream interface to support consumer access to Oracle LONG and LONG RAW data types as Binary Large Objects (BLOBs). The Read method on ISequentialStream allows the consumer to retrieve large amounts of data in manageable chunks.

Oracle Provider Storage Object Limitations

The OLE DB Provider for Oracle storage objects are not blocking.

The length of data presented by a consumer-implemented storage object must be made known to this provider when the row accessor that references the storage object is created. The consumer must bind a length indicator in the DBBINDING structure used for accessor creation.

For more information on | Go to
Binary Large Objects | The overview of "BLOBs and OLE Objects" in the OLE DB Programmer's Reference
The ISequentialStream interface | ISequentialStream


Notifications

The OLE DB Provider for Oracle supports consumer notification on rowset release and fetch position change.

To receive notification, the consumer queries the rowset for a connection-point interface, then connects a consumer-implemented IRowsetNotify interface to the rowset.

For more information on | Go to
Notifications | The overview of "Notifications" in the OLE DB Programmer's Reference
The IRowsetNotify interface | IRowsetNotify


Data Types

This section describes data types in the Oracle Provider.

For more information on | Go to
OLE DB data types | The overview of "Data Types in OLE DB" in Chapter 10 of the OLE DB Programmer's Reference
Mapping data types in rowsets and parameters | Data Type Mapping in Rowsets and Parameters


Data Type Mapping in Rowsets and Parameters

In rowsets and as parameter values, the OLE DB Provider for Oracle represents Oracle data by using the following OLE DB defined data types.

Oracle Server Data Type | OLE DB Data Type
CHAR | DBTYPE_STR
DATE | DBTYPE_DBTIMESTAMP
FLOAT | DBTYPE_R8
INTEGER | DBTYPE_NUMERIC
LONG | DBTYPE_STR
LONG RAW | DBTYPE_BYTES
NUMBER | DBTYPE_NUMERIC (in most cases) DBTYPE_VARNUMERIC (when scale is negative, precision is not specified in the column definition, or scale is larger than what will fit into DBTYPE_NUMERIC)
RAW | DBTYPE_BYTES
VARCHAR2 | DBTYPE_STR

Note The OLE DB Provider for Oracle does not support the Oracle MLSLABEL data type.


Transactions

The OLE DB Provider for Oracle implements local transaction support. The consumer can use distributed or coordinated transactions by using Microsoft Distributed Transaction Coordinator (MS DTC). For consumers requiring transaction control that spans multiple sessions, this provider can join transactions initiated and maintained by the Microsoft Distributed Transaction Coordinator.

By default, the Oracle provider uses an autocommit transaction mode, where each discreet action on a consumer session comprises a complete transaction against an Oracle server. For this provider, autocommit mode is local. Autocommit transactions never span more than a single session.

This provider exposes the ITransactionLocal interface, allowing the consumer to use explicitly and implicitly started transactions on a single Oracle connection. This provider does not support nested local transactions at this time.

For more information on | Go to
Transactions | The overview of "Transactions" in the OLE DB Programmer's Reference
The ITransactionLocal interface | ITransactionLocal


Supporting Local Transactions

A session delimits transaction scope for an Oracle Provider local transaction. When, at consumer direction, this provider submits a request to a connected Oracle server, the request constitutes a unit of work for the provider. Local transactions always wrap one or more units of work on a single Oracle Provider session.

Using the default Oracle Provider autocommit mode, a single unit of work is treated as the scope of a local transaction. No more than one unit ever participates in the local transaction. Upon successful completion of a work unit, the work is committed. On failure, any work begun is rolled back and the error is reported to the consumer.

The consumer can direct more precise control over local transaction scope by using ITransactionLocal. When a consumer session initiates a transaction, all session work units between the transaction start point and the eventual Commit or Abort are treated as an atomic unit. The OLE DB Provider for Oracle implicitly begins a transaction when directed to do so by the consumer. If the consumer requests retention, the session reverts to autocommit mode.

The OLE DB Provider for Oracle supports ITransactionLocal::StartTransaction parameters as follows.

Parameter | Description
isoLevel | In local transactions, this provider supports ISOLATIONLEVEL_READCOMMITTED, ISOLATIONLEVEL_REPEATABLEREAD, ISOLATIONLEVEL_ISOLATED, and the synonyms ISOLATIONLEVEL_CURSORSTABILITY and ISOLATIONLEVEL_SERIALIZABLE.
isoFlags | This provider returns an error for any value other than zero.
pOtherOptions | If not NULL, the provider requests the options object from the interface. The provider returns XACT_E_ NOTIMEOUT if the options object’s ulTimeout member is not zero. The provider ignores the value of the szDescription member.
pulTransactionLevel | If not NULL, the provider returns the nested level of the transaction, which is always 1 at this point (because nested transactions are not supported).

For local transactions, the OLE DB Provider for Oracle implements ITransaction::Abort parameters as follows.

Parameter | Description
pboidReason | Ignored if set. Can safely be NULL.
FRetaining | When TRUE, a new transaction is implicitly begun for the session. The transaction must be committed or aborted by the consumer. When FALSE, this provider reverts to autocommit mode for the session.
FAsync | Asynchronous abort is not supported by this provider. This provider returns XACT_E_NOTSUPPORTED if the value is not FALSE.

For local transactions, the OLE DB Provider for Oracle implements ITransaction::Commit parameters as follows.

Parameter | Description
fRetaining | When TRUE, a new transaction is implicitly begun for the session. The transaction must be committed or aborted by the consumer. When FALSE, this provider reverts to autocommit mode for the session.
GrfTC | Asynchronous and phase-one returns are not supported by this provider. The provider returns XACT_E_ NOTSUPPORTED for any value other than XACTTC_SYNC.
GrfRM | Must be 0.

The OLE DB Provider for Oracle rowsets on the session are preserved on a local commit or abort operation based on the values of the rowset properties DBPROP_ABORTPRESERVE and DBPROP_COMMITPRESERVE. By default, these properties are both VARIANT_TRUE and all the provider's rowsets on the session are retained following an abort or commit operation.

The OLE DB Provider for Oracle does not implement the ITransactionObject interface. A consumer attempt to retrieve a reference on the interface returns E_NOINTERFACE.


Isolation Levels

Oracle clients can control transaction-isolation levels for a connection. To control transaction-isolation level, the consumer uses the following:

  • The DBPROPSET_SESSION property DBPROP_SESS_AUTOCOMMITISOLEVELS for this provider's default autocommit mode. The provider default for the level is DBPROPVAL_TI_READCOMMITTED.

  • The isoLevel parameter of ITransactionLocal::StartTransaction for local manual-commit transactions.

All levels restrict concurrency by applying locks to Oracle objects. As the client requires greater concurrency levels, Oracle applies greater restrictions on concurrent access to data. To maintain the highest level of concurrent access to data, the consumer should intelligently control its requests for specific concurrency levels.

Oracle supports only the READCOMMITTED and SERIALIZABLE isolation levels. All others are promoted to these levels. In high-performance situations, SERIALIZABLE is discouraged because of the number of locks taken to achieve that level of concurrency.

For more information on | Go to
Isolation levels | "Isolation Levels" in Chapter 12, "Transactions," of the OLE DB Programmer's Reference
Locking objects | "Locking" in Chapter 12, "Transactions," of the OLE DB Programmer's Reference


Errors

OLE/COM objects report errors through the HRESULT return code of object member functions. An OLE/COM HRESULT is a bit-packed structure. OLE provides macros that dereference structure members.

The OLE DB Provider for Oracle exposes IErrorLookup to retrieve information about an error.

For more information on | Go to
Errors in OLE DB | The overview on "Errors" in the OLE DB Programmer's Reference
Error lookup services | "Error Lookup Services" in Chapter 13, "Errors," of the OLE DB Programmer's Reference
IErrorLookup interface | IErrorLookup


Return Codes

At the basic level, a member function either succeeds or fails. At a somewhat more precise level, a function can succeed, but its success may not be identical to that intended by the application developer.

When a provider member function returns S_OK, the function succeeded.

When a member function does not return S_OK, the OLE/COM HRESULT-unpacking FAILED and IS_ERROR macros can determine the overall success or failure of a function.

If FAILED or IS_ERROR returns TRUE, the Oracle Provider consumer is ensured that member-function execution failed. When FAILED or IS_ERROR returns FALSE, and the HRESULT does not equal S_OK, then the consumer is ensured that the function succeeded in some sense. The consumer can retrieve detailed information about this success-with-information return from the provider error interfaces.

Consumers will, quite commonly, encounter the DB_S_ERRORSOCCURRED success-with-information HRESULT return. Typically, member functions that return DB_S_ERRORSOCCURRED define one or more parameters that deliver status values to the consumer. No error information may be available to the consumer other than that returned in status-value parameters, so consumers should implement application logic that retrieves status values when they are available.

The Oracle Provider's member functions do not return the success code S_FALSE. Any member function always returns S_OK to indicate success.

For more information on | Go to
Return codes | "Return Codes" in Chapter 13, "Errors," in the OLE DB Programmer's Reference


Programming OLE DB with the SQL Server OLE DB Provider

SQLOLEDB, the Microsoft® SQL Server™ Microsoft SQL Server OLE DB provider, exposes interfaces to consumers wanting access to data on one or more SQL Servers.

When developing an OLE DB consumer, select a provider developed for the data source you want to consume. Use SQLOLEDB to develop an optimized OLE DB consumer for SQL Server databases.

SQLOLEDB is an OLE DB–compliant provider.

The provider recognizes ANSI SQL and Transact-SQL statements as text for OLE DB commands.


System Requirements

SQLOLEDB requires a system running:

  • The Windows 95® operating system on Intel® computers.
    – or -
    The Windows NT® operating system (version 4.0 or later) on Intel or Alpha computers.

  • An appropriately installed and configured SQL Server client Net-Library.

SQLOLEDB supports OLE DB consumers that access SQL Server databases. To use SQLOLEDB, a SQL Server must be installed and running in your enterprise.

Consumer Development

A C or C++ compiler supporting OLE/COM component construction is required to develop an SQLOLEDB consumer.


Installing the SQL Server OLE DB Provider

The Microsoft SQL Server Setup program installs SQLOLEDB when installing the server or when installing the client utilities. Setup registers SQLOLEDB, making it available to OLE through the local registry.

SQLOLEDB Files

Files necessary to develop SQLOLEDB consumers are installed when the appropriate option is selected during setup. Several SQLOLEDB sample applications ship with Sphinx. The samples implement simple SQLOLEDB consumers in C++.

Directory | File | Description
C:\Program files\Common files\System\Ole db | Sqloledb.dll | The dynamic-link library that implements the SQLOLEDB provider.
C:\Mssql7\Include | Oledb.h | The OLE DB SDK header file for OLE DB providers and consumers.
 | Sqloledb.h | The header file used for developing SQLOLEDB consumers.
C:\Mssql7\Lib | Oledb.lib | The library file used for developing SQLOLEDB consumers.
C:\Mssql7\Samples\Sqloledb | All | Samples implementing simple SQLOLEDB consumers. The consumer samples are Microsoft® Visual C++® applications.

The OLE DB SDK

The primary source of information for OLE DB is the OLE DB Programmer’s Reference available with the OLE DB Software Development Kit (SDK). Both the SDK and Programmer’s Reference can be downloaded from the Microsoft Web site at http://www.microsoft.com/data/oledb.

Microsoft SQL Server does not install the OLE DB SDK. To develop OLE DB applications, you need the OLE DB SDK from the Microsoft Web site.


Data Source Objects

OLE DB uses the term data source for the set of OLE DB interfaces used to establish a link to a data store, such as Microsoft SQL Server. Creating an instance of the data source object of the provider is the first task of an OLE DB consumer.

Every OLE DB provider declares a class identifier for itself. The class identifier for SQLOLEDB is the C/C++ macro CLSID_SQLOLEDB. With the class identifier, the consumer uses the OLE CoCreateInstance function to manufacture an instance of the data source object.

SQLOLEDB is an in-process server. Instances of SQLOLEDB objects are created using the CLSCTX_INPROC_SERVER macro to indicate the executable context.

The SQLOLEDB data source object exposes the OLE DB initialization interfaces that allow the consumer to connect to existing SQL Server databases.

Example

This example uses the class identifier macro to create a SQLOLEDB data source object and get a reference to its IDBInitialize interface.

IDBInitialize* pIDBInitialize;
HRESULT    hr;

hr = CoCreateInstance(CLSID_SQLOLEDB, NULL, CLSCTX_INPROC_SERVER,
  IID_IDBInitialize, (void**) &pIDBInitialize);

if (SUCCEEDED(hr))
{
  //  Do stuff with the interface.
  pIDBInitialize->Uninitialize();
  pIDBInitialize->Release();
}
else
{
  // Display error from CoCreateInstance.
}

With successful creation of an instance of a SQLOLEDB data source object, the consumer application can continue on by initializing the data source and creating sessions. OLE DB sessions present the interfaces that allow data access and manipulation.

SQLOLEDB makes its first connection to a specified SQL Server as part of successful data source initialization. The connection is maintained as long as a reference is maintained on any data source initialization interface, or until IDBInitialize::Uninitialize is called.


Initialization and Authorization Properties

SQLOLEDB interprets OLE DB initialization and authorization properties as follows.

Property ID | Description
DBPROP_AUTH_CACHE_ AUTHINFO | Ignored if set. SQLOLEDB does not cache authentication information.
DBPROP_AUTH_ENCRYPT_ PASSWORD | Ignored if set. SQLOLEDB uses standard SQL Server security mechanisms to ensure password privacy.
DBPROP_AUTH_ INTEGRATED | Ignored if set. SQLOLEDB uses SQL Server Authentication Mode by default. Windows NT Authentication Mode can be selected using the SQLOLEDB-specific SSPROP_AUTH_ TRUSTEDCONNECTION property.
DBPROP_AUTH_MASK_ PASSWORD | Ignored if set. SQLOLEDB uses standard SQL Server security mechanisms to ensure password privacy.
DBPROP_AUTH_PASSWORD | The password assigned to a SQL Server login. This property is used when SQL Sever Authentication Mode is selected for authorizing access to a SQL Server database.
DBPROP_AUTH_PERSIST_ ENCRYPTED | Ignored if set.
DBPROP_AUTH_PERSIST_ SENSITIVE_AUTHINFO | SQLOLEDB persists authentication values, including an image of a password, if requested to do so. No encryption is provided.
DBPROP_AUTH_USERID | A SQL Server login. This property is used when SQL Sever Authentication Mode is selected for authorizing access to a SQL Server database.
DBPROP_INIT_ASYNCH | SQLOLEDB does not support this property. An error is returned on an attempt to set its value.
DBPROP_INIT_ DATASOURCE | An existing SQL Server database. On session creation, the database is selected as the execution context for the connection.
DBPROP_INIT_HWND | A window handle from the calling application. A valid window handle is required for the initialization dialog box displayed when prompting for initialization properties is allowed.
DBPROP_INIT_ IMPERSONATION_LEVEL | Not supported by SQLOLEDB. An attempt to set this property generates an error.
DBPROP_INIT_LCID | SQLOLEDB validates the locale ID and returns an error if the locale ID is not supported or is not installed on the client machine.
DBPROP_INIT_LOCATION | The name of an existing SQL Server. Data source initialization establishes a connection to the SQL Server on successful authentication of the user.
DBPROP_INIT_MODE | SQLOLEDB supports read-only (DB_MODE_READ) and read/write (DB_MODE_READWRITE) access permissions for sessions created on an initialized data source.
DBPROP_INIT_PROMPT | SQLOLEDB supports all prompting modes for data source initialization. SQLOLEDB uses DBPROMPT_NOPROMPT as its default setting for the property.
DBPROP_INIT_ PROTECTION_LEVEL | Not supported by SQLOLEDB. An attempt to set the property generates an error.
DBPROP_INIT_ PROVIDERSTRING | Ignored if set.
DBPROP_INIT_TIMEOUT | SQLOLEDB returns an error on initialization if a connection to the SQL Server cannot be established within the number of seconds specified.

SQLOLEDB defines the following additional initialization properties.

Property ID | Description
SSPROP_AUTH_ TRUSTEDCONNECTION | Type: VT_BOOL R/W: R/W Description: User authentication mode. VARIANT_TRUE SQLOLEDB uses Windows NT Authentication Mode to authorize user access to the SQL Server database specified by the DBPROP_INIT_LOCATION and DBPROP_INIT_DATASOURCE property values. VARIANT_FALSE Default SQLOLEDB uses SQL Server Authentication Mode to authorize user access to the SQL Server database. The SQL Server login and password are specified in the DBPROP_AUTH_USERID and DBPROP_AUTH_PASSWORD properties.
SSPROP_INIT_APPNAME | Type: VT_BSTR R/W: R/W Description: The client application name.
SSPROP_INIT_ AUTOTRANSLATE | Type: VT_BOOL R/W: R/W Description: OEM/ANSI character conversion. VARIANT_TRUE Default SQLOLEDB performs OEM/ANSI character conversion when multibyte character strings are retrieved from, or sent to, the SQL Server. VARIANT_FALSE SQLOLEDB does not perform OEM/ANSI character conversion on multibyte character string data.
SSPROP_INIT_ CURRENTLANGUAGE | Type: VT_BSTR R/W: R/W Description: A SQL Server language name. Identifies the language used for system message selection and formatting. The language must be installed on the SQL Server or data source initialization fails.
SSPROP_INIT_ NETWORKADDRESS | Type: VT_BSTR R/W: R/W Description: The network address of the SQL Server specified by the DBPROP_INIT_ LOCATION property.
SSPROP_INIT_ NETWORKLIBRARY | Type: VT_BSTR R/W: R/W Description: The name of the Net-Library (DLL) used to communicate with the SQL Server. The name should not include the path or the .dll file name extension. The default is provided by the SQL Server client configuration.
SSPROP_INIT_PACKETSIZE | Type: VT_I4 R/W: R/W Description: A network packet size in bytes. The packet size property value must be between 512 and 32767. The default SQLOLEDB network packet size is 4096.
SSPROP_INIT_ USEPROCFORPREP | Type: VT_I4 R/W: R/W Description: SQL Server stored procedure use. Defines the use of SQL Server temporary stored procedures to support the ICommandPrepare interface. SSPROPVAL_USEPROCFORPREP_OFF A temporary stored procedure is not created when a command is prepared. SSPROPVAL_USEPROCFORPREP_ON Default A temporary stored procedure is created when a command is prepared. The temporary stored procedures are dropped when the session is released. SSPROPVAL_ USEPROCFORPREP_ON_ DROP A temporary stored procedure is created when a command is prepared. The procedure is dropped when the command is unprepared with ICommandPrepare::Unprepare, or when a new command is specified for the command object with ICommandText::SetCommandText, or when all application references to the command are released.
SSPROP_INIT_WSID | Type: VT_BSTR R/W: R/W Description: A string identifying the workstation.


Sessions

A SQLOLEDB session represents a single connection to a SQL Server.

OLE DB requires that sessions delimit transaction space for a data source. All command objects created from a specific session object will participate in the local or distributed transaction of the session object.

The first session object created on the initialized data source receives the SQL Server connection established at initialization. When all references on the interfaces of the session object are released, the SQL Server connection becomes available to another session object created on the data source.

An additional session object created on the data source establishes its own connection to the SQL Server as specified by the data source. The SQL Server connection is dropped when the application releases all references to objects created that session.

Example

This example shows SQLOLEDB SQL Server connection usage.

int main()
{
  // Interfaces used in the example.
  IDBInitialize*  pIDBInitialize  = NULL;
  IDBCreateSession* pIDBCreateSession = NULL;
  IDBCreateCommand* pICreateCmd1    = NULL;
  IDBCreateCommand* pICreateCmd2    = NULL;
  IDBCreateCommand* pICreateCmd3    = NULL;

  // Initialize COM.
  if (FAILED(CoInitialize(NULL)))
  {
    // Display error from CoInitialize.
    return (-1);
  }

  // Get the memory allocator for this task.
  if (FAILED(CoGetMalloc(MEMCTX_TASK, &g_pIMalloc)))
  {
    // Display error from CoGetMalloc.
    goto EXIT;
  }

  // Create an instance of the data source object.
  if (FAILED(CoCreateInstance(CLSID_SQLOLEDB, NULL,
    CLSCTX_INPROC_SERVER, IID_IDBInitialize, (void**)
    &pIDBInitialize)))
  {
    // Display error from CoCreateInstance.
    goto EXIT;
  }

  // The InitFromPersistedDS function
  // performs IDBInitialize->Initialize() establishing
  // the first application connection to the SQL Server.
  if (FAILED(InitFromPersistedDS(pIDBInitialize, L"MyDataSource",
    NULL, NULL)))
  {
    goto EXIT;
  }

  // The IDBCreateSession interface is implemented on the data source
  // object. Maintaining the reference we receive maintains the
  // connection of the data source to the SQL Server.
  if (FAILED(pIDBInitialize->QueryInterface(IID_IDBCreateSession,
    (void**) &pIDBCreateSession)))
  {
    // Display error from pIDBInitialize.
    goto EXIT;
  }

  // Releasing this has no affect on the SQL Server connection
  // of the data source object because of the reference maintained by
  // pIDBCreateSession.
  pIDBInitialize->Release();
  pIDBInitialize = NULL;

  // The session created next receives the SQL Server connection of
  // the data source object. No new connection is established.
  if (FAILED(pIDBCreateSession->CreateSession(NULL,
    IID_IDBCreateCommand, (IUnknown**) &pICreateCmd1)))
  {
    // Display error from pIDBCreateSession.
    goto EXIT;
  }

  // A new connection to the SQL Server is established to support the
  // next session object created. On successful completion, the
  // application has two active connections on the SQL Server.
  if (FAILED(pIDBCreateSession->CreateSession(NULL,
    IID_IDBCreateCommand, (IUnknown**) &pICreateCmd2)))
  {
    // Display error from pIDBCreateSession.
    goto EXIT;
  }

  // pICreateCmd1 has the data source connection. Because the
  // reference on the IDBCreateSession interface of the data source
  // has not been released, releasing the reference on the session
  // object does not terminate a connection to the SQL Server.
  // However, the connection of the data source object is now
  // available to another session object. After a successful call to
  // Release, the application still has two active connections to the
  // SQL Server.
  pICreateCmd1->Release();
  pICreateCmd1 = NULL;

  // The next session created gets the SQL Server connection
  // of the data source object. The application has two active
  // connections to the SQL Server.
  if (FAILED(pIDBCreateSession->CreateSession(NULL,
    IID_IDBCreateCommand, (IUnknown**) &pICreateCmd3)))
  {
    // Display error from pIDBCreateSession.
    goto EXIT;
  }

EXIT:
  // Even on error, this does not terminate a SQL Server connection
  // because pICreateCmd1 has the connection of the data source
  // object.
  if (pICreateCmd1 != NULL)
    pICreateCmd1->Release();

  // Releasing the reference on pICreateCmd2 terminates the SQL
  // Server connection supporting the session object. The application
  // now has only a single active connection on the SQL Server.
  if (pICreateCmd2 != NULL)
    pICreateCmd2->Release();

  // Even on error, this does not terminate a SQL Server connection
  // because pICreateCmd3 has the connection of the
  // data source object.
  if (pICreateCmd3 != NULL)
    pICreateCmd3->Release();

  // On release of the last reference on a data source interface, the
  // connection of the data source object to the SQL Server is broken.
  // The example application now has no SQL Server connections active.
  if (pIDBCreateSession != NULL)
    pIDBCreateSession->Release();

  // Called only if an error occurred while attempting to get a
  // reference on the IDBCreateSession interface of the data source.
  // If so, the call to IDBInitialize::Uninitialize terminates the
  // connection of the data source object to the SQL Server.
  if (pIDBInitialize != NULL)
  {
    if (FAILED(pIDBInitialize->Uninitialize()))
    {
    // Uninitialize is not required, but it fails if an
    // interface has not been released. We can use it for
    // debugging.
    }
    pIDBInitialize->Release();
  }

  if (g_pIMalloc != NULL)
    g_pIMalloc->Release();

  CoUninitialize();

  return (0);
}

Connecting SQLOLEDB session objects to a SQL Server can generate significant application overhead for applications that continually create and release session objects. The overhead can be minimized by managing SQLOLEDB session objects efficiently. SQLOLEDB applications can keep the SQL Server connection of a session object active by maintaining a reference on at least one interface of the object.

For example, maintaining a pool of command creation object references keeps active connections for those session objects in the pool. As session objects are required, the pool maintenance code passes a valid IDBCreateCommand interface pointer to the application function requiring the session. When the application function no longer requires the session, the function returns the interface pointer back to the pool maintenance code rather than releasing the application’s reference to the command creation object.

Note In the above example, the IDBCreateCommand interface is used because ICommand implements GetDBSession, the only function in command or rowset scope that allows an object to determine the session on which it was created. Therefore, a command object, and only a command object, allows an application to retrieve a data source object pointer from which additional sessions can be created.


Session Properties

SQLOLEDB interprets OLE DB session properties as follows.

Property ID | Description
DBPROP_SESS_ AUTOCOMMITISOLEVELS | SQLOLEDB supports all autocommit transaction isolation levels with the exception of the chaos level, DBPROPVAL_TI_CHAOS.


Persisted Data Source Objects

SQLOLEDB supports persisted data source objects with the IPersistFile interface.

Examples

Persist data source initialization properties

This example shows a function that persists data source initialization properties that define a server, database, and the use of the Windows NT Authentication Mode for connection. The server name and database name are received in the pLocation and pDatasource parameters of the function.

HRESULT SetAndSaveInitProps
(
  IDBInitialize* pIDBInitialize,
  WCHAR* pLocation,
  WCHAR* pDatasource,
  BOOL bUseWinNTAuth
)
{
  const ULONG   nProps = 3;
  ULONG     nSSProps;
  ULONG     nPropSets;
  ULONG     nProp;
  IDBProperties*  pIDBProperties  = NULL;
  IPersistFile* pIPersistFile = NULL;
  DBPROP    aInitProps[nProps];
  DBPROP*   aSSInitProps  = NULL;
  DBPROPSET*  aInitPropSets = NULL;
  HRESULT   hr;

  // Setup arrays based on whether the Windows NT
  // Authentication Mode is used.
  if (bUseWinNTAuth == FALSE)
  {
    nSSProps = 0;
    nPropSets = 1;
  }
  else
  {
    nPropSets = 2;

    nSSProps = 1;
    aSSInitProps = new DBPROP[nSSProps];
  }
  aInitPropSets = new DBPROPSET[nPropSets];

  // Initialize common property options.
  for (nProp = 0; nProp < nProps; nProp++)
  {
    VariantInit(&aInitProps[nProp].vValue);
    aInitProps[nProp].dwOptions = DBPROPOPTIONS_REQUIRED;
    aInitProps[nProp].colid = DB_NULLID;
  }

  // Level of prompting for rest of the connection
  // process.
  aInitProps[0].dwPropertyID = DBPROP_INIT_PROMPT;
  aInitProps[0].vValue.vt = VT_I2;
  aInitProps[0].vValue.iVal = DBPROMPT_NOPROMPT;

  // Server name.
  aInitProps[1].dwPropertyID = DBPROP_INIT_LOCATION;
  aInitProps[1].vValue.vt = VT_BSTR;
  aInitProps[1].vValue.bstrVal = SysAllocString(pLocation);

  // Database.
  aInitProps[2].dwPropertyID = DBPROP_INIT_DATASOURCE;
  aInitProps[2].vValue.vt = VT_BSTR;
  aInitProps[2].vValue.bstrVal = SysAllocString(pDatasource);

  if (bUseWinNTAuth == TRUE)
  {
    // Initialize SQL Server provider-specific properties.
    for (nProp = 0; nProp < nSSProps; nProp++)
    {
    VariantInit(&aSSInitProps[nProp].vValue);
    aSSInitProps[nProp].dwOptions = DBPROPOPTIONS_REQUIRED;
    aSSInitProps[nProp].colid = DB_NULLID;
    }

    // Windows NT Authentication Mode.
    aSSInitProps[0].dwPropertyID = SSPROP_AUTH_TRUSTEDCONNECTION;
    aSSInitProps[0].vValue.vt = VT_BOOL;
    aSSInitProps[0].vValue.boolVal = VARIANT_TRUE;
  }

  // Now that properties are set, construct the PropertySet array.
  aInitPropSets[0].guidPropertySet = DBPROPSET_DBINIT;
  aInitPropSets[0].cProperties = nProps;
  aInitPropSets[0].rgProperties = aInitProps;

  if (bUseWinNTAuth == TRUE)
  {
    aInitPropSets[1].guidPropertySet = DBPROPSET_SQLSERVERDBINIT;
    aInitPropSets[1].cProperties = nSSProps;
    aInitPropSets[1].rgProperties = aSSInitProps;
  }

  // Set initialization properties.
  pIDBInitialize->QueryInterface(IID_IDBProperties,
    (void**) &pIDBProperties);
  hr = pIDBProperties->SetProperties(nPropSets, aInitPropSets);
  if (FAILED(hr))
  {
    // Display error from failed SetProperties.
  }
  pIDBProperties->Release();

  // Free references on OLE known strings.
  for (nProp = 0; nProp < nProps; nProp++)
  {
    if (aInitProps[nProp].vValue.vt == VT_BSTR)
    SysFreeString(aInitProps[nProp].vValue.bstrVal);
  }

  // Free dynamically allocated memory.
  delete [] aInitPropSets;
  delete [] aSSInitProps;

  // On success, persist the data source.
  if (SUCCEEDED(hr))
  {
    pIDBInitialize->QueryInterface(IID_IPersistFile,
    (void**) &pIPersistFile);

    hr = pIPersistFile->Save(OLESTR("MyDataSource"), FALSE);

    if (FAILED(hr))
    {
    // Display errors from IPersistFile interface.
    }
    pIPersistFile->Release();
  }

  return (hr);
}

Use persisted data source initialization properties

This example uses a persisted data source object with additional initialization properties that provide a SQL Server login ID and password.

HRESULT InitFromPersistedDS
(
  IDBInitialize* pIDBInitialize,
  WCHAR* pPersistedDSN,
  WCHAR* pUID,
  WCHAR* pPWD
)
{
  const ULONG   nProps = 3;
  const ULONG   nPropSets = 1;
  ULONG     nProp;
  IDBProperties*  pIDBProperties  = NULL;
  IPersistFile* pIPersistFile = NULL;
  DBPROP    aInitProps[nProps];
  DBPROPSET   aInitPropSets[nPropSets];
  HRESULT   hr;

  // First load the persisted data source information.
  pIDBInitialize->QueryInterface(IID_IPersistFile,
    (void**) &pIPersistFile);

  hr = pIPersistFile->Load(pPersistedDSN, STGM_DIRECT);

  if (FAILED(hr))
  {
    // Display errors from IPersistFile interface.
  }
  pIPersistFile->Release();

  if (FAILED(hr))
  {
    return (hr);
  }

  // Initialize common property options.
  for (nProp = 0; nProp < nProps; nProp++)
  {
    VariantInit(&aInitProps[nProp].vValue);
    aInitProps[nProp].dwOptions = DBPROPOPTIONS_REQUIRED;
    aInitProps[nProp].colid = DB_NULLID;
  }

  // Level of prompting for rest of the connection
  // process.
  aInitProps[0].dwPropertyID = DBPROP_INIT_PROMPT;
  aInitProps[0].vValue.vt = VT_I2;
  aInitProps[0].vValue.iVal = DBPROMPT_NOPROMPT;

  // SQL Server login identifier.
  aInitProps[1].dwPropertyID = DBPROP_AUTH_USERID;
  aInitProps[1].vValue.vt = VT_BSTR;
  aInitProps[1].vValue.bstrVal = SysAllocString(
    (pUID != NULL ? pUID : OLESTR(““)));

  // SQL Server password.
  aInitProps[2].dwPropertyID = DBPROP_AUTH_PASSWORD;
  aInitProps[2].vValue.vt = VT_BSTR;
  aInitProps[2].vValue.bstrVal = SysAllocString(
    (pPWD != NULL ? pPWD : OLESTR(““)));

  // Now that properties are set, construct the PropertySet array.
  aInitPropSets[0].guidPropertySet = DBPROPSET_DBINIT;
  aInitPropSets[0].cProperties = nProps;
  aInitPropSets[0].rgProperties = aInitProps;

  // Set initialization properties.
  pIDBInitialize->QueryInterface(IID_IDBProperties,
    (void**) &pIDBProperties);
  hr = pIDBProperties->SetProperties(nPropSets, aInitPropSets);
  if (FAILED(hr))
  {
    // Display error from failed SetProperties.
  }
  pIDBProperties->Release();

  // Free references on OLE-known strings.
  for (nProp = 0; nProp < nProps; nProp++)
  {
    if (aInitProps[nProp].vValue.vt == VT_BSTR)
    SysFreeString(aInitProps[nProp].vValue.bstrVal);
  }

  if (FAILED(hr))
  {
    return (hr);
  }

  hr = pIDBInitialize->Initialize();
  return (hr);
}

The IPersistFile::Save function can be called before or after calling IDBInitialize::Initialize. Calling the function after a successful return from IDBInitialize::Initialize ensures persisting a valid data source specification.


Commands

SQLOLEDB exposes the ICommand interface and command objects.


Command Syntax

SQLOLEDB recognizes command syntax specified by the DBGUID_DBSQL macro. For SQLOLEDB, the specifier indicates that an amalgam of ODBC, ANSI, and SQL Server-specific Transact-SQL is valid syntax. For example, the following SQL statement uses an ODBC SQL escape sequence to specify the LCASE string function:

SELECT customerid={fn LCASE(CustomerID)} FROM Customers

LCASE returns a character string, converting all uppercase characters to their lowercase equivalents. The ANSI SQL string function LOWER performs the same operation, so the following SQL statement is an ANSI equivalent to the ODBC statement presented above:

SELECT customerid=LOWER(CustomerID) FROM Customers

SQLOLEDB successfully processes either form of the statement when specified as text for a command.

Stored Procedures

When executing an SQL Server stored procedure using an SQLOLEDB command, use the ODBC procedure call escape sequence in the command text. SQLOLEDB then uses the remote procedure call mechanism of SQL Server to optimize command processing. For example, the following ODBC SQL statement is preferred command text over the Transact-SQL form:

ODBC SQL

{call SalesByCategory('Produce', '1995')}

Transact-SQL

EXECUTE SalesByCategory 'Produce', '1995'


Command Parameters

Parameters are marked in command text with the ODBC-specified question mark character. For example, the following ODBC SQL statement is marked for a single input parameter:

{call SalesByCategory('Produce', ?)}

Example

SQLOLEDB supports input parameters in SQL statement commands. On procedure-call commands, SQLOLEDB supports input, output, and input/output parameters. Output parameter values are returned to the application either on execution or when all returned rowsets are exhausted by the application. To ensure that returned values are valid, use IMultipleResults to force rowset consumption.

// Macro used in the example.
#define COUNTRY_MAX_CHARS     15

// Structure supporting the parameters of the example stored procedure.
typedef struct tagSPROCPARAMS
  {
  long    lReturnValue;
  char    acCountry[COUNTRY_MAX_CHARS + 1];
  } SPROCPARAMS;

  // Interfaces used in the example.
  ICommandText*   pICommandText = NULL;
  ICommandWithParameters* pICommandWithParameters = NULL;
  IAccessor*    pIAccessor = NULL;
  IMultipleResults* pIMultipleResults = NULL;
  IRowset*    pIRowset = NULL;

  // Command parameter data.
  DBPARAMS    Params;
  const ULONG   nParams = 2;
  DBPARAMBINDINFO   rgParamBindInfo[nParams] =
    {
    L"DBTYPE_I4",
    L"ReturnVal",
    sizeof(long),
    DBPARAMFLAGS_ISOUTPUT,
    11,
    0,
    L"DBTYPE_VARCHAR",
    L"@Country",
    COUNTRY_MAX_CHARS,
    DBPARAMFLAGS_ISINPUT,
    0,
    0 };
  ULONG     rgParamOrdinals[nParams] = {1,2};

  // Parameter accessor data.
  HACCESSOR     hAccessor;
  DBBINDING     acDBBinding[nParams];
  DBBINDSTATUS    acDBBindStatus[nParams];

  // The command and parameter data.
  WCHAR*      wszSQLString =
    L"{? = call CustomersInCountry(?)}";
  SPROCPARAMS   sprocparams = {0, "USA"};

  // Returned count of rows affected.
  LONG      cRowsAffected = 0;

  HRESULT     hr;

  // Create the command.
  if (FAILED(hr = pIDBCreateCommand->CreateCommand(NULL,
    IID_ICommandText, (IUnknown**) &pICommandText)))
    {
    // Process error from IDBCreateCommand and return.
    }

  // Set the command text value.
  if (FAILED(hr = pICommandText->SetCommandText(DBGUID_DBSQL,
    wszSQLString)))
    {
    // Process error from ICommand and return.
    }

  // Get the ICommandWithParameters interface to set up parameter
  // values.
  if (FAILED(hr = pICommandText->QueryInterface(
    IID_ICommandWithParameters,
    (void**) &pICommandWithParameters)))
    {
    // Process error from ICommand and return.
    }

  // Set parameter information.
  if (FAILED(hr = pICommandWithParameters->SetParameterInfo(nParams,
    rgParamOrdinals, rgParamBindInfo)))
    {
    // Process error from ICommandWithParameters and return.
    }

  // Create parameter accessor, but first set binding structures
  // to indicate the characteristics of each parameter.
  for (ULONG i = 0; i < nParams; i++)
    {
    acDBBinding[i].obLength = 0;
    acDBBinding[i].obStatus = 0;
    acDBBinding[i].pTypeInfo = NULL;
    acDBBinding[i].pObject = NULL;
    acDBBinding[i].pBindExt = NULL;
    acDBBinding[i].dwPart = DBPART_VALUE;
    acDBBinding[i].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
    acDBBinding[i].dwFlags = 0;
    acDBBinding[i].bScale = 0;
    }

  acDBBinding[0].iOrdinal = 1;
  acDBBinding[0].obValue = offsetof(SPROCPARAMS, lReturnValue);
  acDBBinding[0].eParamIO = DBPARAMIO_OUTPUT;
  acDBBinding[0].cbMaxLen = sizeof(long);
  acDBBinding[0].wType = DBTYPE_I4;
  acDBBinding[0].bPrecision = 11;

  acDBBinding[1].iOrdinal = 2;
  acDBBinding[1].obValue = offsetof(SPROCPARAMS, acCountry);
  acDBBinding[1].eParamIO = DBPARAMIO_INPUT;
  acDBBinding[1].cbMaxLen = COUNTRY_MAX_CHARS;
  acDBBinding[1].wType = DBTYPE_STR;
  acDBBinding[1].bPrecision = 0;

  // Get the IAccessor interface, then create the accessor for
  // the defined parameters.
  pICommandWithParameters->QueryInterface(IID_IAccessor,
    (void**) &pIAccessor);

  hr = pIAccessor->CreateAccessor(DBACCESSOR_PARAMETERDATA,
    nParams, acDBBinding, sizeof(SPROCPARAMS), &hAccessor,
    acDBBindStatus);
  if (FAILED(hr))
    {
    // Process error from IAccessor and return.
    }

  // Fill the DBPARAMS structure for the command execution.
  Params.pData = &sprocparams;
  Params.cParamSets = 1;
  Params.hAccessor = hAccessor;

  // Execute the command.
  if (FAILED(hr = pICommandText->Execute(NULL, IID_IMultipleResults,
    &Params, &cRowsAffected, (IUnknown**) &pIMultipleResults)))
    {
    // Process error from ICommand and return.
    }

  // For each rowset or count of rows affected...
  do
    {
    hr = ((IMultipleResults*) pIResults)->GetResult(NULL, 0,
    IID_IRowset, &cRowsAffected, (IUnknown**) &pIRowset);

    switch (hr)
    {
    case S_OK:
      {
      if (pIRowset != NULL)
        {
        // Process data from the rowset and release.
        pIRowset->Release();
        }
      else if (cRowsAffected != -1)
        {
        printf("Command succeeded. %ld rows affected.\n\n",
        cRowsAffected);
        }
      else
        {
        printf("Command succeeded.\n\n");
        }

      break;
      }

    case DB_S_NORESULT:
    case DB_S_STOPLIMITREACHED:
      break;

    default:
      {
      DumpError(pIResults, IID_IMultipleResults);
      break;
      }
    }
    }
  while (hr == S_OK);

  if (SUCCEEDED(hr))
    {
    // At this point, the value of the return is guaranteed correct.
    // If any other output parameters had been specified, then they
    // too would now contain their correct values.
    printf("Return value %d\n", sprocparams.lReturnValue);
    }

The names of stored procedure parameters need not be specified in a DBPARAMBINDINFO structure. Use NULL for the value of the pwszName member to indicate that SQLOLEDB should ignore the parameter name and use only the ordinal specified in the rgParamOrdinals member of ICommandWithParameters::SetParameterInfo.

If the name of a stored procedure parameter is specified, SQLOLEDB checks the name to ensure that it is valid. SQLOLEDB returns an error when it receives an erroneous parameter name from the consumer.


Preparing Commands

SQLOLEDB supports command preparation for optimized multiple execution of a single command; however, command preparation generates overhead, and a consumer does not need to prepare a command to execute it more than once. In general, a command should be prepared if it will be executed more than three times.

SQLOLEDB may create a temporary stored procedure when command text is prepared. Some commands should never be prepared. For example, commands that specify stored procedure execution or include invalid text for SQL Server stored procedure creation, should not be prepared.

If a temporary stored procedure is created, SQLOLEDB executes the temporary stored procedure, returning results as if the statement itself was executed.

Temporary stored procedure creation is controlled by the SQLOLEDB-specific initialization property SSPROP_INIT_USEPROCFORPREP. If the property value is either SSPROPVAL_USEPROCFORPREP_ON or SSPROPVAL_USEPROCFORPREP_ON_DROP, SQLOLEDB attempts to create a stored procedure when a command is prepared. Stored procedure creation succeeds if the application user has sufficient SQL Server permissions.

For consumers that infrequently disconnect, creation of temporary stored procedures can require significant resources of tempdb, the SQL Server system database in which temporary objects are created. When the value of SSPROP_INIT_USEPROCFORPREP is SSPROPVAL_USEPROCFORPREP_ON, temporary stored procedures created by SQLOLEDB are dropped only when the session that created the command loses its connection to the SQL Server. If that connection is the default connection created on data source initialization, then the temporary stored procedure is dropped only when the data source becomes uninitialized.

When the value of SSPROP_INIT_USEPROCFORPREP is SSPROPVAL_USEPROCFORPREP_ON_DROP, SQLOLEDB temporary stored procedures are dropped when one of the following occurs:

  • The consumer uses ICommandText::SetCommandText to indicate a new command.

  • The consumer uses ICommandPrepare::Unprepare to indicate that it no longer requires the command text

  • The consumer releases all references to the command object using the temporary stored procedure

A command object will have at most one temporary stored procedure in tempdb. Any existing temporary stored procedure will represent the current command text of a specific command object.


Commands Generating Multiple-Rowset Results

SQLOLEDB can return multiple rowsets from SQL Server statements. SQL Server statements return multiple-rowset results under the following conditions:

  • Batched SQL statements submitted as a single command.

  • Stored procedures that implement a batch of SQL statements.

  • SQL statements that include the Transact-SQL COMPUTE or COMPUTE BY clause.

Batches

SQLOLEDB recognizes the semicolon character as a batch delimiter for SQL statements:

WCHAR*   wSQLString = L"SELECT * FROM Categories; "
          L"SELECT * FROM Products";

Sending multiple SQL statements in one batch is more efficient than executing each SQL statement separately. Sending one batch reduces the network roundtrips from the client to the server.

Stored Procedures

SQL Server returns a result set for each statement in a stored procedure, so most SQL Server stored procedures return multiple result sets.

COMPUTE BY and COMPUTE

The Transact-SQL COMPUTE BY clause generates subtotals within a SELECT statement result set. The COMPUTE clause generates a total at the end of the result set. SQLOLEDB returns each COMPUTE BY subtotal and the COMPUTE total as a separate rowset result.

Using IMultipleResults

In general, consumers should use the IMultipleResults interface to process the rowset or rowsets returned by SQLOLEDB command execution.

When SQLOLEDB submits a command for execution, SQL Server executes the statement or statements and returns any results. The complete process is a roundtrip from the client to the SQL Server and back. Each client connection to a SQL Server can have at most one active roundtrip. That is, within a SQLOLEDB session, only a single command object can be actively executing or returning results on the connection. This is the default result set behavior of SQL Server client connections.

To complete a roundtrip, a client must process all results from command execution. Because SQLOLEDB command execution can generate multiple-rowset objects as results, use the IMultipleResults interface to ensure that application data retrieval completes the client-initiated roundtrip.

The following Transact-SQL statement generates multiple rowsets, some containing row data from the OrderDetails table and some containing results of the COMPUTE BY clause:

SELECT OrderID, FullPrice = (UnitPrice * Quantity), Discount,
  Discounted = UnitPrice * (1 - Discount) * Quantity
FROM OrderDetails
ORDER BY OrderID
COMPUTE
  SUM(UnitPrice * Quantity), SUM(UnitPrice * (1 - Discount) * Quantity)
  BY OrderID

If a consumer executes a command containing this text and requests a rowset as the returned results interface, only the first set of rows is returned. The consumer may process all rows in the rowset returned, but until the command is canceled no other commands can be executed on the session object. The consumer can cancel the command either using ICommand::Cancel or by releasing all references held on the command object and the derived rowset.

Using IMultipleResults in all instances allows the consumer to get all rowsets generated by command execution and allows consumers to intelligently determine when to cancel command execution and free a session object for use by other commands.

Note When using SQL Server cursors, command execution creates the cursor. SQL Server returns success or failure on the cursor creation; therefore, the roundtrip to the SQL Server is complete upon the return from command execution. Each GetNextRows call then becomes a roundtrip. In this way, multiple active command objects can exist, each processing a rowset that is the result of a fetch from the server cursor. For more information, see "Rowsets and SQL Server Cursors."


Rowsets

SQLOLEDB supports IOpenRowset::OpenRowset (with some restrictions); the result set containing a single rowset. Consumers that support dynamic command text can benefit from rowsets created with ICommand::Execute, where the properties desired in the resulting rowset can constain the text of the command. Rowset properties implemented by SQLOLEDB are listed. SQL Server cursors are exposed through rowsets, allowing the use of SQL Server concurrency control mechanisms.


Creating a Rowset with IOpenRowset

SQLOLEDB supports IOpenRowset::OpenRowset with the following restrictions:

  • A base table or view must be specified in a DBID structure that the pTableID parameter points to.

  • The DBID eKind member must indicate DBKIND_NAME.

  • The DBID uName member must specify the base table or view name as a Unicode character string.

  • The pIndexID parameter of OpenRowset must be NULL.

The result set of IOpenRowset::OpenRowset contains a single rowset. Result sets containing a single rowset can be supported by SQL Server cursors. Cursor support allows the developer to use SQL Server concurrency mechanisms.


Creating Rowsets with ICommand::Execute

For rowsets created with ICommand::Execute, the properties desired in the resulting rowset can constrain the text of the command. This is especially critical for consumers that support dynamic command text.

SQLOLEDB cannot use SQL Server cursors to support the multiple-rowsets result generated by many commands. If a consumer requests a rowset requiring SQL Server cursor support, an error occurs if the command text used generates more than a single rowset as its result. For more information, see Commands Generating Multiple-Rowset Results.

Scrollable SQLOLEDB rowsets are supported by SQL Server cursors. SQL Server imposes limitations on cursors that are sensitive to changes made by other users of the database. Specifically, the rows in some cursors cannot be ordered, and attempting to create a rowset using a command containing an SQL ORDER BY clause can fail. For more information, see Rowsets and SQL Server Cursors.


Rowset Properties and Behaviors

SQLOLEDB implements rowset properties as follows.

Property ID | Description
DBPROP_ABORTPRESERVE R/W: Read/write
Default: VARIANT_FALSE
Description: By default, SQLOLEDB invalidates rowsets after an abort. SQLOLEDB maintains a valid rowset if the property is VARIANT_TRUE.
DBPROP_APPENDONLY | The DBPROP_APPENDONLY rowset property is not implemented by SQLOLEDB. Attempting to read or write the property value generates an error.
DBPROP_
BLOCKINGSTORAGEOBJECTS 		R/W: Read-only
Default: VARIANT_TRUE
Description: SQLOLEDB storage objects block the use of other rowset methods.
DBPROP_BOOKMARKS
DBPROP_
LITERALBOOKMARKS 		R/W: Read/write
Default: VARIANT_FALSE
Description: SQLOLEDB supports bookmarks for rowset row identification when DBPROP_BOOKMARKS or DBPROP_
LITERALBOOKMARKS is VARIANT_
TRUE.

Setting either property to VARIANT_TRUE does not enable rowset positioning by bookmark. Set DBPROP_IRowsetLocate or DBPROP_IRowsetScroll to VARIANT_TRUE to create a rowset supporting rowset positioning by bookmark.

SQLOLEDB uses a SQL Server cursor to support a rowset containing bookmarks. For more information, see Rowsets and SQL Server Cursors.

Note Setting these properties in conflict with other SQLOLEDB cursor-defining properties results in an error. For example, setting the DBPROP_BOOKMARKS to VARIANT_TRUE when DBPROP_OTHERINSERT is also VARIANT_TRUE generates an error when the consumer attempts to open a rowset.
DBPROP_BOOKMARKSKIPPED R/W: Read-only
Default: VARIANT_FALSE
Description: SQLOLEDB returns DB_E_
BADBOOKMARK if the consumer indicates an invalid bookmark when positioning or searching a bookmarked rowset.
DBPROP_BOOKMARKTYPE R/W: Read-only
Default: DBPROPVAL_BMK_NUMERIC
Description: SQLOLEDB implements numeric bookmarks only. A SQLOLEDB bookmark is a 32-bit unsigned integer, type DBTYPE_UI4.
DBPROP_CACHEDEFERRED | The DBPROP_CACHEDEFERRED rowset property is not implemented by SQLOLEDB. Attempting to read or write the property value generates an error.
DBPROP_
CANFETCHBACKWARDS
DBPROP_
CANSCROLLBACKWARDS 		R/W: Read/write
Default: VARIANT_FALSE
Description: SQLOLEDB supports backward fetching and scrolling in nonsequential rowsets. SQLOLEDB creates a cursor-supported rowset when either DBPROP_
CANFETCHBACKWARDS or DBPROP_
CANSCROLLBACKWARDS is VARIANT_
TRUE. For more information, see Rowsets and SQL Server Cursors.
DBPROP_CANHOLDROWS R/W: Read/write
Default: VARIANT_FALSE
Description: By default, SQLOLEDB returns DB_E_ROWSNOTRELEASED if the consumer attempts to obtain more rows for a rowset while pending changes exist on those currently in the rowset. This behavior can be altered.

Setting both DBPROP_CANHOLDROWS and DBPROP_IRowsetChange to VARIANT_
TRUE implies a bookmarked rowset. If both properties are VARIANT_TRUE, the IRowsetLocate interface is available on the rowset and DBPROP_BOOKMARKS and DBPROP_LITERALBOOKMARKS are both VARIANT_TRUE.

SQLOLEDB rowsets containing bookmarks are supported by SQL Server cursors.
DBPROP_
CHANGEINSERTEDROWS 		R/W: Read-only
Default: VARIANT_TRUE
Description: SQLOLEDB allows the consumer to delete or change the data in newly inserted rows.
DBPROP_COLUMNRESTRICT R/W: Read-only
Default: VARIANT_FALSE
Description: SQLOLEDB sets the property to VARIANT_TRUE when a column in a rowset cannot be changed by the consumer. Other columns in the rowset may be updatable and the rows themselves may be deleted.

When the property is VARIANT_TRUE, the consumer examines the dwFlags member of the DBCOLUMNINFO structure to determine whether the value of an individual column can be written or not. For modifiable columns, dwFlags exhibits DBCOLUMNFLAGS_
WRITE.
DBPROP_COMMANDTIMEOUT R/W: Read/write
Default: 0
Description: By default, SQLOLEDB does not time out on ICommand::Execute.
DBPROP_COMMITPRESERVE R/W: Read/write
Default: VARIANT_FALSE
Description: By default, SQLOLEDB invalidates rowsets after a commit. SQLOLEDB maintains a valid rowset if the property is VARIANT_TRUE.
DBPROP_DEFERRED | The DBPROP_DEFERRED rowset property is not implemented by SQLOLEDB. Attempting to read or write the property value generates an error.
DBPROP_
DELAYSTORAGEOBJECTS 		R/W: Read-only
Default: VARIANT_FALSE
Description: SQLOLEDB supports immediate update mode on storage objects.

Changes made to data in a sequential stream object are immediately submitted to SQL Server. Modifications are committed based on the rowset transaction mode.
DBPROP_IAccessor
DBPROP_IColumnsInfo
DBPROP_IConvertType
DBPROP_IRowset
DBPROP_IRowsetInfo 		R/W: Read-only
Default: VARIANT_TRUE
Description: SQLOLEDB supports these interfaces on all rowsets.
DBPROP_IColumnsRowset R/W: Read/write
Default: VARIANT_TRUE
Description: SQLOLEDB supports the IColumnsRowset interface.
DBPROP_
IConnectionPointContainer 		R/W: Read/write
Default: VARIANT_FALSE
DBPROP_IRowsetChange
DBPROP_IRowsetUpdate 		R/W: Read/write
Default: VARIANT_FALSE
Description: SQLOLEDB supports the IRowsetChange and IRowsetUpdate interfaces.

A rowset created with DBPROP_
IRowsetChange equal to VARIANT_TRUE exhibits immediate update mode behaviors.

When DBPROP_IRowsetUpdate is VARIANT_TRUE, DBPROP_IRowsetChange is also VARIANT_TRUE. The rowset exhibits delayed update mode behavior.

SQLOLEDB uses a SQL Server cursor to support rowsets exposing either IRowsetChange or IRowsetUpdate. For more information, see Rowsets and SQL Server.
DBPROP_IRowsetIdentity R/W: Read/write
Default: VARIANT_FALSE
Description: See the OLE DB Programmer's Reference.
DBPROP_IRowsetLocate
DBPROP_IRowsetScroll 		R/W: Read/write
Default: VARIANT_FALSE
Description: SQLOLEDB can expose the IRowsetLocate and IRowsetScroll interfaces.

When DBPROP_IRowsetLocate is VARIANT_TRUE, DBPROP_
CANFETCHBACKWARDS and DBPROP_
CANSCROLLBACKWARDS are also VARIANT_TRUE.

When DBPROP_IRowsetScroll is VARIANT_
TRUE, DBPROP_IRowsetLocate is also VARIANT_TRUE, and both interfaces are available on the rowset.

Bookmarks are required for either interface. SQLOLEDB sets DBPROP_BOOKMARKS and DBPROP_LITERALBOOKMARKS to VARIANT_TRUE when the consumer requests either interface.

SQLOLEDB uses SQL Server cursors to support IRowsetLocate and IRowsetScroll. For more information, see Rowsets and SQL Server cursors.

Note Setting these properties in conflict with other SQLOLEDB cursor-defining properties results in an error. For example, setting the DBPROP_IRowsetScroll to VARIANT_TRUE when DBPROP_OTHERINSERT is also VARIANT_TRUE generates an error when the consumer attempts to open a rowset.
DBPROP_IRowsetResynch R/W: Read/write
Default: VARIANT_FALSE
Description: SQLOLEDB exposes the IRowsetResynch interface on demand. SQLOLEDB can expose the interface on any rowset.
DBPROP_ISupportErrorInfo R/W: Read/write
Default: VARIANT_TRUE
Description: SQLOLEDB exposes the ISupportErrorInfo interface on rowsets.
DBPROP_ILockBytes | The ILockBytes interface is not implemented by SQLOLEDB. Attempting to read or write the property generates an error.
DBPROP_ISequentialStream R/W: Read/write
Default: VARIANT_FALSE
Description: SQLOLEDB exposes the ISequentialStream interface to support long, variable-length data stored in SQL Server.
DBPROP_IStorage | The IStorage interface is not implemented by SQLOLEDB. Attempting to read or write the property generates an error.
DBPROP_IStream | The IStream interface is not implemented by SQLOLEDB. Attempting to read or write the property generates an error.
DBPROP_IMMOBILEROWS R/W: Read/write
Default: VARIANT_TRUE
Description: SQLOLEDB alters the value of the property based on the visibility of changes to the SQL Server data exposed by the rowset. The consumer cannot ever insert rows at a specified location in a SQLOLEDB rowset. Regardless of the property value, SQLOLEDB never reorders a rowset when a row is added to it.

VARIANT_TRUE
Rows inserted by the consumer appear at the end of the current block of rows held in the rowset. Rows inserted by other SQL Server users are not visible to the rowset. DBPROP_
OTHERINSERT is VARIANT_FALSE.

VARIANT_FALSE
Rows inserted by the consumer appear at the end of the current block of rows held in the rowset. However, scrolling the rowset could cause the row to appear in its SQL Server-determined position. This is especially true if the SQL Server table maintaining the rowset data is constrained by a clustered primary key. DBPROP_OTHERINSERT is VARIANT_TRUE.

This property only has value for rowsets supported by SQL Server cursors. For more information, see Rowsets and SQL Server Cursors.

Note Setting this property in conflict with other SQLOLEDB cursor-defining properties results in an error. For example, setting the property to VARIANT_TRUE when DBPROP_OTHERINSERT is also VARIANT_TRUE generates an error when the consumer attempts to open a rowset.
DBPROP_
LITERALIDENTITY 		R/W: Read-only
Default: VARIANT_TRUE
Description: See the OLE DB Programmer's Reference.
DBPROP_MAXOPENROWS R/W: Read-only
Default: 0
Description: SQLOLEDB does not limit the number of rows that can be active in rowsets.
DBPROP_
MAXPENDINGROWS 		R/W: Read-only
Default: 0
Description: SQLOLEDB does not limit the number of rowset rows with changes pending.
DBPROP_MAXROWS R/W: Read/Write
Default: 0
Description: By default, SQLOLEDB does not limit the number of rows in a rowset. When the consumer sets DBPROP_MAXROWS, SQLOLEDB uses the SET ROWCOUNT statement to limit the number of rows in the rowset.

SET ROWCOUNT can cause unintended consequences in SQL Server statement execution.
DBPROP_
MAYWRITECOLUMN 		The DBPROP_MAYWRITECOLUMN rowset property is not implemented by SQLOLEDB. Attempting to read or write the property value generates an error.
DBPROP_MEMORYUSAGE | The DBPROP_MEMORYUSAGE rowset property is not implemented by SQLOLEDB. Attempting to read or write the property value generates an error.
DBPROP_
NOTIFICATIONGRANULARITY 		The DBPROP_
NOTIFICATIONGRANULARITY rowset property is not implemented by SQLOLEDB. Attempting to read or write the property value generates an error.
DBPROP_
NOTIFICATIONPHASES 		R/W: Read-only
Default: DBPROPVAL_NP_OKTODO |
DBPROPVAL_NP_ABOUTTODO |
DBPROPVAL_NP_SYNCHAFTER |
DBPROPVAL_NP_FAILEDTODO |
DBPROPVAL_NP_DIDEVENT
Description: SQLOLEDB supports all notification phases.
DBPROP_NOTIFYCOLUMNSET
DBPROP_NOTIFYROWDELETE
DBPROP_
NOTIFYROWFIRSTCHANGE
DBPROP_
NOTIFYROWINSERT
DBPROP_
NOTIFYROWRESYNCH
DBPROP_
NOTIFYROWSETRELEASE
DBPROP_
NOTIFYROWSETFETCH-
POSITIONCHANGE
DBPROP_
NOTIFYROWUNDOCHANGE
DBPROP_
NOTIFYROWUNDODELETE
DBPROP_
NOTIFYROWUNDOINSERT
DBPROP_
NOTIFYROWUPDATE 		R/W: Read-only
Default: DBPROPVAL_NP_OKTODO |
DBPROPVAL_NP_ABOUTTODO
Description: SQLOLEDB notification phases are cancelable prior to an attempt to perform the rowset modification indicated. SQLOLEDB does not support phase cancellation after the attempt has completed.
DBPROP_
ORDEREDBOOKMARKS 		The DBPROP_ORDEREDBOOKMARKS rowset property is not implemented by SQLOLEDB. Attempting to read or write the property value generates an error.
DBPROP_OTHERINSERT
DBPROP_
OTHERUPDATEDELETE
DBPROP_OWNINSERT
DBPROP_
OWNUPDATEDELETE 		R/W: Read/write
Default: VARIANT_FALSE
Description: Setting change visibility properties causes SQLOLEDB to use SQL Server cursors to support the rowset. For more information, see Rowsets and SQL Server.
DBPROP_QUICKRESTART R/W: Read/write
Default: VARIANT_FALSE
Description: See the OLE DB Programmer's Reference.
DBPROP_
REENTRANTEVENTS 		R/W: Read-only
Default: VARIANT_TRUE
Description: SQLOLEDB rowsets are reentrant and can return DB_E_NOTREENTRANT if a consumer attempts to access a non-reentrant rowset method from a notification callback.
DBPROP_REMOVEDELETED R/W: Read/write
Default: VARIANT_FALSE
Description: SQLOLEDB alters the value of the property based on the visibility of changes to the SQL Server data exposed by the rowset.

VARIANT_TRUE
Rows deleted by the consumer or other SQL Server users are removed from the rowset when the rowset is refreshed. DBPROP_OTHERINSERT is VARIANT_TRUE.

VARIANT_FALSE
Rows deleted by the consumer or other SQL Server users are not removed from the rowset when the rowset is refreshed. The row status value for deleted SQL Server rows in the rowset is DBROWSTATUS_E_DELETED. DBPROP_
OTHERINSERT is VARIANT_FALSE.

This property only has value for rowsets supported by SQL Server cursors. For more information, see Rowsets and SQL Server.
DBPROP_
REPORTMULTIPLECHANGES 		R/W: Read-only
Default: VARIANT_FALSE
Description: See the OLE DB Programmer's Reference.
DBPROP_
RETURNPENDINGINSERTS 		R/W: Read-only
Default: VARIANT_FALSE
Description: When a method that fetches rows is called, SQLOLEDB does not return pending insert rows.
DBPROP_ROWRESTRICT R/W: Read-only
Default: VARIANT_FALSE
Description: SQLOLEDB rowsets do not support access rights based on the row. If IRowsetChange is exposed on a rowset, then SetData can be called by the consumer.
DBPROP_ROWSET_ASYNCH | The DBPROP_ROWSET_ASYNCH rowset property is not implemented by SQLOLEDB. Attempting to read or write the property value generates an error.
DBPROP_
ROWTHREADMODEL 		R/W: Read-only
Default: DBPROPVAL_RT_FREETHREAD
Description: SQLOLEDB supports access to its objects from multiple execution threads of a single consumer.
DBPROP_SERVERCURSOR R/W: Read/write
Default: VARIANT_FALSE
Description: When set, a SQL Server cursor is used to support the rowset. For more information, see Rowsets and SQL Server.
DBPROP_STRONGIDENTITY R/W: Read-only
Default: VARIANT_FALSE
Description: See the OLE DB Programmer's Reference.
DBPROP_
TRANSACTEDOBJECT 		R/W: Read-only
Default: VARIANT_FALSE
Description: SQLOLEDB supports only transacted objects. For more information, see Transactions.
DBPROP_UPDATABILITY R/W: Read/write
Default: 0
Description: SQLOLEDB supports all DBPROP_UPDATABILITY values. Setting DBPROP_UPDATABILITY does not create a modifiable rowset. To make a rowset modifiable, set DBPROP_IRowsetChange or DBPROP_IRowsetUpdate.

SQLOLEDB defines the provider-specific property set DBPROPSET_
SQLSERVERROWSET as follows.

Property ID | Description
SSPROP_CONCURRENCY | Column: No R/W: Read/write Type: VT_I4 Default: SSPROPVAL_CONCUR_READ_ONLY Description: Concurrency control method. The SSPROP_CONCURRENCY property has meaning only when a SQLOLEDB rowset is supported by a SQL Server cursor. SSPROPVAL_CONCUR_ROWVER SQLOLEDB uses row versioning to determine a concurrent access violation. The SQL Server table or tables represented in the rowset must contain a SQL Server row-versioning timestamp column. SSPROPVAL_CONCUR_VALUES SQLOLEDB uses the values of columns in the rowset row to determine a concurrent access violation. SSPROPVAL_CONCUR_LOCK SQL Server locks rows present in the SQLOLEDB rowset. SQL Server prevents row access by SQLOLEDB rowsets generated on any other session or by users connecting another way. SSPROPVAL_CONCUR_READ_ONLY The rowset is read-only. Any other connected user can concurrently access the SQL Server rows in the rowset.
SSPROP_HIDDENCOLUMNS | Column: No R/W: Read/write Type: VT_BOOL Default: VARIANT_FALSE Description: FOR BROWSE versioning columns. SSPROP_HIDDENCOLUMNS has meaning only if the rowset was created from command text specifying the Transact-SQL FOR BROWSE clause in a SELECT statement. VARIANT_TRUE The rowset includes the table primary key and/or timestamp columns that allow row identification. VARIANT_FALSE The rowset does not include row-identifying columns.
SSPROP_MAXBLOBLENGTH | Column: No R/W: Read/write Type: VT_I4 Default: 0 Description: SQLOLEDB executes a SET TEXTLENGTH statement to restrict the length of BLOB data returned in a SELECT statement.
SSPROP_NOBROWSETABLE | Column: No R/W: Read/write Type: VT_BOOL Default: VARIANT_FALSE Description: No temporary table created on FOR BROWSE. SSPROP_NOBROWSETABLE has meaning only if the rowset has been created from command text specifying the Transact-SQL FOR BROWSE clause in a SELECT statement. VARIANT_TRUE A temporary table is not created to support a Transact-SQL SELECT statement containing the FOR BROWSE clause. VARIANT_FALSE A temporary table is created to support a Transact-SQL SELECT statement containing the FOR BROWSE clause.
SSPROP_POSITIONONNEWROW | Column: No R/W: Read/write Type: VT_BOOL Default: VARIANT_FALSE
SSPROP_TEXTPTRLOGGING | Column: No R/W: Read/write Type: VT_BOOL Default: VARIANT_TRUE Description: Log text and image writes. SQL Server can perform nonlogged writes of long, variable, data type columns. VARIANT_TRUE Log SQL Server text and image column writes originating from the rowset. VARIANT_FALSE Do not log SQL Server text and image column writes originating from the rowset. Caution: After performing nonlogged operations against a SQL Server database, the database must be backed up to ensure proper recovery. The database cannot be recovered from transaction log backups and attempting to backup the transaction log results in an error.

See Also

Transactions


Rowsets and SQL Server Cursors

SQLOLEDB exposes SQL Server cursors through rowsets, allowing developers to use SQL Server concurrency control mechanisms. SQLOLEDB creates SQL Server cursors that support rowsets when the rowset properties indicate that the consumer:

  • Requires a server cursor.

  • Must be able to scroll or fetch backward in the rowset.

  • Will dynamically read additions, deletions and changes made by the consumer or by other consumers of the same SQL Server data.

  • Requests bookmarks for row identification.

Specific rowset properties required by the consumer direct SQLOLEDB use of SQL Server cursors. If the consumer sets no rowset properties, or sets all rowset properties to their default values, SQLOLEDB does not create a SQL Server cursor to support the rowset.

The following rowset properties direct SQLOLEDB use of SQL Server cursors. Some properties can be safely combined with others. For example, a rowset that exhibits the DBPROP_IRowsetScroll and DBPROP_IRowsetChange properties will be a bookmark rowset exhibiting immediate update behavior. Other properties are mutually exclusive. For example, a rowset exhibiting DBPROP_OTHERINSERT cannot contain bookmarks.

Property ID | Rowset behavior
DBPROP_SERVERCURSOR | VARIANT_TRUE Cannot update SQL Server data through the rowset. The rowset is sequential, supporting forward scrolling and fetching only. Relative row positioning is supported. Command text can contain an ORDER BY clause.
DBPROP_ CANSCROLLBACKWARDS or DBPROP_ CANFETCHBACKWARDS | VARIANT_TRUE Cannot update SQL Server data through the rowset. The rowset supports scrolling and fetching in either direction. Relative row positioning is supported. Command text can contain an ORDER BY clause.
DBPROP_BOOKMARKS or DBPROP_LITERALBOOKMARKS | VARIANT_TRUE Cannot update SQL Server data through the rowset. The rowset is sequential, supporting forward scrolling and fetching only. Relative row positioning is supported. Command text can contain an ORDER BY clause.
DBPROP_OWNUPDATEDELETE or DBPROP_OWNINSERT or DBPROP_OTHERUPDATEDELETE | VARIANT_TRUE Cannot update SQL Server data through the rowset. The rowset supports scrolling and fetching in either direction. Relative row positioning is supported. Command text can contain an ORDER BY clause.
DBPROP_OTHERINSERT | VARIANT_TRUE Cannot update SQL Server data through the rowset. The rowset supports scrolling and fetching in either direction. Relative row positioning is supported. Command text cannot contain an ORDER BY clause. DBPROP_OTHERINSERT cannot be VARIANT_TRUE if the rowset contains bookmarks. Attempting to create a rowset with this visibility property and bookmarks results in an error.
DBPROP_IRowsetLocate or DBPROP_IRowsetScroll | VARIANT_TRUE Cannot update SQL Server data through the rowset. The rowset supports scrolling and fetching in either direction. Bookmarks and absolute positioning through IRowsetLocate are supported in the rowset. Command text can contain an ORDER BY clause. DBPROP_IRowsetLocate and DBPROP_ IRowsetScroll require bookmarks in the rowset. Attempting to create a rowset with bookmarks and DBPROP_OTHERINSERT set to VARIANT_TRUE results in an error.
DBPROP_IRowsetChange or DBPROP_IRowsetUpdate | VARIANT_TRUE Can update SQL Server data through the rowset. The rowset is sequential, supporting forward scrolling and fetching only. Relative row positioning is supported. Command text cannot contain an ORDER BY clause.
DBPROP_IRowsetLocate or DBPROP_IRowsetScroll and DBPROP_IRowsetChange or DBPROP_IRowsetUpdate | VARIANT_TRUE Can update SQL Server data through the rowset. The rowset supports scrolling and fetching in either direction. Bookmarks and absolute positioning through IRowsetLocate are supported in the rowset. Command text can contain an ORDER BY clause.
DBPROP_IMMOBILEROWS | VARIANT_FALSE Cannot update SQL Server data through the rowset. The rowset supports forward scrolling only. Relative row positioning is supported. Command text cannot contain an ORDER BY clause. DBPROP_IMMOBILEROWS is only available in rowsets that can show SQL Server rows inserted by commands on other sessions or by other users. Attempting to open a rowset with the property set to VARIANT_FALSE on any rowset for which DBPROP_OTHERINSERT cannot be VARIANT_TRUE results in an error.
DBPROP_REMOVEDELETED | VARIANT_TRUE Cannot update SQL Server data through the rowset. The rowset supports forward scrolling only. Relative row positioning is supported. Command text can contain an ORDER BY clause unless constrained by another property.

A SQLOLEDB rowset supported by a SQL Server cursor can be easily created on a SQL Server base table or view by using IOpenRowset::OpenRowset. Specify the table or view by name, passing the required rowset property sets in the rgPropertySets parameter.

Command text that creates a rowset is restricted when the consumer requires that the rowset be supported by a SQL Server cursor. Specifically, the command text is restricted to either a single SELECT statement that returns a single rowset result, or a stored procedure that implements a single SELECT statement returning a single rowset result.

SQL Server Cursor Block Size

When a SQL Server cursor supports a SQLOLEDB rowset, the number of elements in the row handle array parameter of IRowset::GetNextRows or IRowsetLocate::GetRowsAt defines the cursor block size. The rows indicated by the handles in the array are the members of the cursor block.

For rowsets supporting bookmarks, the row handles retrieved by using IRowsetLocate::GetRowsByBookmark define the members of the cursor block.

Regardless of the method used to populate the rowset and form the SQL Server cursor block, the cursor block is active as long as a reference is held by the consumer on the row handles retrieved.


Bulk-Copy Rowsets

SQLOLEDB implements the provider-specific IRowsetFastLoad interface to expose support for Microsoft SQL Server bulk copy from a consumer to an SQL Server table. IRowsetFastLoad exposes the two member functions:

  • InsertRow, which bulk-copies a single row of data to an SQL Server table.

  • Commit, which marks the end of a batch of bulk-copy insertions and writes inserted data to the SQL Server table.


Enabling a Session for IRowsetFastLoad

The consumer notifies SQLOLEDB of its need for bulk copy by setting the SQLOLEDB provider-specific data source property SSPROP_ENABLEFASTLOAD to VARIANT_TRUE. With the property set on the data source, the consumer creates an SQLOLEDB session. The new session allows consumer access to IRowsetFastLoad.

Enabling a session for bulk copy constrains SQLOLEDB support for interfaces on the session. A bulk copy-enabled session exposes only the following interfaces:

  • IDBSchemaRowset

  • IGetDataSource

  • IOpenRowset

  • ISupportErrorInfo

To disable the creation of bulk copy-enabled rowsets and cause the SQLOLEDB session to revert to standard processing, reset SSPROP_ENABLEFASTLOAD to VARIANT_FALSE.


IRowsetFastLoad Rowsets

SQLOLEDB bulk-copy rowsets are write-only, but the rowset exposes interfaces that allow the consumer to determine the structure of an SQL Server table. The following interfaces are exposed on a bulk copy-enabled SQLOLEDB rowset:

  • IAccessor

  • IColumnsInfo

  • IColumnsRowset

  • IConvertType

  • IRowsetFastLoad

  • IRowsetInfo

  • ISupportErrorInfo

The provider-specific properties SSPROP_FASTLOADOPTIONS, SSPROP_FASTLOADKEEPNULLS, and SSPROP_FASTLOADKEEPIDENTITY control behaviors of an SQLOLEDB bulk-copy rowset. The properties are specified in the rgProperties member of an rgPropertySets IOpenRowset parameter member.

Property ID | Description
SSPROP_ FASTLOADKEEPIDENTITY | Column: No R/W: Read/write Type: VT_BOOL Default: VARIANT_FALSE Description: Maintains identity values supplied by the consumer. VARIANT_FALSE Values for an identity column in the SQL Server table are generated by SQL Server. Any value bound for the column is ignored by SQLOLEDB. VARIANT_TRUE The consumer binds an accessor providing a value for an SQL Server identity column. The identity property is not available on columns accepting NULL, so the consumer provides a unique value on each IrowsetFastLoad::Insert call.
SSPROP_FASTLOADKEEPNULLS | Column: No R/W: Read/write Type: VT_BOOL Default: VARIANT_FALSE Description: Maintains NULL for columns with a DEFAULT constraint. SSPROP_FASTLOADKEEPNULLS affects only SQL Server columns that accept NULL and have a DEFAULT constraint applied. VARIANT_FALSE SQL Server inserts the default value for the column when the SQLOLEDB consumer inserts a row containing NULL for the column. VARIANT_TRUE SQL Server inserts NULL for the column value when the SQLOLEDB consumer inserts a row containing NULL for the column.
SSPROP_FASTLOADOPTIONS | Column: No R/W: Read/write Type: VT_BSTR Default: none Description: Reserved for future use.


Updating Data in Rowsets

SQLOLEDB updates SQL Server data when a consumer updates a modifiable rowset containing that data. A modifiable rowset is created when the consumer requests support for either the IRowsetChange or IRowsetUpdate interface.

All SQLOLEDB modifiable rowsets use SQL Server cursors to support the rowset. The SQLOLEDB provider-specific rowset property SSPROP_CONCURRENCY alters SQL Server concurrency control behavior in cursors and determines the behavior of rowset row fetching and data integrity error generation in updatable rowsets.

SQLOLEDB supports row synchronization before or after an update.


Updating Data in SQL Server Cursors

When fetching and updating data through SQL Server cursors, an SQLOLEDB consumer application is bound by the same considerations and constraints that apply to any other client application.

Only rows in SQL Server cursors participate in concurrent data-access control. By default, SQLOLEDB uses optimistic concurrency control, SSPROPVAL_CONCUR_ROWVER, when the consumer requests a modifiable rowset. To alter the level of concurrent access control, the consumer sets the DBPROPSET_SQLSERVERROWSET SSPROP_CONCURRENCY value prior to opening the rowset.

Transaction isolation levels can cause significant lags in row positioning if client application design allows transactions to remain open for long periods of time. By default, SQLOLEDB uses the read-committed isolation level specified by DBPROPVAL_TI_READCOMMITTED. SQLOLEDB supports dirty read isolation when the rowset concurrency is read-only. Therefore, the consumer can request a higher level of isolation in a modifiable rowset but cannot request any lower level successfully.

Immediate and Delayed Update Modes

In immediate update mode, each call to IRowsetChange::SetData results in a roundtrip to the SQL Server. If the consumer makes multiple changes to a single row, it is more efficient to submit all changes with a single SetData call.

In delayed update mode, a roundtrip is made to the SQL Server for each row indicated in the cRows and rghRows parameters of IRowsetUpdate::Update.

In either mode, a roundtrip represents a distinct transaction when no transaction object is open for the rowset.

When using IRowsetUpdate::Update, SQLOLEDB attempts to process each indicated row. An error occurring due to invalid data, length, or status values for any row does not stop SQLOLEDB processing. All other rows participating in the update may be modified or none may. The consumer must check the returned prgRowStatus array to determine failure for any specific row when SQLOLEDB returns DB_S_ERRORSOCCURED.

A consumer should not assume that rows are processed in any specific order. If a consumer requires ordered processing of data modification over more than a single row, then the consumer should establish that order in application logic and open a transaction to enclose the process.


Resynchronizing Rows

SQLOLEDB supports IRowsetResynch on SQL Server cursor-supported rowsets only. IRowsetResynch is not available on demand. The consumer must request the interface prior to opening the rowset.


BLOBs and OLE Objects

SQLOLEDB exposes the ISequentialStream interface to support consumer access to SQL Server text and image data types as Binary Large Objects (BLOBs). The Read method on ISequentialStream allows the consumer to retrieve large amounts of data in manageable chunks.

SQLOLEDB can use a consumer-implemented IStorage interface when the consumer provides the interface pointer in an accessor bound for data modification.

SQLOLEDB Storage Object Limitations

  • SQLOLEDB can support only a single open storage object. Attempts to open more than one storage object (attempts to get a reference on more than one ISequentialStream interface pointer) return DBSTATUS_E_CANTCREATE.

  • SQLOLEDB storage objects are not blocking.

  • The length of data presented by a consumer-implemented storage object must be made known to SQLOLEDB when the row accessor that references the storage object is created. The consumer must bind a length indicator in the DBBINDING structure used for accessor creation.

  • SQLOLEDB supports ISequentialStream::Write for zero-length strings and NULL values only. Attempts to write more than zero bytes through ISequentialStream::Write fail.

  • If a row contains more than a single large data value, the consumer must either use a SQLOLEDB cursor-supported rowset to retrieve row data or process all large data values prior to retrieving other row values.


Getting Large Data

In general, consumers should isolate code that creates a SQLOLEDB storage object from other code that handles data not referenced through an ISequentialStream pointer.

When the consumer indicates a storage object is one member of a row of data values, then the consumer limits its use of storage objects because SQLOLEDB can support only a single open object. In this case, the consumer can fetch only a single row of data in GetNextRows.

SQLOLEDB does not retrieve large data from SQL Server until requested to do so by the consumer. The consumer should bind all short data in one accessor, then use one or more temporary accessors to retrieve large data values as required.

Example

This example retrieves a large data value from a single column.

HRESULT GetUnboundData
  (
  IRowset* pIRowset,
  HROW hRow,
  ULONG nCol,
  BYTE* pUnboundData
  )
  {
  UINT      cbRow = sizeof(IUnknown*) + sizeof(ULONG);
  BYTE*     pRow = new BYTE[cbRow];

  DBOBJECT    dbobject;

  IAccessor*    pIAccessor = NULL;
  HACCESSOR     haccessor;

  DBBINDING     dbbinding;
  ULONG     ulbindstatus;

  ULONG     dwStatus;
  ISequentialStream*  pISequentialStream;
  ULONG     cbRead;

  HRESULT     hr;

  // Set up the DBOBJECT structure.
  dbobject.dwFlags = STGM_READ;
  dbobject.iid = IID_ISequentialStream;

  // Create the DBBINDING, requesting a storage-object pointer from
  // SQLOLEDB.
  dbbinding.iOrdinal = nCol;
  dbbinding.obValue = 0;
  dbbinding.obStatus = sizeof(IUnknown*);
  dbbinding.obLength = 0;
  dbbinding.pTypeInfo = NULL;
  dbbinding.pObject = &dbobject;
  dbbinding.pBindExt = NULL;
  dbbinding.dwPart = DBPART_VALUE | DBPART_STATUS;
  dbbinding.dwMemOwner = DBMEMOWNER_CLIENTOWNED;
  dbbinding.eParamIO = DBPARAMIO_NOTPARAM;
  dbbinding.cbMaxLen = 0;
  dbbinding.dwFlags = 0;
  dbbinding.wType = DBTYPE_IUNKNOWN;
  dbbinding.bPrecision = 0;
  dbbinding.bScale = 0;

  if (FAILED(hr = pIRowset->
    QueryInterface(IID_IAccessor, (void**) &pIAccessor)))
    {
    // Process QueryInterface failure.
    return (hr);
    }

  // Create the accessor.
  if (FAILED(hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1,
    &dbbinding, 0, &haccessor, &ulbindstatus)))
    {
    // Process error from CreateAccessor.
    pIAccessor->Release();
    return (hr);
    }

  // Read and process BLOCK_SIZE bytes at a time.
  if (SUCCEEDED(hr = pIRowset->GetData(hRow, haccessor, pRow)))
    {
    dwStatus = *((ULONG*) (pRow + dbbinding.obStatus));

    if (dwStatus == DBSTATUS_S_ISNULL)
    {
    // Process NULL data
    }
    else if (dwStatus == DBSTATUS_S_OK)
    {
    pISequentialStream = *((ISequentialStream**)
      (pRow + dbbinding.obValue));

    do
      {
      if (SUCCEEDED(hr =
        pISequentialStream->Read(pUnboundData,
        BLOCK_SIZE, &cbRead)))
        {
        pUnboundData += cbRead;
        }
      }
    while (SUCCEEDED(hr) && cbRead >= BLOCK_SIZE);

    pISequentialStream->Release();
    }
    }
  else
    {
    // Process error from GetData.
    }

  pIAccessor->ReleaseAccessor(haccessor, NULL);
  pIAccessor->Release();
  delete [] pRow;

  return (hr);
  }


Tables and Indexes

SQLOLEDB exposes the IIndexDefintion and ITableDefinition interfaces allowing consumers to create, alter, and drop SQL Server tables and indexes.

Valid table and index definitions depend on the version of SQL Server. For more information, see CREATE TABLE and CREATE INDEX.

The ability to create or drop tables and indexes depends on the SQL Server access rights of the consumer-application user. Dropping a table can be further constrained by the presence of declarative referential integrity constraints or other factors. For more information, see DROP TABLE and DROP INDEX.


Creating SQL Server Tables

SQLOLEDB exposes ITableDefinition::CreateTable allowing consumers to create SQL Server tables. Consumers use CreateTable to create consumer-named permanent and temporary tables, and permanent or temporary tables with unique names generated by SQLOLEDB.

When consumers specify the table name in the pwszName member of the uName union in the pTableID parameter, SQLOLEDB creates a SQL Server table with that name. SQL Server table naming constraints apply, and the table name can indicate a permanent table or either a local or global temporary table. For more information, see CREATE TABLE. The ppTableID parameter can be NULL.

SQLOLEDB can generate the names of permanent or temporary tables. When the consumer sets the pTableID parameter to NULL and sets ppTableID to point to a valid DBID*, SQLOLEDB returns the generated name of the table in the pwszName member of the uName union of the DBID pointed to by the value of ppTableID. To create a temporary, SQLOLEDB-named table, the consumer includes the OLE DB table property DBPROP_TBL_TEMPTABLE in a table property set referenced in the rgPropertySets parameter. SQLOLEDB-named temporary tables are local. For more information, see CREATE TABLE.

CreateTable returns DB_E_BADID if the eKind member of the pTableID parameter does not indicate DBKIND_NAME.

DBCOLUMNDESC Usage

The consumer can indicate a column data type by using either the pwszTypeName member or the wType member. If the consumer specifies the data type in pwszTypeName, SQLOLEDB ignores the value of wType.

If using the pwszTypeName member, the consumer specifies the data type by using SQL Server data type names. Valid data type names are returned in the TYPE_NAME column of the PROVIDER_TYPES schema rowset.

SQLOLEDB recognizes a subset of OLE DB-enumerated DBTYPE values in the wType member. For more information, see Data Type Mapping in ITableDefinition.

CreateTable returns DB_E_BADTYPE if consumer sets either the pTypeInfo or pclsid member to specify the column data type.

The consumer specifies the column name in the pwszName member of the uName union of the DBCOLUMNDESC dbcid member. The column name is specified as a Unicode character string. The eKind member of dbcid must be DBKIND_NAME. CreateTable returns DB_E_COLUMNID if eKind is invalid, pwszName is NULL, or if the value of pwszName is not a valid SQL Server identifier.

All column properties are available on all columns defined for the table. CreateTable can return DB_S_ERRORSOCCURRED if property values are set in conflict. CreateTable returns an error when invalid column property settings cause SQL Server table-creation failure.

Column properties in a DBCOLUMNDESC are interpreted as follows.

Property ID Description
DBPROP_COL_
AUTOINCREMENT 		R/W: Read/write
Default: VARIANT_FALSE
Description: Sets the identity property on the column created. For SQL Server, the identity property is valid for a single column within a table. Setting the property to VARIANT_TRUE for more than a single column generates an error when SQLOLEDB attempts to create the table on the server.

The SQL Server identity property is only valid for the integral types and the numeric and decimal types when the scale is 0. Setting the property to VARIANT_TRUE on a column of any other data type generates an error when SQLOLEDB attempts to create the table on the server.

SQLOLEDB returns DB_S_
ERRORSOCCURRED when DBPROP_
COL_AUTOINCREMENT and DBPROP_
COL_NULLABLE are both VARIANT_
TRUE. The column is defined with the SQL Server identity property and the DBPROP_
COL_NULLABLE dwStatus member is set to DBPROPSTATUS_CONFLICTING.
DBPROP_COL_DEFAULT R/W: Read/write
Default: None
Description: Creates a SQL Server DEFAULT constraint for the column.

The vValue DBPROP member can be any of a number of types. The vValue.vt member should specify a type compatible with the data type of the column. For example, defining the BSTR N/A as the default value for a column defined as DBTYPE_WSTR is a compatible match. Defining the same default on a column defined as DBTYPE_R8 generates an error when SQLOLEDB attempts to create the table on the server.
DBPROP_COL_DESCRIPTION R/W: Read/write
Default: None
Description: The DBPROP_COL_
DESCRIPTION column property is not implemented by SQLOLEDB.

The dwStatus member of the DBPROP structure returns DBPROPSTATUS_
NOTSUPPORTED when the consumer attempts to write the property value.

Setting the property does not constitute a fatal error for SQLOLEDB. If all other parameter values are valid, the SQL Server table is created.
DBPROP_COL_FIXEDLENGTH R/W: Read/write
Default: VARIANT_FALSE
Description: SQLOLEDB uses DBPROP_
COL_FIXEDLENGTH to determine data type-mapping when the consumer defines a column’s data type by using the wType member of the DBCOLUMNDESC. For more information, see Data Type Mapping in ITableDefinition.
DBPROP_COL_NULLABLE R/W: Read/write
Default: None
Description: When creating the table, SQLOLEDB indicates whether the column should accept null values if the property is set. When the property is not set, the ability of the column to accept NULL as a value is determined by the SQL Server ANSI NULL default database option.

SQLOLEDB is an ANSI SQL-compliant provider. Connected sessions exhibit ANSI behaviors. If the consumer does not set DBPROP_COL_NULLABLE, columns accept null values.
DBPROP_COL_PRIMARYKEY R/W: Read/write
Default: VARIANT_FALSE
Description: When VARIANT_TRUE, SQLOLEDB creates the column with a primary key constraint.

When defined as a column property, only a single column can determine the constraint. Setting the property VARIANT_TRUE for more than a single column returns an error when SQLOLEDB attempts to create the SQL Server table.

Note The consumer can use IIndexDefinition::CreateIndex to create a primary key constraint on two or more columns.

SQLOLEDB returns DB_S_
ERRORSOCCURRED when DBPROP_
COL_PRIMARYKEY and DBPROP_COL_
UNIQUE are both VARIANT_TRUE. SQLOLEDB creates the column with the unique constraint and sets the dwStatus member of the DBPROP_COL_
PRIMARYKEY property to DBPROPSTATUS_CONFLICTING.

SQLOLEDB returns an error when DBPROP_
COL_PRIMARYKEY and DBPROP_COL_
NULLABLE are both VARIANT_TRUE.

SQLOLEDB returns an error from SQL Server when the consumer attempts to create a primary key constraint on a column of invalid SQL Server data type. Primary key constraints cannot be defined on columns created with the SQL Server data types bit, text, ntext, and image.
DBPROP_COL_UNIQUE R/W: Read/write
Default: VARIANT_FALSE
Description: Applies a SQL Server unique constraint to the column.

When defined as a column property, the constraint is applied on a single column only. The consumer can use IIndexDefinition::
CreateIndex to apply a unique constraint on the combined values of two or more columns.

SQLOLEDB returns DB_S_
ERRORSOCCURRED when DBPROP_COL_
UNIQUE and DBPROP_COL_
PRIMARYKEY are both VARIANT_TRUE. SQLOLEDB creates the column with the unique constraint and sets the dwStatus member of the DBPROP_COL_
PRIMARYKEY property to DBPROPSTATUS_CONFLICTING.

SQLOLEDB returns DB_S_
ERRORSOCCURRED when DBPROP_COL_UNIQUE and DBPROP_
COL_NULLABLE are both VARIANT_
TRUE. SQLOLEDB creates the column with the unique constraint and sets the dwStatus member of the DBPROP_COL_
NULLABLE property to DBPROPSTATUS_CONFLICTING.

SQLOLEDB returns an error from SQL Server when the consumer attempts to create a unique constraint on a column of invalid SQL Server data type. Unique constraints cannot be defined on columns created with the SQL Server data type bit.

When the consumer calls ITableDefinition::CreateTable, SQLOLEDB interprets table properties as follows.

Property ID | Description
DBPROP_TBL_TEMPTABLE | R/W: Read/write Default: VARIANT_FALSE Description: By default, SQLOLEDB creates tables named by the consumer. When VARIANT_TRUE, SQLOLEDB generates a temporary table name for the consumer. The consumer sets the pTableID parameter of CreateTable to NULL. The ppTableID parameter must contain a valid pointer.

If the consumer requests that a rowset be opened on a successfully created table, SQLOLEDB opens a cursor-supported rowset. Any rowset properties can be indicated in the property sets passed.

Example

This example creates a SQL Server table.

////////////////////////////////////////////////////////////////////////
// Create the OrderDetails table as follows:
//
// CREATE TABLE OrderDetails
// (
//  OrderID      int      NOT NULL
//  ProductID   int      NOT NULL
//  CONSTRAINT PK_OrderDetails
//   PRIMARY KEY CLUSTERED (OrderID, ProductID),
//  UnitPrice   money      NOT NULL,
//  Quantity   int      NOT NULL,
//  Discount   decimal(2,2)   NOT NULL
//    DEFAULT 0
// )
//
// The primary key constraint is created in an additional example.
HRESULT CreateTable
  (
  ITableDefinition* pITableDefinition
  )
  {
  DBID    dbidTable;
  const ULONG   nCols = 5;
  ULONG     nCol;
  ULONG     nProp;
  DBCOLUMNDESC  dbcoldesc[nCols];

  HRESULT   hr;

  // Set up column descriptions. First set default property values for
  //  the columns.
  for (nCol = 0; nCol < nCols; nCol++)
    {
    dbcoldesc[nCol].pwszTypeName = NULL;
    dbcoldesc[nCol].pTypeInfo = NULL;
    dbcoldesc[nCol].rgPropertySets = new DBPROPSET;
    dbcoldesc[nCol].pclsid = NULL;
    dbcoldesc[nCol].cPropertySets = 1;
    dbcoldesc[nCol].ulColumnSize = 0;
    dbcoldesc[nCol].dbcid.eKind = DBKIND_NAME;
    dbcoldesc[nCol].wType = DBTYPE_I4;
    dbcoldesc[nCol].bPrecision = 0;
    dbcoldesc[nCol].bScale = 0;

    dbcoldesc[nCol].rgPropertySets[0].rgProperties =
    new DBPROP[NCOLPROPS_MAX];
    dbcoldesc[nCol].rgPropertySets[0].cProperties = NCOLPROPS_MAX;
    dbcoldesc[nCol].rgPropertySets[0].guidPropertySet =
    DBPROPSET_COLUMN;

    for (nProp = 0; nProp < NCOLPROPS_MAX; nProp++)
    {
    dbcoldesc[nCol].rgPropertySets[0].rgProperties[nProp].
      dwOptions = DBPROPOPTIONS_REQUIRED;
    dbcoldesc[nCol].rgPropertySets[0].rgProperties[nProp].colid
       = DB_NULLID;

    VariantInit(
      &(dbcoldesc[nCol].rgPropertySets[0].rgProperties[nProp].
        vValue));

    dbcoldesc[nCol].rgPropertySets[0].rgProperties[nProp].
      vValue.vt = VT_BOOL;
    }
    }

  // Set the column-specific information.
  dbcoldesc[0].dbcid.uName.pwszName = L"OrderID";
  dbcoldesc[0].rgPropertySets[0].rgProperties[0].dwPropertyID =
    DBPROP_COL_NULLABLE;
  dbcoldesc[0].rgPropertySets[0].rgProperties[0].vValue.boolVal =
    VARIANT_FALSE;
  dbcoldesc[0].rgPropertySets[0].cProperties = 1;

  dbcoldesc[1].dbcid.uName.pwszName = L"ProductID";
  dbcoldesc[1].rgPropertySets[0].rgProperties[0].dwPropertyID =
    DBPROP_COL_NULLABLE;
  dbcoldesc[1].rgPropertySets[0].rgProperties[0].vValue.boolVal =
    VARIANT_FALSE;
  dbcoldesc[1].rgPropertySets[0].cProperties = 1;

  dbcoldesc[2].dbcid.uName.pwszName = L"UnitPrice";
  dbcoldesc[2].wType = DBTYPE_CY;
  dbcoldesc[2].rgPropertySets[0].rgProperties[0].dwPropertyID =
    DBPROP_COL_NULLABLE;
  dbcoldesc[2].rgPropertySets[0].rgProperties[0].vValue.boolVal =
    VARIANT_FALSE;
  dbcoldesc[2].rgPropertySets[0].cProperties = 1;

  dbcoldesc[3].dbcid.uName.pwszName = L"Quantity";
  dbcoldesc[3].rgPropertySets[0].rgProperties[0].dwPropertyID =
    DBPROP_COL_NULLABLE;
  dbcoldesc[3].rgPropertySets[0].rgProperties[0].vValue.boolVal =
    VARIANT_FALSE;
  dbcoldesc[3].rgPropertySets[0].cProperties = 1;

  dbcoldesc[4].dbcid.uName.pwszName = L"Discount";
  dbcoldesc[4].wType = DBTYPE_NUMERIC;
  dbcoldesc[4].bPrecision = 2;
  dbcoldesc[4].bScale = 2;
  dbcoldesc[4].rgPropertySets[0].rgProperties[0].dwPropertyID =
    DBPROP_COL_NULLABLE;
  dbcoldesc[4].rgPropertySets[0].rgProperties[0].vValue.boolVal =
    VARIANT_FALSE;
  dbcoldesc[4].rgPropertySets[0].rgProperties[1].dwPropertyID =
    DBPROP_COL_DEFAULT;
  dbcoldesc[4].rgPropertySets[0].rgProperties[1].vValue.vt = VT_BSTR;
  dbcoldesc[4].rgPropertySets[0].rgProperties[1].vValue.bstrVal =
    SysAllocString(L"0");
  dbcoldesc[4].rgPropertySets[0].cProperties = 2;

  // Set up the dbid for OrderDetails.
  dbidTable.eKind = DBKIND_NAME;
  dbidTable.uName.pwszName = L"OrderDetails";

  if (FAILED(hr = pITableDefinition->CreateTable(NULL, &dbidTable,
    nCols, dbcoldesc, NULL, 0, NULL, NULL, NULL)))
    {
    DumpError(pITableDefinition, IID_ITableDefinition);
    goto SAFE_EXIT;
    }

SAFE_EXIT:
  // Clean up dynamic allocation in the property sets.
  for (nCol = 0; nCol < nCols; nCol++)
    {
    for (nProp = 0; nProp < NCOLPROPS_MAX; nProp++)
    {
    if (dbcoldesc[nCol].rgPropertySets[0].rgProperties[nProp].
      vValue.vt == VT_BSTR)
      {
      SysFreeString(dbcoldesc[nCol].rgPropertySets[0].
        rgProperties[nProp].vValue.bstrVal);
      }
    }

    delete [] dbcoldesc[nCol].rgPropertySets[0].rgProperties;
    delete [] dbcoldesc[nCol].rgPropertySets;
    }

  return (hr);
  }

See Also

Data Type Mapping in ITableDefinition


Adding a Column to a SQL Server Table

SQLOLEDB exposes ITableDefinition::AddColumn allowing consumers to add a column to a SQL Server table.

When adding a column to a SQL Server table, the SQLOLEDB consumer is constrained as follows:

  • If DBPROP_COL_AUTOINCREMENT is VARIANT_TRUE, then DBPROP_COL_NULLABLE must be VARIANT_FALSE.

  • If the column is defined with the SQL Server timestamp data type, then DBPROP_COL_NULLABLE must be VARIANT_FALSE.

  • For any other column definition, DBPROP_COL_NULLABLE must be VARIANT_TRUE.

Consumers specify the table name as a Unicode character string in the pwszName member of the uName union in the pTableID parameter. The eKind member of pTableID must be DBKIND_NAME.

The new column name is specified as a Unicode character string in the pwszName member of the uName union in the dbcid member of the DBCOLUMNDESC parameter pColumnDesc. The dbcid eKind member must be DBKIND_NAME.


Removing a Column from a SQL Server Table

SQLOLEDB exposes ITableDefinition::DropColumn allowing consumers to remove a column from a SQL Server table.

Consumers specify the table name as a Unicode character string in the pwszName member of the uName union in the pTableID parameter. The eKind member of pTableID must be DBKIND_NAME.

The consumer indicates a column name in the pwszName member of the uName union in the pColumnID parameter. The column name is a Unicode character string. The eKind member of pColumnID must be DBKIND_NAME.

Note Removing a column is not supported for a consumer connected to a server running Microsoft SQL Server version 4.21a, 6.0 or 6.5. ITableDefinition::DropColumn returns E_FAIL when the consumer application attempts to remove a column.


Dropping a SQL Server Table

SQLOLEDB exposes ITableDefinition::DropTable allowing consumers to remove a SQL Server table from a database.

Consumers specify the table name as a Unicode character string in the pwszName member of the uName union in the pTableID parameter. The eKind member of pTableID must be DBKIND_NAME.


Creating SQL Server Indexes

SQLOLEDB exposes IIndexDefinition::CreateIndex allowing consumers to define new indexes on SQL Server tables.

SQLOLEDB creates table indexes as either indexes or constraints on a table. SQL Server gives constraint-creation privilege to the table owner, the database owner, and members of certain administrative roles. By default, only the table owner can create an index on a table. Therefore, CreateIndex success or failure depends not only on the application user’s access rights but also on the type of index created.

Consumers specify the table name as a Unicode character string in the pwszName member of the uName union in the pTableID parameter. The eKind member of pTableID must be DBKIND_NAME.

The pIndexID parameter can be NULL, and if it is, SQLOLEDB creates a unique name for the index. The consumer can capture the name of the index by specifying a valid pointer to a DBID in the ppIndexID parameter.

The consumer can specify the index name as a Unicode character string in the pwszName member of the uName union of the pIndexID parameter. The eKind member of pIndexID must be DBKIND_NAME.

The consumer specifies the column or columns participating in the index by name. For each DBINDEXCOLUMNDESC structure used in CreateIndex, the eKind member of the pColumnID must be DBKIND_NAME. The name of the column is specified as a Unicode character string in the pwszName member of the uName union in the pColumnID.

SQLOLEDB, and SQL Server, support ascending order on values in the index. SQLOLEDB returns E_INVALIDARG if the consumer specifies DBINDEX_COL_ORDER_DESC in any DBINDEXCOLUMNDESC structure.

CreateIndex interprets index properties as follows.

Property ID | Description
DBPROP_INDEX_AUTOUPDATE | R/W: Read/write Default: None Description: SQLOLEDB does not support DBPROP_INDEX_AUTOUPDATE. Attempts to set the property in CreateIndex cause a DB_S_ERRORSOCCURED return value. The dwStatus member of the property structure indicates DBPROPSTATUS_BADVALUE.
DBPROP_INDEX_CLUSTERED | R/W: Read/write Default: VARIANT_FALSE Description: Controls index clustering. VARIANT_TRUE SQLOLEDB attempts to create a clustered index on the SQL Server table. SQL Server supports at most one clustered index on any table. VARIANT_FALSE SQLOLEDB attempts to create a nonclustered index on the SQL Server table.
DBPROP_INDEX_FILLFACTOR | R/W: Read/write Default: 0 Description: Specifies the percent of an index page used for storage. For more information, see CREATE INDEX. The type of the variant is VT_I4. The value must be greater than or equal to 1 and less than or equal to 100.
DBPROP_INDEX_INITIALIZE | R/W: Read/write Default: None Description: SQLOLEDB does not support DBPROP_INDEX_INITIALIZE. Attempts to set the property in CreateIndex cause a DB_S_ERRORSOCCURED return value. The dwStatus member of the property structure indicates DBPROPSTATUS_BADVALUE.
DBPROP_INDEX_ NULLCOLLATION | R/W: Read/write Default: None Description: SQLOLEDB does not support DBPROP_INDEX_NULLCOLLATION. Attempts to set the property in CreateIndex cause a DB_S_ERRORSOCCURED return value. The dwStatus member of the property structure indicates DBPROPSTATUS_BADVALUE.
DBPROP_INDEX_NULLS | R/W: Read/write Default: None Description: SQLOLEDB does not support DBPROP_INDEX_NULLS. Attempts to set the property in CreateIndex cause a DB_S_ERRORSOCCURED return value. The dwStatus member of the property structure indicates DBPROPSTATUS_BADVALUE.
DBPROP_INDEX_PRIMARYKEY | R/W: Read/write Default: VARIANT_FALSE Description: Creates the index as a referential integrity, primary key constraint. VARIANT_TRUE The index is created to support the table’s primary key constraint. VARIANT_FALSE The index is not used as a primary key constraint for row values in the table.
DBPROP_INDEX_ SORTBOOKMARKS | R/W: Read/write Default: None Description: SQLOLEDB does not support DBPROP_INDEX_SORTBOOKMARKS. Attempts to set the property in CreateIndex cause a DB_S_ERRORSOCCURED return value. The dwStatus member of the property structure indicates DBPROPSTATUS_BADVALUE.
DBPROP_INDEX_TEMPINDEX | R/W: Read/write Default: None Description: SQLOLEDB does not support DBPROP_INDEX_TEMPINDEX. Attempts to set the property in CreateIndex cause a DB_S_ERRORSOCCURED return value. The dwStatus member of the property structure indicates DBPROPSTATUS_BADVALUE.
DBPROP_INDEX_TYPE | R/W: Read/write Default: None Description: SQLOLEDB does not support DBPROP_INDEX_TYPE. Attempts to set the property in CreateIndex cause a DB_S_ERRORSOCCURED return value. The dwStatus member of the property structure indicates DBPROPSTATUS_BADVALUE.
DBPROP_INDEX_UNIQUE | R/W: Read/write Default: VARIANT_FALSE Description: Creates the index as a unique constraint on the participating column or columns. VARIANT_TRUE The index is used to uniquely constrain row values in the table. VARIANT_FALSE The index does not uniquely constrain row values.

Example

This example creates a primary key index.

////////////////////////////////////////////////////////////////////////
// Create the referential integrity, PRIMARY KEY constraint on
// OrderDetails as follows:
//
// CREATE TABLE OrderDetails
// (
//  OrderID      int      NOT NULL
//  ProductID   int      NOT NULL
//    CONSTRAINT PK_OrderDetails
//    PRIMARY KEY CLUSTERED (OrderID, ProductID),
//  UnitPrice   money      NOT NULL,
//  Quantity   int      NOT NULL,
//  Discount   decimal(2,2)   NOT NULL
//    DEFAULT 0
// )
//
HRESULT CreatePrimaryKey
  (
  IIndexDefinition* pIIndexDefinition
  )
  {
  HRESULT     hr = S_OK;

  DBID      dbidTable;
  DBID      dbidIndex;
  const ULONG   nCols = 2;
  ULONG     nCol;
  const ULONG   nProps = 2;
  ULONG     nProp;

  DBINDEXCOLUMNDESC dbidxcoldesc[nCols];
  DBPROP      dbpropIndex[nProps];
  DBPROPSET     dbpropset;

  DBID*     pdbidIndexOut = NULL;

  // Set up identifiers for the table and index.
  dbidTable.eKind = DBKIND_NAME;
  dbidTable.uName.pwszName = L"OrderDetails";

  dbidIndex.eKind = DBKIND_NAME;
  dbidIndex.uName.pwszName = L"PK_OrderDetails";

  // Set up column identifiers.
  for (nCol = 0; nCol < nCols; nCol++)
    {
    dbidxcoldesc[nCol].pColumnID = new DBID;
    dbidxcoldesc[nCol].pColumnID->eKind = DBKIND_NAME;

    dbidxcoldesc[nCol].eIndexColOrder = DBINDEX_COL_ORDER_ASC;
    }
  dbidxcoldesc[0].pColumnID->uName.pwszName = L"OrderID";
  dbidxcoldesc[1].pColumnID->uName.pwszName = L"ProductID";

  // Set properties for the index. The index is clustered,
  // PRIMARY KEY.
  for (nProp = 0; nProp < nProps; nProp++)
    {
    dbpropIndex[nProp].dwOptions = DBPROPOPTIONS_REQUIRED;
    dbpropIndex[nProp].colid = DB_NULLID;

    VariantInit(&(dbpropIndex[nProp].vValue));

    dbpropIndex[nProp].vValue.vt = VT_BOOL;
    }
  dbpropIndex[0].dwPropertyID = DBPROP_INDEX_CLUSTERED;
  dbpropIndex[0].vValue.boolVal = VARIANT_TRUE;

  dbpropIndex[1].dwPropertyID = DBPROP_INDEX_PRIMARYKEY;
  dbpropIndex[1].vValue.boolVal = VARIANT_TRUE;

  dbpropset.rgProperties = dbpropIndex;
  dbpropset.cProperties = nProps;
  dbpropset.guidPropertySet = DBPROPSET_INDEX;

  hr = pIIndexDefinition->CreateIndex(&dbidTable, &dbidIndex, nCols,
    dbidxcoldesc, 1, &dbpropset, &pdbidIndexOut);

  // Clean up dynamically allocated DBIDs.
  for (nCol = 0; nCol < nCols; nCol++)
    {
    delete dbidxcoldesc[nCol].pColumnID;
    }

  return (hr);
  }


Dropping a SQL Server Index

SQLOLEDB exposes IIndexDefinition::DropIndex allowing consumers to remove an index from a SQL Server table.

SQLOLEDB exposes some SQL Server primary key and unique constraints as indexes. The table owner, database owner and some administrative role members can alter a SQL Server table, dropping a constraint. By default, only the table owner can drop an existing index. Therefore, DropIndex success or failure depends not only on the application user’s access rights but also on the type of index indicated.

Consumers specify the table name as a Unicode character string in the pwszName member of the uName union in the pTableID parameter. The eKind member of pTableID must be DBKIND_NAME.

Consumers specify the index name as a Unicode character string in the pwszName member of the uName union in the pIndexID parameter. The eKind member of pIndexID must be DBKIND_NAME.


Notifications

SQLOLEDB supports consumer notification on rowset modification. The consumer receives notification at every phase of rowset modification and on any attempted change.

To receive notification, the consumer queries the rowset for a connection-point interface, then connects a consumer-implemented IRowsetNotify interface to the rowset.

The consumer can cancel a rowset-modification attempt on receiving notification from SQLOLEDB. Any rowset-modification attempt can be canceled prior to the application of the modification by SQLOLEDB. That is, rowset modifications can be canceled when an IRowsetNotify member function indicates the event phase DBEVENTPHASE_OKTODO or DBEVENTPHASE_ABOUTTODO.


Data Types

SQLOLEDB represents SQL Server data by using certain OLE DB–defined data types in rowsets and as parameter values. Data type mapping by the consumer is also available when creating tables using ITableDefinition::CreateTable.


Data Type Mapping in Rowsets and Parameters

In rowsets and as parameter values, SQLOLEDB represents SQL Server data by using the following OLE DB defined data types.

SQL Server data type 		SQLOLEDB type reported in
IColumnsInfo::GetColumnInfo and
ICommandWithParameters::GetParameterInfo
binary | DBTYPE_BYTES
bit | DBTYPE_BOOL
char | DBTYPE_STR
datetime | DBTYPE_DBTIMESTAMP
decimal | DBTYPE_NUMERIC
float | DBTYPE_R8
image | DBTYPE_BYTES
int | DBTYPE_I4
money | DBTYPE_CY
numeric | DBTYPE_NUMERIC
real | DBTYPE_R4
smalldatetime | DBTYPE_DBTIMESTAMP
smallint | DBTYPE_I2
smallmoney | DBTYPE_CY
sysname | DBTYPE_WSTR
text | DBTYPE_STR
timestamp | DBTYPE_BYTES
tinyint | DBTYPE_UI1
uniqueidentifier | DBTYPE_GUID
varbinary | DBTYPE_BYTES
varchar | DBTYPE_STR

SQLOLEDB supports consumer-requested data conversions as shown:


Data Type Mapping in ITableDefinition

When creating tables by using ITableDefinition::CreateTable, the SQLOLEDB consumer can specify SQL Server data types in the pwszTypeName member of the DBCOLUMNDESC array passed. If the consumer specifies a column’s data type by name, then OLE DB type mapping, represented by the wType member of the DBCOLUMNDESC structure, is ignored.

When specifying new column data types with OLE DB types, that is, when using the DBCOLUMNDESC structure wType member, SQLOLEDB maps OLE DB types as follows.

OLE DB data type | SQL Server data type | Additional information
DBTYPE_BOOL | bit |
DBTYPE_BYTES | binary, varbinary or image | SQLOLEDB inspects the ulColumnSize member of the DBCOLUMNDESC structure. Based on the value, and the version of the SQL Server, SQLOLEDB maps the type to image. If the value of ulColumnSize is smaller than the maximum length of a binary data type column, then SQLOLEDB inspects the DBCOLUMNDESC rgPropertySets member. If the DBPROP_COL_ FIXEDLENGTH is VARIANT_ TRUE, SQLOLEDB maps the type to binary. If the value of the property is VARIANT_FALSE, SQLOLEDB maps the type to varbinary. In either case, the DBCOLUMNDESC ulColumnSize member determines the width of the SQL Server column created.
DBTYPE_CY | money |
DBTYPE_ DBTIMESTAMP | datetime |
DBTYPE_GUID | uniqueidentifier |
DBTYPE_I2 | smallint |
DBTYPE_I4 | int |
DBTYPE_NUMERIC | numeric | SQLOLEDB inspects the DBCOLUMDESC bPrecision and bScale members to determine precision and scale for the numeric column.
DBTYPE_R4 | real |
DBTYPE_R8 | float |
DBTYPE_STR | char, varchar or text | SQLOLEDB inspects the ulColumnSize member of the DBCOLUMNDESC structure. Based on the value and the version of the SQL Server, SQLOLEDB maps the type to text. If the value of ulColumnSize is smaller than the maximum length of a multibyte character data type column, then SQLOLEDB inspects the DBCOLUMNDESC rgPropertySets member. If the DBPROP_COL_FIXEDLENGTH is VARIANT_TRUE, SQLOLEDB maps the type to char. If the value of the property is VARIANT_ FALSE, SQLOLEDB maps the type to varchar. In either case, the DBCOLUMNDESC ulColumnSize member determines the width of the SQL Server column created.
DBTYPE_UI1 | TINYINT |

Note When creating a new table, SQLOLEDB maps only the OLE DB data type enumeration values specified in the table above. Attempting to create a table with a column of any other OLE DB type generates an error.


Transactions

SQLOLEDB implements local transaction support. The consumer can use distributed or coordinated transactions by using Microsoft Distributed Transaction Coordinator (MS DTC). For consumers requiring transaction control that spans multiple sessions, SQLOLEDB can join transactions initiated and maintained by the Microsoft Distributed Transaction Coordinator.

By default, SQLOLEDB uses an autocommit transaction mode, where each discreet action on a consumer session comprises a complete transaction against a SQL Server. SQLOLEDB autocommit mode is local. Autocommit transactions never span more than a single session.

SQLOLEDB exposes the ITransactionLocal interface, allowing the consumer to use explicitly and implicitly started transactions on a single SQL Server connection. SQLOLEDB supports nested local transactions.


Supporting Local Transactions

A session delimits transaction scope for a SQLOLEDB local transaction. When, at consumer direction, SQLOLEDB submits a request to a connected SQL Server, the request constitutes a unit of work for SQLOLEDB. Local transactions always wrap one or more units of work on a single SQLOLEDB session.

Using the default SQLOLEDB autocommit mode, a single unit of work is treated as the scope of a local transaction. No more than one unit ever participates in the local transaction. When a session is created, SQLOLEDB begins a transaction for the session. Upon successful completion of a work unit, the work is committed. On failure, any work begun is rolled back and the error is reported to the consumer. In either case, SQLOLEDB begins a new local transaction for the session, so that all work is conducted within a transaction.

The SQLOLEDB consumer can direct more precise control over local transaction scope by using ITransactionLocal. When a consumer session initiates a transaction, all session work units between the transaction start point and the eventual Commit or Abort are treated as an atomic unit. SQLOLEDB implicitly begins a transaction when directed to do so by the consumer. If the consumer does request retention, the session reverts to parent transaction-level behavior, most commonly, autocommit mode.

SQLOLEDB supports ITransactionLocal::StartTransaction parameters as follows.

Parameter | Description
isoLevel | In local transactions, SQLOLEDB supports ISOLATIONLEVEL_READCOMMITTED, ISOLATIONLEVEL_REPEATABLEREAD, ISOLATIONLEVEL_ISOLATED, and the synonyms ISOLATIONLEVEL_CURSORSTABILITY and ISOLATIONLEVEL_SERIALIZABLE.
isoFlags | SQLOLEDB returns an error for any value other than zero.
pOtherOptions | If not NULL, SQLOLEDB requests the options object from the interface. SQLOLEDB returns XACT_E_ NOTIMEOUT if the options object’s ulTimeout member is not zero. SQLOLEDB ignores the value of the szDescription member.
pulTransactionLevel | If not NULL, SQLOLEDB returns the nested level of the transaction.

For local transactions, SQLOLEDB implements ITransaction::Abort parameters as follows.

Parameter | Description
pboidReason | Ignored if set. Can safely be NULL.
FRetaining | When TRUE, a new transaction is implicitly begun for the session. The transaction must be committed or aborted by the consumer. When FALSE, SQLOLEDB reverts to autocommit mode for the session.
FAsync | Asynchronous abort is not supported by SQLOLEDB. SQLOLEDB returns XACT_E_NOTSUPPORTED if the value is not FALSE.

For local transactions, SQLOLEDB implements ITransaction::Commit parameters as follows.

Parameter | Description
fRetaining | When TRUE, a new transaction is implicitly begun for the session. The transaction must be committed or aborted by the consumer. When FALSE, SQLOLEDB reverts to autocommit mode for the session.
GrfTC | Asynchronous and phase one returns are not supported by SQLOLEDB. SQLOLEDB returns XACT_E_ NOTSUPPORTED for any value other than XACTTC_ SYNC.
GrfRM | Must be 0.

SQLOLEDB rowsets on the session are preserved on a local commit or abort based on the values of the rowset properties DBPROP_ABORTPRESERVE and DBPROP_COMMITPRESERVE. By default, these properties are both VARIANT_FALSE and all SQLOLEDB rowsets on the session are lost following an abort or commit.

SQLOLEDB does not implement the ITransactionObject interface. A consumer attempt to retrieve a reference on the interface returns E_NOINTERFACE.

Example

This example uses ITransactionLocal.

// Interfaces used in the example.
IDBCreateSession* pIDBCreateSession = NULL;
ITransaction*   pITransaction   = NULL;
IDBCreateCommand* pIDBCreateCommand = NULL;
IRowset*    pIRowset    = NULL;

HRESULT     hr;

// Get the command creation and local transaction interfaces for the
// session.
if (FAILED(hr = pIDBCreateSession->CreateSession(NULL,
   IID_IDBCreateCommand, (IUnknown**) &pIDBCreateCommand)))
  {
  // Process error from session creation. Release any references and
  // return.
  }

if (FAILED(hr = pIDBCreateCommand->QueryInterface(IID_ITransactionLocal,
  (void**) &pITransaction)))
  {
  // Process error. Release any references and return.
  }

// Start the local transaction.
if (FAILED(hr = ((ITransactionLocal*) pITransaction)->StartTransaction(
  ISOLATIONLEVEL_REPEATABLEREAD, 0, NULL, NULL)))
  {
  // Process error from StartTransaction. Release any references and
  // return.
  }

// Get data into a rowset, then update the data. Functions not
// illustrated in this example.
if (FAILED(hr = ExecuteCommand(pIDBCreateCommand, &pIRowset)))
  {
  // Release any references and return.
  }

// If rowset data update fails, then Abort the transaction, else
// commit. The example is not retaining.
if (FAILED(hr = UpdateDataInRowset(pIRowset, bDelayedUpdate)))
  {
  // Get error from update, then abort.
  pITransaction->Abort(NULL, FALSE, FALSE);
  }
else
  {
  if (FAILED(hr = pITransaction->Commit(FALSE, XACTTC_SYNC, 0)))
    {
    // Get error from failed commit.
    }
  }

if (FAILED(hr))
  {
  // Update of data or commit failed. Release any references and
  // return.
  }

// Release any references and continue.


Supporting Distributed Transactions

SQLOLEDB consumers can use ITransactionJoin::JoinTransaction to participate in a distributed transaction coordinated by MS DTC.

MS DTC exposes COM objects that allow clients to initiate and participate in coordinated transactions across multiple connections to variety of data stores. To initiate a transaction, the SQLOLEDB consumer uses the MS DTC ITransactionDispenser interface. The BeginTransaction member of ITransactionDispenser returns a reference on a distributed transaction object. This reference is passed to SQLOLEDB using JoinTransaction.

MS DTC supports asynchronous commit and abort on distributed transactions. For notification on asynchronous transaction status, the consumer implements the ITransactionOutcomeEvents interface and connects the interface to an MS DTC transaction object.

For distributed transactions, SQLOLEDB implements ITransactionJoin::JoinTransaction parameters as follows.

Parameter | Description
punkTransactionCoord | A pointer to an MS DTC transaction object.
IsoLevel | Ignored by SQLOLEDB. The isolation level for MS DTC-coordinated transactions is determined when the consumer acquires a transaction object from MS DTC.
IsoFlags | Must be 0. SQLOLEDB returns XACT_E_ NOISORETAIN if any other value is specified by the consumer.
POtherOptions | If not NULL, SQLOLEDB requests the options object from the interface. SQLOLEDB returns XACT_E_ NOTIMEOUT if the options object’s ulTimeout member is not zero. SQLOLEDB ignores the value of the szDescription member.

Example

This example coordinates transaction by using MS DTC.

// SQLOLEDB interfaces used in the example.
IDBCreateSession*   pIDBCreateSession = NULL;
ITransactionJoin*   pITransactionJoin = NULL;
IDBCreateCommand*   pIDBCreateCommand = NULL;
IRowset*      pIRowset    = NULL;

// Transaction dispenser and transaction from MS DTC.
ITransactionDispenser*  pITransactionDispenser = NULL;
ITransaction*     pITransaction   = NULL;

  HRESULT     hr;

// Get the command creation interface for the session.
if (FAILED(hr = pIDBCreateSession->CreateSession(NULL,
   IID_IDBCreateCommand, (IUnknown**) &pIDBCreateCommand)))
  {
  // Process error from session creation. Release any references and
  // return.
  }

// Get a transaction dispenser object from MS DTC and
// start a transaction.
if (FAILED(hr = DtcGetTransactionManager(NULL, NULL,
  IID_ITransactionDispenser, 0, 0, NULL,
  (void**) &pITransactionDispenser)))
  {
  // Process error message from MS DTC, release any references,
  // and then return.
  }
if (FAILED(hr = pITransactionDispenser->BeginTransaction(
  NULL, ISOLATIONLEVEL_READCOMMITTED, ISOFLAG_RETAIN_DONTCARE,
  NULL, &pITransaction)))
  {
  // Process error message from MS DTC, release any references,
  // and then return.
  }

// Join the transaction.
if (FAILED(pIDBCreateCommand->QueryInterface(IID_ITransactionJoin,
  (void**) &pITransactionJoin)))
  {
  // Process failure to get an interface, release any references, and
  // then return.
  }
if (FAILED(pITransactionJoin->JoinTransaction(
  (IUnknown*) pITransaction, 0, 0, NULL)))
  {
  // Process join failure, release any references, and then return.
  }

// Get data into a rowset, then update the data. Functions not
// illustrated in this example.
if (FAILED(hr = ExecuteCommand(pIDBCreateCommand, &pIRowset)))
  {
  // Release any references and return.
  }

// If rowset data update fails, then Abort the transaction, else
// commit. The example is not retaining.
if (FAILED(hr = UpdateDataInRowset(pIRowset, bDelayedUpdate)))
  {
  // Get error from update, then abort.
  pITransaction->Abort(NULL, FALSE, FALSE);
  }
else
  {
  if (FAILED(hr = pITransaction->Commit(FALSE, 0, 0)))
    {
    // Get error from failed commit.
    //
    // If a distributed commit fails, Application logic could
    // analyze failure and retry. We’ll abort. The consumer
    // must resolve this somehow.
    pITransaction->Abort(NULL, FALSE, FALSE);
    }
  }

if (FAILED(hr))
  {
  // Update of data or commit failed. Release any references and
  // return.
  }

// Release any references and continue.


Isolation Levels

Microsoft SQL Server clients can control transaction-isolation levels for a connection. To control transaction-isolation level, the SQLOLEDB consumer uses the:

  • DBPROPSET_SESSION property DBPROP_SESS_AUTOCOMMITISOLEVELS for SQLOLEDB default autocommit mode. The SQLOLEDB default for the level is DBPROPVAL_TI_READCOMMITTED.

  • isoLevel parameter of ITransactionLocal::StartTransaction for local manual-commit transactions.

  • isoLevel parameter of ITransactionDispenser::BeginTransaction for MS DTC-coordinated distributed transactions.

SQL Server allows read-only access at the dirty read isolation level. All other levels restrict concurrency by applying locks to SQL Server objects. As the client requires greater concurrency levels, SQL Server applies greater restrictions on concurrent access to data. To maintain the highest level of concurrent access to data, the SQLOLEDB consumer should intelligently control its requests for specific concurrency levels.


Errors

OLE/COM objects report errors through the HRESULT return code of object member functions. An OLE/COM HRESULT is a bit-packed structure. OLE provides macros that dereference structure members.

OLE/COM specifies the IErrorInfo interface. The interface exposes methods like GetDescription, allowing clients to extract error detail from OLE/COM servers. OLE DB extends IErrorInfo to support the return of multiple error information packets on a single-member function execution.

SQLOLEDB exposes the OLE DB record-enhanced IErrorInfo, the custom ISQLErrorInfo, and the provider-specific ISQLServerErrorInfo error object interfaces.


Return Codes

At the grossest level, a member function either succeeds or fails. At a somewhat more precise level, a function can succeed, but its success may not be identical to that intended by the application developer.

When a SQLOLEDB member function returns S_OK, the function succeeded.

When a SQLOLEDB member function does not return S_OK, the OLE/COM HRESULT-unpacking FAILED and IS_ERROR macros can determine the overall success or failure of a function.

If FAILED or IS_ERROR returns TRUE, the SQLOLEDB consumer is ensured that member function execution failed. When FAILED or IS_ERROR return FALSE, and the HRESULT does not equal S_OK, then the SQLOLEDB consumer is ensured that the function succeeded in some sense. The consumer can retrieve detailed information about this success-with-information return from SQLOLEDB error interfaces.

SQLOLEDB consumers will, quite commonly, encounter the DB_S_ERRORSOCCURRED success-with-information HRESULT return. Typically, member functions that return DB_S_ERRORSOCCURRED define one or more parameters that deliver status values to the consumer. No error information may be available to the consumer other than that returned in status-value parameters, so consumers should implement application logic that retrieves status values when they are available.

SQLOLEDB member functions do not return the success code S_FALSE. Any SQLOLEDB member function always returns S_OK to indicate success.


Information in OLE DB Error Interfaces

SQLOLEDB reports some error and status information in the OLE DB-defined error interfaces IErrorInfo, IErrorRecords, and ISQLErrorInfo.

SQLOLEDB supports IErrorInfo member functions as follows.

Member function | Description
GetDescription | A descriptive error message string.
GetGUID | The GUID of the interface that defined the error.
GetHelpContext | Not supported. Returns zero always.
GetHelpFile | Not supported. Returns NULL always.
GetSource | The string Sqloledb.dll.

SQLOLEDB supports consumer-available IErrorRecords member functions as follows.

Member function | Description
GetBasicErrorInfo | Fills an ERRORINFO structure with basic information about an error. An ERRORINFO structure contains members that identify the HRESULT return value for the error and the provider and interface on which the error applies.
GetCustomErrorObject | Returns a reference on an ISQLErrorInfo interface.
GetErrorInfo | Returns a reference on an IErrorInfo interface.
GetErrorParameters | SQLOLEDB does not return parameters to the consumer through GetErrorParameters.
GetRecordCount | The count of error records available.

SQLOLEDB supports ISQLErrorInfo::GetSQLInfo parameters as follows.

Parameter | Description
pbstrSQLState | Returns a SQLSTATE representing for the error. SQLSTATE values are defined in the ANSI SQL92, ODBC and ISO SQL, and API specifications. Neither Microsoft SQL Server nor SQLOLEDB define implementation-specific SQLSTATE values.
plNativeError | Returns the SQL Server error number from master.dbo.sysmessages when available. Native errors are available after a successful attempt to initialize a SQLOLEDB data source. Prior to the attempt, SQLOLEDB always returns zero.


SQL Server Error Detail

SQLOLEDB defines the provider-specific error interface ISQLServerErrorInfo. The interface returns more detail about a SQL Server error and is valuable when command execution or rowset operations fail.

ISQLServerErrorInfo is available on the SQLOLEDB implementation of ISQLErrorInfo. Unlike ISQLErrorInfo, the interface is not defined as a custom error object. The consumer queries ISQLErrorInfo to obtain an ISQLServerErrorInfo reference.

The ISQLServerErrorInfo interface exposes one member function GetErrorInfo. The function returns a pointer to an SSERRORINFO structure and a pointer to a string buffer. Both pointers reference memory the consumer must deallocate by using IMalloc::Free.

SSERRORINFO structure members are interpreted by the consumer as follows.

Member | Description
pwszMessage | SQL Server error message. Identical to the string returned in IErrorInfo::GetDescription.
pwszServer | The name of the session’s SQL Server.
pwszProcedure | If appropriate, the name of the procedure in which the error originated. An empty string otherwise.
lNative | The SQL Server native error number. Identical to the value returned in the plNativeError parameter of ISQLErrorInfo::GetSQLInfo.
bState | The state of a SQL Server error message.
bClass | The severity of a SQL Server error message. For more information, see RAISERROR.
wLineNumber | When applicable, the line number of a stored procedure on which the error occurred.


SQLOLEDB Example: Retrieving Error Information

This example obtains information from the various error interfaces exposed by SQLOLEDB.

////////////////////////////////////////////////////////////////////////
// DumpErrorInfo queries SQLOLEDB error interfaces, retrieving available
// status or error information.
void DumpErrorInfo
  (
  IUnknown* pObjectWithError,
  REFIID IID_InterfaceWithError
  )
  {

  // Interfaces used in the example.
  IErrorInfo*     pIErrorInfoAll    = NULL;
  IErrorInfo*     pIErrorInfoRecord   = NULL;
  IErrorRecords*    pIErrorRecords    = NULL;
  ISupportErrorInfo*  pISupportErrorInfo  = NULL;
  ISQLErrorInfo*    pISQLErrorInfo    = NULL;
  ISQLServerErrorInfo*  pISQLServerErrorInfo  = NULL;

  // Number of error records.
  ULONG       nRecs;
  ULONG       nRec;

  // Basic error information from GetBasicErrorInfo.
  ERRORINFO     errorinfo;

  // IErrorInfo values.
  BSTR        bstrDescription;
  BSTR        bstrSource;

  // ISQLErrorInfo parameters.
  BSTR        bstrSQLSTATE;
  LONG        lNativeError;

  // ISQLServerErrorInfo parameter pointers.
  SSERRORINFO*    pSSErrorInfo = NULL;
  OLECHAR*      pSSErrorStrings = NULL;

  // Hard-code an American English locale for the example.
  DWORD       MYLOCALEID = 0x0409;

  // Only try to obtain error information if the interface supports
  // it.
  if (FAILED(pObjectWithError->QueryInterface(IID_ISupportErrorInfo,
    (void**) &pISupportErrorInfo)) ||
    FAILED(pISupportErrorInfo->
    InterfaceSupportsErrorInfo(IID_InterfaceWithError)))
    {
    wprintf(L"ErrorInfo interface not supported on object");
    return;
    }

  // Don't bother to test the return of GetErrorInfo. It can succeed
  // AND return a NULL pointer in pIErrorInfoAll. Simply test the
  // pointer.
  GetErrorInfo(0, &pIErrorInfoAll);

  if (pIErrorInfoAll != NULL)
    {
    // Test to see if it's a valid OLE DB IErrorInfo exposing a list
    // of records.
    if (SUCCEEDED(pIErrorInfoAll->QueryInterface(IID_IErrorRecords,
    (void**) &pIErrorRecords)))
    {
    pIErrorRecords->GetRecordCount(&nRecs);

    // Within each record, retrieve information from each
    // of the defined interfaces.
    for (nRec = 0; nRec < nRecs; nRec++)
      {
      // From IErrorRecords, get the HRESULT and a reference
      // to the ISQLErrorInfo interface.
      pIErrorRecords->GetBasicErrorInfo(nRec, &errorinfo);
      pIErrorRecords->GetCustomErrorObject(nRec,
        IID_ISQLErrorInfo, (IUnknown**) &pISQLErrorInfo);

      // Display the HRESULT, then use the ISQLErrorInfo.
      wprintf(L"HRESULT:\t%#X\n", errorinfo.hrError);

      if (pISQLErrorInfo != NULL)
        {
        pISQLErrorInfo->GetSQLInfo(&bstrSQLSTATE,
        &lNativeError);

        // Display the SQLSTATE and native error values.
        wprintf(L"SQLSTATE:\t%s\nNative Error:\t%ld\n",
        bstrSQLSTATE, lNativeError);

        // SysFree BSTR references.
        SysFreeString(bstrSQLSTATE);

        // Get the ISQLServerErrorInfo interface from
        // ISQLErrorInfo before releasing the reference.
        pISQLErrorInfo->QueryInterface(
        IID_ISQLServerErrorInfo,
        (void**) &pISQLServerErrorInfo);

        pISQLErrorInfo->Release();
        }

      // Test to ensure the reference is valid, then
      // get error information from ISQLServerErrorInfo.
      if (pISQLServerErrorInfo != NULL)
        {
        pISQLServerErrorInfo->GetErrorInfo(&pSSErrorInfo,
        &pSSErrorStrings);

        // ISQLServerErrorInfo::GetErrorInfo succeeds
        // even when it has nothing to return. Test the
        // pointers before using.
        if (pSSErrorInfo)
        {
        // Display the state and severity from the
        // returned information. The error message comes
        // from IErrorInfo::GetDescription.
        wprintf(L"Error state:\t%d\nSeverity:\t%d\n",
            pSSErrorInfo->bState,
            pSSErrorInfo->bClass);

        // IMalloc::Free needed to release references
        // on returned values. For the example, assume
        // the g_pIMalloc pointer is valid.
        g_pIMalloc->Free(pSSErrorStrings);
        g_pIMalloc->Free(pSSErrorInfo);
        }

        pISQLServerErrorInfo->Release();
        }

      if (SUCCEEDED(pIErrorRecords->GetErrorInfo(nRec,
        MYLOCALEID, &pIErrorInfoRecord)))
        {
        // Get the source and description (error message)
        // from the record's IErrorInfo.
        pIErrorInfoRecord->GetSource(&bstrSource);
        pIErrorInfoRecord->GetDescription(&bstrDescription);

        if (bstrSource != NULL)
        {
        wprintf(L"Source:\t\t%s\n", bstrSource);
        SysFreeString(bstrSource);
        }
        if (bstrDescription != NULL)
        {
        wprintf(L"Error message:\t%s\n",
          bstrDescription);
        SysFreeString(bstrDescription);
        }

        pIErrorInfoRecord->Release();
        }
      }

    pIErrorRecords->Release();
    }
    else
    {
    // IErrorInfo is valid, get the source and
    // description to see what it is.
    pIErrorInfoAll->GetSource(&bstrSource);
    pIErrorInfoAll->GetDescription(&bstrDescription);

    if (bstrSource != NULL)
      {
      wprintf(L"Source:\t\t%s\n", bstrSource);
      SysFreeString(bstrSource);
      }
    if (bstrDescription != NULL)
      {
      wprintf(L"Error message:\t%s\n", bstrDescription);
      SysFreeString(bstrDescription);
      }
    }

    pIErrorInfoAll->Release();
    }
  else
    {
    wprintf(L"GetErrorInfo failed.");
    }

  pISupportErrorInfo->Release();

  return;
  }


SQL Server Message Results

The following Transact-SQL statements do not generate SQLOLEDB rowsets or a count of affected rows when executed:

  • PRINT

  • RAISERROR with a severity of 10 or lower

  • DBCC

  • SET SHOWPLAN

  • SET STATISTICS

These statements either return one or more informational messages, or cause SQL Server to return informational messages in place of rowset or count results. On successful execution, SQLOLEDB returns S_OK and the message or messages are available to the SQLOLEDB consumer.

SQLOLEDB returns S_OK and has one or more informational messages available following execution of many Transact-SQL statements or consumer execution of a SQLOLEDB member function.

The SQLOLEDB consumer allowing dynamic specification of query text should check error interfaces after every member function execution regardless the value of the return code, the presence or absence of a returned IRowset or IMultipleResults interface reference, or a count of affected rows.


Interfaces

IRowsetFastLoad exposes support for SQL Server bulk-copy processing by employing the Commit and InsertRow methods. This allows SQLOLEDB consumers to rapidly add data to an existing SQL Server table.

ISQLServerErrorInfo is defined as the error interface, which returns severity and state details from an SQL Server error.


IRowsetFastLoad

IRowsetFastLoad exposes support for SQL Server bulk-copy processing. SQLOLEDB consumers use the interface to rapidly add data to an existing SQL Server table. For more information, see Bulk-Copy Rowsets.

Method | Description
Commit | Marks the end of a batch of inserted rows and writes the rows to the SQL Server table.
InsertRow | Adds a row to the bulk-copy rowset.

See Also

Bulk-Copy Rowsets


IRowsetFastLoad::Commit

Marks the end of a batch of inserted rows and writes the rows to the SQL Server table.

HRESULT Commit(
BOOLEAN bDone);

Arguments

bDone [in]

If FALSE, the rowset maintains validity and can be used by the consumer for additional row insertion. If TRUE, the rowset loses validity and no further insertion can be done by the consumer.

Return Code

S_OK

The method succeeded and all inserted data has been written to the SQL Server table.

E_FAIL

An error occurred.

E_UNEXPECTED

The method was called on a bulk-copy rowset previously invalidated by IRowsetFastLoad::Commit.

Comments

A SQLOLEDB bulk-copy rowset behaves as a delayed-update mode rowset. As the user inserts row data through the rowset, inserted rows are treated in the same fashion as pending inserts on a rowset supporting IRowsetUpdate.

The consumer must call Commit on the bulk-copy rowset to write inserted rows to the SQL Server table in the same way as the IRowsetUpdate::Update member function is used to submit pending rows to a SQL Server.

If the consumer releases its reference on the bulk-copy rowset without calling Commit, all inserted rows not previously written are lost.

The consumer can batch inserted rows by calling Commit with bDone TRUE. When bDone is FALSE, the rowset becomes invalid. An invalid bulk-copy rowset supports only ISupportErrorInfo and IRowset::Release.


IRowsetFastLoad::InsertRow

Adds a row to the bulk-copy rowset.

HRESULT InsertRow(
HACCESSOR hAccessor,
void* pData);

Arguments

hAccessor [in]

Is the handle of the accessor defining the row data for bulk copy. The accessor referenced is a row accessor, binding consumer-owned memory containing data values.

pData [in]

Is a pointer to the consumer-owned memory containing data values.

Return Code

S_OK

The method succeeded. Any bound status values for all columns have value DBSTATUS_S_OK or DBSTATUS_S_NULL.

E_FAIL

An error occurred. Error information is available from the rowset’s error interfaces.

E_INVALIDARG

hAccessor was a NULL handle or pData was a NULL pointer.

E_OUTOFMEMORY

SQLOLEDB was unable to allocate sufficient memory to complete the request.

E_UNEXPECTED

The method was called on a bulk-copy rowset previously invalidated by IRowsetFastLoad::Commit.

DB_E_BADACCESSORHANDLE

The hAccessor provided by the consumer was invalid.

DB_E_BADACCESSORTYPE

The specified accessor was not a row accessor or did not specify consumer-owned memory.

Comments

An error converting consumer data to the SQL Server data type for a column causes an E_FAIL return from SQLOLEDB. Data can be transmitted to SQL Server on any InsertRow or only on Commit. Therefore, the consumer application can call InsertRow many times with erroneous data before it receives notice that an data type conversion error exists. Because Commit ensures that all data is correctly specified by the consumer, the consumer can use Commit intelligently to validate data as necessary.

SQLOLEDB bulk-copy rowsets are write-only. SQLOLEDB exposes no methods allowing consumer query of the rowset. To abort processing, the consumer can release its reference on IRowsetFastLoad without calling commit. There are no facilities for accessing a consumer-inserted row in the rowset and changing its values or removing it individually from the rowset.


ISQLServerErrorInfo

SQLOLEDB defines the ISQLServerErrorInfo error interface. The interface returns details from a SQL Server error including its severity and state.

Method | Description
GetErrorInfo | Returns a pointer to a SQLOLEDB SSERRORINFO structure containing SQL Server error detail.

See Also

SQL Server Error Detail


ISQLServerErrorInfo::GetErrorInfo

Returns a pointer to a SQLOLEDB SSERRORINFO structure containing SQL Server error detail.

HRESULT GetErrorInfo(
SSERRORINFO**ppSSErrorInfo,
OLECHAR**ppErrorStrings);

Arguments

ppErrorInfo [out]
Is a pointer to a pointer to an SSERRORINFO structure.

ppErrorStrings [out]
Is a pointer to a Unicode character-string pointer.

Return Code

S_OK
The method succeeded.

E_INVALIDARG
Either ppSSErrorInfo or ppErrorStrings was NULL.

E_OUTOFMEMORY
SQLOLEDB was unable to allocate sufficient memory to complete the request.

Comments

SQLOLEDB allocates memory for the SSERRORINFO and OLECHAR strings returned through the pointers passed by the consumer. The consumer must deallocate this memory by using IMalloc::Free when it no longer requires access to the error data.

The SSERRORINFO structure is defined as follows:

typedef struct tagSSErrorInfo
{
LPOLESTR pwszMessage;
LPOLESTR pwszServer;
LPOLESTR pwszProcedure;
LONG lNative;
BYTE bState;
BYTE bClass;
WORD wLineNumber;
}
SSERRORINFO;

Member | Description
PwszMessage | The error message from SQL Server. The message is returned through IErrorInfo::GetDescription.
PwszServer | The name of the SQL Server on which the error occurred.
PwszProcedure | The name of the stored procedure generating the error if the error occurred in a stored procedure. Otherwise, an empty string.
LNative | The SQL Server error number. The error number is identical to that returned in the plNativeError parameter of ISQLErrorInfo::GetSQLInfo.
BState | The state of a SQL Server error. For more information, see RAISERROR.
BClass | The severity of a SQL Server error. For more information, see RAISERROR.
WLineNumber | When applicable, the line of a SQL Server stored procedure that generated the error message.

Pointers in the structure reference addresses in the string returned in ppErrorStrings.


Schema Rowsets

SQLOLEDB exposes the database schema rowset LINKEDSERVERS enumerating organization data sources that can participate in SQL Server distributed queries.


LINKEDSERVERS Rowset

The LINKEDSERVERS enumerates organization data sources that can participate in SQL Server distributed queries.

The LINKEDSERVERS rowset contains the following columns.

Column name | Type indicator | Description
SVR_NAME | DBTYPE_WSTR | The name of a linked server.
SVR_PRODUCT | DBTYPE_WSTR | The manufacturer or other name identifying the type of data store represented by the name of the linked server.
SVR_ PROVIDERNAME | DBTYPE_WSTR | The friendly name of the OLE DB provider used to consume data from the server.
SVR_DATASOURCE | DBTYPE_WSTR | An OLE DB DBPROP_INIT_ DATASOURCE string used to acquire a data source from the provider.
SVR_ PROVIDERSTRING | DBTYPE_WSTR | An OLE DB DBPROP_INIT_ PROVIDERSTRING value used to acquire a data source from the provider.
SVR_LOCATION | DBTYPE_WSTR | An OLE DB DBPROP_INIT_ LOCATION string used to acquire a data source from the provider.


Schema Rowset Support in SQLOLEDB

OLE DB consumers use the interface IDBSchemaRowset to request catalog information, which is returned as rowsets. SQL Server client interfaces rely on stored procedures in order to retrieve schema rowset data. Because the data is extracted from a server's catalog, the catalog stored procedures evolve as the client software evolves.


Catalog Stored Procedures

To support reporting of schema data, SQL Server client interfaces rely on system stored procedures that extract data from a server’s catalog. As client software evolves, the catalog stored procedures also evolve.

When a SQLOLEDB consumer connects to an older version of SQL Server (4.21a, 6.0, or 6.5), SQLOLEDB returns an informational error message stating the catalog stored procedures are out of date.

SQLOLEDB is compatible with older versions of SQL Server. However, not all schema rowsets are supported on older versions of SQL Server unless the catalog stored procedures are upgraded to the current release level.

To upgrade the catalog stored procedures, use an appropriate client utility to run the Transact-SQL Instcat.sql script that ships with the most recent version of SQLOLEDB. Instcat.sql requires system administrator privilege.

Depending on the version of the server, Instcat.sql execution can generate many error messages. All generated errors can be safely ignored if the final line of execution output indicates success.


Overview of the OLE DB Provider for Microsoft Jet

This reference guide is meant to supplement the OLE DB Programmer’s Reference. with information that pertains to the OLE DB Provider for Microsoft Jet (the Jet provider). This guide describes the Jet-specific implementation of standard OLE DB interfaces, methods, and properties.


IAccessor

This section describes Jet-specific implementation of the IAccessor Interfaces. This includes IAccessor::AddRefAccessor, IAccessor::CreateAccessor, IAccessor::GetBindings, and IAccessor::ReleaseAccessor.

To create an accessor, a consumer calls CreateAccessor. The consumer may create and release accessors at any time while the rowset or command remains in existence. When one thread of a consumer shares an accessor with another thread, it calls AddRefAccessor to increment the reference count of that accessor.

When the consumer is done with a rowset, it calls ReleaseAccessor to release any accessors on the rowset, including accessors inherited from the command. When the consumer is done with a command, it calls ReleaseAccessor to release any accessors created on the command. In both cases, the consumer must call ReleaseAccessor once for each reference count on the accessor.

Method | Description
AddRefAccessor | Adds a reference count to an existing accessor.
CreateAccessor | Creates an accessor from a set of bindings.
GetBindings | Returns the bindings in an accessor.
ReleaseAccessor | Releases an accessor.

IAccessor::AddRefAccessor

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

IAccessor::CreateAccessor

CreateAccessor creates an accessor from a set of bindings. For information on how this interface is implemented, see the OLE DB Programmer’s References.

IAccessor::GetBindings

Returns the bindings in an accessor. For information on how this interface is implemented, see the OLE DB Programmer’s References.

IAccessor::ReleaseAccessor

Releases an accessor. For information on how this interface is implemented, see the OLE DB Programmer’s References.

Optimization

Do not try to optimize accessors in this provider. It does not have internal row cache. Therefore, it is not capable of optimization as described in the OLE DB Programmer’s Reference. All accessors are equally optimized as they are in process.


IColumnsInfo

This section describes Jet-specific implementation of the IColumnsInfo interfaces. This includes IColumnsInfo::GetColumnInfo and IColumnsInfo::MapColumnIDs.

IColumnsInfo is the simpler of two interfaces that can be used to expose information about columns of a rowset of a prepared command. It provides a limited set of information in an array.

GetColumnInfo returns the most commonly used metadata: column IDs, data types, updatability, and so on. GetColumnInfo returns the metadata in an array of structures, which can be created and accessed quickly. The metadata returned, however, is limited.

Consumers that require more complete metadata can obtain it by calling IColumnsRowset::GetColumnsRowset.

Note For commands that expose ICommandPrepare, the methods on this interface can be called only after the command is prepared or an instance of the rowset is created. If a command text is set but not prepared, any calls to methods on IColumnsInfo return DB_E_NOTPREPARED. For commands that do not expose ICommandPrepare, the methods on this interface can be called only after the command text has been set.

Method | Description
GetColumnInfo | Returns the column metadata needed by most consumers.
MapColumnIDs | Returns an array of ordinals of the columns in a rowset.

IColumnsInfo::GetColumnsInfo

Returns the column metadata needed by most consumers. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

IColumnsInfo::MapColumnIDs

Returns an array or ordinals of the columns in a rowset that are identified by the specified column IDs. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.


IColumnsRowset

This section describes Jet-specific implementation of the IColumnsRowset interfaces. This includes IColumnsRowset::GetAvailableColumns and IColumnsRowset::GetColumnsRowset.

This interface supplies complete information about columns in a rowset. The methods in it can be called from a rowset or a command.

Consumers use the methods in IColumnsRowset for detailed and flexible information about the columns of a rowset.

Note For commands that expose ICommandPrepare, the methods on this interface can be called only after the command is prepared. Any calls to methods on IColumnsInfo return DB_E_NOTPREPARED. For commands that do not expose ICommandPrepare, the methods on this interface can be called only after the command text has been set.

Method | Description
GetAvailableColumns | Returns a list of optional metadata columns that can be supplied by a rowset.
GetColumnsRowset | Returns a rowset containing metadata about each column in the current rowset.

IColumnsRowset::GetAvailableColumns

Returns a list of optional metadata columns that can be supplied in a column metadata rowset. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

IColumnsRowset::GetColumnsRowset

Returns a rowset containing metadata about each column in the current rowset. This rowset is known as the column metadata rowset and is read-only. For information on how this interface is implemented, see the OLE DB Programmer’s Reference. For a description of the supported DBPROPSET_JETOLEDB_ROWSET and DBPROP_ROWSET properties, see Appendix A of this document.


ICommand

This section describes Jet-specific implementation of the ICommand interfaces. This includes ICommand::Cancel, ICommand::Execute, and ICommand::GetDBSession.

ICommand contains methods to execute commands. A command can be executed many times and the parameter values can vary.

A command object contains a single text command, which is specified through ICommandText.

Method | Description
Cancel | Cancels the current command execution.
Execute | Executes the command.
GetDBSession | Returns an interface pointer to the session that created the command.

ICommand::Cancel

Cancels the current command execution. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

ICommand::Execute

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

Note If the user does not specify any properties with ICommandProperties::SetProperties, then the default rowset is a forward-only snapshot. To alter this default behavior, see the section on ICommandProperties. To see a comprehensive list of supported properties, see Appendix A.

ICommand::GetDBSession

Returns an interface pointer to the session that created the command. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.


ICommandPrepare

This section describes Jet-specific implementation of the ICommandPrepare interfaces. This includes ICommandPrepare::Prepare and ICommandPrepare::Unprepare.

This interface encapsulates command optimization, a separation of compile time and run time, as found in traditional relational database systems. The result of this optimization is a command execution plan.

Commands must be in a prepared state prior to calling the following methods:

  • IColumnsInfo::GetColumnsInfo

  • IColumnsInfo::MapColumnsIDs

  • IColumnsRowset::GetAvailableColumns

  • IColumnsRowset::GetColumnsRowset
Method | Description
Prepare | Validates and optimizes the current command.
Unprepare | Discards the current command execution plan.

ICommandPrepare::Prepare

Validates and optimizes the current command. This interface takes the parameter cExpectedRuns. As described in the OLE DB Programmer’s Reference, by using this parameter, the consumer can indicate how often the command execution plan (which is produced by Prepare) will be used; that is, how often the command is likely to be executed without renewed optimization. A value of zero indicates that the consumer is unable to provide an estimate, and leaves it to the optimizer to choose a default value.

For Jet, this parameter is ignored. A new execution plan is not generated based on this parameter.

To see a comprehensive list of supported properties, see Appendix A.

ICommandPrepare::Unprepare

Discards the current command execution plan. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.


ICommandProperties

This section describes Jet-specific implementation of the ICommandProperties interfaces. This includes ICommandProperties::GetProperties and ICommandProperties::SetProperties.

ICommandProperties specifies to the command the properties from the Rowset property group that must be supported by the rowsets returned by ICommand::Execute. A special, most commonly requested case of these properties is the interfaces the rowset must support. In addition to interfaces, the consumer can request properties that modify the behavior of the rowset or interfaces.

Method | Description
GetProperties | Returns the list of properties in the rowset property group that are currently requested.
SetProperties | Sets properties in the rowset property group.

ICommandProperties::GetProperties

Returns the list of properties in the rowset property group that are currently requested for the rowset. For information on how this interface is implemented, see the OLE DB Programmer’s Reference. For a complete description of the supported DBPROP_JETOLEDB_ROWSET and DBPROP_ROWSET properties, see Appendix A.

ICommandProperties::SetProperties

Sets properties in the rowset property group. This provider supports some properties in addition to the standard implementation specified in the OLE DB Programmer’s Reference. For a complete description of the supported DBPROP_JETOLEDB_ROWSET and DBPROP_ROWSET properties, see Appendix A.


ICommandText

This section describes Jet-specific implementation of the ICommandText interfaces. This includes ICommandText::GetCommandText and ICommandText::SetCommandText.

A command object can have only one text command. When the command text is specified through SetCommandText, it replaces the existing command text.

Method | Description
GetCommandText | Returns the text command set by the last call to SetCommandText.
SetCommandText | Sets the command text, replacing the existing command text.

ICommandText::GetCommandText

Returns the text command set by the last call to SetCommandText. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

ICommandText::SetCommandText

Sets the command text, replacing the existing command text. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

SetCommandText does not alter the value of any properties. That is, ICommandProperties::GetProperties returns the same value for a property regardless of whether it is called before or after SetCommandText and whether SetCommandText succeeded or failed.

Jet supports a maximum of 64,000 bytes in an SQL string.


ICommandWithParameters

This section describes Jet-specific implementation of the ICommandWithParameters interfaces. This includes ICommandWithParameters::GetParameterInfo, ICommandWithParamaters::MapParameterInfo, and ICommandWithParameters::SetParameterInfo.

This interface encapsulates parameters. Parameters are scalar values, or a vector of scalar values, typically expressed in predicates but possibly supported by many providers in any scalar expression.

For scalar parameters of prepared commands, there is a presumption that different parameter values do not require different plans. In other words, a single preparation and its resulting plan are satisfactory for all possible values of scalar parameters.

Parameter values are set when a command is executed. Methods are included here to offer a means for setting and obtaining a list of parameters and their types.

Method | Description
GetParameterInfo | Gets a list of the command’s parameters, their names and their types.
MapParameterNames | Returns an array of column ordinals when given named parameters.
SetParameterInfo | Specifies the native data type of each parameter.

ICommandWithParameters::GetParameterInfo

Gets a list of the command’s parameters, their names, and their types. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

ICommandWithParameters::MapParameterNames

Returns an array of column ordinals when given named parameters. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

ICommandWithParameters::SetParameterInfo

Specifies the native data type of each parameter. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.


IConvertType

This section describes Jet-specific implementation of the ICommandText interfaces. This includes IConvertType::CanConvert.

This interface contains a single method that gives information on the availability of type conversions on a command or on a rowset.

Method | Description
CanConvert | Gives information on the availability of type conversions on a command or on a rowset.

IConvertType::CanConvert

This interface gives information on the availability of type conversions on a command or on a rowset. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

For information on the supported type conversions, see the OLE DB Programmer’s Reference. Do not expect to be able to convert to or from structured storage.


IDBCreateCommand

This section describes Jet-specific implementation of the IDBCreateCommand Interfaces. This includes IDBCreateCommand::CreateCommand.

Consumers call IDBCreateCommand::CreateCommand on a session to obtain a new command.

Method | Description
CreateCommand | Creates a new command.

IDBCreateCommand::CreateCommand

Creates a new command. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.


IDBCreateSession

This section describes Jet-specific implementation of the IDBCreateSession interfaces. This includes IDBCreateSession::CreateSession.

Consumers call IDBCreateSession::CreateSession on a data source object to obtain a new session.

Method | Description
CreateSession | Creates a new session.

IDBCreateSession::CreateSession

Creates a new session from the data source object and returns the requested interface on the newly created session. For information on how this interface is implemented, see the OLE DB Programmer’s Reference. This interface is implemented per the OLE DB Spec. See the OLE DB Spec for more details. To define and set properties on the newly created session, use ISessionProperties::SetProperties.

Jet has a transactional scope on a Jet session level. You can open multiple databases in a Jet session in the same transactional scope. You can not do this with the Jet Provider, version 3.51. Transactional scope exists only within a database.


IDBDataSourceAdmin

This section describes Jet-specific implementation of the IDBDataSourceAdmin interfaces. This includes IDBDataSourceAdmin::CreateDataSource, IDBDataSourceAdmin::DestroyDataSource, IDBDataSourceAdmin::GetCreationProperties, and IDBDataSourceAdmin::ModifyDataSource.

IDBDataSourceAdmin is an interface for creating, destroying, and modifying data sources.

It is important to distinguish between the data source object and the data source. The data source object is the object used by the consumer code. The data source is the actual source of the data, such as a server database.

Method | Description
CreateDataSource | Creates a new data source and data source object, and initializes the data source object to the data source.
DestroyDataSource | Destroys the current data source and leaves the data source object in an uninitialized state.
GetCreationProperties | Returns information about the data source creation properties supported by the provider.
ModifyDataSource | Modifies the current data source.

IDBDataSourceAdmin::CreateDataSource

Creates a new data source and data source object, and initializes the data source object to the data source. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

No data source creation properties are supported. For a complete description of the supported DBPROPSET_DBINIT and DBPROPSET_SESSION properties, see Appendix A.

Note IDBDataSourceAdmin::CreateDataSource will create all new databases in an exclusive mode, regardless of what you set for DBPROP_INIT_MODE. If you want others to be able to open your newly created databases, then you need to close the database and open it again using Initialize/CreateSession with the desired DBPROP_INIT_MODE setting.

IDBDataSourceAdmin::DestroyDataSource

Destroys the current data source and leaves the data source object in an uninitialized state.

This interface is not supported in this provider.

IDBDataSourceAdmin::GetCreationProperties

Returns information about the data source creation properties supported by the data provider. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

For a complete description of the supported DBPROPSET_DBINIT and DBPROPSET_SESSION properties, see Appendix A.

IDBDataSourceAdmin::ModifyDataSource

Modifies the current data source.

This interface is not supported in this provider.


IDBInfo

IDBInfo returns information about the keywords and literals a provider supports.

Method | Description
GetKeywords | Returns a list of provider-specific keywords.
GetLiteralInfo | Returns information about literals used in text commands and in ITableDefinition and IIndexDefiniton.

IDBInfo::GetKeywords

Returns a list of provider-specific keywords. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

IDBInfo::GetLiteralInfo

Returns information about literals used in text commands and in ITableDefinition. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

Note OLE DB version 1.1 and later supports only a single preceding escape character for SQL text. In order to escape SQL strings in this provider, you must enclose the string with square brackets ([ ]).


IDBInitialize

Initializes a data source object or enumerator.

Method | Description
Initialize | Initializes a data source object or enumerator.
Uninitialize | Returns the data source object or enumerator to an uninitialized state.

IDBInitialize::Initialize

Initializes a data source object or enumerator. IDBInitialize::Initialize loads the Jet DLL into memory. If the Jet DLL cannot be loaded, Initialize may fail. Jet Security is not checked in this routine. Authentication is done in IDBCreateSession::CreateSession.

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

IDBInitialize::Uninitialize

Returns the data source object or enumerator to an uninitialized state. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.


IDBProperties

IDBProperties is used to set and get the values of properties on the data source object or enumerator and to get information about all properties supported by the provider.

Before the data source object or enumerator is initialized, the consumer can work only with properties in the initialization property group. After the data source object or enumerator is initialized, the consumer can work with properties in the initialization, data source, and data source information property groups, although it cannot set the value of properties in the initialization property group.

Only properties in the initialization property group are guaranteed to survive uninitialization. That is, if the consumer uninitializes and reinitializes a data source object or enumerator, it might need to reset the values of properties in groups other than initialization.

IDBProperties is a mandatory interface for data source objects and an optional interface for enumerators. However, if an enumerator exposes IDBInitialize, it must expose IDBProperties.

Method | Description
GetProperties | Returns the values of properties in the data source, data source information, and initialization property groups that are currently set on the data source object or the values of properties in the initialization property group that are currently set on the enumerator.
GetPropertyInfo | Returns information about all properties supported by the provider.
SetProperties | Sets properties in the data source and initialization property groups, for data source objects, or the initialization property group, for enumerators.

IDBProperties::GetProperties

Returns the values of properties in the data source, data source information, and initialization property groups that are currently set on the data source object, or the values of properties in the initialization property group that are currently set. For information on how this interface is implemented, see the OLE DB Programmer’s Reference. For a description of data source, data source information, and initialization properties for this interface, see Appendix A in this document.

IDBProperties::GetPropertyInfo

GetPropertyInfo returns information about all properties supported by the provider. For information on how this interface is implemented, see the OLE DB Programmer’s Reference. For a description of data source, data source information, and initialization properties for this interface, see Appendix A in this document.

IDBProperties::SetProperties

Sets properties in the data source and initialization property groups, for data source objects, or the initialization property group, for enumerators. For information on how this interface is implemented, see the OLE DB Programmer’s Reference. For a description of data source and initialization properties for this interface, see Appendix A in this document.


IDBSchemaRowset

Consumers can get information about a data source without knowing its structure by using the IDBSchemaRowset methods.

This schema defines a minimum content of the system schema. It is assumed that consumers will precompile and store specialized queries, such as those for command planning and execution-time plan validation, and that storage engines may have specialized optimizations associated with those plans. Consumers can also look at the schema tables in the schema, and thus discover other schema tables and attributes beyond the minimum set.

For information about the schema rowsets, see Appendix B, “Schema Rowsets,” in this document. This provider returns all of the columns in the rowsets returned. This information may be NULL. Provider-specific columns described in Appendix B are returned after the last column defined by OLE DB.

Schema rowsets are identified by GUIDs. The following table lists these GUIDs and the columns for which restrictions can be specified on the schema rowset. The number of restriction columns for each schema rowset are defined as constants prefixed with CRESTRICTIONS_ in the header files. Restriction values are treated as literals rather than as search patterns. For example, the restriction value “A_C” matches “A_C” but not “ABC.”

GUID | Number of restrictions | Restriction columns
DBSCHEMA_ASSERTIONS | 3 | CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME
DBSCHEMA_CATALOG | 1 | CATALOG_NAME
DBSCHEMA_CHARACTER_SETS | 3 | CHARACTER_SET_CATALOG CHARACTER_SET_SCHEMA CHARACTER_SET_NAME
DBSCHEMA_CHECK_CONSTRAINTS | 3 | CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME
DBSCHEMA_COLLATIONS | 3 | COLLATION_CATALOG COLLATION_SCHEMA COLLATION_NAME
DBSCHEMA_COLUMN_DOMAIN_USAGE | 4 | DOMAIN_CATALOG DOMAIN_SCHEMA DOMAIN_NAME COLUMN_NAME
DBSCHEMA_COLUMN_PRIVILEGES | 6 | TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME GRANTOR GRANTEE
DBSCHEMA_COLUMNS | 4 | TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME
DBSCHEMA_CONSTRAINT_COLUMN_USAGE | 4 | TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME
DBSCHEMA_CONSTRAINT_TABLE_USAGE | 3 | TABLE_CATALOG TABLE_SCHEMA TABLE_NAME
DBSCHEMA_FOREIGN_KEYS | 6 | PK_TABLE_CATALOG PK_TABLE_SCHEMA PK_TABLE_NAME FK_TABLE_CATALOG FK_TABLE_SCHEMA FK_TABLE_NAME
DBSCHEMA_INDEXES | 5 | TABLE_CATALOG TABLE_SCHEMA INDEX_NAME TYPE TABLE_NAME
DBSCHEMA_KEY_COLUMN_USAGE | 7 | CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME
DBSCHEMA_PRIMARY_KEYS | 3 | TABLE_CATALOG TABLE_SCHEMA TABLE_NAME
DBSCHEMA_PROCEDURE_COLUMNS | 4 | PROCEDURE_CATALOG PROCEDURE_SCHEMA PROCEDURE_NAME COLUMN_NAME
DBSCHEMA_PROCEDURE_PARAMETERS | 4 | PROCEDURE_CATALOG PROCEDURE_SCHEMA PROCEDURE_NAME PROCEDURE_TYPE
DBSCHEMA_PROVIDER_TYPES | 2 | DATA_TYPE BEST_MATCH
DBSCHEMA_REFERENTIAL_CONSTRAINTS | 3 | CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME
DBSCHEMA_SCHEMATA | 3 | CATALOG_NAME SCHEMA_NAME SCHEMA_OWNER
DBSCHEMA_SQL_LANGUAGES | 0 | NONE
DBSCHEMA_STATISTICS | 3 | TABLE_CATALOG TABLE_SCHEMA TABLE_NAME
DBSCHEMA_TABLE_CONSTRAINTS | 7 | CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME TABLE_CATALOG TABLE_SCHEMA TABLE_NAME CONSTRAINT_TYPE
DBSCHEMA_TABLE_PRIVILEGES | 5 | TABLE_CATALOG TABLE_SCHEMA TABLE_NAME GRATOR GRANTEE
DBSCHEMA_VIEW_COLUMN_USAGE | 3 | VIEW_CATALOG VIEW_SCHEMA VIEW_NAME
DBSCHEMA_VIEW_TABLE_USAGE | 3 | VIEW_CATALOG VIEW_SCHEMA VIEW_NAME
DBSCHEMA_VIEWS | 3 | TABLE_CATALOG TABLE_SCHEMA TABLE_NAME

For a list of supported schema rowsets, see Appendix B.

Method | Description
GetRowset | Returns a schema rowset.
GetSchemas | Returns a list of schema rowsets accessible by IDBSchemaRowset::GetRowset.

IDBSchemaRowset::GetRowset

Returns a schema rowset. For information on how this interface is implemented, see the OLE DB Programmer’s Reference. For information about the rowset property set, see Appendix A. For more information about the schema rowsets supported, see Appendix B.

IDBSchemaRowset::GetSchemas

Returns a list of schema rowsets accessible by IDBSchemaRowset::GetRowset. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.


IErrorInfo

This interface is not supported.


IErrorLookup

This interface is not supported.


IErrorRecords

This interface is not supported.


IGetDataSource

This is a mandatory interface on the session for obtaining an interface pointer to the data source object.

Method | Description
GetDataSource | Returns an interface pointer on the data source object that created this session.

IGetDataSource::GetDataSource

Returns an interface pointer on the data source object that created the session. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.


IIndexDefinition

IIndexDefinition exposes simple methods to create and drop indexes from the data source object.

Method | Description
CreateIndex | Adds a new index to a base table.
DropIndex | Drops an index from a base table.

IIndexDefinition::CreateIndex

Adds a new index to a base table. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

For a description of the index property set properties supported for this interface, see Appendix A.

IIndexDefinition::DropIndex

Drops an index from the base table. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.


IMultipleResults

IMultipleResults is used to retrieve multiple results (rowsets or row counts) created by a command.

To create a multiple results object, a consumer creates a command, sets the command text, and calls ICommand::Execute with riid set to IID_IMultipleResults. The command text generally specifies multiple results, although this is not required. To retrieve each result in succession, the consumer repeatedly calls IMultipleResults::GetResult

Method | Description
GetResult | Returns the next in a series of multiple results from the provider.

IMultipleResults::GetResult

Returns the next in a series of multiple results from the provider. This interface is not supported.


IOpenRowset

IOpenRowset is a required interface on the session.

The IOpenRowset model enables consumers to open and work directly with individual tables or indexes in a data source by using IOpenRowset::OpenRowset, which generates a rowset of all rows in the table or index.

Method | Description
OpenRowset | Opens and returns a rowset that includes all rows from a single base table or index.

IOpenRowset::OpenRowset

Open and returns a rowset that includes all rows from a single base table or index.

The OLE DB Provider for Jet allows you to open “views” by using this routine in addition to simple queries, where simple queries are nonparameterized, row-returning queries.

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


IRowset

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

IRowset requires IAccessor and IRowsetInfo.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Cursor and Cursor Promotion

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

Cursor types in order of precedence:

  1. Forward-only snapshot

  2. Snapshot

  3. Keyset

  4. Basetables — Dynaset

  5. Append-only dynasets

Regular Rowset

Becoming a Zombie

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

Data type coercion

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

By Reference Data Binding

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

Delayed Update Mode

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

BLOB Support

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

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

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

IRowset::AddRefRows

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

IRowset::GetData

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

IRowset::GetNextRows

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

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

IRowset::ReleaseRows

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

IRowset::RestartPosition

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

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


IRowsetChange

The methods in IRowsetChange are used to update the values of columns in existing rows, delete existing rows, and insert new rows.

IRowsetChange requires IAccessor and IRowset.

The consumer calls methods in IRowsetChange to modify rows as follows.

  • SetData sets column data in an existing row.

  • DeleteRows deletes existing rows.

  • InsertRow creates a new row and sets initial values.

SetData and InsertRow require the use of an accessor.

SetData and InsertRow can fail for a number of reasons. The most common of these is that new data values do not meet the schema or integrity constraints of the column. Furthermore, rowsets can have row-by-row and column-by-column access permissions that override the general permissions of the table or column.

If IRowsetUpdate is exposed on the rowset, then changes made through IRowsetChange are buffered in the rowset and not transmitted to the data source until IRowsetUpdate::Update is called. This is known as delayed update mode. If IRowsetUpdate is not exposed on the rowset, then changes made through IRowsetChange are immediately transmitted to the data source. This is known as immediate update mode.

Method | Description
DeleteRows | Deletes rows.
InsertRow | Creates and initializes a new row.
SetData | Sets data in one or more columns in a row.

IRowsetChange::DeleteRows

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

Note This provider supports only one pending change when the rowset is operating in delayed update mode. DeleteRows counts as a pending change. Other changes that count as a pending change include InsertRow, SetData, and opening a strucutured storage interface (ILockBytes or ISequentialStream) by using IRowset::GetData. This is true even if you open the structured storage in read-only mode This provider requires a lock on the cursor, even when the consumer is requesting a read-structured storage.

IRowsetChange::InsertRow

Creates and initializes a new row. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

Note This provider supports only one pending change when the rowset is operating in delayed update mode. InsertRow is treated as a pending change in delayed update mode.

IRowsetChange::SetData

Sets data values in one or more columns in a row. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

Note provider supports only one pending change when the rowset is operating in delayed update mode. SetData is treated as a pending change in delayed update mode.


IRowsetFind in Jet Provider

IRowsetFind

This interface is not supported.


IRowsetIdentity

IRowsetIdentity indicates the row instance identity that is implemented on the rowset and enables testing for row identity. If a rowset supports this interface, any two row handles representing the same underlying row will always reflect the same data and state.

IRowsetIdentity depends on IRowset.

Consumers call IsSameRow whenever they need to determine if two row handles represent the same underlying row. For example, if a row handle is returned to the consumer during notification, the consumer can compare it to its own list of row handles to see if it is the handle of a row that is of interest.

Method | Description
IsSameRow | Compares two row handles to see if they refer to the same row instance.

IRowsetIdentity::IsSameRow

Compares two row handles to see if they refer to the same row instance. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.


IRowsetIndex

This interface is not supported.


IRowsetInfo

IRowsetInfo provides information about a rowset.

When a consumer gets an interface pointer on a rowset, its first step usually is to determine the rowset’s capabilities by using IUnknown::QueryInterface. It may call GetProperties to learn the properties of the rowset that 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.

IRowsetInfo also provides methods for retrieving objects associated with the rowset. GetSpecification gets the object (command or session) that created the rowset. GetReferencedRowset gets the rowset that is referenced by a bookmark-valued column.

Method | Description
GetProperties | Returns the current setting of all properties supported by the rowset.
GetReferencedRowset | Returns an interface pointer to the rowset to which a bookmark applies.
GetSpecification | Returns an interface pointer on the object (command or session) that created the rowset.

IRowsetInfo::GetProperties

Returns the current settings of all properties supported by the rowset. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

IRowsetInfo::GetReferencedRowset

Returns an interface pointer to the rowset to which a bookmark or chapter applies. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

IRowsetInfo::GetSpecification

Returns an interface pointer on the object (command or session) that created this rowset. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.


IRowsetLocate

IRowsetLocate fetches arbitrary rows of a rowset. A rowset that does not implement this interface is a sequential rowset. IRowsetLocate is a prerequisite for IRowsetScroll.

When IRowsetLocate or one of its direct descendants is present on a rowset, then column zero is the bookmark for the rows.

Method | Description
Compare | Compares two bookmarks.
GetRowsAt | Fetches rows, starting with the row specified by an offset from a bookmark.
GetRowsByBookmark | Fetches the rows that match the specified bookmarks.
Hash | Returns hash values for the specified bookmarks.

IRowsetLocate::Compare

Compares two bookmarks. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

IRowsetLocate::GetRowsAt

Fetches rows starting with the row specified by an offset from a bookmark. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

IRowsetLocate::GetRowsByBookmark

Fetches the rows that match the specified bookmarks. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

IRowsetLocate::Hash

Returns hash values for the specified bookmarks. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.


IRowsetNotify

IRowsetNotify is the callback interface that a consumer must support to connect to local notifications provided by a rowset. The notifications are intended to synchronize objects that are attached to the same rowset instance. The notifications do not reflect changes in underlying shared tables that occur through other programs or users.

The notifications use the standard OLE connection point scheme for events. A rowset supports IConnectionPointContainer and the consumer calls FindConnectionPoint for IID_RowsetNotify to obtain the correct IConnectionPoint interface. The consumer then advises that connection point to connect and supplies a pointer to the consumer’s IRowsetNotify interface.

Method | Description
OnFieldChange | Notifies the consumer on any change to the value of a column.
OnRowChange | Notifies the consumer on the first change to a row, or any change that affects the entire row.
OnRowsetChange | Notifies the consumer on any change affecting the entire rowset.

IRowsetNotify::OnFieldChange

Notifies the consumer on any change to the value of a column. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

IRowsetNotify::OnRowChange

Notifies the consumer on the first change to a row, or any change that affects the entire row. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

IRowsetNotify::OnRowsetChange

Notifies the consumer on any change affecting the entire rowset. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.


IRowsetResynch

IRowsetResynch is used to retrieve the values for rows that are currently visible to the transaction.

IRowsetResynch is used to synchronize rows in the rowset with those in the data source. The primary uses or IRowsetResynch are:

  • To implement optimistic concurrency.

  • To perform row fix-up in a disconnected environment.

IRowsetResynch is useful only at isolation levels lower than Repeatable Read. For isolation levels of Repeatable Read and higher, the data that is currently visible for a row that has already been read does not change.

If the consumer requests IRowsetResynch, it is exposed regardless of the isolation level, even though it is not useful at isolation levels Repeatable Read and higher. This allows consumers to develop common code independent of their isolation level.

Method | Description
GetVisibleData | Gets the data in the data source that is visible to the transaction for the specified row.
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.

IRowsetResynch::GetVisibleData

Gets the data in the data source that is visible to the transaction for the specified row. Not supported in version 3.51 or 3.52 of this provider.

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. Not supported in version 3.51 or 3.52 of this provider.


IRowsetScroll

IRowsetScroll enables consumers to fetch rows located at approximate positions within a rowset. This method can be used for cases where precise positioning is not critical.

Method | Description
GetApproximatePosition | Gets the approximate position of a row corresponding to a specified bookmark.
GetRowsAtRatio | Fetches rows starting from a fractional position in the rowset.

IRowsetScroll::GetApproximatePosition

Gets the approximate position of a row corresponding to a specified bookmark. It is important to keep in mind that this is the approximate position. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

IRowsetScroll::GetRowsAtRatio

Fetches rows starting from a fractional position in the rowset. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.


IRowsetUpdate:IRowsetChange

IRowsetUpdate enables consumers to delay the transmission of changes made with IRowsetChange to the data source. This interface also enables consumers to undo changes before transmission.

If IRowsetUpdate is exposed on the rowset, then changes made through IRowsetChange are buffered in the rowset and not transmitted to the data source until IRowsetUpdate::Update is called. This is known as delayed update mode. If IRowsetUpdate is not exposed on the rowset, then changes made through IRowsetChange are immediately transmitted to the data source. This is known as immediate update mode.

The consumer calls the methods in IRowsetChange to update, delete, and insert rows. The rowset buffers these changes. When the consumer is ready to transmit these changes to the data source, it calls Update. Before calling Update, the consumer can back out any changes with Undo.

Method | Description
GetOriginalData | Gets the data most recently fetched from or transmitted to the data source. Does not get values based on pending changes.
GetPendingRows | Returns a list of rows with pending changes.
GetRowStatus | Returns the status of rows.
Undo | Undoes any changes made to a row since it was last fetched or since Update was called for it.
Update | Transmits any changes made to a row since it was last fetched or since Update was called for it.

IRowsetUpdate::GetOriginalData

Gets the data most recently fetched from or transmitted to the data source. Does not get values based on pending changes. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

IRowsetUpdate::GetPendingRows

Returns a list of rows with pending changes. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

IRowsetUpdate::GetRowStatus

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

IRowsetUpdate::Undo

Undoes any changes made to a row since it was last fetched or Update was called for it. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

IRowsetUpdate::Update

Transmits any changes made to a row since it was last fetched or Update was called for it. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.


IRowsetView

This interface is not supported.


ISessionProperties

ISessionProperties returns information about the properties a session supports and the current settings of those properties.

Method | Description
GetProperties | Returns the list of properties in the SESSION property group that are currently set on the session.
SetProperties | Sets properties in the SESSION property group.

ISessionProperties::GetProperties

Returns the list of properties in the SESSION property group that are currently set on the session. For information on how this interface is implemented, see the OLE DB Programmer’s Reference. For a description of SESSION properties supported, see Appendix A of this document.

ISessionProperties::SetProperties

Sets properties in the SESSION property group. For information on how this interface is implemented, see the OLE DB Programmer’s Reference. For a description of SESSION properties supported, see Appendix A of this document.


ISourcesRowset

ISourcesRowset returns a rowset of data sources and enumerators visible from the current enumerator.

Method | Description
GetSourcesRowset | Returns a rowset of the data sources and enumerators visible from the current enumerator.

ISourcesRowset::GetSourcesRowset

Returns a rowset of the data sources and enumerators visible from the current enumerator. This interface is not supported in this provider.


ISQLErrorInfo

ISQLErrorInfo is used to return the SQLSTATE and native error code.

Consumers call the methods on ISQLErrorInfo to retrieve the SQLSTATE and native error code. To retrieve a custom error object, a consumer calls IErrorRecords::GetCustomErrorObject.

Method | Description
GetSQLInfo | Returns the SQLSTATE and native error code associated with an error.

ISQLErrorInfo::GetSQLInfo

Returns the SQLSTATE and native error code associated with an error. This interface not currently supported in this provider.


ISupportErrorInfo

ISupportErrorInfo is defined by Automation. The following describes how the interface is used in OLE DB.

ISupportErrorInfo indicates whether a specific interface can return Automation error objects. Because OLE DB error objects are returned through the same mechanism as Automation error objects, support for them is also indicated through this interface.

A consumer uses this interface to determine whether a particular OLE DB interface can return an OLE DB error object.

Method | Description
InterfaceSupportsErrorInfo | Indicates whether a specific OLE DB interface can return OLE DB error objects.

ISupportErrorInfo::InterfaceSupportsErrorInfo

Indicates whether a specific OLE DB interface can return OLE DB error objects. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.


ITableDefinition

The ITableDefinition interface exposes simple methods to create, drop, and alter tables on the data source.

Method | Description
AddColumn | Adds a new column to a base table.
CreateTable | Creates a new base table in the data source.
DropColumn | Drops a column from a base table.
DropTable | Drops a base table in the data source.

ITableDefinition::AddColumn

Adds a new column to a base table. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

Note This provider must be able to open a table exclusively. If any other rowsets on this table are open, this routine will fail.

ITableDefinition::CreateTable

For information on how this interface is implemented, see the OLE DB Programmer’s Reference. For a detailed description of the TABLE properties and the JETOLEDB_TABLE properties supported for this interface, see Appendix A of this document.

Note This provider must be able to open a table exclusively. If any other rowsets on this table are open, this routine will fail. If you want to add columns to a rowset created with this method, then you must release the rowset before you can add columns to the table schema.

ITableDefinition::DropColumn

This method drops a column from the base table. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

Note This provider must be able to open a table exclusively. If any other rowsets on this table are open, this routine will fail.

ITableDefinition::DropTable

Drops a base table in the data source. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

Note This provider must be able to open a table exclusively. If any other rowsets on this table are open, this routine will fail.


ITransaction

The ITransaction interface is used to commit, abort, and obtain status information about transactions.

Method | Description
Abort | Aborts a transaction.
Commit | Commits a transaction.
GetTransactionInfo | Returns information about a transaction.

ITransaction::Abort

Aborts a transaction. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

ITransaction::Commit

Commits a transaction. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

ITransaction::GetTransactionInfo

Returns information regarding a transaction. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.


ITransactionJoin

ITransactionJoin is exposed only by providers that support distributed transactions. The consumer calls QueryInterface for ITransactionJoin to determine whether the provider supports coordinated transactions.

Method | Description
GetOptionsObject | Returns an object that can be used to specify configuration options for a subsequent call to JoinTransaction.
JoinTransaction | Requests that the session enlist in a coordinated transaction.

ITransactionJoin::GetOptionsObject

Returns an object that can be used to specify configuration options for a subsequent call to JoinTransaction. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

ITransactionJoin::JoinTransaction

Requests that the session enlist in a coordinated transaction. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.


ITransactionLocal:ITransaction

ITransactionLocal is an optional interface on sessions. It is used to start, commit, and abort transactions on the session.

Method | Description
GetOptionsObject | Returns an object that can be used to specify configuration options for a subsequent call to StartTransaction.
StartTransaction | Begins a new transaction.

ITransactionLocal::GetOptionsObject

Returns an object that can be used to specify configuration options for a subsequent call to StartTransaction. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

ITransactionLocal::StartTransaction

Begins a new transaction. For information on how this interface is implemented, see the OLE DB Programmer’s Reference.

Note Jet supports seven levels of nesting in transactions. The only supported mode for transactions is Read Committed. Setting lesser levels of transactional separation implies Read Committed. Setting higher levels will cause StartTransaction to fail. For more detailed information, see the OLE DB Programmer’s Reference.


ITransactionObject

ITransactionObject enables consumers to obtain a transaction object that is associated with a particular transaction level.

Method | Description
GetTransactionObject | Returns an interface pointer on the transaction object.

ITransactionObject::GetTransactionObject

Returns an interface pointer on the transaction object. This interface in not supported.


ITransactionOptions

ITransactionOptions gets and sets a suite of options associated with a transaction.

Method | Description
GetOptions | Gets a suite of options associated with a transaction.
SetOptions | Sets a suite of options associated with a transaction

.

ITransactionOptions::GetOptions

Gets a suite of options associated with a transaction. This interface is not supported.

ITransactionOptions::SetOptions

Sets a suite of options associated with a transaction. This interface is not supported.


IAlterTable

This interface is not supported.


ITableColumnInfo

This interface is not supported.


Column Properties

The DBPROPSET_COLUMN property set contains the following properties. All of these properties are in the COLUMN property group.

Property ID Description
DBPROP_COL_AUTOINCREMENT Type: VT_BOOL

Typical R/W: R/W

Description: Autoincrement

Specifies whether the values of the column are autoincrementing, that is:

  • VARIANT_TRUE, which indicates the values of the column are autoincrementing.

  • VARIANT_FALSE, which indicates the values of the column are not autoincrementing.
DBPROP_COL_DEFAULT Type: Any

Typical R/W: R/W

Description: Default

A VARIANT value that specifies the default value for an object — typically a domain or column. If the default value is a string, the string must be surrounded by double quotation marks to distinguish it from an object of the same name.

DBPROP_COL_DESCRIPTION Type: VT_BSTR

Typical R/W: R/W
DBPROP_COL_FIXEDLENGTH Type: VT_BOOL

Typical R/W: R/W

Description: Fixed Length

Specifies whether a column is fixed or variable length, that is:

  • VARIANT_TRUE, which indicates the column is fixed length. ulColumnSize in the DBCOLUMNDESC structure contains the fixed-length value.

  • VARIANT_FALSE (or not specified), which indicates the column is variable length. ulColumnSize in the DBCOLUMNDESC structure contains the maximum size of the column. For information about the DBCOLUMNDESC structure, see ITableDefinition::CreateTable in the OLE DB Programmer's Reference.
DBPROP_COL_NULLABLE Type: VT_BOOL

Typical R/W: R/W

Description: Nullable

Specifies whether a column can contain a NULL value, that is:

  • VARIANT_TRUE, which indicates the column can contain NULL values.

  • VARIANT_FALSE, which indicates the column cannot contain NULL values.
DBPROP_COL_PRIMARYKEY Type: VT_BOOL

Typical R/W: Not supported

Description: Primary Key

Specifies whether the column is part of the primary key of the table, that is:

  • VARIANT_TRUE, which indicates the column is part of the primary key.

  • VARIANT_FALSE, which indicates the column is not part of the primary key.
DBPROP_COL_UNIQUE Type: VT_BOOL

Typical R/W: Not supported

Description: Unique

Specifies whether the values of the column must be unique in the table, that is:

  • VARIANT_TRUE, which indicates the values of the column must be unique within the table.

  • VARIANT_FALSE, which indicates the values of the column can be repeated within the table.


Data Source Properties

The DBPROPSET_DATASOURCE property set contains the following properties. All of these properties are in the DATA SOURCE property group.

Property ID | Description
DBPROP_CURRENTCATALOG | Type: VT_BSTR Typical R/W: R/W Description: Current Catalog Specifies the name of the current catalog. The consumer can use the CATALOGS schema rowset to enumerate catalogs.


Data Source Creation Properties

This provider does not support any data source creation properties.


Data Source Information Properties

The DBPROPSET_DATASOURCEINFO property set contains the following properties. All of these properties are in the DATA SOURCE INFORMATION property group. These properties are read-only in this provider and constitute a set of static information about the provider and data source.

Property ID Description
DBPROP_ACTIVESESSIONS Type: VT_I4

Typical R/W: R Only

Description: Active Sessions

Specifies the maximum number of sessions that can exist at the same time. If this property is set to zero, there is no limit.
DBPROP_ASYNCTXNABORT Type: VT_BOOL

Typical R/W: R only

Description: Asynchronous Abort

Specifies whether asynchronous abortion of transactions is allowed.
DBPROPSET_ASYNCTXNCOMMIT Type: VT_BOOL

Typical R/W: R only

Description: Asynchronous Commit

Specifies whether asynchronous commitment of transactions is allowed.
DBPROPSET_BYREFACCESSORS Type: VT_BOOL

Typical R/W: R Only

Description: Pass By Ref Accessors

Specifies whether the provider supports the DBACCESSOR_PASSBYREF flag in IAccessor::CreateAccessor. This applies to both row and parameter accessors.

DBPROP_CATALOGLOCATION Type: VT_I4

Typical R/W: R Only

Description: Catalog Location

Specifies the position in a text command of a catalog name in a qualified table name, that is:

  • DBPROPVAL_CL_START, which indicates the catalog name is at the start of the fully qualified table name.

  • DBPROPVAL_CL_END – The catalog name is at the end of the fully qualified name.
DBPROP_CATALOGTERM Type: VT_BSTR

Typical R/W: R only

Description: Catalog Term

Specifies the name that the data source uses for a catalog, for example, “catalog,” “database,” or “directory.”. This is used to build user interfaces.
DBPROPSET_CATALOGUSAGE Type: VT_I4

Typical R/W: R Only

Description: Catalog Usage

A bitmask specifying how catalog names can be used in text commands, that is, zero or more of the following:

  • DBPROPVAL_CU_DML_STATEMENTS, which indicates that catalog names are supported in all Data Manipulation Language statements.

  • DBPROPVAL_CU_TABLE_DEFINITION, which indicates that catalog names are supported in all table definition statements.

  • DBPROPVAL_CU_INDEX_DEFINITIONS, which indicates that catalog names are supported in all index definition statements.

  • DBPROPVAL_CU_PRIVILEGE_DEFINITION, which indicates that catalog names are supported in all privilege definition statements.
DBPROPSET_COLUMNDEFINITION Type: VT_I4

Typical R/W: R Only

Description: Column Definition

A bitmask defining the valid clauses for the definition of a column, that is:

  • DBRPOPVAL_CD_NOTNULL, which indicates that columns can be created non-nullable.
DBPROP_CONCATNULLBEHAVIOR Type: VT_I4

Typical R/W: R only

Description: Null ConcatenationBehavior

Specifies how the data source handles concatenation of null valued and non-null valued character data type columns, that is:

  • DBPROPVAL_CB_NULLwhich indicates that the result is null valued.

  • DBPROPVAL_CB_NON_NULL, which indicates that the result is the concatenation of the non-null – valued column or columns.
DBRPOP_DATASOURCENAME Type: VT_BSTR

Typical R/W: R only

Description: Data Source Name

The name of the data source. This can be used during the connection process.
DBPROP_DATASOURCEREADONLY Type: VT_BOOL

Typical R/W: R Only

Description: Read-Only Data Source

This can be:

  • VARIANT_TRUE, which indicates that the data source is read-only.

  • VARIANT_FALSE, which indicates that the data source is updatable.
DBPROP_DBMSNAME Type: VT_BSTR

Typical R/W: R only

Description: DBMS Name

Specifices the name of the product accessed by the provider.

DBPROP_DBMSVER Type: VT_BSTR

Typical R/W: R only

Description: DBMS Version

Specifies the version of the product accessed by the provider.

DBPROP_DSOTHREADMODEL Type: VT_I4

Typical R/W: R only

Description: Data Source Object Threading Model

Specifies the threading model of the data source object, that is, one or more of:

  • DBPROPVAL_RT_FREETHREAD

  • DBPROPVAL_RT_APTMTTHREAD

  • DBPROPVAL_RT_SINGLETHREAD
DBPROP_GROUPBY Type: VT_I4

Typical R/W: R only

Description: Group By Support

Specifies the relationship between the columns in a GROUP BY clause and the nonaggregated columns on a select list, that is:

  • DBPROPVAL_GB_EQUALS_SELECT, which indicates that the GROUP BY clause must contain all nonaggregated columns on the select list.

  • DBPROPVAL_GB_CONTAINS_SELECTwhich indicates that the GROUP BY clause must contain all nonaggregated columns in the select list. It can also contain columns that are not on the select list.

  • DBPROPVAL_GB_NO_RELATION, which indicates that the columns in the GROUP BY clause and on the select list are not related. The meaning of nongrouped, nonaggregated columns on the select list is data source–dependent.
DBPROP_HETEROGENEOUSTABLES Type: VT_I4

Typical R/W: R Only

Description: Heterogeneous Table Support

Specifies a bitmask that indicates whether the provider can join tables from different catalogs or providers, that is, zero or more of:

  • DBPROPVAL_HT_DIFFERENT_CATALOGS

  • DBPROPVAL_HT_DIFFERENT_PROVIDERS
DBPROP_IDENTIFIERCASE Type: VT_I4

Typical R/W: R only

Description: Identifier Case Sensitivity

Specifies how identifiers respond to case, that is:

  • DBPROPVAL_IC_UPPER, which indicates that identifiers in SQL are case insensitive and are stored in uppercase in system catalog.

  • DBPROPVAL_IC_LOWER, which indicates that identifiers in SQL are case insensitive and are stored in lowercase in system catalog.

  • DBPROPVAL_IC_SENSITIVE, which indicates that identifiers in SQL are case sensitive and are stored in mixed case in system catalog.

  • DBPROPVAL_IC_MIXED, which indicates that identifiers in SQL are case insensitive and are stored in mixed case in system catalog.
DBPROP_MAXINDEXSIZE Type: VT_I4

Typical R/W: R Only

Description: Maximum Index Size

Specifies the maximum number of bytes allowed in the combined columns of an index. If there is no limit specified or if the limit is unknown, this value defaults to zero.
DBPROP_MAXOPENCHAPTERS Type: VT_I4

Typical R/W: R only

Description: Maximum Open Chapters

Specifies the maximum number of chapters that can be open at any time.

If a chapter must be released before a new chapter can be opened, set this value to one. If the provider does not support chapters, set this value to zero.
DBPROP_MAXROWSIZE Type: VT_I4

Typical R/W: R only

Description: Maximum Row Size

Specifies the maximum length of a single row in a table. If there is no specified limit or the limit is unknown, this value is zero.
DBPROP_MAXROWSIZEINCLUDESBLOB Type: VT_BOOL

Typical R/W: R Only

Description: Maximum Row Size Includes BLOB

Specifies values as either of:

  • VARIANT_TRUE, which indicates that the maximum row size returned for the DBPROP_MAXROWSIZE property includes the length of all BLOB data.

  • VARIANT_FALSE, which indicates that the maximum row size returned for the DBPROP_MAXROWSIZE property does not include the length of all BLOB data.
DBPROP_MAXTABLESINSELECT Type: VT_I4

Typical R/W: R Only

Description: Maximum Tables in SELECT

Specifies the maximum number of tables allowed in the FROM clause of a SELECT statement. If there is no specified limit or the limit is unknown, this value is zero.
DBPROP_MULTIPLEPARAMSETS Type: VT_BOOL

Typical R/W: R Only

Description: Multiple Parameter Sets

Specifies values as either of:

  • VARIANT_TRUE, which indicates that the provider supports multiple parameter sets.

  • VARIANT_FALSE, which indicates that the provider does not support multiple parameter sets.
DBPROP_MULTIPLERESULTS Type: VT_I4

Typical R/W: R only

Description: Multiple Results

Specifies a bitmask that indicates whether the provider supports multiple result objects and what restrictions it places on these objects, that is, zero or more of:

  • DBPROPVAL_MR_SUPPORTED, which indicates that the provider supports multiple result objects.

  • DBPROPVAL_MR_CONCURRENT, which indicates that more than one rowset created by the same multiple results object can exist concurrently.

If this bitmask is not set, the consumer must release the current rowset before calling IMultipleResults::GetResult to get the next result.

If multiple results objects are not supported, DBPROPVAL_MR_NOTSUPPORTED is returned.

DBPROP_MULTIPLESTORAGEOBJECTS Type: VT_BOOL

Typical R/W: R Only

Description: Multiple Storage Objects

Specifies values as either of:

  • VARIANT_TRUE, which indicates that the provider supports multiple open storage objects at the same time.

  • VARIANT_FALSE, which indicates that the provider supports only one open storage object at a time. Any method that attempts to open a second storage object returns a status of DBSTATUS_E_CANTCREATE for the column on which it attempted to open the second storage object, regardless of the rows or columns in which the objects are constructed.
DBPROP_MULTIPLEUPDATE Type: VT_BOOL

Typical R/W: R only

Description: Multiple Table Update

Specifies values as either of:

  • VARIANT_TRUE, which indicates that the provider can update rowsets derived from multiple tables.

  • VARIANT_FALSE, which indicates that the provider cannot update rowsets derived from multiple tables.
DBPROP_NULLCOLLATION Type: VT_I4

Typical R/W: R only

Description: NULL Collation Order

Specifies position of null values on a sorted list, that is:

  • DBPROPVAL_NC_END, which indicates that null values are sorted at the end of the list, regardless of the sort order.

  • DBPROPVAL_NC_HIGH, which indicates that null values are sorted at the high end of the list.

  • DBPROPVAL_NC_LOW, which indicates that null values are sorted at the low end of the list.

  • DBPROPVAL_NC_START, which indicates that null values are sorted at the start of the list, regardless of the sort order.
DBPROP_OLEOBJECTS Type: VT_I4

Typical R/W: R only

Description: OLE Object Support

Specifies a bitmask that indicates the ways in which the provider supports access to BLOBs and OLE objects stored in columns, that is, zero or more of:

  • DBPROPVAL_OO_BLOB, which indicates that the provider supports access to BLOBs as structured objects. The consumer determines what interfaces are supported through DBPROP_STRUCTUREDSTORAGE.

  • DBPROPVAL_OO_IPERSIST, which indicates that the provider supports access to OLE objects through IPersistStream, IPersistStreamInit, or IPersistStorage.
DBPROP_ORDERBYCOLUMNSINSELECT Type: VT_I4

Typical R/W: R Only

Description: Order By Columns In Select List

Specifies values as either of:

  • VARIANT_TRUE, which indicates that columns in an ORDER BY clause must be on the select list.

  • VARIANT_FALSE, which indicates that columns in an ORDER BY clause are not required to be on the select list.
DBPROP_OUTPUTPARAMETERAVAILABILITY Type: VT_I4

Typical R/W: R only

Description: Output Parameter Availability

Specifies the time at which output parameter values become available, that is:

  • DBPROPVAL_OA_NOTSUPPORTED, which indicates that output parameters are not supported.

  • DBPROPVAL_OA_ATEXECUTE, which indicates that output parameter data is available immediately after ICommand::Execute returns.

  • DBPROPVAL_OA_ATROWRELEASE, which indicates that, if a command returns a single result that is a rowset, output parameter data is available when the release of this rowset is complete. If a command returns multiple results, output parameter data is available as soon as either IMultipleResults::GetResult returns DB_S_NORESULT, or the release of the multiple results object is complete. Before the output parameter data is available, the consumer’s bound memory is in an indeterminate state.
DBPROP_PERSISTIDTYPE Type: VT_I4

Typical R/W: R only

Description: Persistent ID Type

Specifies an integer that indicates the type of DBID that the provider uses when persisting DBIDs for tables, indexes, and columns. This is usually the type of DBID that the provider considers to be the most permanent under schema changes and physical data reorganizations.

The value is one of:

  • DBPROPVAL_PT_GUID_NAME

  • DBPROPVAL_PT_GUID_PROPID

  • DBPROPVAL_PT_NAME

  • DBRPOPVAL_PT_GUID
DBPROP_PREPAREABORTBEHAVIOR Type: VT_I4

Typical R/W: R only

Description: Prepare Abort Behavior

Specifies how aborting a transaction affects prepared commands, that is:

  • DBPROPVAL_CB_DELETE, which indicates that aborting a transaction deletes prepared commands. The application must reprepare commands before executing them.

  • DBPROPVAL_DB_PRESERVE, which indicates that aborting a transaction preserves prepared commands. The application can execute commands without repreparing them.
DBPROP_PROCEDURETERM Type: VT_BSTR

Typical R/W: R only

Description: Procedure Term

Specifies a character string with the data source vendor’s name for a procedure, for example, “database procedure,” “stored procedure,” or “procedure.” This is used to build user interfaces.
DBPROP_PROVIDERNAME Type: VT_BSTR

Typical R/W: R only

Description: Provider Name

Specifies the file name of the provider.

DBPROP_PROVIDEROLEDBVER Type: VT_BSTR

Typical R/W: R only

Description: OLE DB Version

Returns the version of OLE DB supported by the provider. The version is returned in the format ##.## ####, where the first two digits are the major version, the next two digits are the minor version, and the last four digits are the release version. The provider can append a description of the provider.
DBPROP_QUOTEDIDENTIFIERCASE Type: VT_I4

Typical R/W: R only

Description: Quoted Identifier Sensitivity

Specifies how quoted identifiers treat case, that is:

  • DBPROPVAL_IC_UPPER, which indicates that identifiers quoted in SQL are case insensitive and are stored in uppercase in system catalog.

  • DBPROPVAL_IC_LOWER, which indicates that identifiers quoted in SQL are case insensitive and are stored in lowercase in system catalog.

  • DBPROPVAL_IC_SENSITIVE, which indicates that identifiers quoted in SQL are case sensitive and are stored in mixed case in system catalog

  • DBPROPVAL_IC_MIXED, which indicates that identifiers quoted in SQL are case insensitive and are stored in mixed case in system catalog.
DBPROP_ROWSETCONVERSIONCOMMAND Type: VT_BOOL

Typical R/W: R only

Description: Rowset Conversions On Command

Specifies values as either of:

  • VARIANT_TRUE, which indicates that callers to IConvertType::CanConvert can inquire ona command about conversions supported on rowsets generated by the command.

  • VARIANT_FALSE, which indicates that callers can inquire on a command only about conversions supported by the command.
DBPROP_SCHEMATERM Type: VT_BSTR

Typical R/W: R only

Description: Schema Term

Specifies the name the data source uses for a schema, for example, “schema” or “owner.”. This is used to build user interfaces.
DBPROP_SCHEMAUSAGE Type: VT_I4

Typical R/W: R only

Description: Schema Usage

Specifies a bitmask that indicates how schema names can be used in text commands, that is, zero or more of:

  • DBPROPVAL_SU_DML_STATEMENTS, which indicates that schema names are supported in all Data Manipulation Language statements.

  • DBPROPVAL_SU_TABLE_DEFINITION, which indicates that schema names are supported in all table definition statements.

  • DBPROPVAL_SU_INDEX_DEFINITION, which indicates that schema names are supported in all index definition statements.

  • DBPROPVAL_SU_PRIVILEGE_DEFINITION, which indicates that schema names are supported in all privilege definition statements.
DBPROP_SQLSUPPORT Type: VT_I4

Typical R/W: R only

Description: SQL Support

Specifies a bitmask that indicates the level of support for SQL, that is, zero or more of:

  • DBPROPVAL_SQL_NONE, which indicates that SQL is not supported.

The following levels correspond to the levels of SQL conformance defined in ODBC version 2.5. These levels are cumulative; that is, if the provider supports one level, it also supports all lower levels.

  • DBPROPVAL_SQL_ODBC_MINIMUM

  • DBPROPVAL_SQL_ODBC_CORE

  • DBPROPVAL_SQL_ODBC_EXTENDED

The following levels correspond to the levels in ANSI SQL 92. These levels are cumulative; that is, if the provider supports one level, it also supports all lower levels.

  • DBPROPVAL_SQL_ANSI92_ENTRY

  • DBPROPVAL_SQL_FIPS_TRANSITIONAL

  • DBPROPVAL_SQL_ANSI92_INTERMEDIATE

  • DBPROPVAL_SQL_ANSI92_FULL

  • DBPROPVAL_SQL_ANSI89_IEF, which indicates that the provider supports the ANSI 89 Integrity Enhancement Facility.
DBPROP_STRUCTUREDSTORAGE Type: VT_I4

Typical R/W: R only

Description: Structured Storage

Specifies a bitmask that indicates which interfaces on a storage object are supported by the rowset, that is, zero or more of:

  • DBPROPVAL_SS_ISEQUENTIALSTREAM

  • DBPROPVAL_SS_ISTREAM

  • DBPROPVAL_SS_ISTORAGE

  • DBPROPVAL_SS_ILOCKBYTES
DBPROP_SUBQUERIES Type: VT_I4

Typical R/W: R only

Description: Subquery Support

Specifies a bitmask that indicates the predicates in text commands that support subqueries, that is, zero or more of:

  • DBPROPVAL_SQ_CORRELATED SUBQUERIES

  • DBPROPVAL_SQ_COMPARISON

  • DBPROPVAL_SQ_EXISTS

  • DBPROPVAL_SQ_IN

  • DBPROPVAL_SQ_QUANTIFIED

The DBPROPVAL_SQ_CORRELATED SUBQUERIES bit indicates that all predicates that support subqueries support correlated subqueries.
DBPROP_SUPPORTEDTXNDDL Type: VT_I4

Typical R/W: R only

Description: Transaction DDL

Specifies whether Data Definition Language (DDL) statements are supported in transactions, that is:

  • DBPROPVAL_TC_NONE, which indicates that transactions are not supported.

  • DBPROPVAL_TC_DML, which indicates that transactions can only contain Data Manipulation Language (DML) statements. DDL statements within a transaction cause an error.

  • DBPROPVAL_TC_DDL_COMMIT, which indicates that transactions can only contain DML statements. DDL statements within a transaction cause the transaction to be committed.

  • DBPROPVAL_TC_DDL_IGNORE, which indicates that transactions can only contain DML statements. DDL statements within a transaction are ignored.

  • DBPROPVAL_TC_ALL, which indicates that transactions can contain DDL and DML statements in any order.
DBPROP_SUPPORTEDTXNISOLEVELS Type: VT_I4

Typical R/W: R only

Description: Isolation Levels

Specifies a bitmask that indicates the supported transaction isolation levels, that is, zero of more of:

  • DBPROPVAL_TI_CHAOS

  • DBPROPVAL_TI_READUNCOMMITTED

  • DBPROPVAL_TI_BROWSE

  • DBPROPVAL_TI_CURSORSTABILITY

  • DBPROPVAL_TI_READCOMMITTED

  • DBPROPVAL_TI_REPEATABLEREAD

  • DBPROPVAL_TI_SERIALIZABLE

  • DBPROPVAL_TI_ISOLATED
DBPROPVAL_SUPPORTEDTXNISORETAIN Type: VT_I4

Typical R/W: R only

Description: Isolation Retention

Specifies a bitmask that indicates the supported transaction isolation retention levels, that is, zero or more of:

  • DBPROPVAL_TR_COMMIT_DC, which indicates that the transaction preserves its isolation across a retaining commit.

  • DBPROPVAL_TR_COMMIT_NO, which indicates that the transaction cannot preserve its isolation across a retaining commit.

  • DBPROPVAL_TR_ABORT_DC, which indicates that the transaction may either preserve or dispose of its isolation across a retaining abort.

  • DBPROPVAL_TR_ABORT, which indicates that the transaction preserves its isolation across a retaining abort.

  • DBPROPVAL_TR_ABORT_NO, which indicates that the transaction cannot preserve its isolation across a retaining abort.

  • DBPROPVAL_TR_DONTCARE, which indicates that the transaction can preserve or dispose of its isolation across a retaining commit or abort. This is the default.

  • DBPROPVAL_TR_BOTH, which indicates that the transaction preserves its isolation across both a retaining commit and a retaining abort.

  • DBPROPVAL_TR_NONE, which indicates that the transaction cannot preserve its isolation across either a retaining commit or abort.

  • DBPROPVAL_TR_OPTIMISTIC, which indicates that optimistic concurrency control is to be used. If DBPROPVAL_TR_OPTIMISTIC is specified, whatever isolation technology is in place in this transaction (such as locking) must not affect the ability of other transactions to make changes to the data and resources that have been manipulated by this transaction. That is, optimistic control is to be used for all data in the transaction.
DBPROP_TABLETERM Type: VT_BSTR

Typical R/W: R only

Description: Table Term

Specifies the name the data source uses for a table.


Index Properties

The DBPROPSET_INDEX property set contains the following properties. All of these properties are in the INDEX property group.

Property ID Description
DBPROP_INDEX_AUTOUPDATE Type: VT_BOOL

Typical R/W: R Only

Description: Auto Update

Specifies whether the index is maintained automatically when changes are made to the corresponding base table, that is:

  • VARIANT_TRUE, which indicates that the index is automatically maintained.

  • VARIANT_FALSE, which indicates that the index must be maintained by the consumer through explicit calls to IRowsetChange.

It is the consumer’s responsibility to ensure consistency of the index as a result of updates to the associated base table.
DBPROP_INDEX_CLUSTERED Type: VT_BOOL

Typical R/W: R Only

Description: Clustered

Specifies whether an index can cluster, that is:

  • VARIANT_TRUE, which indicates that the leaf nodes of the index contain full rows, not bookmarks. This is a way to represent a table clustered by key values.

  • VARIANT_FALSE, which indicates that the leaf nodes of the index contain bookmarks relating to the base table rows whose key values match the key values of the index entry.
DBPROP_INDEX_FILLFACTOR Type: VT_BSTR

Typical R/W: R/W

Description: Fill Factor

For a B+-tree index, this property represents the storage use of page nodes during the creation of the index. The value is an integer, from 1 to 100, that represents the percentage of use of an index node. For a linear hash index, this property represents the storage use of the entire hash structure (the ratio of the used area to the total allocated area) before expansion of file structures occurs.
DBPROP_INDEX_INITIALSIZE Type: VT_I4

Typical R/W: R Only

Description: Initial Size

Specifies the total number of bytes allocated to this structure at creation time.
DBPROP_INDEX_NULLCOLLATION Type: VT_I4

Typical R/W: R Only

Description: NULL Collation

Specifies how null values are collated in the index, that is:

  • DBPROPVAL_NC_END, which indicates that null values are collated at the end of the list, regardless of the collation order.

  • DBPROPVAL_NC_START, which indicates that null values are collated at the start of the list, regardless of the collation order.

  • DBPROPVAL_NC_HIGH, which indicates that null values are collated at the high end of the list.

  • DBPROPVAL_NC_LOW, which indicates that null values are collated at the low end of the list.
DBPROP_INDEX_NULLS Type: VT_I4

Typical R/W: R/W

Description: NULL Keys

Specifies whether null keys are allowed, that is:

  • DBPROPVAL_IN_DISALLOWNULL, which indicates that the index does not allow entries where the key columns are NULL. If the consumer attempts to insert an index entry with a null key, then the provider returns an error.
DBPROP_INDEX_PRIMARYKEY Type: VT_BOOL

Typical R/W: R/W

Description: Primary Key

Specifies whether the index represents the primary key on the table.
DBPROP_INDEX_SORTBOOKMARKS Type: VT_BOOL

Typical R/W: R Only

Description: Sort Bookmarks

Specifies how the index treats repeated keys, that is:

  • VARIANT_TRUE, which indicates that the index sorts repeated keys by bookmark.

  • VARIANT_FALSE, which indicates that the index does not sort repeated keys by bookmark.
DBPROP_INDEX_TEMPINDEX Type: VT_BOOL

Typical R/W: R Only

Description: Temporary Index

Specifies whether the index is temporary, that is:

  • VARIANT_TRUE, which indicates that the index is destroyed when the session is released.

  • VARIANT_FALSE, which indicates that the index is permanent.
DBPROP_INDEX_TYPE Type: VT_I4

Typical R/W: R Only

Description: Index Type

Specifies the type of the index, that is:

  • DBPROPVAL_IT_BTREE, which indicates that the index is a B+-tree.

  • DBPROPVAL_IT_HASH, which indicates that the index is a hash file using linear or extensible hashing.

  • DBPROPVAL_IT_CONTENT, which indicates that the index is a content index.

  • DBPROPVAL_IT_OTHER, which indicates that the index is some other type of index.
DBPROP_INDEX_UNIQUE Type: VT_BOOL

Typical R/W: R/W

Description: Unique

Specifies whether index keys must be unique, that is:

  • VARIANT_TRUE, which indicates that the index keys must be unique.

  • VARIANT_FALSE, which indicates that duplicate keys are allowed.


Initialization Properties

The DBPROPSET_DBINIT property set contains the following properties. All of these properties are in the INITIALIZATION property group.

Property ID (Properties not supported in DBPROPSET_DBINIT) Description
DBPROP_AUTH_CACHE_AUTHINFO Type: VT_BOOL

Typical R/W: R Only

Description : Cache Authorization

Specifies values as either of:

  • VARIANT_TRUE, which indicates that the data source object or enumerator is allowed to cache sensitive authentication information, such as a password, in an internal cache.

  • VARIANT_FALSE , which indicates that the data source object or enumerator is not allowed to cache sensitive authentication information.
DBPROP_AUTH_ENCRYPT_PASSWORD Type: VT_BOOL

Typical R/W: R Only

Description: Encrypt Password

Specifies values as either of:

  • VARIANT_TRUE, which indicates that the consumer requires that the password be sent to the data source or enumerator in an encrypted form. This property uses cryptographic techniques, and as such specifies a stronger form of masking than DBPROP_AUTH_MASKPASSWORD.

  • VARIANT_FALSE, which indicates that the password can be sent without encryption.
DBPROP_AUTH_INTEGRATED Type: VT_BSTR

Typical R/W: Not supported

Description: Integrated Security

Specifies a string that contains the name of the authentication service used by the server. This is used to identify a user using an identity provided by an authentication domain. If the BSTR is a null pointer, this property is used and no other DBPROP_AUTH* properties are needed. If provided, their values are ignored.
DBPROP_AUTH_MASK_PASSWORD Type: VT_BOOL

Typical R/W: R Only

Description: Mask Password

Specifies values as either of:

  • VARIANT_TRUE, which indicates that the consumer requires that the password be sent to the data source or enumerator in a masked form.

  • VARIANT_FALSE, which indicates that the password can be sent in an unmasked form.
DBPROP_AUTH_PASSWORD Type: VT_BSTR

Typical R/W: R/W

Description: Password

Specifies the password to be used when connecting to the data source or enumerator. When the value of this property is retrieved with IDBProperties::GetProperties, the provider might return a mask, such as “******”, or an empty string, instead of the actual password. Note that the password is still set internally and is used when IDBInitialize::Initialize is called.
DBPROP_AUTH_PERSIST_ENCRYPTED Type: VT_BOOL

Typical R/W: R Only

Description: Persist Encrypted

Sepcifies values as either of:

  • VARIANT_TRUE, which indicates that the consumer requires the data source object to persist sensitive authentication information, such as a password, in encrypted form.

  • VARIANT_FALSE, which indicates that the data source object can persist sensitive authentication information without encryption.
DBPROP_AUTH_PERSIST_SENSITIVE_AUTHINFO Type: VT_BOOL

Typical R/W: R Only

Description: Persist Security Info

Specifies values as either of:

  • VARIANT_TRUE, which indicates that the data source object is allowed to persist sensitive authentication information, such as a password, along with other authentication information.

  • VARIANT_FALSE, which indicates that the data source object cannot persist sensitive authentication information.
DBPROP_AUTH_USERID Type: VT_BSTR

Typical R/W: R/W

Description: User ID

Specifies the user ID to be used when connecting to the data source or enumerator.
DBPROP_INIT_ASYNCH Type: VT_I4

Typical R/W: R/W

Description: Asynchronous Processing

Specifies a bitmask that indicates the asynchronous processing performed on the rowset, that is, zero or more of:

  • DBPROPVAL_ASYNCH_INITIALIZE, which indicats that IDBInitialize::Initialize returns immediately, but the actual initialization of the data source is done asynchronously. The data source behaves as an uninitialized data source prior to completing the initialization process, except that any call to IDBInitialize returns E_UNEXPECTED.

If no bits are set (the default), IDBInitialize::Initialize does not return until the data source is completely initialized.

DBPROP_INIT_CONNECT Type: VT_BOOL

Typical R/W: Not Supported

Description: Connect
DBPROP_INIT_DATASOURCE Type: VT_BSTR

Typical R/W: R/W

Description: Data Source

Specifies the name of the database or enumerator to which to connect.
DBPROP_INIT_HWND Type: VT_I4

Typical R/W: R/W

Description: Window Handle

Specifies the window handle to be used if the data source object or enumerator needs to prompt for additional information.
DBPROP_INIT_IMPERSONATION_LEVEL Type: VT_I4

Typical R/W: Not Supported

Description: Impersonation Level

Specifies the level of impersonation that the server is allowed to use when impersonating the client. This property applies only to non–Remote Procedure Call (RPC) network connections. Remote Procedure Call provides similar impersonation levels. The values of this property correspond directly to the levels of impersonation that can be specified for authenticated RPC connections, but can be applied to connections other than authenticated RPC.

Specifies values as one of:

  • DB_IMP_LEVEL_ANONYMOUS, which indicates that the client is anonymous to the server. The server process cannot obtain identification information about the client and cannot impersonate the client.

  • DB_IMP_LEVEL_IDENTIFY, which indicates that the server can obtain the client’s identity. The server can impersonate the client for ACL checking but cannot access system objects as the client.

  • DB_IMP_LEVEL_IMPERSONATE, which indicates that the server process can impersonate the client’s security context while acting on behalf of the client. This information is obtained when the connection is established, not on every call.

  • DB_IMP_LEVEL_DELEGATE, which indicates that the process can impersonate the client’s security context while acting on behalf of the client. The server process can also make outgoing calls to other servers while acting on behalf of the client.
DBPROP_INIT_LCID Type: VT_I4

Typical R/W: R/W

Description: Locale Identifier

The locale ID (LCID) of preference for the consumer. Consumers specify the LCID at initialization. This provides a method by which the server can determine the consumer’s LCID of choice . This property does not guarantee that all text returned to the consumer will be translated according to the LCID.

Providers may wish to set dwOptions in the DBPROP structure for this property to DBPROPOPTIONS_SETIFCHEAP.
DBPROP_INIT_LOCATION Type: VT_BSTR

Typical R/W: Not Supported

Description: Location

Specifies the location of the data source or enumerator to which to connect. Typically, this will be a server name.
DBPROP_INIT_MODE Type: VT_I4

Typical R/W: R/W

Description: Mode

Specifies a bitmask that indicates access permissions, that is, zero or more of:

  • DB_MODE_READ, which indicates Read only.

  • DB_MODE_WRITE, which indicates Write-only.

  • DB_MODE_READWRITE, which indicates read/write (DB_MODE_READ | DB_MODE_WRITE).

  • DB_MODE_SHARE_DENY_READ, which prevents others from opening in read mode.

  • DB_MODE_SHARE_DENY_WRITE, which prevents others from opening in write mode.

  • DB_MODE_SHARE_EXCLUSIVE, which prevents others from opening in read/write mode (DB_MODE_SHARE_DENY_READ | DB_MODE_SHARE_DENY_WRITE).

  • DB_MODE_SHARE_DENY_NONE, which indicates that neither read nor write access can be denied to others.
DBPROP_INIT_PROMPT Type: VT_I2

Typical R/W: R Only

Description: Prompt

Specifies whether to prompt the user during initialization, that is:

  • DBPROMPT_PROMPT, which indiates that the user is always prompted for initialization information.

  • DBPROMPT_COMPLETE, which prompts the user only if more information is needed.

  • DBPROMPT_COMPLETEREQUIRED, which prompts the user only if more information is needed. Does not allow the user to enter optional information.

  • DBPROMPT_NOPROMPT, which does not prompt the user.
DBPROP_INIT_PROTECTION_LEVEL Type: VT_I4

Typical R/W: Not Supported

Description: Protection Level

Specifies the level of protection for data sent between client and server. This property applies only to non–Remote Procedure Call (RPC) network connections. RPC provides similar protection levels. The values of this property correspond directly to the levels of protection that can be specified for authenticated RPC connections, but can be applied to connections other than authenticated RPC.

Specifies values as one of:

  • DB_PROT_LEVEL_NONE, which performs no authentication of data sent to server.

  • DB_PROT_LEVEL_CONNECT, which authenticates only when the client establishes the connection with the server.

  • DB_PROT_LEVEL_CALL, which authenticates the source of the data at the beginning of each request from the client to the server.

  • DB_PROT_LEVEL_PKT, which authenticates that all data received is from the client.

  • DB_PROT_LEVEL_PKT_INTEGRITY, which authenticates that all data received is from the client and that it has not been changed in transit.

  • DB_PROT_LEVEL_PKT_PRIVACY, which authenticates that all data received is from the client, that is, has not been changed in transit, and protects the privacy of the data by encrypting it.
DBPROP_INIT_PROVIDERSTRING Type: VT_BSTR

Typical R/W: R/W

Description: Extended Properties

Specifies a string that contains provider-specific, extended connection information. Use of this property implies that the consumer knows how this string will be interpreted and used by the provider. Consumers should use this property only for provider-specific connection information that cannot be explicitly described through the property mechanism.
DBPROP_INIT_TIMEOUT Type: VT_I4

Typical R/W: Not Supported

Description: Connect Timeout

Specifies the amount of time (in seconds) to wait for initialization to complete.


Provider-Defined Properties in JETOLEDB_INIT

Property ID | Description
DBPROP_JETOLEDB_DATABASEPASSWORD | Type: VT_BSTR Typical R/W: R/W Description: Jet OLEDB: Database Password


Rowset Properties

The DBPROPSET_ROWSET property set contains the following properties. All of these properties are in the ROWSET property group. Additional properties are defined in DBPROPSET_JETOLEDB_ROWSET.

Property ID Description
DBPROP_ABORTPRESERVE Type: VT_BOOL

Typical R/W: R Only

Description: Preserve on Abort

Specifies values as either of:

  • VARIANT_TRUE, which indicates that, after an abort that preserves, the rowset remains active. Therefore, it is possible to manipulate rows.

  • VARIANT_FALSE, which indicates that, after an abort, or an abort that preserves, the only operations allowed on a rowset are to release row and accessor handles and to release the rowset.
DBPROP_APPENDONLY Type: VT_BOOL

Typical R/W: R/W

Description: Append-Only Rowset

If the value is VARIANT_TRUE, when a rowset is opened it will initially be empty. If the rowset was obtained by IOpenRowset::OpenRowset, this is equivalent to positioning the start of the rowset at the end of the table. If the rowset was obtained by executing a command, this is equivalent to placing the start of the rowset at the end of the command’s results. A rowset opened with VARIANT_TRUE will be populated only by those rows inserted in it.

DBPROP_APPENDONLY set to VARIANT_TRUE implies:

  • DBPROP_IrowsetChange is VARIANT_TRUE.

  • DBPROP_OWNINSERT is VARIANT_TRUE

  • DBPROP_UPDATABILITY has the flag DBPROPVAL_UP_INSERT set.

  • DBPROP_OTHERINSERT is VARIANT_TRUE.
DBPROP_BLOCKINGSTORAGEOBJECTS Type: VT_BOOL

Typical R/W: R Only

Description: Blocking Storage Objects

Specifies whether storage objects might prevent use of other methods on the rowset, that is:

  • VARIANT_TRUE, which indicates that, after creating an instance of a storage object, use of other methods on the rowset might be prevented. That is, after a storage object is created and before it is released, methods other than those on the storage objects might return E_UNEXPECTED.

  • VARIANT_FALSE, which indicates that creating instances of storage objects does not prevent the use of other methods.
DBPROP_BOOKMARKS Type: VT_BOOL

Typical R/W: R/W

Description: Use Bookmarks

Specifies whether the rowset supports bookmarks, that is:

  • VARIANT_TRUE, which indicates that the rowset supports bookmarks. Column 0 is the bookmark for the rows. Getting this column obtains a bookmark value, which can be used to reposition to the row.

  • VARIANT_FALSE, which indicates that the rowset does not support bookmarks. The rowset is sequential and the values of the DBPROP_LITERALBOOKMARKS and DBPROP_ORDEREDBOOKMARKS properties are ignored.

The value of this property is automatically set to VARIANT_TRUE if the value of DBPROP_IROWSETLOCATE, DBPROP_LITERALBOOKMARKS, or DBPROP_ORDEREDBOOKMARKS is set to VARIANT_TRUE
DBPROP_BOOKMARKSKIPPED Type: VT_BOOL

Typical R/W: R Only

Description: Skip Deleted Bookmarks

Specifies whether the rowset allows IRowsetLocate::GetRowsAt or IRowsetFind::FindNextRow to continue if a bookmark row was deleted, is a row to which the consumer does not have access rights, or is no longer a member of the rowset.

Specifies values as either of:

  • VARIANT_TRUE, which indicates that GetRowsAt or FindNextRow skips the bookmark row and continues with the next row.

  • VARIANT_FALSE, which indicates that GetRowsAt or FindNextRow returns DB_E_BADBOOKMARK
DBPROP_BOOKMARKTYPE Type: VT_I4

Typical R/W: R Only

Description: BookMark Type

Specifies the bookmark type supported by the rowset, that is:

  • DBPROPVAL_BMK_NUMERIC, which indicates that the bookmark type is numeric. Numeric bookmarks are based on a row's properties that are not dependent on the values of the row’s columns. For instance, a numeric bookmark can be based on the absolute position of the row within rowset, or on a row ID that the storage engine assigned to a tuple when it was created. The validity of numeric bookmarks is not changed by modifying the row’s columns.

  • DBPROPVAL_BMK_KEY, which indicates that the bookmark type is key. Key bookmarks are based on the values of one or more of the row’s columns.These values form a unique key for each row. A key bookmark may be left dangling if the key values of the corresponding row are changed.
DBPROP_CACHEDEFERRED Type: VT_BOOL

Typical R/W: R Only

Description: Cache Deferred Columns

Specifies values as either of:

  • VARIANT_TRUE, which indicates that the provider caches the value of a deferred column when the consumer first gets a value from this column. When the consumer later gets values from this column, the provider returns the value in the cache. The contents of the cache can be overwritten by IRowsetChange::SetData or IRowsetResynch::ResynchRows. The cached value is released when the row is released. Consumers should set the value of this property to VARIANT_TRUE only when this is absolutely necessary, as this might require a large amount of memory in the provider. This could limit the number of rows that can be held at one time. Setting the values of this property to VARIANT_TRUE automatically sets the DBPROP_DEFERRED property to VARIANT_TRUE.

  • VARIANT_FALSE, which indicates that the provider does not cache the value of a deferred column and multiple calls to IRowset::GetData as the column can return different values.
DBPROP_CANFETCHBACKWARDS Type: VT_BOOL

Typical R/W: R/W

Description: Fetch Backwards

Specifies whether the rowset can fetch backwards, that is:

  • VARIANT_TRUE, which indicates that cRows in IRowset::GetNextRows, IRowsetLocate::GetRowsAt, and IRowsetScroll::GetRowsAtRatio can be negative. When it is negative, these methods fetch rows backwards from the specified row.

  • VARIANT_FALSE, which indicates that cRows must be non-negative.
DBPROP_CANHOLDROWS Type: VT_BOOL

Typical R/W: R/W

Description: Hold Rows

Specifies values as either of:

  • VARIANT_TRUE, which indicates that the rowset allows the consumer to retrieve more rows, or change the next fetch position, while holding previously fetched rows with pending changes.

  • VARIANT_FALSE, which indicates that the rowset requires pending changes to be transmitted to the data source and all rows to be released before fetching additional rows, inserting new rows, or changing the next fetch position.
DBPROP_CANSCROLLBACKWARDS Type: VT_BOOL

Typical R/W: R/W

Description: Scroll Backwards

Specifies whether the rowset can scroll backward, that is:

  • VARIANT_TRUE, which indicates that IRowsOffset in IRowset::GetNextRows or IRowsetLocate::GetRowsAt can be negative.

  • VARIANT_FALSE, which indicates that IRowsOffset must be non-negative.

If the rowset supports IRowsetLocate, the value of this property is VARIANT_TRUE, as this method supports backward scrolling by definition.
DBPROP_CHANGEINSERTEDROWS Type: VT_BOOL

Typical R/W: R/W

Description: Change Inserted Rows

Specifies values as either of:

  • VARIANT_TRUE, which indicates that the consumer can call IRowsetChange::DeleteRows or IRowsetChange::SetData for newly inserted rows.

  • VARIANT_FALSE, which indicates whether the consumer calls DeleteRows or SetData for newly inserted rows. DeleteRows returns a status of DBROWSTATUS_E_NEWLYINSERTED for the row and SetData returns DB_E_NEWLYINSERTED.

A newly inserted row is defined as a row for which the insertion has been transmitted to the data source, as opposed to a pending insert row.

DBPROP_COLUMNRESTRICT Type: VT_BOOL

Typical R/W: R Only

Description: Column Privileges

Specifies values as either of:

  • VARIANT_TRUE, which indicats that access rights are restricted on a column-by-column basis. If the rowset exposes IRowsetChange, IRowsetChange::SetData cannot be called for at least one column. A provider must not execute a query that would specify a column for which the consumer has no read access rights.

  • VARIANT_FALSE, which indicates that access rights are not restricted on a column-by-column basis. If the rowset exposes IRowsetChange, SetData can be called for any column in the rowset.

If access is restricted both by row and by column, individual columns of particular rows might have their own stricter access rights, and the consumer may not be permitted to read such columns. The column values will be null. If a null value is contrary to schema rules (Nulls not permitted) then the rowset should not count or return any rows that would have this condition.
DBPROP_COMMANDTIMEOUT Type: VT_I4

Typical R/W: Not Supported

Description: Command Time-out

Specifies the number of seconds before a command times out. A value of 0 indicates an infinite time-out.
DBPROP_COMMITPRESERVE Type: VT_BOOL

Typical R/W: R Only

Description: Preserve On Commit

Specifies values as either of:

  • VARIANT_TRUE, which indicates that after a commit that preserves, the rowset remains active. That is, it is possible to fetch new rows, update, delete, and insert rows, and so on.

  • VARIANT_FALSE, which indicates that after a commit or a commit that preserves, the only operations allowed on a rowset are to release row and accessor handles and to release the rowset.
DBPROP_DEFERRED Type: VT_BOOL

Typical R/W: R Only

Description: Defer Column

Specifies values as either of:

  • VARIANT_TRUE, which indicates that the data in the column is not fetched until an accessor is used on the column.

  • VARIANT_FALSE, which indicates that the data in the column is fetched when the row containing it is fetched.

The value of this property is automatically set to VARIANT_TRUE if the value of the DBPROP_CACHEDEFERRED property is set to VARIANT_TRUE.
DBPROP_DELAYSTORAGEOBJECTS Type: VT_BOOL

Typical R/W: R Only

Description: Delay Storage Object Updates

In delayed update mode, if the value of this property is VARIANT_TRUE, then storage objects are also used in delayed update mode. In particular:

  • Changes to the object are not transmitted to the data source until IRowsetUpdate::Update is called.

  • IRowsetUpdate::Undo undoes any pending changes, and IRowsetUpdate::GetOriginalData retrieves the original value of the object; that is, the object’s value when the row was last fetched or updated and excluding any changes made since then.

In delayed update mode, if the value of this property is VARIANT_FALSE, then storage objects are used in immediate update mode. In particular:

  • Changes to the object are immediately transmitted to the data source,

  • Update has no effect on the object.

  • Undo does not undo changes made to the object since the row was last fetched or updated, and GetOriginalData retrieves the current value of the object, including changes made since the row was last fetched or updated.

In immediate update mode, this property has no effect on storage objects.
DBPROP_IACCESSOR

DBPROP_ICOLUMNSINFO

DBPROP_ICOLUMNSROWSET

DBPROP_ICONNECTIONPOINTCONTAINER

DBPROP_ICONVERTTYPE

DBPROP_IROWSET

DBPROP_IROWSETCHANGE

DBPROP_IROWSETIDENTITY

DBPROP_IROWSETINFO

DBPROP_IROWSETLOCATE

DBPROP_IROWSETRESYNCH

DBPROP_IROWSETSCROLL

DBPROP_IROWSETUPDATE

DBPROP_ISUPPORTERRORINFO

 Type: VT_BOOL

Typical R/W: R/W, except as noted below.

Description: IAccessor

IColumnsInfo

IColumnsRowset

IConnectionPointContainer

IConvertType

IRowset

IRowsetChange

IRowsetIdentity

IRowsetInfo

IRowsetLocate

IRowsetResynch

IRowsetScroll

IRowsetUpdate

ISupportErrorInfo

If the value of any of these properties is set to VARIANT_TRUE, the rowset supports the specified interface. These properties are primarily used to request interfaces through ICommandProperties::SetProperties.

The values of the DBPROP_IROWSET, DBPROP_IACCESSOR, and DBPROP_IROWSETINFO properties are read-only and are always VARIANT_TRUE. They cannot be set to VARIANT_FALSE. If the consumer does not set the value of any of these properties to true, the resulting rowset supports IRowset, IAccessor, IColumnsInfo, IConvertType, and IRowsetInfo.

Setting DBPROP_IROWSETLOCATE to VARIANT_TRUE automatically sets DBPROP_BOOKMARKS to VARIANT_TRUE. Setting DBPROP_IROWSETUPDATE to VARIANT_TRUE automatically sets DBPROP_IROWSETCHANGE to VARIANT_TRUE.
DBPROP_ILOCKBYTES

DBPROP_ISEQUENTIALSTREAM

DBPROP_ISTORAGE

DBPROP_ISTREAM

 Type: VT_BOOL

Typical R/W: R/W

Description: ILockBytes

ISequentialStream

IStorage

IStream

If the value of this property is set to VARIANT_TRUE, the specified column is treated as storage object that exposes the specified interface.

If this property is set on all columns, the provider must be capable of exposing all columns of the rowset as storage objects.
DBPROP_IMMOBILEROWS Type: VT_BOOL

Typical R/W: R Only

Description: Immobile Rows

Specifies values as either of:

  • VARIANT_TRUE, which specifies that the rowset will not reorder inserted or updated rows. For IRowsetChange::InsertRow, rows will appear at the end of the rowset.

  • VARIANT_FALSE, which indicates that if the rowset is ordered, then inserted rows and updated rows (where one or more of the columns in the ordering criteria are updated) obey the ordering criteria of the rowset. If the rowset is not ordered, then inserted rows are not guaranteed to appear in a determinate position and the position of updated rows is not changed.

This property is meaningful only if DBPROP_OWNINSERT is VARIANT_TRUE.
DBPROP_LITERALBOOKMARKS Type: VT_BOOL

Typical R/W: R Only

Description: Literal Bookmarks

Specifies values as either of:

  • VARIANT_TRUE, which indicates that bookmarks can be compared literally. That is, they can be compared as a sequence of bytes. Furthermore, if the bookmarks are ordered (as specified by the DBPROP_ORDEREDBOOKMARKS property), the bytes are guaranteed to be ordered so that an arithmetic comparison as their scalar type yields the same result as a call to IRowsetLocate::Compare. Setting the value of this property to VARIANT_TRUE automatically sets the value of DBPROP_BOOKMARKS to VARIANT_TRUE.

  • VARIANT_FALSE, which indicates that bookmarks can only be compared with IRowsetLocate::Compare.
DBPROP_LITERALIDENTITY Type: VT_BOOL

Typical R/W: R Only

Description: Literal Row Identity

Specifies values as either of:

  • VARIANT_TRUE, which indicates that the consumer can perform a binary comparison of two row handles to determine whether they point to the same row.

  • VARIANT_FALSE, which indicates that the consumer must call IRowsetIdentity::IsSameRow to determine whether two row handles point to the same row.

Whether the handle of a newly inserted row can be successfully compared to another handle is specified by the DBPROP_STRONGIDENTITY property.
DBPROP_MAXOPENROWS Type: VT_I4

Typical R/W: R Only

Description: Maximum Open Rows

Specifies the maximum number of rows that can be active at the same time. This limit does not reflect resource limitations such as RAM, but does apply if the rowset implementation uses some strategy that results in a limit. If there is no limit, the value of this property is zero.
DBPROP_MAXPENDINGROWS Type: VT_I4

Typical R/W: R only

Description: Maximum Pending Rows

Specifies the maximum number of rows that can have pending changes at the same time. This limit does not reflect resource limitations such as RAM, but does apply if the rowset implementation uses some strategy that results in a limit. If there is no limit, this value is zero.
DBPROP_MAXROWS Type: VT_I4

Typical R/W: R Only

Description: Maximum Rows

Specifies the maximum number of rows that can be returned in a rowset. If there is no limit, this value is zero.
DBPROP_MAYWRITECOLUMN Type: VT_BOOL

Typical R/W: R Only

Description: Column Writable

Specifies whether a particular column is writable or not. Note that this property can be set implicitly through the command used to create the rowset. For example, if the rowset is created by the SQL statement SELECT A,B FROM MyTable FOR UPDATE OF A, then this property is VARIANT_TRUE for column A and VARIANT_FALSE for column B.
DBPROP_MEMORYUSAGE Type: VT_I4

Typical R/W: R Only

Description: Memory Usage

Estimates the amount of memory that can be used by the rowset. If it is 0, the rowset can use unlimited memory. If it is 1–99 inclusive, the rowset can use the specified percentage of total available virtual memory (physical and page file). If it is greater than or equal to 100, the rowset can use up to the specified number of kilobytes of memory.
DBPROP_NOTIFICATIONGRANULARITY Type: VT_I4

Typical R/W: R/W

Description: Notification Granularity

Specifies values as one of:

  • DBPROPVAL_NT_SINGLEROW, which indicates that, for methods that operate on multiple rows and generate multiphased notifications, the provider calls IRowsetNotify::OnRowChange separately for each phase for each row. A cancellation affects a single row. It does not affect the other rows, and notifications are still sent for these rows.

  • DBPROPVAL_NT_MULTIPLEROWS, which indicates that, for methods that operate on multiple rows and generate multiphased notifications, then for each phase, the provider calls OnRowChange once for all rows that succeed and once for all rows that fail. This separation can occur at each phase where a change can fail. For example, if IRowsetChange::DeleteRows deletes some rows and fails to delete others during the preliminary work phase, it calls OnRowChange twice: once with DBEVENTPHASE_SYNCHAFTER and the array of handles of rows that it deleted, and once with DBEVENTPHASE_FAILEDTODO and the array of handles of rows it failed to delete. A cancellation affects all rows with handles that were passed to OnRowChange.

  • DBPROP_NOTIFICATIONGRANULARITY does not affect how providers return notifications about events that affect columns or the entire rowset.
DBPROP_NOTIFICATIONPHASES Type: VT_I4

Typical R/W: R Only

Description: Notification Phases

Specifies a bitmask that indicates the notification phases supported by the provider. A combination of two or more of the following:

  • DBPROPVAL_NP_OKTODO

  • DBPROPVAL_NP_ABOUTTODO

  • DBPROPVAL_NP_SYNCHAFTER

  • DBPROPVAL_NP_FAILEDTODO

  • DBPROPVAL_NP_DIDEVENT

The DBPROPVAL_NP_FAILEDTODO and DBPROPVAL_NP_DIDEVENT bits must be returned by all providers that support notifications.
DBPROP_NOTIFYCOLUMNSET

DBPROP_NOTIFYROWDELETE

DBPROP_NOTIFYROWFIRSTCHANGE

DBPROP_NOTIFYROWINSERT

DBPROP_NOTIFYROWRESYNCH

DBPROP_NOTIFYROWSETRELEASE

DBPROP_NOTIFYROWSETFETCHPOSITIONCHANGE

DBPROP_NOTIFYROWUNDOCHANGE

DBPROP_NOTIFYROWUNDODELETE

DBPROP_NOTIFYROWUNDOINSERT

DBPROP_NOTIFYROWUPDATE

 Type: VT_I4

Typical R/W: R Only

Description: Column Set Notification.

Row Delete Notification

Row First Change Notification

Row Insert Notification

Row Resynchronization Notification

Rowset Release Notification

Rowset Fetch Position Change Notification

Row Undo Change Notification

Row Undo Delete Notification

Row Undo Insert Notification

Row Update Notification

Specifies a bitmask that indicates whether the notification phase can be cancelled, that is zero or more of:

  • DBPROPVAL_NP_OKTODO

  • DBPROPVAL_NP_ABOUTTODO

  • DBPROPVAL_NP_SYNCHAFTER
DBPROP_ORDEREDBOOKMARKS Type: VT_BOOL

Typical R/W: R/W

Description: Bookmarks Ordered

Specifies values as either of:

  • VARIANT_TRUE, which indicates that bookmarks can be compared to determine the relative position of their associated rows in the rowset. Setting the value of this property to VARIANT_TRUE automatically sets the value of DBPROP_BOOKMARKS to VARIANT_TRUE.

  • VARIANT_FALSE, which indicates that bookmarks can be compared only for equality. Whether bookmarks can be compared byte-by-byte or must be compared with IRowsetLocate::Compare depends on the value of the DBPROP_LITERALBOOKMARKS property.
DBPROP_OTHERINSERT Type: VT_BOOL

Typical R/W: R/W

Description: Other’s Inserts Visible

Specifies values as either of:

  • VARIANT_TRUE, which indicates that the rowset can see rows inserted by someone other than a consumer of the rowset. That is, if someone other than a consumer of the rowset inserts a row, any consumer of the rowset can see that row the next time it fetches a set of rows containing it. Note that this includes rows inserted by other parties in the same transaction, as well as rows inserted by parties outside the transaction.

    The transaction isolation level does not affect the ability of the rowset to see rows inserted by other parties in the same transaction, such as other rowsets in the same session. However, it does restrict the ability of the rowset to see rows inserted by parties outside the transaction.

  • VARIANT_FALSE, which indicates that the rowset cannot see rows inserted by others.

For programmers accustomed to the cursor model in ODBC, the DBPROP_OTHERUPDATEDDELETE and DBPROP_OTHERINSERT properties correspond to ODBC cursors as follows:

Static Cursor

  • DBPROP_OTHERINSERT = VARIANT_FALSE

  • DBPROP_OTHERUPDATEDELETE = VARIANT_FALSE

Keyset-driven cursor

  • DBPROP_OTHERINSERT = VARIANT_FALSE

  • DBPROP_OTHERUPDATEDELETE = VARIANT_TRUE

Dynamic cursor

  • DBPROP_OTHERINSERT = VARIANT_TRUE

  • DBPROP_OTHERUPDATEDELETE = VARIANT_TRUE

Furthermore, the DBPROP_OWNUPDATEDELETE and DBPROP_OWNINSERT properties correspond to the values returned by the SQL_STATIC_SENSITIVITY information type in SQLGetInfo in ODBC.
DBPROP_OTHERUPDATEDELETE Type: VT_BOOL

Typical R/W: R/W

Description: Other’s Changes Visible

Specifies values as either of:

  • VARIANT_TRUE, which indicates that the rowset can see updates and deletes made by someone other than a consumer of the rowset. That is, suppose someone other than a consumer of the rowset updates the data underlying a row or deletes the row. If the row is released completely, any consumer of the rowset will see that change the next time it fetches the row. Note that this includes updates and deletes made by other parties in the same transaction, as well as updates and deletes by parties outside the transaction.

    The transaction isolation level does not affect the ability of the rowset to see updates or deletes by other parties in the same transaction, such as other rowsets in the same session. However, it does restrict the ability of the rowset to see updates or deletes by parties outside the transaction.

  • VARIANT_FALSE, which indicates that the rowset cannot see updates and deletes by others.
DBPROP_OWNINSERT Type: VT_BOOL

Typical R/W: R/W

Description: Own Inserts Visible

Specifies values as either of:

  • VARIANT_TRUE, which indicates that the rowset can see its own inserts. That is, if a consumer of a rowset inserts a row, any consumer of the rowset can see that row the next time it fetches a set of rows containing it.

    This ability is independent of the transaction isolation level, because all consumers of the rowset share the same transaction.

  • VARIANT_FALSE, which indicates that the rowset cannot see rows inserted by consumers of the rowset unless the command is re-executed.
DBPROP_OWNUPDATEDELETE Type: VT_BOOL

Typical R/W: R/W

Description: Own Changes Visible

Specifies values as either of:

  • VARIANT_TRUE, which indicates that the rowset can see its own updates and deletes. That is, suppose a consumer of the rowset updates or deletes a row. If the row is released completely, any consumer of the rowset will see the update or delete the next time it fetches that row.

    This ability is independent of the transaction isolation level because all consumers of the rowset share the same transaction.

  • VARIANT_FALSE, which indicates that the rowset cannot see updates and deletes by consumers of the rowset unless the command is re-executed.
DBPROP_QUICKRESTART Type: VT_BOOL

Typical R/W: R/W

Description: Quick Restart

Specifies values as either of:

  • VARIANT_TRUE, which indicates that IRowset::RestartPosition is relatively quick to execute. In particular, it does not re-execute the command that created the rowset.

  • VARIANT_FALSE, which indicates that RestartPosition is expensive to execute and requires re-executing the command that created the rowset.

Although the value of this property can be set to VARIANT_TRUE, the provider is not required to honor it. The reason for this is that the provider does not know what the command is at the time the property is set. In particular, the consumer can set this property and then change the command text. However, the provider can fail this property if it is never able to quickly restart the next fetch position. Thus, if a consumer successfully sets this property, it must still check this flag on the rowset to determine if the next fetch position can be quickly set.
DBPROP_REENTRANTEVENTS Type: VT_BOOL

Typical R/W: R Only

Description: Re-entrant events

Specifies values as either of:

  • VARIANT_TRUE, which indicates that the provider supports re-entrancy during callbacks to the IRowsetNotify interface. The provider might not support re-entrancy on all rowset methods. These methods return DB_E_NOTREENTRANT.

  • VARIANT_FALSE, which indicates that the provider does not support such re-entrancy. The provider returns DB_E_NOTREENTRANT on methods called during the notification.

Regardless of this flag, all providers must support IRowset::GetData and IRowset::ReleaseRows calls during notifications, so long as the columns being accessed do not include deferred columns.

DBPROP_REMOVEDELETED Type: VT_BOOL

Typical R/W: R/W

Description: Remove Deleted Rows

If the value of this property is VARIANT_TRUE, the provider removes rows it detects as having been deleted from the rowset. That is, fetching a block of rows that formerly included a deleted row does not return a handle to that row.

Which rows the rowset detects as having been deleted is determined by the DBRPOP_OWNUPDATEDELETE and DBPROP_OTHERUPDATEDELETE properties. Whether the rowset removes these rows is determined by this property.

Note that this property is independent of the transaction isolation level. While the transaction isolation level in some cases determines whether the rowset can detect a row as having been deleted, it has no effect on whether or not the rowset removes that row.

For programmers accustomed to the cursor model in ODBC, this value of the property is always VARIANT_TRUE for rowsets implemented through dynamic cursors. That is, dynamic cursors always remove deleted rows. Whether static and keyset-driven cursors remove deleted rows depends on the value of this property.
DBPROP_REPORTMULTIPLECHANGES Type: VT_BOOL

Typical R/W: R Only

Description: Report Multiple Changes

Specifies values as either of:

  • VARIANT_TRUE, which indicates that an update or delete can affect multiple rows and the provider can detect that multiple rows have been updated or deleted. This happens when a provider cannot uniquely identify a row. For example, the provider might use the values of all the columns in the row to identify the row. If these columns do not include a unique key, an update or delete might affect more than one row.

  • VARIANT_FALSE, which indicates that an update or delete always affects a single row or the provider cannot detect whether it affects multiple rows.
DBPROP_RETURNPENDINGINSERTS Type: VT_BOOL

Typical R/W: R Only

Description: Return Pending Inserts

Specifies values as either of:

  • VARIANT_TRUE, which indicates that the methods that fetch rows, such as IRowset::GetNextRows, can return pending insert rows; that is, rows that have been inserted in delayed update mode but for which IRowsetUpdate::Update has not yet been called.

  • VARIANT_FALSE, which indicates that the methods that fetch rows cannot return pending insert rows.
DBPROP_ROWRESTRICT Type: VT_BOOL

Typical R/W: R Only

Description: Row Privileges

Specifies values as either of:

  • VARIANT_TRUE, which indicates that access rights are restricted on a row-by-row basis. If the rowset supports IRowsetChange, IRowsetChange::SetData can be called for some but not all rows. A rowset must never count or return a handle for a row for which the consumer does not have read access rights.

  • VARIANT_FALSE, which indicates that access rights are not restricted on a row-by-row basis. If the rowset supports IRowsetChange, SetData can be called for any row.
DBPROP_ROWSET_ASYNCH Type: VT_I4

Typical R/W: R/W

Description: Asynchronous Rowset Processing

Specifies a bitmask that indicates the asynchronous processing performed on the rowset.

Specifies values as zero or more of:

  • DBPROPVAL_ASYNCH_INITIALIZE, which indicates that the rowset is initialized asynchronously. The method requesting the rowset returns immediately, but attempting to call any interface other than IConnectionPointContainer to obtain the IID_IDBStatusNotify connection point may fail and the full set of interfaces may not be available on the rowset until asynchronous initialization has completed.

  • DBPROPVAL_ASYNCH_SEQUENTIALPOPULATION, which indicates that the rowset is randomly asynchronously populated. Requests for rows may return DB_E_ENDOFROWSET before the end of the rowset is actually reached. Asynchronously populated rows may be inserted anywhere in the rowset.

  • The consumer may set both DBPROPVALUE_ASYNCH_SEQUENTIALPOPULATION and DBPROPVALUE_ASYNCH_RANDOMPOPULATION bits to request that the rowset be asynchronously populated either sequentially or randomly. The consumer is prepared for asynchronous notifications in OnRowChange as well as from IDBAsynchStatus. Only one property is returned by the rowset. If the rowset is asynchronously populated it returns either DBPROPVAL_ASYNCH_RANDOM or DBPROPVAL_ASYNCH_SEQUENTIAL.

If no bits are set (the default) the rowset is initialized and populated synchronously. All requested interfaces are available when the method requesting the rowset returns and requesting rows block until the requested number of hRows are obtained or the end of the rowset is reached.
DBPROP_ROWTHREADMODEL Type: VT_I4

Typical R/W: R/W

Description: Row Threading Model

Specifies the threading model of the rowsets generated by the command, that is:

  • DBPROPVAL_RT_FREETHREAD

  • DBPROPVAL_RT_APTMTTHREAD

  • DBPROPVAL_RT_SINGLETHREAD
DBPROP_SERVERCURSOR Type: VT_BOOL

Typical R/W: R/W

Description: Server Cursor

If the value of this property is set to VARIANT_TRUE with ICommandProperties::SetProperties, the cursor underlying the rowset (if any) must be materialized on the server.

If the value of this property is not set with SetProperties, it is up to the provider to decide where to materialize the cursor.

The consumer can determine where the cursor was materialized by checking the value of this property on the rowset.
DBPROP_STRONGIDENTITY Type: VT_BOOL

Typical R/W: R Only

Description: Strong Row Identity

Specifies values as either of:

  • VARIANT_TRUE, which indicates that the handles of newly inserted rows can be compared as specified by DBPROP_LITERALIDENTITY.

  • VARIANT_FALSE, which indicates that there is no guarantee that the handles of newly inserted rows can be compared successfully. In this case, IRowsetIdentity::IsSameRow might return DB_E_NEWLYINSERTED.

A newly inserted row is defined to be a row for which an insertion has been transmitted to the data source, as opposed to a pending insert row.

DBPROP_TRANSACTEDOBJECT Type: VT_BOOL

Typical R/W: R/W

Description: Objects Transacted

Specifies values as either of:

  • VARIANT_TRUE, which indicates that any object created on the specified column is transacted. That is, data made visible to the data source through the object can be committed with ITransaction::Commit or aborted with ITransaction::Abort.

  • VARIANT_FALSE, which indicates that any object created on the specified column is not transacted. That is, all changes to the object are permanent once they are made visible to the data source.

If this property is set on a column that does not contain an object, it is ignored.
DBPROP_UPDATABILITY Type: VT_I4

Typical R/W: R/W

Description: Updatability

Specifies a bitmask that indicates the supported methods on IRowsetChange, that is, zero or more of:

  • DBPROPVAL_UP_CHANGE, which indicates that SetData is supported.

  • DBPROPVAL_UP_DELETE, which indicates that DeleteRows is supported.

  • DBPROPVAL_UP_INSERT, which indicates that InsertRow is supported.

  • DBPROP_UPDATABILITY, which should be used in conjunction with DBPROP_IROWSETCHANGE. If DBPROP_IROWSETCHANGE is VARIANT_TRUE and DBPROP_UPDATABILITY is not set, then it is provider-specific what methods are supported on IRowsetChange.


Provider-Specific Properties in JETOLEDB_ROWSET t

Provider-Specific Properties in JETOLEDB_ROWSET

Property ID (Additional Properties Supported in DBPROPSET_JETOLEDB_ROWSET) | Description
DBPROP_JETOLEDB_ODBCPASSTHROUGH CONNECTSTRING | Type: VT_BSTR Typical R/W: R/W Description: ODBC Pass Through Query Connect String
DBPROP_JETOLEDB_ODBCPASSTHROUGH | Type: VT_BOOL Typical R/W: R/W Description: Jet OLEDB: ODBC Pass-Through Statement
DBPROP_JETOLEDB_USEGRBIT | Type: VT_I4 Typical R/W: R/W Description: Jet OLEDB: Use Grbit
DBPROP_JETOLEDB_GRBITVAL | Type: VT_I4 Typical R/W: R/W Description: Jet OLEDB: Grbit Value
DBPROP_JETOLEDB_DBOPTIMISTIC LOCKING | Type: VT_Bool Typical R/W: R/W Description: Jet OLEDB: Optimistic Locking
DBPROP_JETOLEDB_3_5_ENABLEROWSET INDEX | Type: VT_Bool Typical R/W: R/W Description: Jet OLEDB: Enable IRowsetIndex


Session Properties

The DBPROPSET_SESSION property set contains the following properties. All of these properties are in the SESSION property group.

Property ID | Description
DBPROP_SESS_AUTOCOMMITISOLEVELS | Type: VT_I4 Typical R/W: R only Description: Autocommit Isolation Levels Specifies a bitmask that indicates the transaction isolation level while in autocommit mode. The values that can be set in this bitmask are the same as those that can be set for DBPROP_SUPPORTEDTXNISOLEVELS.


Table Properties

The DBPROPSET_TABLE property set contains the following properties. All of these properties are in the TABLE property group.

Property ID Description
DBPROP_TBL_TEMPTABLE Type: VT_BOOL

Typical R/W: R Only

Description: Temporary Table

Specifies whether the table is temporary, that is:

  • VARIANT_TRUE, which indicates that the table is destroyed when the session is released.

  • VARIANT_FALSE, which indicates that the table is created permanently.


Provider-Defined Properties in JETOLEDB_TABLE

Property ID | Description
DBPROP_JETOLEDB_LINK | Type: VT_BOOL Typical R/W: R/W Description: Jet OLEDB: Create Link
DBPROP_JETOLEDB_LINKEXCLUSIVE | Type: VT_BOOL Typical R/W: R/W Description: Jet OLEDB: Exclusive Link
DBPROP_JETOLEDB_LINKDATASOURCE | Type: VT_BSTR Typical R/W: R/W Description: Jet OLEDB: Link Datasource
DBPROP_JETOLEDB_LINKPROVIDERSTRING | Type: VT_BSTR Typical R/W: R/W Description: Jet OLEDB: Link Provider String
DBPROT_JSTOLEDB_LINKREMOTETABLE | Type: VT_BSTR Typical R/W: R/W Description: Jet OLEDB: Remote Table Name


Overview of Schema Rowsets

The schema information specified in OLE DB is based on the assumption that providers support the concepts of a catalog and a schema. The ANSI SQL92 specification defines them as follows:

  • A catalog contains one or more schemas, but always contains a schema named INFORMATION_SCHEMA, which contains the views and domains of the information schema. In Microsoft SQL Server and Microsoft Access terms, a catalog is a database. In ODBC terms, a catalog is a qualifier.

  • A schema is a collection of database objects that are owned or have been created by a particular user. In Microsoft SQL Server and ODBC terms, a schema is an owner. There is no equivalent to a schema in a Microsoft Access database.

Schema information in OLE DB is retrieved by using predefined schema rowsets. This appendix lists the contents of each schema rowset.

Schema rowsets are retrieved with IDBSchemaRowset::GetRowset. GetRowset allows consumers to specify simple restrictions, such as returning all the columns in a particular table. Each schema rowset is guaranteed to return all of the columns in the order shown in the section detailing that rowset. In addition, providers can return provider-specific columns after the last column defined by OLE DB.

In implementing IDBSchemaRowset, it is suggested that providers pay particular attention to rowset construction and data retrieval performance: These commands will be frequently used by consumers.

This information schema also defines a minimum content of the system schema. It is assumed that consumers will prepare and store the specialized commands used for things such as command planning and execution-time plan validation, and storage engines may have specialized optimizations associated with those plans. Consumers can also look at the schema tables in the schema, and thus discover other schema tables and attributes beyond the minimum set.


CHECK_CONSTRAINTS Rowset

The CHECK_CONSTRAINTS rowset identifies the check constraints defined in the catalog that are owned by a given user.

The CHECK_CONSTRAINTS rowset contains the following columns:

Column name | Type indicator | Description
CONSTRAINT_CATALOG | DBTYPE_WSTR | Catalog name. NULL if the provider does not support catalogs
CONSTRAINT_SCHEMA | DBTYPE_WSTR | Unqualified schema name. NULL if the provider does not support schemas.
CONSTRAINT_NAME | DBTYPE_WSTR | Constraint name.
CHECK_CLAUSE | DBTYPE_WSTR | The WHERE clause specified in the CHECK clause.
DESCRIPTION | DBTYPE_WSTR | Human-readable description of the column.

Default Sort Order: CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME.


COLUMNS Rowset

The COLUMNS rowset identifies the columns of tables defined in the catalog that are accessible to a given user.

The COLUMNS rowset contains the following columns:

Column name | Type indicator | Description
TABLE_CATALOG | DBTYPE_WSTR | Catalog name. NULL if the provider does not support catalogs.
TABLE_SCHEMA | DBTYPE_WSTR | Unqualified schema name. NULL if the provider does not support schemas.
COLUMN_NAME DBTYPE_WSTR The name of the column. This might not be unique. If this cannot be determined, a NULL is returned.

This column, together with the COLUMN_GUID and COLUMN_PROPID columns, forms the columns ID. One or more of these columns will be NULL depending on which elements of the DBID structure the provider uses.

If possible, the resulting column ID should be persistent. However, some providers do not support persistent identifiers for columns.

The column ID of a base table should be invariant under views.
COLUMN_GUID | DBTYPE_GUID | Column GUID
COLUMN_PROPID | DBTYPE_UI4 | Column property ID.
ORDINAL_POSITION | DBTYPE_UI4 | The ordinal of the column. Columns are numbered starting from one. NULL if there is no stable ordinal value for the column.
COLUMN_HASDEFAULT DBTYPE_BOOL Specifies value as either of:
  • VARIANT_TRUE, which indicates that the column has a default value.

  • VARIANT_FALSE, which indicates that the column does not have a default value or it is unknown whether the column has a default value.
COLUMN_DEFAULT DBTYPE_WSTR Default value of the column. A provider may expose DBCOLUMN_DEFAULTVALUE but not DBCOLUMN_HASDEFAULT in the rowset returned by IColumnsRowset::GetColumns Rowset.

If the default value is NULL, COLUMN_HASDEFAULT is VARIANT_TRUE, and the COLUMN_DEFAULT column is NULL.
COLUMN_FLAGS | DBTYPE_UI4 | A bitmask that describes column characteristics. The DBCOLUMNFLAGS enumerated type specifies the bits in the bitmask. For information about DBCOLUMNSFLAGS, see IColumnsInfo::GetColumnInfo. This column cannot contain a NULL value.
IS_NULLABLE DBTYPE_BOOL Specifies values as either of:
  • VARIANT_TRUE, which indicates that the column might be nullable.

  • VARIANT_FALSE, which indicates that the column is known not to be nullable.
DATA_TYPE | DBTYPE_UI2 | The indicator of the column’s data type. If the data type of the column varies from row to row, this must be DBTYPE_VARIANT. For a list of valid type indicators, see “Type Indicators” in Appendix A of the OLE DB Programmer’s Reference.
TYPE_GUID | DBTYPE_GUID | The GUID of the column’s data type.
CHARACTER_MAXIMUM_ LENGTH DBTYPE_UI4 The maximum possible length of a value in the column. For character, binary, or bit columns, this is one of:
  • The maximum length of the column in characters, bytes, or bits, respectively, if one is defined.

  • The maximum length of the data type in characters, bytes, or bits, respectively, if the column does not have a defined length.

  • Zero (0) if neither the column nor the data type has a defined maximum length.

  • NULL for all other types of columns.
CHARACTER_OCTET_LENGTH DBTYPE_UI4 Maximum length in octets (bytes) of the column, if the type of the column is character or binary. A value of zero means the column has no maximum length.

NULL for all other types of columns.
NUMERIC_PRECISION DBTYPE_UI2 If the column’s data type is numeric, this is the maximum precision of the column. The precision of columns with a data type of DBTYPE_DECIMAL or DBTYPE_NUMERIC depends on the definition of the column.

If the column’s data type is not numeric, this is NULL
NUMERIC_SCALE | DBTYPE_I2 | If column’s type indicator is DBTYPE_NUMERIC, this is the number of digits to the right of the decimal point. Otherwise, this is NULL
DATETIME_PRECISION | DBTYPE_UI4 | Datetime precision (number of digits in the fractional seconds portion) of the column if the column is a datetime or interval type.
CHARACTER_SET_ CATALOG | DBTYPE_WSTR | Catalog name in which the character set is defined NUL if the provider does not support catalogs or different character sets.
CHARACTER_SET_ SCHEMA | DBTYPE_WSTR | Unqualified schema name in which the character set is defined. NULL if the provider does not support schemas or different character sets.
CHARACTER_SET_NAME | DBTYPE_WSTR | Character set name. NULL if the provider does not support different character sets.
COLLATION_CATALOG | DBTYPE_WSTR | Catalog name in which the collation is defined. NULL if the provider does not support catalogs or different collations.
COLLATION_SCHEMA | DBTYPE_WSTR | Unqualified schema name in which the collation is defined. NULL if the provider does not support schemas or different collations.
COLLATION_NAME | DBTYPE_WSTR | Collation name. NULL if the provider does not support different collations.
DOMAIN_CATALOG | DBTYPE_WSTR | Catalog name in which the domain is defined. NULL if the provider does not support catalogs or domains.
DOMAIN_SCHEMA | DBTYPE_WSTR | Unqualified schema name in which the domain is defined. NULL if the provider does not support schemas or domains.
DOMAIN_NAME | DBTYPE_WSTR | Domain name. NULL if the provider does not support domains.
DESCRIPTION | DBTYPE_WSTR | Human-readable description of the column. For example, the description for a column named Name in the Employee table might be “Employee name.”

Default Sort Order: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME.


CONSTRAINT_TABLE_USAGE Rowset

The CONSTRAINT_TABLE_USAGE rowset identifies the tables that are used by referential constraints, unique constraints, check constraints, and assertions defined in the catalog and owned by a given user.

The CONSTRAINT_TABLE_USAGE rowset contains the following columns:

Column name | Type indicator | Description
TABLE_CATALOG | DBTYPE_WSTR | Catalog name in which the table is defined. NULL if the provider does not support catalogs.
TABLE_SCHEMA | DBTYPE_WSTR | Unqualified schema name in which the table is defined. NULL if the provider does not support schemas.
TABLE_NAME | DBTYPE_WSTR | Table name.
CONSTRAINT_CATALOG | DBTYPE_WSTR | Catalog name. NULL if the provider does not support catalogs.
CONSTRAINT_SCHEMA | DBTYPE_WSTR | Unqualified schema name. NULL if the provider does not support schemas.
CONSTRAINT_NAME | DBTYPE_WSTR | Constraint name.

Default Sort Order: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME.


INDEXES Rowset

The INDEXES rowset identifies the indexes defined in the catalog that are owned by a given user.

The INDEXES rowset contains the following columns:

Column name | Type indicator | Description
TABLE_CATALOG | DBTYPE_WSTR | Catalog name. NULL if the provider does not support catalogs.
TABLE_SCHEMA | DBTYPE_WSTR | Unqualified schema name. NULL if the provider does not support schemas.
TABLE_NAME | DBTYPE_WSTR | Table name.
INDEX_CATALOG | DBTYPE_WSTR | Catalog name. NULL if the provider does not support catalogs.
INDEX_SCHEMA | DBTYPE_WSTR | Unqualified schema name. NULL if the provider does not support schemas.
INDEX_NAME | DBTYPE_WSTR | Index name.
PRIMARY_KEY | DBTYPE_BOOL | Whether the index represents the primary key on the table. NULL if this is not known.
UNIQUE DBTYPE_BOOL Specifies whether index keys must be unique, that is:
  • VARIANT_TRUE, which indicates that the index keys must be unique.

  • VARIANT_FALSE, which indicates that duplicate keys are allowed.
CLUSTERED DBTYPE_BOOL Specifies whether an index is clustered, that is:
  • VARIANT_TRUE, which indicates that the leaf nodes of the index contain full rows, not bookmarks. This is a way to represent a table clustered by key value.

  • VARIANT_FALSE, which indicates that the leaf nodes of the index contain bookmarks of the base table rows whose key value matches the key value of the index entry.
TYPE DBTYPE_UI2 Specifies the type of the index, that is:
  • DBPROPVAL_IT_BTREE, which indicates that the index is a B+-tree.

  • DBPROPVAL_IT_HASH, which indicates that the index is a hash file using, for example, linear or extensible hashing.

  • DBPROPVAL_IT_CONTENT, which indicates that the index is a content index.

  • DBPROPVAL_IT_OTHER, which indicates that the index is some other type of index.
FILL_FACTOR | DBTYPE_I4 | For a B+-tree index, this property represents the storage use factor of page nodes during the creation of the index. The value is an integer from 1 to 100 representing the percentage of use of an index node. For a linear hash index, this property represents the storage use of the entire hash structure (the ratio of used area to total allocated area) before a file structure expansion occurs.
INITIAL_SIZE DBTYPE_I4 Specifies whether null keys are allowed, that is:
  • DBPROPVAL_IN_DISALLOWNULL, which indicates that the index does not allow entries where the key columns are NULL. If the consumer attempts to insert an index entry with a NULL key, then the provider returns an error.

  • DBPROPVAL_IN_IGNORENULL, which indicates that the index does not insert entries containing NULL keys. If the consumer attempts to insert an index entry with a NULL key, then the provider ignores that entry and no error code is returned.

  • DBPROPVAL_IN_IGNOREANYNULL, which indicates that the index does not insert entries where some column key has a NULL value. For an index, that has a multicolumn search key, if the consumer inserts an index entry with NULL value in some column of the search key, then the provider ignores that entry and no error code is returned.
SORT_BOOKMARKS DBTYPE_BOOL Specifies how the index treats repeated keys, that is:
  • VARIANT_TRUE, which indicates that the index sorts repeated keys by bookmark.

  • VARIANT_FALSE, which indicates that the index does not sort repeated keys by bookmark.
AUTO_UPDATE DBTYPE_BOOL Specifies whether the index is maintained automatically when changes are made to the corresponding base table, that is:
  • VARIANT_TRUE, which indicates that the index is automatically maintained.

  • VARIANT_FALSE, which indicates that the index must be maintained by the consumer through explicit calls to IRowsetChange. It is the consumer’s responsibility to ensure that consistency of the index is a result of updates to the associated base table.
NULL_COLLATION DBTYPE_I4 Specifies how NULLs are collated in the index, that is, one of:
  • DBPROPVAL_NC_END, which indicates that NULLs are collated at the end of the list, regardless of the collation order.

  • DBPROPVAL_NC_START, which indicates that NULLs are collated at the start of the list, regardless of the collation order.

  • DBPROPVAL_NC_HIGH, which indicates that NULLs are collated at the high end of the list.

  • DBPROPVAL_NC_LOW, which indicates that NULLs are collated at the low end of the list.
ORDINAL_POSITION | DBTYPE_UI4 | Ordinal position of the column in the index, starting with one.
COLUMN_NAME | DBTYPE_WSTR | Column name. The column, together with the COLUMN_GUID and COLUMN_PROPID columns, forms the column ID. One or more of these columns will be NULL depending on which elements of the DBID structure the provider uses.
COLUMN_GUID | DBTYPE_GUID | Column GUID.
COLUMN_PROPID | DBTYPE_UI4 | Column property ID.
COLLATION DBTYPE_I2 Specifies values as one of:
  • CB_COLLATION_ASC, which indicates that the sort sequence for the column is ascending.

  • DB_COLLATION_DESC, which indicates that the sort sequence for the column is descending.

  • NULL, which indicates that a column sort sequence is not supported.
CARDINALITY | DBTYPE_I4 | Number of unique values in the index.
PAGES | DBTYPE_I4 | Number of pages used to store the index.
FILTER_CONDITION | DBTYPE_WSTR | The WHERE clause identifying the filtering restriction.

Default Sort Order: UNIQUE_TYPE, INDEX_CATALOG, INDEX_SCHEMA, INDEX_NAME, ORDINAL_POSITION.


KEY_COLUMN_USAGE Rowset

The KEY_COLUMN_USAGE rowset identifies the columns defined in the catalog that are constrained as keys by a given user.

The KEY_COLUMN_USAGE rowset contains the following columns:

Column name | Type indicator | Description
CONSTRAINT_CATALOG | DBTYPE_WSTR | Catalog name. NULL if the provider does not support catalogs.
CONSTRAINT_SCHEMA | DBTYPE_WSTR | Unqualified schema name. NULL if the provider does not support schemas.
CONSTRAINT_NAME | DBTYPE_WSTR | Constraint name.
TABLE_CATALOG | DBTYPE_WSTR | Catalog name in which the table containing the key column is defined. NULL if the provider does not support catalogs.
TABLE_SCHEMA | DBTYPE_WSTR | Unqualified schema name in which the table containing the key column is defined. NULL if the provider does not support schemas.
TABLE_NAME | DBTYPE_WSTR | Table name containing the key column.
COLUMN_NAME | DBTYPE_WSTR | Name of the column participating in the unique, primary key, or foreign key. This column, together with the COLUMN_GUID and COLUMN_PROPID columns, forms the column ID. One or more of these columns will be NULL depending on which elements of the DBID structure the provider uses.
COLUMN_GUID | DBTYPE_GUID | Column GUID.
COLUMN_PROPID | DBTYPE_UI4 | Column property ID.
ORDINAL_POSITION | DBTYPE_UI4 | Ordinal position of the column in the constraint being described. If the constraint being described is the key of the cardinality one, the value of ORDINAL_POSITION is always one.

Default Sort Order: CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, ORDINAL_POSITION.


PRIMARY_KEYS Rowset

The PRIMARY_KEYS rowset identifies the primary key columns defined in the catalog by a given user. This schema rowset is built upon an SQL 92 schema view as a convenience to the non-SQL programmer. If supported, it must be synchronized with the related SQL 92 view (CONSTRAINT_COLUMN_USAGE).

The PRIMARY_KEYS rowset contains the following columns:

Column name | Type indicator | Description
TABLE_CATALOG | DBTYPE_WSTR | Catalog name in which the table is defined. NULL if the provider does not support catalogs.
TABLE_SCHEMA | DBTYPE_WSTR | Unqualified schema name in which the table is defined. NULL if the provider does not support schemas.
TABLE_NAME | DBTYPE_WSTR | Table name.
COLUMN_NAME | DBTYPE_WSTR | Primary key column name. This column, together with the COLUMN_GUID and COLUMN_PROPID columns, forms the column ID. One or more of these columns will be NULL depending on which elements of the DBID structure the provider uses.
COLUMN_PROPID | DBTYPE_UI4 | Primary key column property ID.
ORDINAL | DBTYPE_UI4 | The order of the column names (and GUIDs and property IDs) in the key.

Default Sort Order: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME.


PROCEDURES Rowset

The PROCEDURES rowset is an OLE DB extension, based on SQL 3. It identifies the procedures defined in the catalog that are owned by a given user.

The PROCEDURES rowset contains the following columns:

Column name | Type indicator | Description
PROCEDURE_CATALOG | DBTYPE_WSTR | Catalog name. NULL if the provider does not support catalogs.
PROCEDURE_SCHEMA | DBTYPE_WSTR | Unqualified schema name. NULL if the provider does not support schemas.
PROCEDURE_NAME | DBTYPE_WSTR | Procedure name.
PROCEDURE_TYPE DBTYPE_I2 Specifies values as:
  • DB_PT_UNKNOWN, which indicates that it is not known whether there is a return value.

  • DB_PT_PROCEDURE, which indicates a procedure. There is no returned value.

  • DB_PT_FUNCTION, which indicates a function. There is no returned value.
PROCEDURE_DEFINITION | DBTYPE_WSTR | Procedure definition.
DESCRIPTION | DBTYPE_WSTR | Human-readable description of the procedure.

Default Sort Order: PROCEDURE_CATALOG, PROCEDURE_SCHEMA, PROCEDURE_NAME.


PROVIDER_TYPES Rowset

The PROVIDER_TYPES rowset identifies the (base) data types supported by the data provider.

The PROVIDER_TYPES rowset contains the following columns:

Column name | Type indicator | Description
TYPE_NAME | DBTYPE_WSTR | Provider-specific data type name.
DATA_TYPE | DBTYPE_UI2 | The indicator of the data type.
COLUMN_SIZE DBTYPE_UI4 The length of a non-numeric column or parameter refers to either the maximum or the defined length for this type by the provider. For character data, this is the maximum or defined length in characters.

If the data type is numeric, this is the upper bound on the maximum precision of the data type. For the maximum precision of the data type.

LITERAL_PREFIX | DBTYPE_WSTR | Character or characters used to prefix a literal of this type in a text command.
LITERAL_SUFFIX | DBTYPE_WSTR | Character or characters used to suffix a literal of this type in a text command.
CREATE_PARAMS DBTYPE_WSTR The creation parameters are specified by the consumer when creating a column of this data type. For example, the SQL data type DECIMAL needs a precision and a scale. In this case, the creation parameters might be the string “precision, scale.” In the text command to create a DECIMAL column with a precision of 10 and a scale of 2, the value of the TYPE_NAME column might be DECIMAL() and the complete type specification would be DECIMAL(10,2).

The creation parameters appear as a comma-separated list of values, in the order they are to be supplied, with no surrounding parentheses. If a creation parameter is length, maximum length, precision, or scale, “length,” “max length,” “precision,” and “scale” should be used, respectively. If the creation parameters are some other value, it is provider-specific what text is used to describe the creation parameter.

If the data type required creation parameters, “()” generally appears in the type name. This indicates the position at which to insert the creation parameters. If the type name does not include “()”, the creation parameters are enclosed in parentheses and appended to the end of the data type name.
IS_NULLABLE DBTYPE_BOOL Specifies values as:
  • VARIANT_TRUE, which indicates that the data type is nullable.

  • VARIANT_FALSE, which indicates that the data type is not nullable.

  • NULL, which indicates that it is not known whether the data type is nullable.
CASE_SENSITIVE DBTYPE_BOOL Specifies values as either of:
  • VARIANT_TRUE, which indicates that the data type is a character type and is case sensitive.

  • VARIANT_FALSE, which indicates that the data type is not a character type or is not case sensitive.
SEARCHABLE DBTYPE_UI4 If the provider supports ICommandText, then this column is an integer indicating the searchability of a data type. Otherwise, this column is NULL. Specifies values as one of:
  • DB_UNSEARCHABLE, which indicates that the data type cannot be used in a WHERE clause.

  • DB_LIKE_ONLY, which indicates that the data type can be used in a WHERE clause only with the LIKE predicate.

  • DB_ALL_EXCEPT_LIKE, which indicates that the data type can be used in a WHERE clause with all comparison operators except LIKE.

  • DB_SEARCHABLE, which indicates that the data type can be used in a WHERE clause with any comparison operator.
UNSIGNED _ATTRIBUTE DBTYPE_BOOL Specifies values as:
  • VARIANT_TRUE, which indicates that the data type is unsigned.

  • VARIANT_FALSE, which indicates that the data type is signed.

  • NULL, which indicates that it is not applicable to data type.
FIXED_PREC_SCALE DBTYPE_BOOL Specifies values as one of:
  • VARIANT_TRUE, which indicates that the data type has a fixed precision and scale.

  • VARIANT_FALSE, which indicates that the data type does not have a fixed precision and scale.
AUTO_UNIQUE_VALUE DBTYPE_BOOL Specifies values as one of:
  • VARIANT_TRUE, which indicates that values of this type can be autoincrementing.

  • VARIANT_FALSE, which indicates that values of this type cannot be autoincrementing.
LOCAL_TYPE_NAME | DBTYPE_WSTR | Localized version of TYPE_NAME. NULL is returned if a localized name is not supported by the data provider.
MINIMUM_SCALE | DBTYPE_I2 | If the type indicator is DBTYPE_DECIMAL or DBTYPE_NUMERIC, this is the minimum number of digits allowed to the right of the decimal point. Otherwise, this is NULL.
MAXIMUM_SCALE | DBTYPE_I2 | If the type indicator is DBTYPE_DECIMAL or DBTYPE_NUMERIC, this is the maximum number of digits allowed to the right of the decimal point. Otherwise, this is NULL.
GUID | DBTYPE_GUID | The GUID of the type. All types supported by a provider are described in a type library, so each type has a corresponding GUID.
TYPELIB | DBTYPE_WSTR | The type library containing the description of this type.
VERSION | DBTYPE_WSTR | The version of the type definition. Providers may wish to indicate version type definitions. Different providers may use different version schemas, such as a timestamp or number (integer or float). NULL if not supported.
IS_LONG DBTYPE_BOOL This value determines the setting of the DBCOLUMNFLAGS_ISLONG flag returned by GetColumnInfo in IColumnsInfo and GetParameterInfo in ICommandWithParameters. Specifies values as one of:
  • VARIANT_TRUE, which indicates that the data type is a BLOB that contains very long data. The definition of very long data is provider-specific.

  • VARIANT_FALSE, which indicates that the data type is a BLOB that does not contain very long data or is not a BLOB.
BEST_MATCH DBTYPE_BOOL Specifies values as one of:
  • VARIANT_TRUE, which indicates that the data type is the best match between all data types in the data source and the OLE DB data type indicated by the value in the DATA_TYPE column.

  • VARIANT_FALSE, which indicates that the data type is not the best match.

For each set of rows in which the value of the DATA_TYPE column is the same, the BEST_MATCH column is set to VARIANT_TRUE in only one row.

Default Sort Order: DATA_TYPE.


REFERENTIAL_CONSTRAINTS Rowset

The REFERENTIAL_CONSTRAINTS rowset identifies the referential constraints defined in the catalog that are owned by a given user.

The REFERENTIAL_CONSTRAINTS rowset contains the following columns:

Column name | Type indicator | Description
CONSTRAINT_CATALOG | DBTYPE_WSTR | Catalog name. NULL if the provider does not support catalogs.
CONSTRAINT_SCHEMA | DBTYPE_WSTR | Unqualified schema name. NULL if the provider does not support schema.
CONSTRAINT_NAME | DBTYPE_WSTR | Constraint name.
UNIQUE_CONSTRAINT_ CATALOG | DBTYPE_WSTR | Catalog name in which the unique or primary key constraint is defined. NULL if the provider does not support catalogs.
UNIQUE_CONSTRAINT_ SCHEMA | DBTYPE_WSTR | Unqualified schema name in which the unique or primary key constraint is defined. NULL if the provider does not support schemas.
UNIQUE_CONSTRAINT_ NAME | DBTYPE_WSTR | Unique or primary key constraint name.
MATCH_OPTION DBTYPE_WSTR Specifies values as:
  • “NONE,” which indicates that no match type was specified.

  • “PARTIAL,” which indicates that a match type of PARTIAL was specified.

  • “FULL,” which indicates that a match type of FULL was specified.
UPDATE_RULE DBTYPE_WSTR If an update rule was specified, then the UPDATE_RULE value is one of:
  • “CASCADE,” which indicates that a referential action of CASCADE was specified.

  • “SET NULL,” which indicates that a referential action of SET NULL was specified.

  • “SET DEFAULT,” which indicates that a referential action of SET DEFAULT was specified.

  • “NO ACTION,” which indicates that a referential action of NO ACTION was specified.
DELETE_RULE DBTYPE_WSTR If a delete rule was specified, then the DELETE_RULE value is one of:
  • “CASCADE,” which indicates that a referential action of CASCADE was specified.

  • “SET NULL,” which indicates that a referential action of SET NULL was specified.

  • “SET DEFAULT,” which indicates that a referential action of SET DEFAULT was specified.

  • “NO ACTION,” which indicates that a referential action of NO ACTION was specified.
DESCRIPTION | DBTYPE_WSTR | Human-readable description of the column.

Default Sort Order: CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, UNIQUE_CONSTRAINT_CATALOG, UNIQUE_CONSTRAINT_SCHEMA, UNIQUE_CONSTRAINT_NAME.


STATISTICS Rowset

The STATISTICS rowset identifies the statistics defined in the catalog that are owned by a given user.

The STATISTICS rowset contains the following columns:

Column name | Type indicator | Description
TABLE_CATALOG | DBTYPE_WSTR | Catalog name. NULL if the provider does not support catalogs.
TABLE_SCHEMA | DBTYPE_WSTR | Unqualified schema name. NULL if the provider does not support schemas.
TABLE_NAME | DBTYPE_WSTR | Table name.
CARDINALITY | DBTYPE_I4 | Cardinality (number of rows) of the table.

Default Sort Order: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME.


TABLE_CONSTRAINTS Rowset

The TABLE_CONSTRAINTS rowset identifies the table constraints defined in the catalog that are owned by a given user.

The TABLE_CONSTRAINTS rowset contains the following columns:

Column name | Type indicator | Description
CONSTRAINT_CATALOG | DBTYPE_WSTR | Catalog name. NULL if the provider does not support catalogs.
CONSTRAINT_SCHEMA | DBTYPE_WSTR | Unqualified schema name. NULL if the provider does not support schemas.
CONSTRAINT_NAME | DBTYPE_WSTR | Constraint name.
TABLE_CATALOG | DBTYPE_WSTR | Catalog name in which the table is defined. NULL if the provider does not support catalogs.
TABLE_SCHEMA | DBTYPE_WSTR | Unqualified schema name in which the table is defined. NULL if the provider does not support schemas.
TABLE_NAME | DBTYPE_WSTR | Table name.
CONSTRAINT_TYPE DBTYPE_WSTR Constraint type, that is, one of:
  • “UNIQUE”

  • “PRIMARY KEY”

  • “FOREIGN KEY”

  • “CHECK”
IS_DEFERRABLE DBTYEP_BOOL Specifies values as either of:
  • VARIANT_TRUE, which indicates that the table constraint is deferrable.

  • VARIANT_FALSE, which indicates that the table constraint is not deferrable.
INITIALLY_DEFERRED DBTYPE_BOOL Specifies values as either of:
  • VARIANT_TRUE, which indicates that the table constraint is initially deferred.

  • VARIANT_FALSE, which indicates that the table constraint is initially immediate.
DESCRIPTION | DBTYPE_WSTR | Human-readable description of the column.

Default Sort Order: CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, CONSTRAINT_TYPE.


TABLES Rowset

The TABLES rowset identifies the tables defined in the catalog that are accessible to a given user.

The TABLES rowset contains the following columns:

Column name | Type indicator | Description
TABLE_CATALOG | DBTYPE_WSTR | Catalog name. NULL if the provider does not support catalogs.
TABLE_SCHEMA | DBTYPE_WSTR | Unqualified schema name. NULL if the provider does not support schemas.
TABLE_NAME | DBTYPE_WSTR | Table name.
TABLE_TYPE DBTYPE_WSTR Table type, that is, one of the following (or a provider-specific value):
  • “ALIAS”

  • “TABLE”

  • “SYNONYM”

  • “SYSTEM TABLE”

  • “VIEW”

  • “GLOBAL TEMPORARY”

  • “LOCAL TEMPORARY”
TABLE_GUID | DBTYPE_GUID | GUID that uniquely identifies the table. Providers that do not use GUIDs to identify tables should return NULL in this column.
DESCRIPTION | DBTYPE_WSTR | Human-readable description of the table.

Default Sort Order: TABLE_TYPE, TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME.


VIEWS Rowset

The VIEWS rowset identifies the viewed tables defined in the catalog that are accessible to a given user.

The VIEWS rowset contains the following columns:

Column name | Type indicator | Description
TABLE_CATALOG | DBTYPE_WSTR | Catalog name. NULL if the provider does not support catalogs.
TABLE_SCHEMA | DBTYPE_WSTR | Unqualified schema name. NULL if the provider does not support schemas.
TABLE_NAME | DBTYPE_WSTR | View name.
VIEW_DEFINITION | DBTYPE_WSTR | View definition. This is a query expression.
CHECK_OPTION DBTYPE_BOOL Check option, that is:
  • VARIANT_TRUE, which indicates local update checking only.

  • VARIANT_FALSE, which indicates cascaded update checking.
IS_UPDATABLE DBTYPE_BOOL Specifies values as either of:
  • VARIANT_TRUE, which indicates that the view is updatable.

  • VARIANT_FALSE, which indicates that the view is not updatable.
DESCRIPTION | DBTYPE_WSTR | Human-readable description of the view.

Default Sort Order: TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME.


OLE DB Simple Provider Toolkit, Overview

General Overview

The OLE DB Simple Provider Toolkit from Microsoft® offers a framework that simplifies the task of writing OLE DB providers over simple data and exposing this data to any OLE DB consumer (such as ActiveX® Data Objects, or any OLE DB-compliant application or control). The Toolkit is specially designed for quickly building OLE DB providers over simple data sources (for example, in-memory arrays), allowing developers to expose OLE DB interfaces without spending the time needed to build an entire OLE DB provider from scratch. The Toolkit is language neutral, supporting implementations in Microsoft® Visual Basic®, Microsoft® Visual C++®, or Microsoft® Visual J++™.

The OLE DB Simple Provider Toolkit employs several new Microsoft component technologies, including:

  • OLE DB Simple Provider (OSP) interfaces. Also part of the Internet Explorer SDK, this set of easily implemented interfaces (APIs) is used by the Toolkit to map OLE DB requests to simple (for example, array-like) data and raise notifications on changes to the data.

  • Data Source Objects that allow clients written in Visual Basic, Visual J++, and Visual C++ to request access to the data for a particular OSP object and the associated notifications.

  • OLE DB Simple Provider DLL. This DLL (MSDAOSP.DLL) implements and exposes to data consumers the core OLE DB Rowset support over an object that has been coded to the OSP interface and object definition.

The general steps to building a Simple Provider involve writing your OSP data access code (OSP language reference is supplied with the Toolkit), implementing the data source object (which requests an OSP implementation), and registering the OLE DB Simple Provider DLL and OSP implementation DLL. For a more detailed description of these steps, see Building a Simple Provider: Step Summary.

Because the OLE DB Simple Provider Toolkit is designed to quickly build OLE DB providers over simple data, the providers built with this toolkit are less functional than a full OLE DB provider written in Visual C++. You may want to read about these limitations before using the Toolkit.


Technology Overview: Toolkit Components

Components

When you build an OLE DB provider with Microsoft's OLE DB Simple Provider Toolkit, you are actually assembling a provider from several Microsoft component technologies.

Providers built with the Toolkit consist of an OLE DB Simple Provider DLL (MSDAOSP.DLL) that interoperates with one or more implementations of Internet Explorer's OSP (for OLE DB Simple Provider) API. OSP supplies interfaces for basic provider capabilities over simple data, and the OLE DB Simple Provider expands the simple data provider functionality, adding comprehensive support for core OLE DB interfaces and OLE DB properties, with features such as bookmarks and unique row handles (for improved data identification), and limited data conversion capabilities.

The following figure shows, at a high level, the architecture of an OLE DB provider written with the Simple Provider Toolkit:

Following are descriptions of the main components shown in the figure and how they work:

ADO and Other OLE DB Consumers

These can be any OLE DB-compliant application or control on a computer on which the provider is registered. If you are using ADO Recordsets as a consumer, you code the provider identification information in the connection string and receive the data in the form of ADO Recordsets.

OLE DB Simple Provider DLL

The OLE DB Simple Provider DLL (MSDAOSP.DLL) acts as the go-between for the OSP Data Object and the consuming application or control. The DLL consists of code that understands OSP interface calls and does the following:

  • Exposes the OLE DB Data Source, Session, and Rowset objects to consumers such as ADO, data-binding controls, or OLE DB-compliant applications.

  • Uses either the msDataSourceObject function or IDataSource::getDataMember function to select the correct OSP objects.

  • Consumes OSP interfaces from a data source and exposes the data as an OLE DB Rowset.

  • Makes OLE DB notifications available. This information can be passed on to consuming objects, informing them whether data currently being manipulated has changed.

MSDAOSP.DLL assumes that OSP is correctly implemented (according to the OSP specification) and does no internal checking of OSP parameters or return codes.

OSP Data Object

To accommodate different programming language environments, multiple data sources and their OSP implementations, and coordinate different types of notification, the simple data provider architecture includes an abstraction called the OSP Data Object. This object needs to be coded and registered before it is instantiated and used with the OLE DB Simple Provider DLL (MSDAOSP.DLL).

An OSP Data Object implements one or more instances of OSP, with each instance giving access to a particular source of data. The OSP Data Object includes methods that allow the OLE DB Simple Provider DLL to request the desired OSP implementation and, thus, the desired data source:

  • If you are writing the provider in Visual C++ you will use the IDataSource interface and its getDataMember method to request an OSP.

  • If you are writing the provider in Visual Basic or Visual J++ you will use the msDataSourceObject method along with the appropriate IDispatch methods to request an OSP.

In both of the above implementations, the decision as to which OSP to request is based on the DataMember string that is passed in to the OSP Data Object. The format and syntax of that string is implementation-defined and must be specified by each OSP developer and to users of the OSP data source.

To expose a provider's full OLE DB functionality, the OSP Data Object needs to reference the OLE DB Simple Provider DLL (MSDAOSP.DLL). The developer can use one of two methods to accomplish this:

  • Complete the RegisterProvider.reg file supplied with the Toolkit.

  • Supply his or her own registration code for the OSP Data Object.

After the above step has been completed, the developer must register the OSP Data Object using regsvr32.exe (in Visual Basic and Visual C++ implementations) or javareg (in Visual J++).

The OSP Data Object also sends notifications to MSDAOSP.DLL, and data source notifications to MSDAOSP.DLL or external data consumers or controls (that is, applications outside the provider framework but able to access the same data sources). Where the data source notifications go to depends on which of the objects (MSDAOSP.DLL or an external data consumer) obtained the instance of the data source object.


Technology Overview: Notifications

Notifications

Notifications allow the provider layers to communicate changes in the underlying data source to other consumers or controls that register an interest. Notifications are required by the OLE DB Simple Provider DLL. The following figure shows how notifications are issued and received in the OLE DB Simple Provider framework:

There are three levels of notifications that occur in this framework:

OLE DB Notifications

These notifications are issued by the OLE DB Simple Provider DLL (MSDAOSP.DLL), and are consumed by any OLE DB control or application that wants to be notified about changes (through OLE DB) to a Rowset from other consumers. They are implemented through the IRowsetNotify OLE DB interface (for further information, refer to the online OLE DB Programmer's Reference supplied with the OLE DB SDK).

OSP Notifications

These notifications are issued by the OSP object and other objects that might be changing data owned by the OSP object. The notifications consist of methods belonging to the OSP OLEDBSimpleProviderListener interface. The listener for this set of notifications is the MSDAOSP.DLL. For example, suppose one OSP serves an array data source. If another object outside that OSP alters an element in the array data (using the SetVariant OSP method), then it must also issue the OSP notifications (for example, the OLEDBSimpleProviderListener::CellChanged method).

Data Source Notifications

These notifications are issued by the OSP Data Object. It is possible that other controls or applications not consuming data through the MSDAOSP.DLL may be refreshing or changing the same data source held by an OSP. In these cases, data source notifications come into play. For example, suppose a filter property on a control can alter array data exposed by an OSP. If the filter property is changed, then the code must issue data source notifications to indicate that the entire data source has changed.

Data source notifications are listened to by code that has requested data sources through the data binding interfaces (either IDataSource or msDataSourceObject through IDispatch). The listener depends on which object issued IDataSource::getDataMember. MSDAOSP.DLL will listen to these notifications (through DataSourceListener) when it requests an OSP. If a change to the data occurred, the Rowset being held by the OSP is in an invalid state ("zombie" state) and must be released. The application can make a fresh request to get the new data via IDataSource::getDataMember or msDataSourceObject.

Additional Information

OSP and data source listeners (OLEDBSimpleProviderListener and DataSourceListener) are not capable of vetoing or canceling a notification. Thus, developers do not need to check the results of a pre-notification to validate whether the data change has actually occurred.

MSDAOSP.DLL is capable of opening more than one rowset at a time on a simple provider, as long as the simple provider's Data Source and OSP objects support multiple listeners. It is the provider writer's responsibility to accomodate multiple listeners by:

  • maintaing a list of listeners when adding or removing Data Source or OSP listeners, and

  • notifying each listener in this list when firing Data Source or OSP notifications.


Exposing Data Through OLE DB

OLE DB is a strategic Microsoft technology that allows uniform data access to diverse data sources through a library of powerful data manipulation interfaces. OLE DB provides:

  • The ability to expose all types of data through a set of standard interfaces.

  • Seamless integration between consumer applications and controls, data providers, and diverse sources of data (including DBMS data).

  • Plug-and-play usage of service providers, such as query processors and cursor engines. Query processors that understand OLE DB can be "bolted onto" the data access environment, and serve multiple consumer applications and data providers.

The Microsoft OLE DB Simple Provider Toolkit allows developers to use these core OLE DB functionalities with simple data sources, without spending the time needed to build an entire OLE DB provider from scratch. By using the Toolkit, you not only enable OLE DB functionality and save time in the process, but also build a foundation for implementing data access strategies at a higher level.


ActiveX Data Objects

ActiveX Data Objects (ADO)

ActiveX Data Objects (ADO) enables you to write a client application to access and manipulate data in a data source through a provider. ADO is ideally suited to consume data exposed by OLE DB providers, such as those written with the Microsoft OLE DB Simple Provider Toolkit. ADO’s primary benefits are ease of use, high speed, low memory overhead, and a small disk footprint.

By using the Toolkit with ADO, you build a foundation for implementing flexible data access strategies at a higher level. For example, you can combine ADO's ease of application programmability with the Simple Provider Toolkit's ease of developing providers to quickly build end-to-end, single, or multi-tiered applications that address your corporate, intranet, Internet, or enterprise-wide data access needs.

For more information about ADO, including a detailed description of the object model, ADO properties and methods, and complete code samples, refer to the ADO help file supplied with the DA SDK.


OLE DB Rowsets

Rowsets are the central objects that enable all OLE DB data providers to expose data in tabular form. Conceptually, a Rowset is a set of rows in which each row contains columns of data. Base table providers present their data in the form of Rowsets, and query processors present the results of queries in the form of Rowsets.

For detailed information about OLE DB Rowsets, refer to the online OLE DB Programmer's Reference supplied with the OLE DB SDK.


Limitations

The OLE DB Simple Provider Toolkit exposes core OLE DB interfaces and capabilities, and is geared toward making less complex data environments OLE DB compliant. You should evaluate whether the Simple Provider Toolkit is capable of giving you the kinds of data and the functionality that you want in an OLE DB data provider. To guide you in this assessment, see the OLE DB Supported Interfaces section, which lists the interfaces exposed by the Simple Provider DLL.

If you require greater functionality than can be achieved with the Toolkit, or you are working over complex data, consider building an OLE DB provider using Microsoft Visual C++ rather than with the OSP interfaces. For example, providers built with the Toolkit do not support command capability. If your provider must accept commands, then you should consider building it using the OLE DB C++ interface. Although writing providers with Microsoft Visual C++ involves a greater development effort, the providers you write will afford you greater extensibility and a richer set of OLE DB data manipulation routines.


Building a Simple Provider: Step Summary

The OLE DB Simple Provider Toolkit simplifies the provider creation process. In most cases, you should only need to implement the OSP methods, code a data source object, fill out a copy of the supplied registration template, and register the OLE DB Simple Provider DLL (MSDAOSP.DLL) and the OSP Data Object (which will be a compiled DLL in Visual Basic and Visual C++, and a Java class in Visual J++). The other files supplied with each language implementation (header and library files) do not normally require modification.

Following is a summary of the main steps you need to follow when building a Simple Provider:

  1. Choose a language. You can write a Simple Provider using Visual Basic, Visual C++, or Visual J++. The general process of creating a provider is the same in each environment, although certain implementation details vary. The Toolkit contains a set of code samples for each visual language.

  2. Implement OSP. Most of your work will be implementing the OSP functions described in the OSP language reference. You can use the Toolkit's sample OSP implementation file in the language of your choice as a starting point, changing the implementations so that the methods work with your data.

  3. Implement a data source object. You need to implement a data source object (as part of the OSP Data Object) to expose the correct OSP implementation to the OLE DB Simple Provider DLL (MSDAOSP.DLL). Use either IDataSource (in Visual C++ implementations) or msDataSourceObject and the appropriate IDispatch methods (in Visual Basic or Visual J++ implementations).

  4. Implement notifications. Several of the OSP methods require notifications. Code your notifications in the OSP methods where applicable (for example, implementing the OSP notification methods aboutToChangeCell and cellChanged in the body of SetVariant).

  5. Register the OLE DB Simple Provider DLL (MSDAOSP.DLL). Register MSDAOSP.DLL using regsvr32.exe.

  6. Register the OSP Data Object. Once you have finished coding the OSP Data Object, you need to assign it a GUID and register it using regsvr32.exe (in Visual Basic and Visual C++ implementations) or javareg (in Visual J++).

  7. Choose a registration option.
    • If you are fully registering a Simple Provider, you need to fill out a copy of the supplied registration template (the RegisterProvider.reg file) supplied with the Toolkit, and import that file's settings into the Windows registry. This eliminates the need to write code that loads the OSP Data Object and hands it off to MSDAOSP.DLL.

    • If you want to write your own consumer code to instantiate the provider, do not use the RegisterProvider.reg template file. (See Registration Without a .reg File.)


Language Differences

Although the process of building a provider with the Microsoft OLE DB Simple Provider Toolkit is generally the same in Microsoft Visual Basic, Microsoft Visual C++, and Microsoft Visual J++, with the major portion of the work consisting of implementing the OSP functions, there are some implementation differences between the visual languages. The following topics provide implementation information and programming guidelines for building a Simple Provider in each of the Microsoft visual languages:

  • Visual Basic

  • Visual C++

  • Visual J++

The following topics provide information about the supplied code samples in each visual language:

  • Visual Basic Code Sample Information

  • Visual C++ Code Sample Information

  • Visual J++ Code Sample Information


Using the Code Samples

The Microsoft OLE DB Simple Provider Toolkit supplies sets of sample code for building a provider in three different languages. All three samples use the same external data source, Customers.txt (located in c:\Oledbsdk\Osptk\Samples), from which to load data. You can use these samples as a starting point for building your own provider by copying and editing the appropriate files, or by using the sample files as a model and creating your own provider files from scratch.

Select one of the choices below for information on the Simple Provider sample for that language.

  • Visual Basic

  • Visual C++

  • Visual J++


Visual Basic: Information and Guidelines

Here is a brief outline of the steps needed to build an OLE DB Simple Provider in Visual Basic.

Building a Simple Provider in Visual Basic 6

  1. Start Microsoft Visual Basic. From the File menu, select New Project. The New Project dialog box appears. Use this dialog box to select the ActiveX DLL icon.

  2. From the Project menu, select References. The References dialog box appears, listing the available references. Select the Microsoft OLE DB Simple Provider 1.5 Library check box.

    When you select Object Browser from the View menu or press F2 to display the Object Browser dialog box, the classes OLEDBSimpleProvider and OLEDBSimpleProviderListener should appear in the Classes pane of the Object Browser. You can focus on these classes and their members by selecting MSDAOSP in the Project/Library list.

  3. From the Project menu, select Add Class Module. The Add Class Module dialog box appears. Use this dialog box to select and open the Class Module icon. In the Procedure (Declarations) of the Object (General) inside the Code window, type the following as your first line of code:

    Implements OLEDBSimpleProvider

  4. Now you can develop your own simple provider class. Use the OSP language reference supplied with the Toolkit to implement all the OSP methods. You can view the sample code in the file MyOSPObject.cls as a reference.

  5. You also need to add a Data Source class and its GetDataMember function. Add a second class to your project and in its Properties window, select DataSourceBehavior = "1 - vbDataSource". In the Object drop-down box, select Class. In the Procedure drop-down box, select GetDataMember. Implement the GetDataMember by using the DataMember input argument to determine a simple provider object to return. For example,
    Private Sub Class_GetDataMember(Datamember As String, Data As Object)
Dim MyOSP As New MyOSPObject
Set Data = MyOSP
End Sub
  6. From the File menu, select Make ProjectName.dll… to build and register your provider.

Registration

As part of the information required to register the provider, you need a ProgID for the OSP Data Object.

In Visual Basic, the ProgID is the project name followed by the name of the data source object class or control name on which the OSP Data Object is implemented. The two names are separated with a "." (period).

For example, suppose you have a project named "MyOSPProject", and a class that implements the msDataSourceObject method named "MyOSPClass". The ProgID will be "MyOSPProject.MyOSPClass". The Version-Independent ProgID will be " MyOSPProject.MyOSPClass.1".

Programming Notes for Visual Basic 5 Users

  • In Visual Basic 5 implementations, use msDataSourceObject and the appropriate IDispatch methods (such as Invoke) to expose the correct OSP implementation to the Simple Provider DLL (MSDAOSP.DLL).

  • When coding data source notifications in Visual Basic 5, use the addDataSourceListener and removeDataSourceListener methods with IDispatch.

  • Make sure to code the implementation for the addDataSourceListener method in your OSP Data Object. Implementing msDataSourceObject without implementing addDataSourceListener will result in a run-time error when your data source is being used.

  • For Visual Basic 5 users, Step 5 in the previous procedure is:

    You also need to implement two data source functions. Add a second class module and implement msDataSourceObject and addDataSourceListener. For example:

    Public Function msDataSourceObject(DataMember$)

    AS OLEDBSimpleProvider

    You can view the sample code in the file MyDataSource.cls as a reference.


Visual Basic: Code Sample Information

This sample uses class properties introduced in Visual Basic 6. It will not work with earlier versions of Visual Basic. For more information on building simple providers with Visual Basic 5, refer to the previous section, Visual Basic.

The Visual Basic Simple Provider code sample is located in ..\samples\osp\vb and consists of the following files:

File Name | Description
SampleOSP.vbp | Visual Basic project file for the sample.
MyDataSource.cls | Class module for msDataSourceObject.
MyOSPObject.cls Class module containing the following:
  • Routines to create an internal table, load data into the table from an external file, and save the data.

  • OSP method implementations.
SampleOSP_VB.reg | Copy of the RegisterProvider.reg file for use with the Visual Basic sample.


Visual C++: Information and Guidelines

Here is a brief outline of the steps needed to build the OSP component in Visual C++:

  • Create an Automation object with standard registration.

  • Implement IDataSource on the object.

  • Implement the methods for OLEDBSimpleProvider with the object (at least once).

Registration

In Visual C++, you create your own ProgID for the OSP Data Object, and use it when registering the OLE DB provider. Because you control the DLLRegisterServer function, you can use it to write the registry entries instead of running a copy of the supplied RegisterProvider.reg file.

Programming Notes

  • When compiling in Visual C++, make sure to point to the Include directory where the OLE DB and OLEDBSimpleProvider header files are located.

  • When linking in Visual C++, make sure to add the path for the LIB files so you can link the LIB file containing the IIDs for the Simple Provider interfaces and classes.

  • In Visual C++ implementations, use IDataSource to expose the correct OSP implementation to the Simple Provider DLL (MSDAOSP.DLL).

  • When coding data source notifications in Visual C++, use the addDataSourceListener and removeDataSourceListener methods of IDataSource to add and remove listeners.


Visual C++: Code Sample Information

The Visual C++ OSP Simple Provider sample is set up to have MyOSPObject as the implementation of the Sample Provider, and MyDataSource as the implementation of the Data Source object.

The Microsoft Visual C++ Simple Provider code sample is located in ..\samples\osp\vc and consists of the following files:

File Name | Description
MyOSPObject.h | Class and method definitions for MyOSPObject.
MyOSPObject.cpp Implementation code for MyOSPObject. Contains the following:
  • Constructor and destructor, initialization routines, QueryInterface of MyOSPObject.

  • Routines to create an internal table, load data into the table from an external file, and save the data.

  • OSP method implementations.
MyDataSource.h | Class and method definitions for MyDataSource.
MyDataSource.cpp Implements the MyDataSource object and its IDataSource methods. Contains the following:
  • Constructor and destructor, initialization routines, QueryInterface of MyDataSource.

  • IDataSource method implementations, including getDataMember, which gets the OSP implementation.
MyClassFactory.h | Contains the class definitions for MyClassFactory and the DLL entry points.
MyClassFactory.cpp | The sample's implementation of the IClassFactory interface. Implements the main DLL entry point.
SampleOSP_VC.def | Names the overall DLL and lists the entry points.
SampleOSP_VC.dsp | Visual C++ project file for the sample.
SampleOSP_VC.reg | Copy of the RegisterProvider.reg file for use with the Visual C++ sample.
Common.cpp | Common VC++ routines used for this sample.
Common.h | Set of common VC++ macros used for this sample.


Visual J++: Information and Guidelines

Here is a brief outline of the steps needed to build the OSP component in Visual J++:

  • Create a Java application.

  • Implement IDataSource on the Java application.

  • Implement the methods for OLEDBSimpleProvider with the object (at least once).

Registration

In Visual J++, you create your own ProgID for the OSP Data Object. Then use javareg to register the OSP Data Object. You need to use the ProgID you created for the OSP Data Object when registering the provider.

Programming Notes

  • In Visual J++ implementations, use IDataSource and its methods (such as GetDataMember) to expose the correct OSP implementation to the Simple Provider DLL (MSDAOSP.DLL).

  • When coding data source notifications in Visual J++, use the addDataSourceListener and removeDataSourceListener methods with IDataSource to add and remove DataSource listeners.

  • jactivex is available in the Microsoft SDK for Java 2.0, and is now included with Visual J++.


Visual J++: Code Sample Information

The Visual J++ Simple Provider code sample is located in ..\samples\osp\vj and consists of the following files:

File Name | Description
SampleOSP_VJ.dsp | Visual J++ project file for the sample.
MyOSPObject.java Java class module containing the following:
  • Routines to create an internal table, load data into the table from an external file, and save the data.

  • OSP method implementations.
MyDataSource.java | Java class for msDataSourceObject.
SampleOSP_VJ.reg | Copy of the RegisterProvider.reg file for use with the Visual J++ sample.

You will need to generate the Java interface definitions from SimpData.tlb and Msdatsrc.tlb (supplied with the Toolkit) for use when compiling your Java application. Use the tool appropriate for your version of Visual J++ (such as Javatlb) to generate these interface definitions.


Registering Providers

The OLE DB Simple Provider Toolkit includes a registration template (RegisterProvider.reg) that you fill in before registering the OLE DB provider. The template lets you build a .REG file that allows the OLE DB Simple Provider DLL (MSDAOSP.DLL) to recognize the OSP Data Object, which must be registered separately using regsvr32.exe. Using the RegisterProvider.reg file contains all the information you need to know about how to complete the template and import those settings into the Windows® registry.

There are two suggested methods for registering an OLE DB provider built with the Toolkit. Click on either for a listing of the registry entries for each.

  • Full registration. When you register the provider with this method, the provider is recognized and functions most like any OLE DB provider on a computer.

  • Registration without an OSP entry. When you register the provider using this method, you register it without a special entry for the OSP Data Object.


Using the RegisterProvider.reg File

The RegisterProvider.reg file is supplied with the OLE DB Simple Provider Toolkit to automate the process of registering a provider. Use this file only when fully registering a provider (that is, including a registration entry for the OSP Data Object).

Use this topic as a guide to entering the correct information in the RegisterProvider.reg file. Once you are finished completing the template, you should rename it, and double-click the file to import those settings into the Windows registry.

Running the registry file is necessary so that the Simple Provider DLL recognizes the OSP Data Object. You must also register the OSP Data Object separately using regsvr32.exe, or javareg if you coded the object in Microsoft Visual J++.

If you are coding in Microsoft Visual C++, you can add the required registry entries by using DLLRegisterServer instead of running RegisterProvider.reg.

You need to supply the following values before running RegisterProvider.reg:

  • ProgID for OLE DB Provider. A value you provide to identify your OLE DB provider in the registry.

  • Name of OLE DB Provider. The name that your provider will be listed under. It is also created by you. (Note that the name of the provider can be localized, because the ProgID references the object rather than the name. The ProgID is not localized.)

  • GUID for OLE DB Provider. This is a unique GUID that identifies the OLE DB provider you have created. You should create one using a GUID generator.

  • ProgID for OSP Data Object. You determine this value from the OSP Data Object you developed; see the section in each of the development tools as to how this is determined. (For example, in Microsoft Visual Basic, it will be the project name followed by the name of the data source object class or control name. The names are separated by a "." (period).)

Following is a copy of the registration template, and following that, an illustration of a completed registration template showing a set of example values.

Template ========================================================>
REGEDIT4

[HKEY_CLASSES_ROOT\<INSERT:  ProgID for OLE DB Provider>]
@="<INSERT:  Name of OLE DB Provider>"

[HKEY_CLASSES_ROOT\<INSERT: ProgID for OLE DB Provider>\CLSID]
@="<INSERT:  GUID for OLE DB Provider>"

[HKEY_CLASSES_ROOT\CLSID\<INSERT: GUID for OLE DB Provider>]
@="<INSERT: ProgID of OLE DB Provider>"

[HKEY_CLASSES_ROOT\CLSID\<INSERT:  GUID for OLE DB
Provider>\InprocServer32]
@="<INSERT:  Path to OLE DB Directory example -->
c:\\Program Files\\Common Files\\System\\OLE DB\\>MSDAOSP.DLL"
"ThreadingModel"="Both"

[HKEY_CLASSES_ROOT\CLSID\<INSERT:  GUID for
OLE DB Provider>\ProgID]
@="<INSERT:  ProgId of OLE DB Provider>.1"

[HKEY_CLASSES_ROOT\CLSID\<INSERT:  GUID for
OLE DB Provider>\VersionIndependentProgID]
@="<INSERT:  ProgId of OLE DB Provider>"

[HKEY_CLASSES_ROOT\CLSID\<INSERT:  GUID for
OLE DB Provider>\OLE DB Provider]
@="<INSERT:  Name of OLE DB Provider>"

[HKEY_CLASSES_ROOT\CLSID\<INSERT:  GUID for
OLE DB Provider>\OSP Data Object]
@="<INSERT:  ProgID for OSP Data Object>"

==========================================>
Example Values
==========================================>
ProgID for OLE DB Provider = TestPWProv
Name of OLE DB Provider = Microsoft Test OLE DB Provider for OSP
GUID for OLE DB Provider = {6B94E051-E170-11d0-94EA-00C04FB66A50}
ProgId for OSP Data Object= MyOSPProject.OSPClass

==========================================>
Example
==========================================>
REGEDIT4

[HKEY_CLASSES_ROOT\TestPWProv]
@="Microsoft Test OLE DB Provider for OSP"

[HKEY_CLASSES_ROOT\TestPWProv\CLSID]
@="{6B94E051-E170-11d0-94EA-00C04FB66A50}"

[HKEY_CLASSES_ROOT\CLSID\
{6B94E051-E170-11d0-94EA-00C04FB66A50}]
@="TestPWProv"

[HKEY_CLASSES_ROOT\CLSID\
{6B94E051-E170-11d0-94EA-00C04FB66A50}\InprocServer32]
@="c:\\Program Files\\Common Files\\
System\\OLE DB\\MSDAOSP.DLL"
"ThreadingModel"="Both"

[HKEY_CLASSES_ROOT\CLSID\{6B94E051-E170-11d0-94EA-00C04FB66A50}\ProgID]
@="TestPWProv.1"

[HKEY_CLASSES_ROOT\CLSID\{6B94E051-E170-11d0-94EA-00C04FB66A50}
\VersionIndependentProgID]
@="TestPWProv"

[HKEY_CLASSES_ROOT\CLSID\{6B94E051-E170-11d0-94EA-00C04FB66A50}
\OLE DB Provider]
@="Microsoft Test OLE DB Provider for OSP"

[HKEY_CLASSES_ROOT\CLSID\{6B94E051-E170-11d0-94EA-00C04FB66A50}
\OSP Data Object]
@="MyOSPProject.OSPClass"


Full Registration

To fully register a provider, you include a special entry for the OSP Data Object in the registration template file, RegisterProvider.reg. Including this entry eliminates the need to write code that loads the OSP Data Object and hands it off to the MSDAOSP.DLL.

Registration Steps

Perform the following steps to fully register a provider:

  1. Register the Simple Provider DLL (MSDAOSP.DLL) with regsvr32.exe.

  2. Register the OSP object. (For Visual C++ and Visual J++ users only; Visual Basic users will omit this step because that environment automatically registers the DLL.)
    • If you are using Visual C++ and have compiled and linked your OSP object (the DLL containing the OSP implementations and data source object), register it using regsvr32.exe.

    • If you are using Visual J++, register the Java class code for the OSP object using javareg.
  3. Copy, rename, and complete the RegisterProvider.reg template file.

  4. Double-click the RegisterProvider.reg file to import the settings into the Windows registry.

Windows Registry Entries

Following are the entries that are written to the Windows registry after performing steps 1, 2, and 4 in the previous section.

The first group of registry entries, shown below, consists of entries listed under the CLSID subkey that are generated by running regsvr32.exe on MSDAOSP.DLL (step 1).

HKEY_CLASSES_ROOT
CLSID
{CLSID of MSDAOSP.DLL}
InprocServer32
(Default) = "C:\PROGRAMFILES\COMMON FILES\
SYSTEM\OLE DB\MSDAOSP.DLL”
OLE DB Provider
(Default) = "Microsoft OLE DB Simple Provider”
ProgID
(Default) = "MSDAOSP.1”
VersionIndependentProgID
(Default) = "MSDAOSP”

The second group of registry entries, shown below, consists of entries listed under the CLSID subkey that are generated by running regsvr32.exe on the provider DLL (step 2).

HKEY_CLASSES_ROOT
CLSID
{CLSID of OSP Data Object }
InprocServer32
(Default) = "Path of OSP DataObject DLL”
Threading Model = “Both”
ProgID
(Default) = "ProgID for the OSP Data Object”
VersionIndependentProgID
(Default) = "Version independent ProgID for this provider”

The third group of registry entries, shown below, lists the entries under the CLSID subkey that are generated after running the registration template file (step 4).

HKEY_CLASSES_ROOT
CLSID
{CLSID of your OLE DB Provider}
InprocServer32
(Default) = "C:\PROGRAMFILES\COMMON FILES\
SYSTEM\OLE DB\MSDAOSP.DLL”
OLE DB Provider
(Default) = "YOUR SIMPLE PROVIDER NAME”
ProgID
(Default) = "YOUR_PROGID.1”
VersionIndependentProgID
(Default) = "YOUR_PROGID”
OSP Data Object
(Default) = "ProgID of OSP Data Object”


Registration Without a .reg File

You can register a provider without a special entry for the OSP Data Object. In this case, you do not use the RegisterProvider.reg template file supplied with the OLE DB Simple Provider Toolkit.

If you choose this method of registration, you need consumer code to instantiate the provider. The provider must be instantiated with CoCreateInstance, using the GUID of the MSDAOSP.DLL. At run time, the ProgID of the OSP Data Object must be handed off to direct the DLL to the correct OSP object through the DBPROP_INIT_DATASOURCE property.

Registration Steps

Perform the following steps to register a provider without using the RegisterProvider.reg file:

  5. Register the Simple Provider DLL (MSDAOSP.DLL) with regsvr32.exe.

  6. Register the OSP object. (For Visual C++ and Visual J++ users only; Visual Basic users will omit this step because that environment automatically registers the DLL.)
    • If you are using Visual C++ and have compiled and linked your OSP object (the DLL containing the OSP implementations and data source object), register it using regsvr32.exe.

    • If you are using Visual J++, register the Java class code for the OSP object using javareg.

Windows Registry Entries

Following are the entries that are written to the Windows registry after performing steps 1 and 2 in the previous section.

The first group of registry entries, shown below, consists of entries listed under the CLSID subkey that are generated by running regsvr32.exe on MSDAOSP.DLL (step 1).

HKEY_CLASSES_ROOT
CLSID
{CLSID of MSDAOSP.DLL}
InprocServer32
(Default) = "C:\PROGRAMFILES\COMMON FILES\
SYSTEM\OLE DB\MSDAOSP.DLL”
OLE DB Provider
(Default) = " Microsoft OLE DB Simple Provider”
ProgID
(Default) = "MSDAOSP.1”
VersionIndependentProgID
(Default) = "MSDAOSP”

The second group of registry entries, shown below, consists of entries listed under the CLSID subkey that are generated by running regsvr32.exe on the provider DLL (step 2).

HKEY_CLASSES_ROOT
CLSID
{CLSID of OSP Data Object }
InprocServer32
(Default) = "Path of OSP Data Object DLL”
Threading Model = “Both”
ProgID
(Default) = "ProgID specified for the OSP Data Object”
VersionIndependentProgID
(Default) = "Version independent ProgID for this provider”


Registering Multiple Providers

Each provider on a computer will have a different ProgID to point to the same OLE DB Simple Provider DLL (MSDAOSP.DLL). This assignment of ProgIDs is necessary to call the correct method implementations.

An example of the registry entries under the CLSID key that the developer needs to supply for two providers built with the Toolkit, with both residing on the same computer, is shown below. (The redundant registry entries listed under the ProgID and Version-Independent ProgID name keys are not shown.)

//************************************************
//****** REG ENTRIES FOR PROVIDER 1 **********
//************************************************

CLSID
   {CLSID FOR PROV #1}
      InprocServer32
         (Default) = "C:\PROGRAM FILES\COMMON FILES\
                        SYSTEM\OLE DB\MSDAOSP.DLL"
         Threading Model = "Both"
      ProgID
         (Default) = {PROGID FOR PROVIDER #1}
      VersionIndependentProgID
         {VERSION INDEPENDENT PROGID FOR PROVIDER#1}
      OSP Data Object
         {PROGID FOR OSP DATA OBJECT #1}

//************************************************
//****** REG ENTRIES FOR PROVIDER 2 **********
//************************************************

CLSID
   { CLSID FOR PROV #2}
      InprocServer32
         (Default) = "C:\PROGRAM FILES\COMMON FILES\
                        SYSTEM\OLE DB\MSDAOSP.DLL"
         Threading Model = "Both"
      ProgID
         (Default) = {PROGID FOR PROVIDER #2}
      VersionIndependentProgID
         {VERSION INDEPENDENT PROGID FOR PROVIDER#2}
      OSP Data Object
         {PROGID FOR OSP DATA OBJECT #2}

//***************** END **************************


Registration Problems

Following are problems that can occur if a Simple Provider is not registered correctly:

  • If no registration settings have been entered, or no data source initialization property has been set, IOpenRowset::OpenRowset returns the error DB_E_NOTABLE. This indicates that no table exists in the current data source.


OSP Language Reference

OSP (for OLE DB Simple Provider) is a set of Internet Explorer APIs that includes a limited number of COM interfaces and associated methods. It is designed to let you easily expose simple data (such as in-memory arrays of string and variant data types) to data-binding controls in HTML pages, Internet Explorer HTML forms, and consumer applications.

OSP API Documentation

The OSP API interfaces and methods documented here are also described in the Internet Explorer SDK. However, this version of the OSP documentation includes additional information specific to the Toolkit's usage of OSP.

OSP and the Toolkit

The Toolkit uses the OSP API as an integral part of its OLE DB provider architecture. Think of it using OSP as a building block by which OLE DB functionality is exposed over your simple data. As a provider writer using this toolkit, you use OSP methods to:

  • Expose a data source. You must implement a separate OSP for each data source you want to expose.

  • Build OSP Data Objects (ODOs). These data objects do the following:
    • Encapsulate one or more OSPs.

    • Expose the correct OSP instance to the OLE DB Simple Provider DLL (using either the IDataSource interface or the msDataSourceObject method).

    • Send notifications to the OLE DB Simple Provider DLL.

OSP Interfaces

OSP defines two interfaces (click on either to jump directly to its reference page):

  • OLEDBSimpleProvider, which implements the methods used for data access.

  • OLEDBSimpleProviderListener, which specifies the methods to be implemented by the consumer for receiving notifications of data changes.

Method Categories

The OSP methods you can implement with the OLE DB Simple Provider Toolkit can be categorized as follows:

Category | Description
Schema | Obtain basic metadata information about the underlying rows and columns.
Data Access via Variants | Retrieve and set variant values in the data set.
Insert/Deletion | Insert/delete rows and columns into the data set.
Find | Implement basic search functionality across the data.
Localization | Implement functionality for data conversions based on country.
Event handler | Register (and unregister) an event handler, which can be notified of changes to cells, rows, and columns.
Asynchronous data population | Allows data to populate a target application or control asynchronously.
Notifications | Notifies, through an event handler, changes to cells, rows, and columns.


OLEDBSimpleProvider Interface

OLEDBSimpleProvider

OLEDBSimpleProvider is the OSP interface that implements the methods used for data access. It specifies methods for:

  • Adding and deleting rows.

  • Setting the value of cells.

  • Searching through a data set.

  • Populating or retrieving a data set asynchronously.

  • Implementing or removing an event handler.

OLEDBSimpleProvider is identified by the following IID:

{E0E270C0-C0BE-11d0-8FE4-00A0C90A6341}

Data References

When using OLEDBSimpleProvider methods, you reference the exposed array-like data by using row and column indices. Normally, indices begin with a value of 1. Following are two index values with special meaning:

Index Value | Description
-1 | Wildcard value. Refers to an unknown value or all values. For example, a row value of -1 and a column value of 2 refers to all the rows in column 2 (not including the row header information). Note The getVariant and setVariant methods cannot use a value of -1 as an argument for the iRow and iColumn arguments.
0 | Reserved for label information in the case of a row, and header information in the case of a column.

Method List

Following is a list of OLEDBSimpleProvider methods that the OLE DB Simple Provider Toolkit uses, a short description of each, and the category each method belongs to. Click on any method name for detailed information about its implementation.

Method | Description | Category
getRowCount | Returns the number of rows. | Schema
getColumnCount | Returns the number of columns. | Schema
getRWStatus | Returns the read/write status of a column, row, cell, or data set. | Schema
getVariant | Retrieves the value of the cell at the indicated row and column into the pre-allocated variant. | Variant
setVariant | Sets the value of the cell at the row and column indicated to the value of the variant. | Variant
insertRows | Inserts a specified number of empty rows, starting at a specified location. | Insertion and deletion
deleteRows | Deletes a specified number of rows, starting at a specified location. | Insertion and deletion
find | Searches for the specified set of values, starting at a specified location. | Search
getLocale | Indicates the locale of the data. | Localization
isAsync | Indicates whether data is being populated asynchronously. | Asynchronous data population
getEstimatedRows | Returns the total estimated number of rows in the data set. | Asynchronous data population
stopTransfer | Requests that the OSP discontinue asynchronous transfer of data. | Asynchronous data population
addOLEDBSimpleProviderListener | Registers an event handler interface, making it possible to receive notifications of changes to data. | Event handler
removeOLEDBSimpleProviderListener | Unregisters a specified event handler interface. | Event handler


OLEDBSimpleProvider::getRowCount

Returns the number of rows in the data set.

HRESULT getRowCount (
LONG*pcRows);

Parameters

pcRows

[out]
A pointer to the number of rows returned by the pcRow argument. The number does not include the row header information.

Return Codes

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.


OLEDBSimpleProvider::getColumnCount

Returns the number of columns in the data set.

HRESULT getColumnCount (
LONG*pcColumns);

Parameters

pcColumns

[out]
A pointer to the number of columns returned by the pcColumns argument. The number does not include the column label information.

Return Codes

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.


OLEDBSimpleProvider::getRWStatus

Returns the read/write status of a specified column, row, cell, or data set. If one or both of iRow and iColumn have a value of -1, then the read/write status is returned for the entire column, row, or complete data set.

getRWStatus uses the following enumeration:

typedef enum OSPRW {
OSPRW_READONLY = 0,
OSPRW_DEFAULT = 1,
OSPRW_READWRITE = 1,
OSPRW_MIXED = 2,
};

where

Value | Meaning
OSPRW_READONLY | Cell, row, column, or data set is read-only.
OSPRW_DEFAULT OSPRW_READWRITE | Cell, row, column, or data set can be modified (the default).
OSPRW_MIXED | Cell, row, column, or data set read/write status is unknown or row, column, or data set is of mixed status.

HRESULT getRWStatus (
LONG iRow,
LONG iColumn,
OSPRW*prwStatus);

Parameters

iRow

[in]
Position of the specified row. A value of -1 indicates all rows.

iColumn

[in]
Position of the specified column. A value of -1 indicates all columns.

prwStatus

[out]
Enumerated value indicating the returned read/write status of a specified cell, row, column, or data set (see above table for descriptions).

Return Codes

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

Notes

  • If the *prwStatus is OSPRW_MIXED, most callers will treat it as read/write (OSPRW_READWRITE). A "lazy" provider should return a *prwStatus value of OSPRW_MIXED.


OLEDBSimpleProvider::getVariant

Retrieves the value of the cell at the indicated row and column into the pre-allocated variant supplied in pVar.

getVariant uses the following enumeration:

typedef enum OSPFORMAT {
OSPFORMAT_RAW = 0,
OSPFORMAT_DEFAULT = 0,
OSPFORMAT_FORMATTED = 1,
OSPFORMAT_HTML = 2,
};

where

Value | Meaning
OSPFORMAT_RAW OSPFORMAT_DEFAULT | Underlying column type should be used to set or get the value (the default).
OSPFORMAT_FORMATTED | Underlying column type should be converted to a string, and the string returned as a BSTR within the variant. It is mandatory for OSP developers to support the fFormatted argument of getVariant with this value.
OSPFORMAT_HTML | Underlying column type should be converted to an HTML string.

HRESULT getVariant (
LONG iRow,
LONG iColumn,
OSPFORMAT fFormatted,

VARIANT*pVar);

Parameters

iRow

[in]
Position of the specified row.

iColumn

[in]
Position of the specified column.

fFormatted

[in]
Enumerated value that determines the format in which to return the cell value's underlying column type (see previous table and Notes).

pVar

[out]
Based on the value specified in the pVar parameter, pVar will contain either:

  • A value in the underlying type of the column.

  • A string corresponding to the underlying type of the column.

  • An HTML fragment corresponding to the underlying type of the column.

Return Codes

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

Notes

  • getVariant does not support references.

  • For the fFormatted argument, it is mandatory for OSP developers to support getVariant with a value of OSPFORMAT_FORMATTED. The OSP implementation is responsible for type conversion, and in the case of Boolean values, the correctly formatted string must be either "True" or "False".

  • When OSPFORMAT_HTML is requested, the provider may, at its option, return a plain string (for example, unadorned with HTML tags).

  • A data value of NULL is indicated by returning either:
    1. S_OK and a Variant of type VT_NULL, or

    2. S_FALSE and pVar will be ignored

    for any of the fFormatted types.

  • Do not use a value of -1 as an argument with the iRow and iColumn arguments of getVariant. You must retrieve table cells individually; they cannot be retrieved a row or column at a time.


OLEDBSimpleProvider::setVariant

Sets the value of the cell at the row and column indicated to the value of the variant supplied in pVar in the type requested by the value of fFormatted.

setVariant uses the following enumeration:

typedef enum OSPFORMAT {
OSPFORMAT_RAW = 0,
OSPFORMAT_DEFAULT = 0,
OSPFORMAT_FORMATTED = 1,
OSPFORMAT_HTML = 2,
};

where

Value | Meaning
OSPFORMAT_RAW OSPFORMAT_DEFAULT | Underlying column type should be used to set or get the value (the default).
OSPFORMAT_FORMATTED | Underlying column type should be converted to a string, and the string returned as a BSTR within the variant.
OSPFORMAT_HTML | Underlying column type should be converted to an HTML string.

HRESULT setVariant (
LONG iRow,
LONG iColumn,
OSPFORMAT fFormatted,

VARIANT*pVar);

Parameters

iRow

[in]
Position of the specified row.

iColumn

[in]
Position of the specified column.

fFormatted

[in]
Enumerated value that determines the format in which to return the cell value's underlying column type (see above table).

pVar

[in/out]
Based on the value specified in the pVar parameter, pVar will contain either:

  • A value with the underlying type of the column.

  • A string corresponding to the underlying type of the column.

  • An HTML fragment corresponding to the underlying type of the column.

Return Codes

S_OK
The method succeeded.

E_FAIL
The value could not be set, or the provider was read-only.

Notes

  • setVariant does not support references.

  • setVariant requires the following notifications: aboutToChangeCell and cellChanged.

  • With any formatting option, it is the provider's responsibility to perform the appropriate coercion to the underlying type of the column. In cases where the coercion cannot be successfully completed, an appropriate error code should be returned.

  • With the value OSPFORMAT_FORMATTED, the provider is free to interpret the input string however it sees fit. Alternatively, the provider may attempt to parse the input string and heuristically determine an appropriate data type and value to place into the cell; for example, when updating formatted spreadsheet cells.

  • When OSPFORMAT_HTML is requested, the provider may, at its option, return a plain string (for example, unadorned with HTML tags).

  • A data value of NULL is indicated by passing a Variant of type VT_NULL in pVar for any of the fFormatted types.

  • Do not use a value of -1 as an argument with the iRow and iColumn arguments of setVariant. You must set table cells individually; they cannot be set a row or column at a time.


OLEDBSimpleProvider::deleteRows

Deletes a specified number of rows, beginning at iRow, from the data set.

HRESULT deleteRows (
LONG iRow,
LONG cRows,
LONG*pcRowsDeleted);

Parameters

iRow

[in]
Position of the first row at which the deletion begins. A value of -1 indicates all rows.

cRows

[in]
Number of rows to be deleted.

pcRowsDeleted

[out]
Actual number of rows that were successfully deleted from the data set.

Return Codes

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

Notes

  • deleteRows requires the following notifications: aboutToDeleteRows and deletedRows.


OLEDBSimpleProvider::insertRows

Inserts a specified number of rows, beginning at iRow, into the data set.

HRESULT insertRows (
LONG iRow,
LONG cRows,
LONG*pcRowsInserted);

Parameters

iRow

[in]
Position of the first row of the data set at which the insertion begins.

cRows

[in]
Number of rows to be inserted.

*pcRowsInserted

[out]
Actual number of rows that were successfully inserted into the data set.

Return Codes

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

Notes

  • insertRows requires the following notifications: aboutToInsertRows and insertedRows.


OLEDBSimpleProvider::find

Searches for a set of values specified in varSval, in column iColumn starting at iRow. Upon successful completion, the row number with the matching value is returned in piRowFound.

HRESULT find (
LONG iRowStart,
LONG iColumn,
VARIANT varSval,
OSPFIND findFlags,
OSPCOMP compType,
LONG*piRowFound);

Parameters

iRowStart

[in]
Position of the first row of the data set at which the search operation begins.

iColumn

[in]
Position of the first column of the data set at which the search operation begins.

varSvalt

[in]
The target value or values of the search operation.

findFlags

[in]
An enumeration consisting of the following values:

Value | Meaning
OSPFIND_DEFAULT = 0 | Specifies that the search operation should be in ascending order through rows in a case-insensitive manner.
OSPFIND_UP = 1 | Specifies that the search operation should be in descending order through rows.
OSPFIND_CASESENSITIVE = 2 | Specifies case-sensitivity for the search.
OSPFIND_UPCASESENSITIVE = 3 | Specifies that the search operation should be in ascending order through rows in a case-sensitive manner.

compType

[in]
An enumeration consisting of the following values:

Value | Meaning
OSPCOMP_EQ = 1 OSPCOMP_DEFAULT = 1 | Specifies that the search is for the first value equal to varSval.
OSPCOMP_LT = 2 | Specifies that the search is for the first value less than varSval.
OSPCOMP_LE = 3 | Specifies that the search is for the first value less than or equal to varSval.
OSPCOMP_GE = 4 | Specifies that the search is for the first value greater than or equal to varSval.
OSPCOMP_GT = 5 | Specifies that the search is for the first value greater than varSval.
OSPCOMP_NE = 6 | Specifies that the search is for the first value not equal to varSval.

piRowFound

[out]
The number of the row containing the matching value of the search. If no corresponding match is found, find should return the constant -1.

Return Codes

S_OK
The method succeeded.

DISP_E_TYPEMISMATCH

The provider was unable to match the type of the varSval to the underlying type of the column.

E_FAIL
A provider-specific error occurred.

Notes

  • find does compares using the native type of the underlying column. Providers should return DISP_E_TYPEMISMATCH when they are unable to convert the varSval to the underlying type of the column.

  • Comparing string values always compares values as they would be returned from a call to getVariant with OSPFORMAT_FORMATTED. Providers should not coerce values of varSval obtained from a previous call to getVariant with OSPFORMAT_HTML.


OLEDBSimpleProvider::getLocale

This method returns a string that describes the locale (country of origin), as specified in Internet RFC 1766, of the data set. For example, the locale could be “en-us” which is equivalent to “English (United States)”.

HRESULT getLocale (
BSTR*pbstrLocale);

Parameters

pbstrLocale

[out]
A string specifying the locale (LCID) of the data set.

Return Codes

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

Notes

  • If you have IE4 installed, your registry key “HKEY_CLASSES_ROOT\MIME\Database\Rfc1766” contains the legal values for locale.


OLEDBSimpleProvider::isAsync

This method indicates whether provider data will populate the consumer application or control asynchronously.

HRESULT isAsync (
BOOL*pbAsynch);

Parameter

*pbAsynch

[out]
Boolean value indicating that data is returned asynchronously (1) or not (0).

Return Codes

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

Notes

  • isAsync should always return the appropriate state; it should never block.


OLEDBSimpleProvider::getEstimatedRows

Returns an estimate of the total number of rows in the data set. With an asynchronous provider, callers can use this method with getRowCount to get an estimate of the percentage of data received.

HRESULT getEstimatedRows (
LONG*pcRows);

Parameter

pcRows

[out]
Estimated number of rows in the data set.

Return Codes

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

Notes

  • When an estimate of the number of rows cannot be provided, the provider should return a value of -1.

  • Synchronous providers should not block on a call to getEstimatedRows. They should either return a value of -1 to indicate that they cannot provide an estimate, or they should provide the actual row count (the same as would be obtained through getRowCount).

  • Providers should not return the final row count prior to issuing transferComplete.


OLEDBSimpleProvider::stopTransfer

This method requests that OSP discontinue the asynchronous transfer of data.

HRESULT stopTransfer ( );

Return Codes

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

Notes

  • Providers should make every effort to expose the data transferred prior to receiving this request. The provider must expose a valid, consistent OSP interface for such data, including an appropriate count of the columns and rows, and the read/write status.

  • Providers that cannot expose the data prior to receiving this request should discontinue the transfer of data and fail all subsequent calls to the OSP interface.

  • stopTransfer requires the transferComplete notification if provider data will populate the consumer application or control asynchronously.


OLEDBSimpleProvider::addOLEDBSimpleProviderListener

Registers the named event handler interface to receive notifications of changes to data.

HRESULT addOLEDBSimpleProviderListener (
OLEDBSimpleProviderListener*pospIListener);

Parameters

pospIListener

[in]
The event handler interface to be registered.

Return Codes

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

Notes

  • If an event handler was previously specified, addOLEDBSimpleProviderListener releases that event handler prior to registering pospIListener.

  • To correctly implement a provider built with the OLE DB Simple Provider Toolkit, the listener must be the Simple Provider DLL (MSDAOSP.DLL).

  • For multiple OLE DB Rowsets over the same data set, your implementation must support multiple listeners; otherwise only one "active" Rowset will be allowed.


OLEDBSimpleProvider::removeOLEDBSimpleProviderListener

Unregisters a named event handler interface.

HRESULT removeOLEDBSimpleProviderListener (
OLEDBSimpleProviderListener*pospIListener);

Parameters

pospIListener

[in]
The event handler interface to be unregistered.

Return Codes

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

Notes

  • If removeOLEDBSimpleProviderListener specifies an event handler interface different from the one previously registered, code the call to fail with E_FAIL.

  • To correctly implement a provider built with the OLE DB Simple Provider Toolkit, the listener must be the Simple Provider DLL (MSDAOSP.DLL).

  • For multiple OLE DB Rowsets over the same data set, your implementation must support multiple listeners; otherwise only one "active" Rowset will be allowed.


OLEDBSimpleProviderListener Interface

OLEDBSimpleProviderListener

OLEDBSimpleProviderListener is the OSP interface that specifies the methods to be implemented by the host for receiving notifications of data changes. It is identified by the following IID:

{E0E270C1-C0BE-11d0-8FE4-00A0C90A6341}

OSP implementers issue notifications of changes to the underlying data through a single event handler. The event handler, registered through OLEDBSimpleProvider::addOLEDBSimpleProviderListener, implements the OLEDBSimpleProviderListener notification methods.

Notification Methods

Following is a list of the OSP notification methods available with the OLE DB Simple Provider Toolkit, along with a short description of each. There are pre- and post-notifications for each event. Keep in mind that the OSP notifications represent only one category of possible notifications; there are also OLE DB notifications and data source notifications. Click on any method name for the SDK page describing its implementation.

Method | Description
aboutToChangeCell | Indicates that the specified cells are about to be modified.
cellChanged | Indicates that the specified cells were changed.
aboutToDeleteRows | Indicates that one or more specified rows are about to be deleted.
deletedRows | Indicates that one or more specified rows were deleted.
aboutToInsertRows | Indicates that one or more specified rows are about to be inserted.
insertedRows | Indicates that one or more specified rows were inserted.
rowsAvailable | Indicates that a specified sequence of rows are now available.
transferComplete | Indicates that the transfer of the entire data set exposed by OSP is complete.


OLEDBSimpleProviderListener::aboutToChangeCell

Indicates that the specified cells are about to change. aboutToChangeCell is an event handler method, implemented through addOLEDBSimpleProviderListener. This notification is issued when values are modified through setVariant or internally through a provider, and before any data value changes are implemented.

HRESULT aboutToChangeCell (
LONG iRow,
LONG iColumn);

Parameters

iRow

[in]
The row position of the cell(s) to be changed.

iColumn

[in]
The column position of the cell(s) to be changed.

Return Codes

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

Notes

  • Calls to getVariant should return the original, unchanged values or values prior to the return of control from firing the aboutToChangeCell event.

  • You can specify a value of -1 for iRow, iColumn, or both.


OLEDBSimpleProviderListener::cellChanged

cellChanged is an event handler method, implemented through addOLEDBSimpleProviderListener. It indicates that the specified cells were changed. This notification is issued when values are modified through setVariant or internally through a provider.

HRESULT cellChanged (
LONG iRow,
LONG iColumn);

Parameters

iRow

[in]
The row position of the changed cell(s).

iColumn

[in]
The column position of the changed cell(s).

Return Codes

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

Notes

  • This method should be invoked after the data values have changed. Subsequent calls to getVariant should return the new values.

  • You can specify a value of -1 for iRow, iColumn, or both.


OLEDBSimpleProviderListener::aboutToDeleteRows

OLEDBSimpleProviderListener::aboutToDelete
Rows

Indicates that the specified rows are about to be deleted. aboutToDeleteRows is an event handler method, implemented through addOLEDBSimpleProviderListener. This notification is issued just prior to the rows being deleted, either from a call to deleteRows or internally through the provider.

HRESULT aboutToDeleteRows (
LONG iRow,
LONG cRows);

Parameters

iRow

[in]
Starting position of the rows to be deleted.

cRows

[in]
Number of rows to be deleted.

Return Codes

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

Note

  • Access to cells in the deleted rows should remain valid until control is returned from aboutToDeleteRows.


OLEDBSimpleProviderListener::deletedRows

deletedRows is an event handler method, implemented through addOLEDBSimpleProviderListener. It indicates that cRows were deleted beginning at row iRow. The notification is issued when rows are deleted through deleteRows or internally by a provider.

HRESULT deletedRows (
LONG iRow,
LONG cRows);

Parameters

iRow

[in]
The row position of the changed cell(s).

cRows

[in]
Number of rows to be deleted.

Return Codes

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

Notes

  • This function should be called after the rows are deleted.


OLEDBSimpleProviderListener::aboutToInsertRows

OLEDBSimpleProviderListener::aboutToInsert
Rows

Indicates that the specified rows are about to be inserted. aboutToInsertRows is an event handler method, implemented through addOLEDBSimpleProviderListener. This notification is issued just prior to the rows being inserted, either through a call to insertRows or internally by the provider.

HRESULT aboutToInsertRows (
LONG iRow,
LONG cRows);

Parameters

iRow

[in]
Starting position of the rows to be inserted.

cRows

[in]
Number of rows to be inserted.

Return Codes

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.


OLEDBSimpleProviderListener::insertedRows

Indicates that the specified rows were inserted, beginning at the row position specified by iRow. insertedRows is an event handler method, implemented through addOLEDBSimpleProviderListener. This notification is issued when rows are inserted through insertRows or internally through a provider.

HRESULT insertedRows (
LONG iRow,
LONG cRows);

Parameters

iRow

[in]
Starting position of the inserted rows.

cRows

[in]
Number of inserted rows.

Return Codes

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.


OLEDBSimpleProviderListener::rowsAvailable

Indicates that a specified sequence of rows is now available. rowsAvailable is an event handler method, implemented through addOLEDBSimpleProviderListener. This event method notifies data consumers that additional row information is available.

HRESULT rowsAvailable (
LONG iRow,
LONG cRows);

Parameters

iRow

[in]
Starting position of the available rows.

cRows

[in]
Number of available rows.

Return Codes

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

Note

  • OSP consumers expect periodic notifications when data is available, because it is likely they will process or display the data as it arrives. Providers should weigh the advantage of simplifying code by firing this event once for each row, versus its performance impact.


OLEDBSimpleProviderListener::transferComplete

OLEDBSimpleProviderListener::
transferComplete

Indicates that a data transfer is complete. All providers must invoke this event. transferComplete is an event handler method, implemented through addOLEDBSimpleProviderListener. All asynchronous providers must call this function when a transfer of data is complete.

HRESULT transferComplete (
OSPXFER doneReason);

Parameter

doneReason

[in]
Supports the following values:

Value | Meaning
OSPXFER_COMPLETE = 0 | Data transmission completed successfully.
OSPXFER_ABORT = 1 | Data transmission completed because of a call to the Stop method.
OSPXFER_ERROR = 2 | Data transmission completed in an error state.

Return Codes

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

Note

  • For providers that retrieve their data asynchronously, this event signifies that no additional rowsAvailable events will be called for this data set.


IDataSource Interface

IDataSource

IDataSource is implemented by the OSP Data Object.

When the data consumer asks for data, it specifies the data access interface it wants via an IID. With the OLE DB Simple Provider Toolkit, you always use IID_OLEDBSimpleProvider (for other data access interfaces you can use with IDataSource, see the Visual Basic documentation).

Data members are sets of data and are identified by strings (BSTRs). The data member argument used with the IDataSource methods is interpreted as a case-insensitive string; that is, "MyDataMember" = "MYDATAMEMBER" = "mydatamember".

Following are the IDataSource methods used with OSP.

Method | Description
getDataMember | Returns a data access object (such as a row position) for a given data member.
getDataMemberCount | Returns the number of well-known data members.
getDataMemberName | Returns the name of a well-known data member.
addDataSourceListener | Adds a listener to the notification list.
removeDataSourceListener | Removes a listener from the notification list.


IDataSource::getDataMember

Returns a data access object (such as a row position) for a given data member.

HRESULT getDataMember (
DataMember bstrDM
REFIID riid
IUnknown** ppunk);

Parameters

bstrDM

[in]
A string that describes the data member that represents one or more sets of data supported by the data source. This string is interpreted as case-insensitive.

riid

[in]
The interface identifier of the specified data access object.

ppunk

[out]
The pointer of the returned interface.

Return Codes

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_NOINTERFACE
Does not support the specified data access object.

Comments

The default data member is specified by having either a zero-length string or a Null string (they are equivalent). If the data source does not support the desired data access object, it returns E_NOINTERFACE.

Note The return value from getDataMember is always an IUnknown pointer. It is the responsibility of the caller of the method to query for the appropriate interface. That is, the IID parameter does not specify which interface, but rather which data access object type to return.


IDataSource::getDataMemberCount

Returns the number of well-known data members.

HRESULT getDataMemberCount(
long*plCount);

Parameters

plCount

[out]
Number of well-known data members.

Return Codes

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

Comments

For data sources that do not support enumeration, 0 should be returned. Otherwise, the number of well-known data members should be returned.


IDataSource::getDataMemberName

Returns the name of a well-known data member.

HRESULT getDataMember (
long lIndex
DataMember*pbstrDM);

Parameters

lIndex

[in]
An index into the list of data member names.

pbstrDM

[out]
A string that describes the data member name. This string is interpreted as case-insensitive.

Return Codes

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

Comments

A zero-based index parameter is specified. The index must fall between 0 and getDataMemberCount - 1, or this method returns E_FAIL. The default data member ("") should never be returned from this method.


IDataSource::addDataSourceListener

Adds a listener to the notification list.

HRESULT addDataSourceListener(
DataSourceListener*pDSL);

Parameters

pDSL

[in]
Pointer to the interface of the listener. In case of any event, methods are called to that interface.

Return Codes

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

Comments

Checking for duplicate items and returning an error is optional. If the data source knows it will never issue notifications (for example, the data members are always available), it need not implement this method (it can just return S_OK). However, if it does issue notifications, it must multicast them. The HRESULTs returned from the multicast are ignored, and do not end the multicast.


IDataSource::removeDataSourceListener

Removes a listener from the notification list.

HRESULT removeDataSourceListener(
DataSourceListener*pDSL);

Parameters

pDSL

[in]
Pointer to the interface of the listener.

Return Codes

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

Comments

Checking whether the listener has been added and returning E_FAIL is optional. If there are duplicates on the list, one is removed, but which one is unspecified. If the data source does not issue notifications, then it can just return S_OK from this method.


IDataSourceListener Interface

IDataSourceListener

IDataSourceListener is implemented by the OSP Data Object.

The methods of IDataSourceListener notify the data consumer that an existing data member has become available, unavailable, or has been refreshed. The data consumer should release its hold on any data access interfaces for that member and call IDataSource::GetDataMember to get the new data (if available).

Following are the IDataSourceListener methods used with OSP.

Method | Description
dataMemberChanged | Notifies the data consumer that an existing data member has changed.
dataMemberAdded | Notifies the data consumer that an existing data member has become available, unavailable, or refreshed.
dataMemberRemoved | Notifies the data consumer that an existing data member has been removed.


IDataSourceListener::dataMemberChanged

Notifies the data consumer that an existing data member has become available, unavailable, or refreshed.

HRESULT dataMemberChanged(
DataMember bstrDM);

Parameters

bstrDM

[in]
Name of the data member that has changed. This string is interpreted as case-insensitive.

Return Codes

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

Comments

The data consumer should release its hold on any data access interfaces for that member (such as IRowset or IRowPosition), and should call IDataSource::GetDataMember in order to get the new data (if available).


IDataSourceListener::dataMemberAdded

Notifies the data consumer that a new member has been added to the enumeration of well-known data members.

HRESULT dataMemberAdded(
DataMember bstrDM);

Parameters

bstrDM

[in]
Name of the data member that has been added. This string is interpreted as case-insensitive.

Return Codes

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.


IDataSourceListener::dataMemberRemoved

Notifies the data consumer that a member has been removed from the enumeration of well-known data members.

HRESULT dataMemberRemoved(
DataMember bstrDM);

Parameters

bstrDM

[in]
Name of the data member that has been removed. This string is interpreted as case-insensitive.

Return Codes

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.


msDataSourceObject

The msDataSourceObject method described below should only be used when the provider is being written in Microsoft Visual Basic or Microsoft Visual J++. Implement msDataSourceObject in your provider code using the appropriate IDispatch methods.

If you are writing a provider in Visual Basic, msDataSourceObject provides the mechanism by which an OLE DB Rowset maps to the correct OSP implementation. The method accepts a string parameter indicating the correct OSP and returns a pointer to that OSP (which you will then Invoke using a standard IDispatch call). See the supplied Visual Basic code samples for an example.

The signature for msDataSourceObject is as follows:

HRESULT msDataSourceObject(
BSTR qualifier,
LONG**ppUnk);

Parameters

qualifier

[in]
Identifier for a data set when there is more than a single data source. When the parameter is NULL, or a null string (“”) is used, the data interface on the default data set should be returned. This string is interpreted as case-insensitive.

**punk

[out]
Interface pointer to the specified OSP implementation.


DataSourceListener

The DataSourceListener methods described below should only be used when the provider is being written in Microsoft Visual Basic or Microsoft Visual J++. Implement the methods in your provider code using the appropriate IDispatch methods.

There are instances where a consumer already has an interface to a data set supplied by the object, and the shape of the data set changes. For example, the number of columns changes, the name of the columns changes, the type of a column changes, or the entire data set is reordered (the result of a sort operation). In such an instance, there needs to be a way for the consumer to be notified of the change so it can retrieve a new interface on the (changed) data by using a call to msDataSourceObject. Consumers will do this by registering with the provider for a notification.

The object must support a method whereby the consumer can register for receiving such notifications. The method has the following signature:

HRESULT addDataSourceListener(
[in] DataSourceListener*pEvent);

DataSourceListener is defined as follows:

[ local, object, version(1.0)
uuid(7c0ffab2-cd84-11d0-949a-00a0c91110ed) ]

interface DataSourceListener : IUnknown

{
HRESULT dataMemberChanged([in] BSTR qualifier);
HRESULT dataMemberAdded([in] BSTR qualifier);
HRESULT dataMemberRemoved([in] BSTR qualifier);
}

Notes

  • When a data change occurs, the object will call the dataMemberChanged method of the DataSourceListener to notify the consumer that the shape has changed. The object will pass the corresponding qualifier to the consumer. However, only the values NULL or "" are valid for the qualifier. dataMemberAdded and dataMemberRemoved are called to notify the consumer that a multiple-dataset-capable control has a new data set available or has removed a previously existing one. Because providers with multiple data sets are not currently supported, these events are ignored.

  • A data consumer can notify a provider that it is no longer interested in data shape change notifications by calling addDataSourceListener(NULL). Note that this precludes multi-casting event notifications but keeps requirements on controls simple.


List of OLE DB Simple Provider Toolkit Files

File Listing

Following is a list of files supplied with the Microsoft OLE DB Simple Provider Toolkit, their locations, and descriptions.

OLE DB Simple Provider DLL

The MSDAOSP.DLL file (the DLL that maps OSP to OLE DB) must be installed in the following directory:

\Program Files\Common Files\System\OLE DB

The DLL must be registered with regsvr32.exe prior to its use.

All Other Files

The remaining Toolkit files must be installed in the locations specified below. Following is a list of the Toolkit files with descriptions:

File Name | Install Location | Description
Msdaosp.dll | \Program Files\Common Files\System\OLE DB | OLE DB mapping layer file.
Msdaosp.h | \Msdasdk\include\osp | Toolkit header file.
Msdaosp.idl | \Msdasdk\unsupported\idl | Defines the IDispatch DataSourceObject and GUIDs.
Release Notes.txt | \Oledbsdk\Osptk\Doc | Readme file for OSP Toolkit.
Msdatasrc.h | \Msdasdk\include\osp | IDataSource header file.
Msdatsrc.tlb | \WinNT\System32 or \Win95\System | IDataSource type library.
Msdatsrc.idl | \Msdasdk\unsupported\idl | Defines DataSource interface.
Simpdata.h | \Msdasdk\include\osp | IOLEDBSimpleProvider header file.
Simpdata.tlb | \WinNT\System32 or \Win95\System | IOLEDBSimpleProvider type library.
Simpdata.idl | \Msdasdk\unsupported\idl | Defines OLEDBSimpleProvider interface.
Osptk.lib | \Msdasdk\lib\osp | Library containing interface GUIDs.
Customer.txt | \Msdasdk\samples\osp | Data file for provider examples.
RegisterProvider.reg | \Msdasdk\samples\osp | Registration template.
RegisterProvider.txt | \Msdasdk\samples\osp | Registration example.
MyDataSource.cls | \Msdasdk\samples\osp\VB | Visual Basic provider example.
MyOSPObject.cls | \Msdasdk\samples\osp\VB | Visual Basic provider example.
ospsampb.dll | \Msdasdk\samples\osp\VB | Visual Basic provider example.
SampleOSP_VB.reg | \Msdasdk\samples\osp\VB | Visual Basic provider example.
SampleOSP_VB.vbp | \Msdasdk\samples\osp\VB | Visual Basic provider example.
CExList.h | \Msdasdk\samples\osp\VC | Visual C++ provider example.
Common.cpp | \Msdasdk\samples\osp\VC | Visual C++ provider example.
Common.h | \Msdasdk\samples\osp\VC | Visual C++ provider example.
MyClassFactory.cpp | \Msdasdk\samples\osp\VC | Visual C++ provider example.
MyClassFactory.h | \Msdasdk\samples\osp\VC | Visual C++ provider example.
MyDataSource.cpp | \Msdasdk\samples\osp\VC | Visual C++ provider example.
MyDataSource.h | \Msdasdk\samples\osp\VC | Visual C++ provider example.
MyOSPObject.cpp | \Msdasdk\samples\osp\VC | Visual C++ provider example.
MyOSPObject.h | \Msdasdk\samples\osp\VC | Visual C++ provider example.
ospsampc.dll | \Msdasdk\samples\osp\VC | Visual C++ provider example.
SampleOSP_VC.def | \Msdasdk\samples\osp\VC | Visual C++ provider example.
SampleOSP_VC.dsp | \Msdasdk\samples\osp\VC | Visual C++ provider example.
SampleOSP_VC.reg | \Msdasdk\samples\osp\VC | Visual C++ provider example.
MyOSPObject.class | \Msdasdk\samples\osp\VJ | Visual J++ provider example.
MyOSPObject.java | \Msdasdk\samples\osp\VJ | Visual J++ provider example.
MyDataSource.class | \Msdasdk\samples\osp\VJ | Visual J++ provider example.
MyDataSource.java | \Msdasdk\samples\osp\VJ | Visual J++ provider example.
SampleOSP_VJ.dsp | \Msdasdk\samples\osp\VJ | Visual J++ provider example.
SampleOSP_VJ.reg | \Msdasdk\samples\osp\VJ | Visual J++ provider example.


Redistribution of a Provider

Redistributing Providers

To redistribute a provider built with the Microsoft OLE DB Simple Provider Toolkit, you need to do the following:

  • Register the provider on each computer. To do so, copy, edit, and run the Provider Registration template supplied with the Toolkit (the RegisterProvider.reg file) on each computer. Make sure to rename the RegisterProvider.reg file. You supply a ProgID, name, and GUID for your full provider, and point to the name of your component's Data Object.

  • Install and register the OLE DB Simple Provider DLL (MSDAOSP.DLL).

  • Install the Data Object component on each computer and register it as a regular COM object. There are no special restrictions about what this file is named, or where it is placed.


Supported OLE DB Interfaces

Following are the OLE DB interfaces supported by the Microsoft OLE DB Simple Provider Toolkit. Because the Toolkit provides support for redistributable components, you may want to include this information along with the providers you build.

Data Source Object

Interface | Description
IConnectionPointContainer | When implemented on an object, makes the object connectable and expresses the existence of outgoing interfaces on the object. Through this interface a client can either locate a specific connection point for one IID or enumerate the connection points that exist. (Note that this is an OLE, not an OLE DB, interface. For more information, refer to the OLE 2 Programmer's Reference.)
IDBAsynchStatus | Polls for status or cancels the asynchronous operation on the object being initialized, generated, or populated.
IDBCreateSession | Used by consumers to create a new session on a data source object.
IDBInitialize | Initializes a data source object or enumerator.
IDBProperties | Sets and gets the values of properties on the data source object or enumerator, and gets all information about all properties supported by the provider.
IPersist | An interface that is implemented by object applications and used by container applications to get the CLSID associated with an object.
ISupportErrorInfo | Indicates whether a specific interface can return Automation and OLE DB error objects.

Session Object

Interface | Description and Notes
IOpenRowset | Enables consumers to open and work directly with individual tables or indexes in a data source. When using IOpenRowset::OpenRowset against an OLE DB provider written with the OLE DB Simple Provider Toolkit, you can only issue OpenRowset once for each OSP. The second call will fail with a status code of E_FAIL.
IGetDataSource | Mandatory interface on the session for obtaining a data source object.
ISessionProperties | Performs row updates, deletes, and inserts.
ISupportErrorInfo | Indicates whether a specific interface can return Automation and OLE DB error objects.

Rowset Object

Interface | Description
IAccessor | Provides methods for accessor management.
IColumnsInfo | Exposes information about columns of a Rowset or prepared command.
IConnectionPointContainer | When implemented on an object, makes the object connectable and expresses the existence of outgoing interfaces on the object. Through this interface a client can either locate a specific connection point for one IID or enumerate the connection points that exist. (Note that this is an OLE, not an OLE DB, interface. For more information, refer to the OLE 2 Programmer's Reference.)
IConvertType | Provides information about the availability of type conversions on a command or Rowset.
IDBAsynchStatus | Polls for status or cancels the asynchronous operation on the object being initialized, generated, or populated.
IRowset | Provides methods for fetching rows sequentially, getting the data from those rows, and managing the rows.
IRowsetChange | Performs row updates, deletes, and inserts.
IRowsetFind | Allows consumers to find a row within the Rowset matching a specified value.
IRowsetIdentity | Indicates that row instance identity is implemented on the Rowset and enables testing for row identity.
IRowsetInfo | Provides information about a Rowset.
IRowsetLocate | Fetches arbitrary rows of a Rowset.
IRowsetScroll | Enables consumers to fetch rows at approximate positions in the Rowset.
ISupportErrorInfo | Indicates whether a specific interface can return Automation and OLE DB error objects.

Notification Interfaces for Rowset Object

Interface | Description
IDBAsynchNotify | The callback interface that a consumer must support in order to be notified of the progress of asynchronous operations such as initializing a datasource, or opening or populating a Rowset.
IRowsetNotify | The callback interface that a consumer must support to connect to local notifications provided by a Rowset.


Supported OLE DB Properties

Following are the OLE DB properties supported by the OLE DB Simple Provider Toolkit. Because the Toolkit provides support for redistributable components, you may want to include this information along with the providers you build.

Supported Data Source Information Properties

Property | Default Value
DBPROP_ACTIVESESSIONS | 0
DBPROP_BYREFACCESSORS | VARIANT_FALSE
DBPROP_DATASOURCENAME | OSP Data Object ProgID
DBPROP_DATASOURCEREADONLY | VARIANT_FALSE
DBPROP_DSOTHREADMODEL | DBPROPVAL_RT_FREE
DBPROP_INIT_ASYNC | 0
DBPROP_MAXROWSIZE | 0
DBPROP_MULTIPLERESULTS | DBPROP_MR_NOTSUPPORTED
DBPROP_PERSISTENTIDTYPE | DBPROPVAL_PT_NAME
DBPROP_PROVIDERFRIENDLYNAME | "Microsoft OLE DB Simple Provider"
DBPROP_PROVIDERNAME | MSDAOSP.DLL
DBPROP_PROVIDEROLEDBVER | 02.00
DBPROP_PROVIDERVER | 02.00.xxx
DBPROP_SQLSUPPORT | DBPROPVAL_SQL_NONE
DBPROP_TABLETERM | Data Member

Supported Initialization Properties

Property | Default Value
DBPROP_INIT_DATASOURCE | OSP Data Object ProgID
DBPROP_INIT_LCID | LCID corresponding to the string returned by getLocale()

Supported Rowset Properties

Property |  | Default Value
DBPROP_BOOKMARKS |  | VARIANT_TRUE
DBPROP_BOOKMARKSKIPPED |  | VARIANT_TRUE
DBPROP_BOOKMARKTYPE |  | DBPROPVAL_BMK_NUMERIC
DBPROP_CACHEDEFERRED |  | VARIANT_FALSE
DBPROP_CANFETCHBACKWARDS |  | VARIANT_TRUE
DBPROP_CANHOLDROWS |  | VARIANT_TRUE
DBPROP_CANSCROLLBACKWARDS |  | VARIANT_TRUE
DBPROP_CHANGEINSERTEDROWS |  | VARIANT_TRUE
DBPROP_COLUMNRESTRICT |  | OSPStatusRW
DBPROP_DEFERRED |  | VARIANT_TRUE
DBPROP_FINDCOMPAREOPS | DBPROPVAL_CO_EQUALITY | DBPROPVAL_CO_STRING | DBPROPVAL_CO_CASESENSITIVE | DBPROPVAL_CO_CASEINSENSITIVE
DBPROP_IAccessor |  | VARIANT_TRUE
DBPROP_IColumnsInfo |  | VARIANT_TRUE
DBPROP_IColumnsRowset |  | VARIANT_FALSE
DBPROP_IConnectionPointContainer |  | VARIANT_TRUE
DBPROP_IConvertType |  | VARIANT_TRUE
DBPROP_IDBAsyncStatus |  | VARIANT_TRUE
DBPROP_IRowset |  | VARIANT_TRUE
DBPROP_IRowsetChange |  | VARIANT_TRUE
DBPROP_IRowsetFind |  | VARIANT_TRUE
DBPROP_IRowsetIdentity |  | VARIANT_TRUE
DBPROP_IRowsetInfo |  | VARIANT_TRUE
DBPROP_IRowsetLocate |  | VARIANT_TRUE
DBPROP_IRowsetResynch |  | VARIANT_FALSE
DBPROP_IRowsetScroll |  | VARIANT_TRUE
DBPROP_IRowsetUpdate |  | VARIANT_FALSE
DBPROP_ISupportErrorInfo |  | VARIANT_TRUE
DBPROP_IMMOBILEROWS |  | VARIANT_TRUE
DBPROP_LITERALBOOKMARKS |  | VARIANT_FALSE
DBPROP_LITERALIDENTITY |  | VARIANT_TRUE
DBPROP_MAXOPENROWS |  | 0
DBPROP_MAXROWS |  | 0
DBPROP_NOTIFICATIONGRANULARITY |  | DBPROPVAL_NT_MULTIPLEROWS
DBPROP_NOTIFICATIONPHASES |  | DBPROPVAL_NP_ABOUTTODO | DBPROPVAL_NP_SYNCHAFTER | DBPROPVAL_NP_DIDEVENT | DBPROPVAL_NP_FAILEDTODO | DBPROPVAL_NP_OKTODO
DBPROP_NOTIFYCOLUMNSET |  | 0
DBPROP_NOTIFYROWDELETE |  | 0
DBPROP_NOTIFYROWINSERT |  | 0
DBPROP_NOTIFYROWSETRELEASE |  | 0
DBPROP_NOTIFYROWFETCHPOSITIONCHANGE |  | 0
DBPROP_ORDEREDBOOKMARKS |  | VARIANT_FALSE
DBPROP_OTHERINSERT |  | VARIANT_TRUE
DBPROP_OTHERUPDATEDDELETE |  | VARIANT_TRUE
DBPROP_OWNINSERT |  | VARIANT_TRUE
DBPROP_OWNUPDATEDELETE |  | VARIANT_TRUE
DBPROP_QUICKRESTART |  | VARIANT_TRUE
DBPROP_REENTRANTEVENTS |  | VARIANT_FALSE
DBPROP_REMOVEDELETED |  | VARIANT_TRUE
DBPROP_REPORTMULTIPLECHANGES |  | VARIANT_FALSE
DBPROP_ROWRESTRICT |  | OSPStatusRW
DBPROP_ROWSET_ASYNCH |  | (DBPROPVAL_ASYNCH_INITIALIZE | DBPROPVAL_ASYNCH_SEQUENTIALPOPULATION | DBPROPVAL_ASYNCH_RANDOMPOPULATION
DBPROP_ROWTHREADMODEL |  | DBPROPVAL_RT_FREE
DBPROP_STRONGIDENTITY |  | VARIANT_FALSE
DBPROP_UPDATABILITY |  | DBPROPVAL_UP_INSERT | DBPROPVAL_UP_CHANGE | DBPROPVAL_UP_DELETE


Introduction

This document introduces control writers to OLE DB control writing techniques. It isn't a comprehensive study of OLE DB; rather, it discusses how to create functional ActiveX® controls by using OLE DB. In addition, the document is a reference for migrating controls written using obsolete ICursor interfaces to OLE DB.

To get a full description of topics introduced in this document, refer to the OLE DB Programmer’s Reference.

In addition, you may refer to the following sources, which can be found in the Microsoft Developer Network™ (MSDN) in the Platform Software Development Kit (SDK):

  • Automation Programmer’s Reference

  • COM Programmer’s Reference


SDK Components

The ActiveX Control Writer’s Software Development Kit for OLE DB contains this document, header files, libraries with source code, and samples.

Header files are located in the Inc directory.

Header file | Description
adbhelp.h | Adbctl: Data binding helpers
adbctl.h | Adbctl: Main SDK support header file
adbmacrs.h | Adbctl: Macro definitions
advcon.h | Adbctl: IConnectionPoint and IPropertyNotifySink helpers
arrtmpl.h | Array template
bookmark.h | Adbctl: Bookmark helper class
Memstm.h | Abdctl: IStream class for persistence
msstkprp.h | Microsoft stock property pages
persist.h | Adbctl: IPropertyBag and IStream persistence helpers
prophelp.h | Adbctl: Property page and property management helpers
rowset.h | Adbctl: Rowset wrapper class
strutils.h | Adbctl: String helpers for WCHARs, BSTRs, and ANSI-to-Unicode conversions
autoobj.h | Ctlfw: Automation object
ctlobj.h | Ctlfw: OLE control object
debug.h | Ctlfw: Debugging helpers
globals.h | Ctlfw: Global variables
ipserver.h | Ctlfw: Global header file
Localsrv.h | Ctlfw: Server support
Proppage.h | Ctlfw: Property page support
stdenum.h | Ctlfw: Generic enumerator object
unknown.h | Ctlfw: Unknown object
util.h | Ctlfw: Miscellaneous helpers

Libraries are located in the Lib directory.

Library | Description
Adbctl.lib (release) Adbctld.lib (debug) | SDK support library
Ctlfw.lib (release) Ctlfwd.lib (debug) | ActiveX Control Framework library

Sources are located in the Src directory in their own project directories.

Project | Description
Adbctl | Source for SDK library
Ctlfw | Source for ActiveX Control Framework library
Acfwiz | Source for ActiveX Control Framework wizard

Sample projects are located in the Sample directory in their own project directories

Sample | Description
Oledblst | Complex bound ActiveX list box control
Simptext | Simple bound ActiveX text control


System Installation Requirements

This SDK requires the following products to be installed on your computer:

  • Microsoft® Visual Basic® 5.0

  • Microsoft® Visual C++® 5.0

  • Microsoft® Windows 95®/Windows NT® 4.0


Document Conventions

This document uses the following typographic conventions.

Convention | Description
Bold | Bold type indicates language keywords (for example, interfaces, properties, and methods; registry keys and subkeys) and other syntax that must be typed exactly as shown.
italics | Italic type indicates variables or placeholders for information you supply, or word emphasis.
UPPERCASE | Uppercase letters indicate flags, enumeration constants, data types, and structures.
Monospace
 This font is used for program code and error message text.


Overview of Consuming Rowsets

Rowsets are the central objects that enable all OLE DB data providers to expose data in tabular form. Conceptually, a rowset is a set of rows in which each row has columns of data. Base table providers (for example, databases) present their data in the form of rowsets. Query processors present query results in the form of rowsets. This makes it possible to layer components that consume or produce data through the same object.

For more information about rowsets, see the OLE DB Programmer’s Reference, Chapter 4, “Rowsets.”

The following table lists some required interfaces and optional interfaces that can be implemented for a particular rowset.

Interface | Required | Description
IAccessor | Yes | Defines how columns are read and written when rows are accessed.
IColumnsInfo | Yes | Exposes the most commonly used metadata about columns within a rowset.
IConvertType | Yes | Gives information about the availability of type conversions.
IRowset (from which all others are accessed using IUnknown::QueryInterface) | Yes | Provides methods for fetching rows sequentially, getting the data from those rows, and managing rows.
IRowsetInfo | Yes | Provides information about a rowset.
IColumnsRowset | No | Supplies complete information about columns in a rowset.
IConnectionPointContainer | No | Establishes connection points for connectable objects.
IRowsetChange | No | Updates the values of columns in existing rows, deletes existing rows, and inserts new rows.
IRowsetFind | Yes | Allows consumers to find a row within the rowset matching a specified value. This interface is always implemented in a provider or common service component.
IRowsetIdentity | No | Indicates that row-instance identity is implemented on the rowset and enables testing for row identity.
IRowsetLocate | No | Fetches arbitrary rows of a rowset. A rowset that does not implement this interface is a sequential rowset.
IRowsetResynch | No | Retrieves the values for rows that are currently visible to the transaction.
IRowsetScroll | No | Enables consumers to fetch rows at approximate positions in the rowset.
IRowsetUpdate | No | Enables consumers to delay commitment to changes made.
ISupportErrorInfo | No | Indicates whether an interface supports error-handling interfaces.

In addition to the interfaces listed above, there are also service interfaces. These service interfaces, which are particular to OLE DB data binding, are listed in the following table.

Interface | Description
IDataSource | Provides consumers with access to multiple rowsets.
IRowPosition | Allows multiple components to share the concept of a current row over a rowset.


Accessing Rowsets with CRowset

CRowset is a class that wraps some of the functionality of OLE DB rowset interfaces.

The many interfaces that are available to a particular rowset make rowsets cumbersome to consume. Often, more code is dedicated to acquire an interface than is actually required to set up parameters and call methods of the interface itself. To simplify this process, this SDK includes CRowset, a specialized rowset wrapper class that exposes methods that allow users to perform the most basic operations on a rowset without having to manage interfaces. The wrapper traverses interfaces when necessary, caching the commonly used interfaces on demand and releasing cached interfaces on the behalf of a consumer.

It is possible for a consumer to include calls to both the CRowset class and to the OLE DB interfaces directly. The following methods are supplied to allow the programmer to gain access to the OLE DB interfaces exposed by the attached rowset:

// Allow consumers to gain access to key Rowset interfaces.
// These interfaces are returned without AddRef.
//
IRowset* GetRowset(void);
IAccessor* GetAccessor(void);
IRowPosition* GetRowPosition(void);
IDataSource* GetDataSource(void);

These return the requested interface if it is implemented by the attached rowset. If CRowset is not attached, they return Null.

The reference count of the returned interface is not incremented. This allows the caller to quickly test whether an interface is available without having to call Release. The caller must call AddRef to take ownership of a returned interface.

if (m_pRowset->GetRowset() == NULL)
   // Do something that pertains to a detached rowset
   // like returning an error.
else
   // Use the rowset.

The CRowset class is declared in Rowset.h and implemented in Rowset.cpp. CRowset provides its own implementation of some operations that may not be available because a provider does not implement the necessary optional interface.

Attaching Rowset and CRowset Object

To use CRowset, consumers create an instance of the class. They then attach a rowset to the object and begin consumption.

CRowset::Attach

Once an instance of a CRowset object is created, CRowset::Attach must be called to attach it to an IRowset base interface, an IRowPosition interface or an IDataSource interface before it is usable. CRowset::Attach cleans up any interface from a previous attachment before attaching itself to the new interface. It is valid to call this method with a Null argument. In that case, only the cleanup process is performed.

This method performs a number of checks on the IUnknown interface to determine to which of the three interfaces to make the attachment.

HRESULT CRowset::Attach(IUnknown *punk, BSTR bstrDM /* = NULL */)
{
   // If we have a previous attachment then clean up first
   //
   if (m_pDataSource || m_pRowset || m_pRowsetPos)
   {
      SetChapter(NULL);
      ZeroMemory((void*)&m_properties, sizeof m_properties);
      m_columns.RemoveAll();

      // Release all Rowset interfaces
      //
      RELEASE_OBJECT(m_pRowsetPos);
      RELEASE_OBJECT(m_pRowsetLocate);
      RELEASE_OBJECT(m_pRowsetScroll);
      RELEASE_OBJECT(m_pRowsetExactScroll);
      RELEASE_OBJECT(m_pRowsetFind);
      RELEASE_OBJECT(m_pRowsetInfo);
      RELEASE_OBJECT(m_pRowsetIdentity);
      RELEASE_OBJECT(m_pRowsetChange);
      RELEASE_OBJECT(m_pRowsetUpdate);
      RELEASE_OBJECT(m_pAccessor);
      RELEASE_OBJECT(m_pConvertType);
      RELEASE_OBJECT(m_pColumnsInfo);
      RELEASE_OBJECT(m_pRowset);
      RELEASE_OBJECT(m_pDataSource);

      if (m_bstrDM)
      {
         SysFreeString(m_bstrDM);
         m_bstrDM = NULL;
      }
   }
   if (NULL == punk)
      return S_OK;

   // Is this a DataSource interface?
   //
   HRESULT hr = punk->QueryInterface(IID_DataSource, (void**)&m_pDataSource);
   if (m_pDataSource)
   {
      // Try to retrieve an IRowPosition from the DataSource. This call
      // will fail if the DataSource is not initialized, which will
      // leave m_pRowsetPos NULL
      //
      m_pDataSource->getDataMember(bstrDM, IID_IRowPosition, (IUnknown**)&m_pRowsetPos);
      if (bstrDM)
         m_bstrDM = SysAllocString((WCHAR*)bstrDM);

      // Return S_FALSE if the DataSource is not initialized
      //
      if (NULL == m_pRowsetPos)
         hr = S_FALSE;
   }
   else
   {
      // Is this an IRowset?
      //
      hr = punk->QueryInterface(IID_IRowset, (void**)&m_pRowset);

      // Is this a RowPostion interface?
      //
      if (NULL == m_pRowset)
         hr = punk->QueryInterface(IID_IRowPosition, (void**)&m_pRowsetPos);
   }
   // Once we have IRowPosition, we can get the associated IRowset
   //
   if (m_pRowsetPos)
      hr = m_pRowsetPos->GetRowset(IID_IRowset, (IUnknown**)&m_pRowset);

   // Get the rowset properties if IRowset is available
   //
   if (m_pRowset)
   {
      // Do one time initialization of the
      // contained ROWSETPROPERTIES structure
      //
      GetProperties();
   }
   return hr;
}

The optional parameter is used when the interface is an IDataSource. This parameter is the data member name and can be NULL for the default data member.

Getting Rowset Properties

A rowset’s properties tell consumers what optional interfaces the rowset implements, as well as a number of attributes that describe limitations and capabilities of the rowset, how it responds to editing, and its threading model. Consumers are encouraged to query properties for a particular rowset to establish whether certain optional interfaces are available and whether it possesses capabilities required by the consumer.

CRowset::GetRowsetProperties

This method uses IRowsetInfo::GetProperties to retrieve a rowset’s property values. The first argument can be an external IUnknown of a rowset or Null for the attached rowset.

HRESULT CRowset::GetRowsetProperties(IUnknown *pRowset, const DBPROPID *pPropIDs, VARIANT *pValues, ULONG cPropIDs)
{
   // Validate arguments.
   //
   if (!(pPropIDs && pValues && cPropIDs))
      return E_INVALIDARG;

   HRESULT hr;
   IRowsetInfo * pRowsetInfo = NULL;

   if (pRowset && pRowset != m_pRowset)
      // Traverse to desired interface.
      //
      hr = pRowset->QueryInterface(IID_IRowsetInfo, (void**)&pRowsetInfo);
   else
   {
      // Use attached Rowset.
      //
      hr = InitRowsetInfo();
      pRowsetInfo = m_pRowsetInfo;
   }
   RETURN_ON_FAILURE(hr);

   // Initialize DBPROPSET parameter.
   //
   DBPROPIDSET propidSet;
   propidSet.rgPropertyIDs = (DBPROPID*)pPropIDs;
   propidSet.cPropertyIDs = cPropIDs;
   propidSet.guidPropertySet = DBPROPSET_ROWSET;

   // Retrieve property values.
   //
   DBPROPSET *ppropSet = NULL;   // Array DBPROPSET.
   ULONG    cpropSets = 0;      // Number DBPROPSET in array

   hr = pRowsetInfo->GetProperties(1, &propidSet, &cpropSets, &ppropSet);

   if (SUCCEEDED(hr) && NULL != ppropSet)
   {
      // Do a byte copy of the variant property
      // value and leave it up to the caller to call
      // VariantClear when it is done with the value
      // to do any necessary cleanup.
      //
      for (ULONG i = 0; i < ppropSet[0].cProperties; i++)
         pValues[i] = ppropSet[0].rgProperties[i].vValue;

      // Free callee-allocated buffer.
      //

CoTaskMemFree((void*)ppropSet[0].rgProperties);

      CoTaskMemFree((void*)ppropSet);
   }
   // If pRowsetInfo is not ours, then release it.
   //
   if (NULL != pRowsetInfo && pRowsetInfo != m_pRowsetInfo)
      pRowsetInfo->Release();

   return hr;
}

ROWSETPROPERTIES Structure

The ROWSETPROPERTIES structure contains a subset of rowset properties that may interest a consumer. CRowset initializes its own copy of this structure after it has successfully attached itself to a rowset. The definition of this structure is located in Abdhelp.h.

struct ROWSETPROPERTIES
{
   // Value properties.
   //
   struct
   {
      long   MaxOpenRows;   // Maximum rows consumers are allowed to hold.
      long   Updatability;   // IRowsetChange methods supported.
   }   value;

   // Boolean properties.
   //
   struct
   {
      UINT   StrongId      :1;   // Newly inserted HROWs can be compared.
      UINT   LiteralId      :1;   // HROWs can be literally compared.
      UINT   HasBookmarks   :1;   // Rows have self-bookmark columns.
                           //   IRowsetLocate is implemented.
      UINT   CanHoldRows      :1;   // HROWs can be held in between GetRows.
      UINT   CanScrollBack   :1;   // GetRows can scroll backward.
                           //   IRowsetScroll is implemented.
      UINT   CanFetchBack   :1;   // GetRows can fetch backward.
      UINT   CanChange      :1;   // IRowsetChange is implemented.
      UINT   CanUpdate      :1;   // IRowsetUpdate is implemented.

      UINT   IRowsetScroll   :1;      // IRowsetScroll is implemented
      UINT   IRowsetExactScroll:1;   // IRowsetExactScroll is implemented
   }   flag;
};

CRowset::GetProperties

The CRowset::GetProperties method is called by CRowset::Attach to initialize the ROWSETPROPERTIES structure member of CRowset. A read-only reference to the structure is returned so callers can inspect the rowset’s properties.

See Rowset.cpp for the definition of CRowset::GetProperties.

#define IS_TRUE(pv) (VT_BOOL == V_VT(pv) && VARIANT_FALSE != V_BOOL(pv))

const ROWSETPROPERTIES& CRowset::GetProperties(void)
{
   // List of DBPROPSET_ROWSET Properties we
   // are interested in
   //
   static const DBPROPID rgpropid[] =
   {
      // Value props
      //
      DBPROP_MAXOPENROWS,
      DBPROP_UPDATABILITY,

      // BOOL props
      //
      DBPROP_STRONGIDENTITY,
      DBPROP_LITERALIDENTITY,
      DBPROP_BOOKMARKS,
      DBPROP_CANHOLDROWS,
      DBPROP_CANSCROLLBACKWARDS,
      DBPROP_CANFETCHBACKWARDS,
      DBPROP_IRowsetChange,
      DBPROP_IRowsetUpdate,
      DBPROP_IRowsetScroll,
      DBPROP_IRowsetExactScroll
   };
   static const ULONG cPropIDs = sizeof rgpropid/sizeof rgpropid[0];

   // Check to see if we need to initialize
   // the properties structure
   //
   if (NULL == m_pRowsetInfo)
   {
      HRESULT hr = InitRowsetInfo();

      if (SUCCEEDED(hr))
      {
         // Allocate VARIANT values array
         //
         VARIANT *pValues = new VARIANT[cPropIDs];

         if (NULL == pValues)
            hr = E_OUTOFMEMORY;
         else
         {
            // Zero out VARIANTs and get property values
            //
            ZeroMemory((void*)pValues, cPropIDs * sizeof(VARIANT));
            hr = GetRowsetProperties(NULL, rgpropid, pValues, cPropIDs);

            // Save property values in structure
            //
            if (SUCCEEDED(hr))
            {
               m_properties.value.MaxOpenRows   = VT_I4 == V_VT(pValues) ? V_I4(pValues) : 0;
               m_properties.value.Updatability   = VT_I4 == V_VT(pValues+1) ? V_I4(pValues+1) : 0;

               m_properties.flag.StrongId         = IS_TRUE(pValues+2);
               m_properties.flag.LiteralId         = IS_TRUE(pValues+3);
               m_properties.flag.HasBookmarks      = IS_TRUE(pValues+4);
               m_properties.flag.CanHoldRows      = IS_TRUE(pValues+5);
               m_properties.flag.CanScrollBack      = IS_TRUE(pValues+6);
               m_properties.flag.CanFetchBack      = IS_TRUE(pValues+7);
               m_properties.flag.CanChange         = IS_TRUE(pValues+8);
               m_properties.flag.CanUpdate         = IS_TRUE(pValues+9);
               m_properties.flag.IRowsetScroll      = IS_TRUE(pValues+10);
               m_properties.flag.IRowsetExactScroll= IS_TRUE(pValues+11);
            }
            delete [] pValues;
         }
      }
   }
   return m_properties;
}

Columns Information

Once a rowset’s properties have been inspected and it is ready for consumption, the next step is to collect information about the rowset’s columns. A rowset exposes information about its columns through the required IColumnsInfo and the optional IColumnsRowset interfaces. IColumnsRowset exposes metadata in tabular format and is consumed like any other rowset.

Of the two, IColumnsRowset provides the most complete metadata information but is more complicated to consume. IColumnsInfo provides consumers with the most commonly used metadata through an array of DBCOLUMNINFO structures, and is more convenient and easy to consume. For the most part, consumers will find the metadata supplied by DBCOLUMNINFO to be sufficient.

COLUMNINFO Structure

The COLUMNINFO structure is an extension of DBCOLUMNINFO and includes additional metadata about a column. The additional metadata is retrieved using IColumnsRowset, which is demonstrated later in this section. This structure is defined with a constructor and destructor to perform initialization and cleanup of complex data members. Its implementation is in Adbhelp.cpp.

struct COLUMNINFO : public DBCOLUMNINFO
{
public:
   COLUMNINFO();
   ~COLUMNINFO();

   void Clear(BOOL fZeroMemory = TRUE);

// Additional metadata
public:
   VARIANT   varDefault;      // Default value for a column

// Operators
public:
   COLUMNINFO& operator=(const COLUMNINFO& other);
   COLUMNINFO& operator=(const DBCOLUMNINFO& other);
};

CRowset::GetColumns

The CRowset::GetColumns method uses IColumnsInfo::GetColumnInfo to populate an array of COLUMNINFO structures; it then calls CRowset::InitColumnsMetadata to fill additional metadata members for each COLUMNINFO in the array.

HRESULT CRowset::GetColumns(COLUMNINFO*& pColumns, ULONG& cColumns)
{
   // Clear out bound parameters.
   //
   pColumns = NULL;
   cColumns = 0;

   HRESULT hr = InitColumnsInfo();
   RETURN_ON_FAILURE(hr);

   // Get the column information.
   //
   OLECHAR         *wszColumns   = NULL;
   DBCOLUMNINFO   *pDBColumns   = NULL;

   hr = m_pColumnsInfo->GetColumnInfo(&cColumns, &pDBColumns, &wszColumns);

   if (SUCCEEDED(hr) && cColumns > 0)
   {
      // Allocate COLUMNINFO array.
      //
      pColumns = new COLUMNINFO[cColumns];
      if (NULL == pColumns)
      {
         cColumns = 0;
         hr = E_OUTOFMEMORY;
      }
      else for (register ULONG i = 0; i < cColumns; i++)
         pColumns[i] = pDBColumns[i];
   }
   // Free callee-allocated buffers.
   //
   if (pDBColumns)
   {
      for (register ULONG i = 0; i < cColumns; i++)
      {
         if (pDBColumns[i].pTypeInfo)
            pDBColumns[i].pTypeInfo->Release();
      }
      CoTaskMemFree((void*)pDBColumns);
      CoTaskMemFree((void*)wszColumns);
   }
   // Get the default value metadata for
   // each column.
   //
   if (cColumns)
      InitColumnsMetadata(pColumns, cColumns);

   return hr;
}

CRowset::InitColumnsMetadata

This method is a protected member of CRowset and cannot be called by consumers, but it is discussed here because it uses IColumnsRowset::GetColumnsRowset to retrieve the rowset containing column metadata for the base rowset. The column rowset is read-only and can be consumed in the same manner as the base rowset. This method also demonstrates the use of more advanced CRowset methods that are discussed later in this section.

HRESULT CRowset::InitColumnsMetadata(COLUMNINFO*& pColumns, ULONG cColumns)
{
   if (!(pColumns && cColumns))
      return E_INVALIDARG;

   // Determine if the rowset implements IColumnsRowset.
   //
   IColumnsRowset   *pColRowset = NULL;
   HRESULT hr = m_pRowset->QueryInterface(IID_IColumnsRowset, (void **)&pColRowset);
   RETURN_ON_FAILURE(hr);

   IUnknown   *punkRowset   = NULL;   // Columns Rowset
   CRowset      *pRowset   = NULL;   // Used to consume Columns Rowset
   HROW      *phRows      = NULL;   // Receives row handles
   ULONG      cRows      = 0;   // Receives row handles count
   HACCESSOR   haccessor   = NULL;   // Accessor for Columns Rowset
   DBBINDING   dbBinding;         // Binding for default value column
   CBookmark bmk(sizeof DBBMK_FIRSTROW, &DBBMK_FIRSTROW);

   // Get the IRowset for all required metadata columns
   // and the optional default value column. The returned
   // rowset is read-only and can be consumed like any other.
   //
   hr = pColRowset->GetColumnsRowset(NULL, 1, &DBCOLUMN_DEFAULTVALUE,
    IID_IRowset, 0, NULL, &punkRowset);
   pColRowset->Release();
   if (FAILED(hr)) goto CleanUp;

   // Attach it to a CRowset.
   //
   pRowset = new CRowset();
   if (NULL == pRowset)
   {
      hr = E_OUTOFMEMORY;
      goto CleanUp;
   }

   hr = pRowset->Attach(punkRowset);
   if (FAILED(hr)) goto CleanUp;

   // Create binding for the default value column.
   //
   InitBinding(dbBinding, 0, DBTYPE_VARIANT, sizeof(VARIANT), offsetof(COLUMNINFO, varDefault));
   hr = pRowset->GetColumnOrdinals(&dbBinding.iOrdinal, &DBCOLUMN_DEFAULTVALUE, 1);
   if (FAILED(hr) || DB_INVALIDCOLUMN == dbBinding.iOrdinal) goto CleanUp;

   // The number of rows fetched needs
   // to be the same as the number of columns.
   //
   hr = pRowset->GetRows(bmk, 0, (long)cColumns, phRows, cRows);
   if (FAILED(hr) || cRows != cColumns) goto CleanUp;

   // Create an accessor.
   //
   hr = pRowset->CreateAccessor(&dbBinding, 1, haccessor);

   if (NULL != haccessor)
   {
      // Get the default value for each column.
      //
      for (ULONG i = 0; i < cRows; i++)
         pRowset->GetData(phRows[i], haccessor, (void*)(pColumns+i));

      pRowset->ReleaseAccessor(haccessor);
   }

CleanUp:
   if (punkRowset)
      punkRowset->Release();

   if (pRowset)
   {
      if (phRows)
         pRowset->FreeRows(phRows, cRows);

      delete pRowset;
   }
   return hr;
}


Using Bookmarks

Bookmarks allow consumers to quickly refer to rows in a rowset. They are:

  • Used by methods in IRowsetLocate and IRowsetScroll.

  • Valid only during the lifetime of a rowset.

See the OLE DB Programmer’s Reference for more information about bookmarks and the rowset properties that concern bookmarks.

CBookmark Class

The CBookmark class is declared in Bookmark.h and is implemented in Bookmark.cpp. It facilitates the use of bookmarks by handling the necessary conversion between bookmarks and VARIANTs. It also handles both fixed-length and variable-length bookmarks to provide additional convenience to the developer.


Referencing Row Handles

Each row handle has a reference (or usage) count managed by the provider. The reference count is incremented by methods that return row handles, or by IRowset::AddRefRows. Consumers must call IRowset::ReleaseRows each time the reference count is incremented implicitly on their behalf or explicitly by IRowset::AddRefRows. The only exception is IRowset::Release—on the final release, all referenced row handles are implicitly released.

CRowset contains its own versions of AddRefRows and ReleaseRows so that consumers do not have to access the attached base rowset to manage row handles. CRowset::FreeRows is an extended version of CRowset::ReleaseRows; it releases row handles and also frees the caller-allocated row handles array.


Fetching Rows

When rows are fetched, they are returned to the consumer in an array of row handles. A consumer manages a row or sets of rows through row handles. However, row handles are considered a limited resource. Depending upon the provider, there may be a limit on the number of rows that a consumer is allowed to fetch and keep referenced per rowset. To find out the limits, consumers should call IRowsetInfo::GetProperties (which is wrapped as CRowset::GetRowsetProperties) for the appropriate rowset properties. See the OLE DB Programmer’s Reference, “Fetching Rows” in Chapter 4, “Rowsets.” Also see CRowset::GetProperties in Rowset.h and RowsetProperties::value.MaxOpenRows in Adbhelp.h of the SDK wrapper classes.


Updating the Rowset

The IRowsetChange interface is used to update a rowset. Changes may take place immediately or be delayed, depending on whether IRowsetUpdate is exposed by the rowset. For more information, see the OLE DB Programmer’s Reference, Chapter 5, “Updating Data in Rowsets.”

Setting Row Data with CRowset::SetData

Setting data is similar to getting data: The consumer must have a row handle, an accessor handle, and a buffer. The accessor handle describes which columns are being set, and the buffer contains the data. IRowsetChange::SetData is the method used to set data values in a row. CRowset::SetData simply uses IRowsetChange::SetData.

Deleting Rows with CRowset::DeleteRows

IRowsetChange::DeleteRows is used to delete rows from a rowset. Callers are encouraged to supply a DBROWSTATUS array and inspect the row status after deleting the rows. CRowset::DeleteRows is equivalent to IRowsetChange::DeleteRows.

Inserting Rows with CRowset::InsertRow

Inserting a row is simple to do using IRowsetChange::InsertRow. The provider initializes the inserted row based on the binding information defined by the accessor handle argument. If the accessor was created with binding information, then pData must be a valid data buffer. If you have a null accessor—one that was created with no binding information—then pData is ignored and the row is created with default column values. CRowset:: InsertRow is equivalent to IRowsetChange:: InsertRow.

Delaying Updates

Rowsets expose IRowsetUpdate when changes made through IRowsetChange are buffered until IRowsetUpdate::Update is called. Consumers can back out of changes made before IRowsetUpdate::Update is called by calling IRowsetUpdate::Undo. This is referred to as delayed updating (as opposed to immediate updating). CRowset provides wrappers for all IRowsetUpdate methods.


Handling Notifications

Three interfaces provide notifications to their consumers: IDataSource, IRowPosition, and IRowset.

IDataSource and IDataSourceListener

A consumer implements IDataSourceListener to get notifications from an IDataSource object. IDataSourceListener is simple to implement because it is not a COM object and therefore does not require reference counting. A consumer calls IDataSource::AddDataSourceListener to add itself to the notification list, and IDataSource::RemoveDataSourceListener to remove itself from the notification list.

Refer to the IDataSource specification for more detail.

IRowPosition and IRowPositionChange

A consumer implements IRowPositionChange when it wants to know when the current row position is being changed. IRowPositionChange is a COM interface and therefore requires reference counting. A consumer needs to use QueryInterface on the IRowPosition object for its IConnectionPointContainer interface to add or remove its implementation of IRowPositionChange from the notification chain.

To facilitate the use of IConnectionPointContainer, consumers may choose to use the routines and classes implemented in Advcon.cpp and Advcon.h. See the Advise Connections section in Appendix B for more details.

IRowset and IRowsetNotify

A consumer implements IRowsetNotify to receive notifications from the rowset. A consumer needs to use QueryInterface on the IRowset object for its IConnectionPointContainer interface to add or remove its implementation of IRowsetNotify from the notification chain. Again, using the Advise Connections helpers will greatly simplify this process.

For more information about this topic, see the OLE DB Programmer’s Reference, Chapter 9, “Notifications.”


Overview of Creating a Simple Bound Control

This chapter covers the details of creating a simple bound ActiveX control. The control is generated using the ActiveX Controls Framework Wizard. See Appendix A, “ActiveX Controls Framework,” for a discussion of the framework mechanics.

There are two types of data-bound ActiveX controls: simple and complex.

An example of a simple bound control is a masked edit control or an advanced label control. A simple bound control works with one row and column of data at a time, retrieving and updating only one value at a time. Simple bound controls have a simple bound property (or properties).


Creating a Simple Control

To create a simple bound control, you simply add a few attributes to the control’s Object Description Language (ODL) file. The .odl file is compiled into the control’s type library and is read by the control’s container. Adding the bindable attribute to a property declaration tells containers that the property can be bound to a data field. Only one bindable property can have the defaultbind attribute, which tells containers that the property may be bound by default if the user specifies no other bindable property for binding. Adding the displaybind attribute to a bindable property tells containers that the property can be displayed in a list that allows a user to select properties for binding at design time. Once the control is loaded, the container exposes additional properties to allow its user to set the source and column from which data will be exchanged.

For example, when the data-binding attributes are set in a control’s type library, Microsoft® Visual Basic® 5.0 exposes the DataSource and DataField extender properties on behalf of the control for the property marked defaultbind. The default bound property is used by the container to exchange data with the data sources. Some containers may provide additional design time UI. This allows users to add bindings for properties with the bindable and displaybind attributes. Visual Basic provides this UI through the DATABINDINGS extender property.

The container transfers the property value to and from the control through standard Automation procedures. The control notifies the container of a change in the property, or requests permission to update the property using IPropertyNotifySink. This notifies the container whether the control is “dirty” and whether it has to retrieve the bound property’s value.

When the control’s bound property is going to change, it can query the container to determine whether the data can be updated by calling IPropertyNotifySink::OnRequestEdit. If it is granted permission by the container, the control signals its acceptance of the new value by calling IPropertyNotifySink::OnChanged. This type of updating is called pessimistic updating.

Note Although this chapter addresses control creation using the ActiveX Controls Framework, you can modify any control that you create with little effort for use with any control framework.

Adding Attributes to the .odl File

The first step in creating a simple bound OLE control is to add data-binding attribute flags to desired properties in the control’s .odl file. The following table lists the possible .odl data binding attribute flags.

Attribute flag | Flag description
Bindable | Set on properties supporting the IPropertyNotifySink::OnChanged notification. You must set this when using simple or complex data binding.
Requestedit | Set on properties supporting the IPropertyNotifySink::OnRequestEdit notification.
Displaybind | Set on those properties that the control recommends should be displayed to the user as bindable at design time. This is a user interface hint to the container. It must be used with the bindable attribute flag. If bindable is used without displaybind, properties will be accessible at run time and will not appear in the IDE.
Defaultbind | Indicates the single bindable property that best represents the control as a whole. The default bind property will be accessible at design time. This attribute must be used with the bindable attribute. Use of the displaybind attribute is optional if there is only one bindable property, which is the default bind property.

Your control's .odl file should include code similar to this:

[id(DISPID_TEXT), propget, bindable, requestedit, displaybind, defaultbind] HRESULT get_Text([out, retval] BSTR *pbstrText);
[id(DISPID_TEXT), propput, bindable, requestedit, displaybind, defaultbind] HRESULT put_Text([in] BSTR bstrText);

In Visual Basic 5.0, setting the bindable and defaultbind attributes is enough for Visual Basic to display the DataSource and DataField extender properties. Visual Basic 5.0 also recognizes displaybind to determine whether the control would like to have this property appear in the DataBindings dialog. If Visual Basic 5.0 finds properties marked as bindable and displaybind, it adds the DataBindings extender collection. This allows users to describe property-to-data field associations at design time. If properties are marked bindable without the displaybind attribute, the collection does not appear in the Visual Basic 5.0 property browser but is accessible at run time.

Note Our sample control’s Text property contains the displaybind attribute. This directs Visual Basic to add the DataBindings collection and allows users to add bindings through the DataBindings dialog box. You may choose to remove this attribute to eliminate the DataBindings collection if you have only one bindable property.

Notifying the Container

The IPropertyNotifySink interface is a property notification interface used by containers that want to be notified of changes made to a property value, or sometimes disallow changes made to a property value. With the bindable and requestedit attribute flags set, the control has the responsibility to call the appropriate notifications of the IPropertyNotifySink interface. To query the container as to whether it allows changes on a specific property, the control calls IPropertyNotifySink::OnRequestEdit. If the property change is allowed, the control changes the property value and then calls IPropertyNotifySink::OnChanged. Without the requestedit attribute set, there is no need to call the IPropertyNotifySink::OnRequestEdit method; however, you must call IPropertyNotifySink::OnChanged if the bindable attribute is set. This ensures that the container detects changes in the bound property and performs data updates accordingly. The following is a description of the IPropertyNotifySink interface and its methods.

IPropertyNotifySink Interface

By specifying the attributes in the .odl file and implementing the correct interfaces (such as IConnectionPoint and IConnectionPointContainer), the control can call the OnRequestEdit and OnChanged notifications. Refer to the COM Programmer’s Reference for more information about connectable objects.

Here is a description of the IPropertyNotifySink interface and its member methods:

interface IPropertyNotifySink : IUnknown
{
   HRESULT OnChanged(DISPID dispid);
   HRESULT OnRequestEdit(DISPID dispid);
};

HRESULT OnChanged(DISPID dispid)
The OnChanged method is called by the bound control to indicate that the given property has changed its value. It is the responsibility of the bound control to cause an OnChanged notification any time the value of the property changes, regardless of why it changed.

Parameter | Description
dispid | Indicates the property that has changed. DISPID_UNKNOWN may be passed to represent that an unspecified set of properties has changed (the sink must assume that all have changed). For efficiency, it is recommended that controls generate DISPID_UNKNOWN only when a significant number of properties change simultaneously. Note: Visual Basic 5.0 must have a valid DISPID and doesn't assume all properties have changed when DISPID_UNKNOWN is passed in.

HRESULT OnRequestEdit(DISPID dispid)
OnRequestEdit is called by the bound control to indicate that the given property is about to change value. OnRequestEdit notifications must occur before the property actually changes its value because the return value indicates whether the bound control should actually let the property change its value. This may be used by containers to implement read-only links, but it is more commonly used for pessimistic locking on records in a database.

Parameter/ HResult | Description
dispid | Indicates the property that is about to change. DISPID_UNKNOWN may be passed to represent that an unspecified set of properties will change. For efficiency, it is recommended that controls generate DISPID_UNKNOWN only when a significant number of properties change simultaneously.
S_OK | Indicates that the property may change the value (for example, the record was successfully locked). Other values indicate that the property change may not occur.

Assuming that you have an interface pointer to the IPropertyNotifySink interface, the C++ code is as follows:

if (pPropNotifySink->OnRequestEdit(dispid) != S_OK)
   // Do some action to indicate that the property cannot change.
else
   {

   // This is where you put code that changes the property.

   // Call OnChanged to notify the container that the property has changed.
   pPropNotifySink->OnChanged(dispid);
   }

For controls written using the ActiveX Controls Framework, the previous code would change to look as follows:

if (RequestPropertyEdit(dispid))
   // Do some action to indicate that the property cannot change.
else
   {

   // This is where you put code that changes the property.

   //
   // Call PropertyChanged to notify the container that the property has changed.
   PropertyChanged(dispid);
   }

RequestPropertyEdit and PropertyChanged respectively broadcast OnRequestEdit and OnChanged properly to all hosts that have an active IPropertyNotifySink connection to the control.


Example: Creating an Edit Control

The following example creates the Simptext.ocx Edit Control using the ActiveX Control Creation Wizard. Some features that you may normally put in an ActiveX control are left out of these steps for the sake of simplicity. Refer to the Simptxt project in the Samples directory, included with this SDK, for the source code to this sample.

This simple bound text box works very much like the TextBox control in Visual Basic.

Creating the Simptext.ocx Edit Control

The ActiveX Control Creation Wizard is part of the ActiveX Controls Framework.

To create an ActiveX control in Visual Basic

  1. Follow the instructions in Appendix A to start the control wizard.

  2. Type the name of your project—for example, Simptext.

  3. Type in or browse for the directory where you want the project to be saved.

  4. Select the project type; the default project is "ActiveX Control." Click Next.

  5. Type the name of your control—for example, SimpText.

  6. Select the Windows control that you want to subclass—for example, EDIT. Click Next.

  7. Click Finish. The wizard creates a new ActiveX Framework Control in the designated directory.

Once the wizard has successfully completed, you can begin working on the new ActiveX Framework control project.

Modifying the .odl File

The first step to adding a property to a control is to modify its .odl file. See "Adding Atributes to the .odl File" earlier in this chapter for details about modifying the .odl file to add the data-binding attributes.

To add a default bindable property to Simptext, add the following property to the interface definition for the ISimpText declaration. Because the default bindable property should best describe a control's nature, the Text property is appropriate with an Edit control. Open the Simptext.odl file:

[id(DISPID_TEXT), propget, bindable, requestedit, displaybind, defaultbind]
HRESULT Text([out, retval] BSTR *pbstrText);
[id(DISPID_TEXT), propput, bindable, requestedit, displaybind, defaultbind]
HRESULT Text([in] BSTR bstrText);

DISPID_TEXT is defined in Olectl.h because it is a stock property (a property that is very common among ActiveX controls).

After adding a property declaration to the .odl file, you must also add the corresponding declaration to the control’s class definition in its C++ header file. Open SimpTextCtl.h and add the following method declaration to the CSimpTextControl class definition:

STDMETHOD(get_Text)(BSTR * pbstrText);
STDMETHOD(put_Text)(BSTR bstrText);

Managing the Property Value

So far, you've created the simple bound property. In the following procedures, you add a member variable to store the actual value of the Text property, as well as initialize and store the property.

To add a member variable to store the Text property, open the Simptextctl.h file and find the structure definition for SIMPTEXTCTLSTATE. Add the following structure member to the structure:

BSTR bstrText;

Note In the rest of the example, you will be adding code to the Simptextctl.cpp file.

Initializing State Variables

When you first create your control, you want your text property to be the same as the name of your control, just like the Visual Basic TextBox control. The first time you place a new control on a form, the framework's COleControl::InitializeNewState method is called, so this is where you put the code to initialize the m_state.bstrText and other state variables that may apply to this case.

The implementation of CSimpTextControl::InitializeNewState looks like this:

BOOL CSimpTextControl::InitializeNewState(void)
{
   // Make the text property the same as the control name
   // the first time you place the control on the form.
   if (!GetAmbientProperty(DISPID_AMBIENT_DISPLAYNAME, VT_BSTR, (void*)&m_state.bstrText))
      // If this is unsuccessful, then use the base control name.
      m_state.bstrText = SysAllocString(L"SimpText");

   // TRUE to proceed
   // FALSE to fail
   return TRUE;
}

Initializing the Control Window

After the control object is created by the container and the control state is initialized, the host activates the control in place. The control framework prepares the control for display, including creating the control window. To allow the control to hook into this process, COleControl provides two methods: BeforeCreateWindow and AfterCreateWindow.

Call BeforeCreateWindow to allow controls to set up their window styles and captions. CSimpTextControl::BeforeCreateWindow is implemented as follows:

BOOL CSimpTextControl::BeforeCreateWindow
(
  DWORD *pdwWindowStyle,
  DWORD *pdwExWindowStyle,
  LPSTR  pszWindowTitle
)
{
   // Allow text to scroll past the right border.
   //
   *pdwWindowStyle |= ES_AUTOHSCROLL;

   // Give the border a 3-D effect.
   //
   *pdwExWindowStyle |= WS_EX_CLIENTEDGE;

   // Fill in pszWindowTitle if the Text property is initialized.
   //
   if (m_state.bstrText)
   {
      // It doesn’t matter whether conversion succeeds.
      // If it does not succeed, then the Edit control
      // is cleared.
      //
      MAKE_ANSIPTR_FROMWIDE(szText, m_state.bstrText);

      if (szText)
      {
         // The framework allocates a buffer of
         // 128 bytes, so be careful not to exceed
         // this limit.
         //
         lstrcpyn(pszWindowTitle, szText, 127);
      }
      // szText will deallocate when it loses scope.
      // MAKE_ANSIPTR_FROMWIDE is in the framework's UTIL.H file.
   }
  return TRUE;
}

Implement AfterCreateWindow to perform initialization after the control window has been created. If necessary, you can initialize your control during its creation process. To do this, add code to handle the WM_NCCREATE or WM_CREATE messages in your control’s implementation of WindowProc. While the control window is being created, the process is running in a critical section of code. Therefore, any initialization done during WM_NCCREATE and or WM_CREATE must take this into account. If possible, do your initialization in AfterCreateWindow after your control window is been created.

Keeping the Member Variable Persistent

Now that you can store your value in the m_state.bstrText member variable, you need to make sure it is saved and restored whenever the control is restarted. There are two types of persistence:

  • Text, which persists using IPropertyBag.

  • Binary, which persists using IStream.

This SDK provides persistence helpers in Persist.h and Persist.cpp to facilitate persisting properties of the most common types. For more information about implementing persistence, see “Using the Framework” in Appendix A.

Managing the Text Property

Managing the Text property requires that the control handle EN_CHANGE notifications from the Edit window itself. These notifications occur when the user edits the control’s contents. The control also needs to implement CSimpTextControl::get_Text so that it can return a copy of m_state.bstrText to the host.

Returning the Text

CSimpTextControl::get_Text is called when the Text property value is needed. The control may call this method internally, but the host calls it in most cases. The idea here is to return a copy of the text stored in m_state.bstrText. The following implementation of CSimpTextControl::get_Text demonstrates this:

STDMETHODIMP CSimpTextControl::get_Text(BSTR *pbstrText)
{
   CHECK_POINTER(pbstrText);

   if (m_state.bstrText)
      // Make a copy.
      //
      *pbstrText = bstralloc(m_state.bstrText);
   else
      // Return an empty string.
      //
      *pbstrText = SysAllocString(L"");

   return NULL == *pbstrText ? E_OUTOFMEMORY : S_OK;
}

Handling the EN_CHANGE Text Box Message

Any time the value in the text box changes, you want to notify the container of that change. In this case, add code to respond to the EN_CHANGE text box message to notify the container. The Windows Edit control sends the EN_CHANGE notification code with the WM_COMMAND to its owner. This message is reflected back to the ActiveX control through the OCM_COMMAND message. ActiveX Framework controls can handle reflected messages in their implementation of WindowProc. Here is an example of how CSimpTextControl::WindowProc is implemented to handle the EN_CHANGE notification:

LRESULT CSimpTextControl::WindowProc
(
  UINT msg,
  WPARAM wParam,
  LPARAM lParam
)
{
  // TODO: handle any messages here, like in a normal window
  // proc.  Note that for special keys, you'll want to override and
  // implement OnSpecialKey.
    //

   switch (msg)
   {
   // WM_COMMAND message is sent to the container and is
   // reflected back as OCM_COMMAND.
   //
   case OCM_COMMAND:
      // The Text property needs to be updated when
      // the edit control's contents change. The
      // control must request permission from the
      // container to update the text here and in
      // put_Text.
      //
      if (HIWORD(wParam) == EN_CHANGE)
      {
         if (RequestPropertyEdit(DISPID_TEXT))
         {
            // You got permission, so get the new text
            // from the edit control, if any.
            //
            int nLen = GetWindowTextLength(m_hwnd);

            if (nLen)
            {
               // Allocate a buffer for text and a
               // NULL terminator.
               //
               char *szText = new char[++nLen];

               if (NULL != szText)
               {
                  GetWindowText(m_hwnd, szText, nLen);
                  BSTR bstrText = bstralloc(szText);
                  delete [] szText;

                  if (bstrText)
                  {
                     if (m_state.bstrText)
                        SysFreeString(m_state.bstrText);

                     // Assign a new Text value and notify the
                     // container of the change.
                     //
                     m_state.bstrText = bstrText;
                     PropertyChanged(DISPID_TEXT);
                     return 0;
                  }
               }
            }
            else
            {
               // The Edit control is empty, so free any text.
               // Otherwise, nothing needs to be done.
               //
               if (m_state.bstrText)
               {
                  SysFreeString(m_state.bstrText);
                  m_state.bstrText = NULL;
                  PropertyChanged(DISPID_TEXT);
               }
               return 0;
            }
         }
         // NOTE: This code is executed if the container
         // refused the change request, or if the above
         // allocation failed. In either case,
         // you need to call SyncText to keep the edit
         // control content in sync with m_state.Text.
         //
         SyncText();
      }
      // You don't need to pass on to a subclass
      // because this is an OLE-specific message.
      //
      return 0;
   }
   return CallWindowProc((FARPROC)SUBCLASSWNDPROCOFCONTROL(OBJECT_TYPE_CTLSIMPTEXT), m_hwnd, msg, wParam, lParam);
}

Setting the Text

To change the Text property, the control must first notify the host that it’s about to make a change. This is done by calling COleControl::RequestPropertyEdit on the dispatch ID of the property in question. If RequestPropertyEdit returns True, the host has given permission to allow the property to be changed. Then, after a successful change, the control must notify the host that the property has changed by calling COleControl::PropertyChanged on the dispatch ID of the property that was changed.

The implementation of CSimpTextControl::put_Text demonstrates the procedure to notify the host of the change in the Text property:

STDMETHODIMP CSimpTextControl::put_Text(BSTR bstrText)
{
   // Request permission to edit the property
   // and throw an exception if the container
   // does not permit it.
   //
   if (!RequestPropertyEdit(DISPID_TEXT))
      return Exception(CTL_E_SETNOTPERMITTED, 0, 0);

   if (bstrText)
   {
      // Make a copy of the text and return
      // E_OUTOFMEMORY if allocation failed.
      //
      bstrText = bstralloc(bstrText);
      RETURN_ON_NULLALLOC(bstrText);
   }
   // Free the current property.
   //
   if (m_state.bstrText)
      SysFreeString(m_state.bstrText);

   // Assign a new property value. This can
   // be a NULL BSTR.
   //
   m_state.bstrText = bstrText;

   // Synchronize the Edit control's contents
   // with m_state.bstrText.
   //
   SyncText();

   // Notify the container of changes.
   //
   PropertyChanged(DISPID_TEXT);

   return S_OK;
}

Building the Control

To build and register the control

  • From the Build menu in Microsoft Visual C++ 5.0, click Build SIMPTEXT.OCX. Your control is built and ready to be inserted into a container.


Overview of Creating a Complex Bound Control

There are two types of data-bound ActiveX controls: simple and complex.

A complex bound control is exemplified by a list or grid control, which can display or manipulate more than one row or column of data from a data source. Complex bound controls give you more ways to retrieve and search data through the use of OLE DB interfaces.

Complex bound controls have a data-source bound property. However, some controls can have both a data-source bound property and simple bound properties. For example, the DBList control in Visual Basic 5.0 has a data-source bound property (RowSource) and a simple bound property (BoundText). DBList was designed to use a data-source bound property to display a list of data and a simple bound property to update data.

Complex bound controls access data differently than simple bound controls. A complex control uses OLE DB data-binding interfaces (IDataSource and IRowPosition) to gain access to a rowset, then consumes data by using OLE DB interfaces.


Creating a Complex Control

A complex control creates two properties: one of type DataSource and one of type DataMember in the control’s .odl (or .idl) file, which is compiled into its type library. Containers that support data binding recognize these properties and provide the necessary user interface for setting DataSource and DataMember. You can also set these properties in code. Containers that support data binding are Visual Basic, Visual C++ (MFC), and Internet Explorer 4.0. Microsoft Access will support data binding in a subsequent release.

Once DataSource is set, the control calls IDataSource::getDataMember, passing in the value of its DataMember property and retrieving an IRowPosition interface on return. To retrieve the rowset interface, the control calls IRowPosition::GetRowset, after which the process of data consumption can begin.

Complex controls also implement IDataSourceListener to be notified when data becomes available, becomes unavailable, or has been completely refreshed for a particular DataMember. A control calls IDataSource::addDataSourceListener when it wants to be notified, and IDataSource::removeDataSourceListener to remove a previously added listener. For more granular changes to the rowset, IRowsetNotify should be implemented and added to a rowset’s notification list using its IConnectionPointContainer interface. However, rowsets are not required to implement the latter two interfaces. Therefore, their usage may not be supported in some cases.

Creating Properties of Type DataSource and DataMember

The first step in creating a complex bound control is to create the DataSource and DataMember properties by modifying the control's .odl file. Respectively, DataSource and DataMember are properties of type DataSource and DataMember. These types are defined in Msdatsrc.tlb, which should be imported into the control’s library definition using the importlib ODL directive. Controls exposing these properties are recognized by containers as complex data-bound controls.

At design time, the container supplies a list of data sources for the DataSource property. Once a data source is selected for a control, the container supplies a list of data members for the DataMember property. This list is based on the selected data source.

Note Visual Basic 5.0 does not recognize DataSource type properties, and therefore does not supply a list of data sources. Users must set the DataSource and DataMember properties in code.

Modifying the .odl File

To get definitions for DataSource and DataMember types, add the following code within the declaration of your control class library (you must do this before defining any other types):

importlib("STDOLE32.TLB");      // These two lines should
importlib(STDTYPE_TLB);      // already be in the ODL file.
importlib("msdatsrc.tlb");   // This line should be added.

Create your property definition as follows:

[id(DISPID_DATASOURCE), propget]
HRESULT DataSource([out, retval] DataSource** ppdp);
[id(DISPID_DATASOURCE), propputref]
HRESULT DataSource([in] DataSource* pdp);

// The DataMember property
[id(DISPID_DATAMEMBER), propget, bindable]
HRESULT DataMember([out, retval] DataMember* pbstrDataMember);
[id(DISPID_DATAMEMBER), propput, bindable]
HRESULT DataMember([in] DataMember bstrDataMember);

DataSource is defined as type IDataSource, and DataMember is defined as type BSTR in Msdatsrc.h. DISPID_DATASOURCE and DISPID_DATAMEMBER must be explicitly defined for the control and can be any unreserved value.

Note The container can only supply a list of data members at design time if the DataMember property has the bindable attribute flag.

Retrieving the Rowset

Once a control's DataSource property is set, it should cache the IDataSource interface pointer, and perform only the necessary amount of work on the new data source. At design time, this may just involve releasing interfaces and deallocating memory pertaining to the previous data source. At run time, the process becomes more complex because a control may want to repaint its content using the new data. This may require a series of expensive operations. Control writers must decide beforehand what operations their controls must perform with a data source for design time and run time to best optimize the controls' response time. This distinction is illustrated later with the sample complex bound list control.

Ultimately, the control must retrieve the rowset presented by the data source in order to do what it was written for—to consume data. This is done by calling IDataSource::getDataMember, which takes as its first argument the data member name from the DataMember property. This can be Null or an empty string to designate the default data member. The second argument is the interface ID that the control can consume. For most cases, this is IID_IRowPosition. Finally, the third argument is a pointer to a buffer in which the provider returns a pointer to the interface requested.

The following code sample demonstrates the use of IDataSource::getDataMember to request a pointer to an IRowPosition interface:

// This method is called to initialize the member
// variable m_pRowPosition.
HRESULT CMyControl::InitRowPosition(void)
{
   // Initialize if you need to.
   if (m_pRowPosition)
      return S_OK;

   // m_pDataSource is a member variable that caches
   // an IDataSource pointer.
   // m_dataMember is a member variable that stores
   // the current data member name. The default can be
   // NULL or an empty string ("").
   //
   // If getDataMember succeeds and the requested
   // interface pointer returned is NULL, then
   // the consumer must assume that no
   // data is available.
   //
   return m_pDataSource->getDataMember(m_state.dmDataMember, IID_IRowPosition, (IUKNOWN**)&m_pRowPosition);
}

The next step is to retrieve an IRowset interface by using IRowPosition::GetRowset. This method takes as its first argument the interface ID of any rowset interface that may be supported by the provider, and its second argument a buffer in which to return the requested pointer. The following code illustrates this:

// This method is called to initialize
// the class member variable m_pRowset.
//
HRESULT CMyControl::InitRowset(void)
{
   // Initialize if you need to.
   if (m_pRowset)
      return S_OK;

   // Ensures that you have m_pRowPosition.
   HRESULT hr = InitRowPosition();
   RETURN_ON_FAILURE(hr);

   // m_pRowPosition may be NULL if data
   // is not yet avaiable.
   if (NULL == m_pRowPosition)
      return S_FALSE;

   // This line retrieves the rowset and
   // stores it in a class member variable.
   hr = m_pRowPosition->GetRowset(IID_IRowset, (IUnknown **)&m_pRowset);
   return hr;
}

Interfaces for Data Binding

The following is a description of the interfaces that an OLE DB provider must implement to be consumed by OLE DB consumers. For more detailed information about implementing these interfaces, see the OLE DB Programmer’s Reference.

IDataSource

A consumer gains access to a data access interface by calling IDataSource::getDataMember. A consumer can request a number of data access interfaces. Here, the data access interface of interest is IID_IRowPosition.

IRowPosition

Consumers use IRowPosition to track and manage row currency. A consumer can gain access to the rowset object using IRowPosition::GetRowset.

Notifications

Notifications are an important aspect of data binding. They allow a control to track changes in the data source and respond appropriately to them. Refer to the section “Handling Notifications” in Chapter 1 for a review of this subject. Following is a discussion of how a control uses notifications as part of the data-consumption process.

IDataSourceListener

Once a control's DataSource property is set, the control should add its implementation of IDataSourceListener to the data source's notification list before it attempts to call IDataSource methods. This is crucial in the case of IDataSource::getDataMember, because this method may return Null with success when there is no data. When this occurs, a control must be able to handle this case and wait patiently until its IDataSourceListener::dataMemberChanged is called. This event signifies that data is available for the given data member. A control checks the passed-in data member parameter against its own. If the two match, then the control may again call IDataSource::getDataMember.

IDataSourceListener::dataMemberAdded allows consumers to update information that depends on concurrency with the data source's data member list.

IDataSourceListener::dataMemberRemoved must be handled by consumers to prevent them from continuing to access a rowset that is associated with a deleted data member. A consumer should check the data member passed in by the event against the one that the consumer is using and perform any necessary cleanup.

IRowPositionChange

This interface is implemented by consumers to track the current row in the data source. Controls that track the current row must process IRowPositionChange::OnRowPositionChange to stay current.

IRowsetNotify

IRowsetNotify notifications allow multiple consumers attached to the same rowset object to synchronize changes made to the rowset. This is the most comprehensive of the three notification interfaces, but is optional and may not be supported by all providers. For a detailed discussion of IRowsetNotify and notifications, refer to the OLE DB Programmer's Reference.

IConnectionPointContainer and IConnectionPoint

A consumer adds itself to the notification chain of IRowPositionChange and IRowsetNotify by using the IConnectionPointContainer interface. Because providers are not required to support IRowsetNotify, neither are they required to support IConnectionPoint interfaces. When these interfaces are supported, consumers use them to add and remove themselves from a provider’s notification chain.

For more information about the IConnectionPoint and IConnectionPointContainer interfaces, see the COM Programmer’s Reference and Automation Programmer’s Reference.


Example: Creating a List Box Control

The following example creates the Oledblst.ocx list box control using the ActiveX Control Creation Wizard. Some features and details have been left out of the example for the sake of simplicity. Refer to the Oledblst project in the Samples directory, included with this SDK, to find the complete source code for this sample.

In addition to the DataSource and DataMember properties, this complex bound list control has a ListColumn property to select the column specified for the display data. At run time, the list control displays all rows of the data for the selected column. It also has the ability to update or display the current record by setting or retrieving the Text property.

Note This sample subclasses a standard Windows list box control; therefore, the maximum size of the list may vary depending on your operating system. Also, the sample does not support duplicate fields in the column to which it is bound.

Creating the Oledblst.ocx List Box Control

The ActiveX Control Creation Wizard is part of the ActiveX Controls Framework.

To create an ActiveX control in Visual Basic

  1. Follow the instructions in Appendix A to start the control wizard.

  2. Type the name of your project—for example, Oledblst.

  3. Type in or browse for the directory in which you want the project to be saved.

  4. Select the project type; the default project is "ActiveX Control." Click Next.

  5. Type the name of your control—for example, OledbList.

  6. Select the Windows control that you want to subclass—for example, LISTBOX.

  7. Select the Complex Data Bound Control check box. This adds property definitions in the .odl file, necessary data members for the control class, implementation stubs for the DataSource and DataMember properties, and necessary header files to your control sources.

  8. Select the Implement IPerPropertyBrowsing check box. This adds code necessary to support IPerPropertyBrowsing for the control. Click Next.

  9. Click Finish. The wizard creates a new ActiveX Framework Control in the designated directory.

Once the wizard has successfully completed its work, you can start working on the new ActiveX Framework Control project.

Adding Rowset Support

This sample control uses CRowset, the IRowset wrapper class implemented in Rowset.cpp and defined in Rowset.h, to simplify rowset handling. Refer to Chapter 1, “Consuming Rowsets,” for a review.

To use CRowset, open Oledblistctl.h and add the following include statement:

#include <adbctl.h>   // Data bind control library

Next, add CRowset to the COLEDBListControl class inheritance list:

class COLEDBListControl : public COleControl, ….,
                  // Add this line for CRowset
                  public CRowset

Now the control is ready to consume rowsets.

Initializing the Rowset

For complex data-bound controls, the wizard adds declarations for m_pDataSource to the COLEDBListControl class definition, and dmDataMember to the OLEDBLISTCTLSTATE structure definition. These definitions are located in the OleDBListCtl.h header file. It is the control’s responsibility to persist the DataMember property. The DataSource property does not need to be persisted because it is set either by the container or in code. Stub implementations for the DataSource and DataMember properties were also added to Oledblistctl.cpp. The next code fragment illustrates the stubs with code additions highlighted in bold.

HRESULT COLEDBListControl::putref_DataSource(DataSource *pDataSource)
{
   HRESULT hr = S_OK;

   if (m_pDataSource)
   {
      // TODO: Add code to do cleanup.
      AttachRowset(NULL);

      // Clear the previous data source.
      //
      m_pDataSource->Release();
   }
   // Cache the new data source.
   //
   m_pDataSource = pDataSource;

   if (m_pDataSource)
   {
      // Take ownership.
      //
      m_pDataSource->AddRef();

      // TODO: Add code to process the new data source.
   }
   // Repaint the contents at run time.
   //
   if (!DesignMode())
      InvalidateControl(NULL);

   // Notify sinks.
   //
   PropertyChanged(DISPID_DATASOURCE);

   return hr;
}

HRESULT COLEDBListControl::put_DataMember(DataMember dmDataMember)
{
   HRESULT hr = S_OK;

   // Make a copy of the data member.
   //
   if (dmDataMember)
   {
      dmDataMember = (DataMember)COPYBSTR(dmDataMember);
      RETURN_ON_NULLALLOC(dmDataMember);
   }
   // Free the current data member.
   //
   if (m_state.dmDataMember)
      SysFreeString((BSTR)m_state.dmDataMember);

   // Keep the new data member.
   //
   m_state.dmDataMember = dmDataMember;

   // TODO: Add code to handle the change in the data member.
   AttachRowset(NULL);

   // Repaint the contents at run time.
   //
   if (!DesignMode())
      InvalidateControl(NULL);

   // Notify sinks.
   //
   PropertyChanged(DISPID_DATAMEMBER);

   return hr;
}

COLEDBListControl::AttachRowset takes a pointer to an IUnknown interface that can be an IRowset or an IRowPosition interface. When the pointer is Null, the method performs necessary cleanup to detach itself from the currently attached rowset. This method also calls the base class CRowset::Attach method to allow the base class to perform initialization and cleanup. AttachRowset is implemented as follows:

HRESULT COLEDBListControl::AttachRowset(IUnknown *pUnk)
{
   // If you have a previous Rowset, then clean up first.
   //
   if (CRowset::GetRowset())
   {
      // Free the ColumnInfo array.
      FreeColumns();

      // Disconnect notifications.
      m_notifications.Disconnect();

      // Release the row accessor.
      FreeRowAccessor();

      // Clear the list contents.
      ClearContents();

      // Let base class do cleanup.
      CRowset::Attach(NULL);
   }
   // If you are not given a new interface,
   // then the work is done.
   if (NULL == pUnk)
      return S_OK;

   // First let the base class attach to the
   // new rowset.
   HRESULT hr = CRowset::Attach(pUnk);
   RETURN_ON_FAILURE(hr);

   // Connect notifications.
   hr = m_notifications.Connect();

   return hr;
}

To initialize the rowset, you need to call COLEDBListControl::AttachRowset with a pointer to an IRowPosition or an IRowset interface. You want to use an IRowPosition interface and place the initialization code in its own method so you can call it on demand:

HRESULT COLEDBListControl::InitRowset(void)
{
   // If GetRowset returns a value then
   // the rowset is already attached, so
   // return success.
   //
   if (CRowset::GetRowset())
      return S_OK;

   if (m_pDataSource)
   {
      // Request IRowPostion from the data source.
      //
      IUnknown *pRowPos;
      HRESULT hr = m_pDataSource->getDataMember(m_state.dmDataMember, IID_IRowPosition, &pRowPos);
      RETURN_ON_FAILURE(hr);

      // If an interface was returned, then data is
      // available, so attach to the interface.
      //
      if (pRowPos)
      {
         hr = AttachRowset(pRowPos);
         pRowPos->Release();
         return hr;
      }
      // IDataSource::getDataMember succeeds but
      // did not return an interface, so return
      // S_FALSE for no data.
      //
      return S_FALSE;
   }
   // No data source, so return failure.
   //
   return E_FAIL;
}

InitRowset is called before any base-class CRowset methods are accessed to ensure that everything is set up correctly. Calling InitRowset on demand postpones the initialization code until it is needed. The example uses this style of delayed initialization throughout.

Getting Column Information

Column information is relevant at design time to support property browsing for the ListColumn property, and at run time for data binding. Upon request, COLEDBListControl queries for a list of columns and caches this list until it is no longer needed.

Getting the Columns

The columns are cached in a COLUMNINFO array stored in the m_pColumns class member variable. The columns count is stored in the m_pColumns class member variable. The array and its count are initialized in InitColumns and freed in FreeColumns. InitColumns is called prior to accessing the m_pColumns member variable to ensure that the array is initialized. Again, this is an example of delayed initialization.

Calling CRowset::GetColumns

This base-class method allocates a COLUMNINFO (see Adbhelp.h and Adbhelp.cpp) array, and returns the array and column count in the corresponding arguments passed to the method. The implementation of COLEDBListControl::InitColumns demonstrates this:

HRESULT COLEDBListControl::InitColumns(void)
{
   // If you already have columns, then return.
   if (m_pColumns)
      return S_OK;

   // Ensure that the rowset is attached.
   HRESULT hr = InitRowset();

   // Call base class to get COLUMINFO array   if (S_OK == hr)
      hr = CRowset::GetColumns(m_pColumns, m_nColumns);

   return hr;
}

CRowset::GetColumns allocates the COLUMNINFO array on behalf of the caller using the new operator. Callers must explicitly free this array. COLEDBListControl::FreeColumns performs this task:

HRESULT COLEDBListControl::FreeColumns(void)
{
   if (m_pColumns)
   {
      delete [] m_pColumns;
      m_pColumns = NULL;
      m_nColumns = 0;
   }
   return S_OK;
}

Browsing the Column Names

COLEDBListControl implements the ListColumn property, which allows users to specify in which column in the data source the list will display. ListColumn can be set in code if the user knows beforehand what columns are available in the data source. At design time, the control can give the container a list of available values to assign to the ListColumn property by implementing the COM interface IPerPropertyBrowsing.

Adding the ListColumn Property

As with any other property, adding the ListColumn property requires modifying some files. In brief, you need to make the following modifications to the corresponding files.

Add the following define statement to Dispid.h:

#define DISPID_LISTCOLUMN   102

Add the following properties to the interface definition of IOLEDBList in Oledblst.odl:

// ListColumn property
[id(DISPID_LISTCOLUMN), propget]
HRESULT ListColumn([out, retval] BSTR* pbstrColumn);
[id(DISPID_LISTCOLUMN), propput]
HRESULT ListColumn([in] BSTR bstrColumn);

Add the following member to the definition of OLEDBLISTCTLSTATE structure in Oledblistctl.h:

BSTR      bstrListColumn;

Add the following put and get methods to the class definition of COLEDBListControl in Oledblistctl.h:

STDMETHOD(get_ListColumn)(BSTR *pbstrColumn);
STDMETHOD(put_ListColumn)(BSTR bstrColumn);

You can find the implementation for the put and get methods for the ListColumn property in Oledblistctl.cpp.

Implementing IPerPropertyBrowsing

During the control-creation process, the Implements IPerPropertyBrowsing check box was selected. This resulted in the code generated to implement IPerPropertyBrowsing. Stub implementations of IPerPropertyBrowsing::GetPredefinedStrings and IPerPropertyBrowsing::GetPredefinedValue were added to Oledblistctl.cpp.

IPerPropertyBrowsing::GetPredefinedStrings provides the list of strings, whereas the IPerPropertyBrowsing::GetPredefinedValue function returns the string associated with the key or cookie value that was assigned to it by IPerPropertyBrowsing::GetPredefinedStrings.

Implementing IPerPropertyBrowsing::GetPredefinedStrings

This method is called when the container needs the list of strings describing possible values for a property. Containers may also call this method with NULL pointers. If so, then E_POINTER should be returned if the property does not support browsing, and S_OK if it does. Following is the implementation of the method with code additions highlighted in bold.

STDMETHODIMP COLEDBListControl::GetPredefinedStrings(DISPID dispid, CALPOLESTR *prgOleStr, CADWORD *prgCookie)
{
   // GUID of enum type for the property
   //
  const GUID *puuid = NULL;

   // Properties for which we support browsing
   //
   switch (dispid)
  {
   // TODO: Add cases for properties here.

   case DISPID_LISTCOLUMN:
      return GetColumnDisplayStrings(prgOleStr, prgCookie);

   case DISPID_UNKNOWN:   // Handles default w/no case warning.
   default:
      // Containers will call with NULL arguments
      // to test whether we support browsing for
      // a property. When this happens, E_POINTER
      // should be returned for properties that
      // we do not support browsing for; and
      // S_OK for those that we do support.
      //
      if (!prgOleStr || !prgCookie)
         return E_POINTER;

      // We don't support browsing for
      // the requested property.
      //
      return S_FALSE;
  }
   // GetStrings will search the TypeLib for information
   // about the type identified by *puuid.
   //
  return GetStrings(prgOleStr, prgCookie, OBJECT_TYPE_CTLOLEDBLIST, *puuid);
}

COLEDBListControl::GetColumnDisplayStrings fills in the CALPOLESTR and CADWORD structures using the columns information. See the implementation of COLEDBListControl::GetColumnDisplayStrings in Oledblistctl.cpp for more details.

Implementing IPerPropertyBrowsing::GetPredefinedValue

This method is called when the container needs the value of a property associated with the cookie. The cookie value is the same cookie matched with the property value in IPerPropertyBrowsing::GetPredefinedStrings. Here is the implementation of the method with code additions highlighted in bold.

STDMETHODIMP COLEDBListControl::GetPredefinedValue(DISPID dispid, DWORD dwCookie, VARIANT *pVarValue)
{
   switch (dispid)
   {
   // TODO: Add cases for properties here

   case DISPID_LISTCOLUMN:
      if ((ULONG)dwCookie < m_cColumns)
      {
         // Return column name at index formatted as
         // a Variant BSTR since this is the property
         // of for ListColumn
         //
         V_VT(pVarValue) = VT_BSTR;
         V_BSTR(pVarValue) = bstralloc(m_pColumns[dwCookie].pwszName);
         return V_BSTR(pVarValue) == NULL ? E_OUTOFMEMORY : S_OK;
      }
      return E_UNEXPECTED;

   case DISPID_UNKNOWN:   // Handles default w/no case warning
   default:
      // Don't recognize the property
      //
      return E_FAIL;
   }
   // Lookup the property value based on the cookie
   return GetValue(pVarValue, dwCookie);
}

The cookie is interpreted as the zero base index of the corresponding column in the COLUMNINFO array. The inserted code checks to ensure that the cookie does not over-index the array and simply looks up the column name.

Getting Row Data

True data-bound controls, such as those supplied by Visual Basic, employ some implementation of a row cache, which greatly reduces the number of rows that the provider has to bring into memory. To simplify the process, at run time OledbList fills its content with the text from the column designated by the ListColumn property for all the rows in the data source. This is not the optimal behavior, because it would hurt performance and would be a huge drain on system resources for any consumer to attempt to access all the rows at once in a large data source.

To get row data, a consumer usually does the following:

  1. Decides which column to get data for using the rowset's column information.

  2. Defines how the column data should be formatted.

  3. Sets up column bindings and creates an accessor for the bindings.

  4. Fetches rows.

  5. Gets data for each row using the row handle, accessor handle, and a data buffer.

Following this sequence, OledbList performs the following steps:

  1. Determines whether the rowset has a column with the same name as the value of the ListColumn property.

  2. Requests the column data to be formatted as a BSTR.

  3. Sets up a column binding and creates an accessor.

  4. Fetches all the rows sequentially until the end of the rowset is reached.

  5. Gets data for each row fetched and adds it to the list content.

In this procedure, steps 1, 2, and 3 are handled in COLEDBListControl::InitRowAccessor, and steps 4 and 5 are handled in COLEDBListControl::FillListContents.

Creating an Accessor

To display row data at run time, OledbList must first bind to the list column. It does this by first establishing whether the list column is a member of the columns of the attached rowset. Once membership is established, a DBBINDING structure is set up to associate the list column with a data buffer large enough to hold a BSTR. An accessor is then created with the binding information. All this is handled in COLEDBListControl::InitRowAccessor, which is called on demand to ensure that the class member variable m_hRowAccessor is initialized.

COLEDBListControl::InitRowAccessor

Following is the implementation of COLEDBListControl::InitRowAccessor, which can be found in Oledblistctl.cpp:

HRESULT COLEDBListControl::InitRowAccessor(void)
{
   // Return success if already initialized.
   //
   if (m_hRowAccessor)
      return S_OK;

   // ListColumn property must be set; if not,
   // then return S_FALSE for no data.
   //
   if (!m_state.bstrListColumn || '\0' == m_state.bstrListColumn[0])
      return S_FALSE;

   // Ensure that m_pColumns is initialized.
   //
   HRESULT hr = InitColumns();
   if (S_OK != hr)
      return hr;

   // Search for the ListColumn in the Columns array.
   //
   LONG nIndex = m_cColumns;
   while (nIndex--)
   {
      // wstrcmp is in strutils.h
      if (!wstrcmpi((WCHAR*)m_state.bstrListColumn, m_pColumns[nIndex].pwszName))
         break;
   }
   // If ListColumn is not one of the columns,
   // then return S_FALSE for no data.
   //
   if (nIndex < 0)
      return S_FALSE;

   // Zero out the binding structure.
   //
   DBBINDING   dbBinding;
   ZeroMemory((void*)&dbBinding, sizeof dbBinding);

   // Set the column ordinal.
   //
   dbBinding.iOrdinal   = m_pColumns[nIndex].iOrdinal;

   // Save the ordinal for the list column.
   //
   m_listColumnOrdinal = m_pColumns[nIndex].iOrdinal;

   // Set the offset of the start of data in the buffer.
   //
   dbBinding.obValue   = 0;

   // Request data to be returned as a BSTR.
   //
   dbBinding.wType      = DBTYPE_BSTR;

   // Maximum length of the buffer is sizeof(BSTR).
   //
   dbBinding.cbMaxLen   = sizeof(BSTR);

   // We want only the value part of the column data.
   //
   dbBinding.dwPart   = DBPART_VALUE;

   // Create an accessor with this column binding.
   // DBSTATUS is an optional argument. If this
   // fails, you can check the status in the
   // debugger.
   //
   DBSTATUS dbStatus;
   hr = CRowset::CreateAccessor(&dbBinding, 1, m_hRowAccessor, &dbStatus);

   return hr;
}

Freeing the row accessor is implemented as follows:

HRESULT COLEDBListControl::FreeRowAccessor(void)
{
   HRESULT hr = S_OK;

   if (m_hRowAccessor)
   {
      hr = CRowset::ReleaseAccessor(m_hRowAccessor);
      m_hRowAccessor = NULL;
      m_listColumnOrdinal = -1;
   }
   return hr;
}

Getting Data

Once an accessor is created and rows are fetched, the accessor can be used in conjunction with any row handle to get data for that row. Getting data requires a buffer allocated by the consumer that is large enough to hold row data as defined by the binding information used to create the accessor. It is also the responsibility of the consumer to deallocate memory that was allocated by the provider on the consumer's behalf. These operations are illustrated by COLEDBListControl::FillListContent.

COLEDBListControl::FillListContent

This method sequentially fetches all the rows in the attached rowset and fills the list content with text data from the list column. It is implemented as follows:

void COLEDBListControl::FillListContent(void)
{
   // Don't do anything if this is called at design time
   // or if an attempt to fill the list has
   // been done since the last call to ClearContent.
   //
   if (DesignMode() || m_fFilledListContent)
      return;

   // Ensure that the row accessor is initialized.
   //
   HRESULT hr = InitRowAccessor();
   if (S_OK != hr)
      return;

   // Turn off redraw while the list is
   // being populated.
   //
   SendMessage(m_hwnd, WM_SETREDRAW, 0, 0);

   BSTR bstrData;   // Buffer to hold row data
   HROW *phRows;   // Array of row handles returned by callee
   ULONG cRows;   // Number of rows actually fetched

   // Fetch until the end of the rowset.
   //
   do
   {
      // Fetch at offset zero from the current fetch
      // position. ROWS_REQUEST is an arbitrary
      // request If there is discontinuity in the
      // rowset due to deletions; or the number of rows
      // requested is greater then the number of active
      // rows allowed, then the number of rows fetched
      // may be less than the number of rows requested.
      //
      phRows = NULL;   // Must be NULL for callee to allocate

      hr = CRowset::GetNextRows(0, ROWS_REQUEST, phRows, cRows);

      // If no rows were returned then try another fetch.
      //
      if (!cRows)
         continue;

      for (ULONG n = 0; n < cRows; n++)
      {
         // Get data for the row.
         //
         bstrData = NULL;
         CRowset::GetData(phRows[n], m_hRowAccessor, (void*)&bstrData);

         // bstrData may be NULL.
         //
         if (bstrData)
         {
            // Convert to ANSI text.
            MAKE_ANSIPTR_FROMWIDE(szText, bstrData);

            // Free the BSTR.
            SysFreeString(bstrData);
            if (szText)
               SendMessage(m_hwnd, LB_ADDSTRING, 0, (LPARAM)szText);
            else
            {
               hr = E_OUTOFMEMORY;
               break;
            }
         }
         else
            // Add a blank line if data is NULL.
            SendMessage(m_hwnd, LB_ADDSTRING, 0, (LPARAM)(LPSTR)"");
      }
      // Release row handles and free the
      // callee-allocated array.
      //
      CRowset::FreeRows(phRows, cRows);
   }
   // Continue until an operation fails
   // or until the end of the rowset.
   //

while (!(FAILED(hr) || DB_S_ENDOFROWSET == hr));

   // Turn redraw back on and invalidate
   // the control.
   //
   SendMessage(m_hwnd, WM_SETREDRAW, (WPARAM)TRUE, 0);
   m_fFilledListContent = TRUE;

   // Update the Text property.
   //
   UpdateText(DB_NULL_HROW);
}

Adding Notifications

OledbList needs to receive notifications to detect the following conditions:

  • When data becomes available or unavailable.

  • When the current row position has changed.

  • When changes are made to the row or rowset state.

Respectively, each of the items in the list is handled by implementing IDataSourceListener, IRowPositionChange, and IRowsetNotify.

Implementing the IDataSourceListener Interface

Implementing IDataSourceListener is as simple as declaring a class that inherits from the interface, then providing implementations for all of its methods. IDataSource calls IDataSourceListener methods after the operation that caused the notification has finished. Consumers can use these notifications for informational purposes only.

Implementing the IRowPositionChange Interface

IRowPositionChange is a COM interface; therefore, its implementation must also include the implementation of the IUnknown interface. To implement IRowPositionChange, declare a class that inherits from the interface, then implement all IRowPositionChange and IUnknown methods.

Implementing the IRowsetNotify Interface

IRowsetNotify is also a COM interface. To implement IRowsetNotify, declare a class that inherits from the interface, then implement all IRowsetNotify and IUnknown methods.

COLEDBListControl::CNotifications

This class illustrates how to implement the notification interfaces using the ActiveX Control Framework. CUnknownObject provides COM capabilities to classes derived from it, and greatly reduces the complexity of implementing COM interfaces and aggregation.

COLEDBListControl defines CNotifications in line for its m_notifications member variable. The definition of COLEDBListControl::CNotifications is in Oledblistctl.cpp. It is defined as follows:

   class CNotifications :    public CUnknownObject,
                           public IRowPositionChange,
                    public IRowsetNotify,
                    public IDataSourceListener
   {
   public:
      CNotifications();
      ~CNotifications();

   public:
      // IUnknown
      //
      DECLARE_STANDARD_UNKNOWN();

      // IRowPositionChange methods
      //
      STDMETHOD(OnRowPositionChange)(DBREASON eReason, DBEVENTPHASE ePhase, BOOL fCantDeny);

      // IRowsetNotify methods
      //
      STDMETHOD(OnFieldChange)(IRowset *prowset, HROW hRow, ULONG cColumns, ULONG rgColumns[], DBREASON eReason, DBEVENTPHASE ePhase, BOOL fCantDeny);
      STDMETHOD(OnRowChange)(IRowset *prowset, ULONG cRows, const HROW rghRows[], DBREASON eReason, DBEVENTPHASE ePhase, BOOL fCantDeny);
      STDMETHOD(OnRowsetChange)(IRowset *prowset, DBREASON eReason, DBEVENTPHASE ePhase, BOOL fCantDeny);

      // IDataSourceListener methods
      //
      STDMETHOD(dataMemberChanged)(THIS_ DataMember bstrDM);
      STDMETHOD(dataMemberAdded)(THIS_ DataMember bstrDM);
      STDMETHOD(dataMemberRemoved)(THIS_ DataMember bstrDM);

   // Overrideables
   protected:
      // Aggregation query interface support
      //
      virtual HRESULT InternalQueryInterface(REFIID riid, void **ppvObjOut);

   // Implementation
   protected:
      CConnectionsArray   m_connections;   // Manages notifcation connections

      COLEDBListControl* OLEDBList(void);

   // Operations
   public:
      HRESULT Connect(void);
      HRESULT Disconnect(void);

   // Operators
   public:
      operator IUnknown *() {return PrivateUnknown();}
   }
   m_notifications;   // Notification object
   friend class CNotifications;
};

Making the Connection

COLEDBListControl::AttachRowset appropriately calls m_notifications.Connect and m_notifications.Disconnect to engage and disengage notifications. The implementation of CNotifications::Connect and CNotifications::Disconnect uses CConnectionsArray (see Advcon.h and Advcon.cpp) to manage connection links for IRowPositionChange and IRowsetNotify. They call IDataSource::addDataSourceListener and IDataSource::removeDataSourceListener for IDataSourceListener. See the implementation of these methods in Oledblistctl.cpp for more details.

Handling Notifications

It is not necessary to respond to all notifications, even though all methods for a particular notification interface must be implemented. When a notification is not of interest, its implementation should return S_OK. For IRowsetNotify notifications, DB_S_UNWANTEDREASON or DB_S_UNWANTEDPHASE may also be returned.

The following section describes how CNotifications implements its notification handlers.

CNotifications::dataSourceChanged (IDataSourceListener)

The data member argument is compared, without case sensitivity, to the data member that OledbList is using. If they match, the COLEDBListControl::AttachRowset is called with a Null argument to reset the list.

STDMETHODIMP COLEDBListControl::CNotifications::dataMemberChanged(DataMember bstrDM)
{
   // Compare DataMembers.
   //
   if ((NULL == OLEDBList()->m_state.dmDataMember && (NULL == bstrDM || '\0' == bstrDM[0]))
    || !bstrcmpi(bstrDM, (BSTR)OLEDBList()->m_state.dmDataMember))
   {
      // Reset because the attached rowset may
      // no longer be valid or may have changed.
      //
      OLEDBList()->AttachRowset(NULL);
   }
   return S_OK;
}

CNotifications::OnRowPositionChange (IRowPositionChange)

This notification is used to detect when the current row position has changed. Two things must happen in response to this notification:

  • Prior to the row position change, the current row's list column must be updated if the Text property is dirty.

  • After the row position has changed, the Text property must be updated with the list column of the new row.
STDMETHODIMP COLEDBListControl::CNotifications::OnRowPositionChange(DBREASON eReason, DBEVENTPHASE ePhase, BOOL fCantDeny)
{
   // Get the handle of the current row.
   //
   DBPOSITIONFLAGS   posFlags    = NULL;
   HROW         hrow       = NULL;
   HRESULT         hr         = OLEDBList()->GetCurrentRow(hrow, &posFlags);

   if (SUCCEEDED(hr))
   {
      // Prior to row position change
      //
      if (DBEVENTPHASE_OKTODO == ePhase)
         OLEDBList()->UpdateCurrentRow(hrow);

      // After row position change
      //
      else if (DBEVENTPHASE_DIDEVENT == ePhase)
         OLEDBList()->UpdateText(hrow);

      // Must release the row
      //
      OLEDBList()->ReleaseRows(&hrow, 1);
   }
   return S_OK;
}

CNotifications::OnFieldChange (IRowsetNotify)

This notification is sent when columns in a row are being changed. OledbList responds only after the list column has changed:

  • If the current row list column has changed, the Text property must be updated if it is not dirty.

  • The text for the corresponding row in the list is updated.
STDMETHODIMP COLEDBListControl::CNotifications::OnFieldChange(IRowset *prowset, HROW hRow, ULONG cColumns, ULONG rgColumns[], DBREASON eReason, DBEVENTPHASE ePhase, BOOL fCantDeny)
{
   if (DBEVENTPHASE_DIDEVENT == ePhase)
   {
      // Proceed only if the ListColumn is one
      // of the columns that has changed.
      //
      while (cColumns--)
      {
         if (rgColumns[cColumns] == OLEDBList()->m_listColumnOrdinal)
            break;
      }
      if (cColumns < 0)
         return S_OK;

      // Update the Text property only if it has
      // not been changed.
      //
      if (!OLEDBList()->m_fTextDirty)
      {
         DBPOSITIONFLAGS   posFlags    = NULL;
         HROW         hrowCurrent   = NULL;
         HRESULT         hr         = OLEDBList()->GetCurrentRow(hrowCurrent, &posFlags);

         // Update Text property if this is the
         // current row.
         //
         if (SUCCEEDED(hr) && hrowCurrent == hRow)
            OLEDBList()->UpdateText(hRow);

         // Must release the row
         //
         OLEDBList()->ReleaseRows(&hrowCurrent, 1);
      }
      // Update the corresponding row in the list.
      //
      OLEDBList()->UpdateListRow(hRow);
   }
   return DB_S_UNWANTEDPHASE;
}

CNotifications IRowsetNotify::OnRowChange (IRowsetNotify)

This notification is sent when rows are changing. Rows may change for a number of reasons. OledbList responds only after rows are inserted or deleted.

STDMETHODIMP COLEDBListControl::CNotifications::OnRowChange(IRowset *prowset, ULONG cRows, const HROW rghRows[], DBREASON eReason, DBEVENTPHASE ePhase, BOOL fCantDeny)
{
   // Respond only after the row change.
   //
   if (DBEVENTPHASE_DIDEVENT == ePhase)
   {
      switch(eReason)
      {
      case DBREASON_ROW_UNDODELETE:
      case DBREASON_ROW_UNDOINSERT:
      case DBREASON_ROW_DELETE:
      case DBREASON_ROW_INSERT:
         // Clear the list content so
         // it will be refilled when the
         // control repaints.
         //
         // This is not the optimal response
         // but it gets the job done.
         //
         OLEDBList()->ClearContent();
         break;

      default:
         return DB_S_UNWANTEDREASON;
      }
      return S_OK;
   }
   return DB_S_UNWANTEDPHASE;
}

CNotifications IRowsetNotify::OnRowsetChange (IRowsetNotify)

This notification occurs after the rowset is released by the provider or has changed. The response is to detach the rowset.

STDMETHODIMP COLEDBListControl::CNotifications::OnRowsetChange(IRowset *prowset, DBREASON eReason, DBEVENTPHASE ePhase, BOOL fCantDeny)
{
   // Assume the rowset is no longer valid.
   //
   OLEDBList()->AttachRowset(NULL);
   return S_OK;
}

Building the Control

To build and register the control

  • From the Build menu in Microsoft Visual C++ 5.0, click Build OLEDBList.OCX. Your control is built and ready to be inserted into a container.


Overview of Migrating ICursor-Based ActiveX Controls to OLE DB

The complexity of the conversion process varies based on design and implementation techniques used for a particular control. For the most part, you can expect to make modifications in the following areas:

  • Row fetching and data caching code. You’ll spend most of your time in this area because of the major difference between the two data access methods.

  • Updating row data, adding and deleting rows. This area also may require some effort.

  • Column information and bindings. The concept is the same between the two, so conversion should not be difficult.

  • Notification handling. Nearly all ICursor type events have an OLE DB equivalent. This is facilitated by OLE DB's organized approach to notifications.

You can expect very little change in:

  • How your control behaves from a user’s point of view. Your non-data-bind properties virtually remain the same. The control's user interface should not change.

  • Your paint routines. If they are encapsulated properly from the start, the routines should remain intact.

Considerations Before Migrating

Migrating an existing ICursor-based ActiveX control to OLE DB requires that you understand:

  • How rowsets are consumed (see Chapter 1, “Consuming Rowsets” for more information).

  • Basic differences between simple and complex controls (see Chapters 2 and 3).

  • Which OLE DB interfaces provide the same functionality as their ICursor equivalents (see “Interface Mapping Reference” later in this chapter).

Before proceeding, you may want to review the samples shipped with this SDK. Experiment with them to get accustomed to using OLE DB before starting your migration. It may be helpful to look for similarities in the way your existing control and the samples handle data access.

Once you’ve reviewed the samples, you have to decide whether to migrate or rewrite your control. If you decide to migrate, you may want to go through your existing code, and restructure and redesign it to make migration easier. This means removing any ICursor-dependent code from code sections you want to keep.


Differences between ICursor-Based and OLE DB ActiveX Controls

The key to successful migration is understanding the difference between the two data access methods and how they apply to your controls.

All ICursor-based ActiveX controls possess the following characteristics:

  • The control has an ICursor type property that is used by a host to detect whether the control supports data binding.

  • This property is available to users only at design time, so that a data source can be assigned to it. At run time this property cannot be set; when read, it returns nothing.

  • The control writer does not supply code for this property. Rather, the writer must implement the IBoundObject interface to receive notifications of changes in the data source and take appropriate action.

OLE DB ActiveX controls possess these characteristics:

  • The control implements two properties, one of type DataSource and the other of type DataMember. These properties are used by a host to detect whether the control supports data binding.

  • Both properties are available to users at design time and run time.

  • The control writer must implement these properties and take appropriate action when their values are changed.


Interface Mapping Reference

Use the following table to inventory the ICursor interfaces you are using and match them with their OLE DB equivalents. It’s important to learn as much as possible about these equivalents. See the OLE DB Programmer’s Reference and Chapter 5 of this document for interface details. This should help you make a better estimate of the amount of work you have to do.

Cursor interface | OLE DB interface | When to use | Comments
IBoundObjectSite | IDataSource | To retrieve the data source. | The control must call QueryInterface on its client site for IBoundObjectSite. IDataSource is set for the control using its DataSource property.
IBoundObject | IDataSourceListener | To be notified of global changes in the data source. | IBoundObject is a COM interface by the control for which containers call QueryInterface. IDataSourceListener is a simple callback interface implemented by the control, which it adds to the data source using IDataSource::addDataSourceListener.
ICursor | IRowset | To consume data. | ICursor-based consumers use the base interface ICursor to do sequential fetches. OLE DB consumers use the base interface IRowset to do sequential row fetches. Both interfaces are the starting point on which their respective consumers call QueryInterface for more specialized data access interfaces.
ICursorMove and ICursorScroll | IRowsetLocate | To arbitrarily fetch rows. | ICursorMove is used to fetch rows. ICursorScroll extends ICursorMove to support approximate positions and row count for scroll bar interfaces. IRowsetLocate provides this functionality in one interface.
ICursorFind | IRowsetFind | To perform searches for rows based on a simple filter. | ICursorFind supports multiple column filters. IRowsetFind searches a single column.
ICursorUpdateARow | IRowsetChange and IRowsetUpdate | To make changes to a row or a single column. | ICursorUpdateARow acts only on the current row. IRowsetChange acts on a specified row. IRowsetUpdate is available when changes to a rowset can be undone, or committed at a later time.
IEntryID | None | To access large column data or OLE objects. | See the OLE DB Programmer's Reference, Chapter 7, “BLOBs and OLE Objects” for details.
INotifyDBEvents | IRowPositionChange and IRowsetNotify | To receive notifications about row currency changes and changes in row states. | Use IRowPositionChange for notifications about row currency changes. Use IRowsetNotify for changes to the rowset or individual row states.


Overview of Interface Leveling

To meet its goal of providing data access to all types of data in a COM environment, OLE DB is designed as a component technology. 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. OLE DB creates components by identifying common characteristics among different data providers and services, and defining common interfaces to expose those characteristics. So, for example, while a rowset can be obtained through a number of very different mechanisms, the end result is still a rowset with well-defined interfaces, methods, and characteristics. Navigating the results of a complex multitable join is no different from navigating the results of a 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 an ISAM file to a text file or data stream. Obviously, these types of 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. Instead of looking at the differences between providers, OLE DB looks at the similarities and defines the common interfaces used to expose those similarities.

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

OLE DB consumers can choose their level of interoperability with OLE DB providers. Consumers can write to either a specific provider, in which case they know the functionality of the provider at design time, or to consume generic providers. To consume generic providers, the consumer may do one of the following:

  1. Consume a minimum set of functionality and work with all OLE DB providers.

  2. Consume a higher level of functionality and query for support of extended functionality.

  3. Consume a higher level of functionality and invoke service components to implement missing functionality, where available.

Minimum Consumer Functionality

The following table describes the minimum level of functionality that a consumer can be expected to support when talking to any OLE DB provider. This functionality is either implemented directly by the provider, or on behalf of the provider by common service components.

Object | Interface Behavior
DataSource | IConnectionPointContainer for: IDBAsynchNotify
 | IDataSource 1
 | IDBAsynchStatus
 | IDBCreateSession
 | IDBInitialize
 | IDBProperties
 | IPersist
 | ISupportErrorInfo
DataSource Behavior | DBPROP_INIT_PROMPT
Session | IGetDataSource
 | IOpenRowset
 | ISessionProperties
 | ITransactionLocal 3
Rowset | IAccessor
 | IColumnsInfo
 | IConnectionPointContainer for: IDBAsynchNotify, IRowsetNotify
 | IConvertType
 | IDBAsynchNotify
 | IDBAsynchStatus
 | IRowset
 | IRowsetChange 2
 | IRowsetFind
 | IRowsetIdentity
 | IRowsetInfo
 | IRowsetLocate
 | IRowPosition1
 | IRowsetScroll
 | ISupportErrorInfo
Rowset Behavior | DBPROP_CANHOLDROWS
 | DBPROP_CANFETCHBACKWARDS
 | DBPROP_CANSCROLLBACKWARDS
 | DBPROP_COMMITPRESERVE 3
 | DBPROP_REMOVEDELETED 2
 | DBPROP_OWNUPDATEDELETE 2
 | DBPROP_OWNINSERT 2

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

(2) Minimal level of support for providers wanting to expose update capability. Providers not supporting this functionality are treated as read-only by generic consumers.

(3) Minimal level of support for providers wanting to support transactions to general consumers.


Providers

Providers choose the functionality that they support according to the native functionality of the data that they expose. However, in order to be consumed by generic consumers, providers must be able to meet at least the minimum consumer functionality defined above. The developer of the provider can achieve this functionality in one of three ways:

  1. By writing to a set of simple provider-writer interfaces in Java, VB, or C++, and using the Provider Writer's toolkit.

  2. By implementing the minimum provider functionality defined below directly in C or C++, and using service components to implement the additional functionality required for the minimal consumer functionality.

  3. By implementing at least the "Level 1" functionality defined below directly in C or C++.

In addition, providers may expose interfaces for these sets of functionality: rowset processing, commands, and indexes.

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

Minimum Provider Functionality

Provider writers are strongly encouraged to implement the full set of interfaces that apply to their particular type of data. At a minimum, the following interfaces and behavior must be implemented in order to be considered a generic OLE DB provider. Providers implementing this minimum provider functionality can be aggregated by common service components available in the SDK in order to support the minimum consumer functionality.

Object | Interface Behavior
DataSource | IDBCreateSession
 | IDBInitialize
 | IDBProperties
 | IPersist
DataSource Behavior | DBPROP_INIT_PROMPT
Session | IGetDataSource
 | IOpenRowset
 | ISessionProperties
 | ITransactionLocal 3
Rowset | IAccessor
 | IConvertType
 | IColumnsInfo
 | IRowset
 | IRowsetChange 2
 | IRowsetIdentity
 | IRowsetInfo
Rowset Behavior | DBPROP_CANHOLDROWS
 | DBPROP_CANFETCHBACKWARDS
 | DBPROP_COMMITPRESERVE 3
 | DBPROP_OWNUPDATEDELETE 2
 | DBPROP_OWNINSERT 2
 | DBPROP_REMOVEDELETED 2

(2) Minimal level of support for providers wanting to expose update capability. Providers not supporting this functionality are treated as read-only by generic consumers.

(3) Minimal level of support for providers wanting to support transactions to general consumers.

Level 1 Providers

Providers that implement the full set of Level 1 interfaces can be consumed by general consumers without the support of additional service components. Providers that can fully implement this level of functionality are strongly encouraged to do so.

Providers implementing the following functionality in addition to the minimum provider functionality are consumed as full Level 1 providers.

Object | Interface Behavior
DataSource | IConnectionPointContainer for: IDBAsynchNotify
 | IDBAsynchStatus
 | ISupportErrorInfo
Rowset | IDBAsynchStatus
 | IConnectionPointContainer for: IRowsetNotify, IDBAsynchNotify
 | IRowsetFind
 | IRowsetLocate
 | IRowsetScroll
 | ISupportErrorInfo
Rowset Properties | DBPROP_CANSCROLLBACKWARDS

Extended Interfaces

The following additional functionality may be supported by general-purpose providers. Providers are encouraged to support as much from this list as makes sense for their particular type of data.

Object | Interface Behavior
DataSource | IDataSourceAdmin
 | IPersistFile
Session | IDBSchemaRowset
 | IDBView
 | ITableDefinition
 | ITransactionJoin 3
 | ITransactionObject 3
Rowset | IColumnsRowset
 | IRowsetIdentity
 | IRowsetLocate
 | IRowsetResynch 2
 | IRowsetUpdate 2
Rowset Properites | DBPROP_ABORTPRESERVE 3
 | DBPROP_CHANGEINSERTEDROWS
 | DBPROP_LITERALBOOKMARKS
 | DBPROP_ORDEREDBOOKMARKS
 | DBPROP_LITERALIDENTITY
 | DBPROP_BOOKMARKSKIPPED
 | DBPROP_OTHERINSERT
 | DBPROP_OTHERUPDATEDELETE
 | DBPROP_RETURNPENDINGROWS
 | DBPROP_STRONGIDENTITY
 | DBPROP_IMMOBILEROWS
Blobs | DBPROP_OLEOBJECTS
 | DBPROP_MULTIPLESTORAGEOBJECTS
 | DBPROP_STRUCTUREDSTORAGE
 | ISequentialStream
 | IStream
 | IStorage
 | ILockBytes
 | DBPROP_BLOCKINGSTORAGEOBJECTS=FALSE

(2) Extended functionality that may be exposed by updatable providers.

(3) Extended functionality that may be exposed by transacted providers.

Rowset Processing Behavior

Providers that support filtering or sorting of data are encouraged to support the following rowset processing interfaces.

Object | Interface Behavior
Session | IDBView
View | IViewFilter
 | IViewSort
 | IColumnsInfo
 | IAccessor
 | ISupportErrorInfo
 | IViewRowset
 | IViewChapter
Rowset | IChapteredRowset
 | IRowsetView
 | IRowsetFind

Command Behavior

Providers that support more sophisticated query/statement execution support commands. In order to be a general provider of commands, the provider must support the following set of functionality in addition to the minimal provider functionality defined above.

Object | Interface Behavior
DataSource | IDBInfo
Session | IDBCreateCommand
 | ITransactionLocal
Command | IAccessor
 | ICommand
 | ICommandText
 | IColumnsInfo
 | ICommandProperties
 | ICommandWithParameters
 | IConvertType
 | ISupportErrorInfo
Command Properties | MAXTABLESINSELECT>1 (not true for consumer)
Custom Error | ISQLErrorInfo

The following set of extended command functionality may also be supported by command providers.

Object | Interface Behavior
Session | ITransactionJoin
 | ITransactionObject
Command | ICommandPrepare
 | IColumnsRowset
Mult Results Object | IMultipleResults
Rowset | IRowsetResynch
Rowset Properties | DBPROP_CANSCROLLBACKWARDS

Indexing Behavior

Providers built on indexed data can expose index functionality to service providers such as query processors by implementing the following interfaces.

Object | Interface Behavior
Session | IDBView
Rowset | IRowsetIndex
 | IIndexDefinition
 | IChapterRowset
 | IRowsetView
View | IViewFilter
 | IViewSort
 | IColumnsInfo
 | IAccessor
 | ISupportErrorInfo
 | IViewRowset
 | IViewChapter


Overview of API Reference

This chapter provides reference information about the Application Programming Interfaces (APIs) that ActiveX control writers use and implement with OLE DB. The chapter includes discussions of the following:

  • IRowPosition interface

  • IRowPositionChange interface

  • IRowsetFind interface

  • CoTypes, structures, and enumerated types

You can find these interfaces in Oledb.h.

Two other interfaces are also described:

  • IDataSource

  • IDataSourceListener

These interfaces are in \INCLUDE\Msdatasrc.h.


IRowPosition

IRowPosition allows multiple components to share the concept of a current row over a rowset.

When to Implement

IRowPosition is not implemented by the provider. It is implemented by a common service provider component that works over any rowset.

When to Call

IRowPosition is used by multiple components to coordinate the concept of a current position within a rowset. Components call GetRowPosition to get the current row and SetRowPosition to set a new position. Changing the current row generates notification for any listeners of the RowPosition object.

Method | Description
ClearRowPosition | Clears the row position.
GetRowset | Returns the current underlying rowset.
GetRowPosition | Retrieves the current row position.
Initialize | Sets the rowset to be used by the RowPosition object.
SetRowPosition | Sets the current row position. ClearRowPosition must be called prior to setting the new row position.


IRowPosition::Initialize

Initializes the RowPosition object by setting the source rowset.

HRESULT Initialize (
IUnknown *  pSourceRowset);

Parameters

pSourceRowset [in]

Pointer to an interface on the source rowset.

Return Code

S_OK

The method succeeded.

E_FAIL

A provider-specific error occurred.

E_NOINTERFACE

pSourcesRowset does not support the IRowset interface.

DB_E_ALREADYINITIALIZED

The RowPosition object has already been initialized.


IRowPosition::GetRowset

Returns a pointer to the underlying rowset.

HRESULT GetRowset (
REFIID          riid,
IUnknown **      ppRowset);

Parameters

riid [in]

The requested IID for the rowset returned in *ppRowset.

ppRowset [out]

A pointer to memory in which to return the interface pointer of the underlying rowset.

Return Code

S_OK

The method succeeded.

E_NOINTERFACE

riid referenced IID_NULL. ppRowset did not support the interface specified in riid.

E_UNEXPECTED

There is no current rowset.

Comments

Before ppRowset is returned, it is added by reference by the RowPosition object.


IRowPosition:ClearRowPosition

Clears the value of the current hRow.

HRESULT ClearRowPosition ();

Parameters

None

Return Code

S_OK

The method succeeded. Calling GetRowPosition returns a phRow value of DB_NULL_ROW, and a pdwPositionFlag value of DBPOSITION_NOROW.

E_FAIL

A provider-specific error occurred. The row position is unchanged.

DB_E_CANCELED

The operation was canceled by a client during notification. The row position is unchanged.

E_UNEXPECTED

ClearRowPosition was called twice in a row.

Comments

ClearRowPosition fires the following notification phases with the reason DBREASON_ROWPOSITION_CLEARED:

  • DBEVENTPHASE_OKTODO

  • DBEVENTPHASE_ABOUTTODO

  • DBEVENTPHASE_FAILEDTODO (if either of the previous events is canceled)

A call to ClearRowPosition should always be followed immediately by a call to SetRowPosition, which fires the remaining phases.


IRowPosition::SetRowPosition

Sets the current row position to a new hRow.

HRESULT SetRowPosition (
HCHAPTER      hChapter,
HROW         hRow,
DBPOSITIONFLAGS   dwPositionFlags);

Parameters

hChapter [in]

The chapter associated with the current row, or DB_NULL_HCHAPTER if the rowset is not chaptered.

hRow [in]

The new current row.

dwPositionFlags [in]

A flag indicating additional information about the new row position.

Value | Meaning
DBPOSITION_OK | hRow specifies the current row position.
DBPOSITION_NOROW | hRow is DB_NULL_ROW, and the current row position is undefined.
DBPOSITION_BOF | hRow is DB_NULL_ROW, and the row position is before the beginning of the rowset.
DBPOSITION_EOF | hRow is DB_NULL_ROW, and the row position is off the end of the rowset.

Return Code

S_OK

The method succeeded.

E_FAIL

A provider-specific error occurred.

DB_E_CANCELED

The operation was canceled by a client during notification. If the current row position cannot be reset, then the current row will be set to DB_NULL_ROW with a position flag indicating DBPOSITION_NOROW.

E_UNEXPECTED

SetRowPosition was called without first calling ClearRowPosition.

Comments

SetRowPosition must always be called immediately following ClearRowPosition.

SetRowPosition fires the following notification phases with either DBREASON_ROWPOSITION_CHANGED or DBREASON_ROWPOSITION_CHAPTERCHANGED:

  • DBEVENTPHASE_SYNCHAFTER

  • DBEVENTPHASE_DIDEVENT or DBEVENTPHASE_FAILEDTODO

The RowPostion object maintains a reference count on the hRow by calling AddRefRows for the hRow when SetRowPosition is called.


IRowPosition::GetRowPosition

Returns the hRow of the current row position.

HRESULT GetRowPosition (
HCHAPTER *      hChapter,
HROW *         phRow
DBPOSITIONFLAGS *   pdwPositionFlags);

Parameters

hChapter [out]

The chapter associated with the current row, or DB_NULL_HCHAPTER if the rowset is not chaptered.

phRow [out]

A pointer to memory in which to return the hRow of the current row position, or DB_NULL_HROW if there is no current row position.

pdwPositionFlags [out]

A flag indicating additional information about the row position.

Value | Meaning
DBPOSITION_OK | hRow specifies the current row position.
DBPOSITION_NOROW | hRow is DB_NULL_HROW, and the current row position is undefined.
DBPOSITION_BOF | hRow is DB_NULL_HROW, and the row position is before the beginning of the rowset.
DBPOSITION_EOF | hRow is DB_NULL_HROW, and the row position is off the end of the rowset.

Additional values for this flag may be defined in the future. Consumers should be prepared to handle other values for this flag by relying only on the value of phRow for undefined values of pdwPositionFlags.

Return Code

S_OK

The method succeeded.

E_FAIL

The interface has not been initialized.

Comments

Before returning the hRow, the RowPosition object calls AddRefRows to increment the reference count of the hRow.


IRowPositionChange

IRowPositionChange is the callback interface that a consumer must support to be notified when the current row position is changed.

The RowPosition object supports IConnectionPointContainer and the consumer calls FindConnectionPoint for IID_IRowPostionChange to obtain the correct IConnectionPoint interface. The consumer then advises that connection point to connect and supplies a pointer to the consumer’s IRowPositionChange interface.

For more information about notifications, see the OLE DB Programmer’s Reference, Chapter 9, “Notifications.”

When to Implement

IRowPositionChange is implemented by consumers that require notification about changes to the current row position.

When to Call

IRowPositionChange is called by the RowPosition object when a consumer attempts to change the current row position.

Method | Description
OnRowPositionChange | Notifies the consumer of a RowPosition object of a change to the current row position.


IRowPositionChange::OnRowPosition Change

Notifies the consumer of a RowPosition object of a change to the current row position.

HRESULT OnRowPositionChange (
DBREASON       eReason,
DBEVENTPHASE      ePhase,
BOOL         fCantDeny);

Parameters

eReason [in]

The reason of the event that caused this change. If this value is not recognized by the method, the method returns S_OK or DB_S_UNWANTEDREASON.

ePhase [in]

The phase of this notification.

fCantDeny [in]

When this flag is set to TRUE, the consumer cannot veto the event by returning S_FALSE because the provider cannot undo the event.

Return Code

S_OK

The method succeeded.

S_FALSE

The event/phase is vetoed by reason of logical objection or inability to implement.

DB_S_UNWANTEDPHASE

The consumer is not interested in receiving this phase for this reason. The provider can optimize by making no further calls with this reason and phase. The phases for other reasons are unaffected.

DB_S_UNWANTEDREASON

The consumer is not interested in receiving any phases for this reason. The provider can optimize by making no further calls with this reason.

E_FAIL

A provider-specific error occurred.

Comments

Possible reasons are DBREASON_ROWPOSITION_CLEARED, DBREASON_ROWPOSITION_CHANGED, and DBREASON_ROWPOSITION_CHAPTERCHANGED.

DBREASON_ROWPOSITION_CLEARED generates DBEVENTPHASE_OKTODO and DBEVENTPHASE_ABOUTTODO, and DBEVENTPHASE_FAILEDTODO if the notification is canceled.

DBREASON_ROWPOSITION_CHANGED and DBREASON_ROWPOSITIONCHAPTERCHANGED generate DBEVENTPHASE_SYNCHAFTER and DBEVENTPHASE_DIDEVENT, or DBEVENTPHASE_FAILEDTODO if the notification is canceled.

Consumers are required to handle reasons that are not defined in this interface by returning S_OK or DB_S_UNWANTEDREASON.


IRowsetFind

The IRowsetFind interface allows consumers to find a row within the rowset matching a specified value.

When to Implement

IRowsetFind is required for all providers that intend to support general consumers.

When to Call

Consumers call IRowsetFind to locate the next row in the rowset matching a specified value.

Method | Description
FindNextRow | Returns the next row matching the specified criteria.


IRowsetFind::FindNextRow

Begins at the specified bookmark and finds the next row matching the specified value. IRowsetFind does not affect the next fetch position used by IRowset::GetNextRows.

HRESULT FindNextRow (
HCHAPTER                  hChapter,
HACCESSOR               hAccessor,
void *                        pFindValue,
DBCOMPAREOP            CompareOp,
ULONG                     cbBookmark,
const BYTE *               pBookmark,
BOOL                        fSkipCurrent,
LONG                        cRows,
ULONG *                     pcRowsObtained,
HROW **                     prghRows);

Parameters

hChapter [in]

The chapter handle. For nonchaptered rowsets, hChapter is ignored. If this is the first time the chapter is used, the chapter is opened and a reference count is taken to hold the chapter’s state. This reference count is released by calling ReleaseChapter.

hAccessor [in]

Accessor describing the value to be matched. This accessor must describe only a single column. If it describes more than one column, FindNextRow returns DB_E_BADBINDINFO. Valid coercions for FindNextRow are the same as those for SetData.

pFindValue[in]

Pointer to the value to be matched. 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.

CompareOp [in]

Operation to use in comparing the row values.

cbBookmark [in]

Length in bytes of the bookmark, or zero to start from the last row found by IRowsetFind.

pBookmark [in]

Pointer to a bookmark that identifies the row from which to start searching for a match. The consumer is not required to have access rights to this row. However, if this row matches the seek criteria, the provider cannot return it unless the consumer has read permission for it. The consumer can specify a cbBookmark value of zero, and set pBookmark to null, to request that the find occur starting with the last row found by IRowsetFind. If cRows was greater than 1, this is different than the last row returned by IRowsetFind. Thus, forward-only cursors that support IRowsetFind return E_INVALIDARG if cRows is greater than 1. If this is the first time IRowsetFind has been called on the rowset, specifying a null bookmark starts at the beginning of the rowset. If the rowset does not support IRowsetLocate, only the special bookmarks DBBMK_FIRST and DBBMK_LAST, and the null value, can be used. If the rowset does not support scrolling backward, only the null bookmark value can be used. If the rowset is chaptered, the identified row must fall within the specified chapter.

fSkipCurrent [in]

Whether or not to try and match the current row. If FALSE, the current row is compared and, if it matches the criteria, returned. If TRUE, the comparison starts at the first row following the current row in the direction indicated by cRows.

cRows [in]

The number of rows to fetch from the rowset, starting with the first row found. A negative number indicates a backward fetch. Check the DBPROP_CANFETCHBACKWARDS property returned in IRowsetInfo::GetProperties to determine whether this is supported. The search direction is the same as the fetch direction, so a negative count searches backward from the starting position and returns successively earlier rows as subsequent obtained rows. If cRows is zero and there are no other errors, no rows are fetched, but the fetch position for FindNextRow is moved to the next match or off the end of the rowset if no match is found.

pcRowsObtained [out]

A pointer to memory in which to return the actual number of fetched rows.

prghRows [out]

A pointer to memory in which to return an array of handles of the retrieved rows. If *prghRows is not a null pointer on input, it must be a pointer to memory large enough to return the handles of the requested number of rows. If *prghRows is a null pointer on input, the rowset allocates memory for the row handles and returns the address to this memory; the consumer releases this memory with IMalloc::Free after it releases the row handles. If *prghRows was a null pointer on input and *pcRowsObtained is zero on output, the provider does not allocate any memory and ensures that *prghRows is a null pointer on output.

Return Code

S_OK

The method succeeded.

DB_S_ENDOFROWSET

FindNextRow reached the start or the end of the rowset or (in a hierarchical rowset) the start or the end of the chapter, and could not fetch all requested rows because the count extended beyond the end. The next fetch position is before the start or after the end of the rowset. The number of rows actually returned is returned in *pcRowsObtained; this will be less than cRows.

DB_S_ROWLIMITEXCEEDED

Fetching the number of rows specified in cRows would have exceeded the total number of active rows supported by the rowset. The number of rows that were actually fetched is returned in *pcRowsObtained. This condition can occur only when there are more rows available than can be handled by the rowset. Thus, this condition never conflicts with those described in DB_S_ENDOFROWSET and DB_S_STOPLIMITREACHED, both of which imply that no more rows were available.

DB_S_STOPLIMITREACHED

Returning rows requires further execution of the command, such as when the rowset uses a server-side cursor. Execution has been stopped because a resource limit has been reached. The number of rows that were actually fetched is returned in *pcRowsObtained. Execution cannot be resumed.

DB_S_BOOKMARKSKIPPED

The following behavior is supported only on rowsets that set the DBPROP_BOOKMARKSKIPPED property to VARIANT_TRUE. If this property is VARIANT_FALSE, this return code is never returned. The row specified by *pBookmark was deleted, is no longer a member of the rowset, or is a row to which the consumer does not have access rights. FindNextRow skipped that row and began searching with the next row in the direction indicated by cRows. If this condition occurs along with another warning condition, the method returns the code for the other warning condition. Thus, whenever a consumer receives the return code for another warning condition, it should check to see if this condition occurred.

E_FAIL

A provider-specific error occurred.

E_INVALIDARG

pcRowsObtained or prghRows was a null pointer. cbBookmark was not zero and pBookmark was a null pointer.

E_OUTOFMEMORY

The provider was unable to allocate sufficient memory in which to create instances of the rows or return the row handles.

E_UNEXPECTED

ITransaction::Commit or ITransaction::Abort was called and the object is in a zombie state.

DB_E_BADBINDINFO

The specified accessor specified binding information for more than one column.

DB_E_BADBOOKMARK

*pBookmark was invalid; for example, it was incorrectly formed. *pBookmark did not match any rows in the rowset (this includes the case when the row corresponding to the bookmark has been deleted). The rowset was chaptered and *pBookmark did not apply to the specified chapter.

DB_E_BADCHAPTER

The rowset was chaptered and hChapter was invalid. The rowset was single chaptered and the specified chapter was not the open chapter. The consumer must use the open chapter or release the open chapter before specifying a new chapter.

DB_E_BADCOMPAREOP

CompareOp was an invalid value. CompareOp was DBCOMPAREOPS_BEGINSWITH or DBCOMPAREOPS_CONTAINS and pFindValue was not bound as a string value.

DB_E_CANTFETCHBACKWARDS

cRows was negative and the rowset cannot fetch backward.

DB_E_CANTSCROLLBACKWARDS

A bookmark value of DBBMK_FIRST or DBBMK_LAST was specified and the rowset cannot scroll backward. cRows was greater than 1 and the rowset is forward-only.

DB_E_NOTREENTRANT

The consumer called this method while it was processing a notification, and it is an error to call this method while processing the specified DBREASON value.

DB_E_ROWSNOTRELEASED

The provider requires release of prior HROWs before new ones can be obtained (see IDBPropertyInfo::GetPropertyInfo, DBPROP_CANHOLDROWS).

DB_SEC_E_PERMISSIONDENIED

The consumer did not have sufficient permission to get the rows.

Comments

See IRowsetLocate::GetRowsAt for a complete description of the results for given values for a bookmark, offset, and number of rows to fetch.

FindNextRow searches the rowset in the direction indicated by cRows for a row that matches pFindValue according to the specified CompareOp. Once a match is found, if cRows is greater than 1, additional rows are returned in rowset order as if the consumer called GetNextRows starting with the first matching row. If no rows match the criteria, FindNextRow returns DB_S_ENDOFROWSET and pcRowsObtained is set to zero.

When passing a null bookmark value to FindNextRow, the provider starts from the last row found, not from the last row returned, by FindNextRow. This allows the consumer to search for values within the last set of rows returned.

The rowset does not have to be ordered on the column searched. FindNextRow always finds the next row according to the criteria, regardless of the sort order.

FindNextRow increments by one the reference count of each row for which it returns a handle. Thus, if a handle is returned for a row that has already been fetched, the reference count of that row will be greater than 1. ReleaseRows must be called once for each time the handle to a row has been returned.

If the provider encounters a problem fetching a row—for example, data stored in a text file contains a letter in a numeric column—FindNextRow fetches the row normally, returns the row handle, and returns S_OK. However, when the consumer calls GetData for the row, the provider returns DBSTATUS_E_CANTCONVERTVALUE as the status for the offending column.

FindNextRow must always check for the conditions that cause E_INVALIDARG, E_UNEXPECTED, DB_E_CANTFETCHBACKWARDS, DB_E_CANTSCROLLBACKWARDS, DB_E_NOTREENTRANT, and DB_E_ROWSNOTRELEASED before changing the next fetch position. If it returns any other error, the next fetch position is unknown. For example, the provider might have to perform actions that change the next fetch position to determine that the error DB_E_BADSTARTPOSITION occurred. When the next fetch position is unknown, the consumer usually calls RestartPosition to return it to a known position.

For information about the actions of FindNextRow when it fetches a row that it already has in its internal buffers, see “Uniqueness of Rows in the Rowset” in Chapter 4 of the OLE DB Programmer’s Reference. For information about whether FindNextRow can detect changes made to rows in the rowset, see “Visibility of Changes” in Chapter 5 in the same reference.

See Also

IRowset::GetData, IRowsetLocate::GetRowsAt, IRowsetLocate::GetRowsByBookmark


Cotypes, Structures, and Enumerated Types

The following are shared by both IRowsetFind and IViewFilter:

typedef DWORD DBCOMPAREOP;
enum DBCOMPAREOPSENUM {
     DBCOMPAREOPS_LT = 0,
     DBCOMPAREOPS_LE,
     DBCOMPAREOPS_EQ,
     DBCOMPAREOPS_GE,
     DBCOMPAREOPS_GT,
DBCOMPAREOPS_BEGINSWITH,
DBCOMPAREOPS_CONTAINS,
DBCOMPAREOPS_NE
};

CompareOp | Meaning
DBCOMPAREOPS_LT | Match the first value less than the search value.
DBCOMPAREOPS_LE | Match the first value less than or equal to the search value.
DBCOMPAREOPS_EQ | Match the first value equal to the search value.
DBCOMPAREOPS_GE | Match the first value greater than or equal to the search value.
DBCOMPAREOPS_GT | Match the first value greater than the search value.
DBCOMPAREOPS_BEGINSWITH | Match the first value that has the search value as its first characters. This is valid only for values bound as string data types.
DBCOMPAREOPS_CONTAINS | Match the first value that contains the search value. This is valid only for values bound as string data types.
DBCOMPAREOPS_NE | Match the first value that is not equal to the value.
DBCOMPAREOPS_IGNORE | Ignore the corresponding value. When used with FindNextRow, the provider ignores pFindValue and returns the next cRows rows from the current FindNextRow position, skipping one row if fSkipCurrent is TRUE. When used with SetFilter, the provider ignores the corresponding member in pCriteriaData.


IDataSource

IDataSource is implemented by the data source object. By setting the DataSource property, a data consumer has access to the data source’s IDataSource interface and can ask for a data access interface.

When the data consumer asks for data, it specifies which data access interface it wants via an IID. Here are some examples:

  • IID_IOLEDBSimpleProvider

  • IID_IRowPosition

  • IID_IRowset

  • IID_ICursor

  • IID_IVBDSC

If the consumer asks for IID_IUnknown, the data source should return the most appropriate interface for it.

When the data source is hosted in a container, and the container is setting up the bindings on behalf of the user, the container can be “smart” when obtaining the data access interface. It can intervene and provide mappings if the data consumer requires an interface that the data source does not implement.

When the bindings are set in script, however, the container cannot intervene, so the user must supply the mapping manually by creating a data source adapter object.

Data members are sets of data and are identified by strings (BSTRs).

Method | Description
getDataMember | Returns a data access object (such as a row position) for a given data member.
getDataMemberCount | Returns the number of well-known data members.
getDataMemberName | Returns the name of a well-known data member.
addDataSourceListener | Adds a listener to the notification list.
removeDataSourceListener | Removes a listener from the notification list.


IDataSource::getDataMember

Returns a data access object (such as a row position) for a given data member.

HRESULT getDataMember(
DataMember   bstrDM
REFIID   riid
IUnknown   ** ppunk);

Parameters

bstrDM [in]

A string that describes the data member that represents one or more sets of data supported by the data source.

riid [in]

The interface identifier of the specified data access object.

ppunk [out]

The pointer of the returned interface.

Return Code

S_OK

The method succeeded.

E_FAIL

A provider-specific error occurred.

E_NOINTERFACE

Does not support the specified data access object.

Comments

If the data access object is not available, then Null should be returned for the IUnknown. If the data member string is invalid, then an error should be returned indicating its problem (using ISupportErrorInfo if necessary). The default data member is specified by either a zero-length string or a Null string (they are equivalent). The caller can specify which type of data access object it is interested in by specifying an IID of the main data access interface for the object—for example, IID_IRowPosition, IID_ISimpleTabularData, IID_IRowset, IID_ICursor, IID_IVBDSC, and so forth. If the data source does not support the desired data access object, it returns E_NOINTERFACE. This allows the consumer to try multiple interfaces in order of preference. When asked for IID_IUnknown, the data source should return the data access object that is natural for it.

Note The return value from GetDataMember is always an IUnknown pointer, regardless of the specified IID. The caller of the method must query for the appropriate interface. That is, the IID parameter does not specify which interface, but rather which data access object type to return.


IDataSource::getDataMemberCount

Returns the number of well-known data members.

HRESULT getDataMemberCount(
long*   plCount);

Parameters

plCount [out]

Number of well-known data members.

Return Code

S_OK

The method succeeded.

E_FAIL

A provider-specific error occurred.

Comments

For data sources that do not support enumeration, zero should be returned. Otherwise, the number of well-known data members should be returned.


IDataSource::getDataMemberName

Returns the name of a well-known data member.

HRESULT getDataMemberName(
long   lIndex
DataMember*   pbstrDM);

Parameters

lIndex [in]

An index for the list of data member names.

pbstrDM [out]

A string that describes the data member name.

Return Code

S_OK

The method succeeded.

E_FAIL

A provider-specific error occurred.

Comments

A zero-based index parameter is specified. The index must fall between 0 and GetDataMemberCount –1, or this method returns an error. The default data member ("") should never be returned from this method.


IDataSource::addDataSourceListener

Adds a listener to the notification list.

HRESULT addDataSourceListener(
DataSourceListener*   pDSL);

Parameters

pDSL [in]

Pointer to the interface of the listener. If an event occurs, methods are called to that interface.

Return Code

S_OK

The method succeeded.

E_FAIL

A provider-specific error occurred.

Comments

Checking for duplicate items and returning an error is optional. If the data source knows it will never fire notifications (for example, the data members are always available), it need not implement this method (it can just return NOERROR). However, if it does fire notifications, it must multicast them. The HRESULTs returned from the multicast are ignored, and do not end the multicast.


IDataSource::removeDataSourceListener

Removes a listener from the notification list.

HRESULT removeDataSourceListener(
DataSourceListener*   pDSL);

Parameters

pDSL [in]

Pointer to the interface of the listener.

Return Code

S_OK

The method succeeded.

E_FAIL

A provider-specific error occurred.

Comments

Checking whether the listener has been added and returning an error is optional. If there are duplicates on the list, one is removed, but which one is unspecified. If the data source does not fire notifications, then it can just return NOERROR from this method.


IDataSourceListener

A data source is required to return the same IUnknown pointer each time a consumer asks for a particular data member. Once the notification IDataSourceListener::OnDataMemberChanged has been fired, it is valid for the data source to return a different IUnknown (or the same one). Comparison of two data member strings should be done in a case-insensitive manner.

Data sources generally put off getting the data for a particular data member until it is requested. It is possible to obtain a data member asynchronously by returning Null when data is requested, but then later use IDataSourceListener to indicate that the data member has changed. When the consumer asks for the data in response to the notification, it will be available.

Because the data source can choose to provide an enumeration of its data members, and the list of members might change, IDataSourceListener also has the ability to notify interested parties (such as a property browser) that the set has changed, but it is not a requirement that the list be kept up-to-date.

Method | Description
dataMemberChanged | Notifies a data consumer that a data member has become available, unavailable, or refreshed.
dataMemberAdded | Notifies a data consumer that a new member has been added to the enumeration of well-known data members.
dataMemberRemoved | Notifies a data consumer that a member has been removed from the enumeration of well-known data members.


IDataSourceListener::dataMember Changed

Notifies a data consumer that a data member has become available, unavailable, or refreshed.

HRESULT dataMemberChanged(
DataMember   bstrDM);

Parameters

bstrDM [in]

Name of the data member that has changed.

Return Code

S_OK

The method succeeded.

E_FAIL

A provider-specific error occurred.

Comments

The data consumer should release its hold on any data access interfaces for that member (such as IRowset or IRowPosition), and should call IDataSource::GetDataMember to get the new data (if available).


IDataSourceListener::dataMemberAdded

Notifies a data consumer that a new member has been added to the enumeration of well-known data members.

HRESULT dataMemberAdded(
DataMember   bstrDM);

Parameters

bstrDM [in]

Name of the data member that has been added.

Return Code

S_OK

The method succeeded.

E_FAIL

A provider-specific error occurred.


IDataSourceListener::dataMember Removed

Notifies a data consumer that a member has been removed from the enumeration of well-known data members.

HRESULT dataMemberRemoved(
DataMember   bstrDM);

Parameters

bstrDM [in]

Name of the data member that has been removed.

Return Code

S_OK

The method succeeded.

E_FAIL

A provider-specific error occurred.


Overview of ActiveX Controls Framework

The ActiveX Controls Framework is a sample code base from which you can author ActiveX controls for use in existing containers, such as Microsoft Visual Basic, Microsoft Access, and Microsoft Visual FoxPro®, or in future containers of ActiveX controls. This framework is not supported by the Microsoft Corporation; it is provided “as is” because it may be useful.

It differs from the Microsoft Foundation Class Control Developer’s Kit in many ways. Most notably, this framework is considerably more “bare bones.” Only minimal functionality is provided to the programmer. Little in the way of default handlers for various Windows messages or OLE events has been provided. The ability to add them is there, but the code is not. The code base has been designed primarily for performance and reduced code size as much as possible. Whenever there is a choice between ease of use and performance, the latter is typically chosen.

In addition, the code is designed to take advantage of whatever ActiveX features are available from the host. The code uses any available features and falls back to the older ActiveX behavior for those cases where they are unavailable.

The code base is extremely extensible and all the source code is there: You can make it do anything you want.

Note The Active Template Library (ATL) was created to provide developers with a convenient mechanism for building lightweight and fast Component Object Model (COM) objects. COM is the technical underpinning of all Microsoft ActiveX technologies. ATL 2.0 has been extended to support the creation of ActiveX controls, allowing you to write the most efficient COM components in C++. ATL is Microsoft’s recommended framework for building fast, small COM objects. See the Visual C++ ATL documentation for more information. As well, the OLE DB Template Classes in Visual C++ provide an alternate set of wrapper classes.


Using the Framework

To use the framework, you should have a good understanding of the following:

  • Fundamentals of Automation and dual interfaces.

  • How to modify an Object Description Language (.odl) file.

  • How to work with COM persistence interfaces, most notably IStream and IPersistStream. However, you are not required to have much knowledge of the OLE embedding interfaces.

Note Those programmers who do not have specific performance requirements, are not familiar with the many pertinent COM/OLE technologies, or work primarily with the Microsoft Foundation Classes may find the MFC more suited to their needs.


Creating an ActiveX Control

It’s relatively easy to create an ActiveX control using the framework. The provided wizard does most of the work for you; the process is briefly described here.

The framework implements its core functionality in a few C++ classes, notably CAutomationObject, COleControl (which inherits from CAutomationObject), and CPropertyPage. All objects inherit from CUnknownObject, which provides the support for aggregation.

To write an ActiveX control, you need to declare a new object that inherits from COleControl. In addition, you need to inherit from some sort of Automation interface that describes the properties and methods for your control, such as IMyControl. This interface description is generated by the MkTypLib utility and is put in an output file created by MkTypLib (the CtlWiz sets up an environment where this file is named MyControlInterfaces.h). COleControl has a number of virtual methods that are declared as pure, which you simply must implement in your control class. These include WindowProc, LoadBinaryState, LoadTextState, SaveBinaryState, SaveTextState, OnDraw, and RegisterClassData.

To write a property page, you declare a new object that inherits from CPropertyPage. This object must implement a DialogProc function. You can also implement Automation objects and collections by declaring a new object that inherits from CAutomationObject.

Because an ActiveX control is an in-process OLE server, you also need one file to describe all your objects, whether they are controls, Automation objects, or property pages. This file includes a table of all objects and information about them. In addition, it has information about what sort of localization your server would like to use, and what sort of licensing support you'd like to have.

Finally, you need a resource file, an .odl file to describe your interfaces and event interfaces, a .def file to hold your linking information, and a file to define all the Globally Unique Identifiers (GUID) that have been declared.

Although you can create all this information by using one of the sample controls as a starting point for your project, it’s easier to use the control wizard to create a new control.

Using the Control Wizard

The control wizard is available from within Visual C++, and you can use it to create a new ActiveX control project. It is not a robust program and may fail if something unexpected occurs, but it will save you a considerable amount of time in the beginning.

To install and run the control wizard

  1. Follow the installation instructions in the readme file for this SDK.

  2. In Visual C++, open the Tools menu and click Customize.

  3. Select the Add-ins and Macro Files tab.

  4. Browse for and open ADBSDK\bin\AFCWiz.dll. This creates a custom toolbar with an icon for the control wizard. Alternately, you can place the control wizard icon in a menu or on a toolbar by dragging it from the Command tab (with the Add-ins category selected) to the desired menu or toolbar.

  5. To run the control wizard, just click the control wizard icon.

When you run the control wizard for this release, you will receive a warning message (which you can ignore) if you create your control in an existing directory. Also, after you load your control’s project and compile it, you will receive some linker warnings about the structure of your .def file. You can resolve these warnings by removing the entries for PRTMODE, CODE, and DATA. You should also modify your EXPORTS section to look like this:

EXPORTS
DllRegisterServer      @1 PRIVATE
DllUnregisterServer   @2 PRIVATE
DllCanUnloadNow      @3 PRIVATE
DllGetClassObject      @4 PRIVATE


Working with Your ActiveX Control

Once your control is up and running, you can extend its functionality.

In many ways, your control is much like a regular Windows program. Even if your control doesn't have a window (for example, it uses the windowless features of the ActiveX specification), you’re using a window process so you have to paint the client area yourself using regular windows-drawing APIs and handles to device contexts (DC).

To declare your control, use either DEFINE_CONTROLOBJECT or DEFINE_WINDOWLESSCONTROLOBJECT in your control's header file. These structures are self-documenting, but two parameters are worth a little extra discussion.

The first is the dwActivationPolicy parameter. The framework allows your control to take advantage of the delayed-activation features provided by IPointerInactive. If your control uses any of these, you should insert an activation policy into this field. By default, it is 0. Subclassed windows controls should not change this. Other control authors may wish to take advantage of this interface, and should consult the ActiveX specification for more information.

The second parameter is a Boolean value passed in to the DEFINE_WINDOWLESSCONTROL macro, which indicates whether your control has any transparent regions. If this parameter is True, the host has to ensure that your control paints correctly in the z-order of things. If False, your control is required to paint its entire client area, but the host has to do a little less work.

Structure of a Control

Every control in the framework must implement a core set of methods, based on creation semantics and methods that aren’t provided by COleControl. You may also want to override and provide an implementation for several routines. The following table notes many of these methods and routines.

Method/routine | Discussion
static Create | The control wizard generates this routine for you. Every control must create its control object in this routine and then return a pointer to its private unknown (for aggregation support).
Constructor and Destructor | These are generated for you. You should minimize initializations in the constructor to prevent load time from degrading.
RegisterClassData | Required. Even if your control is windowless for the most part, a user may place your control in a host that doesn't support all of the ActiveX features, and the framework will be forced to create an HWND for your control. In this case, you will need a window class. This routine is called only the first time a control of the given type is loaded in a process in which it must have an HWND. Controls should register their window class (using RegisterClass and the WNDCLASS structure) here. In addition, subclassed windows controls should get a pointer to the parent control's WindowProc and set that up in the g_ObjectInfo table using the SUBCLASSWNDPROCOFCONTROL macro. Controls that are invisible at run time should return FALSE in this routine, because it should never get called.
BeforeCreateWindow and AfterCreateWindow | Optional. BeforeCreateWindow is called right before the call to CreateWindow if your control is windowed, but after persistent state has been loaded. Controls should use this opportunity to set the window title for their control in m_szWindowTitle, and can also set up bits in pdwWindowStyle and pdwWindowStyleEx parameters that are passed in. In addition, controls can set their initial caption here in the parameter passed in for this purpose. This does, however, have a limit as defined in CreateInPlaceWindow in Ctlmisc.cpp. Control writers should use this opportunity to set up these defaults as much as possible, as opposed to in the message handler for WM_CREATE, because it typically results in much better performance.
InternalQueryInterface | Your control implements this to support QueryInterface for the control's primary Automation interface, such as IMyControl. You can also use this method to support additional interfaces in your control. For example, if you want to support IPerPropertyBrowsing, set up your control's CMyControl class to inherit from IPerPropertyBrowsing, and support the QueryInterface for IID_IPerPropertyBrowsing in InternalQueryInterface. If the control fails the QueryInterface, then the control should delegate back to COleControl::InternalQueryInterface to see if it accepts the IID.
LoadTextState, LoadBinaryState, SaveTextState, and SaveBinaryState | Required. All controls must implement these persistence routines. See “Persistence” later in this appendix for a discussion of these interfaces.
OnDraw | This routine is called when your control is expected to draw itself. At design time, this call originates from a container calling IViewObject2::Draw. At run time, the controls framework intercepts the WM_PAINT message and translates it into a call to your OnDraw routine. See “Painting a Control” later in this section for more information.
WindowProc | Messages that are not handled in the framework code (such as SimpleFrame messages and WM_PAINT) are sent to your control here. Your control should deal with these messages here. See “Handling Messages in a Control” later in this section for a discussion of this routine.
OnSpecialKey | Messages for various keyboard events, such as moving cursor keys, function keys, and other nonstandard keys, do not go to the WindowProc. Instead, they are sent to the OnSpecialKey routine. If you want your control to handle special keys and accelerators, you should override and implement this routine. The control should return TRUE if it handles a key, or FALSE if it ignores the key.
DoCustomVerb | If your control implements custom verbs in addition to the default property page verb (provided your control has a property page), you should implement this routine and take appropriate action depending on what verb was sent in. Return OLEOBJ_S_INVALIDVERB if you don't recognize the verb given.
OnSetExtent | This is called every time your control is resized. The m_Size SIZEL structure is your control's current size in pixels. Controls should look in here for size information and override OnSetExtent if they want to control how their control is sized.
OnQuickActivate | This routine is called if your control is in a host that supports quick activation and has used IQuickActivate::QuickActivate to activate your control. Your control is given a pointer to a QACONTAINER structure, and should examine the data in it. A nontrivial number of ambients are passed to the control in this way and will save later calls to GetAmbientProperty. See the documentation for IQuickActivate in the ActiveX specification for more information.

In addition, the following methods and routines can be called by an ActiveX control, and often prove to be extremely useful.

Method/routine | Discussion
DoSuperClassPaint | Subclassed window controls can call this from their OnDraw routines to paint themselves. For most window controls, this routine paints them correctly in design time and run time. Some Windows controls, however, require more attention.
RecreateControlWindow | Used for subclassed controls; re-creates the control's hWnd. This is useful if the control is changing a style bit that can't be changed with a SetWindowLong(GWL_STYLE ...) call.
DesignMode | Returns a Boolean value indicating its best guess as to whether the environment is in design mode. If it can't figure it out, it returns False.
GetAmbientProperty | Gets an ambient property from the container. Not all containers return these (they might not support them), so be careful to check the return code.
GetAmbientFont | Gets the current ambient font. A container may not implement this. Controls are encouraged to look for the ambient font in the QACONTAINER structure passed in to OnQuickActivate. Avoid calling GetAmbientFont to save time. Don't forget to release the font once you're finished with it.
ModalDialog | Controls must call ModalDialog before they show a modal dialog box. You can see this when the control is about to show its About Box dialog box.
InvalidateControl | Forces a control to be repainted if you pass in NULL for the rectangle, or invalidates the given area if it's not NULL. This is similar to the InvalidateRect API, but also operates in design mode.
SetControlSize | Controls that change their size out of OnSetExtent should use this routine to set their size. The control passes in a SIZEL structure in pixels and expects a call to OnSetExtent. Be careful of some recursive situations.
PropertyChanged | Whenever the value of a property changes, this routine should be called to notify a host. This causes the host to update any property browsers (such as those in Microsoft Visual Basic 5.0).
RequestPropertyEdit | Whenever a control wants to change a property that is marked as requestedit in the .odl file, the control must call this first and check the return code (TRUE or FALSE).
GetResourceHandle | Controls should call this whenever they're loading a resource that could be localized. This routine gets the handle to the appropriate DLL and deals with satellite DLLs or the lack thereof.
FireEvent | A control passes this routine an EVENTINFO structure, and an event as described in the EVENTINFO is fired. The control also passes parameters to this routine because it is a varargs method.
ControlFromUnknown | Property page code often finds it useful to get the COleControl * pointer from the IUnknown for a control object. This routine does just that.
Exception | Your control, or any Automation object, can use this to send the user an error message. See “Throwing an Exception” later in this section for more information about using this routine.

Windowless controls that want to support windowless in-place activation have routines they must call instead of the Win32® APIs to get the correct functionality. The following is a list of the Win32 functions and their replacements.

Win32 function | Replacement function
GetFocus | OcxGetFocus. Returns a Boolean value indicating whether the control has keyboard focus.
SetFocus | OcxSetFocus. Takes a Boolean parameter indicating whether you want to take the focus or give it up.
GetCapture | OcxGetCapture. Indicates whether or not you have the keyboard capture.
SetCapture | OcxSetCapture. Used to ask for or release the keyboard capture.
GetDC/ReleaseDC | OcxGetDC and OcxReleaseDC. Provides this functionality for windowless controls.
InvalidateRect | OcxInvalidateRect. Lets you force a repaint of your control.

These routines all work correctly if your control has an HWND. If you don’t intend for your control to work without an HWND, then you can safely use the regular Win32 APIs.

Painting a Control

The OnDraw routine is called whenever the control needs to paint. Sometimes the origin of the call is IViewObject2::Draw (as in design mode), and other times it comes from being sent a WM_PAINT message (as handled in ControlWindowProc).

Your control is given a device context (DC), a rectangle to describe where to paint, a rectangle for describing a metafile, and an Information Context (IC, passed in as an HDC) that describes the device. If the device is a metafile, the control must do a little different work. However, if the device is a raster display, the control typically paints to the screen.

Your control must be careful not to make any assumptions about the device context, except that it will be in MM_TEXT mapping mode. Often, there are no default pens, brushes, fonts, or colors selected into the device context. Your control has to do this work itself. This typically means your control will look slightly different in design and run modes.

If the fOptimize parameter passed in is TRUE, then the control writer can take advantage of the optimizations described in the ActiveX specification for not fully cleaning up the device context on exit. Writers are still responsible for deleting GDI objects they create and load, but they can leave things selected in the device context on exit if this parameter is TRUE. See the ActiveX specification for more information.

Finally, the dvAspect parameter is passed in to the OnDraw routine. This is used for multipass drawing, as described in the ActiveX specification (IViewObjectEx). Authors not wishing to support multipass drawing should ignore this parameter; the default setting does not support it.

Handling Messages in a Control

Your control has a method called WindowProc, which is called whenever a message is sent to your control. Your control should respond to messages here. If your control has an HWND, try to reduce the amount of work that is done in the WM_CREATE message handler; instead, try to place it in BeforeCreateWindow.

For certain types of messages, such as keyboard messages for arrow keys and other special keys, your WindowProc routine is not called. OnSpecialKey is called instead. The code in OnSpecialKey should look for WM_KEYDOWN, WM_KEYUP, WM_CHAR, and other messages, and handle them appropriately.

A certain class of messages typically involves notifying a window about events that are usually sent to a window's parent. These include WM_COMMAND, WM_NOTIFY, and WM_CTLCOLOR. These messages are reflected by the host to your control in the form of OCM_COMMAND, OCM_NOTIFY, and OCM_CTLCOLOR. Your controls should look for these messages instead of WM_ messages. See Olectl.h for other OCM_ messages of interest to the control. This discussion does not apply to windowless controls.

Adding a Property

One of the more important parts of a control is often its set of properties. When you create a control with the wizard, you have no properties by default. Adding them is relatively simple.

First, modify the primary dispatch interface for your control in the .odl file. For example, you may have a control called SuperScroll, and you want to add a LargeChange method to the control. You would add the following to the ISuperScroll interface description in the .odl:

 [id(DISPID_LARGECHANGE), propget, helpstring("The largechange property")]
   HRESULT LargeChange([out, retval] long *plLargeChange);
 [id(DISPID_LARGECHANGE), propput]
   HRESULT LargeChange([in] long lLargeChange);

Next, you define DISPID_LARGECHANGE in dispids.h. You then regenerate the type library (.tlb) file by typing this: make SuperScroll.tlb

This also regenerates SuperScrollInterfaces.h. You can then cut and paste the following two lines from SuperScrollInterfaces.h:

STDMETHOD(get_LargeChange)(THIS_ long FAR* plLargeChange) PURE;
STDMETHOD(put_LargeChange)(THIS_ long lLargeChange) PURE;

Finally, take these two lines and add them to your class description for CSuperScroll, and make sure that you remove the PURE declarators at the end. For example:

STDMETHOD(get_LargeChange)(long FAR* plLargeChange);
STDMETHOD(put_LargeChange)(long lLargeChange);

You can now implement these methods in your control file to implement your property.

Note A few standard dispids are defined for you in olectl.h. Whenever you want to declare a property, look in this header first to see if there is a standard dispid for it.

Adding a Method

Adding a method is much like adding a property to your control.

First, you define a dispid for the method. Once you've done that, it's as simple as adding the method to the primary interface for your control. Controls generated with the control wizard already have an About method defined for them.

As an example, define a method called MooCow with three parameters, the last of which is optional. Here is one such method:

 [id(DISPID_MOOCOW), helpstring("Makes your Cow moo")]
   HRESULT MooCow([in] long lSeconds, [in] boolean fLowPitch,
         [in, optional] VARIANT vPitch);

Again, as with properties, you regenerate the type library and then paste the declaration into your control header (without the PURE declarator) and implement it.

Adding an Event

In many situations, you may want your control to fire an event. For example, when a control gets a WM_LBUTTONDOWN message, it often makes sense to fire a MouseDown event. This turns out to be easy.

First, you must define the event in an EVENTINFO structure. There are many ways to do this, including declaring a new global variable for each event type or using an array; an array is neater and is discussed here. Say you want to have KeyDown, KeyUp, and KeyPushed events.

Here's how you might declare them:

typedef enum {
   MyCtlEvent_KeyDown = 0,
   MyCtlEvent_KeyUp = 1,
   MyCtlEvent_KeyPushed = 2
} MYCTLEVENTS;

VARTYPE rgI2 [] = { VT_I2 };

EVENTINFO m_rgMyCtlEvents [] = {
   { DISPID_KEYDOWN, 1, rgI2 },
   { DISPID_KEYUP, 1, rgI2 },
   { DISPID_KEYPUSHED, 1, rgI2 }
};

The EVENTINFO structure has three members: the dispid of the event, the count of arguments in the event, and a pointer to an array of VARTYPE that describes the types of the parameters to the event. Remember, there are dispids defined for you in Olectl.h; whenever you're adding an event, check there first to see if adispid exists for it.

To fire these events from code, just call the following:

FireEvent(&(m_rgMyCtlEvents[MyCtlEvent_KeyDown]), sKeyValue);

Using Standard ActiveX Types

For many controls, you may find it useful to declare properties of types provided by OLE, such as Font, Picture, and Color. Many hosts detect properties of these types and put up convenient browsers for the user to select values for these types.

To declare a property of one of these types, you must first make sure its .odl file includes the following at the top:

importlib(STDTYPE_TLB);

Then, to declare a property of type Font, Picture, or Color, your code would be similar to the following, depending on the type:

[id(DISPID_FONT), propget]
   HRESULT Font([out, retval] IFontDisp **ppFont);
[id(DISPID_FONT), propputref]
   HRESULT Font([in] IFontDisp *pFont);

[id(DISPID_MOUSEICON), propget]
   HRESULT MouseIcon([out, retval] IPictureDisp **ppMouseIcon);
[id(DISPID_MOUSEICON), propputref]
   HRESULT MouseIcon([in] IPictureDisp *pMouseIcon);

[id(DISPID_FORECOLOR), propget]
   HRESULT ForeColor([out, retval] OLE_COLOR *pocForeColor);
[id(DISPID_FORECOLOR), propput]
   HRESULT ForeColor([in] OLE_COLOR ocForeColor);

For the get_ and put_ methods for these types, the control gets a property as declared above. For fonts and pictures, the control probably will use QueryInterface for IFont and IPicture, respectively.

For your control to use a font object, the control typically calls the get_hFont method and passes the resulting HFONT to the control's device context. It handles pictures in much the same way. For fuller descriptions of using these fonts in your application, see the Microsoft Developer Network (MSDN).

To use a color, the control calls OleTranslateColor to convert it to a real COLORREF. An OLE_COLOR is basically a COLORREF with some support for "generic" colors, such as COLOR_WINDOW and COLOR_WINDOWTEXT. To convert one of these into an OLE_COLOR, just join it in a logical OR (|)with 0x80000000. For example, to initialize the control's background to COLOR_WINDOW, set the backcolor property to COLOR_WINDOW | 0x80000000. Then, to paint the backdrop, just call OleTranslateColor and use the resulting COLORREF.

Note The framework uses dual/vtable bound Automation interfaces, and uses Automation functionality to support IDispatch methods on the Automation objects.

Throwing an Exception

Every once in a while, during an operation, your control may need to communicate problems to the user. It does so by throwing an exception. In any of the control's Automation methods or property operators, the control can call the Exception method when exiting, and the method sets up all the appropriate information to trigger the error.

For example:

CMyControl::put_Appearance(long lAppearance)
{
   if (lAppearance == 10)
      return Exception(MYCTL_E_INVALIDPROPERTYVALUE, IDS_ERR_INVALIDPROPERTYVALUE, 0);
   m_lAppearance = lAppearance;
   return S_OK;
}

The arguments to the Exception routine are as follows: the first is the SCODE of the error the control wants to trigger. For errors unique to the control, you should define them like so:

#define MYCTL_E_INVALIDPROPERTYVALUE MAKE_SCODE(SEVERITY_ERROR, FACILITY_CONTROL, 34500)

The second argument is the resource identifier of the string that the control should display. The Exception code correctly gets this information from the control's localized satellite DLL.

The last argument is the helpcontextid that is passed to the help file that is defined in your control's structure.


The Persistent State

One of the most important things your control will do is save and restore its persistent state. This typically is done in one of two ways: through the PropertyBag interfaces for text persistence, and through streams for binary persistence. With the latter, performance is absolutely critical to make your control load quickly.

The ActiveX controls framework requires your control to implement four member functions to support persistence. The control is required to implement LoadTextState, LoadBinaryState, SaveTextState, and SaveBinaryState. If you are certain that the control is never going to be in a host that uses IPersistPropertyBag, then you can ignore the two text interfaces, but this isn't recommended (they are easy to use).

Text Persistence

Text persistence in the framework is done via IPersistPropertyBag and IPropertyBag. All ActiveX controls have an implementation of IPersistPropertyBag, and are given pointers to property bag objects to do their work.

Effectively, the IPropertyBag interface has two routines that you can use: Read and Write. In both, you pass in a VARIANT. With Read, the property is put in the VARIANT if it was saved out. If the property can’t be found (it was never saved out), the default value for that property should be used, and an error should probably not be returned. For Write, a VARIANT with the data is passed in. To persist out a collection or an object (such as a Font or Picture object), you can pass in a VT_UNKNOWN object, and the PropertyBag will use QueryInterface on that object for IPersistPropertyBag or IPersistStream and persist it. This actually proves effective for persisting collections; they can just support IPersistPropertyBag.

The accompanying samples show how to persist out properties using PropertyBags. For controls with many properties, it often makes sense to use a table-driven persistence to reduce code size and bug potential.

For additional information, see the MSDN and Inside OLE (2nd edition), by Kraig Brockschmidt, both of which have descriptions of the IPropertyBag interface.

Binary Persistence

It is critical to work on binary persistence when implementing an ActiveX control. Control load speed can be severely hampered by a poorly written LoadBinaryState routine, so it's critical to spend some time thinking about how to keep this routine fast. The binary persistence code is used by some hosts all the time, and by other hosts when including the control in a generated executable file.

In both binary routines, the control is handed a pointer to an IStream object. The key to load speed is to reduce the number of operations on the stream. For saving, this is slightly less critical.

Typically, a control saves the following information (often in the given order):

  • Some sort of header with a magic number, version, and size information.

  • Fixed-size state information, such as longs, floats, colors, and strings.

  • Variable-size persistent state, such as fonts, pictures, and collections.

Most control writers should start the control's binary persistent state with a header structure that includes a "magic" number that the control can check when loading. You may also want to include some sort of version number so that future versions of the control can distinguish older versions. Finally, you may want the control to write out the number of bytes of data that were written.

The samples in the framework that have a binary persistent state use the following structure:

#define STREAMHDR_MAGIC  0x12345678

typedef struct {
   DWORD dwMagic;
   DWORD dwVersion;
   DWORD cbSize;
} STREAMHDR;

The SaveBinaryState routine saves this information; the LoadBinaryState routine looks for it.

One way to write out all the fixed-size information (and therefore load it efficiently) is to do it all at once. If the control's fixed persistent state is in a structure in the control object, you can write out the structure in the persistence code. Controls generated by the control wizard have a structure defined for them called MYCTLNAMECTLSTATE, into which you can put the control's fixed-state data. Then, when saving, the control does the following:

hr = pStream->Write(&m_state, sizeof(m_state), NULL);

For loading, it becomes as simple as this:

hr = pStream->Read(&(m_state), sizeof(m_state), NULL);

It’s efficient and simple.

For fonts and pictures, it's slightly more complicated. The control has to use QueryInterface on those objects for IPersistStream, and then call the Load or Save method with the stream that the control has been given. Typically, this can be done after the control has written out all other information.

If you follow the above suggestions for persistent state structure, your control's load routine could look something like this:

IPersistStream *pps;
STREAMHDR sh;
HRESULT hr;

// First read in the streamhdr, and make sure we like what we're getting.

hr = pStream->Read(&sh, sizeof(sh), NULL);
RETURN_ON_FAILURE(hr);

// Sanity check.

if (sh.dwMagic != STREAMHDR_MAGIC || sh.cbSize != sizeof(m_state))
   return E_UNEXPECTED;

// Read in the control state information.

hr = pStream->Read(&(m_state), sizeof(m_state), NULL);
RETURN_ON_FAILURE(hr);

// Now read in the font!

OleCreateFontIndirect(&_fdDefault, IID_IFont, (void **)&m_pFont);
RETURN_ON_NULLALLOC(m_pFont);

// QueryInterface it for IPersistStream and load it in.

hr = m_pFont->QueryInterface(IID_IPersistStream, (void **)&pps);
RETURN_ON_FAILURE(hr);

hr = pps->Load(pStream);
pps->Release();

return hr;

This is fast and robust.


Implementing Property Pages

Property pages are an invaluable addition to most ActiveX controls’ design-time functionality. Fortunately, it’s easy to implement them. To do so, you merely need to declare an object that inherits CPropertyPage.

Working with a Property Page

The control's property page is declared in the header file using the DEFINE_PROPERTYPAGE macro, which puts it into the g_ObjectInfo table. The framework supports the creation of the property page object, but you are required to implement the static Create function, which the control wizard generates for you.

The property page is created much like a standard Windows dialog box; use your favorite resource editor to create a DIALOG resource, and then cut and paste it into your control's resource (.rc) file.

The most important method you have to implement is the DialogProc method, which is where all the work takes place. In addition to the regular Windows messages that one would expect in a DialogProc, there are a few additional messages that others working with this framework expect.

Message | Description
PPM_NEWOBJECTS | The control has been given some new objects. The control is expected to populate its page's controls with information from this object. Using the FirstControl and NextControl methods from CPropertyPage, the control can get the relevant information.
PPM_APPLY | The control now has to apply any changes that have occurred. Use the FirstControl and NextControl methods to loop through all the objects for which the property pages were visible and apply the values; it's possible to have more than one object for which a property page is being displayed.
PPM_EDITPROPERTY | The control is expected to set the focus to the control instance, which represents the property of the given dispid. This message is called only if you implement IPerPropertyBrowsing and return a value in MapPropertyToPage.
PPM_FREEOBJECTS | The control frees saved pointers received in PPM_NEWOBJECTS; the objects are no longer valid. This can be called from the property page's destructor, so be careful not to make too many assumptions about the property page.

See any of the sample controls for exact details about these messages.

Navigating Through Associated Objects

Your property pages can operate on one or more controls. When initializing, you typically receive some values from the first control. You can use the FirstControl method to get the object pointer for this control. You can then use QueryInterface for your primary dispatch interface to get properties with which to populate the page.

When told to apply the values (PPM_APPLY), apply them to all objects, which means you have to loop using FirstControl and NextControl, as follows:

for (pUnk = FirstControl(&dwCookie) ; pUnk; pUnk = NextControl(&dwCookie)) {
   hr = pUnk->QueryInterface(IID_IButton, (void **)&pButton);
   if (FAILED(hr)) continue;

   GetDlgItemText(hwnd, IDC_CAPTION, szTmp, 128);
   bstr = BSTRFROMANSI(szTmp);
   ASSERT(bstr, "Operation Failed");
   pButton->put_Caption(bstr);
   SysFreeString(bstr);
   pButton->Release();
}

You do not have to run Release on the return values of FirstControl and NextControl.

Marking Your Page as Dirty

It is moderately important to manually mark the control's property page as dirty at the appropriate times. Typically, the control does this in response to a Windows notification message, such as EN_CHANGE or BN_CLICKED. When the control wants to mark its page as dirty, it should call MakeDirty. This causes the Apply button to be enabled, if it was previously unavailable, and tells the host that the state should be saved before destroying the page.

The following code causes the page to mark itself as dirty when the user changes the text in a Text box in the property page:

case WM_COMMAND:
   switch (LOWORD(wParam)) {
      case IDC_CAPTION:
         if (HIWORD(wParam) == EN_CHANGE)
            MakeDirty();
         break;
   }
   break;


String Manipulation

The ActiveX controls framework provides a robust system of macros for manipulating strings in all the ways that you will see while working with an ActiveX control.

Types of Strings

There are different types of strings in Automation. It’s important to understand them when working with COM, because the potential for memory leaks and bugs associated with strings is very great.

There are two fundamental types of strings: multibyte (which can be ANSI or double byte) and unicode strings. Of the former, you almost always work with some sort of char * pointer (LPSTR, LPCSTR). Of the latter, a few types are commonly used—most notably WCHAR * (LPWSTR, LPWCSTR), BSTR, and OLESTR strings.

LPWSTR pointers are just that— pointers to a wide string. An LPOLESTR pointer is much the same, with some additional COM rules added to it. An OLESTR is merely a wide string, but when it is an out-parameter to a function, it should be allocated using the host's IMalloc allocator (for example, CoTaskMemAlloc).

A BSTR is a string with a length prefix in the memory location preceding the string. To work with a BSTR, you need to use special APIs designed exclusively for them, notably SysAllocString, SysFreeString, and SysStringLen. For more details, see the Automation Programmer’s Reference, available on MSDN and from Microsoft Press®.

These data types are fully interchangeable in terms of compares and copies, but they are not interchangeable in terms of allocation and freeing. It is not acceptable to call SysFreeString on an OLESTR or LPWSTR string.

Variables of type BSTR and OLESTR as in parameters to functions should not be freed as per standard OLE COM conventions. Variables of type BSTR and OLESTR as out parameters should be expected to be freed, and should thus be allocated appropriately.

Working with Strings

For the most part, your controls will be working with multibyte strings, except when you work with OLE. Therefore, in various scenarios you'll either be given a wide string, and need the multibyte version of it, or you'll have a multibyte string, and need a wide string for it.

To solve these problems, the ActiveX controls framework includes the following macros to work with:

  • MAKE_WIDEPTR_FROMANSI(newstringname, convertme)

  • MAKE_ANSIPTR_FROMWIDE(newstringname, convertme)

  • BSTRFROMANSI(ansistr)

  • OLESTRFROMANSI(ansistr)

  • BSTRFROMRESID(resourceid)

  • OLESTRFROMRESID(resourceid)

  • COPYOLESTR(copyme)

  • COPYBSTR(copyme)

The first two macros take a string of a given type and name, and create a variable of the new name; do not declare a variable of this name yourself and then convert the other string into the new variable. This cannot be used as an rvalue in C/C++ expressions, nor can it be an lvalue; it needs to sit on a line by itself.

The last set of macros does all the remaining interesting work. You can get a variable of type BSTR or use a variable of type OLESTR, created by the IMalloc interface, from an ANSI string, or you can copy the OLESTR and BSTR variables. The only additional functions of interest are those that take a WORD, which is a resource ID, and load in a string from your localization DLL (or the main DLL if you don't do satellite localization) and make either a BSTR or OLESTR out of it. This proves useful in a few places where you need a localized string.

Remember that while these macros were designed with a certain amount of speed in mind, converting strings is not a cheap operation. Control writers should try to avoid string conversions.


Localization

The ActiveX controls framework has some support for robust localization of your control—most notably, property pages and any other resources you want to localize.

The scheme is as follows: The resources for the default language (typically English) are in the main in-process server's resources. Then, for each additional language supported, a satellite DLL contains the resources for that language. The name for this satellite DLL is generated by taking the base name for the DLL from a string resource in the main in-process server file, and then adding on a three-letter language code as generated by the function GetLocaleInfo with the LCTYPE LOCALE_SABBREVLANGNAME.

The code first looks for the specific language that is set up in the global variable g_lcidLocale. This should be initialized by all servers. For ActiveX controls and property pages, get this from the host. For Automation servers, you can set this up some other way, often by using the system language. If the satellite DLL for the specific language is not found, the framework looks for the primary language ID with SUBLANG_DEFAULT. If that satellite is not found, a handle to the default resources is returned.

Fully localized type libraries are not supported; most hosts ignore these and just use the default.

Setting up for Localization

If you want to support satellite localization in your in-process server, then you need to set up some variables.

First, set to True the variable g_fSatelliteLocalization in your in-process server file. This instructs all further code paths to use satellite localization.

At some point, set up the g_lcidLocale variable, as described in the previous section. For ActiveX controls, you can use GetAmbientProperty and ask for AMBIENT_DISPID_LOCALE. For property pages, a method on the IPropertyPageSite interface lets you obtain the locale identifier; for Automation objects, you are required to set this up yourself, typically by obtaining the system locale identifier.

Whenever you want to load a resource, make sure you use GetResourceHandle to get the instance handle for the localized resources. This generates the appropriate file name for the satellite DLL and attempts to load it. The satellite DLL contains all the localized resources you may want to use.


Additional Information

The following list includes resources you may find useful.

  • Inside OLE, Second Edition, Kraig Brockschmidt, Microsoft Press. Chapters 3, 13–15 and 24 contain most of the information you'll need to work with an ActiveX control and Automation server. The chapters on persistence should be useful if you are unfamiliar with the topic.

  • COM Programmer’s Reference and Automation Programmer’s Reference, MSDN in the Platform Software Development Kit.

  • MFC Source Code, Visual C++ 5.0. When you’re looking for ideas, the MFC source code proves to be an invaluable resource. Use it frequently.

Host-Specific Notes

Here are some notes about known problems with specific ActiveX control hosts:

  • Microsoft Access 95: Access 95 requires IPerPropertyBrowsing. If you do not implement this interface, you will not able to put one of your controls on a form. See MSDN and the MFC source code for documentation on this interface.

    In addition, you cannot return error SCODEs from GetPredefinedStrings. You must return either S_OK or S_FALSE. Returning an error SCODE causes a failure.

    MapPropertyToPage should return PERPROP_E_NOPAGEAVAILABLE if there are no pages available.

  • TstCon32: The MFC Test container assumes your control is based around a dispinterface, and looks for VARDESC structures of variables. Because framework controls are based around dual interfaces, variables are all in FUNCDESC structures, and the test container will suggest that your control has no properties. This is, of course, incorrect.


Wrapper Class Reference

This chapter describes the wrapper classes that accompany the ActiveX Control Writer’s SDK for OLE DB. You can find the source code for the wrapper classes in the Asbsdk\Inc and Absdk\Src\Adbctl directories of the SDK. You can also load the Adbctl sample (Adbctl.dsw) into Microsoft Visual C++ and use the ClassView pane to view the wrapper classes in the SDK.

In this chapter, the wrapper classes are categorized by usage:

  • Rowsets

  • Miscellaneous

  • IStream

  • String Handling

  • Bookmarks

  • Persistence

  • Property Pages

  • Additional Data Access Support

  • Advise Connections


Rowsets

Rowset functionality is wrapped in the class CRowset.

This wrapper class provides access to the most common rowset functionality through a uniform application programming interface (API) that virtually eliminates the need for users to manage rowset interfaces. You can extend this class functionality by deriving another class from it, and adding more methods or augmenting the existing overridable functions.

The following description provides a brief synopsis of each CRowset method. The description includes the rowset interface and methods that are wrapped by the CRowset method, when applicable. Please refer to the OLE DB Programmer's Reference for more a detailed description of the wrapped methods and to get additional documentation on usage, parameters, and return values.


CRowset

CRowset();

Comments

The default constructor. The object is not usable until the Attach method is called.


GetRowset

IRowset* GetRowset(void);

Comments

Returns the attached IRowset interface pointer. The method returns NULL if there is no attachment or if the attachment was made to an IRowPosition or IDataSource interface, and hence the rowset is not available.


GetAccessor

IAccessor* GetAccessor(void);

Comments

Returns the pointer to the IAccessor interface for the attached rowset. The method returns NULL if there is no rowset available, or if an attachment was not made.


GetRowPosition

IRowPosition* GetRowPosition(void);

Comments

Returns the attached IRowPosition interface pointer. The method returns NULL if there is no attachment, or if the attachment was made to an IRowset interface instead of an IRowPosition or IDataSource interface.

See CRowset::Attach for more detail.


GetDataSource

IDataSource* GetDataSource(void);

Comments

Returns the attached IDataSource interface pointer. The method returns NULL if there is no attachment, or if the attachment was made to an IRowset or IRowPosition interface.

See CRowset::Attach for more detail.


Attach

virtual HRESULT Attach(
IUnknown punk, BSTR bstrDM /* = NULL */);

Parameter

punk

Pointer to the IUnknown interface of an IRowset, IRowPosition, or IDataSource interface. The prior attachment will be released. This argument can be NULL to detach from a previous attachment.

bstrDM

Optional argument, used only if IDataSource is the attachment interface.

Return Code

S_OK

The method succeeded.

E_NOINTERFACE

The given IUnknown does not implement IDataSource, IRowPosition or IRowset.

Comments

Attach must be called with a valid interface before the object is usable. If punk is not NULL, it is first queried for IDataSource. If IDataSource is available, then IDataSource::GetDataMember is called with the optional data member argument. Otherwise, a query is made for IRowPosition. If IRowPosition is available, Attach calls IRowPosition::GetRowset to retrieve the IRowset interface. Otherwise, Attach queries for IRowset.


GetChapter

virtual HCHAPTER GetChapter(void)

Comments

Returns the chapter handle set by CRowset::SetChapter.


SetChapter

virtual HCHAPTER SetChapter(
HCHAPTER hchapter);

Parameter

hchapter

Handle to a hierarchical chapter. When the chapter is set to NULL, all rowset operations act on the attached rowset instead of the chapter rowset.

Comments

The SetChapter method returns the handle to the old chapter.

It sets the hierarchical chapter handle. The chapter must be retrieved from a chapter column of the attached rowset. The given chapter handle is used for all interface methods requiring a chapter handle. See the OLE DB Programmer’s Reference for more information about hierarchical chapters.


GetProperties

virtual const ROWSETPROPERTIES& GetProperties(void);

Comments

Returns a read-only reference to the internal ROWSETPROPERTIES structure that is initialized upon a successful attachment to a rowset interface. You can call this method after a successful Attach to inspect the rowset for required properties.


GetCount

HRESULT GetCount(
long& lCount);

Parameter

lCount Reference to buffer to receive count.

Return Code

S_OK

Method succeeded.

E_NOINTERFACE

IRowsetExactScroll and IRowsetScroll are not available. lCount is set to zero if an error occurs.

E_FAIL

Returned if the object is not attached.

Comments

The GetCount method uses IRowsetExactScroll::GetExactPosition if it is available, or IRowsetScroll::GetApproximatePosition if the former is not available to get the number of rows in a rowset; the latter returns the approximate row count.


GetExactPosition

HRESULT GetExactPosition(
const CBookmark& bmk,
long& lPos);

Parameters

bmk

Self-bookmark of a row.

lPos

Reference to buffer to receive row position. Zero is returned if an error occurs. Otherwise lPos is a one-base row index.

Return Code

S_OK

Method succeeded.

E_NOINTERFACE

IRowsetExactScroll and IRowsetScroll are not available.

E_FAIL

Returned if the object is not attached.

Comments

The GetExactPosition method uses IRowsetExactScroll::GetExactPosition to retrieve the exact position of the row identified by the given bookmark. If IRowsetExactScroll is not available, IRowsetScroll::GetApproximatePosition is used to locate the approximate position of the row. Then, rows are fetched and counted at the approximate position until the row with the specified bookmark is found.


GetApproximatePosition

HRESULT GetApproximatePosition(
const CBookmark& bmk,
long& lPos);

Parameters

bmk

Self-bookmark of a row.

lPos

Reference to a buffer to receive the row position. Zero is returned if an error occurs. Otherwise, lPos is a one-base row index.

Return Code

S_OK

Method succeeded.

E_NOINTERFACE

IRowsetScroll is not available.

E_FAIL

Returned if there is no attachment.

Comments

The GetApproximatePosition method uses IRowsetScroll::GetApproximatePosition to retrieve the approximate position of the row identified by the given bookmark.


GetRowsAtRatio

HRESULT GetRowsAtRatio(
ULONG ulNumerator,
ULONG ulDenominator,
long cRowsRequested,
HROW*& phRows,
ULONG& cRows);

Parameters

ulNumerator

The numerator portion of the ratio.

ulDenominator

The denominator portion of the ratio.

cRowsRequested

The number of rows to fetch. The number of rows actually fetched may be less than this value.

phRows

Reference to buffer to receive HROWs. If this argument is NULL then the callee will allocate an array large enough to hold the number of HROWs returned. Use CRowset::FreeRows, or a combination of CRowset::ReleaseRows and CoTaskMemFree to release the HROWS and free the callee-allocated buffer. If this argument is not NULL, then the caller must have allocated enough space to hold the requested number of HROWs.

cRows

Reference to buffer to receive the numbers of rows actually fetched. This value may be less than or equal to the number of rows requested.

Return Code

S_OK

Method succeeded.

E_NOINTERFACE

IRowsetScroll is not available.

E_FAIL

Returned if the object is not attached.

Comments

Uses IRowsetScroll::GetRowsAtRatio to fetch the number of requested rows at the position calculated by the given ratio.


CreateAccessor

HRESULT CreateAccessor(
const DBBINDING *pBindings,
ULONG cBindings,
HACCESSOR& haccessor,
DBSTATUS *pStatus = NULL);
HRESULT CreateAccessor(
const CBindingsArray bindings,
HACCESSOR& haccessor,
DBSTATUS *pStatus = NULL);

Parameters

bindings

Reference to a bindings array.

pBindings

Pointer to DBBINDING array. Can be NULL if cBindings is zero.

cBindings

The number of bindings in the given array. Can be zero if pBindings is NULL.

haccessor

Reference to buffer to receive created accessor handle. Use CRowset::ReleaseAccessor when the accessor is no longer needed.

pStatus

Pointer to a DBSTATUS array. This array must be allocated by the caller and must contain at least cBindings number of elements. This argument can be NULL if the result status of the individual bindings are not needed.

Return Code

S_OK

Method succeeded.

E_FAIL

Returned if the object is not attached (or if the bindings array is empty).

Comments

Uses IAccessor::CreateAccessor to create a row accessor using the given binding information. If pBindings is NULL and cBindings is zero, a null accessor is created. A null accessor is a special accessor that can be used to insert a row whose columns contain default values. See CRowset::InsertRow.

The bindings array must contain at least one element.


ReleaseAccessor

HRESULT ReleaseAccessor(HACCESSOR haccessor);

Parameter

haccessor

Handle to an accessor.

Return Code

S_OK

Method succeeded.

E_FAIL
Returned if the object is not attached.

Comments

Uses IAccessor::ReleaseAccessor to decrease the reference count of a previously created accessor handle. The accessor must have been created during the lifetime of the attached rowset.


AddRefAccessor

HRESULT AddRefAccessor(
HACCESSOR haccessor);

Parameter

haccessor

Handle to accessor.

Return Code

S_OK

Method succeeded.

E_FAIL

Returned if the object is not attached.

Comments

Uses IAccessor::AddRefAccessor to increment the reference count of a previously created accessor handle. The accessor must have been created during the lifetime of the attached rowset.


GetBindings

HRESULT GetBindings(
HACCESSOR haccessor,
DBBINDING*& pBindings,
ULONG& cBinds,
DBACCESSORFLAGS& dwFlags);

Parameters

haccessor

Handle to accessor.

pBindings

Reference to buffer to receive callee-allocated DBBINDING array. This array must be freed using CoTaskMemFree.

cBinds

Reference to buffer to receive the number of bindings in the array.

dwFlags

Reference to buffer to receive the DBACCESSORFLAGS used to create the accessor handle.

Return Code

S_OK

Method succeeded.

E_FAIL
Returned if the object is not attached.

Comments

Uses IAccessor::GetBindings to retrieve the binding information that was used to create the accessor. The bindings array returned must be freed using CoTaskMemFree.


GetColumns

HRESULT GetColumns(COLUMNINFO*& pCols, ULONG& cCols);

Parameters

pCols

Reference to buffer to receive callee-allocated COLUMNINFO array. This array must be freed using the delete[] operator.

cCols

The number of elements in the array.

Return Code

S_OK
Method succeeded.

E_FAIL
Returned if the object is not attached.

Comments

Uses IColumnInfo and IColumnsRowset to populate an array of COLUMNINFO structures. If IColumnsRowset is not available, then the additional structure member in COLUMNINFO is left zero-initialized.


GetColumnOrdinals

HRESULT GetColumnOrdinals(
ULONG *pOrdinals,
const DBID* pdbColIDs,
ULONG cColIDs);

Parameters

pOrdinals

Caller-allocated array. This array must have at least cColIDs number of elements.

pdbColIDs

Array of DBID for the column IDs whose ordinals are requested.

cColIDs

The number of elements in pdbColIDs.

Return Code

S_OK

Method succeeded.

E_FAIL

Returned if the object is not attached.

Comments

Uses IColumnInfo::MapColumnIDs to fill in the given ULONG array with column ordinals for each of the column IDs given by the DBID array.


GetCurrentRow

HRESULT GetCurrentRow(
HROW& hrow,
DBPOSITIONFLAGS *pdwPositionFlags = NULL);

Parameters

hrow

Reference to buffer to receive the current row handle. Use CRowset::ReleaseRows to release the row handle when it is no longer needed.

pdwPositionFlags

Pointer to buffer to receive DBPOSITIONFLAGS mask. This can be NULL if the caller is not interested in the flags.

Return Code

S_OK

Method succeeded.

E_FAIL

Returned if the object is not attached or is an IRowPosition interface.

Comments

Uses IRowPostion::GetCurrentRow to retrieve the HROW of the current row. This method is available only if the attached interface implemented IRowPosition.


RestartPosition

HRESULT RestartPosition(void);

Return Code

S_OK

Method succeeded.

E_FAIL

Returned if the object is not attached.

Comments

Uses IRowset::RestartPosition to restore the next fetch position to the position when the rowset was first created.


GetNextRows

HRESULT GetNextRows(
long lOffset,
long cRowsRequested,
HROW*& phRows,
ULONG& cRows);

Parameters

lOffset

The offset from the next fetch position to begin fetching.

cRowsRequested

The number of rows to fetch.

phRows

Reference to buffer to receive HROWs. If this argument is NULL then the callee will allocate an array large enough to hold the number of HROWs returned. Use CRowset::FreeRows, or a combination of CRowset::ReleaseRows and CoTaskMemFree, to release the HROWS and free the callee-allocated buffer. If this argument is not NULL, then the caller must have allocated enough space to hold the requested number of HROWs.

Return Code

S_OK

Method succeeded.

E_FAIL

Returned if the object is not attached.

Comments

Uses IRowset:GetNextRows to fetch the next sequence of requested rows.


FreeRows

HRESULT FreeRows(
const HROW *pRows,
ULONG cRows);

Parameters

pRows

Array of HROWs to release. The array must have been allocated by rowset methods or by CoTaskMemAlloc.

cRows

The number of HROWs in the array.

Return Code

S_OK

Method succeeded.

E_FAIL

Returned if the object is not attached.

Comments

Uses a combination of IRowset::ReleaseRows and CoTaskMemFree to release the HROWs and free the buffer.


ReleaseRows

HRESULT ReleaseRows(
const HROW *pRows,
ULONG cRows);

Parameters

pRows

Array of HROWs to release.

cRows

The number of HROWs in the array.

Return Code

S_OK

Method succeeded.

E_FAIL

Returned if the object is not attached.

Comments

Uses IRowset::ReleaseRows to release the HROWs in the given array.


AddRefRows

HRESULT AddRefRows(
const HROW *pRows,
ULONG cRows);

Parameters

pRows

Array of HROWs to release.

cRows

The number of HROWs in the array.

Return Code

S_OK

Method succeeded.

E_FAIL

Returned if the object is not attached.

Comments

Uses IRowset::AddRefRows to increment the reference count of the HROWs in the given array.


GetData

HRESULT GetData(
HROW hrow,
HACCESSOR haccessor,
void *pBuffer);

Parameters

hrow

Handle to a row.

haccessor

Handle to a row accessor.

pBuffer

Caller-defined buffer. This buffer must be large enough to hold data as defined by the binding information used to create the row accessor.

Return Code

S_OK

Method succeeded.

E_FAIL
Returned if the object is not attached.

Comments

Uses IRowset::GetData to retrieve data for the row identified by the given HROW.


SetData

HRESULT SetData(
HROW hrow,
HACCESSOR haccessor,
void *pBuffer);

Parameters

hrow

Handle to a row.

haccessor

Handle to a row accessor.

pBuffer

Caller-defined buffer. This buffer must contain the data formatted as defined by the binding information used to create the row accessor.

Return Code

S_OK

Method succeeded.

E_NOINTERFACE
Returned if IRowsetChange is not available.

E_FAIL
Returned if the object is not attached.

Comments

Uses IRowsetChange::SetData to set data for the row identified by the given HROW.


DeleteRows

HRESULT DeleteRows(
const HROW *phRows,
ULONG cRows,
DBROWSTATUS *pRowsStatus);

Parameters

phRows

Array of row handles.

cRows

The number of row handles in the array.

pRowsStatus

Caller-allocated buffer to receive the status of each row in the array.

Return Code

S_OK

Method succeeded.

E_NOINTERFACE
Returned if IRowsetChange is not available.

E_FAIL
Returned if the object is not attached.

Comments

Uses IRowsetChange::DeleteRows to delete the rows identified by the given array of HROWs.


InsertRow

HRESULT InsertRow(
HACCESSOR haccessor,
void *pData,
HROW& hrow);

Parameters

haccessor

Handle to a row accessor that describes the data format in the buffer pointed to by pData.

pData

Caller-defined buffer containing data for each column for the newly added row, as defined by the binding information used to create the row accessor. This argument can be NULL if haccessor receives a handle to a null accessor. In this case, the row inserted will contain default column data.

hrow

Reference to a buffer to receive the handle to the newly inserted row.

Return Code

S_OK

Method succeeded.

E_NOINTERFACE
Returned if IRowsetChange is not available.

E_FAIL
Returned if the object is not attached.

Comments

Uses IRowsetChange::InsertRow to insert a row.


GetOriginalData

HRESULT GetOriginalData(
HROW hrow,
HACCESSOR haccessor,
void *pData);

Parameters

hrow

Handle to a row.

haccessor

Handle to a row accessor.

pData

Caller-defined buffer. This buffer must be large enough to hold data as defined by the binding information used to create the row accessor.

Return Code

S_OK

Method succeeded.

E_NOINTERFACE
Returned if IRowsetUpdate is not available.

E_FAIL
Returned if the object is not attached.

Comments

Uses IRowsetUpdate::GetOriginalData to retrieve the row data that was last committed to the data source.


GetPendingRows

HRESULT GetPendingRows(
DBPENDINGSTATUS dwStatus,
HROW*& phRows,
ULONG& cRows,
DBPENDINGSTATUS **ppStatus);

Parameters

dwStatus

Status bitmask.

phRows

Reference to buffer to receive callee-allocated HROWs array. Use CRowset::FreeRows or a combination of CRowset::ReleaseRows and CoTaskMemFree to release the HROWs and free the callee-allocated buffer.

cRows

The number of HROWs returned.

ppStatus

Pointer to buffer to receive array of DBPENDINGSTATUS flags for each of the HROWs returned. The array is allocated by the callee and must be freed by the caller using CoTaskMemFree.

Return Code

S_OK

Method succeeded.

E_NOINTERFACE
Returned if IRowsetUpdate is not available.

E_FAIL
Returned if the object is not attached.

Comments

Uses IRowsetUpdate::GetPendingRows to retrieve the array or HROWs whose status bit matches any one of the given status bitmasks.


GetRowStatus

HRESULT GetRowStatus(
const HROW *phRows,
ULONG cRows,
DBPENDINGSTATUS *pStatus);

Parameters

phRows

Pointer to an array of HROWs for which to retrieve status.

cRows

The number of HROWs in the array.

pStatus

Caller-allocated array to receive the status for each of the given HROWs. This array must contain at least cRows elements.

Return Code

S_OK

Method succeeded.

E_NOINTERFACE
Returned if IRowsetUpdate is not available.

E_FAIL
Returned if the object is not attached.

Comments

Uses IRowsetUpdate::GetRowStatus to retrieve the status for each row in the given HROW array.


Undo

HRESULT Undo(
const HROW *phRows,
ULONG cRows,
ULONG *pcRows,
HROW **pphRows,
DBROWSTATUS **ppStatus);

Parameters

phRows

Pointer to an array of HROWs for which to revert changes.

cRows

The number of HROWs in the array.

pcRows

Pointer to a buffer to receive the number of rows that were undone.

pphRows

Pointer to a buffer to receive the callee-allocated HROWs array identifying the rows that were undone. Use CRowset::FreeRows or a combination of CRowset::ReleaseRows and CoTaskMemFree to release the HROWs and free the callee-allocated buffer.

ppStatus

Pointer to a buffer to receive a callee-allocated array of DBROWSTATUS values for each row handle returned. Use CoTaskMemFree to free the callee-allocated array. This argument can be NULL if the call does not want row status information.

Return Code

S_OK

Method succeeded.

E_NOINTERFACE
Returned if IRowsetUpdate is not available.

E_FAIL
Returned if the object is not attached.

Comments

Uses IRowsetUpdate::Undo to revert any changes made to the given HROWs prior to being committed by using IRowsetUpdate::Update.


Update

HRESULT Update(
const HROW *phRows,
ULONG cRows,
ULONG *pcRows,
HROW **pphRows,
DBROWSTATUS **ppStatus);

Parameters

phRows

Pointer to an array of HROWs to commit changes to the data source.

cRows

The number of HROWs in the array.

pcRows

Pointer to a buffer to receive the number of rows that were committed.

pphRows

Pointer to a buffer to receive the callee-allocated HROWs array identifying the rows that were committed. Use CRowset::FreeRows or a combination of CRowset::ReleaseRows and CoTaskMemFree to release the HROWs and free the callee-allocated buffer.

ppStatus

Pointer to a buffer to receive a callee-allocated array of DBROWSTATUS values for each row handle returned. Use CoTaskMemFree to free the callee-allocated array. This argument can be NULL if the call does not want row status information.

Return Code

S_OK

Method succeeded.

E_NOINTERFACE
Returned if IRowsetUpdate is not available.

E_FAIL
Returned if the object is not attached.


GetRows Using Bookmark

HRESULT GetRows(
const CBookmark& bmk,
long lOffset,
long cRowsRequested,
HROW*& phRows,
ULONG& cRows);

Parameters

bmk

Bookmark identifying the row to begin the fetch.

lOffset

Signed offset from the row identified by bmk to begin the fetch.

cRowsRequested

The number of rows to fetch.

phRows

Reference to buffer to receive HROWs. If this argument is NULL then the callee will allocate an array large enough to hold the number of HROWs returned. Use CRowset::FreeRows, or a combination of CRowset::ReleaseRows and CoTaskMemFree to release the HROWS and free the callee-allocated buffer. If this argument is not NULL, then the caller must have allocated enough space to hold the requested number of HROWs.

cRows

The number of HROWs actually fetched.

Return Code

S_OK

Method succeeded.

E_NOINTERFACE
Returned if IRowsetLocate is not available.

E_FAIL
Returned if the object is not attached.

Comments

Uses IRowsetLocate::GetRowsAt to retrieve the requested number of HROWs from the data source.


GetRows Using Bookmark Array

HRESULT GetRows(
const CBookmark *pBookmarks,
ULONG cBookmarks,
HROW* phRows,
DBROWSTATUS *pRowStatus = NULL);

Parameters

pBookmarks

Bookmark array.

cBookmarks

The number of bookmarks in the array.

phRows

Reference to buffer to receive HROWs. If this argument is NULL then the callee will allocate an array large enough to hold the number of HROWs returned. Use CRowset::FreeRows, or a combination of CRowset::ReleaseRows and CoTaskMemFree to release the HROWS and free the callee-allocated buffer. If this argument is not NULL, then the caller must have allocated enough space to hold the requested number of HROWs.

pRowStatus

Caller-allocated array to receive the row status for each of the HROWs fetched. This array must contain at least cBookmarks elements.

Return Code

S_OK

Method succeeded.

E_NOINTERFACE
Returned if IRowsetLocate is not available.

E_FAIL
Returned if the object is not attached.

Comments

Uses IRowsetLocate::GetRowsByBookmark to retrieve the HROWs identified by bookmarks in the given bookmark array.


CompareBookmarks

HRESULT CompareBookmarks(
const CBookmark& bmk1,
const CBookmark& bmk2,
DBCOMPARE& dbCmp);

Parameters

bmk1

Bookmark of a row.

bmk2

Bookmark of a row.

dbCmp

Reference to a buffer to receive the result of the comparison.

Return Code

S_OK

Method succeeded.

E_NOINTERFACE

Returned if IRowsetLocate is not available.

E_FAIL

Returned if the object is not attached.

Comments

Uses IRowsetLocate::Compare to compare two bookmarks.


GetRowsetProperties

HRESULT GetRowsetProperties(
IUnknown *pRowset,
const DBPROPID *pPropIDs,
VARIANT *pValues,
ULONG cPropIDs);

Parameters

pRowset

Pointer to an IRowset interface. This can be NULL to get properties on the attached rowset. If no rowset is attached, then E_FAIL is returned.

pPropIDs

Array of DBPROPID to request values for.

pValues

Array of variants to receive the values of the requested properties. This array must contain uninitialized variants, because their values are not cleared by this method prior to being set.

cPropIDs

The number of properties to query. This is the size of both pPropIDs and pValues arrays.

Return Code

S_OK

Method succeeded.

E_FAIL
Returned if the object is not attached.

Comments

Uses IRowsetInfo::GetProperties to retrieve properties for a particular IRowset.


IsSameRow Using Row Handle

HRESULT IsSameRow(
HROW hrow1,
HROW hrow2);

Parameters

hrow1

Handle to a row.

hrow2

Handle to a row.

Return Code

S_OK
Returned if both HROWs identify the same row in a data source.

S_FALSE
Returned if the HROWs identify different rows.

E_NOINTERFACE
Returned if IRowsetIdentity is not available.

E_FAIL
Returned if the object is not attached.

Comments

Uses IRowsetIdentity::IsSameRow to test whether two HROWs identify the same row in the data source.


IsSameRow Using Bookmark

HRESULT IsSameRow(
const CBookmark& bmk1,
const CBookmark& bmk2);

Parameters

bmk1

Bookmark of a row.

bmk2

Bookmark of a row.

Return Code

S_OK
Returned if the bookmarks identify the same row.

S_FALSE
Returned if they do not.

E_FAIL
Returned if the object is not attached, or any return values that may be generated by CRowset::GetRows and CRowset::IsSameRow(HROW, HROW). This method does not wrap any particular rowset interface method, but is provided as a convenience to the programmer.

Comments

Uses CRowset::CompareBookmarks to determine whether the two bookmarks identify the same row in the data source. If CRowset::CompareBookmarks fails, then the HROWs of the rows identified by the bookmarks are fetched. If the fetch was successful, then CRowset::IsSameRow(HROW, HROW) is used to compare the two rows.


CanConvert

HRESULT CanConvert(
DBTYPE dbtFrom,
DBTYPE dbtTo);

Parameters

dbtFrom

Type to convert from.

dbtTo

Type to convert to.

Return Code

S_OK

Returned if the types can be converted.

S_FALSE
Returned if the types cannot be converted.

E_NOINTERFACE

Returned if IConvertType is not available.

E_FAIL

Returned if the object is not attached.

Comments

Uses IConvertType::CanConvert to determine whether one type can be converted to another by the provider.


GetSelfBookmark

HRESULT GetSelfBookmark(
HROW hrow,
CBookmark& bmk);

Parameters

hrow

Handle to a row.

bmk

Reference to a bookmark object to receive the given row’s self-bookmark. bmk is cleared prior to being set with new bookmark data.

Return Code

S_OK

Method succeeded.

E_FAIL
Returned if the object is not attached, or any DB error code.

Comments

Use to retrieve the self-bookmark of the row identified by the given HROW. This method does not wrap any particular rowset interface method, but is provided as a convenience to the programmer.


Miscellaneous

Miscellaneous functionality is composed of macros and CArray.


Macros

The following macros are used to perform repetitive tasks.

Macro | Description
IS_SAME_GUIDS(riid1, riid2) | Use to test whether two UUIDs are the same. Evaluates to TRUE if the given UUIDs are the same, FALSE if not.
RETURN_ON_FAILURE(hr) | Use to test a return value within a function that returns HRESULT. If hr is a failure code, then the function returns the failure code to the caller. Otherwise, execution continues to the next statement.
RETURN_ON_NULLALLOC(ptr) | Use to test whether a pointer is NULL within a function that returns HRESULT. If the pointer is NULL, then the function returns E_OUTOFMEMORY to the caller. Otherwise, execution continues to the next statement.
RELEASE_OBJECT(ptr) | Use to release a COM object pointer. If the pointer is not NULL, then its IUnknown::Release method is called and the pointer is set to NULL.
QUICK_RELEASE(ptr) | Similar to RELEASE_OBJECT, except that the pointer is not set to NULL.
ADDREF_OBJECT(ptr) | Use to add a reference to a COM object pointer. If the pointer is not NULL, then its IUnknown::AddRef method is called.
CHECK_POINTER(val) | Use within a function that returns an HRESULT to check whether a pointer is not NULL and can be written up to four bytes. If the pointer is NULL or cannot be written up to four bytes, then E_POINTER is returned.
CHECK_POINTER_SIZE(val, sz) | Similar to CHECK_POINTER, except the user must specify the size to check whether the pointer can be written to.


CArray Template Class

CArray<TYPE, ARG_TYPE> is a template class that can be used to manage a collection of data of any type. To use this template with a C++ class, the class must implement a default constructor and override the assignment operator.

Part | Description
TYPE | The storage type. This can be an intrinsic C/C++ type, a user-defined structure or class, or pointer to any type.
ARG_TYPE | The storage type. This can be an intrinsic C/C++ type, a user-defined structure or class, or pointer to any type.


CArray Integer

CArray(
int nGrowSize = 10);

Parameters

nGrowSize

The number of elements by which to grow the array when additional memory is needed to add more elements to the array.


Add to Array

int Add(
ARG_TYPE item)
int Add(
const TYPE *pitem,
int nCount)

Parameter

item

Item to add to the array.

pitem

Pointer to the location of the first item.

nCount

Number of items to add.

Comments

Returns the new size of the array, or zero if allocation failed.

Adds a copy of the given item to the end of the array, or adds an array of items to the array; a copy of each item is added.


SetAt

BOOL SetAt(
int index,
ARG_TYPE item)

Parameters

index

Index of the item in the array to modify.

item

Item to copy.

Comments

Returns TRUE if successful, FALSE if the index is out of bounds.

Copies the contents of the given item to the item in the array at the given index.


InsertAt

int InsertAt(
int index,
ARG_TYPE item)
int InsertAt(
int index,
const TYPE *pitem,
int nCount)

Parameters

index

Index of the insertion point. This can be between 0 and the array size.

item

Item whose content is copied to the newly inserted item.

pitem

C array of items to copy.

nCount

Number of items in pitems to copy. The array grows by this value.

Comments

Returns the new size of the array, zero if allocation failed, or –1 if the index is out of bounds.

Inserts a copy of the given item at the given index or inserts a copy of the given array of items starting at the given index.


DeleteAt

int DeleteAt(
int index,
int nCount = 1)

Parameters

index

Index item to be deleted.

nCount

The number of items to delete, including the one at the given index.

Comments

Returns the number of elements left in the array. Deletes items starting at the specified index.


RemoveAll

void RemoveAll(void);

Comments

Removes all elements from the array.


Attach to Array

void Attach(
TYPE* pItems,
int nSize);

Parameters

pItems

C array allocated with CoTaskMemAlloc.

nSize

The size of the given C array.

Comments

Attaches an external C array to the array. RemoveAll is called implicitly to clear the current content. pItems must have been allocated with CoTaskMemAlloc.

pItems must be a valid pointer, and nSize must not be greater then the array size. No argument validation is done.


SetSize Array

BOOL SetSize(
int nNewSize,
int nGrowBy = -1);

Parameters

nNewSize

New array size. The elements are destroyed or constructed appropriately.

nGrowSize

New grow size. This value is ignored if it is less than zero.

Return Code

TRUE if successful or FALSE if insufficient memory.

Comments

Sets the new size for the array and, optionally, a new grow size.


GetSize

int GetSize(void) const;

Comments

Returns the current array size. This is zero if there are no elements.


GetUpperBound

int GetUpperBound(void) const;

Comments

Returns the base-zero, upper-bound index of the array. If the array is empty, then –1 is returned.


Item

TYPE Item(int index) const;
TYPE& Item(int index);

Parameter

index

Index of the item to return.

Return Code

Returns the item at the given index or returns the reference to the item at the given index.

Comments

Accesses a copy of the item at the given index. No bounds checking is done.


GetItems

const TYPE* GetItems(void) const;
TYPE* GetItems(void);

Comments

Returns the allocated memory buffer that is used to store array items. This is NULL if the array is not initialized.


operator[]

TYPE operator[](
int index) const;
TYPE& operator[](
int index);

Parameter

index

The index of the element in the array you want to access.

Comments

These operators are equivalent to the Items method. They allow the array to be used like C arrays in code.


IStream

IStream functionality in the ActiveX Control Writer’s SDK for OLE DB is composed of CMemStream.


CMemStream

The CMemStream implementation of IStream using a byte array. This class supports a subset of IStream methods required by COM to persist objects and data. It should be used for intermediate storage to reduce the number of reads and writes to the main IStream during persistence.

For a description of IStream methods and parameters, see the COM Programmer's Reference. You could use the CopyTo method to write CMemStream data to any stream.


Read

HRESULT Read(
void __RPC_FAR *pv,
ULONG cb,
ULONG __RPC_FAR *pcbRead);

Comments

Supported.


Write

HRESULT Write(
const void __RPC_FAR *pv,
ULONG cb,
ULONG __RPC_FAR *pcbWritten);

Comments

Supported.


Seek

HRESULT Seek(
LARGE_INTEGER dlibMove,
DWORD dwOrigin,
ULARGE_INTEGER __RPC_FAR *plibNewPosition);

Comments

Supported.


SetSize Used in IStream

HRESULT SetSize(
ULARGE_INTEGER libNewSize);

Comments

Supported.


CopyTo

HRESULT CopyTo(
IStream __RPC_FAR *pstm,
ULARGE_INTEGER cb,
ULARGE_INTEGER __RPC_FAR *pcbRead,
ULARGE_INTEGER __RPC_FAR *pcbWritten);

Comments

Supported.


Commit

HRESULT Commit(
DWORD grfCommitFlags);

Comments

Not supported. Returns E_NOTIMPL.


Revert

HRESULT Revert(void);

Comments

Not supported. Returns E_NOTIMPL.


LockRegion

HRESULT LockRegion(
ULARGE_INTEGER libOffset,
ULARGE_INTEGER cb,
DWORD dwLockType);

Comments

Not supported. Returns E_NOTIMPL.


UnlockRegion

HRESULT UnlockRegion(
ULARGE_INTEGER libOffset,
ULARGE_INTEGER cb,
DWORD dwLockType);

Comments

Not supported. Returns E_NOTIMPL.


Stat

HRESULT Stat(
STATSTG __RPC_FAR *pstatstg,
DWORD grfStatFlag);

Comments

Supported.


Clone

HRESULT Clone(
IStream __RPC_FAR *__RPC_FAR *ppstm);

Comments

Supported.


String Handling

String-handling functionality is composed of String Support.


String Support

Several macros and string API helpers are provided in the ActiveX Control Writer’s SDK for OLE DB, and they help make your code more readable. The string API allows you to leave the C run-time library out of your code for string functionality.


MAKE_MULTIBYTESTRING

MAKE_MULTIBYTESTRING(name, arg)

Parameters

name

Variable name of the allocated ANSI string pointer.

arg

Source string, must be a BSTR or a WCHAR*.

Comments

Use this macro to convert a wide string to an ANSI multibyte string. The buffer returned by this macro is automatically deallocated when it goes out of scope.

Example

   // Convert BSTR to ANSI string buffer.
   // Must check for NULL allocation.
   //
   MAKE_MULTIBYTESTRING(szText, bstrText)
   if (NULL == szText) return E_OUTOFMEMORY;

   // Use allocated ANSI string.
   //
   SetWindowText(m_hwnd, szText);

   //
   // szText will be freed when it loses scope.
   //


ANSI Strings

String type Value
Comparison: int lstrncmp(const char *sz1, const char *sz2, int cChars);
int lstrncmpi(const char *sz1, const char *sz2, int cChars);
Allocation: char *stralloc(const char *sz);
char *stralloc(const BSTR bstr);
char *stralloc(const WCHAR *wsz);

Comments

Frees memory allocated with stralloc using CoTaskMemFree.


BSTR

String type | Value
Comparison int bstrcmp(const BSTR bstr1, const BSTR bstr2);
int bstrncmp(const BSTR bstr1, const BSTR bstr2, int cChars);
int bstrcmpi(const BSTR bstr1, const BSTR bstr2);
int bstrncmpi(const BSTR bstr1, const BSTR bstr2, int cChars);
Length | int bstrlen(const BSTR bstr);
Copy BSTR bstrcpy(BSTR& bstrDst, const BSTR bstrSrc);
BSTR bstrncpy(BSTR& bstrDst, const BSTR bstrSrc, int cChars);
Allocate BSTR bstralloc(const BSTR bstr);
BSTR bstralloc(const char *sz);
BSTR bstralloc(const WCHAR *wsz);

Comments

Frees memory allocated by bstralloc using SysFreeString.


Wide Strings

String type | Value
Comparison int wstrcmp(const WCHAR *wsz1, const WCHAR *wsz2);
int wstrncmp(const WCHAR *wsz1, const WCHAR *wsz2, int cChars);
int wstrcmpi(const WCHAR *wsz1, const WCHAR *wsz2);
int wstrncmpi(const WCHAR *wsz1, const WCHAR *wsz2, int cChars);
Length | int wstrlen(const WCHAR *wsz);
Copy WCHAR *wstrcpy(WCHAR *wszDst, const WCHAR *wszSrc);
WCHAR *wstrncpy(WCHAR *wszDst, const WCHAR *wszSrc, int cChars);
Allocate WCHAR *wstralloc(const WCHAR *wsz);
WCHAR *wstralloc(const BSTR bstr);
WCHAR *wstralloc(const WCHAR *wsz);

Comments

Frees memory allocated by wstralloc using CoTaskMemFree.


Bookmarks

Bookmark functionality is composed of:

  • Standard bookmarks

  • CBookmark class


Standard Bookmarks

The following constants are used to identify standard bookmarks. For more information about bookmarks, see the OLE DB Programmer's Reference or the Bookmarks section of this SDK.

Constant | Description
BMK_SIZE | Defined as sizeof(BYTE). This is used for the size of all standard bookmarks.
BYTE BMK_INVALID | Value of an undefined or invalid standard bookmark.
BYTE BMK_CURRENT | Standard cursor bookmark value for the current row.
BYTE BMK_BEGINNING | Standard cursor bookmark value for the start of the recordset, before the first row.
BYTE BMK_END | Standard cursor bookmark value for the end of the recordset, after the last row.
BYTE DBBMK_FIRSTROW | Standard rowset bookmark value for the first row in the recordset.
BYTE DBBMK_LASTROW | Standard rowset bookmark value for the last row in the recordset.


CBookmark Class

Used to store and manage bookmark data. Bookmark data up to 4 bytes long is stored in line. Data longer than 4 bytes is stored in an allocated byte array.

CBookmark

CBookmark();
CBookmark(
const CBookmark& other);
CBookmark(
ULONG ulSize,
const BYTE *pBlob);
CBookmark(
const BLOB& blob);

Parameters

ulSize

Size of the bookmark data in bytes.

pBlob

Pointer to bookmark data. The contents of pBlob are copied.

blob

Blob structure containing bookmark data. The contents of blob are copied.

Comments

Bookmarks created with the default constructor are invalid. The copy constructor duplicates a bookmark. The other two constructors initialize the object.


IsInvalid

BOOL IsInvalid(void)

Comments

Returns TRUE if the bookmark is not initialized.


IsBeginning

BOOL IsBeginning(void) const

Comments

Returns TRUE if the bookmark is a standard-cursor beginning of a recordset bookmark. Should not be used with Rowset bookmarks.


IsCurrent

BOOL IsCurrent(void) const

Comments

Returns TRUE if the bookmark is a standard-cursor, current-row bookmark. Should not be used with Rowset bookmarks.


IsEnd

BOOL IsEnd(void) const

Comments

Returns TRUE if the bookmark is a standard-cursor end of a recordset bookmark. Should not be used with Rowset bookmarks.


IsFirstRow

BOOL IsFirstRow(void) const

Comments

Returns TRUE if the bookmark is a standard Rowset first-row bookmark. Should not be used with Cursor bookmarks.


IsLastRow

BOOL IsLastRow(void) const

Comments

Returns TRUE if the bookmark is a standard Rowset last-row bookmark. Should not be used with Cursor bookmarks.


IsBookmark

static BOOL IsBookmark(
const VARIANT& var)

Parameters

var

An object of type Variant.

Comments

Tests whether a VARIANT is a byte array. Bookmarks are represented as Variant byte arrays.


Clear Memory of Bookmark Data

void Clear(void);

Comments

Frees any memory allocated to store the bookmark data and uninitializes the bookmark.


Set

HRESULT Set(
ULONG ulSize,
const BYTE *pBlob);
HRESULT Set(
const VARIANT& var);

Parameters

ulSize

Size of bookmark data in bytes. If ulSize is zero, then the bookmark is cleared and uninitialized.

pBlob

Pointer to bookmark data. Ignored if ulSize is zero.

var

Variant of type byte array.

Return Code

S_OK

Method succeeded.

E_OUTOFMEMORY

Returned if allocation failed.

E_UNEXPECTED

Returned if the array could not be locked if you use the Variant.

E_INVALIDARG

Returned if the Variant is not a byte array.

Comments

Sets new data for the bookmark. Clear is implicitly called prior to setting the new data to free any memory allocated for any current bookmark data. The new data is copied.


Get

HRESULT Get(
VARIANT& var) const;

Parameters

var

Variant to receive the byte array.

Return Code

S_OK

Method succeeded.

E_OUTOFMEMORY

Returned if allocation failed.

DISPID_E_ARRAYISLOCKED

Returned if the Variant contained a locked array.

DISPID_E_BADVARTYPE

Returned if the Variant contained an invalid type.

E_INVALIDARG

Returned if the Variant is not initialized properly.

Comments

Copies bookmark data to the Variant as a byte array. The Variant must be previously initialized. The Variant is cleared before being set. If the bookmark is not initialized, then the Variant is left cleared.


BLOB

operator BLOB&()
operator const BLOB&() const

Comments

Returns BLOB base structure.


ULONG

operator ULONG() const

Comments

Returns the size of the bookmark data.


void

operator void*()
operator const void*() const

Comments

Returns a void pointer to the bookmark data.


BYTE

operator BYTE*()
operator const BYTE*() const

Comments

Returns a byte pointer to the bookmark data.


operator== Comparing Bookmarks

BOOL operator==(
const CBookmark& other) const;
BOOL operator==(
const VARIANT& var) const;

Parameters

other

An object of type Variant.

var

An object of type CBookmark.

Comments

This is an overloaded operator that performs a binary comparison to another bookmark. The other bookmark can be a CBookmark object or a Variant representation of a bookmark.


operator= Used to Copy Bookmark

CBookmark& operator=(
const CBookmark& other);

Parameters

other

An object of type CBookmark.

Comments

Copies the contents of another bookmark. Clears current bookmark data before copying new data.


Persistence

Persistence functionality is composed of:

  • Constants

  • lass CPersistProperties


Constants

PERSIST_STREAM_ID

Defined in Persist.h as 0xABCDEF01. This is the default ID used by CPersistProperties to identify persisted streams.


class CPersistProperties

This class helps you manage a collection of properties to be persisted. This class does all the work necessary to exchange property values between the user-defined buffer and the persistence medium.

To use the CPersistProperties class

  1. Declare an instance of the class.

  2. Add properties using AddProp.

  3. Use EnablePropSave to designate that a property should be saved. All properties are enabled for saving by default.

  4. Call Load and Save to persist to the desired medium.

  5. Use IsPropSaved to query whether a property is saved after a Load or Save.

  6. Use GetVersionPersisted to determine the version number of loaded properties.

A property is identified by its offset in the user buffer (see AddProp). Once persisted, the user buffer should not be modified where the offset of existing properties are changed. You can add new properties to the user buffer as long as you don’t modify existing property offsets. If you modify the existing property offsets, then you should change the stream ID to invalidate previously persisted streams.

Change the version number as desired to distinguish the current version of the properties from those previously persisted. Properties will continue to load if the existing version is the same or higher than previous versions. If the current version is lower than the persisted version, then the properties will not load.

All methods of the base class CArray are available.


CPersistProperties

CPersistProperties(
DWORD dwVersion,
DWORD dwStreamID = PERSIST_STREAM_ID);

Parameters

dwVersion

Stream version. Properties will not load if the persisted stream version is greater than the current version.

dwStreamID

Stream ID. Properties will not load if the persisted stream ID differs.

Comments

Constructs an instance of the object.

Increment the version number when the user buffer changes as a result of adding more properties, but existing property offsets have not been modified. This allows older streams to load. Change the stream ID if the user buffer has changed, where one or more previous property types have changed in relation to their offsets. This prevents erroneous stream loads.


AddProp

HRESULT AddProp(
const WCHAR* wszName,
VARTYPE vtType,
long lOffset,
void *pDefValue);

Parameters

wszName

Pointer to the property name, used for PropertyBag persisting. This pointer must be constant data and must not change during the lifetime of the object.

wtType

Variant data type of the property. The following property types are supported.

Property type | Description
VT_BOOL | Use for properties of VARIANT_BOOL.
VT_I2 | Use for numeric properties of type short.
VT_I4 | Use for numeric properties of type long or integer.
VT_R4 | Use for numeric properties of type float.
VT_R8 | Use for numeric properties of type double.
VT_LPWSTR | Use for wide string properties.
VT_LPSTR | Use for ANSI/multibyte string properties.
VT_BSTR | Use for BSTR string properties (Basic strings).
VT_UNKNOWN | Use for interface properties that support IPersistStream and IPersistPropertyBag and are co-creatable. For example, IFont and IPicture are interfaces that meet these requirements.

lOffset

Offset to property in the user data buffer. This is unique among the properties. If a property is already added with the same offset, then the latter add will replace the former.

pDefValue

The default value of the property. Properties are not persisted if the current value matches the default value. Depending on the property data type, this argument is interpreted as follows.

Property type | Description
VT_BOOL | Pointer to VARIANT_BOOL; the value is copied.
VT_I2 | Pointer to a short; the value is copied.
VT_I4 | Pointer to a long; the value is copied.
VT_R4 | Pointer to a float; the value is copied.
VT_R8 | Pointer to a double; the value is copied.
VT_LPWSTR | Pointer to the string data; the pointer is stored and referenced as needed.
VT_LPSTR | Pointer to the string data; the pointer is stored and referenced as needed.
VT_BSTR | Pointer to the string data; the pointer is stored and referenced as needed.
VT_UNKNOWN | Pointer to a CLSID; the pointer is stored and referenced as needed.

Returns

S_OK

Method succeeded.

E_OUTOFMEMORY
Returned if allocation failed.

E_INVALIDARG
Returned if the Variant type is not supported.

Comments

Adds a property to the persistence collection.

For VT_UNKNOWN, pDefValue must point to an IID for the interface object. After a load, the IUnknown is QI for the interface identified by the given IID, and is stored at the specified offset in the user buffer.

Property offsets are offsets within a contiguous buffer kept by the caller. A property offset can be an offset to a data member within a class or a data member within a structure. Regardless, for each CPersistProperties collection, the offsets must be within the same buffer. The buffer and the buffer size are passed to the Load and Save methods for persistence.


EnablePropSave

HRESULT EnablePropSave(
long lOffset,
BOOL fEnable = TRUE);

Parameters

lOffset

Offset in the user buffer that identifies the property.

fEnable

TRUE to enable a property for save.

Return Code

S_OK

Method succeeded.

E_INVALIDARG
Returned if a property with the given offset is not found.

Comments

Enables a property for saving. When added, all properties are save-enabled by default. The property will not be persisted if saving is disabled.


Load

HRESULT Load(
IStream *pStream,
void *pBuffer,
long cbBuffer);
HRESULT Load(
IPropertyBag *pPropertyBag,
IErrorLog *pErrorLog,
void *pBuffer,
long cbBuffer);

Parameters

pStream

Pointer to IStream from which to load properties.

pBuffer

User-defined buffer. This data is not modified.

cbBuffer

Size of the user buffer. Buffer overwrite is checked using this buffer size.

pPropertyBag

Pointer to IPropertyBag from which to load.

pErrorLog

Pointer to IErrorLog interface. Load errors are sent to the log file.

Return Code

S_OK

Method succeeded.

E_FAIL
Returned if the persisted version is greater or stream IDs differ.

Comments (Load IStream)

Loads properties from the stream. All properties saved to the stream are loaded into memory, but only those properties currently in the collection are set, provided that they were loaded (previously saved). This allows the current collection to contain fewer or more properties than the persisted stream.

Comments (Load PropertyBag)

Loads properties from the PropertyBag. When loading, all properties in the collection are attempted. If a property fails to load, then it is assumed that it was not persisted. Its offset in the user buffer remains unmodified. IsProperSaved will return FALSE for any property not loaded.

If a property is loaded, then the current value of its location in the user buffer is properly freed. Each property is freed according to its data type.

Property type | What happens
VT_BOOL | Value replaced.
VT_I2 | Value replaced.
VT_I4 | Value replaced.
VT_R4 | Value replaced.
VT_R8 | Value replaced.
VT_LPWSTR | Current pointer is freed using CoTaskMemFree.
VT_LPSTR | Current pointer is freed using CoTaskMemFree.
VT_BSTR | Current pointer is freed using SysFreeString.
VT_UNKNOWN | Current pointer is freed using IUknown::Release.

Call IsPropSaved to test whether a property was actually saved or loaded.


Save

HRESULT Save(
IStream *pStream,
const void *pBuffer,
long cbBuffer);
HRESULT Save(
IPropertyBag *pPropertyBag,
const void *pBuffer,
long cbBuffer,
BOOL fWriteDefaults = FALSE);

Parameters

pStream

Pointer to IStream to which to save properties.

pBuffer

User-defined buffer. This data is not modified.

cbBuffer

Size of the user buffer. Buffer overflow is checked using this buffer size.

pPropertyBag

Pointer to IPropertyBag to which to save properties.

fWriteDefault

TRUE to persist all properties, FALSE to persist only those that are not the default.

Return Code

S_OK if succeeded, otherwise error code.

Comments (Save IStream)

Save properties to the stream.

Use EnablePropSave to prevent a property from being saved. Otherwise, only properties without default values are saved. Use IsPropSaved to test whether the property was actually saved.

Comments (Save IPropertyBag)

Saves properties to the PropertyBag.

Use EnablePropSave to prevent a property from being saved. This supersedes fWriteDefaults. Use IsPropSaved to test whether the property was actually saved.


IsPropSaved

BOOL IsPropSaved(
long lOffset);

Parameters

Offset

The offset into the user-defined buffer.

Comments

Returns TRUE if the property identified by the given offset was saved. Call after a successful Load to test whether the property was previously saved and loaded. Call after a successful Save to test whether a property was saved.


GetVersionPersisted

DWORD GetVersionPersisted(void)

Comments

Called after a Load to check the persisted version number. If the load was successful, then this value will always be less than or equal to the current version number.


Property Pages

Property pages and property browsing functionality is composed of:

  • Error handling

  • String resources

  • Macros

  • Additional property support

  • Class CCtlPropertyPage

  • Overridable functions


Error Handling

Property pages may receive incorrect user input and require code for error handling and recovery. A property page should be able to handle any type of user input or system error, such as an out-of-memory error. This SDK supplies macros and support functions to handle most error conditions.


String Resources

IDS_SHOW_ERROR_TITLE

The error macros use this constant to load the string resource for the error message box title. Before using the macros, you need to undefine the constant and define it to the string ID that you want it to use. You can do this as many times as needed within a source module.

Example

// Define error title to first property page title.
//
#undef IDS_SHOW_ERROR_TITLE
#define IDS_SHOW_ERROR_TITLE  IDS_GENERAL_PROPPAGE_TITLE

For the error-handling macros to work properly, the control must define the following string IDs and resources:

// These string IDs must be defined in the resource header file.
//
#define IDS_ERR_FORMAT         1   // Generic error format message
#define IDS_ERR_INVALIDENTRY   2   // Invalid entry error format message
#define IDS_ERR_OUTOFMEMORY      7   // Out-of-memory error message

// These string resources must be defined in the resource file.
// The error message does not have to be the same, but the formatting must be present.
//
STRINGTABLE DISCARDABLE
BEGIN
   IDS_ERR_FORMAT      "Error #%d"
   IDS_ERR_INVALIDENTRY   "Invalid entry for %s"
   IDS_ERR_OUTOFMEMORY   "Out of memory"
END


Macros Used with Property Pages

The following error macros are used in conjunction with property pages. They act on errors returned by control properties and methods, and assume that the control has thrown an exception before returning the error code. The exception information is used to compose the error message that is displayed to the user.

Controls do not throw exceptions for the E_OUTOFMEMORY error code, so the error message for this code is defined by the IDS_ERR_OUTOFMEMORY string resource. Error codes returned without exceptions are displayed with the generic error message defined by the IDS_ERR_FORMAT string resource.

The INVALID_ENTRY_* macros use the string resource defined by the IDS_ERR_INVALIDENTRY string ID to format the error message.

Macro | Description
ERROR_ON_FAILURE(hr) | Test whether hr is an error code; if so, display the error. Execution continues to the next statement.
IF_SUCCEEDED(hr) | Test whether hr is an error code; if so, display the error. Otherwise, execute the statement block.
ON_ERROR_RETURN(hr, ret) | Test whether hr is an error code; if so, display the error and return ret. Otherwise, continue executing the next statement.
ON_ERROR_GOTO(hr, label) | Test whether hr is an error code; if so, display the error and goto label. Otherwise, continue executing the next statement.
ON_ERROR_EDIT_GOTO(hr, id, label) | Test whether hr is an error code; if so, display the error, set focus to the edit control identified by id, and goto label. Otherwise, continue executing the next statement.
ON_ERROR_EDIT_RETURN(hr, id, ret) | Test whether hr is an error code; if so, display the error, set focus to the edit control identified by id, and return ret. Otherwise, continue executing the next statement.
ON_INVALID_ENTRY_RETURN(f, id, ret) | If f is TRUE, then continue executing the next statement. Otherwise, display an invalid entry error message box, set focus to the edit control identified by id, and return ret. The control prior to the edit control must be a static text control because its text is used in the error message to identify the invalid property.
ON_INVALID_ENTRY_GOTO(f, id, label) | If f is TRUE, then continue executing the next statement. Otherwise, display an invalid entry error message box, set focus to the edit control identified by id, and goto label. The control prior to the edit control must be a static text control because its text is used in the error message to identify the invalid property.


ShowError

void ShowError(
HRESULT hrInput,
WORD idPropPageTitle);

Parameters

hrInput

Error code returned by a control property or method.

idPropPageTitle

String resource used for the error message box title. The error macros use IDS_SHOW_ERROR_TITLE as the string resource ID.

Comments

Shows an error message box using exception information that is thrown by the control before calling this function. This function is usually called by the above error macros. If no exception information is available, then the generic error message is displayed.


GetControlTag

void GetControlTag(
HWND hwnd,
UINT uCtlID,
char *szBuf,
int cbBuf);

Parameters

hwnd

Window handle of the property page dialog box.

uCtlID

ID of an edit control with an invalid entry. This control must have a static text control prior to it that identifies the edit control whose ID is uCtlID.

szBuf

Buffer to receive the static text.

cbBuf

The buffer size.

Comments

Fills szBuf with the text of the static control prior to the control identified by uCtlID. This function is called through the use of the ON_INVALID_ENTRY_* macros to extract the text that identifies the edit control whose entry is invalid.


SetFocusToEdit

void SetFocusToEdit(
HWND hwnd,
UINT uIDSTitle,
UINT uCtlID,
BOOL fError = FALSE);

Parameters

hwnd

Window handle of the property page dialog box.

uCtlID

ID of an edit control with an invalid entry. This control must have a static text control prior to it that identifies if fError is TRUE.

fError

If TRUE, then an invalid entry error message box is displayed. This function calls GetControlTag to get the static text, and is called by the ON_INVALID_ENTRY_* macros.

Comments

Uses the WM_NEXTDLGCTL dialog message to set the focus to the edit control identified by uCtlID.


Additional Property Support

GetStrings fills an array of strings for an enumeration type identified by the given IID in the type info. GetValue retrieves the value in the GetStrings array based on the cookie.


GetStrings

HRESULT GetStrings(
CALPOLESTR *prgOleStr,
CADWORD *prgCookie,
int iObjectType,
REFIID riid,
int ctItems = 0);

Parameters

prgOleStr

CALPOLESTR* parameter passed to GetPredefinedStrings.

prgCookie

CADWORD* parameter passed to GetPredefinedStrings.

iObjectType

Index of the control info object in the global OBJECTINFO array g_ObjectInfo.

riid

GUID of enumeration type.

cItems

Limit of how many items to return. This is zero for all enum items.

Comments

Called by IPerPropertyBrowse::GetPredefinedStrings to populate the CALPOLESTR* and CADWORD* structures with string information for enumeration type properties.


GetValue

HRESULT GetValue(
VARIANT *pVarOut,
DWORD dwCookie);

Parameters

pVarOut

VARIANT* parameter passed to GetPredefinedValue.

dwCookie

DWORD passed to GetPredefinedValue.

Comments

Called by IPerPropertyBrowse::GetPredefinedValue to retrieve the enumeration value identified by the given cookie. The cookie must have been created by a call to GetStrings.


struct COMBOTOGUID

struct COMBOTOGUID

Parameters

WORD id

ID of a combo box.

const GUID *pGuid;

Pointer to enumeration type’s UUID.

Comments

This structure is used to establish a relationship between a combo box and the GUID of an enumeration type defined in the .odl file.


FillAllComboBoxes

HRESULT FillAllComboBoxes(
HWND hwnd, int iObjType,
COMBOTOGUID *pComboToGuid);

Parameters

hwnd

Window handle of the property page dialog box.

iObjType

Object index of the control. This index is the zero-base index of the control object in the global object array g_ObjectInfo.

pComboToGuid

Array of COMBOTOGUID structures. The last structure must contain NULL entries.

Comments

Fills all combo boxes in the array pComboToGuid using type information for each combo box associated with an enumeration type’s GUID.


FillComboBox

HRESULT FillComboBox(
HWND hwnd,
ITypeInfo *pTypeInfo);

Parameters

hwnd

Window handle of the combo box.

pTypeInfo

Pointer to ITypeInfo for enum.

Comments

Called by FillAllComboBoxes to fill a combo box with enumeration entries.


class CCtlPropertyPage

Inheriting from CPropertyPage and IPropertyNotifySink, this class provides some basic property page functionality. All property pages must establish an IPropertyNotifySink advise connection with the first control object to detect property changes made through other user-interface mechanisms other than the property page itself. This class provides overrides that allow derived property pages to get notifications without having to explicitly connect to a control.


CCtlPropertyPage

CCtlPropertyPage(
IUnknown *pUnkOuter,
int nObjIndex);

Parameters

pUnkOuter

Pointer to IUnknown for aggregation. This can be NULL.

nObjIndex

Zero-based index of the property page OBJECTINFO structure in the global array g_ObjectInfo.

Comments

The class constructor.


IsDirty

BOOL IsDirty()

Comments

Returns TRUE if the page is dirty.


SetDirtyFlag

void SetDirtyFlag(
BOOL fDirty = TRUE);

Parameter

fDirty

TRUE to dirty the page. FALSE to mark the page as clean.

Comments

Sets the dirty flag for the page. The page should be dirtied when the user changes property values in the UI, which enables the Apply button. SetDirtyFlag(FALSE) should be called after handling the PPM_APPLY message to disable the Apply button.


EnableModeless

void EnableModeless(
BOOL fEnable = TRUE);

Parameter

fEnable

TRUE to enable host’s UI components. FALSE to disable them.

Comments

Call before displaying modal dialogs to disable the host’s UI components. EnableModeless(FALSE) should be called before invoking a modal dialog, and EnableModeless(TRUE) should be called after the modal dialog is dismissed.


Advise Using IUnknown

HRESULT Advise(
IUnknown *pUnkAdvise);

Parameter

pUnkAdvise

Pointer IUnknown of the control to advise on. If this is NULL, then any current advise connection is disengaged.

Comments

Call to establish an IPropertyNotifySink connection with a control. This is called by the base class for the first control prior to passing the PPM_NEWOBJECTS to the PageDialogProc.


DialogProc

virtual BOOL DialogProc(HWND, UINT, WPARAM, LPARAM);

Comments

Overrides ActiveX Framework base class CPropertyPage. Object derived from CCtlPropertyPage should override PageDialogProc instead. See PageDialogProc.


Overridable Functions

These overrides are necessary for you to put the functionality into the property page you are designing.


InternalQueryInterface

virtual HRESULT InternalQueryInterface(
REFIID riid,
void **ppUnk);

Parameters

riid

The UUID of the requested interface.

ppUnk

Pointer to a buffer to receive a pointer to an interface, if implemented.

Return Code

S_OK
Returned if the interface is implemented.

E_NOINTERFACE

Comments

CUnknownObject overridable. Derived classes should override this method to respond to their own interfaces. You will find that the base class CctlPropertyPage implementation of InternalQueryInterface is sufficient for most cases. If you do override this method, then you will be required to call CCtlPropertyPage::InternalQueryInterface for any interfaces that your property page does not support.


PageDialogProc

virtual BOOL PageDialogProc(HWND, UINT, WPARAM, LPARAM) PURE;

Parameters

See the parameter list for the Win32 SDK DialogProc function.

Comments

Must be overridden by a derived class to handle dialog messages. CCtlPropertyPage::DialogProc preprocesses messages before passing them to this method. Derived classes shouldn’t need to extend DialogProc unless the base-class handling of desired messages is not sufficient.


OnPropertyChanged

virtual HRESULT OnPropertyChanged(
IUnknown *pUnkSrc,
DISPID dispid)

Parameters

pUnkSrc

Pointer to IUnknown of a notifying control.

dispid

DISPID of a property that changed.

Comments

Derived classes should override this method to respond to changes in control properties. This method is not called during PPM_APPLY processing to reduce notification traffic, because the notifications are assumed to be generated by the page itself. To have full control over the notification traffic, derived classes should override IPropertyNotifySink::OnChanged.


OnPropertyRequestEdit

virtual HRESULT OnPropertyRequestEdit(
IUnknown *pUnkSrc,
DISPID dispid)

Parameters

pUnkSrc

Pointer to IUnknown of a notifying control.

dispid

DISPID of a property requesting a change.

Comments

Derived classes should override this method to respond to change requests for control properties.


Additional Data Access Support

Additional data access support is composed of:

  • COLUMNINFO structure

  • ROWSETPROPERTIES structure

  • Class CBindingsArray


COLUMNINFO structure

COLUMNINFO is an example of how users can extend existing data structures. The COLUMNINFO structure extends DBCOLUMNINFO to contain additional metadata. This structure also implements constructors and destructors to provide automatic cleanup of complex data members. CRowset::GetColumns returns an array of COLUMNINFO structures.


COLUMNINFO( )

Default constructor. All data members are zero initialized.


Operator= Used with COLUMNINFO

COLUMNINFO& operator=(
const COLUMNINFO& other)
COLUMNINFO& operator=(
const DBCOLUMNINFO& other)

Comments

Overloaded assignment operator. The left side of the COLUMNINFO structure contents are cleared before the assignment is made. The right side of the structure is left intact. If the assignment succeeds, both structures will be synonymous (COLUMNINFO to COLUMNINFO). In addition, the DBCOLUMNINFO portion to the left of COLUMNINFO is synonymous to the right of DBCOLUMNINFO; and additional data members of the left COLUMNINFO structure will be zero initialized.


Clear Memory of Complex Data

void Clear(
BOOL fZeroMemory )

Parameters

fZeroMemory

If TRUE, then structure members are zero initialized.

Comments

Clears the structure contents by freeing memory associated with complex data members. Clear is called implicitly before any assignment, and by the destructor.


Members

VARIANT varDefault

Additional metadata that can be stored in the structure.

Also see DBCOLUMNINFO for a description of its members.


ROWSETPROPERTIES structure

This structure is returned by CRowset::GetProperties and can be used by callers to examine the most common properties of a rowset that may be of interest. Its members are grouped into two substructures:


Value Data Members

  • value.MaxOpenRows The maximum number of rows that consumers are allowed to reference at one time. See DBPROP_MAXOPENROWS for more detail.

  • value.Updatability The bitmask of IRowsetChange methods supported. See DBPROP_UPDATABILITY for more details.


Boolean Flags

The implementation of CRowset::GetProperties demonstrates how to use CRowset::GetRowsetProperties to query rowset properties.

Flag | Description
flag.StrongId | TRUE if a newly inserted HROW can be compared to existing HROWS. See DBPROP_STRONGIDENTITY for more details.
flag. LiteralId | TRUE if HROWs can be compared literally. See DBPROP_LITERALIDENTITY for more details.
flag. HasBookmarks | TRUE if rows in the rowset have a self-bookmark column. When TRUE, the rowset will also expose IRowsetLocate. See DBPROP_BOOKMARKS for more details.
flag. CanHoldRows | TRUE if HROWs can be referenced in between fetches. See DBPROP_CANHOLDROWS for more details.
flag. CanScrollBack | TRUE if the rowset allows backward scrolling. See DBPROP_CANSCROLLBACKWARDS for more details.
flag. CanFetchBack | TRUE if the rowset allows rows to be fetched backward. See DBPROP_CANFETCHBACKWARDS for more details.
flag. CanChange | TRUE if the rowset supports IRowsetChanged. See DBPROP_IRowsetChange for more details.
flag. CanUpdate | TRUE if the rowset supports IRowsetUpdate. See DBPROP_IRowsetUpdate for more details.
flag. IRowsetScroll | TRUE if the rowset supports IRowsetScroll. See DBPROP_IRowsetScroll for more details.
flag. IRowsetExactScroll | TRUE if the rowset supports IRowsetExactScroll. See DBPROP_IRowsetExactScroll for more details.


Class CBindingsArray

CBindingsArray can be used to manage DBBINDING structures. All methods of CArray are available.


CBindingsArray

CBindingsArray()

Comments

Default constructor. All data members are zero initialized.


AddBindings

HRESULT AddBinding(
const DBBINDING& dbBinding)
HRESULT AddBinding(
ULONG iOrdinal,
DBTYPE wType,
ULONG cbMaxLen,
ULONG obValue,
ULONG obLength = UNUSED_OFFSET,
ULONG obStatus = UNUSED_OFFSET,
BYTE bPrecision = 0,
BYTE bScale = 0)

Parameters

dbBinding

reference to the DBBINDING structure to copy.

iOrdinal

Column ordinal.

wType

DBTYPE to which to convert the column data.

cbMaxLen

Maximum length of the data member is the user buffer.

obValue

Offset to start of the data member in the user buffer.

obStatus = UNUSED_OFFSET

Offset to the DBSTATUS member in the user buffer.

obLength = UNUSED_OFFSET

Offset to the length member in the user buffer.

bPrecision =0

Precision to use when converting to DBTYPE_NUMERIC or DBTYPE_DECIMAL.

bScale

=0 Scale to use when converting to DBTYPE_NUMERIC or DBTYPE_DECIMAL.

Comments

Adds an entry to the array using the exact copy of the given DBBINDING structure. The original structure is left intact. The longer form of AddBinding adds an entry to the array using the given parameters to set up the entry.

See the implementation of the InitBinding helper function for more details about the interpretation of this method's parameters.


ClearID

void ClearID(
DBID& dbid)

Parameters

dbid

A column ID.

Comments

Clears a column ID by properly deallocating its pointers.


CopyID

void CopyID(DBID& dbidDst, const DBID& dbidSrc)

Parameters

dbidDst

The destination column ID.

dbidSrc

The column ID to be copied.

Comments

Copies a column ID.


IsSameIDs

BOOL IsSameIDs(
const DBID& dbid1,
const DBID& dbid2)

Parameters

dbid1

The first column ID.

dbid1

The second column ID.

Comments

Compares two column IDs for equality.


IsBmkColumn

BOOL IsBmkColumn(
const DBCOLUMNINFO& col)

Parameters

Col

Column metadata in a DBCOLUMNINFO structure.

Comments

Tests for a bookmark column.


InitBinding

void InitBinding(
DBBINDING& dbBinding,
ULONG iOrdinal,
DBTYPE wType,
ULONG cbMaxLen,
ULONG obValue,
ULONG obLength /*= UNUSED_OFFSET*/,
ULONG obStatus /*= UNUSED_OFFSET */,
BYTE bPrecision /*=0*/,
BYTE bScale /*=0*/)

Parameters

dbBinding

The structure to initialize for binding.

iOrdinal

The ordinal of the column or parameter to which the binding applies.

wType

The indicator of the data type of the value part of the buffer.

CbMaxLen

The length in bytes of the consumer’s data structure allocated for the data value.

obValue

The offset in bytes in the consumer’s buffer to the value part.

obLength

The offset in bytes in the consumer’s buffer to the length part.

obStatus

The offset in bytes in the consumer’s buffer to the status part.

bPrecision

The maximum precision to use when getting data and wType is DBTYPE_NUMERIC.

bScale

The scale to use when getting data, and wType is DBTYPE_NUMERIC or DBTYPE_DECIMAL.

Comments

Initializes a DBBINDING structure for accessing rows. For more information, see the OLE DB Programmer's Reference for details about the DBBINDING structure.


Advise Connections

Advise connection functionality is composed of:

  • Class CAdviseConnection

  • Class CConnectionsArray

  • Class CPropNotifySink

  • Helpers


Class CAdviseConnection

This class is used to manage a single connection point. It is used by CConnectionsArray to manage a set of connection points. It keeps data that is necessary to maintain a connection and disconnection.


CAdviseConnection

CAdviseConnection( )
CAdviseConnection (
REFIID riid )

Parameters

riid

Reference to the IID of the desired connection point interface.


Operator = Used with CAdviseConnection

CAdviseConnection& operator=(
const CAdviseConnection& other)

Parameters

other

Reference to the right-side CAdviseConnection object.

Comments

Unadvise is called implicitly on the left object before the assignment is done. The assignment will duplicate the active connection of the right object, which results in two active connections. If the right object is not connected, then only the m_guid data member is copied to the left object.


Advise with Notification

HRESULT Advise(
IUnknown pUnkSrc,
IUnknown pUnkNotify,
BOOL fAddRefNotify /* = TRUE */ )

Parameters

pUnkNotifier

Object from which notifications are originating. This object must implement IConnectionPointContainer.

pUnkNotify

Object to receive notifications. This object must implement the notification interface identified by the riid argument of the constructor.

fAddRefNotify

TRUE if pUnkNotify should have its reference count incremented during the advise connection (notification).

Comments

Advise can be called successively without calling Unadvise. Any previous advise connection will be implicitly disconnected on the behalf of the caller.


Unadvise Any Advise Connection

HRESULT CAdviseConnection::Unadvise(void)

Comments

Call to ensure that any advise connection is disconnected and interfaces are appropriately released. This method does nothing if there is no active Advise connection.


SetNotifyInterface

HRESULT CAdviseConnection::SetNotifyInterface(
REFIID riid)

Parameters

riid

Reference to the IID of the desired connection point interface.

Return Code

E_FAIL
Returned if the advise connection is currently active.

S_OK
Method succeeded. No attempt is made to validate the new GUID.

Comments

This method is called to set a new notification interface, which will be used to establish an advise connection on the next call to Advise. If the default constructor is used to create the object, then this method must be called to set the notification interface prior to calling Advise.


GetNotifyInterface

REFIID CAdviseConnection::GetNotifyInterface(void)

Comments

This method returns the reference to the GUID of the current notification interface. The GUID returned is the same one that’s passed in during the class construction or the last one set by SetNotifyInterface. A reference to GUID_NULL is returned if the default constructor was used to create the object and SetNotifyInterface was not called.

BOOL CAdviseConnection::IsAdvising(void)

Comments

This method returns TRUE if there is an active advise connection.


Class CConnectionsArray

Use this class to manage multiple advise connections. All methods of CArray are also supported. Each connection is identified by its notification interface GUID. This array supports only one connection per GUID.


CConnectionsArray

CConnectionsArray()

Comments

Default constructor.


Add Advise Connection

HRESULT CConnectionsArray::Add(
REFIID riid,
IUnknown *pUnkNotifier,
IUnknown pUnkNotify,
BOOL fAddRefNotify = TRUE )

Parameters

riid

IID of the notification interface.

*pUnkNotifier

Pointer to IUknown of the notifier object.

*pUnkNotify

Pointer to IUknown of the object to be notified.

fAddRefNotify

TRUE to add a reference count to pUnkNotify during an active advise connection.

Return Code

See AdviseConnect.

Comments

Adds an advise connection. The object referenced by pUnkNotify must implement the notification interface identified by riid. fAddRefNotify should be set to FALSE when it is not desirable to increment pUnkNotify’s reference count. riid is unique in the array, in that only one entry will be created for multiple calls with the same riid. If an entry already exists, then the current advise connection is disconnected, if any; and a new advise connection is made between the given pUnkNotifier and pUnkNotify.


Remove

HRESULT CConnectionsArray::Remove(
REFIID riid)

Parameters

riid

IID of the notification interface to be removed from the array.

Return Code

See AdviseDisconnect. If riid is not found, then S_OK is returned.

Comments

Removes the advise connection entry whose notification interface IID matches the given riid. Any active advise connection is disconnected before the entry is physically removed from the array.


Find

int Find(
REFIID riid)

Parameters

riid

GUID of the notification interface to search for.

Comments

Searches the array for the existence of a connection entry whose notification interface GUID matches the given riid. A zero-based index is returned if a match is found. Otherwise, –1 is returned.


Class CPropNotifySink

This class can be used to manage IPropertyNotifySink advise connections.


CPropNotifySink

CPropNotifySink(
IPropertyNotifySink *pPropNotify,
DISPID dispidPropNotifier,
BOOL fAddRefNotify = TRUE,
BOOL fAddRefNotifier = TRUE);

Parameters

*pPropNotify

The caller must supply an implementation of IPropertyNotifySink, which is pointed to by this parameter.

dispidPropNotifier

The DISPID used to identify the notifying object.

fAddRefNotify

TRUE to keep a reference count on pPropNotify during an advise connection.

fAddRefNotifier

TRUE to keep a reference count on the notifier object while connection is active.

Comments

CPropNotifySink is a COM object, and therefore provides implementation for the following IUnknown methods:

  • STDMETHODIMP CPropNotifySink::QueryInterface(REFIID riid, LPVOID FAR* ppvObj)

  • STDMETHODIMP_(ULONG) CPropNotifySink::AddRef(void)

  • STDMETHODIMP_(ULONG) CPropNotifySink::Release(void)

CPropNotifySink also implements the IPropertyNotifySink interface, which has the following methods:

  • STDMETHODIMP CPropNotifySink::OnChanged(DISPID dispid)

  • STDMETHODIMP CPropNotifySink::OnRequestEdit(DISPID dispid)

The above methods process notifications from the notifier and pass the notifications to the IPropertyNotifySink interface, which is passed in through the pPropNotify parameter of the constructor.


IsAdvising

BOOL IsAdvising(void)

Comments

Returns TRUE if the advise connection is active.


GetPropID

DISPID GetPropID(void)

Comments

Returns the DISPID of the notifier’s property that caused the notification. This is typically called by the implementation of OnChanged and OnRequestEdit of the caller’s IPropertyNotifySink interface.


Advise Connection

HRESULT CPropNotifySink::Advise(
IUnknown *pUnkNotifier)

Parameters

*pUnkNotifier

Source interface with which to establish the advise connection. This object must implement IConnectionPointContainer.

Return Code

S_OK or error code.

Comments

Called to establish an advise connection to the given object. If fAddRefSource is TRUE (see constructor), then a reference count is kept on the interface during the connection.

The notifier object is identified by the dispidPropNotifier value passed to the constructor. This DISPID is passed to the OnChanged and OnRequestEdit methods of the caller’s implementation of IPropertyNotifySink, which is passed to the constructor by the pPropNotify parameter.

This method can be called successively. It will call Unadvise to properly disconnect any previous connection.


Unadvise Most Recent Advise Connection

virtual HRESULT CPropNotifySink::Unadvise(void)

Comments

Called to disconnect from the most recent advisory connection.


Helpers

This section documents helpers for the wrapper class. See CAdvise::Connection.


AdviseConnect

HRESULT WINAPI AdviseConnect(
LPUNKNOWN pUnkNotifier,
 REFIID riid,
LPUNKNOWN pUnkNotify,
DWORD* pdwCookie,
BOOL fAddRef = TRUE);

Parameters

pUnkNotifier

Object from which notifications are originating. This object must implement IConnectionPointContainer.

riid

GUID of the notification interface to be used for the connection.

pUnkNotify

Pointer to IUnknown of the object to receive notifications. This object must implement the notification interface identified by the riid parameter.

pdwCookie

Pointer to a DWORD buffer to store proprietary data about the connection. This cookie is passed to AdviseDisconnect when the caller is done with the connection.

fAddRef

TRUE to keep a reference count on the pUnkNotify object during the notification.

Return Code

S_OK

Method succeeded.

E_NOINTERFACE

E_OUTOFMEMORY

Comments

Call to initiate an advise connection using the IConnectionPointContainer and IConnectionPoint interfaces.

Set fAddRef to FALSE to prevent the notifier object from keeping a reference count on pUnkNotify during a connection. If fAddRef is FALSE during a call to AdviseConnect, then it must be FALSE during the call to AdviseDisconnect to prevent reference-counting problems.


AdviseDisconnect

HRESULT WINAPI AdviseDisconnect(
LPUNKNOWN pUnkNotifier,
REFIID riid,
LPUNKNOWN pUnkNotify,
DWORD dwCookie,
BOOL fAddRef )

Parameters

pUnkNotifier

Object from which notifications are originating. This object must implement IConnectionPointContainer.

riid

UUID of the notification interface to be used for the connection.

pUnkNotify

Pointer to IUnknown of the object to receive notifications. This object must implement the notification interface identified by the riid parameter. This parameter is ignored if fAddRef is FALSE.

pdwCookie

DWORD cookie from a previous call to AdviseConnect.

fAddRef

TRUE if a reference count was kept on pUnkNotify during the connection. If TRUE, then pUnkNotify must be a valid pointer to the original IUnknown used in a prior call to AdviseConnection. Otherwise, it is ignored.

Return Code

S_OK

Method succeeded.

E_NOINTERFACE

E_OUTOFMEMORY

Comments

Called to disconnect an advisory connection. dwCookie must be the value returned from the prior call to AdviseConnect, and pUnkNotifier, riid, pUnkNotify, and fAddRef must be the same as the prior call to AdviseConnect. If fAddRef is FALSE, then pUnkNotify is not used.


Microsoft OLE DB for OLAP Overview

Microsoft® OLE DB for OLAP (Online Analytical Processing) is a set of objects and interfaces that extends OLE DB to provide access to multidimensional data sources.

OLE DB for OLAP (Online Analytical Processing) is a set of COM (Component Object Model) objects and interfaces designed to facilitate communication between providers and consumers of multidimensional data. OLE DB for OLAP is an extension of OLE DB version 2.0 to provide access to multidimensional data sources.

For information on support for Microsoft data access components, see the Getting Help from Microsoft Technical Support page

© 1998 Microsoft Corporation. All rights reserved.

OLE DB for OLAP Documentation

The OLE DB for OLAP Programmer's Reference contains the documentation for OLE DB for OLAP, including general concepts, objects and schema rowsets, interface reference, multidimensional expressions (MDX), and code samples.

For the most recent updates to OLE DB for OLAP documentation, see the Web page for OLE DB Release Notes at http://www.microsoft.com/data/oledb/olap/.

Microsoft PivotTable Service

The Microsoft® PivotTable® Service is an OLE DB for OLAP provider. It is an in-process desktop OLAP server designed to provide online and offline data analysis functionality, and online access to OLAP data as a client of the Microsoft Decision Support Services OLAP Server (which is not included with Microsoft OLE DB for OLAP).

Microsoft PivotTable Service contains data analysis, cube building, and smart cache management functionality. It provides interfaces to data as well as functionality to manipulate data.

OLE DB Samples

For the documentation of the OLE DB for OLAP sample applications, see "OLE DB for OLAP Sample Applications" in the Data Access SDK Technical Articles.


OLE DB for OLAP Programmer's Reference

OLE DB for OLAP Programmer’s Reference


About This Manual

Information in this document is subject to change without notice. Companies, names, and data used in examples herein are fictitious unless otherwise noted. No part of this document may be reproduced or transmitted in any form or by any means, electronic or mechanical, for any purpose, without the express written permission of Microsoft Corporation.

© 1998 Microsoft Corporation. All rights reserved.


Audience

This manual assumes:

  • Working knowledge of the C or C++ programming language.

  • Knowledge of COM.

  • Knowledge of OLE DB.

  • General knowledge of multidimensional and relational database concepts and query languages.


Document Conventions (OLE DB for OLAP)

Document Conventions

This manual uses the following typographic conventions.

Format | Used for
WIN.INI | Uppercase letters indicate file names, MDX statements, macro names, and terms used at the operating-system command level.
#include <io.h>
 Sans serif font is used for sample command lines and program code, or BNF constructs.
Argument | Italicized words indicate information that the user or the application must provide, or word emphasis.
IMDFind | Bold type indicates that syntax must be typed exactly as shown, including method names.

In addition, the following words and phrases have specific meanings in the OLE DB for OLAP specification:

  • Unless otherwise specified, the word cube refers to the metadata object in a multidimensional schema, not the geometrical object.

  • The phrase tabular data provider or TDP refers to any data provider that exposes its data in the form of an OLE DB rowset. The phrase multidimensional data provider or MDP refers to any data provider that exposes its data as a multidimensional hypercube. Any TDP that exposes OLE DB for OLAP is, by definition, an MDP as well as a TDP.


Chapter 1

OLE DB for OLAP Overview

Microsoft® OLE DB for OLAP (Online Analytical Processing) is a set of objects and interfaces that extends OLE DB to provide access to multidimensional data sources. This chapter provides a basic understanding of the design goals of OLE DB for OLAP for this version and future directions for the specification.


Design Goals in This Version

OLE DB for OLAP is a set of Component Object Model (COM) objects and interfaces designed to facilitate communication between providers and consumers of multidimensional data. OLE DB for OLAP is an integral part of OLE DB version 2.0, and has many of the same design goals.

The following are specific features of OLE DB for OLAP:

  • OLE DB for OLAP interfaces enable representation, expression, transportation, and efficient navigation of multidimensional data within COM.

  • OLE DB for OLAP is optimized, to the extent that such optimizations do not represent tradeoffs, for all of the following configurations:
    • A single process space in which the consumer (or service provider) and the multidimensional data provider (MDP) run in the same process. An example of this configuration is an OLE DB for OLAP provider and the query engine of an RDBMS running in the same process.

    • A consumer and an MDP running on different machines or in separate processes. An example of this configuration is a pivot table control working with an MDP.

    • Service providers exposing OLE DB for OLAP interfaces and consuming other OLE DB interfaces.
  • OLE DB for OLAP leverages the OLE DB architecture. OLE DB for OLAP interfaces can interconnect so that the same interfaces are applicable at various levels of data providers and service providers.

  • OLE DB for OLAP reuses as much of OLE DB as possible. The number of OLE DB for OLAP–specific objects and interfaces are minimized.

  • OLE DB for OLAP is designed for interoperable access. That is, OLE DB for OLAP interfaces cover common consumer requirements for processing multidimensional data, but these interfaces are factored in a way that enables existing MDPs to expose OLE DB for OLAP without implementing changes at the server level.

  • OLE DB for OLAP is designed to enforce symmetry between dimensions and measures. In other words, the interface exposes measures as a dimension with functionality equivalent to all other dimensions.


Future Directions

OLE DB for OLAP version 1.0 does not define any new interfaces or language clauses for updating cube schemas. However, providers can achieve the same effect by supporting existing mechanisms for updating rowsets in order to provide updatable schema rowsets. Vendors may also define proprietary language extensions for Data Definition Language (DDL) functionality.

Note DDL functionality is not a goal for OLE DB for OLAP version 1.0. It is a goal for the next major version and should be considered part of the future direction of the OLE DB for OLAP specification.


Chapter 2

OLE DB for OLAP Concepts

This chapter provides a broad overview of OLE DB for OLAP concepts and includes the following topics:

  • Enumerating and initializing OLE DB for OLAP data source objects

  • Understanding and using multidimensional schemas

  • MDX and datasets

  • Delimited identifiers

  • Datasets

  • Flattening a dataset to produce a rowset

  • Range rowsets

  • Updating cell data


Introduction to OLE DB for OLAP

Introduction

OLE DB for OLAP is a set of objects and interfaces for developing and accessing multidimensional data providers (MDPs). MDPs present data in multidimensional views, and tabular data providers (TDPs) present data in tabular views. It is possible for a single data source object to support both tabular and multidimensional presentations.


Enumerating and Initializing OLE DB for OLAP Data Source Objects

OLE DB enumerators are used to locate MDPs and create OLE DB data source objects, which are then used to create session objects, and so on. In this respect, initializing an MDP is no different from initializing any other OLE DB provider.

However, the consumer needs to easily determine the type of a given data source. For example, a data source “locator” may implement option buttons, which allow the user to browse for TDPs, MDPs, or both TDPs and MDPs. It is useful for the provider to expose data source types at enumeration time.

The following is a new value added to the SOURCES_TYPE column in the SOURCES rowset for OLE DB:

#define DBSOURCETYPE_DATASOURCE_MDP 3

Furthermore, the DBSOURCETYPE_DATASOURCE value is renamed as follows:

#define DBSOURCETYPE_DATASOURCE_TDP DBSOURCETYPE_DATASOURCE

The consumer uses these values to determine whether it is interacting with a TDP or an MDP.


Registry Entries

Like tabular data providers, multidimensional data providers are identified in the registry by the OLE DB provider subkey under the provider’s class ID. It is possible to create a provider that supports both tabular and multidimensional data. Therefore, in addition to the other required subkeys and values described in the OLE DB Programmer's Reference, providers must have one or both of the following values under the HKEY_CLASSES_ROOT\CLSID subkey:

ProviderCLSID\OLE DB Provider = Description

ProviderCLSID\OLE DB MD Provider = Description


Understanding and Using Multidimensional Schemas

OLE DB for OLAP provides interfaces and methods for providers to expose schemas and consumers to view schemas. OLE DB for OLAP does not mandate that MDPs expose the schema objects defined in this specification; rather, it simply provides a way for schema objects to be exposed if the MDP chooses to support them.

OLE DB for OLAP provides interfaces that enable users to browse a set of metadata objects. Most providers expose a subset of all OLE DB for OLAP metadata objects.

The central metadata object recognized by OLE DB for OLAP is the cube. This is a set of related dimensions, which defines an n-dimensional manifold. The data points of interest are the points in this manifold and each such point is uniquely identified by a set of coordinates. Each coordinate is a member of one component dimension. For example, SalesData is a cube with five dimensions: Salesperson, Geography, Time, Measures, and Products.

Note OLE DB for OLAP handles the Measures dimension in the same way that it handles all other dimensions, so there is complete symmetry between dimensions and measures.

A schema is composed of a set of cubes. As in the relational case, the main purpose of a schema is to impose high-level authorization policies. A set of schemas is grouped together into a catalog. Just because there are catalogs and schemas, a provider doesn’t have to support this two-level grouping system. It may support catalogs only, schemas only, both, or neither.

A cube has a set of dimensions. Each dimension consists of a set of members. The members of a dimension can be consolidated or aggregated along a hierarchy. Some dimensions can have more than one hierarchy. This means that members can aggregate, or “roll up,” in multiple ways. Each hierarchy has levels, and each level is made up of a set of members.

For example, consider the SalesData cube mentioned previously. The Geography dimension has the following set of members (consider just two continents for simplicity):

{All, Europe, North America, Canada, Germany, UK, USA, Canada-East, Canada-West, England, Germany-North, Germany-South, Ireland, Scotland, USA-NE, USA-SE, USA-NW, USA-SW, Wales, Belfast, Berlin, Boise, Boston, Calgary, Cardiff, Cookstown, Dover, Edinburgh, Glasgow, Hamburg, Houston, LA, London, Miami, Munich, New York, Ottawa, Pembroke, Seattle, Shreveport, Stuttgart, Toronto, Vancouver}

There exists a natural hierarchy in this dimension, as below:

The World

Continents

Countries

Regions

Cities

Each of these categories is a level. Each level has a list of members associated with it as follows:

  • The World = {All}

  • Continents = {Europe, North America}

  • Countries = {Canada, Germany, UK, USA}

  • Regions = {Canada-East, Canada-West, England, Germany-North, Germany-South, Ireland, Scotland, USA-NE, USA-SE, USA-NW, USA-SW, Wales}

  • Cities = {Belfast, Berlin, Boise, Boston, Calgary, Cardiff, Cookstown, Dover, Edinburgh, Glasgow, Hamburg, Houston, LA, London, Miami, Munich, New York, Ottawa, Pembroke, Seattle, Shreveport, Stuttgart, Toronto, Vancouver}

Members at the leaf level have no children, and member(s) at the root level have no parents. All other members have at least one parent and at least one child. For example, a partial traversal of the hierarchy tree in the Geography dimension yields the following parent-child relationships:

{All} (parent of) {Europe, North America}

{North America} (parent of) {Canada ,USA}

{USA} (parent of) {USA-NE, USA-NW, USA-SE, USA-SW}

{USA-NW} (parent of) {Boise, Seattle}

To see the need for multiple simultaneous hierarchies, consider the Time dimension of the SalesData cube. There are two ways to roll up to the Year level from the Days level, as follows:

Year

Quarter

Month

Day of Month

or

Year

Week

Day of the Week

This example also illustrates another characteristic: Some members of the Week level of the Year-Week hierarchy do not appear in any level of the Year-Quarter hierarchy. Thus, a hierarchy need not include all members of a dimension. However, if there is just a single hierarchy in a dimension, it is common for all members of the dimension to be included in the hierarchy.

The final concept is properties. In a Salesperson dimension, there is usually a need to store additional data about each salesperson — data such as “Address” or “Phone Number.” This additional information is called a property. Each level of a hierarchy tends to have a different set of properties, but all members in a given level have the same set of properties. For example, consider the Geography dimension. Each city can have the properties NUMBER_OF_RETAIL_OUTLETS, WAREHOUSE_NAME, and SALESREP, but these properties may not apply at the Region level. Instead, a region might have properties such as REGIONAL_MANAGER and IMPORT_POINT.


Browsing Metadata

Multidimensional schema rowsets can be browsed by the IDBSchemaRowset interface. Consumers can get information about the available cubes, dimensions, hierarchies, levels, members, and member properties. Because IDBSchemaRowset is an interface on the session object, the consumer needs to have a session object before it can browse for metadata.

OLE DB for OLAP defines schema rowsets for the following metadata objects:

  • Catalogs

  • Schemas

  • Cubes

  • Measures

  • Dimensions

  • Hierarchies

  • Levels

  • Properties (for each dimension level)

  • Members

Note OLE DB for OLAP rowsets for Catalog and schema are the same as for OLE DB.

Each schema rowset is identified by a GUID. IDBSchemaRowset::GetRowset takes this GUID as an argument and returns the requested schema rowset.

A schema rowset for a given metadata object has one row for every instance of that object present in the schema. For example, if there are five cubes available in the schema, then the CUBES rowset has five rows. Each column of a schema rowset gives one piece of information about that object. For example, the CUBES rowset has the following 11 columns, each of which gives a different piece of information about the cube:

Column name | Information conveyed about the cube
CATALOG_NAME | Name of the catalog to which this cube belongs.
SCHEMA_NAME | Name of the schema to which this cube belongs.
CUBE_NAME | Name of the cube.
CUBE_TYPE | Indicates either a regular cube or a “virtual” cube.
CUBE_GUID | Cube GUID.
CREATED_ON | Date and time of cube creation.
LAST_SCHEMA_UPDATE | Date and time of last schema update.
SCHEMA_UPDATED_BY | User ID of the person doing the last schema update.
LAST_DATA_UPDATE | Date and time of last data update.
DATA_UPDATED_BY | User ID of the person doing the last data update.
DESCRIPTION | A meaningful description of the cube.

Often it is not desirable to retrieve information about all instances of a metadata object. For example, an application typically needs the dimensions belonging to a single cube, rather than all the dimensions available in the schema. Thusm there must be a way to restrict a schema rowset based on certain conditions.

OLE DB defines a generic method to accomplish this, called restrictions. Each schema rowset has a defined set of restrictions. The restrictions are typically conditions on one of the columns of a schema rowset. For example, the DIMENSIONS rowset can be restricted by the CATALOG_NAME, SCHEMA_NAME, CUBE_NAME, DIMENSION_NAME, and DIMENSION_UNIQUE_NAME properties. One of the arguments to IDBSchemaRowset::GetRowset is an array of restrictions. This array has as many elements as the number of restrictions on the particular schema object. For example, the number of restrictions on the DIMENSIONS schema rowset is five. To restrict the DIMENSIONS rowset to only contain dimensions belonging to a particular cube, the application puts the cube name into element number three of the restrictions array.

For more information on restrictions in OLE DB, see the OLE DB Programmer’s Reference. For more information on restrictions in OLE DB for OLAP schema rowsets, see “Session Object” in Chapter 4 of this document.


Virtual Cubes

The CUBES rowset has a column called CUBE_TYPE for which there are two possible values: CUBE and VIRTUAL CUBE.

A CUBE value denotes an ordinary cube composed of dimensions and data points. A VIRTUAL CUBE value is obtained through a query on one or more cubes or virtual cubes whose result resembles a cube. In other words, a cube is to a virtual cube as a table is to a view.


Hypercubes and Multicubes

Multidimensional databases can present their data to an application using two types of cubes: hypercubes and multicubes. In the hypercube model, all data appears logically as a single cube. All parts of the manifold represented by this hypercube have identical dimensionality. In the multicube model, data is segmented into a set of smaller cubes, each of which is composed of a subset of the available dimensions.

Hypercubes and multicubes differ in terms of available metadata. In a hypercube, each dimension belongs to one cube only. A dimension is “owned” by the hypercube. In a multicube, a dimension can be part of multiple cubes. That is, dimensions are not “owned” by any one cube; rather, they are available to all cubes. In fact, there can be dimensions that do not belong to any cube.

In the OLE DB for OLAP metadata model, it is possible to browse both hypercube and multicube schemas. The CUBES rowset lists all the available cubes, regardless of whether there is a single hypercube or many multicubes. The DIMENSIONS rowset has one row for each dimension/cube combination. If there are dimensions that are not part of any cube, then the CUBE_NAME field of the DIMENSIONS rowset is NULL.

Multiple Hypercubes vs. True Multicubes

In a true multicube system, dimensions “exist” in the catalog or the schema. Different subcubes use a subset of these dimensions. By contrast, in a “multiple hypercube” system, the catalog or the schema has multiple cubes, each with its own set of dimensions.

The important distinction between these two systems for OLE DB for OLAP schema rowsets is this: In the true multicube system, if there are two rows in the DIMENSIONS rowset for which the DIMENSION_NAME value is the same (and the CUBE_NAME value is different), then these two rows represent the same dimension. This is because the subcubes are built from the same pool of available dimensions. However, in a multiple hypercube scenario, it is possible for two hypercubes to have a dimension of the same name, each of which has different characteristics. In this case, the DIMENSION_UNIQUE_NAME value is guaranteed to be different. Consumers checking dimension names between cubes for doing natural joins must make sure to check DIMENSION_UNIQUE_NAME values, not DIMENSION_NAME values.


Browsing Members

To browse members, use IDBSchemaRowset::GetRowset to ask for the MEMBERS rowset. This rowset straddles the line between data and metadata. There can be a lot of members in a dimension, so this rowset is too big to be considered metadata. On the other hand, the list of members is reasonably static, a characteristic usually associated with metadata.

Note Due to the potentially large number of members on a dimension, materializing a MEMBERS rowset without any restrictions (or restricted only up to the dimension name) may take a while to be returned. Therefore, consumers should be judicious in asking for a MEMBERS rowset without any restrictions.

There is another distinctive characteristic of the MEMBERS rowset: Its structure varies depending on the parent level. This schema rowset contains the list of member names as well as property values for each member, with each property being a column. Because the list of properties varies based on the level that the member belongs to, the structure of the rowset also varies similarly. All of this assumes that the provider supports named levels. For a description of what happens when the provider does not support named levels, see “Named and Unnamed Levels.”

Using restrictions, the consumer can query for the children, parent, ancestors, descendants, and siblings of a given member. For a complete description of restrictions on the MEMBERS rowset, see “MEMBERS Rowset” in Chapter 4.


Named and Unnamed Levels

A data source supports named levels if it can refer to all members at a particular level of a hierarchy by a name. In the SalesData cube, Continents is the name of the member set at level 1 (root = 0) of the hierarchy. This set has the members {Europe, North America}.

Not all multidimensional data sources support named levels. Many sources support the concept of a level as a set of members that occur at a given distance from the root of the hierarchy. In such a data source, users cannot give the name “Continents” to a set of members. Instead, they need to decide which members occur at a distance of one from the root of the World-Continent hierarchy.

OLE DB for OLAP does not require a data source to support named levels. The LEVELS and MEMBERS rowsets contain information about both the level name and the level position (that is, the distance of the level from the root). OLE DB for OLAP does require that, if the provider does not support named levels, the provider must make up a dummy level name containing all members of a hierarchy (or a dimension). The dummy name must be the name of the dimension.

As mentioned in “Browsing Members,” the MEMBERS rowset contains additional columns at the end, one column for each property. When asking for the MEMBERS rowset, OLE DB for OLAP requires that the restrictions together unambiguously identify a level. When the provider does not support named levels, all members belong to a dummy level. Consequently, a restriction based on the LEVEL_NAME column has no effect. The consumer can specify additional restrictions on LEVEL_NUMBER, MEMBER_NAME, PARENT_UNIQUE_NAME, and so on. The resulting rowset will have property columns for the union of the properties of each member in the rowset. Each row in the rowset (which describes a member) will have values for property columns that apply to that member. Values for all other property columns are NULL.


Balanced and Ragged Hierarchies

In general, hierarchies can be either balanced or ragged.

In a balanced hierarchy, the parent of every member comes from the level immediately above the member. A family tree is an example of a balanced hierarchy. Most hierarchies on the time dimension are also balanced.

Ragged hierarchy is the more common type. In a ragged hierarchy, the parent of a member can come from any level above the level of the member, not just from the level immediately above.

Many hierarchies in a multidimensional schema are not balanced. For example, the hierarchy in a Geography dimension is usually not balanced — at least, it is not balanced conceptually. This is because some countries are divided into states, and states have counties and cities. However, other countries have regions and cities, but no states.

OLE DB for OLAP supports both balanced and ragged hierarchies. The MEMBERS schema rowset has not only a parent name, but also the level number of the parent. Similarly, the restrictions on this rowset enable you to browse not only by moving up and down the levels, but also by moving up and down the inheritance tree.


Programming Considerations

Many clients need to get schema information several times during the course of a session with the server. It is strongly recommended that clients cache the schema information from all schema rowsets. However, do not cache the MEMBERS rowset. All other schema rowsets are typically orders of magnitude smaller than the MEMBERS rowset.) Although caching of schema rowset information results in a slightly larger working set for the application, the gain in performance far outweighs the increase in working set.

Applications should not attempt to cache the MEMBERS schema rowset. Typically, this rowset has hundreds of thousands of rows. There is seldom any need for all the members. It is more common to need a very restricted subset of the members under certain conditions — specifically, when rendering an axis of a dataset. However, the axis rowsets are designed to contain most of the information that an application might need; that is, most of the columns in an axis rowset are the same as the columns in the MEMBERS rowset. If the application needs additional information — for example, additional columns from the MEMBERS rowset are required for those members on the axis — the application can use DIMENSION PROPERTIES clause in the MDX statement that created the dataset. This puts these additional columns on the axis rowset.

For more information on axis rowsets, see “Axis Rowsets” in this chapter. For more information on the DIMENSION PROPERTIES clause, see “Retrieving Member Properties” in this chapter.


Multidimensional Expressions (MDX) and Datasets

After establishing a connection and getting an interface on the session object, the consumer has to specify a query. Execution of a query results in a dataset. All data in a multidimensional data source exists as value points in one or more n-dimensional cubes. The result of a user query encompasses one or more cubes, and extracts a set of points from these cubes as another p-dimensional cube. The value of p is usually no more than 4 and is typically 2 or 3. The resulting p-dimensional cube is called a dataset. This is similar to a query in a relational database system where the query encompasses one or more tables and returns a result set that has the same structure as that of a table.

The dataset itself looks like a cube, but the “dimensions” of the dataset are called axes. Each axis consists of one or more dimensions projected on it. For a more complete description of the dataset, see “Datasets.”

OLE DB for OLAP defines a rich syntax for specifying a dataset, called Multidimensional Expressions (MDX). This can be viewed as a set of macros defined on top of the SQL language. Although these macros are generally useful for querying an OLAP data source, their precise semantics are defined by means of their SQL language mappings.

The following sections discuss concepts of MDX and provide examples of its use.


Implementation Strategies for Providers

Providers have various options for implementing MDX. Which option to implement depends on what is already available from the data store.

Providers with Tabular Storage

If the provider stores OLAP data as a set of tables (for example, a central fact table with associated dimension tables), then it is likely that SQL is the preferred data access language to extract data from the store. Such providers should implement a preprocessor that maps a statement in MDX to an equivalent SQL query. For more information on how this mapping can be done, see Chapter 6, “Mapping MDX to SQL Statements.”

This approach is suited ideally for relational OLAP (ROLAP) servers. Rather than implementing support for a new language, they can leverage their existing SQL language implementation.

Note Relational OLAP, or ROLAP, is a term used to describe OLAP systems that are based on a relational DBMS. A ROLAP server is usually a full-fledged relational DBMS that stores OLAP data as a set of tables. This is in contrast to multidimensional OLAP, or MOLAP, which refers to OLAP systems that store data in inherently multidimensional structures.

Providers with Nontabular Storage

Providers with nontabular storage are usually multidimensional OLAP (MOLAP) systems. MOLAP servers typically have storage structures that are inherently multidimensional. MDX statements provide a direct expression of the query capabilities of these servers. If a MOLAP server has a query language of its own, MDX can be mapped on top of this language. This is usually possible because MDX represents the fundamental query operations that any OLAP client requires.


Parts of an MDX Statement

To specify a dataset, the MDX statement must contain information about:

  • The cube or cubes that set the scope of the query.

  • The number of axes.

  • The dimensions projected on each axis and level of nesting at which each dimension appears on the axis.

  • The members or member tuples from each dimension to include, and the sort order.

  • The members from a nonprojected dimension on which the data will be filtered for members from projected dimensions.

A typical MDX statement takes the following form:

SELECT <axis_specification> [, <axis_specification>...]
FROM <cube_specification>
WHERE <slicer_specification>

The following sections explain each part of this statement.

Note The full syntax of the expression that defines a dataset contains significantly more features than the above example. This example conveys the broad outlines of the query process. For more information, see Chapter 3, “Advanced MDX.” For a complete syntax, see Appendix B, “MDX Grammar.”


Axis and Slicer Dimensions

When formulating a query, an application typically looks at the cubes and divides the set of dimensions into two subsets as follows:

  • Axis dimensions for which data is retrieved for multiple members.

  • Slicer dimensions for which data is retrieved for a single member.

For example, suppose there are four dimensions: Geography, Products, Measures, and Quarters. There are two measures: Sales and Cost. The application wants to see Sales for USA for each product category and each quarter. The output looks like the following:

In this example, the Quarters and Products dimensions are the axis dimensions. Measures and Geography are the slicer dimensions. From a geometrical standpoint, begin with a four-dimensional hypercube. Each dimension is an edge of this hypercube. Then, move along the Measures edge and select the point “Sales.” This selects a three-dimensional cube associated with this coordinate. In this cube, move along the Geography edge and select the point “USA.” This selects a two-dimensional slice in the cube. Then display the appropriate subslice of the two-dimensional slice.

Essentially, slicer dimensions serve to extract a p-dimensional shadow of the n-dimensional cube (p <= n). The axis dimensions slice this shadow cube along one or more axes and display the result in tabular form.


Cube and Dataset Example

In this example — which will be repeatedly referenced in the rest of this document — SalesCube has the following six dimensions:

  • SalesPerson

  • Geography, a hierarchy that has the levels Continents, Countries, States, and so on

  • Quarters, a hierarchy that has the levels Quarters, Months, Days

  • Years

  • Measures, of which the three measures are Sales, PercentChange, and BudgetedSales

  • Products

Consider a dataset on this cube that looks like this:

The characteristics of this dataset are:

  • Axis dimensions: Quarters, SalesPerson, Geography

  • Slicer dimensions: Measures, Years, Products

  • Two axes: COLUMN, x-axis, or axis 0; and ROW, y-axis, or axis 1

  • x-axis: Salesperson and Geography dimensions (the latter is nested in the former)

  • y-axis: Quarters dimension


Establishing Cube Context

In the expression template shown in the section “Parts of an MDX Statement,” the FROM cube_specification clause specifies the cubes to query. The cube specification is just the name of the cube. If multiple cubes are involved, then it is the comma-separated list of those cube names.

Note The ability to query across more than one cube is not part of core level MDX language. In other words, a provider need not support cross-cube queries in order to be compliant with OLE DB for OLAP. Applications can check the property MDPROP_JOINCUBES in the DBPROPSET_DATASOURCEINFO property group to find out if a provider supports this functionality.

For SalesCube, the FROM clause looks like this:

FROM SalesCube


Specifying the Contents of an Axis

The number of axes and the contents of each axis are specified after the SELECT keyword and immediately before the FROM clause in the expression template shown in “Parts of an MDX Statement.”

Each <axis_specification> value specifies one axis. The number of axes in the dataset is equal to the number of <axis_specification> values in the MDX statement.

To understand what constitutes an <axis_specification> value, we must take a look at the nature of an axis.

Contents of the x-Axis

In the dataset of the section “Cube and Dataset Example,” the x-axis of the dataset has two nested dimensions: Geography and SalesPerson. From the Geography dimension, four members are selected: Seattle, Boston, USA_South, Japan. Two members are selected from the SalesPerson dimension: Venkatrao and Netz. This yields a total of eight coordinates on this axis (8 = 4*2).

You can represent each coordinate as a tuple with two members — one from the SalesPerson dimension and another from the Geography dimension — as follows:

(Venkatrao, Seattle), (Venkatrao, Boston), (Venkatrao, USA_South),
(Venkatrao, Japan), (Netz, Seattle), (Netz, Boston), (Netz, USA_South),
(Netz, Japan)

You can consider an axis as being composed of a set of tuples. If there is only one dimension on an axis, then each tuple is just a member. If there are two dimensions, then two components compose each tuple with each component being a member of one of the dimensions. In general, if there are n dimensions nested on an axis, then n components compose each tuple where each component is a member of one of the nested dimensions.

Contents of the y-Axis

The y-axis of the dataset has the following set of tuples. (In this case, each tuple has just one component, making it the same as a member because there is only one dimension on this axis.)

Jan, Feb, Mar, Qtr2, Qtr3, Oct, Nov, Dec

Set, Tuple, and Member Specification

An axis contains a set of tuples. A tuple is a vector of members. To specify the coordinates of an axis:

  1. Create an expression that generates members. Some expressions result in a single member while others result in a set of members.

  2. Obtain the desired set of members for each dimension on the axis.

  3. Take a cross product of these member sets to yield a set of tuples that represents an axis.

An application can use the simple expression syntax of OLE DB for OLAP to perform these operations. For the complete syntax of these expressions, see Appendix B, “MDX Grammar.” The following is a brief look at some of the important concepts.

There are many ways of choosing a set of members:

  • Enumeration of members. The easiest way to specify a set of members is simply to list the desired members:
    {Venkatrao, Netz}
  • Set expressions. A set function generates a new set of members. For example, the function
    USA.CHILDREN

    generates the set

    {USA_North, USA_South}

    A comma-separated list of set expressions also results in a set. For example, the expression

    {USA_North.CHILDREN, USA_South, Japan}

    results in the set

    {Seattle, Boston, USA_South, Japan}

Other ways of set specification make use of the natural order of members in a dimension. For example, the expression

Netz:Pizzo

on the SalesPerson dimension, whose members are naturally ordered by last name, yields the set

{Netz, Nakonieczny, Oks, Pace, Patel, Patterson, Pederson, Perin, Pizzo}

Note The expression is an inclusive range. That is, the members on both sides of the colon ( : ) specifier are included.

After an application generates sets that contain the members that it wants from each dimension on a given axis, it can use the CROSSJOIN function to get a cross product of these sets.

Putting all these concepts together, a <set_expression> value for the x-axis in the example dataset is

CROSSJOIN({Venkatrao, Netz},
{USA_North.CHILDREN, USA_South, Japan})

The expression for the y-axis in the example dataset is

{Qtr1.CHILDREN, Qtr2, Qtr3, Qtr4.CHILDREN}

Note The MDX syntax is much more extensive than what is shown in these few examples. Some functionality not covered here includes:

  • Set operations, such as UNION or INTERSECT.

  • Functions to order sets, such as ORDER or HIERARCHIZE.

  • Filter functions, such as TOPCOUNT, TOPPERCENT, BOTTOMCOUNT, BOTTOMPERCENT, or FILTER.

  • Tree navigation functions, such as DESCENDANTS.

    Chapter 3, “Advanced MDX,” covers MDX in more detail. Appendix B, “MDX Grammar,” has the full syntax of MDX.

Projecting Tuple Sets On Axes

After an application has defined the set of tuples for each axis, it needs to specify which set is projected on which axis. Going back to the query template in the section “Parts of an MDX Statement,” there is an <axis_specification> value for each axis in the dataset.

The <axis_specification> syntax looks like the following:

<axis_specification> ::= <set> ON <axis_name>
<axis_name> ::= COLUMNS | ROWS | PAGES | SECTIONS | CHAPTERS
              | AXIS(index)

Each axis is associated with a number: 0 for the x-axis, 1 for the y-axis, 2 for the z-axis, and so on. To project a set on an axis, an application uses the syntax AXIS(<index>). The <index> value is the axis number. The first five axes — COLUMNS, ROWS, PAGES, SECTIONS, and CHAPTERS — can be used in place of AXIS(0), AXIS(1), AXIS(2), AXIS(3), and AXIS(4), respectively.

It is an error for an MDX statement to skip axes, or to not start from axis 0. That is, every statement must start from the COLUMNS axis, and must not miss any intermediate axes, such as having COLUMNS and PAGES axes but no ROWS axis.

Note OLE DB for OLAP does not impose any restrictions on the number of axes supported in a dataset other than the minimum requirement of three for OLE DB for OLAP compliance. To find out the number of axes supported by the provider, the application can check the value of the MDPROP_AXES property in the DBPROPSET_DATASOURCEINFO property set.

The SELECT List

The SELECT list for the example dataset follows:

SELECT CROSSJOIN({Venkatrao, Netz},
{USA_North.CHILDREN, USA_South, Japan}) ON COLUMNS,
{Qtr1.CHILDREN, Qtr2, Qtr3, Qtr4.CHILDREN} ON ROWS

Discontiguous Axes in SELECT List

You should not specify a SELECT list where the axes are not contiguous. For instance, it is an error to have ROWS and PAGES, but not COLUMNS; or to have COLUMNS and PAGES, but not ROWS.

However, you may specify a SELECT list with no axes. It is not an error to have an empty SELECT list. In this case, all dimensions are slicer dimensions, and the MDX statement selects at most one cell.


Specifying Slicer Dimensions

Dimensions not explicitly assigned to an axis are assumed to be slicer dimensions and are filtered on their default members. If any such dimensions do not have default members, an error occurs.

The slicer dimensions for the example dataset are Measures, Years, and Products. By making these the slicer dimensions, the application is requesting the dataset for Sales (on the Measures dimension), 1991 (on the Years dimension), and All (all products on the Products dimension). For the example dataset, you can express the slicing condition as follows:

WHERE (Sales, Years, Products.All)

Because the member name “All” might not be unique within the cube, it must be qualified with the dimension name to make it unambiguous. For more information, see “Name Space and Unique Names” in this chapter.

Note A slicer need not always be a tuple. In its most general form, a slicer is a set of tuples, even though it is likely that most providers will not support multiple tuples in a slicer. The property MDPROP_SLICER returns information on the provider capabilities in this area. If a slicer is a set of tuples, then it is equivalent to the one tuple for which each component is a dynamic aggregate of the members selected from that dimension. For example, if the slicer is:

WHERE CROSSJOIN({Sales}, {[1991], [1992]})

it is equivalent to:

WHERE {(Sales, [1991]), (Sales, [1992])}

and the provider generates a dynamic member on the Years dimension, which is the aggregate of 1991 and 1992. This dynamic member is used as the slicer coordinate for that dimension. Based on the context, the provider picks the right aggregation function to use. Semantically, the effect is the same as using the AGGREGATE function. For more information on the AGGREGATE function, see “Numeric Functions” in Chapter 3.


Example Dataset Expression

Combining the SELECT list with the FROM and WHERE clauses, the query for the example dataset is as follows:

SELECT CROSSJOIN({Venkatrao, Netz},
   {USA_North.CHILDREN, USA_South, Japan}) ON COLUMNS,
   {Qtr1.CHILDREN, Qtr2, Qtr3, Qtr4.CHILDREN} ON ROWS
FROM SalesCube
WHERE (Sales, [1991], Products.All)


Name Space and Unique Names

The query expression examples given in previous sections use member names. MDX syntax allows expressions containing dimension, hierarchy, and level names as well.

If all data sources require that all object names used within a cube be unique, then you can use unqualified names (as in the examples). The domain within which an object name must be unique is called the name space of that object. In other words, if the name space of all objects were the cube, there would be no problems. However, this is not always the case.

Data sources that allow for more granular name spaces require that hierarchy and level names be unique within a cube.

However, there can be very granular name spaces for members, such as just the set of all siblings. That is, for any given member, each of its children must have unique names. Slightly less granular is the case where the name space of members is the set of members at the same level; that is, all members belonging to a level (such as States) must be uniquely named.

Because of such granular name spaces, names used within a query expression can sometimes be ambiguous. One way to make the names unambiguous is by qualifying a name to the extent necessary to make it unique. For example, in the Geography dimension, the name Portland is ambiguous because there is a Portland in Maine and another in Oregon. So, to make it unambiguous, it needs to be qualified by the state name: Oregon.Portland or Maine.Portland.

Note that this is further complicated by the fact that when the same member is part of multiple hierarchies, it behaves like two different members. Therefore, the unique name for a member must take this into account and generate two unique names for a member that is present in two different hierarchies.

Qualifying the name is just one way to ensure uniqueness. However, it may not always work. For example, if the name of an ancestor member changes, then the unique name is no longer valid. Therefore, any queries stored by the application become invalid.

A better way is for the provider to give the application a unique name. How the provider constructs a unique name is up to the provider, and is of no concern to the application.

OLE DB for OLAP has a mechanism that an application can use to get the unique name of any object in the multidimensional schema. This information is available in the schema rowsets. The UNIQUE_NAME column in a schema rowset always contains the name of an object that is unambiguous within the context of the cube.

When using the name of an object in an expression, the application must use the string obtained in the UNIQUE_NAME column of the schema rowset. It is up to the provider to make sure that this name is unambiguous. (This theme of using schema rowsets to help formulate a query is not unique to OLE DB for OLAP. It applies to OLE DB in general.)

An application should not make any assumptions of what the unique name given by a provider contains. Specifically, it should not assume that the unique name is a qualified name. Providers may generate a unique name in any way. The provider only guarantees that the unique name unambiguously references an object.

Note If you are dealing with name spaces of objects across all cubes in a schema or across all schemas in a catalog (queries involving multiple cubes), then all object names used must be qualified by cube name and, if necessary, by catalog name and schema name. This applies even to object names obtained from the UNIQUE_NAME column of a schema rowset.

Provider Implementation Considerations for Unique Names

In general, there are two ways for a provider to generate a unique name:

  • Use a qualified name. In this method, the unique name is qualified to the extent needed to make it unambiguous. For example, a member may be qualified by all of its ancestors all the way to the root, and the root itself may be qualified by the name of the hierarchy and the dimension.

  • Use a unique string with no implied structure. In this method, the provider generates a unique name that is just a string with no internal structure. It may be a string that represents a hash table location, for instance.

Using a qualified name as a unique name can have performance advantages. Because the name encodes the “path” to a member, it decreases the search required to locate that member. The disadvantage is that if the name of an ancestor changes, then the path becomes invalid. If the path becomes invalid, the queries stored in the application no longer work.

Using a unique string with no internal structure gets around this problem. However, because it no longer contains the path, the search time required to locate the member increases.

Note that it is possible to create a method that has the advantages of both these approaches. For example, the provider can return a string that encodes the “location” information without tying that to the uniqueness of the name itself.

Providers should keep these considerations in mind when deciding on what to expose as a unique name.


Using the OLE DB Command Object

When you have an MDX statement that represents the desired dataset, the next step is to submit the query for execution. OLE DB for OLAP uses the existing infrastructure in OLE DB for query execution. This infrastructure takes the form of the command object. The command object has an interface called ICommandText, which has several methods that, when taken together, provide the ability to execute queries:

  • ICommand::SetCommandText associates the query string with the command object.

  • ICommand::GetCommandText gets the query string associated with the command object.

  • ICommand::Execute executes the query associated with the command object.

  • ICommand::Cancel attempts to cancel a currently executing query.

  • ICommand::GetDBSession gets an interface pointer to the session object that created this command object.

Execution of an MDX statement usually results in the creation of a dataset, unless the application chooses to ask for a rowset object to be created (which represents a flattened dataset). For more information, see “Flattening a Dataset to Produce a Rowset.”


Delimited Identifiers

When used in the context of a DBMS, the term identifier denotes any object name that appears in the schema. For example, the cube name is an identifier, as is the dimension name, hierarchy name, level name, member name, property name, and so on.

Many provider implementations allow special characters in an identifier, such as the space character in the member name “Microsoft Office.” Certain providers also allow language keywords to be used as object names. However, it is better to avoid this because the presence of such identifiers can easily disrupt a parser. In the following example, an MDX statement generates a syntax error because the text Microsoft Office.Children cannot be parsed.

SELECT Microsoft Office.Children ON COLUMNS, Quarters.MEMBERS ON ROWS
FROM SalesCube

In this statement, the COLUMNS axis was intended to include all the children of the member Microsoft Office. The code needs a way to delimit the identifier “Microsoft Office.” To do this, use delimit characters, which are special characters that enclose the identifier and give the parser the ability to treat everything between the delimit characters as a single token. Typically, the square bracket characters ( [ ] ) are used to delimit the quotation. By using square brackets as the delimit characters, the earlier expression can be rewritten as

SELECT [Microsoft Office].Children ON COLUMNS, Quarters.MEMBERS ON ROWS
FROM SalesCube

This statement can be parsed unambiguously.

Delimited identifiers should be used when sending MDX statements to the provider. They should also be used when restricting schema rowsets. Consumers, especially those working with multiple data sources, are strongly encouraged to delimit all identifiers used in an MDX statement or a schema rowset restriction.


Delimited Identifiers Returned from the Provider

This discussion so far has been about the need for quoting identifiers when the consumer inputs a query string or schema rowset restriction. This section will discuss when and why a provider returns delimited identifiers.

Consumers frequently use the information returned from schema rowsets to formulate a query. For example, the consumer user interface may allow a user to select a set of displayed members and drop them onto an axis of a dataset. When displaying the members, MEMBER_CAPTION is used. The act of dropping the set of members onto an axis triggers a routine in the consumer that formulates an axis specification. Here, it will use the MEMBER_UNIQUE_NAME column from the MEMBERS rowset. As mentioned in the section “Name Space and Unique Names,” one possible way a provider can generate a unique name is by constructing it out of various component names, with the period (.) as the separator between these component names. Such providers should make sure that each component is delimited; otherwise, the query may have trouble parsing.

An example will make this clearer. There is a city called New Albany in New Jersey, USA, and also one in Nova Scotia, Canada. If the provider returned the MEMBER_UNIQUE_NAME column of the MEMBERS rowset for these two members without quoting, the returned values will be USA.New Jersey.New Albany and Canada.Nova Scotia.New Albany, respectively. The consumer can put delimit characters around the entire name — [USA.New Jersey.New Albany] and [Canada.Nova Scotia.New Albany]. However, the provider will be unable to find out the individual components that form the unique name because it is unclear whether the period (.) acts as a separator or is a part of the name. To get around these problems, the provider should return the MEMBER_UNIQUE_NAME column with each component delimited — [USA].[New Jersey].[New Albany] and [Canada].[Nova Scotia].[New Albany].

A provider that constructs a unique name out of component names should return component-delimited identifiers (that is, each component of a compound identifier is delimited) in the following places:

  • All _UNIQUE_NAME columns of a schema rowset.

  • The MEMBER_UNIQUE_NAME column in the axis rowset.

  • All column names in the flattened dataset.

  • Column values of any columns that correspond to the MEMBER_UNIQUE_NAME property in a flattened dataset.

Note that providers are not required to construct unique names by using the “path” to a member (or any other schema object). The provider is free to construct the unique name in any way. Applications should treat a unique name as an opaque string with no structure. For more information, see the section “Name Space and Unique Names” in this chapter.


Differences in Delimit Characters

The previous examples used square brackets ( [ ] ) as delimit characters, a very common implementation. However, other, equally common delimit characters exist, and some that are not so common. A consumer cannot rely on any particular delimit character if working with multiple data sources but should check for a provider’s quotation characters. This ensures interoperability.

The OLE DB method IDBInfo::GetLiteralInfo provides a way a consumer can interrogate the provider to find the delimit character. The consumer should use DBLITERAL = DBLITERAL_QUOTE. In the returned DBLITERALINFO structure, the pwszLiteralValue column contains the delimit characters.

If it contains just one character, then the same character is used at both ends of the delimited identifier. A common example is the double quotation mark ("), which is used as "USA"."New Jersey"."New Albany". If it contains two characters, then the first character begins the delimited identifier and the second character terminates it. The double bracket characters are an example of this.


Datasets

The dataset encapsulates a multidimensional result set and is the result of executing an MDX statement. From the discussion in the section “Axes and Slicer Dimensions” in this chapter, it is clear that a dataset consists of a set of axes. The points along an axis are called coordinates. For an axis that consists of a single dimension, these coordinates are a subset of the members of that dimension. If an axis consists of more than one dimension, then each coordinate is a compound entity that has n parts, where n is the number of dimensions oriented along that axis. Each part of the coordinate is a member from one constituent dimension.

For example, if the “Geography” and “Product” dimensions are oriented along the x-axis of a dataset, a coordinate along this axis may be (USA, Computers). In this example, the “USA” member of the Geography dimension has been combined with the “Computers” member of the Products dimension to result in the compound entity, the tuple. Note that determining a coordinate along the x-axis in this example requires members from each dimension oriented along it.

At the intersection of axes coordinates are cells. Each cell has multiple pieces of information associated with it. The data itself is one such piece of information. Other pieces of information are:

  • A formatted string, which is the form of cell data that can be displayed

  • The cell ordinal number

  • Display information such as fore color or back color

These are collectively called cell properties.


Dataset Objects for Multidimensional Results

Dataset Objects

The dataset object is an object that provides an abstraction for a multidimensional result set. It has interfaces and methods on it that:

  • Give the number of axes and the contents of each axis.

  • Provide information about the slicer dimensions (the tuple in the WHERE clause).

  • Create an accessor.

  • Describe the number, data type, and so on of cell properties.

  • Retrieve cell property values.

  • Search the dataset for a given cell and search the axes for a given tuple.

  • Provide information on supported data type conversions.

  • Retrieve an interface on the command object that created the dataset object.

As far as possible, efforts have been made to reuse the existing OLE DB infrastructure for navigation, description, and so on.


Describing the Axes

A dataset is a discrete collection consisting of data points dimensioned by the axes. In order to manipulate the dataset, the application needs information about the following:

  • Number of axes in the dataset

  • What tuples appear on a given axis and the number of tuples

  • Number of dimensions on each axis and their names

  • Number of coordinates on the axis

The method IMDDataset::GetAxisInfo returns an array of structures, one structure for each axis. This structure, denoted as MDAXISINFO in the header files, has fields that contain the axis number, the number of dimensions on the axis, the number of coordinates on the axis, the number of columns corresponding to each dimension on the axis, and the name of each dimension projected on the axis. The field containing the dimension names is an array. The order of dimension nesting is implicitly contained in the order of dimension names that appear on this array — the outermost dimension appears first, and so on. This method also returns the number of axes in the dataset. For more information on the structure of MDAXISINFO, see “MDAXISINFO Structure” in Chapter 4.

Axis Rowset

After the consumer obtains basic information about the axes, the next step is to get detailed information on what each tuple on the axis looks like. This means that the consumer needs to get the properties of each member of each tuple (recall that a tuple is made of one or more components, and each component is a member). All this information is available as a rowset, called the axis rowset. There exists one axis rowset per axis. You can retrieve an axis rowset with IMDDataset::GetAxisRowset. This function takes as an argument the axis ordinal number and returns the corresponding axis rowset.

Each row in the axis rowset corresponds to a tuple on the axis. The first column of the axis rowset is called TUPLE_ORDINAL (see “Cell Navigation in a Dataset” for more information on the tuple ordinal), and this contains the ordinal number of the tuple that a given row corresponds to. The remaining columns contain member properties.

Regardless of whether you choose any member properties by using the DIMENSION PROPERTIES clause or not (see “Retrieving Member Properties”), the axis rowset always contains several properties for the dimension members, such as its unique name, the level to which it belongs, its display caption, etc. For a list of these properties, see “Axis Rowsets” in Chapter 4.

One column for each property will be included in the axis rowset for each projected dimension on the axis. If you specify more properties for a dimension (by using the DIMENSION PROPERTIES keyword), those properties will appear as additional columns after the columns for the default properties for the dimension. The axis rowset for an axis contains as many rows as there are axis tuples. Calling IColumnsInfo on the axis rowset retrieves metadata such as name, data type, and so on, of the columns of the axis rowset.


Returning Slicer Information

After finding how the axes are laid out, the next step is to find out what the slicer dimensions are, and which members are chosen from the slicer dimension. For the purposes of returning slicer dimension information, the set of slicer dimensions is treated as if the dimensions were on a special axis, called the SLICER axis. For the purposes of axis ordering, the SLICER axis is assumed to come after all other axes. The identifier for the SLICER axis is MDAXIS_SLICERS, and is defined to be the constant 0xffffffff, the largest ULONG (32-bit) value possible.

As it does for any other axis, the MDAXISINFO structure returns information about the SLICER axis. The cDimensions field contains the number of slicer dimensions; cCoordinates is 1 for most providers because the slicer for most providers is just one tuple. rgpwszDimensionNames array contains the names of all slicer dimensions and rgcColumns contains the number of columns corresponding to each slicer dimension on the axis rowset. The axis rowset for the slicer dimension usually has just one row because most providers only support one tuple for a slicer.


Cell Navigation in a Dataset

After the structure of the axes is known, the application needs to navigate the cells in the dataset. Navigation is the process of traversing through the various cells in the dataset and zeroing in on the set of cells that you want. In order to navigate through a dataset, there must be a way to refer to cells that determines their relative position in the dataset. This leads to the following three modes of navigation:

  • Navigating by cell ordinal. In this method, you assign each cell a unique number. To navigate, the application simply refers to the ordinal number of the cell it wants (or the starting cell of the set of cells it wants). In other words, the application says, “Go to cell number x and fetch...”

  • Navigating by axes coordinates. In this method, you identify a cell by giving the tuple on each axis at the intersection of which the cell lies. To navigate, the application gives a set of coordinates that identify the cells it is interested in. In other words, the application says “Give me the cell at the intersection of the coordinates (5, 3, 2)...” (5th coordinate on x-axis, 3rd coordinate on y-axis and 2nd coordinate on the z-axis).

  • Navigating by member names. This is a variation on the previous method. You identify a cell by a set of members. Each tuple, which is formed of members, is broken down into its constituent parts and this is used for identification of the desired cells. In other words, the application says, “Give me data value for Sales in USA for 1992, Quarter1 for all Salesreps.” In a sense, this is the “natural” way to think about the cells in a dataset.

OLE DB for OLAP allows for all these three methods of navigation as follows:

  • For navigating by cell ordinal, each coordinate (tuple) on an axis is assigned a number starting from zero and increasing as you move along the axis. This number is called the tuple ordinal. OLE DB for OLAP also defines an algorithm for assigning a unique ordinal to each cell in a dataset, given the tuple ordinals of its coordinates. The method IMDDataset::GetCellData takes as arguments the cell ordinals of the starting cell and the ending cell of the desired range. It then returns those cells contained in the slice of the dataset bounded by the starting cell at its upper left corner and the ending cell at its lower right corner.

  • For navigating by axes coordinates, OLE DB for OLAP defines the method IMDFind::FindTuple, which takes in a set of members (which form the constituents of the desired tuple) and returns the tuple ordinal. The application can make multiple calls to this method to find the tuple ordinal of each coordinate, use the algorithm in OLE DB for OLAP to derive the cell ordinal, and call IMDDataset::GetCellData. The tuple ordinal for each tuple on an axis is also available in the axis rowset.

  • For navigating by member names, OLE DB for OLAP defines the method IMDFind::FindCell, which takes an array of members, a starting cell ordinal, and returns the corresponding cell ordinal. You can now use the IMDDataset::GetCellData method.

For more information on the formula used to derive a cell ordinal given its tuple ordinals, see the description of IMDDataset::GetCellData in Chapter 4.


Describing and Fetching Cell Data

Before the application can actually fetch cell data, it needs to know exactly what data is available in each cell, their data types, and so on. This section describes how these tasks are handled.

A Conceptual Model for Cell Data

Each cell in the dataset has several pieces of data associated with it. First and foremost is the actual data value itself. As described in “Retrieving Cell Properties,” OLE DB for OLAP requires all providers to support at least two other properties: FORMATTED_VALUE and CELL_ORDINAL. You can use the CELL PROPERTIES clause to select the cell properties that the consumer wants to retrieve.

Each cell in the dataset is modeled as a row and each column in this row represents a cell property. Therefore, the dataset itself becomes a set of rows, with each row containing the data for a cell. Modeling a dataset in this fashion enables the reuse of existing methods and structures in OLE DB for describing and binding to data.

Describing Cell Properties

Because properties of a cell are modeled as columns in a row, you can use the interface IColumnsInfo to find out the name, data type, number of bytes required to transfer the data, and other related information about each property. For more information on IColumnsInfo, see the OLE DB Programmer’s Reference.

Data Type of VALUE

The VALUE property of a cell, depending on the cell that you are looking at, may contain data of different types. For example, consider the query:

SELECT NON EMPTY Products.MEMBERS DIMENSION PROPERTIES
         Products.SKU, Products.SRP ON ROWS,
NON EMPTY CROSSJOIN(Years, (Sales, BudgetedSales, PercentChange))
   ON COLUMNS
FROM SalesCube
WHERE (January, SalesPerson.All, Geography.USA)

The dataset that the application displays as:

You can see that the NON EMPTY clause suppressed the “PercentChange” column for 1996, because the company did not track its financials before 1996. Therefore, this measure makes no sense.

In this dataset, the heavily shaded regions are displayed using the information from the axes rowsets. The rest is a display of cells. Among these, the data type of the cells in the lightly shaded region is double, but the data type of the cells in the unshaded region is integer.

Because each cell is modeled as a row, the rowset corresponding to the dataset has 20 rows. The ordinal number of each cell is in parentheses below the cell data. The data type of the VALUE column in rows 4, 9, 14, and 19 is double and the data type of the other rows is integer. The interface IColumnsInfo is not designed to describe rowsets for which the data type of a column varies based on the row. (This same problem does not exist for any other cell properties defined in OLE DB for OLAP.)

To solve this problem, OLE DB for OLAP treats the VALUE property of a cell as the DBTYPE_VARIANT data type of OLE DB. This data type is capable of holding data of many different types. The consumer can look at the vt column of the VARIANT structure, determine the actual type of VALUE, and interpret it accordingly.

Information on Supported Conversions

When fetching cell data, the consumer can ask the provider to perform data conversion to a desired target type. To do this, the consumer needs to know what data type conversions the provider supports. This information is available through the IConvertType interface. This is a mandatory interface on the dataset object. It has a single method, IConvertType::CanConvert. This method takes a source type and a target type and returns S_OK if the source type can be converted to the target type, or S_FALSE otherwise.

Getting Cell Data

OLE DB for OLAP uses the same model OLE DB uses for retrieving data. In this model, the consumer:

  1. Allocates a buffer to hold the data it wants to fetch. The size of this buffer is determined by the data type, precision, scale, length, and so on of the column data, and this information is available through the methods in IColumnsInfo interface.

  2. Chooses the columns in the dataset’s rowset that it wants to retrieve (in other words, the cell properties that it wants).

  3. Allocates an array of DBBINDING structures, one structure for every column in the rowset. This structure is populated with the following information:
    • The column ordinal that corresponds to this structure.

    • The desired data type. This is the type that the application wants the data to be in; that is, the target type. If the target type is different from the source type, then the provider performs the necessary data type conversion.

    • Offset in the buffer where the data for this column is to be placed.

    • Offset in the buffer where the number of retrieved bytes is to be placed (for string and other variable length data).

    • Offset in the buffer where the status indicator is to be placed.

    • Other pieces of information not relevant to this discussion.
  4. Obtains the IAccessor interface on the dataset object and uses IAccessor::CreateAccessor to create an accessor and associate it with the set of bindings (that is, the array of DBBINDING structures). This method returns a handle to the created accessor.

  5. Gets the IMDDataset interface and calls IMDDataset::GetCellData method. This method takes as arguments:
    • The accessor created in step 4.

    • Address of the buffer that was allocated in step 1.

    • The cell ordinal that indicates the first cell of the cell set.

    • The cell ordinal that indicates the last cell to fetch.

    Using the binding information contained in the accessor, the provider populates the consumer’s buffer.

For more information on accessors, DBBINDING structure and data types, see the OLE DB Programmer’s Reference.


Stateless Navigation

The methods that navigate the dataset do not depend on the concept of a “current position.” In other words, there are no navigation/fetch interfaces in OLE DB for OLAP that assume that there is a current cell. To fetch a cell, the application specifies the ordinal position of the cell that it wants, and asks to fetch the data for that cell. Simplicity is the primary reason for the stateless method of navigation. Providers do not need to maintain state information regarding which cell the consumer is “on” at this time.


Postprocessing and New Dataset Specification

After obtaining a new dataset, the consumer typically wants to perform several operations on it, such as pivoting, changing dimension orientation, sorting, or rotation. In general, there are two approaches to accommodating such functionality:

  • Treat each operation as a modification to the command specification. In this method, every operation results in the creation of a new dataset.

  • Postprocessing, in which the consumer creates a dataset object from some command specification. Once the object is created, further user operations modify the dataset object, resulting in a change to the data manifold represented by the dataset. There is no need to create a new dataset or alter the original command specification that created the dataset.

OLE DB for OLAP takes the first approach. The primary reason is simplicity: Any operation that the user wants to perform on a dataset is necessarily a subset of the operations needed to create the dataset from the cube. By considering postprocessing operators as incremental changes to the original dataset specification, all the semantics of the command specification process (which have already been defined) can be leveraged. The postprocessing approach requires first identifying the postprocessing operators as a subset of the original command operators, then defining semantics for each of these operators, and so on. There is no significant functionality or ease-of-use gain for the consumer anyway.


Creation of Multiple Datasets

After a command object has been used to create a dataset, it is not possible to modify the command specification and create another dataset by using the same command object unless the original dataset and associated rowsets (the axes rowsets, the range rowset, and the flattened rowset) are closed.

If the application wants to operate on the dataset to create a new dataset, it has to either close the original dataset before reusing the command or it has to create a new command object.


Getting the Command Object from a Dataset

In OLE DB, the recurring theme is that there is a method on an object that returns an interface on the object that created it. For example, the method IGetDataSource::GetDatasource is a method on the session object that returns an interface pointer on the data source object that created it.

Accordingly, the dataset object needs a method that returns an interface on the command object that created it. This method is IMDDataset::GetSpecification. The word “specification” here means the object that contains the dataset specification; that is, the command object.


Flattening a Dataset to Produce a Rowset

It is often useful for consumers to get a flattened view of a dataset. The process of flattening can be illustrated by considering the following dataset. The axis dimensions are Products, Geography, Quarters. Slicer dimensions are Measures, Years, and SalesRep.

Flattening this dataset yields the following rowset:

[Quarters]. [MEMBER_ CAPTION] | [The World]. [North America]. [USA]. [Com- puters] | [The World]. [North America]. [USA]. [Expan- sion_ Cards] | [The World]. [North America]. [USA]. [Floppy_ Drives] | [The World]. [Asia]. [Com- puters] | [The World]. [Asia]. [Expan- sion_ Cards] | [The World]. [Asia]. [Floppy_ Drives]
Qtr1 | 4622 | 1245 | 2356 | 5622 | 2245 | 54356
Qtr2 | 34069 | 25 | 235847 | 454353 | 5454 | 45651
Qtr3 | 39 | 439058 | 5920 | 4525 | 793 | 3135
Qtr4 | 53948 | 432 | 42908 | 3287 | 125885 | 3968

Note In this table, the names of columns 2–7 are created by concatenating names from the Geography and Products dimensions. It is assumed that the provider fully qualifies the member names from the Geography dimensions to ensure uniqueness — hence “[The World].[Asia]” instead of “[Asia]”. On the other hand, it is assumed that member names from the Products dimension do not need to be fully qualified — hence “[Computers]” rather than “[All Products].[Product Line].[Computers]”. The provider is only required to return unique names. The extent to which the names are qualified in order to make them unique is up to the provider. Note also that a provider need not use qualification as a way of ensuring uniqueness. A provider can use any method that it wants to.


Flattening Algorithm

OLE DB for OLAP defines the following algorithm for flattening a dataset:

  1. The components of each tuple on the COLUMNS axis are concatenated together with a period (.) separator to yield compound names. Each component consists of a unique name. Call this set of names X.

  2. For every non-X axis dimension Di:

    a. Let k be the lowermost level on the axis from this dimension.

    b. Let Li be the set of unique names of levels above (and including) k up to (but not including) the root level. If Di has multiple members at the root level, add the unique name of the root level to Li. Li must contain unique names.

    c. Case:

    i. If there is a DIMENSION PROPERTIES clause in the MDX statement that created the dataset, then let Pi be the list of properties specified in this clause, enclosed in [ ] (thus, if DIMENSION PROPERTIES MEMBER_UNIQUE_NAME is specified, the Pi value contains the string "[MEMBER_UNIQUE_NAME]").

    ii. Otherwise, let Pi contain the string "[MEMBER_CAPTION]".

    For each element l of Li, and each element p of Pi, concatenate l and p together, separated by a period (.). Let Ci be the resulting set of compound names.

  3. Let C represent the union of all Ci values obtained in step 2.

  4. The flattened dataset has the following columns:

    a. Each name in C is a column.

    b. The levels from the highest numbered axis come first, followed by lower numbered axes.

    c. Within the levels of each axis, the levels of the outermost dimension come first.

    d. Within the levels of each dimension, the lower numbered levels (that is, levels at a higher degree of aggregation) come first, followed by higher numbered levels (that is, levels with a lower degree of aggregation).

  5. Among the properties for a given level, the order is as specified in the DIMENSION PROPERTIES clause.

  6. If there is no DIMENSION PROPERTIES clause, then the column name is just the unique name of the level (see step 2 — Ci is the same as Li). The CAPTION property is returned as the value of this column.

Note The main idea behind a flattened rowset is that it is a quick and easy method for rowset-based consumers to display multidimensional data. Therefore, the default (without the DIMENSION PROPERTIES clause) is to return the display caption.

  7. Each name in X is a column.

    a. These columns appear after the columns from L.

    b. Within X, the columns are in tuple order.

    c. The names in X are always the unique names, regardless of whether the DIMENSION PROPERTIES clause exists or not. Consequently, any properties specified in this clause are ignored.

  8. If a dimension has a single member at the root level, and that member appears on an axis, then this is represented in the flattened rowset by a row that has a NULL in every column that corresponds to a level below it.


Using Measures on the COLUMNS Axis

In the special case of a dataset consisting of only the Measures dimension along the x-axis, the resulting flattened view is typical of the way in which atomic OLAP data is commonly stored in a relational database. This table of granular data is called a fact table. The combination of a fact table and a set of related dimension tables make up a star schema configuration.

For example, consider the MDX statement below (unqualified names are assumed to be unique):

SELECT
   {Sales, Cost} ON COLUMNS,
   CROSSJOIN({USA, Asia}, {Qtr1, Qtr2, Qtr3, Qtr4}} ON ROWS
FROM SalesCube
WHERE ([1991], Computers)

This produces the dataset below:

This yields the flattened rowset:

[Continents]. [MEMBER_ CAPTION] | [Countries]. [MEMBER_ CAPTION] | [Quarters]. [MEMBER_ CAPTION] | [Measures]. [Cost] | [Measures]. [Sales]
North America | USA | Qtr1 | 123 | 456
North America | USA | Qtr2 | 789 | 1011
North America | USA | Qtr3 | 1213 | 1415
North America | USA | Qtr4 | 1617 | 1819
Asia | NULL | Qtr1 | 2021 | 2223
Asia | NULL | Qtr2 | 2425 | 2627
Asia | NULL | Qtr3 | 2829 | 3031
Asia | NULL | Qtr4 | 3233 | 3435

This representation is intuitive and corresponds to the way such data is usually displayed in a tabular view.

This example also illustrates step 2 of the algorithm given in “Flattening Algorithm”: If the DIMENSION PROPERTIES clause is not specified, then it is equivalent to specifying DIMENSION PROPERTIES MEMBER_CAPTION.


A More Complex Example

The flattened dataset from the section “Cube and Dataset Example.” looks like this:

[Quarters]. [MEMBER_ CAPTION] | [Months]. [MEMBER_ CAPTION] | [Venkatrao]. [The World]. [North America]. [USA]. [USA_North]. [Seattle] | [Venkatrao]. [The World]. [North America]. [USA]. [USA_North]. [Boston] | [Venkatrao]. [The World]. [North America]. [USA]. [USA_South]
Qtr1 | Jan | 00 | 10 | 20
Qtr1 | Feb | 01 | 11 | 21
Qtr1 | Mar | 02 | 12 | 22
Qtr2 | NULL | 03 | 13 | 23
Qtr3 | NULL | 04 | 14 | 24
Qtr4 | Oct | 05 | 15 | 25
Qtr4 | Nov | 06 | 16 | 26
Qtr4 | Dec | 07 | 17 | 27

(This is the same table continued.)

[Venkatrao]. [The World]. [Asia]. [Japan] | [Netz]. [The World]. [North America]. [USA]. [USA_North]. [Seattle] | [Netz]. [The World]. [North America]. [USA]. [USA_North]. [Boston] | [Netz]. [The World]. [North America]. [USA]. [USA_South] | [Netz]. [The World]. [Asia]. [Japan]
30 | 40 | 50 | 60 | 70
31 | 41 | 51 | 61 | 71
32 | 42 | 52 | 62 | 72
33 | 43 | 53 | 63 | 73
34 | 44 | 54 | 64 | 74
35 | 45 | 55 | 65 | 75
36 | 46 | 56 | 66 | 76
37 | 47 | 57 | 67 | 77

Note In this example, it is assumed that members from the Geography dimension need to be fully qualified to ensure uniqueness, while the members from all other dimensions do not.


Example Using DIMENSION PROPERTIES

Consider the MDX statement

SELECT
      {Measures.Cost, Measures.Sales}
            DIMENSION PROPERTIES MEMBER_CAPTION ON COLUMNS,
      CROSSJOIN({Venkatrao, Netz}, Products.MEMBERS)
            DIMENSION PROPERTIES MEMBER_CAPTION, MEMBER_UNIQUE_NAME,
            SalesRep.[Phone Number], Products.SKU ON ROWS
FROM Sales
WHERE ([1991], Qtr1, USA)

This results in the following flattened rowset:

[Sales- rep]. [Phone Num- ber] | [Sales- rep]. [MEM- BER_ CAP- TION] | [Sales- rep]. [MEM- BER_ UNIQUE_ NAME] | [Pro- ducts]. [SKU] | [Prod- ucts]. [MEM- BER_ CAP- TION] | [Pro- ducts]. [MEM- BER_ UNIQUE_ NAME] | [Meas- ures]. [Cost] | [Meas- ures]. [Sales]
555-1212 | Venka-trao | [Sales-rep]. [Venka-trao] | 125678 | Microsoft Excel | [Products]. [Microsoft Excel] | 345678 | NULL
555-1212 | Venka-trao | [Sales-rep]. [Venka-trao] | 135742 | Microsoft Word | [Products]. [Microsoft Word] | 823456 | 789101
555-1214 | Netz | [Sales-rep]. [Netz] | 125678 | Microsoft Excel | [Products]. [Microsoft Excel] | 346587 | 678432
555-1214 | Netz | [Sales-rep]. [Netz] | 135742 | Microsoft Word | [Products]. [Microsoft Word] | 932456 | 732489

It is important to note the following things:

  • It is assumed that the Products dimension has only one level.

  • All names that are made of multiple components — that is, all column names and column values for the UNIQUE_NAME columns — are component-delimited. (Each component is delimited.)

  • Although the CAPTION property was asked for on the COLUMNS axis, it was ignored, and column names consist of UNIQUE_NAME values.

  • For an explanation of the NULL values in the first row, see “Nulls.”


Order of Members in a Flattened Rowset

The order of members in the flattened rowset is the same as the order of those members in the axis. In other words, member order remains invariant during the flattening operation. Level transitions on an axis appear on different columns, thereby nesting the values.


Getting a Flattened Rowset

The guaranteed way for a consumer to get the flattened rowset of a dataset is by setting the DBPROP_IRowset property in the DBPROPSET_ROWSET property set to VARIANT_TRUE. It should do so by using ICommandProperties::SetProperties on the command object. It should also set the rguidDialect argument of the ICommandText::SetCommandText method to MDGUID_MDX.

For more information on the rules that determine whether a rowset object or a dataset object is returned from an MDX query, see “Controlling the Creation of Instances of a Rowset or a Dataset” in Chapter 4.


Nulls

Nulls appear in the flattened rowset in two different situations:

  • In the flattened dataset of “A More Complex Example” in this chapter, column 2 of rows 4 and 5 contained NULL. This indicates that this member is not present on the axis. Looking back at the dataset, one can see that “Months” for Qtr3 and Qtr4 are not on the axis.

  • When a cell is empty. The flattened dataset of “Example Using DIMENSION PROPERTIES” has NULLs in the last column of the first row. This is because Venkatrao did not have any sales of Microsoft Excel in USA for the first quarter of 1991. The corresponding cell in the dataset is empty.

When a consumer calls IRowset::GetData, DBSTATUS_ISNULL is returned. DBSTATUS_ISNULL is one of the status codes defined in OLE DB. An application allocates a buffer, and for each column it wants, it has space to hold:

  • The actual data value (called the VALUE part).

  • The status after fetch, such as NULL indication or truncation indication (called the STATUS part).

  • The number of bytes of data fetched for variable-length data (called the LENGTH part).

DBSTATUS_ISNULL is written into the STATUS part of the consumer’s buffer. When this status occurs, the VALUE part is meaningless.


Lack of Support for Named Levels

The OLE DB for OLAP algorithm for generating column names for the flattened dataset relies on the fact that levels can be named. However, some data sources do not support named levels. For such data sources, the following methods are possible:

  • They can generate dummy names for the tree levels that they possess. This means that the column names in the set L described in the section “Flattening Algorithm” contain dummy names generated by the provider.

  • They can generate set L so that there is one column per dimension, as opposed to one column per level.

  • They can choose to not support flattening.

OLE DB for OLAP strongly recommends that the provider support the dummy name method described above. This makes the provider accessible to a larger class of applications.

You can determine exactly which one of these options a provider chooses by the value of the MDPROP_FLATTENING_SUPPORT property in the DBPROPSET_DATASOURCEINFO property set.

If the value of this property is:

Value | Meaning
MDPROPVAL_FS_ FULL_SUPPORT | Provider supports flattening as described in the section “Flattening Algorithm.”
MDPROPVAL_FS_ GENERATED_COLUMN | Provider supports flattening as described in method 1 above.
MDPROPVAL_FS_ GENERATED_DIMENSION | Provider does not support flattening. For such a provider, calling ICommand::Execute with a language dialect of MDGUID_MDX and asking for the IRowset interface results in an error (E_NOINTERFACE)
MDPROPVAL_FS_ NO_SUPPORT | Provider does not support flattening. For such a provider, calling ICommand::Execute with a language dialect of MDGUID_MDX and asking for the IRowset interface results in an error (E_NOINTERFACE)


Range Rowsets

The range rowset is a rowset of which each row corresponds to a cell in the dataset. Each selected cell property appears as a column in this rowset. The range rowset can be created from the dataset by the IMDRangeRowset::GetRangeRowset method. The range rowset does not have to contain all the cells of the dataset. A subset of cells may be chosen in a manner similar to IMDDataset::GetCellData. IMDRangeRowset is an optional interface on the dataset object. For information on how to find out if the range rowset is supported by the provider or not, see “Data Source Property, MDPROP_RANGEROWSET.”

Consider the dataset in “Cube and Dataset Example.” The call

IMDRangeRowset::GetRangeRowset(NULL, 1, 19, cProperties, rgPropertySets,
   &pIRowsetChange);

returns the following rowsets, assuming that the default properties — CELL_ORDINAL, VALUE, and FORMATTED_VALUE — were selected:

CELL_ORDINAL | VALUE | FORMATTED_VALUE
1 | 10 | “10”
2 | 20 | “20”
3 | 30 | “30”
9 | 11 | “11”
10 | 21 | “21”
11 | 31 | “31”
17 | 12 | “12”
18 | 22 | “22”
19 | 32 | “32”

Note The quotation marks around values in the FORMATTED_VALUE column are there as a visual aid to point out that they are strings. The provider does not return the quotes.

The primary uses of the range rowset are:

  • Remoting. Remoting a dataset amounts to remoting the axis rowsets, the MDAXISINFO structures associated with each axis, and the range rowset. The advantage is that if a provider knows how to remote a rowset, then implementing dataset remoting is a straightforward step.

  • Updating. Updating the cells of a cube is tantamount to updating the range rowset. The section “Updating Cell Data” discusses this in detail.

The following rules govern the structure of the range rowset:

  1. The range rowset always contains CELL_ORDINAL as the first column, regardless of whether it was specified in the CELL PROPERTIES clause of the dataset. The other properties appear as subsequent columns, according to the following rules:
    • If the MDX statement that created the dataset had a CELL PROPERTIES clause, then the columns appear in the order specified in that clause.

    • Otherwise, the columns are CELL_ORDINAL, VALUE, and FORMATTED_VALUE, in that order.

    • What happens in these cases is provider-specific:

      Either the MDX statement has CELL_ORDINAL specified in the CELL PROPERTIES clause, but not as the first property (for example, CELL PROPERTIES VALUE, CELL_ORDINAL), or the MDX statement has a property specified multiple times in the CELL PROPERTIES clause (for example, CELL PROPERTIES CELL_ORDINAL, VALUE, FORMATTED_VALUE, VALUE).

  2. The CELL_ORDINAL column is always read-only.

  3. The other columns may or may not be read-only, based on provider-defined conditions. If the range rowset can be updated (that is, if it supports the IRowsetChange interface), then usually at least one column, frequently the VALUE column, can be updated. Providers usually support the updating of other columns as well. To determine if a column can be updated, consumers must check dwFlags in the DBCOLUMNSINFO structure for the column.

  4. The rows in the range rowset are ordered by CELL_ORDINAL column.


Updating Cell Data

Unless one or more of the coordinates of a cell in a dataset is a calculated member, the cell is a point in the n-dimensional manifold represented by the cube. Therefore, updating cells in a dataset is tantamount to updating cube data. By using OLE DB for OLAP, cell data in a cube can be updated by providing methods for updating the cells of a dataset.

Updating is accomplished by using the range rowset. The consumer can update this rowset by using the IRowsetChange interface — just like any other OLE DB rowset.

Note the following rules:

  1. The CELL_ORDINAL column is always read-only.

  2. The other columns may or may not be read-only, based on provider-defined conditions. If the range rowset can be updated (that is, if it supports the IRowsetChange interface), usually at least one column, frequently the VALUE column, can be updated. Providers usually support the updating of other columns as well. Consumers must check dwFlags in the DBCOLUMNSINFO structure for the column to determine if a column can be updated.

  3. If one or more coordinates of a cell contains a calculated member, the semantics of deleting and updating the corresponding row in the range rowset are provider-specific. Typically, this results in DB_S_ERRORSOCCURRED or DB_E_ERRORSOCCURRED.

  4. If a cell corresponds to an aggregation, the corresponding row in the range rowset may or may not be updatable, depending on the capabilities of the provider. See “Updating Aggregate Cells” in this chapter. Whether it can be deleted is provider-specific.


Data Source Property MDPROP_RANGEROWSET

The data source property MDPROP_RANGEROWSET returns information on whether the provider supports updating of cells through the IMDRangeRowset interface:

  • If the value of this property is MDPROPVAL_RR_NORANGEROWSET, then the provider does not support getting the range rowset — that is, the interface IMDRangeRowset is not supported.

  • If the value is MDPROPVAL_RR_READONLY, then the provider supports the IMDRangeRowset interface, but the returned rowset is read-only.

  • If it returns MDPROPVAL_RR_UPDATE, then the provider supports IMDRangeRowset interface with at least one column (typically the VALUE column) that can be updated — that is, the provider supports the IRowsetChange interface on the range rowset.

Consumers should not use the range rowset purely to fetch cell data (even though it is possible to do it). The IMDDataset::GetCellData method is more optimized for the typical navigation requirements of an OLAP application. Moreover, because the range rowset may have to support the update interfaces, it may not be as efficient as fetching cell data by using IMDDataset::GetCellData.


Update and Delete Semantics

The semantics of updating are straightforward.

Deleting a cell means, for that point in the n-dimensional space, there is no value. If that cell is included in a dataset and fetched by using IMDDataset::GetCellData, then the STATUS part of the consumer’s buffer has the value MDSTATUS_S_CELLEMPTY.

Deletion can also be accomplished by setting the STATUS part of the consumer’s buffer that corresponds to the VALUE column to MDSTATUS_S_CELLEMPTY, and calling IRowsetChange::SetData.

Updating Aggregate Cells

When updating a cell in the dataset that is an aggregate, what should happen to the cells below it? That is, what should happen to the cells that combine to create the aggregate? For example, all cells with the coordinate Qtr1 on the time dimension are aggregate cells because they depend on cells with a month as a coordinate (Jan, Feb, etc.).

In this case, behavior varies among providers. Some providers do not allow updates on nonatomic cells. Other providers allow a nonatomic cell to be updated, but they do not change the value of atomic cells beneath it.

The property MDPROP_AGGREGATECELL_UPDATE can be used by the consumer to find out whether a provider supports update of nonatomic cells. If it does, the property can also yield more information on a provider’s behavior when such a cell is updated.


Insert Does Not Mean Anything

It does not make sense to insert a cell into the range rowset because the number of cells in the cube is fixed. The only way to “make” new cells is to add new members to one or more dimensions. Therefore, calling IRowsetChange::InsertRow on the range rowset results in E_UNEXPECTED.


Update Dependencies

Updating the value of a cell results in the updating of other dependent properties, such as FORMATTED_VALUE.

It is provider specific whether dependent properties can be updated. If they can be updated, then it is provider-specific what happens to the determining property (such as VALUE) when a dependent property (such as FORMATTED_VALUE) is updated. For cleaner semantics, providers are encouraged to make dependent properties read-only. This specification does not mandate which properties are dependent on which other properties.

Updating a cell (in other words, the corresponding row in the range rowset) that has an atomic value results in changes to all aggregates based on that cell.


Visibility of Changes

When a consumer updates or deletes a row, changes may or may not visible when the consumer releases the row handle and refetches the row. The same uncertainty exists about updates to dependent properties. The answer depends on the rowset property DBPROP_OWNUPDATEDELETE. If the value of this property is VARIANT_TRUE, then the changes are visible on refetching. If the value is VARIANT_FALSE, then the changes are not visible unless the command is re-executed.

Note Explicit updates by the consumer are known to it. Therefore, there is no need to refetch the updated rows. However, explicit updates may result in implicit updates of the dependent properties. The consumer needs to refetch or re-execute the command to see these changes.

Visibility of Changes in the Dataset

Because a consumer can be actively navigating through the dataset when making changes through the range rowset, it may not be known when changes are visible in a dataset. The current solution to this is provider-specific.


Chapter 3

Advanced MDX

This chapter takes a closer look at MDX syntax. It looks at the syntax elements for retrieving member and cell properties and at the use of default hierarchies and members.


Retrieving Member Properties

The set expression in the axis specification for a given axis selects tuples to populate the axis. The dataset returns some basic information about each member in each tuple, such as the member name, parent level, the number of children, and so on. These are referred to as member properties. Many times, members have additional properties associated with them. Properties are usually available for all members at a given level. For example, the Products level may offer the SKU, SRP, Weight, and Volume properties for each product. Those properties are not dimensions, but contain additional information about dimension members. An application may want to extend member information by adding member properties on the axis. Each level of each dimension may contain a set of available properties for the members.

Additional member properties can be selected by using the DIMENSION PROPERTIES keyword after the set expression of the axis specification. For example, the expression

SELECT NON EMPTY Products.MEMBERS
   DIMENSION PROPERTIES Products.SKU, Products.SRP ON ROWS,
CROSSJOIN(Years, (Sales, BudgetedSales)) ON COLUMNS
FROM SalesCube
WHERE (January, SalesPerson.All, Geography.USA)

returns the following dataset:

You may specify only those dimension properties projected on the axis for that particular axis.

OLE DB for OLAP requires that all providers support a list of mandatory member properties, which are the same as the columns of the MEMBERS rowset. For more information, see “Mandatory Member Properties” in Chapter 4.

The syntax for specifying properties in the axis specification is <property_name> for mandatory properties, and <level_name>.<property_name> for all other properties. Because optional properties are associated with levels, named levels have to exist in order for this to work. If the data source does not support named levels, OLE DB for OLAP requires that it recognize a dummy level whose name is the same as the dimension name. All members belong to this level. Now the consumer can get any optional property by referring to it as <dimension_name>.<property_name>.

Note Because mandatory properties cannot be qualified by the dimension name or the level name, a consumer cannot choose different mandatory properties for different dimensions (or levels) on an axis. For example, if the ROWS axis has Geography and SalesRep dimensions, then the consumer cannot choose MEMBER_CAPTION for the Geography dimension, and MEMBER_UNIQUE_NAME for the SalesRep dimension. It has to choose the same property (or properties) for all dimensions on an axis.


Retrieving Cell Properties

A cell may have more properties than the obvious VALUE. The complex and sophisticated nature of multidimensional cubes allows cells to have enhanced properties that enable smart formatting and coloring. Support for these enhanced properties may vary from provider to provider. OLE DB for OLAP recognizes the following properties as typical properties that a provider may support for a cell:

Property name 		Mandatory/
Optional
Data type
Description
CELL_ORDINAL | Mandatory | DBTYPE_UI4 | Ordinal number of the cell in the dataset. For more information, see “Cell Navigation in a Dataset.”
VALUE | Mandatory | DBTYPE_VARIANT | Actual data value.
FORMATTED_VALUE | Mandatory | DBTYPE_WSTR | Character string that represents a formatted display of VALUE.
FORMAT_STRING | Optional | DBTYPE_WSTR | Format string used to create FORMATTED_VALUE. For more information, see “Contents of FORMAT_STRING” in this section.
FORE_COLOR | Optional | DBTYPE_UI4 | Foreground color for displaying VALUE. For more information, see “Contents of FORE COLOR and BACK COLOR” in this section.
BACK_COLOR | Optional | DBTYPE_UI4 | Background color for displaying VALUE. For more information, see “Contents of FORE COLOR and BACK COLOR” in this section.
FONT_NAME | Optional | DBTYPE_WSTR | Font to be used to display VALUE.
FONT_SIZE | Optional | DBTYPE_UI2 | Font size to be used to display VALUE.
FONT_FLAGS Optional DBTYPE_I4 Bitmask detailing effects on the font. Can be the result of a bitwise OR operation of one or more of the following:
  • MDFF_BOLD

  • MDFF_ITALIC

  • MDFF_UNDERLINE

  • MDFF_STRIKEOUT

A provider must support the properties labeled as “Mandatory” in this table. A provider may also support other properties not listed. To find out what cell properties a provider supports, an application can obtain the PROPERTIES rowset with a restriction on the PROPERTY_TYPE column set to MDPROP_CELL. The application can specify exactly what properties it wants to see by using the CELL PROPERTIES keyword after the WHERE clause. For example, the expression

SELECT NON EMPTY Products DIMENSION PROPERTIES Products.SKU, Products.SRP
   ON ROWS,
CROSSJOIN (Years, (Sales, BudgetedSales)) ON COLUMNS
FROM SalesCube
WHERE (January, SalesPerson.All, Geography.USA)
CELL PROPERTIES VALUE, FORMATTED_VALUE, FORE_COLOR, BACK_COLOR

will yield a dataset in which each cell has the properties VALUE, FORMATTED_VALUE, FORE_COLOR, and BACK_COLOR.

If the application does not specify any properties for a cell in the MDX statement, the provider assumes a default set of properties that must include CELL_ORDINAL, VALUE, and FORMATTED_VALUE, in that order. It can also include other properties that are provider-specific (for example, provider-specific mandatory properties). It must not include any of the optional properties specified in the table above. If the consumer uses the CELL PROPERTIES clause, then only those properties selected in this clause are returned. (If the clause does not include any of the default properties mentioned above, then those default properties are not returned.)

The properties present in the dataset, their data types, and so on, are available by means of the IColumnsInfo interface on the dataset. You can determine the cell properties in the data source that are available for use in the CELL PROPERTIES clause by using the PROPERTIES rowset and restricting it with the PROPERTY_TYPE column.


Contents of FORMAT_STRING

The FORMAT_STRING property contains a format string that was used to generate the FORMATTED_VALUE property. The contents of FORMAT_STRING vary, depending on the data type of the value.

For Character Values

A format expression for strings can have one section or two sections separated by a semicolon (;).

If you use | The result is
One section only | The format applies to all string data.
Two sections | The first section applies to string data, the second to null values and zero-length strings (" ").

The following characters can appear in the format string for character strings:

Character | Description
@ | Character placeholder. Displays a character or a space. If the string has a character in the position where the at symbol (@) appears in the format string, it displays the character. Otherwise, it displays a space in that position. Placeholders are filled from right to left unless there is an exclamation point character (!) in the format string.
& | Character placeholder. Displays a character or nothing. If the string has a character in the position where the ampersand (&) appears, it displays the character. Otherwise, it displays nothing. Placeholders are filled from right to left unless there is an exclamation point character (!) in the format string.
< | Forces lowercase. Displays all characters in lowercase format.
> | Forces uppercase. Display all characters in uppercase format.
! | Forces left-to-right fill of placeholders. (The default is to fill placeholders from right to left.)

For Numeric Values

A user-defined format expression for numbers can have anywhere from one to four sections separated by semicolons. If the format argument contains one of the named numeric formats, only one section is allowed.

If you use | The result is
One section only | The format expression applies to all values.
Two sections | The first section applies to positive values and zeros, the second to negative values.
Three sections | The first section applies to positive values, the second to negative values, and the third to zeros.
Four sections | The first section applies to positive values, the second to negative values, the third to zeros, and the fourth to null values.

The following example has two sections: The first section defines the format for positive values and zeros, and the second section defines the format for negative values.

"$#,##0;($#,##0)"

If you include semicolons with nothing between them, the missing section is printed using the format of the positive value. For example, the following format displays positive and negative values using the format in the first section and displays "Zero" if the value is zero:

"$#,##0;;\Z\e\r\o"

The following table identifies the characters that can appear in the format string for number formats.

Character | Description
None | Displays the number with no formatting.
0 Digit placeholder. Displays a digit or a zero.

If the expression has a digit in the position where the 0 appears in the format string, it displays the digit. Otherwise, it displays a zero in that position.

If the number has fewer digits than there are zeros (on either side of the decimal) in the format expression, it displays leading or trailing zeros.

If the number has more digits to the right of the decimal separator than there are zeros to the right of the decimal separator in the format expression, it rounds the number to as many decimal places as there are zeros.

If the number has more digits to the left of the decimal separator than there are zeros to the left of the decimal separator in the format expression, it displays the extra digits without modification.
# Digit placeholder. Displays a digit or nothing. If the expression has a digit in the position where the # appears in the format string, it displays the digit. Otherwise, it displays nothing in that position. This symbol works like the 0 digit placeholder, except that leading and trailing zeros are not displayed if the number has the same or fewer digits than there are # characters on either side of the decimal separator in the format expression.
. Decimal placeholder. (In some locales, a comma is used as the decimal separator.) The decimal placeholder determines how many digits are displayed to the left and right of the decimal separator. If the format expression contains only number signs (#) to the left of this symbol, numbers smaller than 1 begin with a decimal separator. To display a leading zero displayed with fractional numbers, use 0 as the first digit placeholder to the left of the decimal separator. The actual character used as a decimal placeholder in the formatted output depends on the number format recognized by your system.
% Percentage placeholder. The expression is multiplied by 100. The percent character (%) is inserted in the position where it appears in the format string.
, Thousand separator. (In some locales, a period is used as a thousand separator.) The thousand separator separates thousands from hundreds within a number that has four or more places to the left of the decimal separator. Standard use of the thousand separator is specified if the format contains a thousand separator surrounded by digit placeholders (0 or #). Two adjacent thousand separators, or a thousand separator immediately to the left of the decimal separator (whether or not a decimal is specified), mean "scale the number by dividing it by 1000, rounding as needed." For example, you can use the format string "##0,," to represent 100 million as 100. Numbers smaller than 1 million are displayed as 0. Two adjacent thousand separators in any position other than immediately to the left of the decimal separator are treated simply as specifying the use of a thousand separator. The actual character used as the thousand separator in the formatted output depends on the number format recognized by your system.
: Time separator. (In some locales, other characters may be used to represent the time separator.) The time separator separates hours, minutes, and seconds when time values are formatted. The actual character used as the time separator in formatted output is determined by your system settings.
/ Date separator. (In some locales, other characters may be used to represent the date separator.) The date separator separates the day, month, and year when date values are formatted. The actual character used as the date separator in formatted output is determined by your system settings.
E- E+ e- e+ | Scientific format. If the format expression contains at least one digit placeholder (0 or #) to the right of E-, E+, e-, or e+, the number is displayed in scientific format and E or e is inserted between the number and its exponent. The number of digit placeholders to the right determines the number of digits in the exponent. Use E- or e- to place a minus sign next to negative exponents. Use E+ or e+ to place a minus sign next to negative exponents and a plus sign next to positive exponents.
- + $ ( ) Displays a literal character. To display a character other than one of those listed, precede it with a backslash (\) or enclose it in double quotation marks (" ").
\ Displays the next character in the format string. To display a character that has special meaning as a literal character, precede it with a backslash (\). The backslash itself is not displayed. Using a backslash is the same as enclosing the next character in double quotation marks. To display a backslash, use two backslashes (\\).Examples of characters that cannot be displayed as literal characters are the date-formatting and time-formatting characters (a, c, d, h, m, n, p, q, s, t, w, y, /, and :), the numeric-formatting characters (#, 0, %, E, e, comma, and period), and the string-formatting characters (@, &, <, >, and !).
"ABC" | Displays the string inside the double quotation marks (" "). To include a string in format from within code, you must use Chr(34) to enclose the text (34 is the character code for a double quotation mark).

For Date/Time Values

The following table identifies characters that can appear in the format string for date/time formats:

Character | Description
(:) | Time separator. (In some locales, other characters may be used to represent the time separator.) The time separator separates hours, minutes, and seconds when time values are formatted. The actual character used as the time separator in formatted output is determined by your system settings.
(/) | Date separator. (In some locales, other characters may be used to represent the date separator.) The date separator separates the day, month, and year when date values are formatted. The actual character used as the date separator in formatted output is determined by your system settings.
C | Displays the date as ddddd and displays the time as ttttt, in that order. Displays only date information if there is no fractional part to the date serial number. Displays only time information if there is no integer portion.
d | Displays the day as a number without a leading zero (1–31).
dd | Displays the day as a number with a leading zero (01–31).
ddd | Displays the day as an abbreviation (Sun–Sat).
dddd | Displays the day as a full name (Sunday–Saturday).
ddddd | Displays the date as a complete date (including day, month, and year), formatted according to your system's short date format setting. For Microsoft Windows®, the default short date format is m/d/yy.
dddddd | Displays a date serial number as a complete date (including day, month, and year), formatted according to the long date setting recognized by your system. For Microsoft Windows, the default long date format is mmmm dd, yyyy.
w | Displays the day of the week as a number (1 for Sunday through 7 for Saturday).
ww | Displays the week of the year as a number (1–54).
m | Displays the month as a number without a leading zero (1–12). If m immediately follows h or hh, the minute rather than the month is displayed.
mm | Displays the month as a number with a leading zero (01–12). If m immediately follows h or hh, the minute rather than the month is displayed.
mmm | Displays the month as an abbreviation (Jan–Dec).
mmmm | Displays the month as a full month name (January–December).
q | Displays the quarter of the year as a number (1–4).
y | Displays the day of the year as a number (1–366).
yy | Displays the year as a two-digit number (00–99).
yyyy | Displays the year as a four-digit number (100–9999).
h | Displays the hour as a number without leading zeros (0–23).
hh | Displays the hour as a number with leading zeros (00–23).
n | Displays the minute as a number without leading zeros (0–59).
nn | Displays the minute as a number with leading zeros (00–59).
s | Displays the second as a number without leading zeros (0–59).
ss | Displays the second as a number with leading zeros (00–59).
t t t t t | Displays a time as a complete time (including hour, minute, and second), formatted using the time separator defined by the time format recognized by your system. A leading zero is displayed if the leading zero option is selected and the time is before 10:00 A.M. or P.M. For Microsoft Windows, the default time format is h:mm:ss.
AM/PM | Uses the 12-hour clock. Displays an uppercase AM with any hour before noon; displays an uppercase PM with any hour between noon and 11:59 P.M.
am/pm | Uses the 12-hour clock. Displays a lowercase AM with any hour before noon; displays a lowercase PM with any hour between noon and 11:59 P.M.
A/P | Uses the 12-hour clock. Displays an uppercase A with any hour before noon; displays an uppercase P with any hour between noon and 11:59 P.M.
a/p | Uses the 12-hour clock. Displays a lowercase A with any hour before noon; displays a lowercase P with any hour between noon and 11:59 P.M.
AMPM | Uses the 12-hour clock. Displays the AM string literal as defined by your system with any hour before noon; displays the PM string literal as defined by your system with any hour between noon and 11:59 P.M. AMPM can be either uppercase or lowercase, but the case of the string displayed matches the string as defined by your system settings. For Microsoft Windows, the default format is AM/PM.


Contents of FORE_COLOR and BACK_COLOR

Both FORE_COLOR and BACK_COLOR are 4-byte integers. The high byte is always zero. The lower 3 bytes, from least to most significant byte, determine the amount of red, green, and blue, respectively. The red, green, and blue are represented by a number between 0 and 255.


Join Compatibility

As previously discussed in chapter 2, the FROM clause is used to specify the name of the source cube. Some providers may support joining cubes that share common dimensions. These providers can specify a comma-delimited list of cubes to be joined in the FROM clause.

Note It is not necessary to specify the relationship between cubes in order to perform a join because joining is always by common dimensions (dimensions with the same name).

The semantics of joining by common dimensions, called a natural join, are as follows:

  1. Two cubes are said to have common dimensions if and only if the dimensions have the same DIMENSION_UNIQUE_NAME. For more information, see “Multiple Hypercubes vs. True Multicubes” in Chapter 2.

  2. Let the notation X(A, B, C) indicate a cube X which is composed of dimensions A, B, and C.

  3. Let X(A, B, C) and Y(C, D, E) be two cubes with the common dimension C.

  4. If X.C and Y.C do not have an identical set of members, then X and Y cannot be joined.

  5. Assuming X.C and Y.C have an identical set of members: The natural join of X and Y results in a cube XY(A, B, C, D, E) such that the dataset
    SELECT XY.A ON COLUMNS, XY.B ON ROWS, XY.C ON PAGES
WHERE (D.ALL, E.ALL)

    is the same as the cube X(A, B, C).

    The dataset

    SELECT XY.C ON COLUMNS, XY.D ON ROWS, XY.E ON PAGES
WHERE (A.ALL, B.ALL)

    is the same as the cube Y(C, D, E).

  6. The natural join operation associates and commutes (except for the dimension order).

Even though the above rules defined semantics for cubes with three dimensions each and one dimension common, the generalization to other cases is straightforward.

The method of joining cubes described here is similar to a NATURAL JOIN in a relational database system. Vendors can extend the language to support other types of joins.


Empty FROM Clause

Some providers derive the cube implicitly from the specified axis and slicer dimensions. For such providers, there is no need to specify a cube name in the FROM clause. Support for implicitly deriving a cube name from the rest of MDX is an optional feature: It is not required for compliance with OLE DB for OLAP.

To find out whether a data source supports this feature, consumers can check to see if the MDPROPVAL_MJC_IMPLICITCUBE bit is set in the data source property MDPROP_MDX_JOINCUBES.


Default Hierarchy and Member

Each dimension has a default hierarchy associated with it and each hierarchy may have a default member. These defaults are used in the following scenarios:

  • A query specifies that a dimension will be positioned on one of the axes, but does not choose any members from that dimension to occur along the specified axis. For example, the Time dimension is along the x-axis, but no members have been specified. When this happens, the provider chooses a hierarchy and member as the defaults and places these defaults on the axis.

  • A query indicates that a dimension be a slicer dimension, but does not choose a member as the slicer. When this happens, the provider uses a default slicer, which yields the default member.

  • IMDCell::FindCell does not specify a member for one of the constituent dimensions of an axis. When this happens, the provider uses the default member for that dimension.

  • IMDFind::FindTuple does not specify a member for a constituent dimension on the axis. When this happens, the provider uses the default member for that dimension.

The DIMENSIONS rowset has a column called DEFAULT_HIERARCHY that indicates the default hierarchy for a dimension. The HIERARCHIES rowset has a column called DEFAULT_MEMBER that indicates the default member of the hierarchy. Also, the expressions dimension.DEFAULTMEMBER and hierarchy.DEFAULTMEMBER may be used to explicitly select the default member of a dimension or hierarchy.

Note The default member of a dimension is just the default member of the default hierarchy of that dimension.

OLE DB for OLAP does not require that every dimension of every hierarchy have a default member. Applications must check to see if a default exists before using it in an expression.


Types of Expressions in MDX

An MDX statement contains expressions that operate on sets, tuples, members, numbers and strings. These expressions form the basis of OLE DB for OLAP’s ability to provide a rich and powerful syntax for specifying datasets.

Expressions in MDX operate on the following basic entities:

  • Number. A number can be an exact numeric such as integer or decimal, or an approximate numeric such as a float or double. You can compose numbers by the usual operators of addition, subtraction, multiplication, and division to yield other numbers. These compositions constitute numeric value expression elements.

  • String: A string is a sequence of characters.

  • Member. This represents a member of a dimension. A member value expression can operate upon a member element, which yields other member elements. For example, Boston.Parent yields the parent of the member Boston, which is Massachusetts.

  • Tuple. A tuple is a collection of members from different dimensions. For example, (Boston, [1995]) is a tuple formed by members of two dimensions: Geography and Time. A single member is a degenerated case of a tuple and can be used as an expression without the parentheses.

  • Set. A set is a collection of tuple elements. A set value expression operates on member, tuple, and set elements to yield other sets. A numeric function operates on a set element and yields a number. For example, the function SUM({USA, France}, Sales) operates on the set {USA, France} and yields the sum of Sales. A tuple value expression operates on a set element and yields tuple elements.


Set Value Expressions

For any expression examples in the following sections, SampleSet is defined to be

{USA, Buffalo, France, NYC, London, California, LA, Nice, UK, Paris}

with sales data as indicated in this table:

Location | 1995 sales | 1996 sales
UK | 1900 | 1700
London | 250 | 300
France | 2500 | 2500
Paris | 365 | 250
Nice | 27 | 100
USA | 5000 | 6500
NYC | 900 | 1100
Buffalo | 300 | 200
California | 2000 | 3500
LA | 500 | 900

<dimension>.MEMBERS

The set of all members in dimension.

For example,

Geography.MEMBERS

is the set of all members in the Geography dimension.

<hierarchy>.MEMBERS

The set of all members in hierarchy.

For example,

Time.Quarterly.MEMBERS

is the set of all members in the Quarters hierarchy of the Time dimension.

<level>.MEMBERS

All members at a given level in a dimension.

<member>.CHILDREN

All children of member.

DESCENDANTS(<member>, <level> [, <desc_flags>])

All descendants of member at level. By default, only members at the specified level will be included. This corresponds to a desc_flag value of SELF. By changing the value of desc_flag, the consumer can include or exclude descendants at level, the children before level, and the children after level (until the leaf).

For example,

DESCENDANTS(USA, Counties)

yields all Counties in USA, and is the same as

DESCENDANTS(USA, Counties, SELF)

The statement

DESCENDANTS(USA, Counties, BEFORE)

yields all states in USA. If another level exists between Countries and States, members from that level will also be returned.

And the statement

DESCENDANTS(USA, Counties, AFTER)

yields all cities in USA. If another level exists after Cities, members from that level will also be returned.

DESCENDANTS(USA, Counties, BEFORE_AND_AFTER)

yields all states and cities in USA.

ORDER(<set>, {<string_value_expression> | <numeric_value_expression>}
[, ASC | DESC | BASC | BDESC])

There are two varieties of ORDER: hierarchized (ASC or DESC) and nonhierarchized (BASC or BDESC where “B” indicates “Break hierarchy”). The hierarchized ordering first arranges members according to their position in the hierarchy. Then it orders each level. The nonhierarchized ordering arranges members in the set without regard to the hierarchy. In the absence of an explicit specification, ASC is the default.

ORDER(SampleSet, ([1995], Sales), DESC)

hierarchizes all members and sorts each level according to Sales. Sales are compared at the highest level when constructing the sorted list. Therefore, if the sum of Sales in all California cities is less than the sum of Sales in all New York cities, California and California.LA will appear below NYC in the sorted, descending list.

The result of

ORDER(SampleSet, ([1995], Sales), DESC)

is as follows.

Location |  |  | 1995 sales
USA |  |  | 5000
 | California |  | 2000
 |  | LA | 500
 |  | Buffalo | 300
 |  | NYC | 900
France |  |  | 2500
 |  | Paris | 365
 |  | Nice | 27
UK |  |  | 1900
 |  | London | 250

ORDER(SampleSet, ([1995], Sales), BDESC)

sorts the members according to their values without regard for their relative positions in the member hierarchy. In this example, numeric values are sorted by 1995 sales per city, including aggregate sales values by state and country.

The result of

ORDER(SampleSet, ([1995], Sales), BDESC)

is as follows.

Location | 1995 sales
USA | 5000
France | 2500
California | 2000
UK | 1900
NYC | 900
LA | 500
Paris | 365
Buffalo | 300
London | 250
Nice | 27

Note When the input set has 2 elements for which the string_value_expression or numeric_value_expression has the same value, then the input order is preserved. For example if the sales for USA & Europe is 300 each, and that for Asia is 100, then the call:

ORDER({USA, Europe, Asia}, Sales, BASC)

returns the set {Asia, USA, Europe}, and not the set {Asia, Europe, USA}.

HIERARCHIZE(<set>)

Hierarchizes the set. Members in a level are sorted in their natural order, the default ordering of the members along a dimension when no other sort conditions are specified. HIERARCHIZE will always retain duplicates.

HIERARCHIZE(SampleSet)

returns the set in natural order. The hierarchized dataset follows (assuming that the natural order for the data source is alphabetical):

France |  |
 |  | Nice
 |  | Paris
UK |  |
 |  | London
USA |  |
 | California |
 |  | LA
 |  | Buffalo
 |  | NYC

TOPCOUNT(<set>, <index> [, <numeric_value_expression>])

Sorts on the numeric value expression (if any) and picks up the top index items. This function always breaks the hierarchy. The BOTTOMCOUNT function is similar.

TOPPERCENT(<set>, <percentage>, <numeric_value_expression>)

Sorts on numeric_value_expression and picks up the top x (the smallest number possible) elements such that their percentage of the result of numeric_value_expression is at least percentage. This function always breaks the hierarchy. The BOTTOMPERCENT function is similar.

TOPSUM(<set>, <value>, <numeric_value_expression>)

Sorts on numeric_value_expression and picks up the top x (the smallest number possible) elements such that their sum is at least value. This function always breaks the hierarchy. The BOTTOMSUM function is similar.

FILTER(<set>, <search_condition>)

Filters set based on search_condition.

For the expression

FILTER(SampleSet, (Sales,[1996]) < (Sales, [1995]))

the following result is returned:

{UK, Paris, Buffalo}

UNION(<set1>, <set2> [, ALL])

Joins the two input sets by union, eliminating duplicates by default. The ALL flag keeps duplicates in the joined set. Duplicates are deleted from the tail. The expression {set1, set2} does not remove duplicates.

Another way of joining by union is to use a comma-separated list of sets within braces. For example,

{USA.CHILDREN, CANADA.CHILDREN}

is an implicit union and is equivalent to

UNION(USA.CHILDREN, CANADA.CHILDREN, ALL)

Duplicated members are always retained in an implicit union.

DISTINCT(<set>)

Deletes duplicate tuples from a set. Duplicates are deleted from the tail.

INTERSECT(<set1>, <set2> [,ALL])

Finds the intersection of two input sets. By default, duplicates are eliminated from both sets prior to intersection. The optional ALL retains duplicates. Because there are several ways for ALL to work, it is worth mentioning the algorithm: Nonduplicated elements are intersected as usual. For each duplicate in set1, match it with a duplicate in set2, if one exists, and keep matching duplicates in the intersected set.

EXCEPT(<set1>, <set2> [,ALL])

Finds the difference between two sets. Duplicates are eliminated from both sets prior to finding the difference. The optional ALL flag retains duplicates. Matching duplicates in set1 are eliminated and non-matching duplicates are retained.

CROSSJOIN(<set1>, <set2>)

Generates the cross product of the input sets. The order of tuples in the resulting set is as follows:

If set1 = {x1, x2,…,xn} and set2 = {y1, y2, …, yn}, then CROSSJOIN(set1, set2) is:

{(x1, y1), (x1, y2),...,(x1, yn), (x2, y1), (x2, y2),...,
(x2, yn),..., (xn, y1), (xn, y2),..., (xn, yn)}

EXTRACT(<set>, <dimension>[, <dimension>...])

This is the opposite of the CROSSJOIN function. The resulting set consists of tuples from the extracted dimension elements. This function always removes the duplicates.

GENERATE(<set1>, <set2> [,ALL])

This is best explained with an example:

GENERATE({USA, France}, DESCENDANTS(Geography.CURRENTMEMBER, Cities))

This will repeatedly go through for each member of the set {USA, France} and apply the expression DESCENDANTS(Geography.CURRENTMEMBER, Cities). Each such application results in a set. (Application to USA will generate the set of all cities in USA; application to France will generate all cities in France.) These sets are joined by union to get the result of this function. In this example, all cities in USA and France will be the result. In general, GENERATE(<set1>, <set_expression>) will apply the set expression to each member of set1 and join the results by union.

If set1 is not related to set_expression by means of CURRENTMEMBER, then GENERATE results in a simple replication of the set implied by set_expression, with as many replications as there are tuples in set1. If the optional ALL flag is specified, then all duplicates are retained in the result. If ALL is not specified, then duplicates are removed. For example,

GENERATE({USA, FRANCE}, {SEATTLE, BOSTON}, ALL)

will result in

{SEATTLE, BOSTON, SEATTLE, BOSTON}

However, if ALL was not specified, then the result is

{SEATTLE, BOSTON}


Member Value Expressions

<member>.{PARENT | FIRSTCHILD | LASTCHILD | PREVMEMBER | NEXTMEMBER}

PREVMEMBER gives the previous member along the level implied by member.

NEXTMEMBER gives the next member along the level implied by member.

FIRSTCHILD. The children of any member are arranged along an implicit "natural" order, and FIRSTCHILD returns the first child of a member according to this order.

LASTCHILD gives the last child of a member according to the natural order.

PARENT gives the parent of a member.

<member>.LEAD(<index>)

Gives the member that is index positions away along the member’s dimension, in the dimension’s natural order. The numbering of the positions is zero-based.

The function <member>.LAG is similar, except that it looks in the opposite direction. If index is negative, then LEAD becomes LAG and vice versa.

<dimension>[.CURRENTMEMBER]

Gives the current member along dimension. The concept of “currency” occurs in functions such as GENERATE where the semantics include some form of iteration through members of a set.

<member>.{FIRSTSIBLING | LASTSIBLING}

Returns the first/last sibling at the parent level the of member. For example,

May.FIRSTSIBLING

returns April.

The expression

May.LASTSIBLING

returns June (assuming that the parent level is quarters).

ANCESTOR(<member>, <level>)

Returns the ancestor of member at the specified level.

COUSIN(<member>, <ancestor_member>)

This function is best illustrated by an example:

COUSIN([1996].March, [1994])

yields the member [1994].March.


Tuple Value Expressions

<set>.CURRENTMEMBER

Gives the current tuple in set. The concept of “currency” occurs in functions such as GENERATE where the semantics include some form of iteration through tuples of a set.

<set>[.ITEM](<string_expression> [, <string_expression>...] | <index>)

Returns a tuple from set. The <string_expression> [, <string_expression>...] specifies which tuple to retrieve. The index value specifies the position of the tuple in set. The numbering of the tuples is zero-based.


Numeric Functions

SUM(<set>[, <numeric_value_expression>])

Sums set based on an optional numeric value expression.

For example,

SUM({USA, FRANCE}, Sales.VALUE)

will return the sum of sales for USA and France. This can also be written as

SUM({USA, FRANCE}, Sales)

which is a more intuitive way. The production <numeric_value_expression> ::= <tuple>[.VALUE] is used, and the optional VALUE keyword is left out.

If a numeric value expression is not specified, then it is implied by the other coordinates that appear in the MDX statement. For example, in the statement

WITH MEMBER Geography.NewMember AS SUM({USA, France})
SELECT NewMember ON COLUMNS,
         Quarters.MEMBERS ON ROWS
   FROM SalesCube
WHERE ([1991], [Salesperson].[All], Sales, [Products].[All])

the ROWS expression provides a coordinate from the Quarters dimension. The WHERE clause provides the other four coordinates from the Years, Products, Salesperson, and Measures dimensions. These coordinates together identify a value in the cube for USA. They also identify a value for France. The SUM function adds these two values.

AGGREGATE(<set>[, <numeric_value_expression>])

This function uses the “proper” aggregate function based on the context. For example, consider the following expression:

WITH MEMBER Geography.Total AS AGGREGATE({USA, France})
SELECT {Measures.SumSales, Measures.MaxSales} ON COLUMNS,
      {USA, France, Total} ON ROWS
FROM SalesCube
WHERE ([1991], [Products].[All], [Salesperson].[All],
      [Quarters].[All])

In this expression, the calculated member Total is displayed against the measure “SumSales” and the measure “MaxSales.” In the former case, the provider computes Total by adding (with SUM) and in the latter case, the provider computes Total by taking the maximum. What happens when the set argument contains members from the MEASURES dimension is provider-specific.

COUNT(<set>[, INCLUDEEMPTY])

Counts the number of tuples in set. The optional INCLUDEEMPTY flag includes empty cells in the count. For more information, see “Empty Cells."

AVG(<set>[, <numeric_value_expression>])

Computes the average of the tuples in set based on numeric_value_expression. Similar numeric functions are:

  • MEDIAN(<set>[, <numeric_value_expression>])

  • MIN(<set>[, <numeric_value_expression>])

  • MAX(<set>[, <numeric_value_expression>])

  • VARIANCE(<set>[, <numeric_value_expression>])

  • STDDEV(<set>[, <numeric_value_expression>])

  • RANK(<tuple>, <set>)

Note Each of these functions requires an implicit count of the number of cells, which does not include empty cells. In order to force the inclusion of empty cells, the application must use the COALESCEEMPTY() function. For more information, see “Empty Cells” in this chapter.


Time Series Functions

The set of time series functions in MDX provides a powerful tool for data analysis. The time series functions described in this section are, in fact, member, set, or numeric functions.

Even though these functions are called “time series functions,” they work equally well with any other dimension. This is because the semantics of these functions do not rely on the underlying dimension being the Time dimension. In fact, in many cases, there exist actual scenarios where they can be useful on other dimensions. However, by far their most common use is with the Time dimension, hence their name. The exceptions to this are the x TD (YTD, MTD, QTD, WTD) functions, which are applicable to the Time dimension only.

It is useful to clarify what we mean by “Time dimension.” This does not necessarily refer to a dimension whose name is “Time.” Rather, it refers to the dimension whose DIMENSION_TYPE property in the DIMENSIONS rowset is MD_DIMTYPE_TIME.

The time series functions operate on the dimension that is specified implicitly as part of their set, member or level arguments. However, these arguments are optional for many time series functions; so it is possible to invoke the function with no set, member or level arguments. In such a case, the provider applies this function to that dimension whose DIMENSION_TYPE property is MD_DIMTYPE_TIME. If there exist multiple dimensions whose DIMENSION_TYPE property is MD_DIMTYPE_TIME, then which one is chosen is provider specific.

Time Series Set Value Expressions

PERIODSTODATE([<level>[, <member>]]))

Within the scope of level, returns the set of periods on the level of member, starting with the first period and ending with member. If no level or member is specified, then the member value is Time.CURRENTMEMBER and level is the parent level of Time.CURRENTMEMBER. If a level is specified, then member is dimension.CURRENTMEMBER, where dimension is the dimension of level.

PERIODSTODATE(Quarter, [05-Sep-1997])

returns the set of days from the beginning of Quarter3. (This is the member at the Quarter level that is the ancestor of [05-Sep-1997]) through [05-Sep-1997].)

PERIODSTODATE(Year)

returns the set of members from the beginning of the year that is the ancestor of Time.CURRENTMEMBER, through Time.CURRENTMEMBER.

PERIODSTODATE()

returns the set of members from the beginning of the containing period of Time.CURRENTMEMBER to Time.CURRENTMEMBER. All the returned members are at the same level as Time.CURRENTMEMBER.

PERIODSTODATE(level, member) is the same as TOPCOUNT(Descendants(Ancestor(member, level), member.Level), 1):member

xTD([<member>])

A shortcut function to PERIODSTODATE that predefines the level argument to be Year (YTD), Month (MTD), Quarter (QTD), or Week (WTD). If no member is specified, then the default is Time.CURRENTMEMBER.

LASTPERIODS(<index>[, <member>])

Returns the set of index periods ending with member and starting with the member lagging index – 1 from member. This is the same as LAG(member, index – 1):member.

For example,

LASTPERIODS(5, [1991June])

returns the set

{[1991Feb], [1991Mar], [1991Apr], [1991May], [1991June]}

If member is not specified, then it is Time.CURRENTMEMBER.

Time Series Member Value Expressions

PARALLELPERIOD([<level>[, <index>[, <member>]]])

This function is similar to the COUSIN function, but is more closely related to Time series. It takes the ancestor of member at level (call it ancestor); then it takes the sibling of ancestor that lags by index, and returns the parallel period of member among the descendants of ancestor.

This function has the following defaults:

  • Default member value is Time.CURRENTMEMBER if level is not specified. Otherwise it is dimension.CURRENTMEMBER, where dimension is the dimension to which level belongs.

  • Default index is 1.

  • Default level is the level of the parent of member.

    This function is equivalent to:

    COUSIN(member,LAG(ANCESTOR(member,level),index)

    PARALLELPERIOD(Year,2, [96 Qtr 3])

    returns the member [94 Qtr 3].

    PARALLELPERIOD(Year,2)

    returns the parallel period of Time.CURRENTMEMBER, two years ago. That is, if Time.CURRENTMEMBER is [1993June], then the returned member is [1991June].

    PARALLELPERIOD(Year)

    returns the parallel period of Time.CURRENTMEMBER from last year. That is, if Time.CURRENTMEMBER is [1993June], then the returned member is [1992June].

    PARALLELPERIOD()

    returns the parallel period in the immediately prior sibling to the parent of Time.CURRENTMEMBER. For example, if Time.CURRENTMEMBER is [1993June], then the returned member is [1993March]. The parent of ([1993June] is Quarter2, whose immediately prior sibling is Quarter1, in which the parallel period is [1993March].

OPENINGPERIOD([<level>[, <member>]])

This function returns the first period among the descendants of member at level. For example, OPENINGPERIOD(Month, [1991]) returns [1991January]. If no member is specified, then the default is Time.CURRENTMEMBER. If no level is specified, then it is the level below that of member. This function is equivalent to TOPCOUNT(Descendants(,member, level), 1).

The function CLOSINGPERIOD is very similar, the only difference being it returns the last sibling instead of the first sibling.

Time Series Analysis (Numeric) Functions

These are functions pertaining to finding covariance, correlation, and linear regression by using the least squares method.

COVARIANCE(<set>, <numeric_value_expression>
[, <numeric_value_expression>])

CORRELATION(<set>, <numeric_value_expression>
[, <numeric_value_expression>])

Covariance and correlation are measures of linear association. Both functions evaluate set against the first numeric value expression to get the values for the y-axis. The set is evaluated against the second numeric value expression (if present) to get the set of values for the x-axis. If the second numeric value expression is not present, then the members of set are used as values for the x-axis.

The latter case does not make much sense for most dimensions (for example, SalesPerson). However, it does make sense for the Time dimension. Covariance and correlation are calculated for these x and y values.

Linear regression by using the least squares method calculates the equation of the best fit line for a series of points. For the purposes of this section, let the regression line be given by the equation

y = ax+b

where a is called the intercept and b is called the slope.

LINREGINTERCEPT(<set>, <numeric_value_expression>
[, <numeric_value_expression>])

LINREGSLOPE(<set>, <numeric_value_expression>
[, <numeric_value_expression>])

LINREGVARIANCE(<set>, <numeric_value_expression>
[, <numeric_value_expression>])

LINREGR2(<set>, <numeric_value_expression>
[, <numeric_value_expression>])

Evaluates set against the first numeric value expression to get the set of values for the y-axis. Evaluates set against the second numeric value expression (if present) to get the set of values for the x-axis. If the second numeric value expression is not present, then it uses the members of set as values for the x-axis.

The latter case does not make much sense for most dimensions (for example, SalesPerson). However, it does make sense for the time dimension.

After obtaining the set of points, LINREGINTERCEPT returns the intercept of the regression line (b in the equation above). LINREGSLOPE returns the slope of the regression line (a in the equation above). LINREGVARIANCE returns the statistical variance that describes the fit of the linear equation to the points, and LINREGR2 returns the statistical R2 that describes the fit of the linear equation to the points.

LINREGPOINT(<numeric_value_expression>, <set>,
<numeric_value_expression> [, <numeric_value_expression>])

Uses the last three arguments — set and numeric_value_expression — like the other LINREG xxx functions to calculate the regression line. Now, it evaluates the first argument (numeric_value_expression) and uses the resulting number as the “x” value in the regression equation (y = ax + b) to calculate the “y” value.

For example, the dimension “Campaign” contains members that represent the various advertising campaigns. There are two measures associated with each campaign — the cost of the campaign and the revenues resulting from the campaign. To find the correlation between the cost of the campaign and the revenues that it generates, you use the regression line y = ax + b, where x is the cost and y is the revenue. The a and b values are calculated by using:

a = LinRegCoef(Campaigns.Members, Revenues, Cost)
b = LinRegConst(Campaigns.Members, Revenues, Cost)

For another example, you can find the trend in Sales for the sales of the last nine periods and forecast the next period:

a = LinRegSlope(LastPeriods(9), Sales)
b = LinRegIntercept(LastPeriods(9), Sales)
NextPeriod = LinRegPoint(10, LastPeriods(9), Sales)


WHERE Clause Overrides

Each individual set, member, tuple, or numeric function in an MDX statement always executes in the larger context of the entire statement. For example, consider the FILTER function in this expression:

SELECT FILTER(SalesPerson.MEMBERS, [1996].VALUE > 500) ON COLUMNS,
   Quarters.MEMBERS ON ROWS
FROM SalesCube
WHERE ([Geography].[All], [Products].[All], [1996], Sales)

The second argument of FILTER, “[1996].VALUE,” does not contain enough information by itself. You need six coordinates — one from each of the six dimensions — to determine VALUE. The argument contains only one coordinate, from the Years dimension. But in the context of the entire SELECT statement, the other coordinates are available. You can get [Geography].[All], [Products].[All], and [Sales] as three coordinates from the WHERE clause. Two others, one from the SalesPerson dimension and another from the Quarters dimension, are available from the axis expressions for the COLUMNS axis and the ROWS axis, respectively.

A special case arises when a coordinate is specified in both the WHERE clause and within the expression. For example, suppose an application calls for a dataset that, on the COLUMNS axis, contains 1996 budgeted sales for all the states in the United States which had more than 500 units of ActualSales in 1995, and on the ROWS axis, the Quarters. This dataset can be created by the following statement:

SELECT FILTER({USA.CHILDREN}, ([1995], ActualSales) > 500) ON COLUMNS,
   Quarters.MEMBERS ON ROWS
FROM SalesCube
WHERE ([1996], BudgetedSales, [Products].[All], [SalesPerson].[All])

As the FILTER function is evaluated for each state in the United States, it already has the coordinates ([1996], BudgetedSales) from the WHERE clause. However, it receives the coordinates ([1995], ActualSales) from the FILTER function. To avoid potential conflict, the argument of the FILTER function takes precedence. In general, any coordinates obtained from the WHERE clause are overridden by coordinates that are specified within an axis specification.


Creating a Cell Within the Context of a Cube

For certain applications, it may be desirable to return data for a single cell within a cube. For example, executives may have a decision support application written in Microsoft Excel that uses data from a multidimensional data source. At the top of the main window when the application starts every day, they want to view the quarter-to-date worldwide sales for the current year across all products and customers.

The solution is to create a dataset for which all dimensions are slicer dimensions. The MDX statement for doing this take the following form:

SELECT FROM cube_name WHERE slicer_specification

This results in a dataset with exactly one cell. Because no axis dimensions are specified, the slicer specification zeroes in on the desired point in the entire cube.

Note that in this case where there are no axes and hence only one cell:

  • GetAxisInfo returns 0 for *pcAxes and a null pointer in *prgAxisInfo.

  • The axis rowsets for all axes will be empty, except for the axis MDAXIS_SLICERS. The slicers axis will contain information on the slicer conditions that created the single cell.

  • The single cell can be addressed by the cell ordinal 0.


Querying on Property Values

The need to query on property values frequently arises in practical situations. The Products dimension has a list of various kinds of apparel that are being sold at a retail store chain. Suppose that the [Product Name] level of the Products dimension had three properties associated with it: Color, Type, and [Age Group].

To find the sales for children’s red T-shirts (=Type), use the following MDX statement:

SELECT [Measures].[Sales] ON COLUMNS,
   Filter(Product.MEMBERS, ([Product Name].[Color] = "Red" AND
                            [Product Name].[Age Group] = "Children" AND
                            [Product Name].[Type] = "Tshirt")) ON ROWS
FROM ...

In general, property[.VALUE] is a value expression primary and hence can be used anywhere a value expression can be used, including most set value expressions and numeric functions.


Using Outer References in a Query

In a multicube database, available data is segmented into various subcubes. For example, the data for actual sales and the data for budgeted sales may be in two subcubes. In such situations, it is useful to have the ability to reference a member (or in general, a tuple) from a cube that is not part of the FROM clause of an MDX statement.

In this example using Actual Sales and Budgeted Sales cubes, suppose you want the actual sales of all products which exceeded their budgeted sales. In this query, data is being retrieved from the Actual Sales cube, but there is a filter condition on a tuple from the Budgeted Sales cube. The MDX statement for this query looks as follows:

SELECT
FILTER(Products.MEMBERS, [Measures].[Sales] >
   [Budgeted Sales].[Measures].[Sales]) ON COLUMNS,
   Grography.MEMBERS ON ROWS
FROM [Actual Sales]
WHERE ([1997])

Note An outer reference in the FILTER function is made to the Sales member of the Measures dimension of the Budgeted Sales cube. The outer reference just refers to one coordinate of the Budgeted Sales cube. Like the other coordinates, they are derived from the MDX statement for the dimensions that are common between Actual Sales and Budgeted Sales cubes. The coordinate is the default member of that dimension for dimensions in the Budgeted Sales cube that are not in the Actual Sales cube.


Named Sets and Calculated Members

You can use MDX to specify a name for set expressions (named sets). You can also define formulas and treat the formula as a new member.

Named sets are useful because they can be used concisely in other MDX expressions. Creating formulas and treating them as members of a dimension is a very powerful feature and has a variety of applications. The following examples illustrate these concepts.


Example 1

On the ROWS axis, show the Actual Sales and percent change from last year’s sales for the product lines for which actual is less than budget. This should be sorted by Sales Last Year. On the COLUMNS axis, show the top two cities in the top two countries in the regions Europe and America.

The MDX statement that retrieves this dataset is

WITH MEMBER
   Measures.[% Change from Last Year Sales] AS Sales /
      (Sales, Year.PREVMEMBER)
SELECT GENERATE(
   GENERATE({America, Europe},
            TOPCOUNT(Geography.CURRENTMEMBER.CHILDREN, 2, Sales)),
   TOPCOUNT(Geography.CURRENTMEMBER.CHILDREN, 2, Sales)) ON COLUMNS,
   CROSSJOIN({Sales, [% Change from Last Year Sales]},
            ORDER(FILTER([Product Lines].MEMBERS,
               (Sales,Actual)<(Sales,Budget))
               (Sales, Year.PREVMEMBER))) ON ROWS
FROM SalesCube
WHERE ([1997], Actual)

The WITH MEMBER clause defines a new member called % Change from Last Year Sales. This appears on the Measures dimension (as indicated by the qualifier [Measures]). This member is specified by the formula Sales/(Sales, Year.PREVMEMBER). Within the context of the rest of the expression, this new member is evaluated for each value in the n-dimensional manifold selected by the dataset to yield the required result.


Example 2

On the ROWS axis, show the Actual Sales for the states {WA, OR, CA} as a percentage of the sales in the rest of the USA (except Hawaii and Alaska) for the following: All Products in the Applications Division, All Versions of GreatOffice and Database 96. On the COLUMNS axis, show all quarters and all months for 1997.

The MDX statement that retrieves this dataset is

WITH MEMBER
   USA.[North West Ratio] AS SUM({WA, OR, CA}, Sales) /
   SUM(EXCEPT(USA.CHILDREN, {Hawai, Alaska}), Sales)
SELECT HIERARCHIZE({[1997], [1997].CHILDREN, DESCENDANTS([1997],
                      Months)}) ON ROWS,
                   {[Applications Division] , GreatOffice.CHILDREN,
                      [Database 96] } ON COLUMNS
FROM SalesCube
WHERE ([North West Ratio], Actual)

The WITH MEMBER clause defines a new member called North West Ratio. This member is obtained by taking the percentage of the sum of Sales for the states OR, WA, and CA, and dividing this by the sales for all states in the USA except Alaska and Hawaii. By qualifying the name of this member with USA, the data source recognizes that this should be on the Geography dimension.


Formula Solve Order

When a dataset has multiple formulas, there can be a problem with the order of formula evaluation. To understand the problem, consider the MDX statement

WITH
   MEMBER [Year].[Annual Growth] AS ([1996] - [1997])
   MEMBER [Status].[Performance] AS (([Actual] - [Planned]) /
      [Planned] * 100)
SELECT
   {[1994], [1995], [Annual Growth]} ON COLUMNS,
   {[Actual], [Planned], [Performance]} ON ROWS
FROM SalesBitmap

This produces a dataset that looks like this:

The cell shaded in gray could be either Annual Growth of Performance or Performance of Annual Growth. If it is Annual Growth of Performance, then the value in the cell is 75 – 50 = 25. If it is Performance of Annual Growth, the value in the cell is (80 – 20)/20 * 100 = 300.

This shows the clear need to establish an order for applying the formulas. To do this, you assign a “solve order” to each formula. When two formulas are in conflict, the one with the lower solve order gets resolved first.

Solve orders are assigned for calculated members by using the solve_order_specification clause. This clause is part of member_specification. For the example above, to get Annual Growth of Performance, the MDX statement looks like this (the text in bold indicates the changes):

WITH
MEMBER [Year].[Annual Growth] AS
  ([1997] - [1996]), SOLVE_ORDER = 1
MEMBER [Status].[Performance] AS
  (([Actual] - [Planned]) / [Planned] * 100), SOLVE_ORDER = 2
SELECT
{[1996], [1997], [Annual Growth]} ON COLUMNS,
{[Actual], [Planned], [Performance]} ON ROWS
FROM SalesBitmap

To get Performance of Annual Growth, the MDX statement looks like this (the text in bold indicates the changes):

WITH
MEMBER [Year].[Annual Growth] AS
  ([1997] - [1996]), SOLVE_ORDER = 2
MEMBER [Status].[Performance] AS
  (([Actual] - [Planned]) / [Planned] * 100), SOLVE_ORDER = 1
SELECT
{[1996], [1997], [Annual Growth]} ON COLUMNS,
{[Actual], [Planned], [Performance]} ON ROWS
FROM SalesBitmap

The following rules define the semantics of SOLVE_ORDER:

  • When multiple expressions are in conflict, the statements with the lower solve orders get resolved first, followed by those with the higher solve order. What happens when conflicting expressions have the same solve order is provider-specific.

  • Solve orders must always be greater than or equal to 0.

  • The default solve order of a formula is 0.


Longer Lifetime and Scope: Named Sets and Calculated Members

The scope of the named sets and calculated members specified in the previous section is just the MDX statement in which they appear. Consequently, their lifetime is also limited to the life of the query execution.

It is frequently useful to have named sets and calculated members available for the life of a session, and visible to all queries in that session. When used with the UI-related set functions, named sets provide a powerful mechanism to create queries that not only preserve the state, but also provide an efficient method to process UI operations. For more information on how to do this, see “Preserving State During UI Operations” in this section.

To create a named set or a calculated member with longer scope and lifetime, use the following syntax:

create_formula_statement:= CREATE [scope] formula_specification

The syntax for formula_specification is exactly the same as the syntax used in creating named sets and calculated members. For information on syntax, see “Named Sets and Calculated Members” in this chapter. The create formula statement creates a named set or a named member.

The scope and lifetime of this created entity depends on the value of scope. There are two possible values of scope:

  • GLOBAL. The created entity will be visible for all queries across all sessions. Its lifetime is until an explicit drop formula statement is issued.

  • SESSION. The created entity will be visible for all queries within the session. Its lifetime is the same as that of the session within which it is created. When the session dies, the entity will be automatically dropped.

As an example, the MDX statement below creates the same dataset that is specified in “Cube and Dataset Example”:

CREATE SalesCube.ColumnsAxisSet AS CROSSJOIN({Venkatrao, Netz},
   {USA_North.CHILDREN, USA_South, Japan})
CREATE SalesCube.RowsAxisSet AS {Qtr1.CHILDREN, Qtr2, Qtr3,
   Qtr4.CHILDREN}
SELECT ColumnsAxisSet ON COLUMNS, RowsAxisSet ON ROWS
FROM SalesCube
WHERE (Sales, [1991], Products.All)

The following rules determine the semantics of named sets and members that are created by using create_formula_statement:

  • If a scope is not specified, then it defaults to SESSION. In the above example, both ColumnsAxisSet and RowsAxisSet have a scope of SESSION.

  • The name of the set or member being created must be qualified by the name of the cube. The absence of such a qualification is a syntax error. This is because there is no inherent cube context associated with a session. Therefore, the member or the set name has to provide the cube context. Thus, in the above example, the sets ColumnsAxisSet and RowsAxisSet were qualified by the cube name, SalesCube.

  • Two SESSION scope named sets/members with the same name can exist on the same cube, as long as they are in two different sessions. This is because the scope of each is the session that created it . They are not visible outside the session.

  • Named sets and members created with SESSION scope can be explicitly dropped during the session that created them.

  • It is an error to drop a named set/member when it is being used by a query.


Visibility in Schema Rowsets

Calculated members and named sets are not visible in schema rowsets, regardless of whether they are SESSION scope or GLOBAL scope.

The MDX set operations — such as dimension.MEMBERS, hierarchy.MEMBERS, level.MEMBERS — do not put any calculated members in the resulting set.


Set Functions for Expressing UI Operations

For applications that have a graphical user interface for displaying multidimensional schemas and datasets, drill down and drill up operations are commonly exposed in the UI. Drilling operations can take place on a per-member basis or on a per-level basis. For example, “Take the set {USA, France}, and give me a set that contains USA, France, and all the children of USA” drills down by member. On the other hand, “Take the set {USA, France} and give me a set that contains USA, France, and all the cities in USA” drills down by level. Any of these drilling operations can be combined with a top or a bottom condition. For example, “Take the set {USA, France}, and give me a set that contains USA, France, and the top 3 children of USA based on Sales.”

MDX has a rich set of set functions that encapsulate these UI operations.


Drilling by Member

To drill down by a member, use the DRILLDOWNMEMBER function.

DRILLDOWNMEMBER(<set1>, <set2>[, RECURSIVE])

Drills down members in set1 that are present in set2. If RECURSIVE is not specified, then each member of set2 is checked against the original set1 and if it is present it is drilled down. If RECURSIVE is specified, then each member of set2 is checked against the result of the previous application of a member from set2. Typically, set2 is a subset of set1 in the invocation of this function.

For example, the call

DRILLDOWNMEMBER({USA, France, Canada}, {USA, Washington, Canada})

returns the set

{USA, <all states in USA>, France, Canada, <all provinces in Canada>}

The member “Washington” from set2 had no effect since it does not occur in set1. Note that the members that result from the drill down are hierarchized to occur under their parents.

If the optional RECURSIVE flag is used in the above call, as shown here

DRILLDOWNMEMBER({USA, France, Canada}, {USA, Washington, Canada},
   RECURSIVE)

then the following set is returned:

{USA, <all states USA before Washington>, Washington,
<all cities in Washington>, <all states in USA after Washington>,
France, Canada, <all provinces in Canada>}

This is because the following process takes place:

  1. The member “USA” from set2 is inspected to see if present in set1. It is present. Therefore, it is drilled down to yield the set that has “USA,” all states in USA, “France,” and “Canada.” Call this set set1 result1.

  2. The second member of set2, “Washington,” is inspected to see if present in set1 result1. It is. Therefore, “Washington” is drilled down to yield all cities in Washington, and a union is formed with the result and set1 result2.

  3. The third member of set2, “Canada,” is inspected to see if present in set1 result2. It is. Therefore, it is drilled down to get all the provinces in Canada, and a union is formed with this result and set1 result2 to get the final set.

    Note that this is sensitive to the order of the members in set2. If set2 were {Washington, USA, Canada}, then the result would not contain all the cities in Washington. This is because set1 is checked to see if it contains “Washington” first. It does not at this stage because USA has not been drilled down yet.

    A typical usage scenario for an application is: set1 is the set of members from a given dimension on a given axis. (Note that set1 may itself be the result of an expression.) The user clicks the plus (+) sign next to a member m of set1 in the UI. The application now calls DRILLDOWNMEMBER(set1, {m}) to drill down on m. More sophisticated UIs can expose options for recursive drill downs, drill downs on multiple members, and so on.

    The set returned by this function is described in the following algorithm:

  4. Let set3 equal set1, the empty set.

  5. In the FOR statement, let X = set3 if RECURSIVE and let X = set1 if not.

  6. For each member m of set2, and for each occurrence of m in the set X:

    a. Let p be the member immediately after m in set X.

    b. If p is a descendant of m, then break.

    c. Otherwise, let set3 = HIERARCHIZE(Union(set3, m.CHILDREN))

  7. Return set3.

    In the above algorithm, the BREAK command pops out of the IF clause and begins another iteration.

The DRILLDOWNMEMBERTOP function is a variation of DRILLDOWNMEMBER. Instead of including all children (or descendants) of a member, it just includes the top n children (or descendants). The syntax of this function is:

DRILLDOWNMEMBERTOP(<set1>, <set2>, <index>
[, [<numeric_value_expression>][, RECURSIVE]])

The desired number of children (or descendants) is specified by index, and numeric_value_expression gives the criterion. For example, the call

DRILLDOWNMEMBERTOP({USA, France, Canada}, {USA, Washington, Canada},
   5, Sales)

returns the set

{USA, <top 5 states in USA based on Sales>, France, Canada,
<top 5 provinces in Canada based on Sales>}

Use of the RECURSIVE flag has an effect similar to its effect in DRILLDOWNMEMBER.

For example, the call

DRILLDOWNMEMBERTOP({USA, France, Canada}, {USA, Canada}, 5, Sales,
   RECURSIVE)

returns the set (assuming the top 5 states in USA were California, Texas, Florida, Georgia, and Washigton)

{USA, California, Texas, Florida, Georgia, Washington,
<top 5 cities in Washington based on sales>, France, Canada,
<top 5 provinces in Canada based on Sales>}

The function DRILLDOWNMEMBERBOTTOM is similar, except that the bottom condition is applied instead of the top condition.

To drill up by a member, use the DRILLUPMEMBER function. The syntax is:

DRILLUPMEMBER(<set1>, <set2>)

Drills up the members in set1 that are present in set2. As in the case of DRILLDOWNMEMBER, set2 is usually a subset of set1 in the invocation of this function.

For example, the call

DRILLUPMEMBER({USA, Alabama, Alaska, France, Canada, Ontario, Quebec},
   {USA, Canada})

returns the set

{USA, France, Canada}

Note that DRILLUPMEMBER does not have a RECURSIVE flag. This is because when a member is drilled up, it is common to have all the descendants (not just the immediate children) removed. In other words, drilling up is usually recursive. Therefore, the call

DRILLUPMEMBER({USA, California, Los Angles, Washington, Seattle,
   King County, France, Canada}, {USA})

returns the set

{USA, France, Canada}

As a result, all descendants of USA — California, Los Angeles, Washington, Seattle, King County — have been drilled up, even though only California and Washington are the direct children of USA.

The set returned by this function is described by the following algorithm:

  1. For each member m of set2 and for each occurrence of m in set1, remove from set1 all the descendants of m that directly follow m.

  2. Return set1.

The function TOGGLEDRILLSTATE is a combination of DRILLDOWNMEMBER and DRILLUPMEMBER. The syntax of this function is:

TOGGLEDRILLSTATE(<set1>, <set2>[, RECURSIVE])

Toggles the drill state of each member of set2 that is present in set1. If a member m of set2 that is present in set1 is drilled down (that is, has a descendant), then DRILLUPMEMBER(set1, {m}) is applied. If it is drilled up (that is, there is no descendant of m that immediately follows m), then DRILLDOWNMEMBER(set1, {m}[, RECURSIVE]) is applied to set1. The optional RECURSIVE flag is used if TOGGLEDRILLSTATE was called with RECURSIVE.


Drilling by Level

DRILLDOWNLEVEL(<set1> [, <level>]])

To drill down by a level, the DRILLDOWNLEVEL function is used. This function drills down members in set1, at the level specified by level, to one level below.

For example, the call

DRILLDOWNLEVEL({USA, California, Washington, France, Canada, Ontario,
   Quebec}, States)

returns the set

{USA, California, <all cities in California>, Washington,
<all cities in Washington>, France, Canada, Ontario,
<all cities in Ontario>, Quebec, <all cities in Quebec>}

Note that the members that result from the drill down are hierarchized to occur under their parents. Among the original members in set1, order is preserved. Thus, in the above example, France still occurs after USA and Canada still occurs after France.

If level is not specified, then it is assumed to be the level of the lowest level member of set1.

For example, the call

DRILLDOWNLEVEL({North America, USA, California, Washington, France,
   Canada})

is equivalent to the call

DRILLDOWNLEVEL({North America, USA, California, Washington, France,
   Canada}, States)

When there is no member at the specified level in set1, this call has no effect.

For example, the call

DRILLDOWNLEVEL({USA, California, Washington, France, Canada, Ontario,
   Quebec}, Cities)

returns the original set unchanged.

For example, in a typical application, set1 is the set of members from a given dimension on a given axis. (Note that set1 may itself be the result of an expression.) The UI has a way to specify “drill down to level” — for example, by using a drop-down list box of levels. In response to a user selection, the application calls DRILLDOWNLEVEL with set1 and the level specified by the user.

The set returned by this function is described in the following algorithm:

  1. If level is not specified, then let level be the level of the lowest level member in set1.

  2. Let set2 = set1.

  3. For each member m in set1:

    a. if m is at level, and

    b. if there are no descendants of m immediately following m,

    then set2 = UNION(set2, m.CHILDREN, ALL).

  4. Return set2.

DRILLDOWNLEVELTOP(<set>, <index>[, [<level>]
[, <numeric_value_expression>]])

The function DRILLDOWNLEVELTOP is a variation of DRILLDOWNLEVEL. Instead of including all children of a member at the specified level, it just includes the top n children.

For example, the call

DRILLDOWNLEVELTOP({USA, California, Washington, France, Canada,
Ontario, Quebec}, 5, States, Sales)

returns the set

{USA, California, <top 5 cities in California based on sales>,
Washington, <top 5 cities in Washington based on sales>, France,
Canada, Ontario, <top 5 cities in Ontario based on sales>, Quebec,
<top 5 cities in Quebec based on sales>}

As another example, the call

DRILLDOWNLEVEL({North America, USA, California, Washington, France,
   Canada}, 5, States, Sales)

results in the set

{USA, California, <top 5 cities in California>, Washington,
<top 5 cities in Washington>, France, Canada}

The function DRILLDOWNLEVELBOTTOM is similar, except that the bottom condition is applied instead of the top condition.

DRILLUPLEVEL(<set1>[, <level>])

To drill up by level, the DRILLUPLEVEL function is used.

This function drills up members in set1 that are below the specified level. If level is not specified, it is assumed to be one less than the level of the lowest level member in set1.

For example, the call

DRILLUPLEVEL({USA, California, <all cities in California>, Washington,
<all cities in Washington>, France, Canada, Ontario,
<all cities in Ontario>, Quebec, <all cities in Quebec>}, States)

returns the set

{USA, California, Washington, France, Canada, Ontario, Quebec}

If the level in the above call were changed from States to Countries, then the resulting set would be

{USA, France, Canada}

The set returned by this function is described by the following algorithm:

  1. For each member m in set1, if m is below level, then remove m from set1.

  2. Return set1.
    14. Note the following about DRILLDOWNLEVEL and DRILLUPLEVEL:
      1. Calling DRILLUPLEVEL without a level parameter on a set that has only the root level members has no effect.

      2. Calling DRILLDOWNLEVEL without a level parameter has no effect when the input set has all non-leaf members drilled down to their leaf level descendants.


Preserving State During UI Operations

Many graphical user interface (GUI) OLAP tools do the following:

  • Create a dataset based on some initial criteria.

  • Enable the user to “operate” on the dataset by using UI operations such as drill down and drill up.

The brute-force way to accommodate such UI operations is to recreate the MDX query by going into the axis expressions. This is not only cumbersome, but can also be inefficient. MDX provides a powerful and simple alternative.

The idea is to represent each axis expression as a named set. When the user does roll upand drill downs, functions such as DRILLDOWNMEMBER, DRILLDOWNLEVEL, or DRILLDOWNMEMBER can be used on the named set. That way, the application does not have to peek into the axis expression and modify it.

For example, consider the dataset described in the section “Cube and Dataset Example” of chapter 2. By using named sets, the MDX statement for this dataset looks like this:

CREATE SalesCube.ColumnsAxisSet AS
  CROSSJOIN({Venkatrao, Netz},
  {USA_North.CHILDREN, USA_South, Japan})
CREATE SalesCube.RowsAxisSet AS
  {Qtr1.CHILDREN, Qtr2, Qtr3, Qtr4.CHILDREN}
SELECT ColumnsAxisSet ON COLUMNS, RowsAxisSet ON ROWS
FROM SalesCube
WHERE (Sales, [1991], Products.All)

Now, suppose that the user drills down on Qtr2. This operation can be expressed easily by using the following MDX statement (which occurs in the same session, so the named sets ColumnsAxisSet and RowsAxisSet are still visible and alive):

SELECT
  DRILLDOWNMEMBER(ColumnsAxisSet, {Qtr2}) ON COLUMNS,
  RowsAxisSet ON ROWS
FROM SalesCube
WHERE (Sales, [1991], Products.All)

Now the user drills up on Qtr1, which can be expressed by

SELECT
  DRILLUPMEMBER(DRILLDOWNMEMBER(ColumnsAxisSet,
    {Qtr2}), {Qtr1}) ON COLUMNS,
    RowsAxisSet ON ROWS
FROM SalesCube
WHERE (Sales, [1991], Products.All)

Essentially, using named sets provides a way to preserve the state of the dataset when it was created. Further operations on the dataset can be modeled as set function calls on these named sets.


Conditional Expressions

MDX has conditional clauses of several types, with the different types steadily increasing in functionality and complexity.


If Clause

The if clause, iif, can be used to test any search condition and choose one value or another based on whether the test is true or false. The BMF construct is:

<if_expression> ::= iif(<search_condition>, <true_part>, <false_part>)
<true_part> ::= <value_expression>
<false_part> ::= <value_expression>

The data type of true_part must be the same as the data type of false_part. That is, either both are numeric value expressions or both are string value expressions.

The following example demonstrates the use of iif. The query displays actual sales, budgeted sales, and variance (that is, the difference between actual and budgeted sales) for Asia and Europe for each quarter of 1991, for all sales reps and all products. The conditional expression is used to keep the variance always positive.

WITH MEMBER [Measures].[Variance] AS iif ((Sales > BudgetedSales),
  (Sales - BudgetedSales), (BudgetedSales - Sales))
SELECT
  {Sales, BudgetedSales, Variance} ON COLUMNS,
  CROSSJOIN({Asia, Europe}, {Qtr1, Qtr2, Qtr3, Qtr4}) ON ROWS
FROM SalesCube
WHERE ([1991], [SalesPerson].[ALL], [Products].[ALL])


Simple Case Expression

The simple case expression takes the following form:

<simple_case> ::= CASE <case_operand>
                       <simple_when_clause>...
                       [ <else_clause> ]
                  END

<simple_when_clause> ::= WHEN <when_operand> THEN <result>

<else_clause> ::= ELSE <value_expression>

<case_operand> ::= <value_expression>

<when_operand> ::= <value_expression>

<result> ::= <value_expression>

This clause evaluates the case operand and returns a result based on its value, as specified by the WHEN clauses.

The following example demonstrates its use. This example requests the sales of each sales rep across all products and across all regions for FY97. Assume that there is a measure called PerfRating that contains a number that rates each salesperson. The number is from 1 to 5, with 5 being the best. To use an adjective that describes the rating, rather than a numerical code, the CASE statement maps each performance rating to a word(s) such as “Excellent” or “Very Poor.”

WITH MEMBER [Measures].[Performance] AS CASE [Measures].[PerfRating]
                                 WHEN 5 THEN "Excellent"
                                 WHEN 4 THEN "Very Good"
                                 WHEN 3 THEN "Good"
                                 WHEN 2 THEN "Poor"
                                 WHEN 1 THEN "Very Poor"
                                 ELSE "Unknown"
                                 END
SELECT {[Measures].[Sales], [Measures].[Performance]} ON COLUMNS,
      SalesPerson.MEMBERS ON ROWS
FROM SalesCube
WHERE ([1997], [Geography].[All], [Products].[All])

If there is no ELSE specified, and none of the when operands match the case operand, the result of the CASE is an empty cell value. For more information, see “Empty Cells” in this chapter.


Searched Case

In the simple case expression example, a measure called “PerfRating” exists, which contains a numerical rating from 1 to 5 for each salesperson. Assume that this rating is based on the percentage by which each salesperson beat her budgeted sales. Anything above 100% is a performance rating of 5; from 51% to 100% is 4, from 1% to 50% is 3, from -24% to 0% is 2, and -25% or less is 1.

The simple CASE statement is not capable of expressing this logic because the when operand of the simple WHEN clause is a value expression, rather than a search condition. In the searched case, the when operand is a search condition. Clearly, a case operand is no longer needed.

The construct of the searched case clause is:

<searched_case> ::= CASE
                    <searched_when_clause>...
                    [ <else_clause> ]
                    END

<searched_when_clause> ::= WHEN <search_condition> THEN <result>

The expansion of other nonterminals is the same as that for the simple case. The following example uses searched case to generate a performance rating for each sales rep, based on the logic explained above:

WITH MEMBER [Measures].[PerfTemp] AS
      ([Measures].[Sales] - [Measures].[BudgetedSales])/
      [Measures].[BudgetedSales]*100
WITH MEMBER [Measures].[PerfRating] AS
      CASE
         WHEN [Measures].[PerfTemp] > 100 THEN 5
         WHEN [Measures].[PerfTemp] > 50  THEN 4
         WHEN [Measures].[PerfTemp] > 0   THEN 3
         WHEN [Measures].[PerfTemp] > -25 THEN 2
         ELSE 1
      END
SELECT {[Measures].[Sales], [Measures].[PerfRating]}
      ON COLUMNS,
      SalesPerson.MEMBERS ON ROWS
FROM SalesCube
WHERE ([1997], [Geography].[All], [Products].[All])

Note that the WHEN clauses are evaluated in the order specified; that is, the value of CASE is the result of the first searched when clause whose search condition is TRUE. That happens when the second WHEN clause in the above example just checks to see if PerfTemp > 50, rather than checking if PerfTemp > 50 AND PerfTemp <= 100.

As with simple case, if no ELSE is specified, and the search conditions of all the WHEN clauses is FALSE, then the result of the CASE is an empty cell value. For more information, see “Empty Cells” in this chapter.

Finally, the simple case can be rewritten as a searched case. The simple case

CASE case_operand
 WHEN when_operand1 THEN result1
WHEN when_operand2 THEN result2
...
END

is equivalent to the following searched case:

CASE
WHEN case_operand = when_operand1 THEN result1
WHEN case_operand = when_operand2 THEN result2
...
END


Empty Cells

Usually cubes have empty data points (cells). For example (using SalesCube from the cube and dataset example), the SalesPerson “Venkatrao” sells products only in the USA, so the point corresponding to the coordinates (Venkatrao, Canada, Years.All, Quarters.All, Sales) has no data. In fact, no point has data whose coordinates are (Venkatrao, Geography.x, Years.any, Quarters.<any>, Sales), where x <> “USA” and any represents any member of a dimension.

Empty cells affect the evaluation of value expressions and search conditions. To see why this is so, note that a value expression is composed of value expression primaries. One of the value expression primaries is tuple[.VALUE], which returns the value of a cell in the cube (some of whose coordinates are specified explicitly by tuple, and others are available implicitly from the context of the MDX expression). This cell can be an empty cell. There are three cases where the empty cells affect expression evaluation:

  • In a numeric value expression, this value can be added, subtracted, multiplied, or divided by other values. It can also appear as the parameter of any function that has a numeric_value_expression argument.

  • In a string value expression, this value can be concatenated to another string.

  • A search condition is composed of Boolean primaries. A Boolean primary is of the form:
    <boolean_primary> ::= <value_expression> <comp_op> <value_expression>

A value expression will be made up of the value expression primary and this will lead to the first two cases described above.


Semantics of Empty Cell Value

In order to precisely define the semantics of empty cells in these situations, MDX defines the concept of an empty cell value. This is a special value that is present in the empty cell. The following rules determine the semantics of the empty cell value:

  1. The function ISEMPTY(value_expression) returns TRUE if and only if value_expression is the empty cell value. Otherwise it returns FALSE.

  2. When the empty cell value is an operand for any of the numeric operators (+, -, *, /), it behaves like the number zero.

  3. When the empty cell value is an operand for the string concatenation operator (||), it behaves like the empty string.

  4. When the empty cell value is an operand for any of the comparison operators (=. <>, >=, <=, >, <), it behaves like the number zero or the empty string, depending on whether the data type of the other operand is numeric or string, respectively.

  5. When collating numeric values, the empty cell value collates in the same place as zero. Between the empty cell value and zero, empty collates before zero.

  6. When collating string values, the empty cell value collates in the same place as the empty string. Between the empty cell value and the empty string, empty collates before an empty string.


COALESCEEMPTY Function

This function is used to coalesce an empty cell value to a number or a string. It is a type of case expression. Note that unlike the other types of case expressions — the simple and the searched case — support for COALESCEEMPTY is mandatory.

The BNF construct of the COALESCEEMPTY function is:

<coalesce_empty> ::= COALESCEEMPTY (<value_expression>
                                 [, <value_expression> ]...)

This function is used to coerce an empty cell value to a different value. For example, COALESCEEMPTY(V1, V2) returns V2 if V1 evaluates to the empty cell value; otherwise, it returns V1. COALESCEEMPTY(V1, V2,…, Vn) returns the first (from the left) argument Vk, 1 <= k <= n, which evaluates to a nonempty value. If all arguments evaluates to the empty cell value, then it returns the empty cell value.

Note that COALESCEEMPTY is simply a special case of a CASE expression.

COALESCEEMPTY(V1, V2)

is the same as:

CASE WHEN NOT ISEMPTY(V1) THEN V1 ELSE V2 END

The expression

COALESCEEMPTY(V1, V2, ..., Vn), n > 2

is the same as:

CASE WHEN NOT ISEMPTY(V1) THEN V1 ELSE CASE(V1, V2, ..., Vn-1)


Counting Empty Cells

Functions such as COUNT and AVERAGE evaluate a count of cells. You should question whether to evaluate an empty cell by this type of function In practice, it sometimes makes sense to count the number of empty cells. For example, when counting the number of sales reps, the salesrep should be included in the count regardless of whether she sold something. Each no-sale results in an empty cell. There are other situations in which empty cells should not be counted, such as when getting the average of sales over a certain domain. In this case, counting the “no sales” cells would inaccurately decrease the average.

MDX allows for both these scenarios. All numeric functions other than COUNT ignore empty cells. If empty cells need to be included in the count, the consumer can use the COALESCEEMPTY function to force a cell to be treated as a “regular” cell. The COUNT function has an optional argument, INCLUDEEMPTY, that includes empty cells in the count. In the absence of INCLUDEEMPTY, COUNT ignores empty cells.


Empty Cells in a Dataset

Frequently, the MDX statement that specifies a dataset will include empty cells in the dataset. Just like any other cell, an empty cell is associated with a set of tuples. There are some tuples for which every cell is empty at the intersection of this tuple and any other tuple in all of the other axes. Such tuples are called empty tuples.

Depending on an application’s presentation requirements, it is sometimes desirable to have empty tuples in the dataset and sometimes not. The MDX keyword NON EMPTY is used to filter out empty tuples. Using this keyword in a query filters out all empty tuples along an axis. The BNF construct is:

<MDX_Statement> ::= SELECT <axis_specification>
                        [, <axis_specification>...]
                    FROM <cube_specification>
                    WHERE <slicer_specification>

<axis_specification> ::= [NON EMPTY] <set_expression> ON <axis_name>

If NON EMPTY is specified, empty tuples are removed from the axis. The default is for empty tuples to appear on the axis.

Empty cells are possible even if NON EMPTY is specified. For example, suppose that the salesrep Venkatrao did not sell anything during the first half of 1996. During the second half of 1996, he sold only Office products, but no Operating System products. In 1997, he sold all products. Now consider the dataset that results from the following MDX statement:

SELECT
   NON EMPTY CROSSJOIN({Venkatrao, Netz}, {Office, [Operating Systems]})
      ON COLUMNS,
   NON EMPTY {[1996], [1997]} ON ROWS
FROM SalesCube
WHERE (Sales)

This results in the following dataset:

 | Venkatrao |  | Netz |
 | Office | Operating Systems | Office | Operating Systems
1996 | 4032 |  | 9876 | 13
1997 | 10002 | 54 | 18734 | 21

The cell that has operating systems for Venkatrao in 1996 is empty because Venkatrao did not sell any operating systems in all of 1996. NON EMPTY does not suppress this because the cell is not associated with any empty tuple.

Note It is not possible to filter out the empty tuples by applying some kind of a set expression during the axis specification because the cell data is populated after all axis specifications are evaluated. Any filter in the axis specification cannot be based on data in the cells of a dataset. Therefore, filtering out the empty tuples from an axis is done only after the set expressions of all of the axes have been evaluated.


Chapter 4

OLE DB for OLAP Objects and Schema Rowsets

This chapter describes the objects and rowsets available through the OLE DB for OLAP interfaces. The following objects, which are unique to OLE DB for OLAP or differ from generic OLE DB objects when used with OLE DB for OLAP, are described in the following sections:

  • Enumerator object

  • Data source object

  • Session object

  • Dataset object

OLE DB for OLAP adds a number of properties to the DBPROPSET_DATASOURCEINFO property group of a data source object. These properties give the consumer information about variations among multidimensional providers. These properties are listed and explained in the section “Data Source Object.”

OLE DB for OLAP uses a number of schema rowsets that are new to OLE DB. They are listed below, and they are described in full in the section “Session Object.”

  • CUBES rowset

  • DIMENSIONS rowset

  • HIERARCHIES rowset

  • LEVELS rowset

  • MEASURES rowset

  • MEMBERS rowset

  • PROPERTIES rowset

The description of a dataset’s axes also requires descriptions of the MDAXISINFO structure and the layout of the axes rowsets. These are described in the section “Dataset Object.”

Finally, the rowset object plays an essential role in OLE DB for OLAP. A discussion of this appears in the section “Role of the Rowset Object in OLE DB for OLAP.”


Enumerator Object

OLE DB for OLAP introduces one change to the sources rowset returned by the enumerator.


New SOURCE_TYPE

A new type is added to the SOURCE_TYPE column of the SOURCES rowset:

#define DBSOURCETYPE_DATASOURCE_MDP   3

This value enables an application to find out whether a row in the SOURCES rowset corresponds to an enumerator, a tabular data provider (TDP), or a multidimensional data provider (MDP).

The value DBSOURCETYPE_DATASOURCE indicates that a data source is a TDP. To make this name similar to the name introduced previously, add another define:

#define DBSOURCETYPE_DATASOURCE_TDP DBSOURCETYPE_DATASOURCE

If a data source is both a TDP and an MDP, then the sources rowset will have two rows for this data source. One row has the SOURCE_TYPE set to DBSOURCETYPE_DATASOURCE_TDP, and the other row has the SOURCE_TYPE set to DBSOURCETYPE_DATASOURCE_MDP. For more information on TDPs and MDPs, see “Registry Entries” in Chapter 2.


Data Source Object

OLE DB for OLAP adds a number of properties to the DBPROPSET_DATASOURCEINFO property set of a data source object. These properties give the consumer information about variations among multidimensional providers.

Property ID Description
MDPROP_FLATTENING_
SUPPORT 		Type: VT_I4

Typical R/W: R

Description: Flattening Support.

If the value of this property is:

  • MDPROPVAL_FS_FULL_SUPPORT, then the provider supports flattening as described in “Flattening a Dataset to Produce a Rowset” in chapter 2.

  • MDPROPVAL_FS_GENERATED_COLUMN, then the provider supports flattening by using dummy names, as described in Chapter 2, “Lack of Support for Named Levels.”

  • MDPROPVAL_FS_GENERATED_DIMENSION, then the provider supports flattening by generating one column per dimension as described in Chapter 2, “Lack of Support for Named Levels.”

  • MDPROPVAL_FS_NO_SUPPORT, then the provider does not support flattening. For such a provider, calling ICommand::Execute with a language dialect of MDGUID_MDX and asking for the IRowset interface returns an error (E_NOINTERFACE).
MDPROP_NAMED_LEVELS Type: VT_I4

Typical R/W: R

Description: Support for named levels.

A bitmask specifying whether the provider supports named levels and/or numbered levels. Can be one or more of the following values:

  • MDPROPVAL_NL_NAMEDLEVELS, which means that the provider supports named levels.

  • MDPROPVAL_NL_NUMBEREDLEVELS, which means that the provider supports numbered levels using the LEVELS(n) function.
DBPROP_DATASOURCE_TYPE Type: VT_I4

Typical R/W: R

Description: Data source type.

If the value of this property is:

  • DBPROPVAL_DST_TDP, then it means that the provider is a tabular data provider.

  • DBPROPVAL_DST_MDP, then it means that the provider is a multidimensional provider.

  • DBPROPVAL_DST_TDPANDMDP, then it means that the provider is both a TDP and MDP.

Note that DBPROPVAL_DST_TDPANDMDP is the bitwise OR of DBPROPVAL_DST_TDP and DBPROPVAL_DST_MDP.
MDPROP_MDX_JOINCUBES Type: VT_I4

Typical R/W: R

Description: Support for query joining multiple cubes.

A bitmask that can be a combination of zero or more of the following:

  • MDPROPVAL_MJC_IMPLICITCUBE, which means that the provider supports an empty FROM clause. The cube is implicitly resolved by the axis and slicer dimensions.

  • MDPROPVAL_MJC_SINGLECUBE, which means that the provider supports only one cube in the CONTEXT clause of the MDX statement.

  • MDPROPVAL_MJC_MULTICUBES, which means that the provider supports more than one cube in the CONTEXT clause of the MDX statement.
MDPROP_AXES Type: VT_I4

Typical R/W: R

Description: Number of axes in the dataset.

The value of this property is the maximum number of axes that the provider supports in the dataset. To be compliant with OLE DB for OLAP, this value must be at least 3.
MDPROP_AGGREGATECELL_
UPDATE 		Type: VT_I4

Typical R/W: R

Description: Support for updating aggregated cells.

If the value of this property is:

  • MDPROPVAL_AU_UNSUPPORTED, then it means that the provider does not support updating nonatomic cells.

  • MDPROPVAL_AU_UNCHANGED, then it means that the provider supports updating of aggregated cells, but the value of cells beneath the aggregated cell remains unchanged.

  • MDPROPVAL_AU_UNKNOWN, then it means that the provider supports updating of aggregated cells, but the value of cells beneath the aggregated cell remains undefined.
MDPROP_RANGEROWSET Type: VT_I4

Typical R/W: R

Description: Support for cell updates.

If the value of this property is:

  • MDPROPVAL_RR_NORANGEROWSET, then it means that the provider does not support IMDRangeRowset interface.

  • MDPROPVAL_RR_READONLY, then it means that the provider supports a read-only range rowset.

  • MDPROPVAL_RR_UPDATE, then it means that the provider supports an updatable range rowset.
MDPROP_MDX_SLICER Type: VT_I4

Typical R/W: R

Description: The capabilities in the WHERE clause of an MDX statement.

If the value of this property is:

  • MDPROPVAL_MS_SINGLETUPLE, it means that the provider supports only one tuple in the WHERE clause.

  • MDPROPVAL_MS_MULTIPLETUPLES, it means that the provider supports more than one tuple in the WHERE clause.
MDPROP_MDX_
OBJQUALIFICATION 		Type: VT_I4

Typical R/W: R

Description: Provider’s ability to qualify a cube name.

A bitmask specifying how MD schema object names can be qualified in an MDX expression. Can be a combination of zero or more of the following:

  • MDPROPVAL_MOQ_DATASOURCE _CUBE, which means that cubes can be qualified by the data source name.

  • MDPROPVAL_MCQ_CATALOG _CUBE, which means that cubes can be qualified by the catalog name.

  • MDPROPVAL_MCQ_SCHEMA _CUBE, which means that cubes can be qualified by the schema name.

    Note If none of the above bits are set, then the provider does not support cube qualification.

  • MDPROPVAL_MOQ_CUBE_DIM, which means that dimensions can be qualified by cube name.

  • MDPROPVAL_MOQ_DIM_HIER, which means that hierarchies can be qualified by dimension names.

  • MDPROPVAL_MOQ_DIMHIER_LEVEL, which means that levels can be qualified by the dimension name and/or hierarchy name. Note that this property applies only if named levels are supported, which can be checked by the property MDPROP_NAMED_LEVELS. If named levels are not supported then this bit must be set.

  • MDPROPVAL_MOQ_DIMHIER_MEMBER, which means that members can be qualified by a dimension name and/or a hierarchy name.

  • MDPROPVAL_MOQ_LEVEL_MEMBER, which means that members can be qualified by a level name.

  • MDPROPVAL_MOQ_MEMBER_MEMBER, which means that members can be qualified by their ancestor names.
MDPROP_MDX_
OUTERREFERENCE 		Type: VT_I4

Typical R/W: R

Description: Support for outer reference in an MDX statement.

  • If the value of this property is
    • MDPROPVAL_MO_TUPLE, then it means that the provider supports tuple[.VALUE] to be qualified by a cube_name argument in an MDX statement.
MDPROP_MDX_
QUERYBYPROPERTY 		Type: VT_BOOL

Typical R/W: R

Description: Support for querying by property values in an MDX statement.
MDPROP_MDX_CASESUPPORT Type: VT_I4

Typical R/W: R

Description: Support for MDX case statements.

A bitmask specifying the support in a provider for case statements. Can be a combination of zero or more of the following:

  • MDPROPVAL_MC_SIMPLECASE, which means that the provider supports a simple case statement.

  • MDPROPVAL_MC_SEARCHEDCASE, which means that the provider supports a searched case statement.
MDPROP_MDX_STRING_
COMPOP 		Type: VT_I4

Typical R/W: R

Description: Support for string comparison operators other than equals and not-equals operators.

A bitmask specifying the support in a provider for string comparison operators. Can be a combination of zero or more of the following:

  • MDPROPVAL_MSC_LESSTHAN, which means that the provider supports the less-than operator.

  • MDPROPVAL_MSC_GREATERTHAN, which means that the provider supports the greater-than operator.

  • MDPROPVAL_MSC_LESSTHANEQUAL, which means that the provider supports the less-than-or-equal-to operator.

  • MDPROPVAL_MSC_GREATERTHANEQUAL, which means that the provider supports the greater-than-or-equal-to operator.
MDPROP_MDX_DESCFLAGS Type: VT_I4

Typical R/W: R

Description: Support for various <desc flag> values in the DESCENDANTS function. Can be a combination of zero or more of the following:

  • MDPROPVAL_MD_BEFORE, which means that the provider supports the flag BEFORE.

  • MDPROPVAL_MD_AFTER, which means that the provider supports the flag AFTER.

  • MDPROPVAL_MD_SELF, which means that the provider supports the flag SELF. Note that this bit must be set by all providers because support for SELF is mandatory.

Support for other desc flag values, such as SELF_AND_BEFORE, are implied by multiple bits being on.
MDPROP_MDX_SET_
FUNCTIONS 		Type: VT_I4

Typical R/W: R

Description: Support for various set functions. Can be a combination of zero or more of the following:

  • MDPROPVAL_MSF_TOPPERCENT, which means that the provider supports the function TOPPERCENT.

  • MDPROPVAL_MSF_BOTTOMPERCENT, which means that the provider supports the function BOTTOMPERCENT.

  • MDPROPVAL_MSF_TOPSUM, which means that the provider supports the function TOPSUM.

  • MDPROP_MSF_BOTTOMSUM, which means that the provider supports the function BOTTOMSUM.

  • MDPROP_MSF_PERIODSTODATE, which means that the provider supports the function PERIODSTODATE.

  • MDPROP_MSF_LASTPERIODS, which means that the provider supports the function LASTPERIODS.

  • MDPROP_MSF_YTD, which means that the provider supports the function YTD.

  • MDPROP_MSF_QTD, which means that the provider supports the function QTD.

  • MDPROP_MSF_MTD, which means that the provider supports the function MTD.

  • MDPROP_MSF_WTD, which means that the provider supports the function WTD.

  • MDPROP_MSF_DRILLDOWNMEMBER, which means that the provider supports the function DRILLDOWNMEMBER.

  • MDPROP_MSF_DRILLDOWNLEVEL, which means that the provider supports the function DRILLDOWNLEVEL.

  • MDPROP_MSF_DRILLDOWNMEMBERTOP, which means that the provider supports the function DRILLDOWNMEMBERTOP.

  • MDPROP_MSF_DRILLDOWNMEMBERBOTTOM, which means that the provider supports the function DRILLDOWNMEMBERBOTTOM.

  • MDPROP_MSF_DRILLDOWNLEVELTOP, which means that the provider supports the function DRILLDOWNLEVELTOP.

  • MDPROP_MSF_DRILLDOWNLEVELBOTTOM, which means that the provider supports the function DRILLDOWNLEVELBOTTOM.

  • MDPROP_MSF_DRILLUPMEMBER, which means that the provider supports the function DRILLUPMEMBER.

  • MDPROP_MSF_DRILLUPLEVEL, which means that the provider supports the function DRILLUPLEVEL.

  • MDPROPVAL_MSF_TOGGLEDRILLSTATE, which means that the provider supports the function TOGGLEDRILLSTATE.
MDPROP_MDX_MEMBER_
FUNCTIONS 		Type: VT_I4

Typical R/W: R

Description: Support for various member functions. Can be a combination of zero or more of the following:

  • MDPROPVAL_MMF_COUSIN, which means that the provider supports the function COUSIN.

  • MDPROPVAL_MMF_PARALLELPERIOD, which means that the provider supports the function PARALLELPERIOD.

  • MDPROPVAL_MMF_OPENINGPERIOD, which means that the provider supports the function OPENINGPERIOD.

  • MDPROP_MMF_CLOSINGPERIOD, which means that the provider supports the function CLOSINGPERIOD.
MDPROP_MDX_NUMERIC_
FUNCTIONS 		Type: VT_I4

Typical R/W: R

Description: Support for various numeric functions. Can be a combination of zero or more of the following:

  • MDPROPVAL_MNF_MEDIAN, which means that the provider supports the function MEDIAN.

  • MDPROPVAL_MNF_VAR, which means that the provider supports the function VAR.

  • MDPROPVAL_MNF_STDDEV, which means that the provider supports the function STDDEV.

  • MDPROP_MNF_RANK, which means that the provider supports the function RANK.

  • MDPROPVAL_MNF_AGGREGATE, which means that the provider supports the function AGGREGATE.

  • MDPROPVAL_MNF_COVARIANCE, which means that the provider supports the function COVARIANCE.

  • MDPROPVAL_MNF_CORRELATION, which means that the provider supports the function CORRELATION.

  • MDPROP_MNF_LINREGSLOPE, which means that the provider supports the function LINREGSLOPE.

  • MDPROPVAL_MNF_LINREGVARIANCE, which means that the provider supports the function LINREGVARIANCE.

  • MDPROPVAL_MNF_LINREG2, which means that the provider supports the function LINREG2.

  • MDPROPVAL_MNF_LINREGPOINT, which means that the provider supports the function LINREGPOINT.
MDPROP_MDX_FORMULAS Type: VT_I4

Typical R/W: R

Description: Support for creation of named sets and calculated members. Can be a combination of zero or more of the following:

  • MDPROPVAL_MF_WITH_CALCMEMBERS, which means that the provider supports the creation of calculated members by using the WITH clause before a SELECT.

  • MDPROPVAL_MF_WITH_NAMEDSETS, which means that the provider supports the creation of named sets by using the WITH clause before a SELECT.

  • MDPROPVAL_MF_CREATE_CALCMEMBERS, which means that the provider supports the creation of named calculated members by using the CREATE clause.

  • MDPROPVAL_MF_CREATE_NAMEDSETS, which means that the provider supports the creation of named sets by using the CREATE clause.

  • MDPROPVAL_MF_SCOPE_SESSION, which means that the provider supports the scope value of SESSION during the creation of named sets and calculated members.

  • MDPROPVAL_MF_SCOPE_GLOBAL, which means that the provider supports the scope value of GLOBAL during the creation of named sets and calculated members.
MDPROP_MDX_NONMEASURE_
EXPRESSONS 		Type: VT_I4

Typical R/W: R

Description: The capabilities in the numeric_value_expression argument of set functions.

If the value of this property is:

  • MDPROPVAL_MNE_MEASURESONLY, then it means that the provider supports only expressions involving measure dimension members.

  • MDPROPVAL_MNE_ALLDIMENSIONS, then it means that the provider supports expressions involving members from any dimension.

For example, say Revenue and Cost are measures, and USA is a member of Geography dimension. In this case, in set functions that take a numeric_value_expression argument, expressions such as Revenue/3 and (Revenue - Cost)/Cost*100 are made of measures only. However, in expressions such as (USA, Sales)/3 are made of measures and nonmeasures.


Keywords Returned in IDBInfo::GetKeywords

In OLE DB, the data source object has an interface called IDBInfo, one of whose methods is IDBInfo::GetKeywords. This method returns all the keywords that apply to a provider. When a keyword is used in a query, it must be quoted. A consumer can use the list of keywords returned by this method to decide which identifier needs to be quoted.

Note It is a good practice to quote all identifiers in an MDX expression, regardless of whether they are keywords. _UNIQUE_NAME values retrieved from a schema rowset are already quoted, so there is no need to quote them again.

MDX introduces new keywords. Any provider that declares itself to be an MDP (in the sources rowset returned by ISourcesRowset::GetSourcesRowset) must return at least the following list in IDBInfo::GetKeywords:

  • AND

  • AS

  • AXIS

  • BACK_COLOR

  • CASE

  • CATALOG_NAME

  • CELL

  • CELL_ORDINAL

  • CHAPTERS

  • CHILDREN

  • CHILDREN_CARDINALITY

  • COALESCEEMPTY

  • COLUMNS

  • CUBE

  • CUBE_NAME

  • CURRENTMEMBER

  • DEFAULTMEMBER

  • DESCRIPTION

  • DIMENSION

  • DIMENSION_UNIQUE_NAME

  • ELSE

  • EMPTY

  • END

  • FIRSTCHILD

  • FIRSTSIBLING

  • FONT_FLAGS

  • FONT_NAME

  • FONT_SIZE

  • FORE_COLOR

  • FORMAT_STRING

  • FORMATTED_VALUE

  • FROM

  • HIERARCHY_UNIQUE_NAME

  • IIF

  • ISEMPTY

  • ITEM

  • LASTCHILD

  • LASTSIBLING

  • LEVEL_UNIQUE_NAME

  • LEVELS

  • MEMBER

  • MEMBER_CAPTION

  • MEMBER_GUID

  • MEMBER_NAME

  • MEMBER_ORDINAL

  • MEMBER_TYPE

  • MEMBER_UNIQUE_NAME

  • MEMBERS

  • NEXTMEMBER

  • NON

  • NOT

  • ON

  • OR

  • PAGES

  • PARENT

  • PARENT_LEVEL

  • PARENT_UNIQUE_NAME

  • PREVMEMBER

  • PROPERTIES

  • REFRESH

  • ROWS

  • SCHEMA_NAME

  • SECTIONS

  • SELECT

  • SET

  • SOLVE_ORDER

  • VALUE

  • WHEN

  • WHERE

  • WITH

  • XOR

If the provider declares itself to be a TDP also, it has to return (in addition to the above list), a list of the keywords listed in the OLE DB Programmer’s Reference.


Information about MD Schema Object Names

Different providers have different restrictions on what the names of objects in an MD schema can be. For instance, a provider may have a restriction that all cube names should be less than 15 characters long and the character “&” may not occur in a cube name. OLE DB has a general mechanism by which one can obtain information about such literals: You use a DBLITERALINFO structure and the method IDBInfo::GetLiteralInfo. For information on how to use this structure and this method, see the reference page for IDBInfo::GetLiteralInfo in the OLE DB Programmer’s Reference.

OLE DB for OLAP adds the following constants to the DBLITERAL enumeration:

Value | Description
DBLITERAL_CUBE_NAME | A cube name in an MDX command.
DBLITERAL_DIMENSION_NAME | A dimension name in an MDX command.
DBLITERAL_HIERARCHY_NAME | A hierarchy name in an MDX command.
DBLITERAL_LEVEL_NAME | A level name in an MDX command.
DBLITERAL_MEMBER_NAME | A member name in an MDX command
DBLITERAL_PROPERTY_NAME | A property name in an MDX command.

For all these values of DBLITERAL, the pwszLiteralValue of the DBLITERALINFO structure is not used.


Session Object

A consumer creates a Session object using a data source object just as it does in OLE DB (by calling IDBCreateSession::CreateSession()). A session establishes the current catalog or schema of the consumer. Within this context are zero or more cubes.


New Additions to IDBSchemaRowset

Multidimensional schema rowsets are browsable by the IDBSchemaRowset interface. Consumers can get information about the available cubes, dimensions, hierarchies, levels, and members available at the data source.

The following schema objects are relevant for OLE DB for OLAP: catalogs, schemas, cubes, measures, dimensions, hierarchies, levels, members, and properties (for each dimension level).

Note It is assumed that a multidimensional data source has catalogs, which contain schemas, which in turn contain cubes. Of course, some providers may have only catalogs or only schemas, or neither. This is no different from the TDP case.

Schema rowsets are identified by GUIDs. The following table lists these GUIDs and the columns for which restrictions can be specified on the SCHEMA rowset, for each schema object. The number of restriction columns for each SCHEMA rowset is defined as constants prefixed with CRESTRICTIONS_ in the header files. Restriction values are treated as literals rather than as search patterns.

GUID | Number of restrictions | Restriction columns
MDSCHEMA_CUBES | 3 | CATALOG_NAME, SCHEMA_NAME, CUBE_NAME
MDSCHEMA_DIMENSIONS | 5 | CATALOG_NAME, SCHEMA_NAME, CUBE_NAME, DIMENSION_NAME, DIMENSION_UNIQUE_NAME
MDSCHEMA_HIERARCHIES | 6 | CATALOG_NAME, SCHEMA_NAME, CUBE_NAME, DIMENSION_UNIQUE_NAME, HIERARCHY_NAME, HIERARCHY_UNIQUE_NAME
MDSCHEMA_LEVELS | 7 | CATALOG_NAME, SCHEMA_NAME, CUBE_NAME, DIMENSION_UNIQUE_NAME, HIERARCHY_UNIQUE_NAME, LEVEL_NAME, LEVEL_UNIQUE_NAME
MDSCHEMA_MEASURES | 5 | CATALOG_NAME, SCHEMA_NAME, CUBE_NAME, MEASURE_NAME, MEASURE_UNIQUE_NAME
MDSCHEMA_PROPERTIES | 9 | CATALOG_NAME, SCHEMA_NAME, CUBE_NAME, DIMENSION_UNIQUE_NAME, HIERARCHY_UNIQUE_NAME, LEVEL_UNIQUE_NAME, MEMBER_UNIQUE_NAME, PROPERTY_NAME, PROPERTY_TYPE
MDSCHEMA_MEMBERS | 12 | CATALOG_NAME, SCHEMA_NAME, CUBE_NAME, DIMENSION_UNIQUE_NAME, HIERARCHY_UNIQUE_NAME, LEVEL_UNIQUE_NAME, LEVEL_NUMBER, MEMBER_NAME, MEMBER_UNIQUE_NAME, MEMBER_CAPTION, MEMBER_TYPE, and tree operator

The provider must support the following GUIDs if the provider lists itself as an MDP in the SOURCES rowset returned by ISourcesRowset::GetSourcesRowset().

  • MDSCHEMA_CUBES

  • MDSCHEMA_DIMENSIONS

  • MDSCHEMA_HIERARCHIES

  • MDSCHEMA_LEVELS

  • MDSCHEMA_MEASURES

  • MDSCHEMA_PROPERTIES

  • MDSCHEMA_MEMBERS

Some columns in schema rowsets are optional. If a provider does not support an optional column, it will return NULL for that column name in the schema rowset. Note that a column can be mandatory and still contain NULL under certain circumstances. For example, the NUMERIC_SCALE column of the MEASURES rowset can be NULL if the data type of the MEASURE is not DBTYPE_NUMERIC or DBTYPE_DECIMAL. Such cases are noted in the reference section.

CATALOG_NAME, SCHEMA_NAME, _GUID, and DESCRIPTION are optional for all rowsets. The following columns are also optional in schema rowsets. (All other columns are mandatory.)

  • CUBES rowset: CREATED_ON, LAST_SCHEMA_UPDATE, SCHEMA_UPDATED_BY, LAST_DATA_UPDATE, DATA_UPDATED_BY

  • DIMENSIONS rowset: None

  • HIERARCHIES rowset: ALL_MEMBER

  • LEVELS rowset: None

  • MEASURES rowset: None

  • PROPERTIES rowset: None

  • MEMBERS rowset: None

Most of the OLE DB for OLAP interfaces treat measures just like any other dimension. In fact, the measures dimension appears in the DIMENSIONS rowset. The MEASURES rowset is really the MEMBERS rowset restricted by the measures dimension. It appears separately because consumers are usually interested in special information about a measure that does not necessarily apply to the members of another dimension.

If a provider does not support the concepts of catalogs or schemas, it does not support the DBSCHEMA_CATALOG and DBSCHEMA_SCHEMATA rowsets. A consumer can call IDBSchemaRowset::GetSchemas to find out which schema rowsets are supported. If the provider does not support catalogs or schemas, then specifying a restriction on either of these results in DB_E_NOTSUPPORTED.


Object Names in Schema Rowsets

First, all object names are strings. That is, the _NAME and _UNIQUE_NAME columns in the schema rowset are strings.

Each SCHEMA rowset deals with a particular object in the multidimensional data model — catalog, schema, cube, dimension, hierarchy, level, members, and properties. Many schema rowsets contain columns of the following types: _NAME, _UNIQUE_NAME, _CAPTION, and DESCRIPTION. These are four related concepts, but not the same. An example will illustrate the differences between these notions. Consider the entry in the MEMBERS rowset for the city of Portland, in Oregon. The MEMBER_NAME column will contain “Portland.” The MEMBER_UNIQUE_NAME column has “USA.Oregon.Portland”: The qualification is necessary because just the name “Portland” is ambiguous. There is another Portland in the state of Maine. (But note that a provider is not required to generate a unique name by the qualification. It is free to generate it in any way. The only requirement is that it must be unique.) The MEMBER_CAPTION column is anything that the DBA considers as a displayable name: It can be “Portland” or “Portland, the city of roses,” etc. The DESCRIPTION column is a string that can quite literally be anything, such as “Portland is the home of the Trailblazers. It nestles in the shadow of the looming Mt. Hood, whose fiery volcanic origin is hidden beneath a beautiful and tranquil exterior.” Consumers typically use the CAPTION column for display. The UNIQUE_NAME column is used for querying and for restricting other schema rowsets.

The concept of UNIQUE_NAME applies to all multidimensional schema objects except for the catalog, schema, and cube names. For these objects, the consumer is required to provide the qualification, if needed.

Note For providers that generate unique names using qualification, each component of a UNIQUE_NAME should be delimited. For more information, see “Delimited Identifiers” in Chapter 2.

The schema rowset of the schema object (from dimension on down) contains columns for the UNIQUE_NAME values of all its ancestor objects (up to and including the dimension, but not cube, schema, and catalog). It has a column for the NAME of itself and for the display CAPTION.


Sizes of UNIQUE_NAME Columns

Many providers generate the values of XXX_UNIQUE_NAME columns from concatenating several names. For instance, the unique name of a level may be <dim_name>.<hier_name>.<level_name>. In such a case, the provider may not be able to report the maximum sizes of such columns (when methods on the IColumnsInfo interface are called to determine a size). If they are unable to determine the maximum size, providers return a size of ~0 (all the bits are set to 1) to indicate that they are unable to accurately determine it.


CUBES Rowset

The CUBES rowset contains information about the available cubes in a schema (or the catalog, if the provider does not support schemas). It has the following columns:

Column name | Type indicator | Description
CATALOG_NAME | DBTYPE_WSTR | The name of the catalog to which this cube belongs. NULL if the provider does not support catalogs.
SCHEMA_NAME | DBTYPE_WSTR | The name of the schema to which this cube belongs. NULL if the provider does not support schemas.
CUBE_NAME | DBTYPE_WSTR | Name of the cube.
CUBE_TYPE DBTYPE_WSTR Cube type. One of the following, or a provider-specific value:
  • “CUBE”

  • “VIRTUAL CUBE”
CUBE_GUID | DBTYPE_GUID | Cube GUID. NULL if no GUID exists.
CREATED_ON | DBTYPE_DBTIMESTAMP | Date and time of cube creation. NULL if unknown.
LAST_SCHEMA_UPDATE | DBTYPE_DBTIMESTAMP | Date and time of last schema update. NULL if unknown.
SCHEMA_UPDATED_BY | DBTYPE_WSTR | User ID of the person doing the last schema update. NULL if unknown.
LAST_DATA_UPDATE | DBTYPE_DBTIMESTAMP | Date and time of last data update. NULL if unknown.
DATA_UPDATED_BY | DBTYPE_WSTR | User ID of the person doing the last data update. NULL if unknown.
DESCRIPTION | DBTYPE_WSTR | A human-readable description of the cube. If no description exists, then this is NULL.

The default sort order is CATALOG_NAME, SCHEMA_NAME, and CUBE_NAME.


DIMENSIONS Rowset

The DIMENSIONS rowset contains information about the dimensions in a given cube. It has one row for each dimension.

Note Since OLE DB for OLAP treats measures as just another dimension, there is a row returned in the DIMENSIONS rowset for the measures dimension.

The rowset has the following structure:

Column name | Type indicator | Description
CATALOG_NAME | DBTYPE_WSTR | The name of the catalog to which this dimension belongs. NULL if the provider does not support catalogs.
SCHEMA_NAME | DBTYPE_WSTR | The name of the schema to which this dimension belongs. NULL if the provider does not support schemas.
CUBE_NAME | DBTYPE_WSTR | Name of the cube to which this dimension belongs. NULL if this dimension does not belong to any cube (which is possible in multicube architectures. For more information, see “Hypercubes and Multicubes” in Chapter 2).
DIMENSION_NAME | DBTYPE_WSTR | Name of the dimension. If a dimension is part of more than one cube, then there is one row for each cube/dimension combination. For more information, see “Hypercubes and Multicubes” in Chapter 2.
DIMENSION_
UNIQUE_NAME 		DBTYPE_WSTR Unique name of the dimension. For providers that generate unique names by qualification, each component of this name is delimited.
DIMENSION_GUID | DBTYPE_GUID | The GUID of the dimension. NULL if no GUID exists.
DIMENSION_CAPTION | DBTYPE_WSTR | A label or a caption associated with the dimension. Used primarily for display purposes. If a caption does not exist, DIMENSION_NAME is returned.
DIMENSION_ORDINAL | DBTYPE_UI4 | Ordinal number of the dimension, among the group of dimensions that form the cube.
DIMENSION_TYPE DBTYPE_I2 The dimension type. Can be one of the following values:
  • MD_DIMTYPE_TIME, which indicates a time dimension.

  • MD_DIMTYPE_MEASURE, which indicates a measure dimension.

  • MD_DIMTYPE_OTHER, which indicates the dimension is neither a time dimension nor a measure dimension.

  • MD_DIMTYPE_UNKNOWN, which indicates that the provider is unable to classify the dimension.
DIMENSION_
CARDINALITY 		DBTYPE_UI4 The number of members in the dimension. Note that this value can be an approximation of the real cardinality. Consumers should not rely on this being accurate.
DEFAULT_HIERARCHY | DBTYPE_WSTR | The default hierarchy for this dimension. This is a unique name. NULL if no default hierarchy exists.
DESCRIPTION | DBTYPE_WSTR | A human-readable description of the dimension. NULL if no description exists.

The default sort order is: CATALOG_NAME, SCHEMA_NAME, CUBE_NAME, and DIMENSION_NAME.


HIERARCHIES Rowset

The HIERARCHIES rowset contains information about the hierarchies available in a dimension. If the dimension does not contain a hierarchy or has just one hierarchy, the HIERARCHY_NAME column will contain a NULL, and both the HIERARCHY_UNIQUE_NAME and HIERARCHY_CAPTION columns will contain the name of the dimension. The rowset has the following structure:

Column name | Type indicator | Description
CATALOG_NAME | DBTYPE_WSTR | The name of the catalog to which this hierarchy belongs. NULL if the provider does not support catalogs.
SCHEMA_NAME | DBTYPE_WSTR | The name of the schema to which this hierarchy belongs. NULL if the provider does not support schemas.
CUBE_NAME | DBTYPE_WSTR | Name of the cube to which this hierarchy belongs.
DIMENSION_
UNIQUE_NAME 		DBTYPE_WSTR Unique name of the dimension to which this hierarchy belongs. For providers that generate unique names by qualification, each component of this name is delimited.
HIERARCHY_NAME | DBTYE_WSTR | Name of the hierarchy. If the dimension does not contain a hierarchy or has just one hierarchy, this column contains a NULL.
HIERARCHY_
UNIQUE_NAME 		DBTYPE_WSTR Unique name of the hierarchy. This must be an unambiguous name. If the dimension does not contain a hierarchy or has just one hierarchy, this column will contain the name of the dimension. For providers that generate unique names by qualification, each component of this name is delimited.
HIERARCHY_GUID | DBTYPE_GUID | Hierarchy GUID. NULL if there is no GUID.
HIERARCHY_CAPTION | DBTYPE_WSTR | A label or a caption associated with the hierarchy. Used primarily for display purposes. If a caption does not exist, HIERARCHY_NAME is returned. If the dimension does not contain a hierarchy or has just one hierarchy, this column will contain the name of the dimension.
DIMENSION_TYPE DBTYPE_I2 The type of the dimension to which this hierarchy belongs. Can be one of the following values:
  • MD_DIMTYPE_TIME. which indicates a time dimension.

  • MD_DIMTYPE_MEASURE. which indicates a measure dimension.

  • MD_DIMTYPE_OTHER. which indicates that the dimension is neither a time dimension nor a measure dimension.

  • MD_DIMTYPE_UNKNOWN, which indicates that the provider is unable to classify the dimension.

HIERARCHY_ CARDINALITY | DBTYPE_UI4 | The number of members in the hierarchy. Because of the presence of multiple hierarchies, this number may not be the same as DIMENSION_CARDINALITY. Note that this value can be an approximation of the real cardinality. Consumers should not rely on this being accurate.
DEFAULT_MEMBER | DBTYPE_WSTR | The default member for this hierarchy. This is a unique name. Note that every hierarchy has to have a default member.
ALL_MEMBER | DBTYPE_WSTR | This is the member at the highest level of rollup in the hierarchy. Note that this can be different from DEFAULT_MEMBER.
DESCRIPTION | DBTYPE_WSTR | A human-readable description of the hierarchy. NULL if no description exists.

The default sort order is: CATALOG_NAME, SCHEMA_NAME, CUBE_NAME, DIMENSION_UNIQUE_NAME, and HIERARCHY_NAME.


LEVELS Rowset

The LEVELS rowset contains information about the levels available in a dimension. It has the following structure:

Column name | Type indicator | Description
CATALOG_NAME | DBTYPE_WSTR | The name of the catalog to which this level belongs. NULL if the provider does not support catalogs.
SCHEMA_NAME | DBTYPE_WSTR | The name of the schema to which this level belongs. NULL if the provider does not support schemas.
CUBE_NAME | DBTYPE_WSTR | Name of the cube to which this level belongs.
DIMENSION_
UNIQUE_NAME 		DBTYPE_WSTR Unique name of the dimension to which this level belongs. For providers that generate unique names by qualification, each component of this name is delimited.
HIERARCHY_
UNIQUE_NAME 		DBTYE_WSTR Unique name of the hierarchy. If the level belongs to more than one hierarchy, then there is one row for each hierarchy to which it belongs. For providers that generate unique names by qualification, each component of this name is delimited.
LEVEL_NAME | DBTYPE_WSTR | Name of the level.
LEVEL_UNIQUE_NAME | DBTYPE_WSTR | Unique name of the level. For providers that generate unique names by qualification, each component of this name is delimited.
LEVEL_GUID | DBTYPE_GUID | Level GUID. NULL if the level does not have a GUID.
LEVEL_CAPTION | DBTYPE_WSTR | A label or a caption associated with the hierarchy. Used primarily for display purposes. If a caption does not exist, LEVEL_NAME is returned.
LEVEL_NUMBER | DBTYPE_UI4 | The distance of the level from the root of the hierarchy. The root level is zero.
LEVEL_CARDINALITY | DBTYPE_UI4 | The number of members in a level. Note that this value can be an approximation of the real cardinality. Consumers should not rely on this being accurate.
LEVEL_TYPE DBTYPE_I4 The following bit masks are used to specify the level characteristics:
  • MDLEVEL_TYPE_REGULAR

  • MDLEVEL_TYPE_ALL

  • MDLEVEL_TYPE_CALCULATED

  • MDLEVEL_TYPE_TIME

  • MDLEVEL_TYPE_TIME_YEARS

  • MDLEVEL_TYPE_TIME_HALF_YEAR

  • MDLEVEL_TYPE_TIME_QUARTERS

  • MDLEVEL_TYPE_TIME_MONTHS

  • MDLEVEL_TYPE_TIME_WEEKS

  • MDLEVEL_TYPE_TIME_DAYS

  • MDLEVEL_TYPE_TIME_HOURS

  • MDLEVEL_TYPE_TIME_MINUTES

  • MDLEVEL_TYPE_TIME_SECONDS

  • MDLEVEL_TYPE_TIME_UNDEFINED

  • MDLEVEL_TYPE_UNKNOWN
DESCRIPTION | DBTYPE_WSTR | A human-readable description of the level. NULL if no description exists.

The default sort order is: CATALOG_NAME, SCHEMA_NAME, CUBE_NAME, DIMENSION_UNIQUE_NAME, HIERARCHY_UNIQUE_NAME, and LEVEL_NUMBER.

Note The LEVEL_TYPE column exists mainly as a means to transmit formatting instructions to an application. For example, a level with a type of LEVEL_TYPE_TIME_DAYS may indicate to an application that the members of the level should be expressed in text format (in other words, “Sunday,” “Monday,” “Tuesday,” and so on). LEVEL_TYPE_REGULAR can be used to signify a level that does not require any special formatting.


MEASURES Rowset

The MEASURES rowset contains information about the available measures. It has the following structure:

Column name | Type indicator | Description
CATALOG_NAME | DBTYPE_WSTR | The name of the catalog to which this measure belongs. NULL if the provider does not support catalogs.
SCHEMA_NAME | DBTYPE_WSTR | The name of the schema to which this measure belongs. NULL if the provider does not support schemas.
CUBE_NAME | DBTYPE_WSTR | Name of the cube to which this measure belongs.
MEASURE_NAME | DBTYPE_WSTR | Name of the measure.
MEASURE_
UNIQUE_NAME 		DBTYPE_WSTR Unique name of the measure. For providers that generate unique names by qualification, each component of this name is delimited.
MEASURE_CAPTION | DBTYPE_WSTR | A label or a caption associated with the measure. Used primarily for display purposes. If a caption does not exist, MEASURE_NAME is returned.
MEASURE_GUID | DBTYPE_GUID | Measure GUID. NULL if the measure does not have a GUID.
MEASURE_
AGGREGATOR 		DBTYPE_I4 How a measure was derived. Can be one of the following values:
  • MDMEASURE_AGGR_SUM

  • MDMEASURE_AGGR_COUNT

  • MDMEASURE_AGGR_MIN

  • MDMEASURE_AGGR_MAX

  • MDMEASURE_AGGR_AVG

  • MDMEASURE_AGGR_VAR

  • MDMEASURE_AGGR_STD

    The aggregation function was SUM, COUNT, MIN, MAX, AVG, VAR, or STDEV, respectively.

  • MDMEASURE_AGGR_CALCULATED

    The measure was derived from a formula that was not any single function above.

  • MDMEASURE_AGGR_UNKNOWN

    The aggregation function or formula used to derive this measure was unknown.
DATA_TYPE | DBTYPE_UI2 | Data type of the measure. Can be any of the types listed in Appendix A of the OLE DB Programmer's Reference.
NUMERIC_PRECISION | DBTYPE_UI2 | If the measure object’s data type is exact numeric, this is the maximum precision of the property. NULL for all other property types.
NUMERIC_SCALE | DBTYPE_I2 | If the measure object’s type indicator is DBTYPE_NUMERIC or DBTYPE_DECIMAL, this is the number of digits to the right of the decimal point. Otherwise, this is NULL.
MEASURE_UNITS | DBTYPE_WSTR | The unit of measurement. Some examples are “dollars,” “boxes sold,” “tons shipped,” “pounds,” and “millions.” Typically, a consumer appends this to the display caption of the measure. If a unit is not available, the provider returns an empty string.
DESCRIPTION | DBTYPE_WSTR | A human-readable description of the measure. NULL if no description exists.

The default sort order is: CATALOG_NAME, SCHEMA_NAME, CUBE_NAME, and MEASURE_NAME.


PROPERTIES Rowset

The PROPERTIES rowset contains information about the available properties for each level of the dimension. The assumption here is that each level defines a class of members. The properties of all members in this class are the same. For a data source that does not support named levels, there is a dummy level that includes all the members in the dimension. The name of this level is the same as the name of the dimension.

The PROPERTIES rowset also contains all available properties for cells in a dataset. Because every provider is going to have at least three properties (the VALUE, FORMATTED_VALUE, and CELL_ORDINAL properties of a cell), the PROPERTIES rowset always has at least three rows. Consumers can distinguish between member properties and cell properties by looking at the PROPERTY_TYPE column. Note that a property can be both a member property and a cell property. Consumers can retrieve only the member property, only the cell property, or both, by restricting the PROPERTY_TYPE field of this rowset.

Column name | Type indicator | Description
CATALOG_NAME | DBTYPE_WSTR | The name of the catalog to which this property belongs. NULL if the provider does not support catalogs.
SCHEMA_NAME | DBTYPE_WSTR | The name of the schema to which this property belongs. NULL if the provider does not support schemas.
CUBE_NAME | DBTYPE_WSTR | Name of the cube to which this property belongs.
DIMENSION_UNIQUE_
NAME 		DBTYPE_WSTR Unique name of the dimension. For providers that generate unique names by qualification, each component of this name is delimited.
HIERARCHY_UNIQUE_
NAME 		DBTYPE_WSTR Unique name of the hierarchy. For providers that generate unique names by qualification, each component of this name is delimited.
LEVEL_UNIQUE_
NAME 		DBTYPE_WSTR Unique name of the level to which this property belongs. If the provider does not support named levels, then it should return the DIMENSION_UNIQUE_NAME value for this field. For providers that generate unique names by qualification, each component of this name is delimited.
MEMBER_UNIQUE_
NAME 		DBTYPE_WSTR This is used for data sources that do not support named levels or have properties on a member-by-member basis. This contains the unique name of the member to which the property belongs. If the property applies to all members in a level, then this is NULL. For providers that generate unique names by qualification, each component of this name is delimited.
PROPERTY_TYPE DBTYPE_I2 The following bit masks are used to describe the property type:
  • MDPROP_MEMBER, which indicates that this is a property of a member. Can be used in the DIMENSION PROPERTIES clause of the SELECT list.

  • MDPROP_CELL, which indicates that this is a property of the cell. Can be used in the CELL PROPERTIES clause that occurs at the end of the SELECT statement.
PROPERTY_NAME | DBTYPE_WSTR | Name of the property.
PROPERTY_CAPTION | DBTYPE_WSTR | A label or a caption associated with the property. Used primarily for display purposes. If a caption does not exist, PROPERTY_NAME is returned. .
DATA_TYPE | DBTYPE_UI2 | Data type of the property. Can be any of the types listed in Appendix A of the OLE DB Programmer's Reference.
CHARACTER_
MAXIMUM_LENGTH 		DBTYPE_UI4 The maximum possible length of a value in this property. For character, binary, and bit properties, this is one of the following:
  • The maximum length of the property in characters, bytes, or bits, respectively, if one is defined.

  • Zero (0) if there is no defined maximum length.

  • NULL for all other properties.
CHARACTER_OCTET_
LENGTH 		DBTYPE_UI4 Maximum length in octets (bytes) of the property, if the type of the property is either character or binary. A value of 0 means that the property has no defined maximum length. NULL for all other property types.
NUMERIC_PRECISION | DBTYPE_UI2 | If the property object’s data is numeric, this is the maximum precision of the property. NULL for all other property types.
NUMERIC_SCALE | DBTYPE_I2 | If the property object’s type indicator is DBTYPE_NUMERIC or DBTYPE_DECIMAL, this is the number of digits to the right of the decimal point. Otherwise, this is NULL.
DESCRIPTION | DBTYPE_WSTR | A human-readable description of the property. NULL if no description exists.

The default sort order is: PROPERTY_TYPE, CATALOG_NAME, SCHEMA_NAME, CUBE_NAME, DIMENSION_UNIQUE_NAME, HIERARCHY_UNIQUE_NAME, and LEVEL_UNIQUE_NAME.


MEMBERS Rowset

The MEMBERS rowset contains information about the available members. It has the following structure:

Column name | Type indicator | Description
CATALOG_NAME | DBTYPE_WSTR | The name of the catalog to which this member belongs. NULL if the provider does not support catalogs.
SCHEMA_NAME | DBTYPE_WSTR | The name of the schema to which this member belongs. NULL if the provider does not support schemas.
CUBE_NAME | DBTYPE_WSTR | Name of the cube to which this member belongs.
DIMENSION_
UNIQUE_NAME 		DBTYPE_WSTR Unique name of the dimension to which this member belongs. For providers that generate unique names by qualification, each component of this name is delimited.
HIERARCHY_
UNIQUE_NAME 		DBTYPE_WSTR Unique name of the hierarchy. If the member belongs to more than one hierarchy, then there is one row for each hierarchy to which it belongs. For providers that generate unique names by qualification, each component of this name is delimited.
LEVEL_
UNIQUE_NAME 		DBTYPE_WSTR Unique name of the level to which the member belongs. For providers that generate unique names by qualification, each component of this name is delimited.
LEVEL_NUMBER | DBTYPE_UI4 | Member's position (distance from the root level, zero based) of the member.
MEMBER_ORDINAL | DBTYPE_UI4 | Ordinal number of the member. This is the sort rank of the member when members of this dimension are sorted in their natural sort order. If providers do not have the concept of natural ordering, then this should be the rank when sorted by MEMBER_NAME.
MEMBER_NAME | DBTYPE_WSTR | Name of the member.
MEMBER_TYPE DBTYPE_I4 The type of the member. Can be one of the following values:
  • MDMEMBER_TYPE_REGULAR

  • MDMEMBER_TYPE_ALL

  • MDMEMBER_TYPE_MEASURE

  • MDMEMBER_TYPE_FORMULA

  • MDMEMBER_TYPE_UNKNOWN

  • Note that MDMEMBER_TYPE_FORMULA takes precedence over MDMEMBER_TYPE_MEASURE. Thus, if there is a formula (calculated) member on the measures dimension, it is listed as MDMEMBER_TYPE_FORMULA.
MEMBER_GUID | DBTYPE_GUID | Member GUID. NULL if no GUID.
MEMBER_CAPTION | DBTYPE_WSTR | A label or a caption associated with the member. Used primarily for display purposes. If a caption does not exist, then MEMBER_NAME is returned.
CHILDREN_
CARDINALITY 		DBTYPE_UI4 Number of children that the member has. This can be an estimate of the number of children, and not exact. Consumers should not rely on this being the exact count. Providers should return as good an estimate as possible.
PARENT_LEVEL | DBTYPE_UI4 | Level number (distance from the root level, zero based) of the member's parent. This has the value 0 for the root level.
PARENT_
UNIQUE_NAME 		DBTYPE_WSTR Unique name of the member’s parent. NULL is returned for any members at the root level. For providers that generate unique names by qualification, each component of this name is delimited.
PARENT_COUNT | DBTYPE_UI4 | The count of the number of parents that this member has. For more information, see “Complex Member Relationships” in this chapter.
DESCRIPTION | DBTYPE_WSTR | A human-readable description of the member.
Zero or more columns one for each property of the member | Depends on the PROPERTIES rowset | A list of zero or more columns, one for each property at this level. The structure of these columns can be gleaned from the PROPERTIES rowset.

The default sort order is: CATALOG_NAME, SCHEMA_NAME, CUBE_NAME, DIMENSION_UNIQUE_NAME, HIERARCHY_UNIQUE_NAME, LEVEL_UNIQUE_NAME, LEVEL_NUMBER, and MEMBER_ORDINAL.


Restrictions

The MEMBERS rowset can have up to 12 restrictions. The first 11 are straightforward — they are columns in the MEMBERS rowset: CATALOG_NAME, SCHEMA_NAME, CUBE_NAME, DIMENSION_UNIQUE_NAME, HIERARCHY_UNIQUE_NAME, LEVEL_UNIQUE_NAME, LEVEL_NUMBER, MEMBER_NAME, MEMBER_UNIQUE_NAME, MEMBER_CAPTION, and MEMBER_TYPE. The eleventh restriction, called the tree operation, is not on any particular column of the MEMBERS rowset; rather, it specifies a tree operator. The idea is that the consumer specifies a MEMBER_UNIQUE_NAME restriction and the tree operator (ANCESTORS, CHILDREN, SIBLINGS, PARENT, DESCENDANTS, SELF) to obtain the desired set of members. The SELF operator includes the row for the member itself in the list of returned rows. The following constants are defined in the header file:

// tree operations on restriction 12 on members rowset
#define MDTREEOP_ANCESTORS      0x00000020
#define MDTREEOP_CHILDREN      0x00000001
#define MDTREEOP_SIBLINGS       0x00000002
#define MDTREEOP_PARENT       0x00000004
#define MDTREEOP_SELF         0x00000008
#define MDTREEOP_DESCENDANTS   0x00000010

These designations comprise a bit mask and may be combined.

Effect of Multiple Restrictions

For the most part, specifying more than one restriction results in each of the restrictions being joined by an AND operation. For example, if the consumer specifies a level name restriction of “General Managers” and the member name restriction of “Bill,” then the effect is to find “Bill” among “General Managers.”

The sole exceptions to this rule are the tree operator restrictions. Tree operators operate on a particular member. This member is itself specified by using the other restrictions, such as member name, level name, level number, and so forth. The tree operator interacts with other restrictions as follows:

  • All restrictions other than the tree operator are joined by an AND operation and applied to the MEMBERS rowset to get the members.

  • The tree operator is then applied to these members to yield the required rows.

The consumer can combine more than one tree operator by using the bitwise-OR operator of C/C++. MDTREEOP_CHILDREN | MDTREEOP_SELF returns a rowset containing the list of children of the specified member and the member itself.

The data type for the tree operator restriction that the provider expects in the rgRestrictionsArray parameter of IDBSchemaRowset::GetRowset is DBTYPE_I4.

Note PARENT_UNIQUE_NAME is not a restriction because specifying the tree operator MEMBER_PARENT can do the same thing.

Possibility of Duplicates

When using the tree operator restriction, it is possible that the resulting rowset will contain duplicate rows. For example, suppose that the non-tree restrictions select two members that are siblings of each other. The tree restriction is MDTREEOP_SIBLINGS. This will cause the list of siblings to be repeated in the resulting rowset because the list of siblings was specified twice.


Same Member in Multiple Hierarchies

The MEMBERS rowset has LEVEL_UNIQUE_NAME and LEVEL_NUMBER. These two columns indicate the member's position in the hierarchy. However, it is possible that the same member belongs to more than one hierarchy. OLE DB for OLAP requires that there be a row in the MEMBERS rowset for each occurrence of a member in a different hierarchy. So if a member M belongs to two different hierarchies X and Y, then the MEMBERS rowset has two rows, for which MEMBER_UNIQUE_NAME is “X.M” and “Y.M.” Note that the member name “M” has been qualified here in order to make it unique with the hierarchy name. In this case, the MEMBER_CAPTION for both these rows will be the same.


Complex Member Relationships

Several providers allow member relationships that are not strict trees. They are directed acyclic graphs (DAGs). For such providers, a member can have multiple parents. The column PARENT_COUNT gives the number of parents in such a case.

When member relationships form a DAG, each link between a member and its parent is represented as a separate row in the members rowset. Therefore, the MEMBER_UNIQUE_NAME value is not the primary key of the members rowset. Rather, it is the combination of MEMBER_UNIQUE_NAME and PARENT_UNIQUE_NAME that is the primary key.

Among the multiple parents, there is one that is the “real” parent (that is, it represents the preferred rollup path). The others are “step” parents. Conversely, a parent has “real” children and “step” children. The tree operators for ancestors, descendants, parent, and children all follow the “real” links and not the “step” links.


Command Object

The Command object supports text commands in the MDX expression syntax by using the OLE DB ICommandText interface. Consumers use the ICommandText::SetCommandText method to specify the command and the ICommand::Execute method to process the command. For commands that may be used several times, consumers can prepare the command with the ICommandPrepare::Prepare method. For a complete discussion of OLE DB Command object, see the OLE DB Programmer's Reference. For additional information about the MDX expression syntax defined by OLE DB for OLAP and its use, see “Multidimensional Expressions (MDX) and Dataset” and “Advanced MDX” in Chapter 2. For the complete syntax of MDX, see Appendix C, “MDX Grammar.”

OLE DB for OLAP defines a GUID — MDGUID_MDX — which denotes the MDX expression syntax. This GUID is used in ICommandText::SetCommandText to identify to the provider that the query language being used is the MDX expression syntax defined in Appendix C of this document.


Semantics of DBGUID_DEFAULT

OLE DB defines a special language dialect identifier, DBGUID_DEFAULT, which denotes the default language dialect. In the context of OLE DB for OLAP, the following semantics apply to the use of this identifier:

  • If the provider is both a TDP and an MDP, then it must interpret the command string as SQL when DBGUID_DEFAULT is passed as the dialect identifier.

  • If the provider is an MDP, then it should interpret the command string as MDX when DBGUID_DEFAULT is passed as the dialect identifier.

Note MD consumers are strongly discouraged from using DBGUID_DEFAULT for creating datasets. They must use DBGUID_MDX.


Controlling the Creation of Instances of a Rowset or a Dataset

Executing an MDX query can either result in a dataset, or it can result in the flattened rowset. The application can control which object (rowset or a dataset) can result from a query. The following rules determine the object resulting from ICommandText::Execute when the GUID specified in the rguidDialect argument of ICommandText::SetCommandText is MDGUID_MDX. (The provider applies the following rules in the sequence shown below, and exits if any rule is satisfied.)

  1. If the consumer has set the property DBPROP_IRowset to VARIANT_TRUE in the property set DBPROPSET_ROWSET by using ICommandProperties::SetProperties, then a Rowset object is created.

  2. If the consumer asks for any interface on the Dataset object, then a Dataset object is created.

  3. If the consumer asks for any interface on the Rowset object that does not exist on the dataset, then a Rowset object is created.

Therefore, the guaranteed way for a consumer to get the flattened rowset of a dataset is by setting the DBPROP_IRowset property to VARIANT_TRUE. The guaranteed way for a consumer to get a dataset is to set DBPROP_IRowset property to VARIANT_FALSE (or leave it untouched) and ask for an interface on the Dataset object.


Dataset Object

This is the only new object defined by OLE DB for OLAP. The following diagram shows the interfaces available on this object. Mandatory interfaces are shown on the right and optional interfaces are shown on the left.


IMDDataset Interface

This is the core interface on the Dataset object. It is a mandatory interface on this object. It has methods to:

  • Find axes information (GetAxisInfo).

  • Get axis rowsets (GetAxisRowset).

  • Fetch properties of a range of cells (GetCellData).

  • Get an interface on the Command object that created this dataset (GetSpecification).

  • Free the array of MDAXISINFO structures returned by the GetAxisInfo method (FreeAxisInfo).

This is the definition of the IMDDataset interface:

interface IMDDataset : public IUnknown
{
HRESULT FreeAxisInfo(
   ULONG         cAxes,
   MDAXISINFO*   rgAxisInfo);

HRESULT GetAxisInfo(
   ULONG*        pcAxes,
   MDAXISINFO**  prgAxisInfo);

HRESULT GetAxisRowset(
   IUnknown*     pUnkOuter,
   ULONG         iAxis,
   REFIID        riid,
   ULONG         cPropertySets,
   DBPROPSET     rgPropertySets[],
   IUnknown**    ppRowset);

HRESULT GetCellData(
   HACCESSOR     hAccessor,
   ULONG         ulStartCell,
   ULONG         ulEndCell,
   VOID*         pvData);

HRESULT GetSpecification(
   REFIID        riid,
   IUnknown**    ppSpecification);
};


MDAXISINFO Structure

This structure contains information about an axis in the dataset. The method IMDDataset::GetAxisInfo returns an array of these structures, one for each axis in the dataset. It looks like this:

typedef struct MDAXISINFO
{
   ULONG      cbSize;
   ULONG      iAxis;
   ULONG      cDimensions;
   ULONG      cCoordinates;
   ULONG      *rgcColumns;
   LPOLESTR   *rgpwszDimensionNames;
} MDAXISINFO

The cbSize field contains the size of the structure, in bytes. The iAxis field contains the number of the axis. Axes in a dataset are numbered starting from 0. Axis 0 is the column axis; axis 1 is the row axis; additional axes have numbers 2, 3, and so on. cDimensions indicates the count of dimensions on the axis. cCoordinates contains the count of coordinates on the axis. rgcColumns is a pointer to an array of integers, and each element of this array contains the count of columns in the axis rowset for this particular dimension. (For more information, see “Axis Rowsets.”) The field rgpwszDimensionNames is a pointer to an array of strings. The provider allocates this array. It contains the names of each dimension. Obviously, the cardinality of both these arrays is cDimensions.

Providers must make sure to modify the structure only within the bounds of cbSize. Consumers must make sure to use the structure only within the bounds of cbSize.

Consumers must use the IMDDataset::FreeAxisInfo method to free the array of MDAXISINFO structures returned by the IMDDataset::GetAxisInfo method. This ensures that all the memory allocated by the provider for this array and for the fields in each element of this array is properly freed. Use of IMalloc::Free on the array pointer does not guarantee that all this memory will be freed.

For providers, the presence of IMDDataset::FreeAxisInfo method means that the memory for MDAXISINFO array and the fields of each of its elements need not all be allocated in one contiguous block. The memory can be allocated in any convenient set of blocks which can be freed when the consumer calls IMDDataset::FreeAxisInfo.

For more information on how consumers should move from one element to the next in the MDAXISINFO array, see the page describing IMDDataset::GetAxisInfo in Chapter 4.


Axis Rowsets

There is one rowset for every axis. This rowset has at least 5n+1 columns where n is the number of dimensions projected along the axis (because there are five default properties that are available for each axis, and one more column for the TUPLE_ORDINAL). In addition to this, there may be provider-specific default properties, or the application may have selected additional properties by using the DIMENSION PROPERTIES clause. Because of this, it is possible that the number of columns corresponding to a dimension is more than five. The field rgcColumns in the MDAXISINFO structure for this axis points to an array containing the number of columns in the axis rowset for this dimension.

The axis rowset has columns in the following order:

  • TUPLE_ORDINAL column.

  • Columns for the outermost dimension.

  • Columns for dimensions with inner nestings.

  • Within the columns for each dimension, the five default properties occur first, followed by any provider-specific properties, followed by the properties explicitly selected in the DIMENSION PROPERTIES clause.

The five default properties for each dimension, in order of their occurrence in the axis rowset, are:

  • MEMBER_UNIQUE_NAME. The unique name of the member. This is the same as MEMBER_UNIQUE_NAME in the MEMBERS rowset.

  • MEMBER_CAPTION. The display name of the member. This is the same as the MEMBER_CAPTION column of the MEMBERS rowset.

  • LEVEL_UNIQUE_NAME. The name of the level to which this member belongs. This is the same as the LEVEL_UNIQUE_NAME column of the MEMBERS rowset.

  • LEVEL_NUMBER. The distance of the member from the root (zero based, root = 0). This is the same as the LEVEL_NUMBER column of the MEMBERS rowset.

  • DISPLAY_INFO. This property contains various items of information that help an application render the axis. It is a 4-byte value. The value of the least significant 2 bytes is a 16-bit unsigned integer that contains the number of children of a member. In the most significant 2 bytes, each bit potentially conveys one piece of display information. For OLE DB,meanings are defined for only 2 bits (the least significant bits), as below:

    • The least significant bit (rightmost bit), if set, indicates that this member is drilled down. That is, at least one child of this member appears on the axis, immediately following the member. This can be tested by the bitmask MDDISPINFO_DRILLED_DOWN. This bit can be used by applications to display a “+” or a “–“ next to the member.

    • The next bit (the second least significant bit), if set, indicates that the parent of this member is the same as the parent of the immediately preceding member. This can be tested by the bitmask MDDISPINFO_SAME_PARENT_AS_PREV. The use of this bit becomes clear when one considers the problem of displaying the dataset shown in “Cube and Dataset Example” in Chapter 2. Specifically, how does the application get information about the ancestors of Seattle and Boston (“USA_North” and “USA”)? It can do so by using the MDTREEOP_ANCESTORS property on the members rowset. After getting the list of ancestors for Seattle, it must do the same for Boston. This is wasteful because the application already got the information when it got the ancestors for Seattle. The bit MDDISPINFO_SAME_PARENT_AS_PREV is useful in this scenario. It will be set for “Boston,” and the application can use the ancestor information that it already has instead of querying the schema rowset again.

    Note the following rules about DISPLAY_INFO:

    • The value in the first 2 bytes of DISPLAY_INFO (the count of children) is an unsigned value.

    • If the number of children exceeds 65535 (the largest 16-bit number possible), the provider still returns 65535 in the first 2 bytes of DISPLAY_INFO. Therefore, the application’s interpretation of the number 65535 in this field should be “greater than or equal to 65535 children.” If the application wants to know the count of children beyond 65535, then it should ask for the CHILDREN_CARDINALITY property in the DIMENSION_PROPERTIES clause.

    • Just like the CHILDREN_CARDINALITY property, the count of children returned in the first 2 bytes of DISPLAY_INFO may not be exact.

    • MDDISPINFO_DRILLED_DOWN is set only if a child immediately follows a member on the axis. For instance, if the axis has {North_America, South_America, USA}, then the MDDISPINFO_DRILLED_DOWN bit is not set for North_America because USA does not immediately follow North_America.

    • MDDISPINFO_DRILLED_DOWN does not require that all the children of a member be present on the axis, immediately following the member. It just requires at least one child to be present immediately following the member.

    • The bitmasks MDDISPINFO_DRILLED_DOWN and MDDISPINFO_SAME_PARENT_AS_PREV are 32-bit bitmasks. The application should apply it to all the 4 bytes of DISPLAY_INFO.

To distinguish these column names, they are prefixed by dimension_name. For example, the column names for the SalesRep dimension are: “[SalesRep].[MEMBER_UNIQUE_NAME]”, “[SalesRep].[MEMBER_CAPTION]”, “[SalesRep].[LEVEL_UNIQUE_NAME]”, “[SalesRep].[LEVEL_NUMBER]”, “[SalesRep].[DISPLAY_INFO]”.

The data types for TUPLE_ORDINAL and the five mandatory properties are:

  • TUPLE_ORDINAL — DBTYPE_UI4

  • MEMBER_UNIQUE_NAME — DBTYPE_WSTR

  • MEMBER_CAPTION — DBTYPE_WSTR

  • LEVEL_UNIQUE_NAME — DBTYPE_WSTR

  • LEVEL_NUMBER — DBTYPE_I4

  • DISPLAY_INFO — DBTYPE_UI4.

The data types of provider-specific properties and properties selected by using the DIMENSION PROPERTIES clause are available in the PROPERTIES schema rowset. This information is also available by calling IColumnsInfo on the Rowset object corresponding to an axis rowset.

An Example

Consider a dataset that looks like this:

This dataset has two axes, x and y (0 and 1).

The x-axis has the following elements:

  • Two dimensions: Salesperson and Geography.

  • Salesperson dimension has members from the Name level.

  • Geography dimension has members from the Country, Region, and City levels.

    Note that the country JAPAN has no regions defined in this database. Note also that USA_North has its cities expanded while USA_South does not have its cities expanded.

The y-axis has the following elements:

  • One dimension: Time.

  • Two levels: Quarters and Month.

    Qtr1 and Qtr4 have their months expanded while Qtr2 and Qtr3 do not.

The MDAXISINFO structure for the x-axis has:

  • cbSize = 24 (assuming ULONG values and pointers are 4 bytes each)

  • iAxis = 0

  • cDimensions = 2

  • cCoordinates = 8

  • rgcColumns = pointer to array [5, 5]. There are no provider-specific mandatory properties

  • rgpwszDimensionNames = pointer to array [SalesRep, Geography]

The MDAXISINFO structure for the y-axis has:

  • cbSize = 24 (assuming ULONG values and pointers are 4 bytes each)

  • iAxis = 1

  • cDimensions = 1

  • cCoordinates = 8

  • rgcColumns = pointer to array [5]. There are no provider-specific mandatory properties

  • rgpwszDimensionNames = pointer to array [Quarters]

The axis rowset for the x-axis is shown in the table below. (The value of DISPLAY_INFO is shown in the form “x, y, z” where z = number of children, y = MDDISPINFO_DRILLED_DOWN and x = MDDISPINFO_SAME_PARENT_AS_PREV.)

TUPLE_ ORDINAL | [SalesRep]. [MEMBER_ UNIQUE_ NAME] | [SalesRep]. [MEMBER_ CAPTION] | [SalesRep]. [LEVEL_ UNIQUE_ NAME] | [SalesRep]. [LEVEL_ NUMBER] | [SalesRep]. [DISPLAY_ INFO]
0 | [SalesRep]. [Venkatrao] | Venkatrao | RepName | 1 | 0, 0, 0
1 | [SalesRep]. [Venkatrao] | Venkatrao | RepName | 1 | 0, 0, 0
2 | [SalesRep]. [Venkatrao] | Venkatrao | RepName | 1 | 0, 0, 0
3 | [SalesRep]. [Venkatrao] | Venkatrao | RepName | 1 | 0, 0, 0
4 | [SalesRep]. [Netz] | Netz | RepName | 1 | 0, 0, 0
5 | [SalesRep]. [Netz] | Netz | RepName | 1 | 0, 0, 0
6 | [SalesRep]. [Netz] | Netz | RepName | 1 | 0, 0, 0
7 | [SalesRep]. [Netz] | Netz | RepName | 1 | 0, 0, 0

(This is the same table continued.)

[Geography]. [MEMBER_ UNIQUE_ NAME] | [Geography]. [MEMBER_ CAPTION] | [Geography]. [LEVEL_ UNIQUE_ NAME] | [Geography]. [LEVEL_ NUMBER] | [Geography]. [DISPLAY_ INFO]
[The World]. [North America]. [USA]. [USA_North]. [Seattle] | Seattle | City | 5 | 0, 0, 0
[The World]. [North America]. [USA]. [USA_North]. [Boston] | Boston | City | 5 | 1, 0, 0
[The World]. [North America]. [USA]. [USA_South] | Southern Region, USA | Region | 3 | 0, 0, 25
[The World]. [Asia]. [Japan] | Japan | Country | 2 | 0, 0, 12
[The World]. [North America]. [USA]. [USA_North]. [Seattle] | Seattle | City | 5 | 0, 0, 0
[The World]. [North America]. [USA]. [USA_North]. [Boston] | Boston | City | 5 | 1, 0, 0
[The World]. [North America]. [USA]. [USA_South] | Southern Region, USA | Region | 3 | 0, 0, 25
[The World]. [Asia]. [Japan] | Japan | Country | 2 | 0, 0, 12

The axis rowset for the y-axis is as shown below. (The value of DISPLAY_INFO is shown in the form “x, y, z” where z = number of children, y = MDDISPINFO_DRILLED_DOWN, and x = MDDISPINFO_ SAME_PARENT_AS_PREV.)

TUPLE_ ORDINAL | [Quarters]. [MEMBER_ UNIQUE_ NAME] | [Quarters]. [MEMBER_ CAPTION] | [Quarters]. [LEVEL_ UNIQUE_ NAME] | [Quarters]. [LEVEL_ NUMBER] | [Quarters]. [DISPLAY_ INFO]
0 | Jan | January | Month | 2 | 0, 0, 31
1 | Feb | February | Month | 2 | 1, 0, 29
2 | Mar | March | Month | 2 | 1, 0, 31
3 | Qtr2 | Quarter 2 | Quarter | 1 | 0, 0, 3
4 | Qtr3 | Quarter 3 | Quarter | 1 | 1, 0, 3
5 | Oct | October | Month | 2 | 0, 0, 31
6 | Nov | November | Month | 2 | 1, 0, 30
7 | Dec | December | Month | 2 | 1, 0, 31

Note In this example, it is assumed that the members of the SalesRep and Geography dimensions need to be qualified and members from all other dimensions need not be qualified (for ensuring uniqueness). It is also assumed that all level names are unique without qualification.

Effect of DIMENSION PROPERTIES Clause on Axis Rowsets

If the MDX expression contains a DIMENSION PROPERTIES clause that selects properties of members, these properties are available in the axis rowset. Each property appears as a column in the axis rowset, whose name is <level_name>.<property_name>, where <level_name> is the unique name of the level to which the property belogs. The property columns (if any) come after the mandatory columns (MEMBER_UNIQUE_NAME, MEMBER_CAPTION, LEVEL_UNIQUE_NAME, LEVEL_NUMBER, and DISPLAY_INFO) for each dimension.

Note As mentioned before, MEMBER_UNIQUE_NAME, MEMBER_CAPTION, LEVEL_UNIQUE_NAME, LEVEL_NUMBER, and DISPLAY_INFO are always available in the axis rowset. If these properties are specified in the DIMENSION PROPERTIES clause, they are ignored. The effect is as if they were not in the DIMENSION PROPERTIES clause.

Mandatory Member Properties

All providers are required to support the following mandatory properties for each member:

  • CATALOG_NAME

  • SCHEMA_NAME

  • CUBE_NAME

  • DIMENSION_UNIQUE_NAME

  • HIERARCHY_UNIQUE_NAME

  • LEVEL_UNIQUE_NAME

  • LEVEL_NUMBER

  • MEMBER_UNIQUE_NAME

  • MEMBER_NAME

  • MEMBER_TYPE

  • MEMBER_GUID

  • MEMBER_CAPTION

  • MEMBER_ORDINAL

  • CHILDREN_CARDINALITY

  • PARENT_LEVEL

  • PARENT_UNIQUE_NAME

  • PARENT_COUNT

  • DESCRIPTION

The meanings of these properties are the same as the respective columns of the MEMBERS rowset. (See “MEMBERS Rowset” in this chapter.)

The consumer can include any of these properties in the axis rowset by specifying them on the DIMENSION PROPERTIES clause.

Axis Rowsets and Calculated Members

Calculated members have well-defined values for all the mandatory member properties. They have a name, a unique name (which can be the qualified name or a different unique name generated by the provider), and so on. Most of these are derived from the member definition in the WITH clause or in the CREATE clause.

The following properties need special mention:

  • MEMBER_GUID is always NULL.

  • MEMBER_CAPTION is the same as the member name.

  • CHILDREN_CARDINALITY is usually zero.

The values of the nonmandatory member properties, if any, are provider-defined.


Finding Slicer Information

Every dimension in the cube has to be either an axis dimension or a slicer dimension. The method for finding information about dimension members that are on an axis was described in the previous sections. For the purposes of returning slicer dimension information, the set of slicer dimensions is treated as if they were on a special axis, called the slicer axis. For the purposes of axis ordering, the slicer axis is assumed to come after all other axes. The identifier for the slicer axis is MDAXIS_SLICERS, and is defined to be the constant 0xffffffff, the largest ULONG (32-bit) value possible.

As it does for any other axis, the MDAXISINFO structure returns information about the slicer axis as well. The cDimensions field contains the number of slicer dimensions; cCoordinates is 1 for most providers since the slicer for most providers is just one tuple. The rgpwszDimensionNames value points to the array containing the names of all slicer dimensions, and rgcColumns points to the array containing the number of columns corresponding to each slicer dimension, on the axis rowset. The axis rowset for the slicer dimension usually has just one row since most providers support only one tuple for a slicer.

The slicer axis rowset for the example dataset in Chapter 2, “Cube and Dataset Example,” is shown below. Since there are 16 columns in this rowset, and all will not fit on the same page, only the MEMBER_UNIQUE_NAME and LEVEL_UNIQUE_NAME columns are shown for each dimension.

TUPLE_ ORDI- NAL | [MEAS- URES]. [MEM- BER_ UNIQUE_ NAME] | [MEAS- URES]. [LEVEL_ UNIQUE_ NAME] | [YEARS]. [MEM- BER_ UNIQUE_ NAME] | [YEARS]. [LEVEL_ UNIQUE_ NAME] | [PRO- DUCTS]. [MEM- BER_ UNIQUE_ NAME] | [PRO- DUCTS]. [LEVEL_ UNIQUE_ NAME]
0 | Sales | Measures | [1991] | Years | [Products]. [All] | [Products]. [All]

Note MEASURES_LEVEL is just “Measures,” the name of the dimension itself. This is because, in the example, there was no hierarchy in this dimension, so the level name was a dummy name made up by the provider. It used the name of the dimension itself to create the dummy name. By a similar reasoning, the name of the YEARS_LEVEL is “Years.” Also, it is assumed that “Sales” and “[1991]” are unique and therefore do not need to be qualified.

The columns for each slicer dimension are available in the order that these dimensions are listed in the rgpwszDimensionNames array. This order is not necessarily the same as the order of dimensions as inferred from the members constituting the slicer tuple. For example, if the slicer tuple is ([Measures].[Sales], [Years].[1991], [Products].[All]), and rgpwszDimensionNames is [Years, Measures, Products], then the columns for Years dimension come first, followed by those for Measures and Products. Consumers should not derive column order from the slicer tuple; rather, they should derive it from the dimension name array of MDAXISINFO.


IMDFind Interface

This is an optional interface on the Dataset object. It has methods to:

  • Find the ordinal of a cell in the dataset, given the array of members that identify it (FindCell).

  • Find the tuple ordinals on an axis, given the constituent member names and a starting tuple ordinal (FindTuple).
interface IMDFind : public IUnknown
{
HRESULT FindCell(
   ULONG       ulStartingOrdinal,
   ULONG       cMembers,
   LPCOLESTR*  rgpwszMember,
   ULONG*      pulCellOrdinal);

HRESULT FindTuple(
   ULONG       ulAxisIdentifier,
   ULONG       ulStartingOrdinal,
   ULONG       cMembers,
   LPCOLESTR*  rgpwszMember,
   ULONG*      pulTupleOrdinal);
};


IMDRangeRowset Interface

This is an optional interface on the Dataset object. It has one method, GetRangeRowset, which gets the update rowset for a range of cells in the dataset. This rowset can be used to update cell value and other properties.

interface IMDRangeRowset : public IUnknown
{
HRESULT GetRangeRowset(
   IUnknown*         pUnkOuter,
   ULONG             ulStartCell,
   ULONG             ulEndCell,
   REFIID            riid,
   ULONG             cPropertySets,
   DBPROPSET         rgPropertySets,
   IUnknown**        ppRowset);
};


Creating an Accessor

Before fetching cell data, the application has to create an accessor that describes the structure of the data buffer. This is done using IAccessor::CreateAccessor method. The accessor created using this method is given as an input parameter to the IMDDataset::GetCellData method. Since GetCellData can fetch multiple cells in a single call, the provider needs to know the size of each cell in the data buffer. Applications must provide this size in the cbRowSize argument of IAccessor::CreateAccessor method. Failure to do so will result in an error from this method and an accessor will not be created.


Unchanged Interfaces

The IAccessor, IColumnsInfo, IConvertType, and ISupportErrorInfo interfaces are the same as currently defined in the OLE DB Programmer's Reference.


Role of the Rowset Object in OLE DB for OLAP

The Rowset object is the OLE DB universal abstraction for tabular data. Rowsets are used extensively by OLE DB for OLAP; there are essentially three areas of OLE DB for OLAP that return rowsets:

  • Schema rowsets. These are used to return schema information.

  • Axis rowsets. These are used to return information about the axes of a dataset.

  • Range rowset. Each cell in a dataset is modeled as a row, and the collection of cells that the application is interested in updating is presented as a range rowset.

This means that an OLE DB for OLAP provider must implement the Rowset object and all the mandatory interfaces on that object. However, OLE DB for OLAP rowsets usually are significantly simpler than the general OLE DB rowset. They do not need to implement bookmarks or scrolling. The axis rowsets and the schema rowsets need not implement updatability either.

Nothing prevents a provider from implementing a rowset with all of the above features. However, it does not need to in order to be compliant with OLE DB for OLAP.


Chapter 5

OLE DB for OLAP Code Samples

This chapter provides several OLE DB for OLAP code samples illustrating how to do the following:

  • Use the root enumerator and the sources rowset to identify a multidimensional provider (MDP).

  • Create an instance of a data source by using the DISPLAY_NAME column of the sources rowset.

  • Create an instance of a data source directly by using the provider's CLSID.

  • Browse schemas using schema rowsets.

  • Create a dataset and obtain axis information.

  • Create a dataset and obtain the cell data.


Connecting to an MDP by Using the Root Enumerator

Connection to a MDP requires using the root enumerator to create a rowset representing all OLE DB providers. This function returns a pointer to the IDBInitialize interface for an MDP data source:

//
HRESULT MDPConnectUsingRootEnum(IDBInitialize** ppIDBInitialize)
{
   HRESULT hr;
   assert(*ppIDBInitialize == NULL);

   // The ENUMINFO structure is used to bind data source
   // rowset data for the provider list.
   struct ENUMINFO
   {
      WCHAR            wszName[MAX_NAME_LEN];
      WCHAR            wszParseName[MAX_NAME_LEN];
      WCHAR            wszDescription[MAX_NAME_LEN];
      DBTYPE         wType;
      VARIANT_BOOL   fIsParent;
   };

   //Initialize the OLE DB enumerator and obtain rowset
   ISourcesRowset* pISourcesRowset = NULL;
   hr = CoCreateInstance(CLSID_OLEDB_ENUMERATOR, NULL,
               CLSCTX_INPROC_SERVER, IID_ISourcesRowset,
               (void**)&pISourcesRowset);

   IRowset* pIRowset = NULL;
   hr = pISourcesRowset->GetSourcesRowset(NULL,
      IID_IRowset, 0, NULL, (IUnknown**)&pIRowset);

   //Create accessor
   IAccessor* pIAccessor = NULL;
   hr = pIRowset->QueryInterface(IID_IAccessor, (void**)&pIAccessor);

   // rgBindings array defines column data to be bound
   // to the ENUMINFO data structure.
   ULONG cBindings = 5;
   DBBINDING rgBindings[cBindings] =
   {
      1, offsetof(ENUMINFO, wszName), 0, 0, NULL, NULL, NULL,
         DBPART_VALUE, DBMEMOWNER_CLIENTOWNED, DBPARAMIO_NOTPARAM,
         MAX_NAME_LEN, 0, DBTYPE_WSTR, 0, 0,

      2, offsetof(ENUMINFO, wszParseName), 0, 0, NULL, NULL, NULL,
         DBPART_VALUE, DBMEMOWNER_CLIENTOWNED, DBPARAMIO_NOTPARAM,
         MAX_NAME_LEN, 0, DBTYPE_WSTR, 0, 0,

      3, offsetof(ENUMINFO, wszDescription), 0, 0, NULL, NULL, NULL,
         DBPART_VALUE, DBMEMOWNER_CLIENTOWNED, DBPARAMIO_NOTPARAM,
         MAX_NAME_LEN, 0, DBTYPE_WSTR, 0, 0,

      4, offsetof(ENUMINFO, wType), 0, 0, NULL, NULL, NULL, DBPART_VALUE,
         DBMEMOWNER_CLIENTOWNED, DBPARAMIO_NOTPARAM, sizeof(DBTYPE), 0,
         DBTYPE_UI2, 0, 0,

      5, offsetof(ENUMINFO, fIsParent), 0, 0, NULL, NULL, NULL,
         DBPART_VALUE, DBMEMOWNER_CLIENTOWNED, DBPARAMIO_NOTPARAM,
         sizeof(VARIANT_BOOL 0, DBTYPE_BOOL, 0, 0,
   };

   HACCESSOR hAccessor = DB_NULL_HACCESSOR;
   hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA,
      cBindings, rgBindings, 0, &hAccessor, NULL);

   //Obtain IParseDisplayName interface
   IParseDisplayName*   pIParseDisplayName = NULL;
   hr = pISourcesRowset->
      QueryInterface(IID_IParseDisplayName, (void**)&pIParseDisplayName);

   //Loop through the entire returned rowset
   HROW* rghRows = NULL;
   ULONG cRowsObtained = 0;
   ULONG cEnumInfo = 0;
   ENUMINFO* rgEnumInfo;
   while (SUCCEEDED(hr))
   {
      hr = pIRowset->GetNextRows(NULL, 0, 20, &cRowsObtained, &rghRows);
      if (FAILED(hr) break;

      if (cRowsObtained == 0)
      {
         // ENDOFROWSET
         break;
      }

      //Alloc room for ProviderInfo (in chunks)
      rgEnumInfo =
         (ENUMINFO*)CoTaskMemRealloc(rgEnumInfo,
            (cEnumInfo+cRowsObtained) * sizeof(ENUMINFO));
      memset(&rgEnumInfo[cEnumInfo], 0,
         sizeof(ENUMINFO)*cRowsObtained);

      //Loop over rows obtained and get ProviderInfo
      for (ULONG i=0; i<cRowsObtained; i++)
      {
         //Get the data
         hr = pIRowset->GetData(rghRows[i], hAccessor,
            (void*)&rgEnumInfo[cEnumInfo]));
         if (FAILED(hr) break;
         cEnumInfo++;
      }

      //Release all the rows
      hr = pIRowset->ReleaseRows(cRowsObtained, rghRows, NULL, NULL,
         NULL);
      CoTaskMemFree(rghRows);
      rghRows = NULL;
   }

   // if successfully obtained a set of providers...
   if (SUCCEEDED(hr) && cEnumInfo)
   {
      // rgEnumInfo[cEnumInfo] contains enumerated info for all providers
      for (ULONG i=0; i<cEnumInfo; i++)
      {
         // find multidimensional provider and connect
         // with IParseDisplayName
         if (rgEnumInfo[i].wType == DBSOURCETYPE_DATASOURCE_MDP)
         {
         // Connect to MDP provider using IMoniker
         // Create binding context, use default options
            IBindCtx* pIBindCtx = NULL;
            hr = CreateBindCtx(0, &pIBindCtx);

            if (SUCCEEDED(hr))
            {
               ULONG chEaten = 0;
               IMoniker* pIMoniker = NULL;
               hr = pIParseDisplayName->ParseDisplayName(pIBindCtx,
                  rgEnumInfo[i].wszParseName, &chEaten, &pIMoniker));
               }
               if (SUCCEEDED(hr))
               {
                  hr = BindMoniker(pIMoniker, 0, IID_IUnknown,
                     (void**)&pIDBInitialize));
               }
               if (pIBindCtx) pIBindCtx->Release();
               if (pIMoniker) pIMoniker->Release();

               if (SUCCEEDED(hr))
               {
                  // If ParseDisplayName() and BindMoniker() have
                  // succeeded, pIDBInitialize is a valid
                  // interface pointer to the MDP data source.
                  break;
               }
            }
         }
      }
   }
   // free enum info and rowset handles
   CoTaskMemFree(rgEnumInfo);
   CoTaskMemFree(rghRows);
   hr = pIParseDisplayName->Release();
   hr = pIAccessor->ReleaseAccessor(hAccessor,NULL);
   hr = pIAccessor->Release();
   hr = pIRowset->Release();
   hr = pISourcesRowset->Release();
   return hr;
}


Creating an Instance of a Data Source Directly by Using the Provider's CLSID

This example connects directly to a specific MDP by using the provider’s unique class identifier, in this case represented by CLSID_GENOLAP. After obtaining an IID_IDBSchemaRowset pointer, this function proceeds to call the function MDPSchemaSample() (the code for which appears in the next section) in order to traverse the schema rowset for the MDP data source.

//
HRESULT MDPConnectUsingClsId(void)
{
   HRESULT hr;

   // Initialize property values
   for (ULONG i=0; i < NUMELEM(rgProperties); i++)
      VariantInit(&rgProperties[i].vValue);

   // Create the instance of the MSOLAP provider
   IDBInitialize *pIDBInitialize = NULL;
   hr = CoCreateInstance(CLSID_MSOLAP, NULL, CLSCTX_INPROC_SERVER,
      IID_IDBInitialize, (LPVOID*)&pIDBInitialize);

   IDBProperties *pIDBProperties = NULL;
   hr = pIDBInitialize->QueryInterface(IID_IDBProperties, (void **)
                  &pIDBProperties);

   // Identify property set
   DBPROPSET rgPropertySet[1];
   DBPROP      rgProperties[1];
   rgPropertySet[0].rgProperties    = rgProperties;
   rgPropertySet[0].cProperties   = 1;
   rgPropertySet[0].guidPropertySet = DBPROPSET_DBINIT;

   // Fill in data source
   rgProperties[0].dwPropertyID = DBPROP_INIT_DATASOURCE;
   rgProperties[0].dwOptions = DBPROPOPTIONS_REQUIRED;
   rgProperties[0].colid = DB_NULLID;
   rgProperties[0].vValue.vt = VT_BSTR;
   rgProperties[0].vValue.bstrVal   = SysAllocString(L"LocalHost");
   assert(rgProperties[0].vValue.bstrVal);

   hr = pIDBProperties->
         SetProperties(NUMELEM(rgPropertySet), rgPropertySet);

   // Initialize and create the session.
   hr = pIDBInitialize->Initialize();

   IDBCreateSession *pIDBCreateSession = NULL;
   hr = pIDBInitialize->
      QueryInterface(IID_IDBCreateSession,
         (void **) &pIDBCreateSession);

   IDBSchemaRowset *pIDBSchemaRowset = NULL;
   hr = pIDBCreateSession->CreateSession(NULL, IID_IDBSchemaRowset,
      (IUnknown **) &pIDBSchemaRowset);

   IRowset* pIRowset = NULL;
   hr = MDPSchemaSample(pIDBSchemaRowset, (IUnknown**)pIRowset);

   if (pIDBInitialize) pIDBInitialize->Release();
   if (pIDBProperties) pIDBProperties->Release();
   if (pIDBCreateSession) pIDBCreateSession->Release();
   if (pIDBSchemaRowset) pIDBSchemaRowset->Release();
   if (pIRowset) pIRowset->Release();
   return hr;
}


Creating a Schema Rowset

This example creates a schema rowset that represents the dimensions schema. This function returns an IRowset interface pointer for the schema rowset.

//
HRESULT MDPSchemaSample(IDBSchemaRowset* pIDBSchemaRowset,
                        IUnknown** ppIUnknown)
{
   HRESULT hr;
   assert(*ppIUnknown == NULL);

   IDBSchemaRowset *pIDBSchemaRowset = NULL;
   hr = pIUnknown->QueryInterface(IID_IDBSchemaRowset,
      (void **) &pIDBSchemaRowset);

   // Get a list of the schemas supported by the provider
   GUID *rgSchemas = NULL;
   ULONG *rgRestrictionSupport = NULL;
   ULONG cSchemas;
   hr = pIDBSchemaRowset->GetSchemas( &cSchemas, &rgSchemas,
      &rgRestrictionSupport );

   // Create a rowset that represents the dimensions.Check if
   // MDSCHEMA_DIMENSIONS is in the list returned by GetSchemas()
   ULONG cRestrictions = 0;
   BOOL bSchemaSupported = FALSE;
   for (ULONG i=0; i < cSchemas; i++)
   {
      if (rgSchemas[i] == MDSCHEMA_DIMENSIONS)
      {
         bSchemaSupported = TRUE;
         cRestrictions = rgRestrictionSupport[i];
      }
   }
   if (bSchemaSupported)
   {
      IDBCreateCommand *pIDBCreateCommand = NULL;
      VARIANT rgRestrictions[MAX_RESTRICTIONS];

      // Initialize the restrictions
      for (ULONG j=0; j < cRestrictions; j++)
         VariantInit( &rgRestrictions[j] );

      rgRestrictions[CUBE_NAME].bstrVal = SysAllocString(
                                 L"Video Store Sales");
      rgRestrictions[CUBE_NAME].vt = VT_BSTR;
      assert(rgRestrictions[CUBE_NAME].bstrVal);

      rgRestrictions[DIMENSION_NAME].bstrVal =
         SysAllocString( L"Customer Location");
      rgRestrictions[DIMENSION_NAME].vt = VT_BSTR;
      assert(rgRestrictions[DIMENSION_NAME].bstrVal);

      // Create a schema rowset for the "Customer Location" dimension
      hr = pIDBSchemaRowset->GetRowset(NULL, MDSCHEMA_DIMENSIONS,
                  cRestrictions, rgRestrictions, IID_IRowset, 0, NULL,
                  (IUnknown **)ppIUnknown ));

      // free the restrictions
      for (j=0; j < cRestrictions; j++)
         VariantClear(&rgRestrictions[j]);
   }

   if (rgSchemas) CoTaskMemFree(rgSchemas);
   if (rgRestrictionSupport) CoTaskMemFree(rgRestrictionSupport);
   if (pIDBSchemaRowset) pIDBSchemaRowset->Release();
   return hr;
}


Creating a Dataset and Obtaining Axis Information

This function executes an MDX query statement specified by pwszQuery. GetAxisInfo and GetAxisRowset are called to traverse the column information for each axis in the MDX query.

HRESULT MDPQueryColumnInfo(ICommandText *pICommandText, pwszQuery)
{
   HRESULT hr;

   struct COLUMNDATA
   {
      SDWORD   dwLength;      // length of data
      DBSTATUS   dwStatus;      // status of column
      SHORT      wPadding;
      BYTE      bData[1];      // variable length data
   };

   // Execute the query
   IMDDataset* pIMDDataset = NULL;
   hr = pICommandText->SetCommandText(DBGUID_MDX, pwszQuery);
   hr = pICommandText->Execute(NULL, IID_IMDDataset, NULL, NULL,
                               (IUnknown **)&pIMDDataset ));

   // Fetch and traverse the axis info
   ULONG cAxis;
   MDAXISINFO* rgAxisInfo = NULL;
   hr = pIMDDataset->GetAxisInfo( &cAxis, &rgAxisInfo );

   for (ULONG iAxis=0; iAxis < cAxis; iAxis++)
   {
      // rgAxisInfo contains the array of dimensions for each axis
      for (ULONG iDim=0; iDim <
         rgAxisInfo[iAxis].cDimensions; iDim++)
      {
         // rgAxisInfo[iAxis].rgpwszDimensionNames points
         // to the dimension name
         assert(rgAxisInfo[iAxis].rgpwszDimensionNames);
      }

      // Fetch the axis rowset for each axis
      IRowset* pIrowset = NULL;
      hr = pIMDDataset->GetAxisRowset(NULL, iAxis,
                           IID_IRowset, 0, NULL, (IUnknown**)&pIRowset));

      // Fetch the column info for the axis rowset
      IColumnsInfo *pIColumnsInfo = NULL;
      hr = pIRowset->QueryInterface(IID_IColumnsInfo,
                              (void**)&pIColumnsInfo);

      ULONG cCol;
      WCHAR* pStringsBuffer = NULL;
      DBCOLUMNINFO* pInfo = NULL;
      hr = pIColumnsInfo->GetColumnInfo(&cCol, &pInfo, &pStringsBuffer);

      // Create bindings for all columns, in same order as given by
      // GetColumnInfo().Bind everything as string, skip DBTYPE_VECTOR
      // type columns
      ULONG dwOffset = 0;
      ULONG iBind = 0;
      ULONG cBind = 0;
      DBBINDING* rgBind = (DBBINDING*)CoTaskMemAlloc(
                              cCol*sizeof(DBBINDING));
      for (ULONG iCol=0; iCol < cCol; iCol++)
      {
         // Skip columns of type _VECTOR (Probably binary data)
         if (pInfo[iCol].wType & DBTYPE_VECTOR)
            continue;

         rgBind[iBind].iOrdinal  = pInfo[iCol].iOrdinal;
         rgBind[iBind].obValue   = dwOffset +
            offsetof(COLUMNDATA,bData);
         rgBind[iBind].obLength  = dwOffset +
            offsetof(COLUMNDATA,dwLength);
         rgBind[iBind].obStatus  = dwOffset +
            offsetof(COLUMNDATA,dwStatus);
         rgBind[iBind].pTypeInfo = NULL;
         rgBind[iBind].pObject   = NULL;
         rgBind[iBind].pBindExt  = NULL;
         rgBind[iBind].cbMaxLen  = pInfo[iCol].ulColumnSize;
         rgBind[iBind].dwFlags   = 0;
         rgBind[iBind].eParamIO  = DBPARAMIO_NOTPARAM;
         rgBind[iBind].dwPart    = DBPART_VALUE |
                                   DBPART_LENGTH |
                                   DBPART_STATUS;
         rgBind[iBind].dwMemOwner= DBMEMOWNER_CLIENTOWNED;
         rgBind[iBind].bPrecision= 0;
         rgBind[iBind].bScale    = 0;
         rgBind[iBind].wType     = DBTYPE_STR;

         dwOffset += rgBind[iBind].cbMaxLen +
            offsetof(COLUMNDATA,bData);
         iBind++;
      }
      cBind = iBind;

      // Create the accessor.
      IAccessor* pIAccessor;
      hr = pIRowset->QueryInterface(IID_IAccessor, (void**)&pIAccessor);
      hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, cBind, rgBind,
                                      dwOffset, phAccessor, NULL);

      // Allocate a buffer for a single row of data.
      ULONG cbRowsize = dwOffset;
      BYTE* pData = (BYTE *)CoTaskMemAlloc(cbRowSize);

      while (SUCCEEDED(hr))
      {
         // Prepare internal buffers and get handles to // the rows.
         // Fetch 20 rows at a time
         ULONG cRowsObtained;
         hr = pIRowset->GetNextRows(NULL, 0, 20, &cRowsObtained, &pRows);

         // break on EndOfRowset
         if (cRowsObtained == 0)   break;

         for (ULONG iRow=0; iRow < cRowsObtained; iRow++)
         {
            // Clear buffer.
            memset(pData, 0, cbRowSize);

            // Get the row data.
            hr = pIRowset->GetData(rghRows[iRow], hAccessor, pData);

            // traverse each bound column value for a single row
            // use pColumn to access each column’s data values
            for (iBind=0; iBind < cBind; iBind++)
            {
               // advance to the column value
               (COLUMNDATA*)pColumn = (COLUMNDATA *)(pData +
                  rgBind[iBind].obLength);
               // (WCHAR*)pColumn->bData points to the string value
             }
         }
         // Release the row handles.
         hr = pIRowset->ReleaseRows(cRowsObtained, rghRows, NULL, NULL,
                                    NULL);
      }
      // Free the accessor and rowset
      hr = pIAccessor->ReleaseAccessor(hAccessor, NULL);
      hr = pIAccessor->Release();
      hr = pIRowset->Release();

      // Free the row data and bindings
      CoTaskMemFree(pData);
      CoTaskMemFree(rgBind);

      // Free the column info.
      CoTaskMemFree(pInfo);
      CoTaskMemFree(pwszStringsBuffer);
   }
   hr = pIMDDataset->FreeAxisInfo(cAxis, rgAxisInfo);
   hr = pIMDDataset->Release();
   return hr;
}


Creating a Dataset and Getting Cell Data

MDPDataset() is used to get the cell data for an MDX query specified by pwszQuery, compute the cell data coordinates, and call IMDDataset::GetCellData() for each cell in the dataset.

//
HRESULT MDPDataset(ICommandText *pICommandText, pwszQuery)
{
   HRESULT hr;

   struct COLUMNDATA
   {
      SDWORD   dwLength;   // length of data (not space allocated)
      DBSTATUS   dwStatus;   // status of column
      VARIANT   var;         // variant value
   };
   // Execute the MDX query
   IMDDataset* pIMDDataset = NULL;
   hr = pICommandText->SetCommandText(DBGUID_MDX, pwszQuery);
   hr = pICommandText->Execute(NULL, IID_IMDDataset, NULL, NULL,
                               (IUnknown **)&pIMDDataset ));

   // Fetch the axis info to compute the coordinates
   ULONG cAxis;
   MDAXISINFO* rgAxisInfo = NULL;
   hr = pIMDDataset->GetAxisInfo( &cAxis, &rgAxisInfo );

   // Calculate the coordinate "offset factors". These are used to
   // translate from a point in cell space to a set of axis coordinates.
   // Example, consider a dataset with three axes and the following
   // number of coordinates for each axis:
   //   {x,y,z} = {3,4,5}
   //      rgAxisOffset[2] = 3*4
   //      rgAxisOffset[1] = 3
   //      rgAxisOffset[0] = 1
   // Thus, where p represents the cell's ordinal value
   //      z = p % 12
   //      y = (p - z*12) % 3
   //      x = (p - z*12 - y*3)
   // And,
   //      p = x + 3*y + 12*z

   ULONG cAxisTuple;
   ULONG iOffset = 1;
   ULONG ulMaxCoord = 0;
   ULONG rgAxisOffset[MAX_DIMS];   // Array of offset multipliers

   // for all axes, excluding slicer axis
   for (ULONG iAxis=0; iAxis < cAxis-1; iAxis++)
   {
      rgAxisOffset[iAxis] = iOffset;
      cAxisTuple = rgAxisInfo[iAxis].cCoordinates;
      iOffset *= cAxisTuple;
   }
   ulMaxCoord = iOffset;
   ULONG cSliceCoord = rgAxisOffset[cAxis-1]

   // bind to the column values for each cell
   IColumnsInfo *pIColumnsInfo = NULL;
   hr = pIMDDataset->QueryInterface(IID_IColumnsInfo,
                        (void**)&pIColumnsInfo);

   ULONG cCol;
   WCHAR* pStringsBuffer = NULL;
   DBCOLUMNINFO* pInfo = NULL;
   hr = pIColumnsInfo->GetColumnInfo(&cCol, &pInfo, &pStringsBuffer);

   // Create bindings for each cell's columns, ordered as returned by
   // GetColumnInfo(). Bind everything as Variant.
   dwOffset = 0;
   ULONG iBind = 0;
   ULONG cBind = 0;
   for (ULONG iCol=0; iCol < cCol; iCol++)
   {
      rgBind[iBind].iOrdinal  = pInfo[iCol].iOrdinal;
      rgBind[iBind].obValue   = dwOffset +
         offsetof(COLUMNDATA,var);
      rgBind[iBind].obLength  = dwOffset +
         offsetof(COLUMNDATA,dwLength);
      rgBind[iBind].obStatus  = dwOffset +
         offsetof(COLUMNDATA,dwStatus);
      rgBind[iBind].pTypeInfo = NULL;
      rgBind[iBind].pObject   = NULL;
      rgBind[iBind].pBindExt  = NULL;
      rgBind[iBind].cbMaxLen  = sizeof(VARIANT);
      rgBind[iBind].dwFlags   = 0;
      rgBind[iBind].eParamIO  = DBPARAMIO_NOTPARAM;
      rgBind[iBind].dwPart    = DBPART_VALUE |
                                DBPART_LENGTH |
                                DBPART_STATUS;
      rgBind[iBind].dwMemOwner= DBMEMOWNER_CLIENTOWNED;
      rgBind[iBind].wType     = DBTYPE_VARIANT;
      rgBind[iBind].bPrecision= 0;
      rgBind[iBind].bScale    = 0;

      dwOffset += rgBind[iBind].cbMaxLen + offsetof(COLUMNDATA,var);
      iBind++;
   }
   cBind = iBind;

   // Create the accessor.
   IAccessor* pIAccessor;
   hr = pIMDDataset->QueryInterface(IID_IAccessor, void**)&pIAccessor);
   // Note that the value of dwOffset contains the size of a cell
   // failure to specify this value will result in an error.
   hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, cBind, rgBind,
                                   dwOffset, phAccessor, NULL);

   pIAccessor->Release();
   pIColumnsInfo->Release();

   // Allocate a buffer for a single cell in a slice
   ULONG cbRowsize = dwOffset;
   pData = (BYTE*)CoTaskMemAlloc(cbRowSize);

   // Fetch each cell in the dataset
   for (ULONG ulCellCoord=0; ulCellCoord < ulMaxCoord; ulCellCoord++)
   {
      // Populate cell buffer.
      hr = pIMDDataset->GetCellData(hAccessor, ulCellCoord, ulCellCoord,
                                    pData);

      // Traverse each bound cell property value for a single cell "row"
      // Use pColumn to access each column’s data values
      for (iBind=0; iBind < cBind; iBind++)
      {
         // advance to the column value
         (COLUMNDATA*)pColumn = (COLUMNDATA *)(pData +
                                 rgBind[iBind].obLength);
         // pColumn->var points to the variant value
      }
   }
   // Free the accessor and rowset
   hr = pIAccessor->ReleaseAccessor(hAccessor, NULL);
   hr = pIAccessor->Release();
   hr = pIColumnsInfo->Release();

   // Free the row data and bindings
   CoTaskMemFree(pData);
   CoTaskMemFree(rgBind);

   // Free the column info.
   CoTaskMemFree(pInfo);
   CoTaskMemFree(pwszStringsBuffer);

   hr = pIMDDataset->FreeAxisInfo(cAxis, rgAxisInfo);
   hr = pIMDDataset->Release();
   return hr;
}


Chapter 6

Mapping MDX to SQL Statements

The mappings described in this section assume that the cube is stored as a set of tables in an SQL schema. A precise specification of the mappings requires a correspondingly precise specification of the structure of the tables that constitute a cube. One such representation is detailed here. There are many possible ways in which a cube can be represented as a set of tables. Discussion of which particular representation is the most efficient is beyond the scope of this chapter. If the representations differ, it is straightforward to alter mappings to accommodate the differences.


MEMBERS Table

This table contains all the members in all the dimensions of the cube:

MEMBER_NAME | NATURAL_SORT_RANK
Geography.All.USA | 1
Geography.All.USA.Kansas | 14
Geography.All Canada | 3
Geography.All.USA.NewYork.Buffalo | 5
Geography.All.USA.Kansas.Topeka | 1
…and other rows |

The MEMBER_NAME and PARENT_NAME values are fully qualified, unambiguous names; that is, they will be fully qualified by the path all the way to the dimension name. The NATURAL_SORT_RANK value is the rank of a member when it is sorted in the natural sort order of the dimension. Note that this is the rank among all the children of a member. This makes inserts easier. If a new city is inserted into the list of cities for Germany, then there should be no need to update the natural sort rank of all cities in the world.


LEVELS Table

This table contains all the levels of all the dimensions of a cube:

LEVEL_NAME | LEVEL | DIMENSION_NAME
Geography.All | 0 | Geography
Geography.Countries | 1 | Geography
Geography.States | 2 | Geography
Geography.Cities | 3 | Geography
Geography.Zipcode | 4 | Geography
Time.All | 0 | Time
…and so on. |  |

Note that the LEVEL_NAME column contains a level name qualified by the dimension name. For example, the “All” level of the Geography dimension is ambiguous because many dimensions have “All” as the root level. Therefore, the name is “Geography.All,” which makes it unambiguous.


FACT Table

This table contains all the facts. Conceptually, this table is the result of applying a CUBE operator to the table that contains the atomic detail records. It has one column for each level in each dimension of the cube. The VALUE column has the actual data value.

By definition, there are n+1 columns in this table, where n is the number of rows in the LEVELS table. The names of n columns are taken from the LEVEL_NAME column of the LEVELS table. The additional column is called VALUE.

This table contains not only the “atomic” facts, but also the “aggregations.” For instance, there is a row for “Sales for 3rd day of September 1996,” which is an atomic fact. There are also rows for “Sales for September 1996,” “Sales for Quarter3, 1996,” “Sales for 1996” (all aggregations), and so on.


Mapping Overview

A dataset is a complex entity. In relational terms, it is not convenient to represent a dataset as a two-dimensional table. Instead, it is represented as a set of multiple tables. There is a total of n+1 tables, where n is the number of axes in the dataset.

Consider an MDX statement of the form:

SELECT
   <axis specification> ON COLUMNS,
   <axis specification> ON ROWS
FROM <cube specification>
WHERE <slicer specification>

This statement is mapped to a set of SQL statements, which results in the following three tables:

  • The COLUMNS axis table. This table has one row for each tuple in the COLUMNS axis. This table is used as the basis of an axis rowset. (It can be appropriately joined with the MEMBERS table and the LEVELS table to get an axis rowset.)

  • The ROWS axis table. This table has one row for each tuple in the ROWS axis.

  • The VALUES table. This table contains the values at the intersection points of the COLUMNS and ROWS tables. Each axis table has a RANK column that specifies the tuple coordinate on the axis. The VALUES table has two columns: RANK and VALUE. The RANK column contains the cell ordinal and the VALUE column contains the data value.


FROM Clause

In an MDX statement, the FROM clause has a cube name. In the corresponding SQL statements (note that there are multiple statements because multiple tables must be generated), FROM clauses will contain the MEMBERS table (for generating the axis tables) or the FACT table (for generating the VALUES table). Depending on the query, there might also be the LEVELS table joined to the MEMBERS table when generating the axes tables.


Axis Expressions

Each axis specification results in an SQL SELECT statement, the select list of which has one column for each dimension oriented on that axis, and a RANK column, which represents the rank of a tuple on the axis. (Recall that sets are ordered in MDX.) For example, the axis specification

CROSSJOIN({MSAccess, MSWord}, {Kansas, Buffalo, Topeka, USA, Canada})
   ON COLUMNS

will result in a SELECT statement that generates the following virtual table:

Name1 | Name2 | Rank
Products.All.Office.Access | Geography.All.USA.Kansas | 1
Products.All.Office.Access | Geography.All.USA.NewYork.Buffalo | 2
Products.All.Office.Access | Geography.All.USA.KansasTopeka | 3
Products.All.Office.Access | Geography.All.USA | 4
Products.All.Office.Access | Geography.All.Canada | 5
Products.All.Office.Word | Geography.All.USAKansas | 6
Products.All.Office.Word | Geography.All.USA.NewYork.Buffalo | 7
Products.All.Office.Word | Geography.All.USA.KansasTopeka | 8
Products.All.Office.Word | Geography.All.USA | 9
Products.All.Office.Word | Geography.All.Canada | 10

Note This table can be joined with the MEMBERS table to yield the axis rowset.

The generation of this table requires multiple SQL statements, which operate on the MEMBERS and LEVELS tables. The virtual tables generated by the intermediate SELECT statements are used as input to the subsequent SELECT statements. The section “A Complete Example” has several examples that make this process clear.


WHERE Clause

The tables corresponding to the axis specifications are joined with the FACT table. The WHERE clause on the MDX statement appears on the WHERE clause of this SELECT statement, whose select list contains the VALUE column and the RANK columns. Each row in the virtual table created by this SELECT statement represents a cell in the dataset. The RANK column contains the Cell ordinal. When taken together with the axis tables described above, this table represents the entire dataset.


Simple Extensions to SQL-92

The mapping of MDX to a set of SQL statements requires a few simple extensions to SQL-92. This section specifies these extensions.


Local Temporary Views

Note that an MDX statement results in a sequence of SQL SELECT statements. As mentioned before, the result of each such statement is a virtual table that is used in the FROM clause of one or more subsequent statements. To do this, you need to give a name to the result of a SELECT statement, which can be used for the duration of that session as a table in any subsequent SELECT statement.

The CREATE LOCAL TEMPORARY VIEW statement is used for this purpose. The syntax is:

CREATE LOCAL TEMPORARY VIEW <view_name>
   AS <query_expression>

This statement creates a temporary view that is visible within the module for the duration of the session. It is assumed that these views are destroyed when the session terminates, so explicit DROP VIEW statements are not shown.


Special Value, ALL()

Special Value: ALL()

The FACT table contains rows that represent aggregates, as well as rows that represent atomic data. The aggregate rows have a special value in the columns that represent aggregated levels — for example, in the CITY and ZIPCODE columns of a row that contains the STATE level aggregation. Because queries for mapping MDX make heavy use of such special values, this makes it easier to define cleaner and simpler semantics for this.

This special value is denoted syntactically as the result of the built-in function ALL(). Semantics of this value are determined by the following rules:

  • ALL() = ALL() is always true.

  • ALL() <comparison_operator> <any_non-ALL()_value> is a well-formed expression, regardless of the data type of <any_non-ALL()_value>.

  • The semantics of comparing ALL() to any non-ALL() value are the same as comparing NULL to any non-NULL value. Specifically, ALL() != <any_non-ALL()_value> is always true.

  • NULL collates before ALL(), and ALL() collates before all non-NULL and non-ALL() values.

  • With respect to NULL comparison, ALL() behaves like all other non-NULL values.


Rank Operations

Rank Clause

Order of tuples is important in the sets processed by MDX. To preserve order, you need to generate a RANK column for every set. That column contains ordering information. The first tuple has a rank value of 1, the second has a rank value of 2, and so on.

Rank values are generated by an extension to SQL: the RANK clause. This clause occurs at the end of a query expression (such as a SELECT statement). The input to this clause is a table and its output is also a table. The syntax is:

<rank_clause> ::=
          query_expression
          RANK ROWS | LEVELS [AS <column_name>]
          [RANKORDER BY <sort_specification_list>]
          [RANKGROUP BY <grouping_column_reference_list>]

The semantics of this clause are as follows:

  • If a RANKGROUP BY clause is present, then the set of incoming rows is divided into groups (according to the RANKGROUP BY clause). Otherwise, there is only one group. The following steps are performed on each group:
    1. If a RANKORDER BY clause is present, then the rows are sorted accordingly.

    2. Rows are counted in their sort order (if any) by using two counters, row count and level count. Both counters start at 0. The first row increments both counts. Every input row increments row count by 1. The level count is incremented by 1 if a sort specification list is present and an input row differs from its preceding row in at least one element of the sort specification list.
  • The value for the new column (the one specified in <column_name> above) depends on whether ROWS or LEVELS was specified. If ROWS was specified, then the value is row count. If LEVELS is specified, then it is level count.

  • If a name for the new column is not specified (using the AS <column_name> clause), then its name is RANK.

  • There is one output row for every input row. The output rows have the same columns as the input rows, and have the extra rank column whose value is as described above.

RANKTOP Clause

Many OLAP operations require that a set of input tuples be separated into groups, and the top x rows be picked from each group (according to some criteria). As always, there is the requirement that the ordering be preserved. Clearly, what is required is an enhanced RANK clause that has the ability to pick the top x rows. This is achieved by the RANKTOP clause.

This clause occurs at the end of a query expression (that is, a SELECT statement). The input to this clause is a table and its output is also a table. The syntax is

<ranktop_clause> ::=
          <query_expression>
          RANKTOP <numeric_value_expression> ROWS
               | LEVELS [AS <column_name>]
          [RANKORDER BY <sort_specification_list>]
          [RANKGROUP BY <grouping_column_reference_list>]

The semantics of this clause are as follows:

  • The first five items of semantics description are the same as the first four items of the RANK clause.

  • An output row is produced if the value of the new column (the one specified in <column_name>) is less than or equal to <numeric_value_expression>.The output rows have the same columns as the input rows, and have the extra RANK column whose value is as described above.

Multiple RANK and RANKTOP Clauses

Both RANK and RANKTOP clauses are part of a query expression. That is, their output is a table. Therefore, if a query expression has more than one RANK or RANKTOP clause, then they are evaluated in the order of appearance. That is, the first clause results in a table that is used as the input to the second clause, which is in turn used as an input to the third clause, and so on.

For example, in the query

SELECT Name, Newrank AS Rank
FROM
         (((SELECT Name, "1" AS Src, Rank FROM S1
         UNION ALL
         SELECT Name, "2" AS Src, Rank FROM S2)
      RANKTOP 1 ROWS RANKORDER BY Src, Rank RANKGROUP BY Name)
   RANK ROWS AS NewRank RANKORDER BY Src, Rank)
ORDER BY Rank

The innermost query expression (indented to the maximum) is used as an input to the RANKTOP clause. The output of the RANKTOP clause, which is again a table (shown above as being indented by two levels), is used as an input to the RANK clause. The output of the RANK clause is also a table (shown above as being indented by one level), which is used as input to the FROM clause.


New Functions

The following functions are new. They operate on a fully qualified member name. These functions are merely string manipulation functions. Therefore, they are not aware of any metadata beyond the input fully qualified name.

PARENT(<fq_name>)

Returns the fully qualified parent name of <fq_name>. Semantically, this means: output the substring of <fq_name> that is the result of chopping off the rightmost period (.) and everything after it. If there are no periods in the input string, return NULL.

ANCESTOR(<fq_name>, <generation>)

Returns the ancestor of <fq_name> at <generation> levels above <fq_name>. For example,

ANCESTOR(Geography.All.USA.Kansas.Topeka, 1) =
   Geography.All.USA.Kansas

Semantically, the output results from removing the <generation>th period and everything following it, and then returning the resulting string. If LEVEL(<fq_name>) + 1 <= <generation>, then the result is the dimension name.

LEVEL(<fq_name>)

Returns the zero-based level (root = 0) of the input member. Semantically, the output is equal to counting the number of periods in <fq_name> and decrementing it by 1. If <fq_name> does not have any periods, then –1 is returned.

REVERSE(<fq_name>)

Reverses <fq_name>. For example, “Geography.All.USA.Kansas” is converted to “Kansas.USA.All.Geography.”

COMPONENT(<fq_name>, <level>)

Gives the fully qualified name of the ancestor of the input member at the specified level. The special level –1 can be used to get the dimension name. NULL is returned if <level> is less than –1, ALL() is returned if <level> is greater than LEVEL(<fq_name>). For more information on ALL(), see “Special Value, ALL().”


Sample Cube

The examples below assume a cube called SalesCube with the following structure:

  • The cube has four dimensions: Geography, Measures, Time, Products.

  • Geography dimension has:
    • Level 0: All; one member, also called All

    • Level 1: Country (Two members: USA, Canada)

    • Level 2: State (USA has two states: Kansas, New York. Canada has two states, BC and Quebec)

    • Level 3: City (Kansas has Topeka, Wichita. New York has Buffalo, Ithaca. BC has Vancouver, Victoria. Quebec has Montreal, Sherbrooke)

    • Level 4: Zipcode
  • Time has:
    • Level 0: All; one member, also All.

    • Level 1: Year. Members are the usual suspects.

    • Level 2: Month. Members are the usual suspects

    • Level 3: Day. Members are the usual suspects
  • There are two measures: Sales and Cost.

  • There are many product levels with many members, but for this example, assume “Office” as a member belonging to the “ProductLine” level (at level 1), and Microsoft Access and Word belonging to the Products level (at level 2).


Mapping Sets

The fundamental concept in MDX is that of a set. Each axis is composed of a set of tuples. The slicer condition (WHERE clause) is a set (usually with just one tuple). This section describes how to convert a set expression into a table.

Note This section is not exhaustive. That is, it does not describe how to map every single set value expression. Several representative set value expressions are considered for the purposes of illustration. The intention is to convey the concepts, which will help vendors to come up with mappings optimized for their SQL implementations.


Tables and Sets

Dimensionality of a Set

Many MDX functions take a set or sets as input, and return a set as output. A set consists of tuples. A tuple is a vector of members. Let n be the number of members in any given tuple. The value n is called as the dimensionality of the set. Note the following rules:

  • Each member of the tuple must be from a different dimension.

  • The dimension of the pth member of a tuple is the same across all tuples. (The value of p ranges from 1 to n.)

Table Associated with a Set

When mapping a statement in MDX, a set maps to a table. This table has n+1 columns, where n is the dimensionality of the set. There is one column for each dimension, and a RANK column that has the rank of a tuple within the set. This column is needed because sets in MDX are ordered. A table in SQL is not.

Any intermediate table in the expansion of a MDX also has a RANK column. The expansions below use this implicit column in an ORDER BY clause to preserve the order. Note that the notion of rank is somewhat similar to that of an identity column.

Use of ORDER BY

Many sections in this document deal with how to expand MDX set value expressions into equivalent SQL statements. For set value expressions that return another set (such as ORDER and CROSSJOIN), the final result of the expansion is a SELECT statement (which returns a virtual table, also known as set). This SELECT statement always contains an ORDER BY on the RANK column. This is appropriate for a section that deals only with mapping a set value expression. However, it is inefficient to do this when evaluating a full-fledged MDX statement that has multiple such functions along an axis. In that case, the ORDER BY is done at the end after evaluating the entire MDX expression.


Literal Sets

Literal set, as used here, means a set that is specified by an enumerated list of tuples. Consider the set

{(x,y), (a,b)}

In this set, x and a are members of dimension Name1; y and b belong to dimension Name2. The literal set shown above can be expanded into a table value expression such as the following:

CREATE LOCAL TEMPORARY VIEW T1(Name1, Name2, Rank) AS
SELECT Name1, Name2
FROM ( VALUES (x,y,1), (a,b, 2) ) AS T2(Name1, Name2, Rank)
ORDER BY Rank

In the above statement, the FROM clause contains a table specification, which is in the form of an enumerated list of rows. The SQL-92 <table_value_constructor> syntax has been used for this purpose.

The detailed syntax, derived from SQL-92, is as follows:

<table_reference> ::= <derived_table> [AS] <correlation_name>
     [<left_paren> <derived_column_list> <right_paren> ] | ...
<derived_table> ::= <table_subquery>
<table_subquery> ::= ( <simple_table> | ... )
<simple_table> ::= <table_value_constructor> |...
<table_value_constructor> ::=
     VALUES <row_value_constructor> [ {, <row_value_constructor>} ... ]
<row_value_constructor> = ::=
     ( <value_expression> [ {, <value_expression> ... ] ) | ...

This nets out to the fact that a table can be defined from literals as:

<literal_table> ::= (VALUES(<literal>[{, <literal>}...)
                    [{,( <literal> [ {,<literal>}...)}...] )
                    [AS] <correlation_name>[ <left_paren>
                         <derived_column_list> <right_paren> ]

The result is a table T for which each row is a tuple of the input set. OLE DB for OLAP requires that the member names x, y, z, a, b, and c be unambiguous names. Unambiguous names need not be fully qualified. They are just qualified to the extent needed to make them unique. When generating an SQL expression for it, the expression must return fully qualified names for these members. This is required because the MEMBER table has fully qualified names, and it does equijoins with this table while evaluating an MDX expression.

In order to get a table T from T1, which contains the fully qualified names, you must use the following:

CREATE LOCAL TEMPORARY VIEW T(Name1, Name2, Rank) AS
SELECT M1.Name1, M2.Name2, T1.Rank
FROM MEMBER M1, MEMBER M2, T1
WHERE M1.Name LIKE ("%." || T.Name1)
AND M2.Name LIKE ("%." || T.Name2)
ORDER BY T1.Rank

In the above query, “||” is the string concatenation operator.


UNION

Removing Duplicates

Consider UNION(S1, S2) where:

S1 = {Kansas, Buffalo, Topeka, USA, Canada}

S2 = {Kansas, Topeka, Wichita, Canada}

  1. As shown in “Literal Sets,” make two tables — S1 and S2 — that contain the fully qualified member names in the sets S1 and S2 respectively.

  2. The following query does the union:
    SELECT Name, Newrank AS Rank
FROM
         (((SELECT Name, "1" AS Src, Rank FROM S1
         UNION ALL
         SELECT Name, "2" AS Src, Rank FROM S2)
      RANKTOP 1 ROWS RANKORDER BY Src, Rank RANKGROUP BY Name)
   RANK ROWS AS NewRank RANKORDER BY Src, Rank)
ORDER BY Rank

    In English, the query:

    1. Makes a union of S1 and S2, with the Src column tagging which set a row came from.

    2. The RankTop operation groups duplicates into separate groups by using the GROUP BY clause and orders each group by Src, Rank, and picks up the first one. This ensures that duplicates are eliminated from the tail.

    3. The Rank orders the filtered rows by Src and Rank and assigns a new rank to each row so that input ordering is preserved.

Retaining Duplicates

Consider UNION(S1, S2, ALL). As in the previous section, tables are generated for S1 and S2. Then you use the following query:

SELECT Name, Newrank AS Rank
FROM
      ((SELECT Name, "1" AS Src, Rank FROM S1
      UNION ALL
      SELECT Name, "2" AS Src, Rank FROM S2)
   RANK ROWS AS NewRank RANKORDER BY Src, Rank)
ORDER BY Rank

This is simpler because you need not remove duplicates. After the union, the Rank operation generates a rank value for each of the rows of the table joined by union. The outer SELECT emits this table, ordering it by the NewRank (renamed as Rank) column.

Generalizing for Sets with Arbitrary Dimensionality

Each tuple has more than one element for these sets. Replace the Name column in the select list of each SELECT statement above by a list of columns (which is equivalent to the number of dimensions in the input sets).


EXCEPT

Removing Duplicates

Consider EXCEPT(S1, S2), where:

S1 = {Kansas, Buffalo, Buffalo, Topeka, Topeka, Wichita, Canada, BC}

S2 = {Buffalo, Topeka, USA, Quebec}

  1. As shown in "Literal Sets," make two tables — S1 and S2 — that contain the fully qualified member names in the sets S1 and S2, respectively.

  2. The following query does the EXCEPT operation:
    SELECT Name, Newrank AS Rank
FROM
         (((SELECT Name, Rank FROM S1
         WHERE S1.Name NOT IN (SELECT Name FROM S2))
      RANKTOP 1 LEVELS RANKORDER BY Rank RANKGROUP BY Name)
   RANK ROWS AS NewRank RANKORDER BY Rank)
ORDER BY Rank

    In English, the query:

    1. Selects the rows of S1 that are not in S2.

    2. The RANKTOP operation groups duplicates into separate groups by using the GROUP BY clause and orders each group by Rank and picks up the first one. This ensures that duplicates are eliminated from the tail.

    3. The RANK operation orders the filtered rows by Rank and assigns a new rank to each row so that input ordering is preserved.

Note You cannot use the EXCEPT operation of SQL-92 because duplicate elimination is not guaranteed to remove duplicates from the tail.

Retaining Duplicates

Consider EXCEPT(S1, S2, ALL). As in the previous section, tables are generated for S1 and S2. Then you use the following query:

SELECT Name, Newrank AS Rank
FROM
      ((SELECT Name, Rank FROM S1
      WHERE S1.Name NOT IN (SELECT Name FROM S2))
   RANK ROWS AS NewRank RANKORDER BY Rank)
ORDER BY Rank

This is simpler because there is no need to remove duplicates. After the query that finds the difference, the RANK operation generates a rank value for each of the rows of the resulting table. The outer SELECT emits this table, ordering it by the NewRank (renamed as Rank) column.

Note You cannot use EXCEPT ALL from SQL-92 because its semantics are as follows: If a row r appears n times in S1 and m times in S2, then r appears exactly p times in the result, where p is the greater than m – n and 0. Moreover, the order of these p rows is not preserved.

Generalizing for Sets with Arbitrary Dimensionality

Each tuple has more than one element for these sets. Replace the NAME column in the select list of each SELECT statement above by a list of columns (equivalent to the number of dimensions in the input sets). Add as many NOT IN clauses in the WHERE as there are columns, one for each column.


INTERSECT

Removing Duplicates

Consider EXCEPT(S1, S2) where:

S1 = {Kansas, Buffalo, Buffalo, Topeka, Topeka, Wichita, Canada, BC}

S2 = {Buffalo, Topeka, Topeka, Canada, Quebec}

  1. As shown in "Literal Sets," make two tables — S1 and S2 — that contain the fully qualified member names in the sets S1 and S2, respectively.

  2. The following query does the intersect:
    SELECT Name, NewRank AS Rank
FROM
         (((SELECT Name, Rank FROM S1
         WHERE S1.Name IN (SELECT Name FROM S2))
      RANKTOP 1 LEVELS RANKORDER BY Rank RANKGROUP BY Name)
   RANK ROWS AS NewRank RANKORDER BY Rank)
ORDER BY Rank

    In English, the query:

    1. Selects the rows of S1 that are also in S2.

    2. The RANKTOP operation groups duplicates into separate groups by using the GROUP BY clause and orders each group by Rank and picks up the first one. This ensures that duplicates are eliminated from the tail.

    3. The RANK operation orders the filtered rows by Rank and assigns a new rank to each row so that input ordering is preserved.

Note You cannot use the INTERSECT operation of SQL-92 because duplicate elimination is not guaranteed to remove duplicates from the tail.

Retaining Duplicates

Consider INTERSECT(S1, S2, ALL). As in the previous section, tables are generated for S1 and S2. Then you use the following query:

SELECT Name, NewRank AS Rank
FROM
      ((SELECT Name, Rank FROM S1
      WHERE S1.Name IN (SELECT Name FROM S2))
   RANK ROWS AS NewRank RANKORDER BY Rank)
ORDER BY Rank

This is simpler because there is no need to remove duplicates. After the query that finds the intersect, the RANK operation generates a rank value for each of the rows of the resulting table. The outer SELECT emits this table, ordering it by the NewRank (renamed as Rank) column.

Note You cannot use INTERSECT ALL from SQL-92 because its semantics are as follows: If a row r appears n times in S1 and m times in S2, then r appears exactly p times in the result, where p is the lesser of m and n. Moreover, the order of these p rows is not preserved.

Generalizing for Sets with Arbitrary Dimensionality

Each tuple has more than one element for these sets. Replace the NAME column in the select list of each SELECT statement above by a list of columns (equivalent to the number of dimensions in the input sets). Add as many IN clauses in the WHERE as there are columns, one for each column.


CROSSJOIN

Consider CROSSJOIN(S1, S2). This can be expanded as follows:

  1. Make a table out of the sets S1 and S2. If a set is literal (that is, its members are explicitly enumerated), then you make this table as shown in "Literal Sets." If it is the result of an expression, look at the section dealing with that expression to see how to get the table.

  2. The following query performs the CROSSJOIN operation:
    SELECT S1.*\Rank, S2.*\Rank, Newrank AS Rank
FROM (SELECT S1.*\Rank, S2.*\Rank FROM S1, S2)
RANK ROWS AS NewRank RANKORDER BY Rank1, Rank2

    The notation “S1.*\Rank” means “All columns of S1, minus the Rank column.”


HIERARCHIZE

The first method discussed is how HIERARCHIZE works for a set whose dimensionality is 1. This method will be generalized to sets with arbitrary dimensionality.

Sets with Dimensionality = 1

Consider HIERARCHIZE(S), where S = {Kansas, USA, Canada, Buffalo, Topeka}

  1. Gather information:
    1. As detailed in "Literal Sets," make two tables — S1 and S2 — that contain the fully qualified member names in the sets S1 and S2, respectively.

      Note In a full-fledged MDX expression, there may be no need to do this because the input set to HIERARCHIZE may be derived from another expression.

    2. Use the following query to verify that all members belong to the same dimension. If the returned count is greater than 1, then it is an invalid set.
      SELECT COUNT DISTINCT COMPONENT(S.Name, -1)
FROM S
    3. Get the dimension name by using this query:
      SELECT DISTINCT COMPONENT(S.Name, -1)
FROM S
    4. Get the level number of each level represented in the input set:
      SELECT DISTINCT LEVEL(S.Name)
FROM S
  2. The objective is to create a table T that looks like this:
Name | Level | Rank1 | Rank2 | Rank3
Geography.All.USA.Kansas | 2 | 1 | 14 | NULL
Geography.All.USA | 1 | 1 | NULL | NULL
Geography.All.Canada | 1 | 3 | NULL | NULL
Geography.All.USA.NewYork.Buffalo | 3 | 1 | 27 | 5
Geography.All.USA.Kansas.Topeka | 3 | 1 | 14 | 1

For each member of set S, there should be a RANKX column, which contains the rank of each of its ancestors. To get the hierarchized set from T, just use this:

SELECT Name, Rank
FROM
SELECT * FROM T
    RANK ROWS AS Rank RANKORDER BY Rank1, Rank2, Rank3
ORDER BY Rank

Note The number of RANKX columns in T is m + 1 where m = maximum level in the set S. This is the case even if there are unrepresented levels in S, such as when there are members from COUNTRY and CITY level, but none from the STATE level.

To do this, you:

  1. Define a table that has elements of set S and its levelo:
    CREATE LOCAL TEMPORARY VIEW S1(Name)
AS
SELECT S.Name
FROM S JOIN members AS M ON(S.Name = M.Level_Name)
  2. Now make a UNION of three SELECT operations (three is the level of the lowermost member in S):
    SELECT S1.*, M1.Natural_Sort_Rank AS Rank1,
   NULL AS Rank2, NULL AS Rank3
FROM S1 JOIN Members AS M1 ON S1.Name = M1.Member_Name
WHERE S1.Level = 0
UNION
SELECT S1.*, M1.Natural_Sort_Rank AS Rank1,
   M2.Natural_Sort_Rank AS Rank2, NULL AS Rank3
FROM (S1 JOIN Members AS M2 ON S1.Name = M2.Member_Name)
JOIN Members AS M1 ON ANCESTOR(S1.Name, 1)= M1.Member_Name
WHERE S1.Level = 1
UNION
SELECT S1.*, M1.Natural_Sort_Rank AS Rank1,
   M2.Natural_Sort_Rank AS Rank2, M3.Natural_Sort_Rank AS M3
FROM (((S1 JOIN Members AS M3 ON S1.Name = M3.Name)
JOIN Members AS M2 ON ANCESTOR(S1.Name, 2) = M2.Name)
JOIN Members AS M1 ON ANCESTOR(S1.Name, 3) = M1.Name)
WHERE S1.Level = 2

    Note The WHERE clause on each UNION iterates from Level = 0 through the level of the lowermost member in S1. Note also that the ANCESTOR function in the FROM iterates from 1 through 1 + the level of the lowermost member in S1.

Generalizing for Sets with Arbitrary Dimensionality

Consider HIERARCHIZE(S), where

S = {(Kansas, 1996), (Buffalo, 1995.Q4), (USA, 1995.Mar), (Buffalo, 1995), (USA, 1995), (Kansas, 1996.Q4), (Kansas, 1996.Q1), (USA, 1995.Q1)}

The steps in hierarchizing this set are:

  1. Let S(Name1, Name2, Rank) be the table associated with the set S.

  2. Let S1, S2 be tables:
    • First step:
      CREATE LOCAL TEMPORARY VIEW S1(Name, Rank)
AS
SELECT DISTINCT Name1, Rank FROM S
    • Second step:
      CREATE LOCAL TEMPORARY VIEW S2(Name, Rank)
AS
SELECT DISTINCT Name2, Rank FROM S
  3. HIERARCHIZE S1 and S2 as explained in "Sets with Dimensionality = 1." Let the resulting hierarchized sets be D1 and D2.

  4. Now use the following query to yield the hierarchized set:
    SELECT S.Name1, S.Name2, NewRank as Rank
FROM
   (SELECT S.Name1, S.Name2
   FROM (S JOIN D1 ON S.Name1 = D1.Name)
      JOIN D2 ON S.Name2 = D2.Name)
   RANK ROWS AS NewRank RANKORDER BY D1.Rank, D2.Rank

Generalizing this to sets with dimensionality greater than two should be straightforward.


A Complete Example

Consider the query:

SELECT
   CROSSJOIN ({Access, Word}, {Kansas, Buffalo, Topeka, USA, Canada})
      ON COLUMNS,
   {[1994].Q1.MEMBERS, [1994].Q2, [1994].Q3, [1994].Q4.MEMBERS} ON ROWS
FROM SalesCube
WHERE (Sales)


COLUMNS Axis Expression

The COLUMNS axis expression consists of the CROSSJOIN function applied on two literal sets. By applying the mappings described in "Literal Sets," you get two virtual tables. These virtual tables are used as input to the SQL statements described in "CROSSJOIN." The result of these operations yields table C:

Name1 | Name2 | Rank
Products.All.Office.Access | Geography.All.USA.Kansas | 1
Products.All.Office.Access | Geography.All.USA.NewYork.Buffalo | 2
Products.All.Office.Access | Geography.All.USA.KansasTopeka | 3
Products.All.Office.Access | Geography.All.USA | 4
Products.All.Office.Access | Geography.All.Canada | 5
Products.All.Office.Word | Geography.All.USAKansas | 6
Products.All.Office.Word | Geography.All.USA.NewYork.Buffalo | 7
Products.All.Office.Word | Geography.All.USA.KansasTopeka | 8
Products.All.Office.Word | Geography.All.USA | 9
Products.All.Office.Word | Geography.All.Canada | 10

Denote by Cprime the following table:

Product | ProdLine | Product. All | City | State | Country | Geo- graphy. All | Rank
Products. All.Office. Access | Products. All.Office. | Product. All | ALL() | Geo- graphy. USA. Kansas | Geo- graphy. USA | Geo- graphy. All | 1
Products. All.Office. Access | Products. All.Office. | Product. All | Geo- graphy. USA. NewYork. Buffalo | Geo- graphy. USA. NewYork | Geo- graphy. USA | Geo- graphy. All | 2
Products. All.Office. Access | Products. All.Office. | Product. All | Geo- graphy. USA. Kansas. Topeka | Geo- graphy. USA. Kansas | Geo- graphy. USA | Geo- graphy. All | 3
Products. All.Office. Access | Products. All.Office. | Product. All | ALL() | ALL() | Geo- graphy. USA | Geo- graphy. All | 4
Products. All.Office. Access | Products. All.Office. | Product. All | ALL() | ALL() | Geo- graphy. Canada | Geo- graphy. All | 5
Products. All.Office. Word | Products. All.Office. | Product. All | ALL() | Geo- graphy. USA. Kansas | Geo- graphy. USA | Geo- graphy. All | 6
Products. All.Office. Word | Products. All.Office. | Product. All | Geo- graphy. USA. NewYork. Buffalo | Geo- graphy. USA. NewYork | Geo- graphy. USA | Geo- graphy. All | 7
Products. All.Office. Word | Products. All.Office. | Product. All | Geo- graphy. USA. Kansas. Topeka | Geo- graphy. USA. Kansas | Geo- graphy. USA | Geo- graphy. All | 8
Products. All.Office. Word | Products. All.Office. | Product. All | ALL() | ALL() | Geo- graphy. USA | Geo- graphy. All | 9
Products. All.Office. Word | Products. All.Office. | Product. All | ALL() | ALL() | Geo- graphy. Canada | Geo- graphy. All | 10

Cprime is obtained from C by using the following steps:

  1. Find out the number of nested dimensions in the axis expression:
    SELECT COUNT DISTINCT COMPONENT(Name, -1) FROM C

    For our example, this is 2.

  2. Find the names of each dimension:
    SELECT DISTINCT COMPONENT(Name, -1) FROM C

    For our example, this is Products, Geography.

  3. For each dimension, find out the deepest member in the axis expression:
    SELECT MAX(LEVEL(Name)) FROM C GROUP BY COMPONENT(Name, -1)

    For our example, this is 3 for Geography and 2 for Products.

  4. Make two tables — C1 and C2. C1 has all members in C from Products and C2 has all members in C from Geography.
    CREATE LOCAL TEMPORARY VIEW C1(Name, Rank) AS

   SELECT Name, NewRank AS Rank
FROM
     (SELECT Name1 FROM C WHERE COMPONENT(Name, -1) = "Products"
     RANK ROWS AS NewRank RANKORDER BY Rank)

CREATE LOCAL TEMPORARY VIEW C2(Name, Rank) AS

   SELECT Name, NewRank AS Rank
FROM
     (SELECT Name2 FROM C WHERE COMPONENT(Name, -1) = “Geography”
          RANK ROWS AS NewRank RANKORDER BY Rank)
  5. Now make Cprime:
    SELECT Product AS COMPONENT(C1.Name, 2),
   ProdLine AS COMPONENT(C1.Name, 1),
   Product.All AS COMPONENT(C1.Name, 0),
   City AS COMPONENT(C2.Name, 3),
   State AS COMPONENT(C2.Name, 2),
   Country AS COMPONENT(C2.Name, 1),
   Geography.All AS COMPONENT(C2.Name, 0)
   NewRank AS Rank
FROM
   (C1 CROSS JOIN C2) RANK ROWS AS NewRank
RANKORDER BY C1.Rank, C2.Rank


ROWS Axis Expression

In a manner similar to that described in “COLUMNS Axis Expression,” obtain the tables R and Rprime.

Table R:

Name | Rank
Time.All.1994.Jan | 1
Time.All.1994.Feb | 2
Time.All.1994.Mar | 3
Time.All.1994.Q2 | 4
Time.All.1994.Q3 | 5
Time.All.1994.Oct | 6
Time.All.1994.Nov | 7
Time.All.1994.Dec | 8

Table Rprime:

Month | Quarter | Year | Time.All | Rank
Time.All.1994.Jan | Time.All.1994.Q1 | 1994 | Time.All | 1
Time.All.1994.Feb | Time.All.1994.Q1 | 1994 | Time.All | 2
Time.All.1994.Mar | Time.All.1994.Q1 | 1994 | Time.All | 3
ALL() | Time.All.1994.Q2 | 1994 | Time.All | 4
ALL() | Time.All.1994.Q3 | 1994 | Time.All | 5
Time.All.1994.Oct | Time.All.1994.Q4 | 1994 | Time.All | 6
Time.All.1994.Nov | Time.All.1994.Q4 | 1994 | Time.All | 7
Time.All.1994.Dec | Time.All.1994.Q4 | 1994 | Time.All | 8


Getting Cell Values

The query for getting the cell values is as follows:

SELECT Value, NewRank AS Rank
FROM
   (SELECT Value
   FROM
      (Fact AS F JOIN Cprime AS C ON
         (   C.[Product.All] = F.[Product.All]
         AND C.ProdLine = F.ProdLine
         AND C.Product = F.Product
         AND C.[Geography.All] = F.[Geography.All]
         AND C.Country = F.Country
         AND C.State = F.State
         AND C.City = F.City
         AND F.Zipcode = ALL()
         )
      JOIN Rprime AS R ON
         (   R.[Time.All] = F.[Time.All]
         AND R.Year)  = F.Year
         AND R.Quarter = F.Quarter
         AND R.Month = F.Month
         AND F.Day = ALL()
         )
      )
   WHERE F.Measures = "Sales")
   RANK ROWS AS newrank RANKORDER BY Rank1, Rank2
ORDER BY Rank


Chapter 7

OLE DB for OLAP Reference

This chapter describes all interfaces and methods specific to OLE DB for OLAP. However, implementing a multidimensional consumer or provider requires additional methods and interfaces not dealt with here. They are part of OLE DB, and more information about them can be found in the OLE DB Programmer’s Reference.


IMDDataset

IMDDataset is a mandatory interface on the Dataset object. This interface provides methods for finding information about axes, axis rowsets, cell data, and the Command object that created the dataset.

Method | Description
FreeAxisInfo | Frees the array of MDAXISINFO structures returned by IMDDataset::GetAxisInfo method.
GetAxisInfo | Returns the number of axes in the dataset and detailed information about each axis.
GetAxisRowset | Returns the specified axis rowset.
GetCellData | Returns the cell value and formatted value of a range of cells.
GetSpecification | Returns an interface on the Command object that created the dataset.


IMDDataset::FreeAxisInfo

Frees the array of MDAXISINFO structures returned by IMDDataset::GetAxisInfo.

HRESULT FreeAxisInfo(
   ULONG            cAxes,
   MDAXISINFO*      rgAxisInfo);

Parameters

cAxes[in]

Number of elements in the rgAxisInfo array.

rgAxisInfo[in]

A pointer to an array of MDAXISINFO structures.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
cAxes is not zero and rgAxisInfo is NULL.

cAxes is zero and rgAxisInfo is not NULL.

Comments

rgAxisInfo must point to the array returned by IMDDataset::GetAxisInfo. If not, the effect of this method is undefined.

If cAxes is zero or rgAxisInfo is a null pointer, then this method does nothing.


IMDDataset::GetAxisInfo

Returns the number of axes in the current dataset and detailed information about each axis.

HRESULT GetAxisInfo(
   ULONG*            pcAxes,
   MDAXISINFO**      prgAxisInfo);

Parameters

*pcAxes[out]

A pointer to a long integer. The provider populates this with the number of elements in the prgAxisInfo array, which is the number of axes in the dataset.

prgAxisInfo[out]

A pointer to an array of MDAXISINFO structures. Each member of this array contains information about one axis. The provider allocates this array as long as prgAxisInfo is not a null pointer.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
pcAxes was a null pointer.

Both pcAxes and prgAxisInfo were null pointers.

E_OUTOFMEMORY
The provider was unable to allocate sufficient memory to return the array of MDAXISINFO structures.

Comments

Information about the slicer dimensions is returned as an MDAXISINFO structure, whose iAxis is MDAXIS_SLICERS. Information about each axis is available in the form of the MDAXISINFO structure. (For a description of the various structure fields, see “MDAXISINFO Structure” in Chapter 4).

When moving from one element of the MDAXISINFO array to the next, consumers should use the macro PMDAXISINFO_GETAT or MDAXISINFO_GETAT. These macros are defined in the header as:

// Return a ptr to a MDAXISINFO struct given a pointer to an array of
// MDAXISINFO structures typically used by applications to iterate over
// the array returned by IMDDataset::GetAxisInfo
#define PMDAXISINFO_GETAT(rgAxisInfo,iAxis) \
   ((MDAXISINFO *)(((BYTE*)rgAxisInfo) + iAxis * rgAxisInfo[0].cbSize))

// Return a MDAXISINFO structure given a pointer to an array of
// MDAXISINFO structures typically used by applications to iterate over
// the array returned by IMDDataset::GetAxisInfo
#define MDAXISINFO_GETAT(rgAxisInfo,iAxis) \
   (*PMDAXISINFO_GETAT(rgAxisInfo, iAxis))

PMDAXISINFO_GETAT takes a pointer to an MDAXISINFO array and a desired axis (index into the array). It returns a pointer to the MDAXISINFO structure for that axis. MDAXISINFO_GETAT simply dereferences the pointer returned by PMDAXISINFO_GETAT.

For example, the following code processes the MDAXISINFO array returned by this method in a loop:

pIMDDataset->GetAxisInfo(&cAxes, &rgAxisInfo);
for (iAxis = 0; iAxis < cAxes; iAxis++)
{
   ProcessAxisRowset(PMDAXISINFO_GETAT(rgAxisInfo, iAxis);
}

Consumers must use IMDDataset::FreeAxisInfo to free memory allocated for prgAxisInfo. Using IMalloc::Free may not free all the memory allocated for the array of MDAXISINFO structures.

If prgAxisInfo is a null pointer, then the provider returns only the number of axes in the dataset.

If an error occurs, *pcAxes is set to 0 and *prgAxisInfo is set to NULL.


IMDDataset::GetAxisRowset

Returns the specified axis rowset.

HRESULT GetAxisRowset(
   IUnknown*   pUnkOuter,
   ULONG         iAxis,
   REFIID      riid,
   ULONG         cPropertySets,
   DBPROPSET   rgPropertySets[],
   IUnknown**   ppRowset);

Parameters

pUnkOuter[in]

A pointer to the controlling IUnknown if the axis rowset is being created as part of an aggregate. If IMDDataset isn’t being aggregated, it is a null pointer.

iAxis[in]

The numeric identifier of an axis.

OLE DB for OLAP defines manifest constants for commonly used axes: MDAXIS_COLUMNS, MDAXIS_ROWS, MDAXIS_PAGES, MDAXIS_SECTIONS, and MDAXIS_ CHAPTERS. They denote (respectively) the columns, rows, pages, sections, and chapters axes. For the purposes of this function, the set of slicer dimensions is treated as a special axis and is identified by the constant MDAXIS_SLICERS.

riid[in]

The IID of the requested rowset interface.

cPropertySets[in]

The number of DBPROPSET structures in rgPropertySets. If this is 0, the provider ignores rgPropertySets.

rgPropertySets[in/out]

An array of DBPROPSET structures, containing properties and values to be set. The properties specified in these structures must belong to the rowset property group. If the same property is specified more than once in rgPropertySets, which value to be used is provider-specific.

ppRowset[out]

A pointer to memory in which to return the requested interface pointer on the axis rowset. The returned rowset is read-only.

Return Code

S_OK
The method succeeded. In all DBPROP structures passed to the method, dwStatus is set to DBPROPSTATUS_OK.

DB_S_ERRORSOCCURRED
The rowset was opened but one or more properties — for which the dwOptions element of the DBPROP structure was DBPROPOPTIONS_SETIFCHEAP — were not set. The consumer checks dwStatus in the DBPROP structures to determine which properties weren’t set. The method can fail to set properties for a number of reasons, including:

  • The property wasn’t supported by the provider.

  • The property wasn’t in the rowset property group.

  • The property set wasn’t supported by the provider.

  • It wasn’t cheap to set the property.

  • The colid value in the DBPROP structure was invalid.

  • The value of dwOptions in the DBPROP structure was invalid.

  • The data type in vValue in the DBPROP structure wasn’t the data type of the property or wasn’t VT_EMPTY.

  • The value in vValue in the DBPROP structure was invalid.

  • The property’s value conflicted with an existing property.

  • A property was specified to be applied to all columns, but couldn’t be applied to one or more columns.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
ppRowset was a null pointer.

cPropertySets was greater than 0 and rgPropertySets was a null pointer.

In an element of rgPropertySets, cProperties wasn’t zero and rgProperties was a null pointer.

E_NOINTERFACE
The axis rowset didn’t support the interface specified in riid.

MD_E_INVALIDAXIS
iAxis wasn’t a valid axis for this Dataset.

DB_E_ERRORSOCCURRED
No rowset was returned because one or more properties — for which the dwOptions element of the DBPROP structure was DBPROPOPTIONS_REQUIRED — were not set. The consumer checks dwStatus in the DBPROP structures to determine which properties weren’t set. None of the properties are remembered. The method can fail to set properties for any of the reasons specified in DB_S_ERRORSOCCURRED except for the reason that states that it wasn’t cheap to set the property.

DB_E_NOAGGREGATION
pUnkOuter wasn’t a null pointer and the axis rowset doesn’t support aggregation.

Comments

An axis rowset contains the name, display caption, level name, depth, and number of children for each tuple component or member along the axis. The axis rowset also contains a column that has the ordinal for each axis coordinate. This ordinal can be used when calculating the cell ordinal for fetching the cell data with.IMDDataset::GetCellData.

Additional columns may be present for a dimension if:

  • The provider returns provider-specific columns.

  • The consumer has requested to see member properties using the DIMENSION PROPERTIES clause in the SELECT statement.

rgcColumns in the MDAXISINFO structure points to an array containing the number of columns in the axis rowset for each constituent dimension.

The axis for which the rowset is requested is identified by its numeric identifier.

For a complete description of the columns in an axis rowset and their data types, see “Axis Rowsets” in Chapter 3.

The set of slicer dimensions is treated as a special axis and is identified by the axis identifier MDAXIS_SLICERS. Calling this method with iAxis set to MDAXIS_SLICERS returns information about the slicer dimensions in the form of an axis rowset. For more information, see “Finding Slicer Information” in Chapter 3.

If an error occurs, *ppRowset is set to NULL.


IMDDataset::GetCellData

Returns the cell value, formatted value, cell ordinal number, and other requested properties of a range of cells.

HRESULT GetCellData(
   HACCESSOR         hAccessor,
   ULONG               ulStartCell,
   ULONG               ulEndCell,
   VOID*               pData);

Parameters

hAccessor[in]

The handle of the accessor to use. If hAccessor is the handle of a null accessor, then IMDDataset::GetCellData doesn’t get any data or property values.

ulStartCell[in]

The cell ordinal of the first cell in the range.

ulEndCell[in]

The cell ordinal of the last cell in the range.

pData[out]

A pointer to the 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 (cell properties) for all rows (cells) bound by the accessor is set to one of DBSTATUS_S_OK, DBSTATUS_S_ISNULL, or DBSTATUS_S_TRUNCATED.

DB_S_ERRORSOCCURRED
An error occurred while returning data for one or more columns (cell properties) in one or more rows (cells), but data was successfully returned for at least one property of one cell. To determine the cells or properties for which data was returned, the consumer checks the status values. For a list of status values that can be returned by this method, see “Possible Return and Status Values” in the comments section.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
pData was a null pointer and the accessor wasn’t a null accessor.

ulStartCell or ulEndCell is invalid.

DB_E_BADACCESSORHANDLE
hAccessor was invalid. Providers aren’t required to check for this condition, because doing so might slow the method significantly.

DB_E_BADACCESSORTYPE
The specified accessor was not a row accessor.

DB_E_ERRORSOCCURRED
Errors occurred while returning data for all properties of all cells. To determine what errors occurred, the consumer checks the status values. For a list of status values that can be returned by this method, see “Possible Return and Status Values” in the comments section.

MD_E_INVALIDCELLRANGE
cbRowSize of the IAccessor::CreateAccessor call that created hAccessor was 0, and ulStartCell was not equal to ulEndCell.

ulStartCell was greater than ulEndCell and ulEndCell wasn’t zero.

ulStartCell, ulEndCell, or both specified a cell ordinal that wasn’t in the dataset.

If this method performs deferred accessor validation and that validation takes place before any data is transferred, it can also return for the reasons listed in the corresponding DBBINDSTATUS values in IAccessor::CreateAccessor any of the following return codes:

  • E_NOINTERFACE

  • DB_E_BADBINDINFO

  • DB_E_BADORDINAL

  • DB_E_BADSTORAGEFLAGS

  • DB_E_UNSUPPORTEDCONVERSION

Comments

This method allows for the retrieval of a dataset slice. The consumer identifies the desired slice by specifying the ulStartCell and ulEndCell parameters; ulStartCell is the upper left corner of the slice and ulEndCell is the lower right corner of the slice. Because these parameters are used to identify the slice, not all cells between ulStartCell and ulEndCell are necessarily fetched. This is illustrated in the following diagram of a two-axis dataset. The heavily outlined cells are the slice of the dataset identified by ulStartCell and ulEndCell. They are fetched. The shaded cells have ordinal values less that ulEndCell and greater than ulStartCell, but are not fetched.

Determining Cell Ordinals

To facilitate navigation among cells, OLE DB for OLAP uses an ordinal value system to uniquely identify cells within a dataset. The ordinal value is computed from the tuple ordinals of the tuples forming the cell coordinates. The tuple ordinal is the zero-based distance of the tuple from the origin of that axis.

Conceptually, cells are numbered in a dataset as if the dataset were a p-dimensional array, where p is the number of axes. Cells are addressed in row-major order. Below is the formula for calculating the ordinal number of a cell:

Available Data

Each cell is treated as a row in a rowset, and cell properties are treated as columns. The actual properties available depend on the MDX expression that generated the dataset. For a detailed discussion of cell properties, see “Retrieving Cell Properties” in Chapter 2. IColumnsInfo can also be used to find out the number of columns or properties available, and name, data type, and so on.

Special Cases

If the consumer specifies zero for ulStartCell and a nonzero value for ulEndCell, the provider fetches the cells in the slice identified by the first cell in the dataset and the specified ending cell. If the consumer specifies a nonzero value for ulStartCell and zero for ulEndCell, the provider fetches the cells in the slice identified by the specified start cell and the last cell in the dataset. Except for this case, it is an error to have ulStartCell greater than ulEndCell.

If the consumer specifies a ulStartCell or ulEndCell value that falls outside the dataset, the method fails.

Possible Return and Status Values

As the provider returns cell data, it sets the status field of each bound property value buffer. Using this value, the consumer can determine which property values were successfully fetched.

Note Consumers are strongly encouraged to bind to status values. If the status is other than DBSTATUS_S_OK, the provider is not obligated to touch the value part of the consumer’s buffer.

The method produces return values as follows:

  • Success. Return code is S_OK. No errors occurred while getting data for any properties. There may have been warnings (for example, data truncation warnings) when fetching property values for certain cells.

  • Partial success. Return code is DB_S_ERRORSOCCURRED. There were errors while retrieving cell properties for one or more cells. At least one property value was successfully retrieved for at least one cell.

  • Failure. Return code is DB_E_ERRORSOCCURRED. No property was successfully retrieved.

The following table lists all possible status values:

Status value 		Success, warning, or error
Data
returned

Description
DBSTATUS_S_OK | Success | Y | A non-NULL value was returned by the provider.
DBSTATUS_S_
ISNULL 		Success Y A NULL value was returned by the provider.
MDSTATUS_S_
CELLEMPTY 		Success Y The cell was empty. This occurs when there is no data in the cube at the coordinates of the cell. Using the NON EMPTY clause of the MDX statement guarantees that there are no empty cells.
DBSTATUS_S_
TRUNCATED 		Warning Y Variable length data or nonsignificant digits of numeric data were truncated.
DBSTATUS_E_
BADACCESSOR 		Error N Accessor validation was deferred and was performed while the method returned data. The binding was invalid for this column or parameter.
DBSTATUS_E_
CANTCONVERTVALUE 		Error N The data value couldn’t be converted for reasons other than sign mismatch or data overflow. For example, the data in the data source was corrupted but the row was still retrievable.
DBSTATUS_E_CANTCREATE Error N One of the following conditions:
  • The provider couldn’t allocate memory in which to return data.

  • The type indicator for the column was DBTYPE_IUNKNOWN and a storage object was already open on the column.

  • The type indicator for the column was DBTYPE_IUNKNOWN, the provider supports only one open storage object at a time (DBPROP_MULTIPLESTORAGEOBJECTS was VARIANT_FALSE), and a storage object was already open on the rowset.
DBSTATUS_E_
DATAOVERFLOW 		Error N Conversion failed because the data value overflowed the type specified for the value part in the consumer’s buffer.
DBSTATUS_E_
SIGNMISMATCH 		Error N Conversion failed because the data value was signed and the type specified for the value part in the consumer’s buffer was unsigned.
DBSTATUS_E_UNAVAILABLE | Error | N | Value couldn’t be determined by the provider. For example, the row was just created, the default for the column wasn’t available, and the consumer hadn’t yet set a new value.

Errors During Cell Fetch

When fetching a range of cells, there can be errors in fetching a given column or property of a given row or cell. Examples of such errors include truncation and arithmetic overflow. This does not necessarily invalidate all the data fetched. Errors are localized to individual columns and are indicated in the STATUS part of the consumer’s buffer for each column. This is a 2-byte field containing manifest constants denoting different error and status indicators. The status indicators are listed and their meanings are defined in the OLE DB Programmer’s Reference.

Using cbRowSize of IAccessor::CreateAccessor

Because this method fetches properties for more than one cell, the provider should know how long each row is. A “row” in this context means the area allocated in the consumer’s buffer to hold all properties pertaining to one cell. This information should be given in the cbRowSize parameter of IAccessor::CreateAccessor. If the value of this parameter is zero, it means that the consumer wants to fetch only one row of data (one cell). In this case, it is an error to specify a ulStartCell different from the ulEndCell.

The consumer’s buffer for holding cell data must be laid out in such a way that each row is directly adjacent to the next row. Within a row, the columns must be laid out at the offsets specified by the DBBINDING structure. There must be enough space allocated to fetch the requested number of cells. Consumers are advised to pad cbRowSize appropriately for machine alignment (Intel®, 4-byte).


IMDDataset::GetSpecification

Returns an interface pointer on the Command object that created the dataset.

HRESULT GetSpecification (
   REFIID          riid,
   IUnknown **      ppSpecification);

Parameters

riid[in]

The IID of the interface on which to return a pointer.

ppSpecification[out]

A pointer to memory in which to return the interface pointer. If the provider doesn’t have an object that created the dataset, it sets ppSpecification to a null pointer and returns S_FALSE. If IMDDataset::GetSpecification fails, it must attempt to set ppSpecification to a null pointer.

Return Code

S_OK
The method succeeded.

S_FALSE
The provider doesn’t have a Command object that created the dataset.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
ppSpecification was a null pointer.

E_NOINTERFACE
The object that created this dataset did not support the interface specified in riid.

Comments

This method makes no logical change to the state of the current dataset.


IMDFind

IMDFind is an optional interface on the dataset object. This interface consists of methods to find the ordinal number of a cell in the dataset and the ordinal number of a tuple on the axis.

Method | Description
FindCell | Finds and returns a cell ordinal based on an array of members and a starting cell ordinal in the dataset.
FindTuple | Returns a pointer to a tuple ordinal based on array of members, a starting ordinal, and an axis identifier.


IMDFind::FindCell

Finds and returns a cell ordinal based on an array of members and a starting cell ordinal in the dataset.

HRESULT FindCell(
   ULONG         ulStartingOrdinal,
   ULONG         cMembers,
   LPCOLESTR*   rgpwszMember,
   ULONG*      pulCellOrdinal);

Parameters

ulStartingOrdinal[in]

The cell ordinal from which to begin the search. For more information, see comments.

cMembers[in]

The cardinality of the rgpwszMember array.

rgpwszMember[in]

An array of members. Each element of the array contains a member along a given axis. For more information, see comments.

pulCellOrdinal[out]

The ordinal value of the cell specified by the members in the rgpwszMember array.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
pulCellOrdinal was a null pointer.

cMembers was less than 1.

MD_E_BADCOORDINATE
ulStartingOrdinal did not identify a cell in the dataset.

The set of members for a given axis did not identify a valid tuple.

The set of tuples resulting from the member array did not identify a valid cell in the Dataset beyond ulStartingOrdinal (inclusive).

One or more elements of the member array specified a member that does not exist.

One or more elements of the member array contained an ambiguous member specification.

Comments

The search is initiated from the cell identified by ulStartingOrdinal, in the direction of increasing axis ordinals. The starting cell is included in the search.

Each member string is null terminated.

The members must be listed in increasing order of numeric axis identifiers and members for a given axis must form a tuple on that axis. Constituent members of this tuple are listed in increasing order of dimension nesting — that is, members of the outermost dimension come first, followed by members of inner dimensions.

Note Members must be specified with enough qualifiers to create an unambiguous reference. Using MEMBER_UNIQUE_NAME from the MEMBERS rowset accomplishes this, because it is qualified to the extent required to create an unambiguous reference.

The members collectively specify a cell in the dataset. The provider generates a cell ordinal, identifying this cell, and returns a pointer to the ordinal, which can then be used as the input argument to IMDDataset::GetCellData(). For more information on cell ordinals and how they are generated, see IMDDataset::GetCellData.

This method searches for an exact match in that it compares for equality. Since members must be qualified to create an unambiguous reference, there is no possibility (by definition) of finding more than one cell.

Because the same tuple can be repeated on an axis, there can be more than one cell identified by the same set of members. The parameter ulStartingOrdinal can be used to find all such occurrences. You call this method successively, setting ulStartingOrdinal to 0 for the first call. Successive calls increment the value of *pulCellOrdinal returned by the previous call and use the new value for ulStartingOrdinal.

The default member of a dimension (if one exists) is used in the absence of an explicit member specification. To use the default member for a given dimension, you set the corresponding array element to a null pointer. If rgpwszMember is a null pointer, then default members are used for all the constituent dimensions.

If an error occurs, the contents of *pulCellOrdinal are undefined.

Note The FindCell method calls FindTuple for each axis, and uses the returned AxisTupleOrdinals to calculate the cell coordinate.


IMDFind::FindTuple

Returns a pointer to a tuple ordinal based on array of members, a starting ordinal, and an axis identifier.

HRESULT FindTuple(
   ULONG            ulAxisIdentifier,
   ULONG            ulStartingOrdinal,
   ULONG            cMembers,
   LPCOLESTR*      rgpwszMember,
   ULONG*         pulTupleOrdinal);

Parameters

ulAxisIdentifier[in]

The numeric identifier of an axis.

ulStartingOrdinal[in]

The ordinal of the starting tuple on the axis. FindTuple begins with this ordinal and continues in the direction of increasing axis ordinals.

cMembers[in]

The cardinality of the rgpwszMember array.

rgpwszMember[in]

An array of members. For more information, see comments.

pulTupleOrdinal[out]

A pointer to a tuple ordinal. The tuple ordinal is used to calculate the cell ordinal.

Return Code

S_OK
The method succeeded.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
pulTupleOrdinal was a null pointer.

cMembers was less than 1.

MD_E_INVALIDAXIS
ulAxisIdentifier did not identify a valid axis.

MD_E_BADTUPLE
The ulStartingOrdinal was greater than the number of tuples on the specified axis.

The array of members did not match any tuple on the axis occurring after ulStartingOrdinal (inclusive)

The array of members contained multiple members from the same dimension.

One or more elements of the member array specified a member that does not exist.

One or more elements of the member array contained an ambiguous member specification.

Comments

ulAxisIdentifier is the axis number. Axes are numbered starting from 0 (the column axis).

ulStartingOrdinal is the ordinal number of the tuple from which to begin the search. This tuple is included in the search. In many cases, the axis specification is such that the same tuple can occur more than once on an axis. Because FindTuple finds one tuple per call, ulStartingOrdinal can be used iteratively to find multiple occurrences of the same tuple. Successive calls to this method increment the *pulTupleOrdinal returned by the previous call, and use this as the value of ulStartingOrdinal for the next call.

rgpwszMember is an array of member names. Each name is null terminated. Member names must be qualified to the degree needed to avoid any ambiguity. Using the MEMBER_UNIQUE_NAME column of the MEMBERS rowset guarantees this. The members are in the order of dimension nesting: Members of the outermost dimension come first, followed by members of inner dimensions.

pulTupleOrdinal is the ordinal number of the tuple on the axis, as returned in *plTupleOrdinal. Each tuple along an axis is numbered, starting from zero and increasing as you move to the right. This ordinal number can be used to calculate cell ordinals, using the formula described in IMDDataset::GetCellData.

This method performs an exact match in that it compares for equality. Combined with the fact that members should be qualified enough to create an unambiguous reference, this means there is no possibility, by definition, of finding more than one cell.

Note that a tuple ordinal for each tuple on an axis is available in the axis rowset.

You can indicate that you want the default member for a given dimension on an axis by setting the corresponding array element to a null pointer. If rgpwszMember is a null pointer, then the default member is used for all dimensions on the axis. If a dimension does not have a default member, then this case results in MD_E_BADTUPLE.

If an error occurs, then the contents of *pulTupleOrdinal are undefined.


IMDRangeRowset

IMDRangeRowset is an optional interface on the dataset object. This interface has a single method that is used to update the cells in a dataset.

Method | Description
GetRangeRowset | Gets a range rowset that is associated with the dataset. Each row in the range rowset corresponds to a cell in the dataset. The columns represent the cell properties. This rowset is updatable, and can be used to update the cells in the cube if the provider supports this feature.


IMDRangeRowset:: GetRangeRowset

IMDRangeRowset::
GetRangeRowset

Returns the range rowset for the desired set of cells in the dataset.

HRESULT GetRangeRowset(
   IUnknown*         pUnkOuter,
   ULONG               ulStartCell,
   ULONG               ulEndCell,
   REFIID            riid,
   ULONG               cPropertySets,
   DBPROPSET         rgPropertySets,
   IUnknown**         ppRowset);

Parameters

pUnkOuter[in]

A pointer to the controlling IUnknown if the range rowset is being created as part of an aggregate.

ulStartCell[in]

The cell ordinal of the first cell in the range.

ulEndCell[in]

The cell ordinal of the last cell in the range.

riid[in]

The IID of the requested rowset interface.

cPropertySets[in]

The number of DBPROPSET structures in rgPropertySets. If this is zero, the provider ignores rgPropertySets.

rgPropertySets[in/out]

An array of DBPROPSET structures, containing properties and values to be set. The properties specified in these structures must belong to the ROWSET property group. If the same property is specified more than once in rgPropertySets, then the value to be used is provider-specific.

ppRowset[out]

A pointer to memory in which to return the requested interface pointer on the range rowset.

Return Code

S_OK
The method succeeded. The status of all columns (cell properties) for all rows (cells) bound by the accessor is set to DBSTATUS_S_OK, DBSTATUS_S_ISNULL, or DBSTATUS_S_TRUNCATED.

DB_S_ERRORSOCCURRED
The rowset was opened but one or more properties — for which the dwOptions element of the DBPROP structure was DBPROPOPTIONS_SETIFCHEAP — were not set. The consumer checks dwStatus in the DBPROP structures to determine which properties were not set. The method can fail to set properties for a number of reasons, including:

  • The property was not supported by the provider.

  • The property was not in the ROWSET property group.

  • The property set was not supported by the provider.

  • It was not cheap to set the property.

  • colid in the DBPROP structure was invalid.

  • The value of dwOptions in the DBPROP structure was invalid.

  • The data type in vValue in the DBPROP structure was not the property data type of the property or was not VT_EMPTY.

  • The value in vValue in the DBPROP structure was invalid.

  • The property’s value conflicted with an existing property.

  • A property was specified to be applied to all columns, but could not be applied to one or more columns.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
ulStartCell or ulEndCell is invalid.

ppRowset was a null pointer.

cPropertySets was greater than zero and rgPropertySets was a null pointer.

In an element of rgPropertySets, cProperties was not zero and rgProperties was a null pointer.

E_NOINTERFACE
The range rowset did not support the interface specified in riid.

DB_E_ERRORSOCCURRED
No rowset was returned because one or more properties — for which the dwOptions element of the DBPROP structure was DBPROPOPTIONS_REQUIRED — were not set. The consumer checks dwStatus in the DBPROP structures to determine which properties were not set. None of the properties are remembered. The method can fail to set properties for any of the reasons specified in DB_S_ERRORSOCCURRED except for the reason that states that it was not cheap to set the property.

MD_E_INVALIDCELLRANGE
ulStartCell was greater than ulEndCell and ulEndCell was not zero.

ulStartCell, ulEndCell, or both specified a cell ordinal that was not in the dataset.

Comments

This method allows the retrieval of a range rowset based on a slice of the dataset. The consumer identifies the desired slice by specifying the ulStartCell and ulEndCell parameters. The ulStartCell parameter is the upper left corner of the slice and ulEndCell is the lower right corner of the slice. The slice of the dataset identified by these parameters is the same as that described for IMDDataset::GetCellData.

The range rowset contains one row for each cell in the slice. The first column of the this rowset is always CELL_ORDINAL. Subsequent columns are those that are selected in the creation of the dataset. If the provider supports cell updatability, then one or more columns of the range rowset can be updated. Typically, this includes the VALUE column.

For more information, see “Updating Cell Data” in Chapter 2.


Appendix A

CoTypes

This appendix lists the CoTypes for OLE DB for OLAP objects. It also lists the CoTypes for OLE DB objects enhanced by OLE DB for OLAP.

Sessions

Note For exclusively multidimensional providers, IOpenRowset is an optional interface and IDBCreateCommand is a mandatory interface.

CoType TSession {
   [mandatory]   interface IGetDataSource;
   [mandatory]   interface IOpenRowset;
   [mandatory]   interface ISessionProperties;
   [optional]   interface IDBCreateCommand;
   [optional]   interface IDBSchemaRowset;
   [optional]   interface IIndexDefinition;
   [optional]   interface ISupportErrorInfo;
   [optional]   interface ITableDefinition;
}

Datasets

CoType TDataset {
   [mandatory]   interface IMDDataset;
   [mandatory]   interface IAccessor;
   [mandatory]   interface IConvertType;
   [mandatory]   interface IColumnsInfo;
   [optional]   interface IMDFind;
   [optional]   interface IMDRangeRowset;
   [optional]   interface ISupportErrorInfo;
}


Appendix B

Multidimensional Expressions (MDX) Grammar

This appendix contains the syntax for expressions and formulas and the syntax for the MDX statements that OLE DB for OLAP uses to specify a dataset. The grammar is divided into several subclauses, each of which contains the Backus-Normal Form (BNF) for a set of syntax elements.

At the end of each subclause, there is a section called “Leveling Rules.” This section enumerates the optional productions. An OLE DB for OLAP–compliant provider must support all productions other than the productions mentioned in these rules.


Elements

<identifier> ::= <regular_identifier> | <delimited_identifier>

<regular_identifier> ::= <alpha_char> [{<alpha_char> | <digit>
                                      | <underscore>}...]

<delimited_identifier> ::=
     <start_delimiter>{<double_end_delimiter> | <nondelimit_end_symbol>}
          [{<double_end_delimiter> | <nondelimit_end_symbol> }...]
     <end_delimiter>

<start_delimiter> ::= <open_bracket>

<end_delimiter> ::= <close_bracket>

<double_end_delimiter> ::= <end_delimiter> end_delimiter>

<nondelimit_end_symbol> ::= !! <any_character_except_delimit_end_symbol>

<cube_name> ::= [ [ [ <data_source>] <catalog_name>] [<schema_name>].]
                <identifier>

<data_source> ::= <identifier>

<catalog_name> ::= <identifier>

<schema_name> ::= <identifier>

<dim_hier> ::= [<cube_name>.]<dimension_name>
            | [[<cube_name>.]< dimension_name>.]<hierarchy_name>

<dimension_name> ::= <identifier>
                   | <member>.DIMENSION
                   | <level>.DIMENSION
                   | <hierarchy>.DIMENSION

<hierarchy_name> ::= <identifier>
                   | < member>.HIERARCHY
                   | <level>.HIERARCHY

<level> ::= [<dim_hier>.]< identifier>
           | <dim_hier>.LEVELS(<index>)
           | <member>.LEVEL

Note The first production is for the case when named levels are supported. The second production is for the case when named levels are not supported.

<member> ::= [<level>.]<identifier>
            | <dim_hier>.<identifier>
            | <member>.<identifier>
            | <member_value_expression>

Note The <member>.<identifier> recognizes the fact that members may sometimes need to be qualified by their parent names. For example, "Portland" is a city in Oregon, and also in Maine. So a reference to Portland will be either Oregon.Portland or Maine.Portland.

<property> ::= <mandatory_property> | <user_defined_property>

<mandatory_property> ::= CATALOG_NAME
                       | SCHEMA_NAME
                       | CUBE_NAME
                       | DIMENSION_UNIQUE_NAME
                       | HIERARCHY_UNIQUE_NAME
                       | LEVEL_UNIQUE_NAME
                       | LEVEL_NUMBER
                       | MEMBER_UNIQUE_NAME
                       | MEMBER_NAME
                       | MEMBER_TYPE
                       | MEMBER_GUID
                       | MEMBER_CAPTION
                       | MEMBER_ORDINAL
                       | CHILDREN_CARDINALITY
                       | PARENT_LEVEL
                       | PARENT_UNIQUE_NAME
                       | PARENT_COUNT
                       | DESCRIPTION

<user_defined_property> ::= <dim_hier>.<identifier>
                          | <level>.<identifier>
                          | <member>.<identifier>

Note The three productions recognize the fact that a property can apply to all the members of a dimension, or all the members of a level, or just to a member.

<tuple> ::= <member>
         | (<member> [, <member>...])
         | <tuple_value_expression>

Note Each member must be from a different dimension or from a different hierarchy.

<set> ::= <member>:<member>

Note Each member must be from the same hierarchy and the same level.

        | <set_value_expression>
        | <open_brace>[<set>|<tuple> [, <set>|<tuple>...]]<close_brace>

Note Duplicates (if any) are always retained when specifying sets in this fashion.

        | (<set>)

<open_brace> ::= {

<close_brace> ::= }

<open_bracket> ::= [

<close_bracket> ::= ]

<underscore> ::= _

<alpha_char> ::= a | b | c | ...| z | A | B | C | ... | Z

<digit> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9


Leveling Rules for Elements

  1. The ability to qualify a cube name by one or more of <data_source>, <catalog_name>, or <schema_name> is optional. Consumers can check the value of the property MDPROP_MDX_OBJQUALIFICATION to see whether a provider supports cube qualification.

  2. The ability to qualify a dimension name by a cube name is optional. Consumers can check the value of the property MDPROP_MDX_OBJQUALIFICATION to see whether a provider supports dimension qualification.

  3. The ability to qualify a hierarchy name by a dimension name or by cube name and dimension name is optional. Consumers can check the value of the property MDPROP_MDX_OBJQUALIFICATION to see whether a provider supports hierarchy qualification.

  4. The provider need support only one of the two productions for <level>. If it supports
    <level> ::= [<dim_hier>.] <identifier>

    then the ability to qualify by <dim_hier> is optional.

    Consumers can check the value of the property MDPROP_NAMED_LEVELS to see if the provider supports named levels. If it does, then the consumer can check MDPROP_MDX_OBJQUALIFICATION to see whether named levels can be qualified by <dim_hier>.

  5. The ability to qualify a member by a level, a member, or <dim_hier> is optional. Consumers can check the value of the property MDPROP_MDX_OBJQUALIFICATION to see whether a provider supports member qualification.

Note Several leveling rules above make it optional to qualify multidimensional schema object names. However, this does not imply that the ability to generate unique names for members, levels, dimensions, and hierarchies is optional. Providers are required to furnish unique names in the schema rowsets for these objects. If providers generate unique names by means other than qualification, then the ability to qualify is optional. For more information, see “Provider Implementation Considerations for Unique Names” in Chapter 2.


Expressions

Note The syntax of <value_expression> is generally the same as SQL-92, subclause 6.11, <value_expression>. Differences are:

  • <tuple>[.VALUE], <property>[.VALUE], and <conditional_expression> are new values for <value_expression_primary>.

  • There are new values for <numeric_value_function>, mainly for statistical analysis.

  • The BNF for <value_expression_primary>, <character_string_literal>, and <string_value_expression> have been shortened by eliminating several intermediate nonterminals.

<value_expression> ::= <numeric_value_expression>
                     | <string_value_expression>

<numeric_value_expression> ::= <term>
                   | <numeric_value_expression> {<plus> | <minus>} <term>

<term> ::= <factor> | <term> {<asterisk> | <solidus>} <factor>

<factor> ::= [<sign>] <numeric_primary>

<sign> ::= + | -

<plus> ::= +

<minus> ::= -

<asterisk>::= *

<solidus> ::= /

<numeric_primary> ::= <value_expression_primary>
                    | <numeric_value_function>

Note The data type of <value_expression_primary> in the above production shall be numeric.

<value_expression_primary> ::= <unsigned_numeric_literal>
                            | (<value_expression>)
                            |  <character_string_literal>
                            | [<cube_name>.]<tuple>[.VALUE]
                            |  <property>[.VALUE]
                            |  <conditional_expression>

<conditional_expression> ::= <if_expression> | <case_expression>

<if_expression> ::= iif(<search_condition>, <true_part>, <false_part>)

<true_part> ::= <value_expression>

<false_part> ::= <value_expression>

<case_expression> ::= <simple_case> | <searched_case> | <coalesce_empty>

<simple_case> ::= CASE <case_operand>
                     <simple_when_clause>...
                    [<else_clause>]
                  END

<searched_case> ::= CASE
                       <searched_when_clause>...
                      [<else_clause>]
                    END

<simple_when_clause> ::= WHEN <when_operand> THEN <result>

<searched_when_clause> ::= WHEN <search_condition> THEN <result>

<else_clause> ::= ELSE <value_expression>

<case_operand> ::= <value_expression>

<when_operand> ::= <value_expression>

<result> ::= <value_expression>

<coalesce_empty> ::= COALESCEEMPTY (<value_expression>
                                 {, <value_expression> }...)

<signed_numeric_literal> ::= [<sign>]<unsigned_numeric_literal>

<unsigned_numeric_literal> ::= <exact_numeric_literal>
                             | <approximate_numeric_literal>

<exact_numeric_literal> ::= <unsigned_integer>[.<unsigned_integer>]
                          | <unsigned_integer>.
                          | .<unsigned_integer>

<unsigned_integer> ::= {<digit>}...

<approximate_numeric_literal> ::= <mantissa>E<exponent>

<mantissa> ::= < exact_numeric_literal>

<exponent> ::= [<sign>]<unsigned_integer>

<string_value_expression> ::= <value_expression_primary>
                            | <string_value_expression>
                              <concatenation_operator>
                              <value_expression_primary>

Note The data type of <value_expression_primary> in the above production shall be a character string.

<character_string_literal>::= <quote>[<character_representation>...]
                              <quote>

<character_representation> ::= <nonquote_character> | <quote_symbol>

<nonquote_character> ::= !!
                    <any_character_in_the_character_set_other_than_quote>

<quote_symbol> ::= <quote> <quote>

<quote>  ::= ’

<concatenation_operator> ::= ||


Leveling Rules for Expressions

The following productions for <value_expression_primary> are optional:

  1. The ability to qualify <tuple>[.VALUE] by <cube_name> in a value expression primary is optional. Consumers can check the value of the property MDPROP_MDX_OUTERREFERENCE to see whether a provider supports this feature.

  2. <property>[.VALUE]. Consumers can check the value of the property MDPROP_MDX_QUERYBYPROPERTY to see whether a provider supports this feature.

  3. <simple_case>, <searched_case>. Consumers can check the value of the property MDPROP_MDX_CASESUPPORT to see whether a provider supports this feature.


Search Condition

<search_condition> ::= <boolean_term>
                     | <search_condition> {OR | XOR} <boolean_term>

<boolean_term> ::= <boolean_factor> | <boolean_term> AND <boolean_factor>

<boolean_factor> ::= [NOT] <boolean_primary>

<boolean_primary> ::= <value_expression> <comp_op> <value_expression>
                    | ISEMPTY(<value_expression>)
                    | (<search_condition>)

<comp_op> ::= <equals_operator>
            | <not_equals_operator>
            | <less_than_operator>
            | <greater_than_operator>
            | <less_than_or_equals_operator>
            | <greater_than_or_equals_operator>

<equals_operator> ::= =

<not_equals_operator> ::= <>

<greater_than_operator> ::= >

<less_than_operator> ::= <

<greater_than_or_equals_operator> ::= >=

<less_than_or_equals_operator> ::= <=


Leveling Rules for Search Condition

If <value_expression> in a <boolean_primary> value is a string value expression, then support for <comp_op> values other than <equals_operator> and <not_equals_operator> is optional. Consumers can check the value of the property MDPROP_MDX_STRING_COMPOP to see whether a provider supports this feature.


Set Value Expression

<index> ::= <numeric_value_expression>

Note <index> denotes an integer argument. If an arbitrary <numeric_value_expression> appears here, then it is truncated to the nearest integer.

<percentage> ::= <numeric_value_expression>

<set_value_expression> ::= <dim_hier>.MEMBERS
                         | <level>.MEMBERS
                         | <member>.CHILDREN
                         | BOTTOMCOUNT(<set>, <index>
                              [, <numeric_value_expression>])
                         | BOTTOMPERCENT(<set>, <percentage>,
                              <numeric_value_expression>)
                         | BOTTOMSUM(<set>, <numeric_value_expression>,
                              <numeric_value_expression>)
                         | CROSSJOIN(<set>, <set>)
                         | DESCENDANTS(<member>, <level> [,<desc_flags>])

Note In the absence of explicit <desc_flags> specification, SELF is the default.

                         | DISTINCT(<set>)
                         | DRILLDOWNLEVEL(<set> [, <level>]])
                         | DRILLDOWNLEVELBOTTOM(<set>, <index>
                             [,[<level>] [, <numeric_value_expression>]])
                         | DRILLDOWNLEVELTOP(<set>, <index>[, [<level>]
                             [, <numeric_value_expression>]])
                         | DRILLDOWNMEMBER(<set>, <set>[, RECURSIVE])
                         | DRILLDOWNMEMBERBOTTOM(<set>, <set>, <index>
                            [, <numeric_value_expression>][, RECURSIVE]])
                         | DRILLDOWNMEMBERTOP(<set>, <set>, <index>
                           [, [<numeric_value_expression>][, RECURSIVE]])
                         | DRILLUPLEVEL(<set>[, <level>]])
                         | DRILLUPMEMBER(<set>, <set>)
                         | EXCEPT(<set>, <set> [, ALL])
                         | EXTRACT(<set>, <dim_hier>[, <dim_hier>...])
                         | FILTER(<set>, <search_condition>)
                         | GENERATE(<set>, <set> [, ALL])
                         | HIERARCHIZE(<set>)
                         | INTERSECT(<set>, <set> [, ALL])
                         | LASTPERIODS(<index> [, <member>])
                         | MTD([<member>])
                         | ORDER(<set>, <value_expression>
                             [, ASC | DESC | BASC | BDESC])

Note In the absence of explicit specification, ASC is the default.

                         | PERIODSTODATE([<level>[, <member>]])
                         | QTD([<member>])
                         | TOGGLEDRILLSTATE(<set1>, <set2>[, RECURSIVE])

Note With the exception of CROSSJOIN, all set functions that take more than one <set> argument require that the two set arguments have tuples of the same dimensionality.

                         | TOPCOUNT(<set>, <index>
                             [, <numeric_value_expression>])
                         | TOPPERCENT(<set>, <percentage>,
                             <numeric_value_expression>)
                         | TOPSUM(<set>, <numeric_value_expression>,
                             <numeric_value_expression>)
                         | UNION(<set>, <set> [, ALL])
                         | WTD([<member>])
                         | YTD(<member>)

<desc_flags> ::= SELF
               | AFTER
               | BEFORE
               | BEFORE_AND_AFTER
               | SELF_AND_AFTER
               | SELF_AND_BEFORE
               | SELF_BEFORE_AFTER


Leveling Rules for Set Value Expression

  1. Support for <desc_flags> other than SELF is optional. Consumers can check the value of the property MDPROP_MDX_SET_FUNCTIONS to see whether a provider supports this feature.

  2. The following set functions are optional: TOPPERCENT, TOPSUM, BOTTOMPERCENT, BOTTOMSUM. Consumers can check the value of the MDPROP_MDX_SET_FUNCTIONS property to see whether a provider supports this feature.

  3. The following set functions are optional: PERIODSTODATE, LASTPERIODS, YTD, QTD, MTD, WTD. Consumers can check the value of the MDPROP_MDX_SET_FUNCTIONS property to see whether a provider supports this feature.

  4. The ability to have expressions containing nonmeasure dimension members in the <numeric_value_expression> argument of set functions. For example, if Revenue and Cost are measures and USA is a member of Geography dimension, then in set functions that take a <numeric_value_expression> argument, the support for expressions such as Revenue/3 and (Revenue - Cost)/Cost*100 are mandatory. However, support for expressions such as (USA, Sales)/3 are optional. Consumers can check the value of the property MDPROP_MDX_NONMEASURE_EXPRESSIONS to see whether a provider supports this feature.


Member Value Expression

<member_value_expression> ::= <member>.{PARENT | FIRSTCHILD | LASTCHILD
                                        | PREVMEMBER | NEXTMEMBER}
                            | <member>.LEAD(<index>)
                            | <member>.LAG(<index>)

Note LAG(<index>) is the same as LEAD(–<index>)

                            | <member>.{FIRSTSIBLING | LASTSIBLING}
                            | <dimension>.[CURRENTMEMBER]
                            | <dimension>.DEFAULTMEMBER
                            | <hierarchy>.DEFAULTMEMBER
                            | ANCESTOR(<member>, <level>)
                            | CLOSINGPERIOD([<level>[, <member>])
                            | COUSIN(<member>, <member>)
                            | OPENINGPERIOD([<level>[, <member>])
                            | PARALLELPERIOD([<level>[, <index>
                                             [, <member>]]])


Leveling Rules for Member Value Expression

The following member functions are optional: COUSIN, PARALLELPERIOD, OPENINGPERIOD, CLOSINGPERIOD. Consumers can check the value of the property MDPROP_MDX_MEMBER_FUNCTIONS to see whether a provider supports this feature.


Tuple Value Expression

<tuple_value_expression> ::= <set>.CURRENTMEMBER
                           | <set>[.ITEM](<string_value_expression>
                              [, <string_value_expression>...] | <index>)


Numeric Value Function

<numeric_value_function> ::=
       AGGREGATE(<set> [, <numeric_value_expression>])
     | AVG(<set>[, <numeric_value_expression>])
     | CORRELATION(<set> [, <numeric_value_expression>]
          [, <numeric_value_expression>])
     | COVARIANCE(<set>[, <numeric_value_expression>
          [, <numeric_value_expression>])
     | COUNT(<set>[, INCLUDEEMPTY])
     | LINREGINTERCEPT(<set>[, <numeric_value_expression>
          [, <numeric_value_expression>])
     | LINREGPOINT(<numeric_value_expression>, <set>,
          <numeric_value_expression> [, <numeric_value_expression>])
     | LINREGR2(<set>, <numeric_value_expression>
          [, <numeric_value_expression>])
     | LINREGSLOPE(<set>[, <numeric_value_expression>
          [, <numeric_value_expression>])
     | LINREGVARIANCE(<set>, <numeric_value_expression>
          [, <numeric_value_expression>])
     | MAX(<set>[, <numeric_value_expression>])
     | MEDIAN(<set>[, <numeric_value_expression>])
     | MIN(<set>[, <numeric_value_expression>])
     | RANK(<tuple>, <set>)
     | STDEV(<set>[, <numeric_value_expression>])
     | SUM(<set>[, <numeric_value_expression>])
     | VAR(<set>[, <numeric_value_expression>])


Leveling Rules for Numeric Value Function

The following numeric functions are optional: MEDIAN, VAR, STDEV, RANK, AGGREGATE, COVARIANCE, CORRELATION, LINREGSLOPE, LINREGVARIANCE, LINREGR2, LINREGPOINT. Consumers can check the value of the property MDPROP_MDX_NUMERIC_FUNCTIONS to see whether a provider supports this feature.


MDX Statement

<MDX_statement> ::= <select_statement>
                  | <create_formula_statement>
                  | <drop_formula_statement>

<select_statement> ::= [WITH <formula_specification>]
                        SELECT [<axis_specification>
                               [, <axis_specification>...]]
                        FROM [<cube_specification>]
                        WHERE [<slicer_specification>]
                        [<cell_props>]

<formula_specification> ::= <single_formula_specification>
                           [<single_formula_specification>...]

<single_formula_specification> ::= <member_specification>
                                 | <set_specification>

<member_specification> ::= MEMBER <member_name> AS <value_expression>
                                [, <solve_order_specification>]
                                [, <member_property_definition>...]

<member_name> ::= <member>.<identifier>
                | <cube_name>.<member>.<identifier>

Note

  1. The identifier defines a new member. The qualification member has enough information to specify the dimension, and the level in the dimension that this new member should be on.

  2. If <member_name> is part of a member specification that appears in a create formula statement or is part of a drop formula statement, then it must be qualified by a cube name, as in the second production above.

<solve_order_specification> ::= SOLVE_ORDER = <unsigned_integer>

<member_property_definition> ::= <identifier> = <value_expression>

Note Since the property definition appears in the context of a member definition, there is enough information to associate the identifier (which is the property name) in the above production with a member.

<set_specification> ::= SET <set_name> AS <set>

<set_name> ::= <identifier> | <cube_name>.<identifier>

Note If <set_name> is part of a set specification that appears in a create formula statement or is part of a drop formula statement, then it must be qualified by a cube name, as in the second production above.

<axis_specification> ::= [NON EMPTY] <set> [<dim_props>] ON <axis_name>

<axis_name> ::= COLUMNS
              | ROWS
              | PAGES
              | CHAPTERS
              | SECTIONS
              | AXIS(<index>)

<dim_props> ::= [DIMENSION] PROPERTIES <property> [, <property>...]

<cube_specification> ::= [<cube_name>] [, <cube_name>]

<slicer_specification> ::= {<set> | <tuple>}

<cell_props> ::= [CELL] PROPERTIES <cell_property> [, <cell_property>...]

<cell_property> ::= <mandatory_cell_property>
                  | <optional_cell_property>
                  | <provider_specific_cell_property>

<mandatory_cell_property> ::= CELL_ORDINAL | VALUE | FORMATTED_VALUE

<optional_cell_property> ::= FORMAT_STRING
                           | FORE_COLOR
                           | BACK_COLOR
                           | FONT_NAME
                           | FONT_SIZE
                           | FONT_FLAGS

<provider_specific_cell_property> ::= <identifier>

<create_formula_statement> ::= CREATE [<scope>]<formula_specification>

<drop_formula_statement> ::= <drop_member_statement>
                           | <drop_set_statement>

<drop_member_statement> ::= DROP MEMBER <member_name>
                                     [, <member_name>...]

<drop_set_statement> ::= DROP SET <set_name> [, <set_name>...]

<scope> := GLOBAL | SESSION


Leveling Rules for MDX Statement

  1. Support for <formula_specification> is optional. Consumers can check the value of the property MDPROP_MDX_FORMULAS to see whether a provider supports this feature.

  2. Support for <set> in <slicer_specification> is optional. Consumers can check the value of the property MDPROP_MDX_SLICER to see whether a provider supports this feature.

  3. Support for more than one cube name in <cube_specification> is optional. Support for having no cube name in the FROM clause (that is, the cube is implicitly defined by the axis and slicer dimensions) is also optional. Consumers can check the value of the property MDPROP_MDX_JOINCUBES to see whether a provider supports this feature.

  4. The axis names CHAPTERS and SECTIONS are optional. Consumers can check the value of the property MDPROP_AXES to see whether a provider supports this feature.

  5. Support for <index> > 2 in the AXIS(<index>) function is optional. Consumers can check the value of the property MDPROP_AXES to see whether a provider supports this feature.

  6. Support for <create_formula_statement> is optional. Consumers can check the value of the property MDPROP_MDX_FORMULAS to see whether a provider supports this feature.

  7. Support for <scope> of GLOBAL is optional. Consumers can check the value of the property MDPROP_MDX_FORMULAS to see whether a provider supports this feature.


Microsoft PivotTable Service Specification

The release of Microsoft® PivotTable® Service addressed by these specifications and implementation notes is a beta version of the final software. These specifications and the software implementation thereof are subject to change in future releases.


PivotTable Service Overview

The Microsoft® PivotTable® Service is an OLE DB for OLAP provider. It is an in-process desktop OLAP server designed to provide online and offline data analysis functionality, and online access to OLAP data as a client of the Microsoft Decision Support Services OLAP Server.

Microsoft PivotTable Service contains data analysis, cube building, and smart cache management functionality. It provides interfaces to data as well as functionality to manipulate data. Microsoft PivotTable Service stores data locally on the client for offline analysis, and also offers connectivity to the multidimensional data managed by the Microsoft OLAP Server as well as to non-OLAP relational data sources.

Microsoft PivotTable Service exposes SQL and multidimensional expressions (MDX) defined by Microsoft OLE DB and Microsoft OLE DB for OLAP as its native consumer interfaces. Microsoft PivotTable Service also extends the language defined in the OLE DB for OLAP specifications by adding data definition language (DDL) statements to define and manipulate the structure of multidimensional data cubes.

This document describes the way client applications use the interfaces provided by Microsoft PivotTable Service by discussing the methods to be invoked and statements to be executed in typical usage scenarios. The purpose of this document is to allow Microsoft PivotTable Service consumers to gain understanding of the general architecture and as an implementation guide for developers.

The following diagram describes the Microsoft PivotTable Service architecture:

Microsoft PivotTable Service serves several important goals:

  • Enables client applications to create a local multidimensional cube directly from a relational data source.

  • Connects to the Microsoft OLAP Server as a client component. This means that client applications connect to the same component both for Microsoft OLAP Server services and for stand-alone desktop analysis services.

  • Functions as a service provider for relational data sources that implements multidimensional functionality and exposes the OLE DB for OLAP interfaces with MDX extensions.

  • Functions as a desktop mobile OLAP client that enables users to download multidimensional data from data sources and store the data on the local machine for offline analysis.

Microsoft PivotTable Service is a local, in-process multidimensional data server. The functionality available to the data consumer client from the PivotTable Service is a subset of that provided by the Microsoft OLAP Server:

  • Scalability & Performance: Microsoft PivotTable Service is single-threaded and can work with only a single data cube partition. The Microsoft PivotTable Service does not implement an internal aggregation management system for defining or storing aggregations, so its performance has a direct relationship to the amount of data it manages.

  • Backend functionality: Microsoft PivotTable Service does not offer typical DBA support for activities such as defining users, data access rules, databases, virtual cubes, and global calculated members. However, Microsoft PivotTable Service offers DDL substitutes that enable operations for defining new data cubes and the population of these cubes with data.


Storage Alternatives

Microsoft® PivotTable® Service can store multidimensional data in three data cube storage scenarios:

  1. Create a data cube and load data from an OLEDB rowset provider or a SQL relational database. Microsoft PivotTable Service can be disconnected from the data source after the data cube is created and stored locally on the client as a MOLAP cube.

  2. Create a data cube definition and store it as a ROLAP cube. Dimensions and data are not stored.

  3. Create a data cube definition with dimension definitions and store it locally.


Connection Model

A client connects to Microsoft® PivotTable® Service through the OLE DB for OLAP interfaces. Additional special properties for the Data Source object are used to declare connection criteria specific to Microsoft PivotTable Service, such as creating a local cube, when initializing a session.

Microsoft PivotTable Service also supports ODBC connections. The OLEDB _SOURCE_DSN property may contain either:

  • an ODBC DSN or

  • an entire ODBC connection string

The provider may be either a Microsoft OLAP Server or Microsoft PivotTable Service.

Property | Description
DBPROP_INIT_DATASOURCE | Name of server machine or local cube file
DBPROP_INIT_CATALOG | Name of default (initial) database (catalog)
DBPROP_AUTH_USERUID | Ignored in this release
DBPROP_AUTH_PASSWORD | Ignored in this release
DBPROP_INIT_ASYNCH | Used for asynchronous initialization
MDPROP_INIT_CREATE_CUBE | (optional) CREATE CUBE statement to create a local cube file Otherwise: NULL
MDPROP_INIT_INSERT_INTO | (optional) INSERT INTO statement to populate a local cube file created with CREATE CUBE Otherwise: NULL
DBPROP_MSMD_SOURCE_DSN | ODBC connection string, OLEDB connection string, or DSN for source relational database, used only when creating a local cube file
DBPROP_MSMD_SOURCE_UID | ODBC UID for source relational database, used only when creating a local cube file
DBPROP_MSMD_SOURCE_PWD | ODBC PWD for source relational database, used only when creating a local cube file
DBPROP_INIT_PROVIDERSTRING | A predefined string containing other initialization properties.
MSMD_USEEXISTINGFILE | If the first character of this string is Y, T, or a numeric digit other than 0, CREATE CUBE and INSERT INTO properties will be ignored.
MSMD_ARTIFICIALDATA | If the first character of this string is Y, T, or a numeric digit other than 0, artificial aggregate values will be calculated using a simple algorithm instead of calculating real values.

Notes

The following notes about properties apply when connecting to Microsoft PivotTable Service:

  • When working with relational data sources, the server name specified is the name of the local Microsoft PivotTable Service, which provides a gateway to the relational data source. The relational data source is specified when the cube is to be populated using the INSERT INTO statement. The same string format is used when specifying the <source> in the INSERT INTO statement (see Appendix A).

  • The IDataInit component enables you to persist these properties in a connection string that can then be used to initiate the session.

  • DBPROP_INIT_DATASOURCE: Usage depends on the operation mode:
    1. When working with the Microsoft OLAP Server, this is the name of the server machine, or LOCALHOST, or empty for the local machine.

    2. When working with local cube files this is the name of the cube file. Cube files have the default extension of .cub. A .cub file is a multidimensional database and may contain multiple cubes.
  • DBPROP_AUTH_USERUID: Ignored in this release.

  • DBPROP_AUTH_PASSWORD: Ignored in this release.

  • DBPROP_MSMD_SOURCE_DSN: The relational database's ODBC DSN.

  • DBPROP_MSMD_SOURCE_UID: The relational database's ODBC UID.

  • DBPROP_MSMD_SOURCE_PWD: The relational database's ODBC PWD.

  • DBPROP_INIT_PROVIDERSTRING: This is a standard OLE DB property, but does not specify usage, which is specific to those providers that use it. The Microsoft PivotTable Service uses this property as follows:
    • This property is used by ADO and ADOMD

    • The property string is semicolon delimited. Double quotes may be used to delimit individual values.

    • During initialization (IDBInit::Initialize), every recognized property from the DBPROP_INIT_PROVIDERSTRING is copied into its respective property value, overwriting existing property values.

    • After completion of the initialization process (connection to a database or creation of a data cube), a new value for DBPROP_INIT_PROVIDERSTRING is created by concatenating all properties that were used during the initialization process, then concatenating unrecognized properties at the end of the string.
  • It is the client application's responsibility to persist the connection string. Also, if the cube file contains more than one cube and the application intends to work with a specific cube, this cube name should also be persisted.

  • MSMD_USEEXISTINGFILE: Specifies whether to override the CREATE CUBE and INSERT INTO properties

  • MSMD_ARTIFICIALDATA: Specifies whether Microsoft PivotTable Service should produce artificial example aggregate values instead of calculating real aggregations.


Persisting Local Cubes - Creating the Cube File

Persisting Local Cubes – Creating the Cube File

Microsoft® PivotTable® Service enables client applications to persist data cubes in local files (default extension is ".cub"). Local data cubes are stored in structured (compound document) files that are the local equivalent of Microsoft OLAP Server objects. More than one local data cube can be stored in a .cub file. A local data cube file name can be specified in DBPROP_INIT_DATASOURCE, and if the specified .cub file doesn't exist, the file will be automatically created when the data cube is initialized with the CREATE CUBE statement.


Defining Cubes

Microsoft® PivotTable® Service can be used to define data cubes for local storage only; when connected to a Microsoft OLAP Server, creation of data cubes on the server is not allowed. The CREATE CUBE command is used to define the logical data structure of a data cube, and the INSERT INTO statement populates the cube. The syntax of the Data Definition Language (DDL) statements used to define a cube are described in the section "Microsoft PivotTable Service MDX Reference." The source of data used to create a local cube must be a tabular data provider, such as a relational database or Microsoft OLAP Server. Creation of local cubes using a local cube file as the data source is not implemented in this release.


Populating Cubes

The INSERT INTO statement is used to populate data cubes with dimension structure and data. The syntax of the INSERT INTO statement is described in Appendix A. If the cube file specified in the DBPROP_INIT_DATASOURCE initialization property does not exist, it will be created during processing of the INSERT INTO statement.

The INSERT INTO statement may perform the following operations on a cube:

  • Bind data cube elements to source relational view columns.

  • Populate the structures of the data cube dimensions.

  • Populate the data.

Binding

Binding specifies the column of the relational table that will be used to populate an element (level, property, or measure) in the cube. Binding is specified in the INSERT INTO statement.

Populating the Dimensions Structure

The dimensions structure of a data cube is populated during the execution of the INSERT INTO command by a series of SELECT statements executed on the data source to generate a dimension tree. After all of these SELECT queries have been executed, the dimensions of the data cube are fully populated and become static.

Populating the Data

Data may be read from the server in one of two modes:

  • Immediate population of cube data (default mode): The entire table is read from the data source to the client machine. If a local cube file is specified, the data is persisted in the local cube file, and the data source is then disconnected.

  • Deferred data population: In client-server situations when DEFER DATA is specified, data is sent to the client only when necessary to fulfill a request of the client application. No data is persisted on the client, although the data cube definition and the dimension members may be persisted in a local file. The DEFER DATA clause is used in the INSERT INTO statement to defer data population. See INSERT INTO in Appendix A for additional details, including the related ATTEMPT_DEFER_DATA option.

Aggregations

When Microsoft PivotTable Service is connected as a client to a Microsoft OLAP Server, aggregations that were created for the cube on the server will be automatically created in the cached cube on the client. This improves the performance of the local cube.


Refreshing Cubes

The REFRESH CUBE statement refreshes the local cube by clearing the local cube file or cache and rebuilding it from the original data source. Refreshing a cube is also referred to as recalculation. The syntax of REFRESH CUBE is specified in Appendix A.

  • If Microsoft® PivotTable® Service is connected to a Microsoft OLAP Server, the REFRESH CUBE statement causes the local memory cache to be cleared. The local cache is incrementally refreshed as queries are executed against the cube on the server.

  • If Microsoft PivotTable Service is connected to a relational database, executing the REFRESH CUBE statement causes the local memory and disk cache to be cleared and the dimensions repopulated.

  • If Microsoft PivotTable Service is connected to a local cube, executing the REFRESH CUBE statement clears the cube file and performs CREATE CUBE and INSERT INTO statements to recreate the cube from the original source database.

  • Recalculation of a cube may also be initiated by the server when Microsoft PivotTable Service is connected to a Microsoft OLAP Server. In this case, the Microsoft OLAP Server updates the data and notifies Microsoft PivotTable Service on the client to refresh the client's cache. This notification is done automatically during the next available communication initiated by the client. Also, all of the client caches are erased and opened queries (OLE DB for OLAP datasets) are recalculated. The LAST_UPDATED column of the cube changes the time stamp.

It is the client application's responsibility to be aware of recalculation notifications. The recalculation of a cube may result in situations where queries are no longer valid since members used by original queries may no longer exist. It is the client application's responsibility to re-validate queries after a recalculation.


Calculated Members

Calculated members may be defined either within the MDX SELECT statement (in the DEFINE clause) or may be defined on the SESSION scope using the CREATE MEMBER statement. The syntax for these statements is described in Appendix A.

Note Calculated members are not persisted as part of the cube. Therefore it is the responsibility of the client application to persist the definition of the calculated members and manage the instantiation of those members for each new session with the cube.


Microsoft PivotTable Service MDX Reference

This section supplements MDX syntax defined by OLE DB for OLAP Design Specification, which are not repeated herein, and contains the reference and Backus Normal Form (BNF) for the additional MDX statements implemented by Microsoft® PivotTable® Service:

  • CREATE CUBE – Define the logical structure of a cube

  • DROP CUBE – Delete an existing cube

  • REFRESH CUBE – Re-read the data that formed the cube

  • INSERT INTO – Bind the cube to a data source and populate the cube with data.

  • CREATE MEMBER – Define a calculated member on the session level

  • DROP MEMBER – Delete a calculated member

  • ALTER MEMBER – Change the definition of a calculated member


CREATE CUBE Statement

The purpose of the CREATE CUBE statement is to define a new cube structure. This statement shares much of the syntax and semantics of SQL-92 syntax and shares the semantics of the CREATE TABLE statement. However, the CREATE CUBE statement contains syntax specific to data cubes.

The CREATE CUBE statement defines the structure of a new cube. However, the cube is not populated when this statement is executed. The cube is populated using the INSERT INTO statement in a manner similar to the SQL-92 approach for creating and populating tables.

A cube is defined as a collection of measures, each having an aggregate function, and a set of dimensions, each containing one or more levels. There may be special dimensions such as time dimensions and special levels (such as ALL and various time units).

Optionally, dimensions may include multiple hierarchies. Each hierarchy contains levels. The example below shows a time dimension that contains two hierarchies. The remainder of the dimensions each contain a single NULL hierarchy; therefore the hierarchy is not specified in the dimension definition.

BNF

CREATE CUBE <cube name> <open paren>
DIMENSION <dimension name> [TYPE <dimension type>],
<hierarchy def> [<hierarchy def>...]
[{, DIMENSION <dimension name> [TYPE <dimension type>],
<hierarchy def> [<hierarchy def>...]}...] ,
MEASURE <measure name> <function def> [<member-properties>]
[{, MEASURE <measure name> <function def> [<member-properties>]}...]
<close paren>

<cube name> ::= [[<datasource>.]<database name>.]<legal name>

<dimension name> ::= <legal name>

<dimension type> ::= TIME

<hierarchy def> ::= [HIERARCHY <hierarchy name>,] <level def> [,<level def>...]

<level def> ::= LEVEL <level name> [TYPE <level type>]

<level type> ::= ALL | YEAR | HALFYEAR | QUARTER | MONTH | WEEK | DAY |
HOUR | MINUTE | SECOND

<function def> ::= FUNCTION <function name>

<function name> ::= SUM | MIN | MAX | COUNT | AVG | STD | VAR

Note Only SUM, MIN, MAX, and COUNT are supported in this release.

EXAMPLE

CREATE CUBE Sales (
DIMENSION Time TYPE TIME,
   HIERARCHY [Fiscal],
      LEVEL [Fiscal Year] TYPE YEAR,
      LEVEL [Fiscal Qtr] TYPE QUARTER,
      LEVEL [Fiscal Month] TYPE MONTH,
   HIERARCHY [Calendar],
      LEVEL [Calendar Year] TYPE YEAR,
      LEVEL [Calendar Month] TYPE MONTH,
DIMENSION Products,
      LEVEL [All Products] TYPE ALL,
      LEVEL Category,
      LEVEL [Sub Category],
      LEVEL [Product Name],
DIMENSION Geography,
      LEVEL [Whole World] TYPE ALL,
      LEVEL Region,
      LEVEL Country,
      LEVEL City,
MEASURE  [$ Sales]
   FUNCTION SUM
   FORMAT 'Currency'
MEASURE [Units Sold]
   FUNCTION SUM
)


DROP CUBE Statement

The DROP CUBE statement is used to delete an existing cube. This statement is valid only when working in the desktop mode (i.e. when not connected to a Microsoft® OLAP Server).

BNF

<drop-cube> ::= DROP CUBE <cube-name>


REFRESH CUBE Statement

When connected to a Microsoft® OLAP Server, the REFRESH CUBE statement causes the data cube cached on the client to be cleared. The content of the local cache is incrementally refreshed from the server cube as data is requested from the server.

BNF

<refresh-cube> ::= REFRESH CUBE <cube-name>


INSERT INTO Statement

The INSERT INTO statement binds a logical cube to an existing relational view and populates this cube with dimension members and data.

BNF

<insert-statement> ::= INSERT INTO <target-clause> <property-list>
[<options-clause>] <source-clause>

<target-clause> ::= <cube-name> <open-paren> <target-element-list>
<close-paren>

<target-element-list> ::= <target-element>[, <target-element-list>]

<target-element> ::= [<dim-name>.[<hierarchy-name>.]]<level-name>
| <time-dim-name>
| [Measures.]<measure-name>
| SKIPONECOLUMN

<property-list> ::= <property>[, <property-list>]

<property> ::= <property-name> | <property-name> = <value>

<source-clause> ::= SELECT <columns-list>
FROM <tables-list>
[ WHERE <where-clause> ]
| DirectlyFromCachedRowset <hex-number>

<options-clause> ::= OPTIONS <options-list>

<options-list> ::= <option>[, <options-list>]

<option> ::= <defer-options> | < analysis-options>

<defer-options> ::= DEFER_DATA | ATTEMPT_DEFER

<analysis-options> ::= PASSTHROUGH | ATTEMPT_ANALYSIS

<columns-list> ::= <column-expression> [, < columns-list> ]

<column-expression> ::= <column-expression-name>

<column-expression-name> ::= <column-name> [AS <alias-name>]
| <alias name> <column-name>

<column-name> ::= <table-name>.<column-name>
| <column-function> | <ODBC scalar function> | <braced-expression>

<column function> ::= <identifier>(…)

<ODBC scalar function> ::= {fn<column-function>}

<braced-expression> ::= (…)

<tables list> ::= <table_expression> [, <table_list>]

<table_expression> ::= <table-name> [ [AS] <table-alias>]

<table-alias> ::= <identifier>

<table_name> ::= <identifier>

<where clause> ::= <where-condition> [AND <where-clause>]

<where condition> ::= <join-constraint> | <application constraint>

<join-constraint> ::= <column-name> = <column-name>
| <open-paren><column-name> = <column-name><close-paren>

<application-constraint> ::= (…)
| NOT (…)

| (…) OR (…)

<identifier> ::= <letter>{<letter>|<digit>|<underline>|<dollar>|<sharp>}…

Examples

Example 1

INSERT INTO MyCube (Year, Month, [Product Group], [Product Name], Country, Sales, Cost)
OPTIONS DEFER_DATA
SELECT MyTable.Year, MyTable.Month, MyTable.ProdGroup, MyTable.ProdName, MyTable.Country, MyTable.Sales, MyTable.Cost
FROM MyTable
WHERE MyTable.SalesRep = "Amir" and MyTable.CustomerGroup = "Industry"

Example 2

INSERT INTO MyCube (Year, Month, [Product Group], [Product Name], Country, Sales, Cost)
OPTIONS PASSTHROUGH SELECT MyTable.Year, MyTable.Month, MyTable.ProdGroup, MyTable.ProdName, MyTable.Country, MyTable.Sales, MyTable.Cost
FROM MyTable
WHERE MyTable.SalesRep = "Amir" and MyTable.CustomerGroup = "Industry"

Note The PASSTHROUGH key word specifies that the following SELECT statement is to be passed directly to the database engine with no parsing by Microsoft® PivotTable® Service.

Example 3

INSERT INTO MyCube (Year, Month, [Product Group], [Product Name], Country, Sales, Cost)
DIRECTLYFROMCACHEDROWSET 0x00001284

Note: The DIRECTLYFROMCACHEDROWSET keyword directs data to be read from the address in memory identified in quotes immediately after the keyword. It is the responsibility of the client to specify the correct address in memory. At runtime, the number is assumed to be the in-process address of an IUnknown pointer to an OLEDB rowset.

INSERT INTO Notes

  • Each level and each measure in a cube is derived from a column in the select statement.

  • When using an INSERT INTO statement, the columns specified in the associated SELECT statement are bound to the elements of the INSERT INTO statement in the order specified and in a one-to-one relationship. If a column is specified in the SELECT statement that does not have a related element in the INSERT INTO statement, the keyword SKIPONECOLUMN may be used in the INSERT INTO statement as a placeholder for the unused column. SKIPONECOLUMN may be used more than once.

  • Names of elements in an INSERT INTO are level and measures names, sometimes qualified with dimension name or the word MEASURES to avoid ambiguity.

  • Automatically generated time dimensions must be specified as TYPE TIME in a CREATE CUBE statement, and each level of the dimension must specify TYPE <level-type>.

  • The Where clause can have both application and join constraints. The Parser will parse only join constraints and will use the join constraint to find a path from all tables to the fact table and to the dimension tables. The Application constraint is used only to specify constraints on tables, and is passed to the query without modifications.

  • Expressions between parentheses are considered to be application constraints. For example, if the expression Sales.Product_ID = Products.Product_ID AND Sales.Customer_ID = Customers.Customer_ID is enclosed in parentheses, it will be treated as an application constraint and will not be used as a join constraint. It is the responsibility of the client to ensure that parentheses are used only around application constraints, such as (Product.Price < 100 AND Product.Category = 1).

Passthrough and Advanced Query Processing

The PASSTHROUGH option causes the SELECT query to be passed directly to the source database without modification or delay by Microsoft® PivotTable® Service. If PASSTHROUGH is not specified, Microsoft PivotTable Service parses the query and formulates an equivalent query that uses knowledge of data and index structures and is often more efficient than the specified query.

The DEFER_DATA option causes the query to be parsed locally and executed only when necessary to retrieve data to satisfy a user request. The ATTEMPT_DEFER option causes Microsoft PivotTable Service to attempt to parse the query and defer data loading if successful, or, if the query cannot be parsed, to process the specified query immediately as if the PASSTHROUGH had been specified. The ATTEMPT_ANALYSIS option causes Microsoft PivotTable Service to attempt to parse the query and formulate an equivalent query (process in the MOLAP mode), or, if the query can't be parsed, to process the specified query immediately as if the PASSTHROUGH had been specified.

Passthrough Compatibility Matrix

 | Parse | None | PassThrough | Attempt_Analysis
None | Succeeded | MOLAP | MOLAP(PT) | Molap
 | Failed | Error | N/A | MOLAP(PT)
DEFER_ DATA | Succeeded | ROLAP | Error | ROLAP
 | Failed | Error | N/A | Error
ATTEMPT_ DEFER | Succeeded | ROLAP | MOLAP(PT) | ROLAP
 | Failed | MOLAP(PT) | N/A | MOLAP(PT)


CREATE MEMBER, DROP MEMBER, ALTER MEMBER Statements

The CREATE MEMBER, DROP MEMBER, and ALTER MEMBER statements allow the creation, deletion, and modification of calculated members. Calculated members can be defined in the scope of a single query (as part of the DEFINE clause in the SELECT statement, or on the session level (by using the CREATE MEMBER statement). A newly created member may have several properties such as Name, Expression, Format, Fore_Color and Back_Color. The syntax allows setting these properties.

BNF

Member functions serve to extend an existing Dimension and Level with an additional member.

<MDX statement> ::= <select-clause-list> <from-clause> <optional-where-clause>
<optional-define-formula-clause-list> <optional-define-set-clause-list>
| <create-formula-clause>
| <alter-formula-clause>
| <drop-formula-clause>
| <create-set-clause>
| <alter-set-clause>
| <drop-set-clause>

<optional-define-formula-clause-list> ::= <empty>
| <define-formula-clause-list>

<define-formula-clause-list> ::= <define-formula-clause>
| <define-formula-clause>, <define-formula-clause-list>

<define-formula-clause> ::= DEFINE <define-formula-subset>

<create-formula-clause> ::= CREATE <optional-scope> <define-formula-subset>

<alter-formula-clause> ::= ALTER <define-formula-subset>

<drop-formula-clause> ::= DROP MEMBER <fully-qualified-member-name>

<define-formula-subset> ::= MEMBER <fully-qualified-member-name> AS
<property-definition-list>

<property-definition-list> ::= <property-definition>
| <property-definition>, <property-definition-list>

<property-definition> ::= <property-identifier> = <property-value>

<property-identifier> ::= <identifier>

<property-value> ::= <string>
| <number>

<optional-define-set-clause-list> ::= <empty>
| <define-set-clause-list>

<define-set-clause-list> ::= <define-set-clause>
| <define-set-clause>, <define-set-clause-list>

<define-set-clause> ::= DEFINE <define-set-subset>

<create-set-clause> ::= CREATE <optional-scope> <define-set-subset>

<alter-set-clause> ::= ALTER <define-set-subset>

<drop-set-clause> ::= DROP SET <set-name>

<define-set-subset> ::= SET <set-expression> AS <set-name>

<optional-scope> ::= <empty> | GLOBAL | SESSION

Example

select … define member Measures.Measure.Margin AS
   expression = "sales - cost",
   forecolor = "if( margin < 0, red, green )"

select topnpro on rows
   { cxt, scale, margin } on columns
   from sales
   define member sales.measures.margin as
      expression = "sales - cost"
   define set topcount(products,10,5.98) AS topnpro

create member Products.Product.[Office] AS
expression = "[Word] + [Excel] + [Access]"

Scope

There are several scopes an object (formula or set) may occur within. A separate syntax is used for each scope.

  • Query scope. The visibility and lifetime of the object is limited to the query. The formula is defined in an individual query. Query scope overloads session and global scope.

  • Session scope. The visibility and lifetime of the object is limited to the session that created it. Session scope overloads global scope.

  • Global scope. The visibility extends to other users, and its lifetime extends beyond the session from which it was created.

Notes

  • Objects specified within an individual query have query scope.

  • Objects not specifying a scope have session scope.

  • ALTER and DROP always affect the most local scope (i.e. session before global).

Quoted Strings in Member Definitions

Strings inside member definitions are delimited with either single or double quotes. These can be nested arbitrarily by alternating single and double quotes.

Standard Properties

Some standard property names exist. When connected to a Microsoft® OLAP Server, the list of standard properties is either supported or available to be supported, as the Administrator chooses.

Additional properties may be available. The properties depend upon the cube definition, and represent information relevant to the Dimension / Level in the cube.

Property | Meaning
Expression | Value, an additive numeric quantity. It need not be additive, since it might be used only as a displayed member. For instance, percentages might be returned as the result of a calculation.
SolveOrder | The order in which the formula will be solved in case of intersection with other formulas.
Format_String | MS Office format string - for the cell values
Alignment | 0-Left, 1-right, 2-center - for the cell values
Fore_Color | RGB value – for the cell values
Back_Color | RGB value – for the cell values
Font_Name | The font name - for the cell values
Font_Size | The font size - for the cell values
… |

Ambiguities and Name Conflicts

Ambiguities (name conflicts) can arise. It is the responsibility of the client application to provide unambiguous (qualified) names. It is the responsibility of the provider code to warn of potential ambiguities, or to provide notification (alerts?) if ambiguities exist. To prevent name conflicts, the following rules apply:

  • In the INSERT INTO statement, level name can be qualified with dimension name. Measure name can be qualified with the keyword MEASURES. For example:
    Create Cube myCube (
   Dimension Products,
      Level ID,
   Dimension Customer,
      Level ID,
   Measure ID);
Insert Into myCube (Products.ID, Customer.ID, Measures.ID) Select Dim_Products.ID, Dim_Customers.ID, Sales_ID From Dim_Products, Dim_Customers, Sales Where Sales.ID=Products.ID AND Sales.ID = Customers.ID;
  • Existing members may not be overridden. For instance, one cannot create a new member function "WA" if "WA" already exists.

SELECT

The SELECT statement is described in the OLE DB for OLAP specification.


Implementation Notes

This section describes how OLE DB and OLE DB for OLAP objects are implemented in this beta release of Microsoft® PivotTable® Service. The discussion identifies which interfaces are supported, open issues related to OLE DB for OLAP and Microsoft PivotTable Service, and implementation limitations.


Data Source Objects

Data source objects are described in the OLE DB specification.

Supported Interfaces

  • IDBAsynchStatus

  • IDBCreateSession

  • IDBInfo

  • IDBInitialize

  • IDBProperties

  • ISupportErrorInfo

Microsoft PivotTable Service Issues

Cube information is cached for each data source, and may not reflect current data for multi-user data sources.

The following table lists the properties used for initialization:

Property | Description
DBPROP_INIT_DATASOURCE | Name of server machine or local cube file
DBPROP_INIT_CATALOG | Name of default (initial) database (catalog)
DBPROP_AUTH_USERID | Ignored in this release
DBPROP_AUTH_PASSWORD | Ignored in this release
DBPROP_INIT_ASYNCH | Used for asynchronous initialization
DBPROP_MSMD_CREATE_CUBE | (optional) CREATE CUBE statement to create a local cube file Otherwise: NULL
DBPROP_MSMD_INSERT_INTO | (optional) INSERT INTO statement to populate a local cube file created with CREATE CUBE Otherwise: NULL
DBPROP_MSMD_SOURCE_DSN | ODBC connection string, OLEDB connection string, or DSN for source relational database, used only when creating a local cube file
DBPROP_MSMD_SOURCE_UID | ODBC UID for source relational database, used only when creating a local cube file
DBPROP_MSMD_SOURCE_PWD | ODBC PWD for source relational database, used only when creating a local cube file
DBPROP_INIT_PROVIDERSTRING | A predefined string containing other initialization properties.
MSMD_USEEXISTINGFILE | If the first character of this string is Y, T, or a numeric digit other than 0, CREATE CUBE and INSERT INTO properties will be ignored.
MSMD_ARTIFICIALDATA | If the first character of this string is Y, T, or a numeric digit other than 0, artificial aggregate values will be calculated using a simple algorithm instead of calculating real values.

Limitations

IDBInfo is not fully tested.

IPersistFile is not fully supported

Asysnchronous initialization supports polling only through IDBAsynchStatus. The IDBAsynchNotify connection point is not supported.


Session Objects

Session objects are described in the OLE DB specification.

Supported Interfaces

  • IDBCreateCommand

  • IDBCreateSession

  • IDBSchemaRowset

  • IGetDataSource

  • IOpenRowset

  • ISessionProperties

  • ISupportErrorInfo

Microsoft PivotTable Service Issues

None.

Limitations

IOpenRowset is not supported.

ISessionProperties is not fully tested.


Command Objects

Command objects are described in the OLE DB specification.

Supported Interfaces

  • ICommandProperties

  • ICommandText

  • IConvertType

  • ISupportErrorInfo

Microsoft PivotTable Service Issues

ICommandText is implemented to support textual queries as suggested in the OLE DB for OLAP specification.

Two GUID dialects are available for use with ICommandText::SetCommandText. They are MDGUID_MDX and DBGUID_MDSQL. When MDX is used, Microsoft® PivotTable® Service supports the multidimensional expression (MDX) syntax described in the OLE DB for OLAP specification.

When DBGUID_DEFAULT or DBGUID_NULL is used, the provider attempts to determine if the query is SQL or MDX. It is recommended that explicit use of MDGUID_MDX or DBGUID_SQL be specified to avoid ambiguities.

A subset of SQL is supported.

SQL queries against local cube files are not supported in this release, and will return an error.

Limitations

IAccessor is not available on the Command object. This is a required interface for OLE DB, but Microsoft PivotTable Service uses the OLE DB Simple Provider component distributed with the OLE DB SDK for rowset support; this component does not allow IAccessor on the Command object.

ICommandProperties is not complete.


Rowset Objects

Rowset objects are described in the OLE DB specification.

Supported Interfaces

  • IAccessor

  • IColumnsInfo

  • IConvertType

  • IDBAsynchStatus

  • IRowset

  • IRowsetLocate

  • ISupportErrorInfo

Microsoft PivotTable Service Issues

The OLE DB SDK includes a component that is used by Microsoft® PivotTable® Service for rowset support. This component may impose additional limitations (and may be replaced by a different tool in future versions).

Microsoft PivotTable Service uses the OLE DB Simple Provider component distributed with the OLE DB SDK for rowset support, which may impose other limitations. A different tool may be implemented in a later release.

All member properties are supported with Flattened Rowsets, as defined in the OLE DB for OLAP specification.

Limitations

All rowsets are read-only.


Dataset Objects

Dataset objects are described in the OLE DB for OLAP specification.

Supported Interfaces

  • IAccessor

  • IColumnsInfo

  • IConvertType

  • IDBAsynchStatus

  • IMDDataset

  • ISupportErrorInfo

Microsoft OLE DB for OLAP PivotTable Service Issues

IMDFind is not supported.

All cell properties are supported.

All member properties defined as columns in the schema rowset are supported.

Limitations

All query results are read-only.


Schema Rowsets

This section describes the contents of schema rowsets, as defined in the current implementation of Microsoft® PivotTable® Service. Each rowset is described by a table. Note that:

  • Some of the values in the "Description" column are listed as NULL because they are unknown. (The OLE DB for OLAP specification may require that some columns be non-NULL.)

  • The term "fully-qualified name" means that all parent elements are included in the name, up to but not including, the cube name, and that all elements are enclosed in brackets. For members, all parents are included and level names are excluded – for example:
    [HighwaysDim].[LocationHier].[USA].[Washington].[Seattle]

AXIS Rowset

Note The AXIS Rowset is not a true schema rowset because it will not be returned by IDBSchemaRowset.

The current OLE DB for OLAP specification provides two separate names for columns appearing in AXIS rowsets. Microsoft PivotTable Service uses the names shown in the following table.

Name | Description
MEMBER_UNIQUE_NAME Fully-qualified member name, for example:
[HighwaysDim].[LocationHier].[USA].
[Washington].[Seattle]
MEMBER_CAPTION Naked member name, for example:
Seattle
LEVEL_UNIQUE_NAME Level unique name, for example:
[HighwaysDim].[LocationHier].[CityLevel]
LEVEL_NUMBER | Level depth, zero-based
CHILDREN Can be one of the following:
  • Zero, if it is guaranteed that no children exist

  • The actual child count

  • A large constant if the count is unknown. This constant is 1000 in the current implementation.

Member properties are also supported and will appear in the AxisRowset if specified in the query. Member properties can be specified for each axis but not for each dimension. For each dimension in the axis where member properties are specified, the corresponding columns from the MEMBERS schema rowset will appear. Consumers can use the information in AxisInfo to navigate this information.

CUBES Rowset

Name | Description
CATALOG_NAME | Database name.
SCHEMA_NAME | Always NULL
CUBE_NAME | Cube name
CUBE_TYPE | Either "CUBE" or "VIRTUAL CUBE"
CUBE_GUID | Always NULL
CREATED_ON | Always NULL
LAST_SCHEMA_UPDATE | Time when last cube or dimension was processed
SCHEMA_UPDATED_BY | Always NULL
LAST_DATA_UPDATE | Time when last cube or dimension was processed
DATA_UPDATED_BY | Always NULL
DESCRIPTION | Always NULL

DIMENSIONS Rowset

Name | Description
CATALOG_NAME | Database name
SCHEMA_NAME | Always NULL
CUBE_NAME | Cube name
DIMENSION_NAME | Dimension name
DIMENSION_UNIQUE_NAME | Fully-qualified dimension name.
DIMENSION_GUID | Always NULL
DIMENSION_ORDINAL | Zero-based ordinal. The measures dimension is represented by the ordinal 0.
DIMENSION_CAPTION | Dimension name
DIMENSION_TYPE One of the following values:
  • MD_DIMTYPE_MEASURE

  • MD_DIMTYPE_TIME

  • MD_DIMTYPE_OTHER
DIMENSION_CARDINALITY | Count of members. For dimensions with many members, the count may be an approximation.
DEFAULT_HIERARCHY | Returns the first hierarchy in the list for this dimension; NULL if this hierarchy has no name, otherwise a fully-qualified hierarchy name.
DESCRIPTION | Always NULL

HIERARCHIES Rowset

Name | Description
CATALOG_NAME | Database name
SCHEMA_NAME | Always NULL
CUBE_NAME | Cube name
DIMENSION_NAME | Dimension name
DIMENSION_UNIQUE_NAME | Fully-qualified dimension name
HIERARCHY_NAME | Hierarchy name
HIERARCHY_UNIQUE_NAME | Fully-qualified hierarchy name. (This is just the dimension name if there is only one hierarchy). All elements are enclosed in brackets.
HIERARCHY_GUID | Always NULL
DIMENSION_TYPE One of the following values:
  • MD_DIMTYPE_MEASURE

  • MD_DIMTYPE_TIME

  • MD_DIMTYPE_OTHER
HIERARCHY_CAPTION | Dimension name <space> <Hierarchy name>. (If no hierarchy exists, then just the dimension name.)
DEFAULT_MEMBER | Always returns the very first member
ALL_MEMBER | The name of the default member if the first level is "All"; otherwise NULL.
DESCRIPTION | Always NULL

LEVELS Rowset

Name | Description
CATALOG_NAME | Database name
SCHEMA_NAME | Always NULL
CUBE_NAME | Cube name
DIMENSION_UNIQUE_NAME | Fully-qualified dimension name
HIERARCHY_UNIQUE_NAME | Hierarchy unique name
LEVEL_NAME | Level name
LEVEL_UNIQUE_NAME | Fully-qualified level name
LEVEL_GUID | Always NULL
LEVEL_CAPTION | Level name
LEVEL_NUMBER | Zero-based ordinal of level of this dimension
LEVEL_TYPE One of the following values:
  • MDLEVEL_TYPE_ REGULAR

  • MDLEVEL_TYPE_ALL

  • MDLEVEL_TYPE_CALCULATED

  • MDLEVEL_TYPE_TIME

  • MDLEVEL_TYPE_RESERVED1

  • MDLEVEL_TYPE_TIME_YEARS

  • MDLEVEL_TYPE_TIME_HALF_YEAR

  • MDLEVEL_TYPE_TIME_QUARTERS

  • MDLEVEL_TYPE_TIME_MONTHS

  • MDLEVEL_TYPE_TIME_WEEKS

  • MDLEVEL_TYPE_TIME_DAYS

  • MDLEVEL_TYPE_TIME_HOURS

  • MDLEVEL_TYPE_TIME_MINUTES

  • MDLEVEL_TYPE_TIME_SECONDS

  • MDLEVEL_TYPE_TIME_UNDEFINED

  • MDLEVEL_TYPE_UNKNOWN
DESCRIPTION | Always NULL

MEASURES Rowset

Name | Description
CATALOG_NAME | Database name
SCHEMA_NAME | Always NULL
CUBE_NAME | Cube name
MEASURE_NAME | Measure name
MEASURE_UNIQUE_NAME | Fully-qualified member name, including dimension. Suitable for use in a query.
MEASURE_CAPTION | Measure name
MEASURES_GUID | Always NULL
MEASURE_AGGREGATOR Returns one of the following values:
  • MDMEASURE_AGGR_SUM

  • MDMEASURE_AGGR_COUNT

  • MDMEASURE_AGGR_MIN

  • MDMEASURE_AGGR_MAX
DATA_TYPE OLE DB data type that most closely matches the OLAP Provider type. One of the following values:
  • DBTYPE_I2

  • DBTYPE_I4

  • DBTYPE_I8

  • DBTYPE_R4

  • DBTYPE_R8
NUMERIC_PRECISION | Numeric precision for OLE DB data type
NUMERIC_SCALE | Numeric scale for OLE DB data type
DESCRIPTION | Always NULL

PROPERTIES Rowset

Microsoft PivotTable Service supports cell properties only in this release.

Name | Description
CATALOG_NAME | Always NULL
SCHEMA_NAME | Always NULL
CUBE_NAME | Always NULL
DIMENSION_UNIQUE_NAME | Always NULL
HIERARCHY_UNIQUE_NAME | Always NULL
LEVEL_UNIQUE_NAME | Always NULL
MEMBER_UNIQUE_NAME | Always NULL
PROPERTY_TYPE | Always MDPROP_CELL. A bitmask indicating that this is a property of the cell. Can be used in the CELL PROPERTIES clause that occurs at the end of a SELECT statement.
PROPERTY_NAME One of the following values:
  • VALUE

  • FORMATTED_VALUE

  • CELL_ORDINAL

  • FORMAT_STRING

  • FORE_COLOR

  • BACK_COLOR

  • FONT_NAME

  • FONT_SIZE

  • FONT_FLAGS
PROPERTY_CAPTION | Property name
DATA_TYPE | Data type, as listed in the OLE DB specification
CHARACTER_MAXIMUM_LENGTH | 255, if appropriate
CHARACTER_OCTET_LENGTH | As appropriate
NUMERIC_PRECISION | As appropriate
NUMERIC_SCALE | As appropriate
DESCRIPTION | Always NULL

MEMBERS Rowset

Name | Description
CATALOG_NAME | Database name
SCHEMA_NAME | Always NULL
CUBE_NAME | Cube name
DIMENSION_UNIQUE_NAME | Dimension unique name
HIERARCHY_UNIQUE _NAME | Hierarchy unique name
LEVEL_UNIQUE _NAME | Level unique name
LEVEL_NUMBER | Zero-based ordinal of level of this dimension
MEMBER_NAME | Member name without parent member
MEMBER_UNIQUE_NAME | Fully-qualified member name, including dimension, hierarchy, and all parent members.
MEMBER_TYPE One of the following:
  • MDMEMBER_TYPE_REGULAR

  • MDMEMBER_TYPE_ALL

  • MDMEMBER_TYPE_MEASURE

  • MDMEMBER_TYPE_FORMULA
MEMBER_GUID | Always NULL
MEMBER_CAPTION | Member name without parent member
CHILDREN Can be one of the following:
  • Zero, if it is guaranteed that no children exist

  • The actual child count

  • A large constant if the count is unknown. This constant is 1000 in the current implementation.
PARENT_LEVEL | LEVEL_NUMBER minus 1 (0 for root level).
PARENT_NAME | Parent member name. NULL for root level of dimension.

Schema Rowset Restrictions

TREE_OPERATOR is implemented. Operators are combined with bitwise OR. The resulting rowset is the union of each operation, as required by the OLE DB for OLAP specification.

Note that duplicate rows may be generated when the algorithm for tree operators is used – for example, when retrieving the siblings of all members in a level – but this presents no practical problems.

MEMBER_NAME acts like most other restrictions. You can view the rowset as the result of a WHERE clause constraint on a giant table of all members. (This is the normal model presented by the OLE DB for OLAP specification.) Duplicates will appear. For instance, restricting member name to Portland might yield two rows, with parent names of ME and OR. Note that the member name restriction should not be enclosed in brackets, since it does not appear in the context where brackets would be needed.

The MEMBER_UNIQUE_NAME restriction acts like a WHERE clause. Input to the restriction can be fully qualified with elements enclosed in brackets. It is not an error for MEMBER_UNIQUE_NAME to result in no members – for example, city [XXX], which does not exist, will return an empty rowset.

Giving nonstring restrictions (for example, VT_NULL) for member names will yield an empty rowset.

The MEMBER_UNIQUE_NAME restriction can accept unqualified names, but will return only one matching row for the cube, even if several exist. The restriction is expected to execute more quickly than the MEMBER_NAME restriction because more information is available for use in searching.


Deviations from or Clarifications to the OLE DB for OLAP Specification

This section discusses the ways in which the current release of Microsoft® PivotTable® Service either differs from or expands upon the OLE DB or OLE DB for OLAP specifications.

Commands

Accessors are not available from Command objects. This is due to the use of components for rowsets that do not provide for accessor creation outside the scope of a rowset.

OLE DB Properties

OLE DB properties (DBPROPSET) are not fully implemented.

OLAP Properties

Dimension properties (properties derived from a dimension or level) are not supported. None will be returned from the PROPERTIES or MEMBERS schema rowsets. None can be requested in an MDX query.

Member properties are supported. The member properties are not returned from the PROPERTIES rowset. Only the standard cell properties are exposed.

Filter Axes

IMDDataset::GetAxisInfo returns the filter axis as the last element of the MDAXISINFO array. In the filter axis, iAxis=(~0)=MDAXIS_FILTERS.

The AXIS rowset for filter axes can be obtained by requesting either iAxis=cAxis-1 (the last axis), or iAxis=MDAXIS_FILTERS.

Measures

Microsoft PivotTable Service exposes measures as the first dimension (ordinal 0), in the dimensions rowset with the name "Measures". This dimension has one level named "MeasuresLevel". (The OLE DB for OLAP specification suggests, but does not require, that the name of the level be "Measures"; providers can select a different name.)

Microsoft PivotTable Service does not embed level names in the fully-qualified member names that it returns. (This is not an issue with respect to the OLE DB for OLAP specification, because it simply says that providers are to present unambiguous member names.)

Flattened Rowsets

Microsoft PivotTable Service uses the following algorithm to determine whether to create a flattened rowset or a dataset. First, Microsoft PivotTable Service recognizes the IIDs IRowset, IRowsetIdentity, IRowsetInfo, IRowsetLocate, and IRowsetScroll, Then it examines DBPROPSET_Rowset and DBPROP_IRowset, which serve as a means for requesting a flattened rowset rather than a dataset. If these indicators are absent, a dataset is produced. Datasets are the preferred method for navigating the result of an MDX query.

Flattened rowsets report the data type of each column as DBTYPE_VARIANT. Clients still bind columns as strings if appropriate, and the conversion will occur successfully.

For flattened rowsets, Microsoft PivotTable Service returns DBSTATUS_S_ISNULL rather than MDSTATUS_S_NOTONAXIS. Both of these values are versions of NULL, but as a practical matter that is acceptable because the OLE DB for OLAP specification allows multiple versions of NULL in order to support missing sublevel elements. The term "missing sublevel elements" refers to the situation that arises when there are many levels for a dimension and some of the axis tuples do not exist at the lowest level. For example, consider the hierarchy [Country].[State].[City]. If [State] is the finest level of granularity, the [City] member name is returned as DBSTATUS_S_ISNULL. (DBSTATUS_S_ISNULL is the representation expected from a ROLAP implementation.)

IMDFind Interface

The IMDFind interface is not supported.

MDX Syntax

This section describes current deviations from or clarifications to the MDX syntax as described in the OLE DB for OLAP specification.

  • Using an ambiguous member name or level name will not generate a warning or error and could return any one of the members that matches the ambiguous name.

  • Defined sets cannot appear inside a WHERE clause. The reason for this limitation is that a defined set may contain a formula (for example, "TOP") that depends upon the context defined by the WHERE clause.

  • Division operations return double-precision floating point values even when both operands are integers; this differs from the behavior of Microsoft® SQL Server™.

  • In the Elements topic, <dim-hier>.LEVELS(index) is not supported.

  • In the Expressions topic, <tuple>.VALUE is not supported.

  • In the Set Value Expression topic, the following differences apply:

  • When a virtual dimension of a cube contains more than 1,000 members, the HIERARCHIZE function does not sort members according to their natural order and does not delete duplicate members from a set when sorting it.

  • The TOPCOUNT and BOTTOMCOUNT functions will return incorrect results if the optional third parameter, <numeric value expression> is not specified.

  • The functions PERIODSTODATE, LASTPERIODS, YTD, QTD, MTD, and WTD are not implemented.

  • In the Member Value Expression topic, the following differences apply:

  • The FIRSTCHILD and LASTCHILD functions may return incorrect values.

  • The keyword CURRENTMEMBER is implemented as CURRENT.

  • The optional COUSIN, PARALLELPERIOD, OPENINGPERIOD, and CLOSINGPERIOD functions are not implemented.

  • In the Tuple Value Expression topic, the keyword CURRENTMEMBER is implemented as CURRENT, and specification by <index> is not supported.

  • In the Numeric Value Function topic, the optional MEDIAN, AGGREGATE, COVARIANCE, CORRELATION, LINREGSLOPE, LINREGINTERCEPT, LINREGVARIANCE, LINREG2, and LINREGPOINT functions are not implemented.

  • The FONT_FLAGS member property is not supported in formulas.

  • Formulas support the properties EXPRESSION, SOLVEORDER, and FORMAT_STRING.


Additional Considerations

This section discusses additional considerations specific to this release of Microsoft® PivotTable® Service.

Unambiguous Names

This implementation uses two concepts for names and their use: either names are unique within the scope of their "parent" (for example, a level is unique within its parent hierarchy) or names are unique within some "global" scope. OLE DB for OLAP currently uses both naming methods. Microsoft PivotTable Service allows duplicate member, level, and hierarchy names. Cube names must be unique within a database. Dimension names must be unique within a cube (and within a database if the dimension is public). To avoid costly checks for uniqueness, Microsoft PivotTable Service delivers unambiguous names that contain delimited elements and are fully qualified within the scope of some known cube. For example:

[Geography Dimension].[Region Hierarchy].[USA].[NY].[New York]

Although Microsoft PivotTable Service strives to be consistent in this approach, such consistency is not required by the OLE DB for OLAP specification, according to which names need not contain delimited elements and need not be fully qualified.

Empty Axes

An "empty" axis is an axis with no members. It is equivalent to an empty rowset or cursor in a SQL statement. For example, the SQL query

SELECT * FROM Sales WHERE Amount >1000000 AND Salesperson = 'Jacobsen'

may not return any rows. However, an empty axis is not the same as an absent axis; an empty axis is present, but it contains no members.

You can obtain MDAXISINFO and the AXIS rowset for an empty axis. It is not possible to obtain a cell value because cell values are defined at the intersection of each dimension member from each axis (including the filter axis) and one axis has no such intersection. Another way to explain this is that the set of p axes defines a p-dimensional result, and the cells comprise the volume of the p-dimensional space. When an axis is empty, the length along that dimension is 0, and therefore the volume of the p-dimensional space is 0.

Axis members in Microsoft PivotTable Service correspond to rows in SQL queries. The result of an axis expression is a set of tuples that are dimension/level members, in the same way that a SQL query results in a set of tuples that are column members.

Using this analogy, it is legal to have no rows from an SQL query, and no dimension/level members on an axis. However, it is illegal to ask for no columns in a SQL query, and legal to ask for no dimensions in an axis expression. (Although legal, the request is probably useless.)

It is legal, however, to have an axis with no dimensions. MDX supports the notion of an empty axis, which is different from an illegal axis. One can easily create an empty axis by specifying {}. Empty axes are frequently encountered when using the "NON-EMPTY" clause, since it removes axis tuples containing only empty values.

No Axes

A query with no axes has one cell. Each dimension is accounted for and is present in the filter axis. The single cell coordinate is zero. You might expect that the p-dimensional space occupied by the result set has zero volume, but that is not true because there are no displayed axes to define the space other than the filter axis, which has only one tuple.

Recursive Formulas and Sets

In general, formulas and sets can be defined in terms of other formulas and sets.

Threading Issues

Microsoft PivotTable Service is thread safe. Its OLE COM threading model is "Both" (no thread marshalling for "Apartment-threaded" or "Free-threaded" clients).

Microsoft PivotTable Service establishes a single connection to the server for each DataSource object. The communication protocol is serialized, with the granularity of the DataSource (this is subject to change in the future).

Rowsets are not thread safe. This includes axis rowsets, schema rowsets, and flattened datasets.


MDXSample

This generic OLE DB for OLAP client application, written in Visual Basic®, uses ADO MD to obtain schema information and query a multidimensional data source. The application consists of a query window to enter MDX queries and a schema window where schema information is presented in a tree view as well as a dataset grid where query data is displayed in a pivot table.

To connect to the sample OLAP provider:

  1. Start the MDX Sample application (MDXSampl.exe).

  2. In the initial connection dialog box, enter the name and path to the sample cube database (BobsVideo.cub) in the Server text box, and enter MSOLAP in the Provider text box.

To enter or edit a sample query:

  3. Use Open from the File menu to open the MDXQuery.mdx sample query file.

  4. Select a sample query from the Queries box on the toolbar.

MDXSamples.mdx is a text file which contains several MDX queries designed for the sample cube database file, BobsVideo.cub. New queries can be entered and saved as well.

Fully commented Visual Basic source code for the MDX Sample Application can be viewed by opening the MDXSampl.vbp project file in Visual Basic. The frmMain.frm or Main.frm file contains sample code. A search on "ADO" provides specific information and comments on how ADO MD is used by this application. These commented code examples are useful for creating a new MDX querying application.


OLE DB Test Tools Overview

OLE DB SDK Test Tools Overview

Testing components are provided to help you develop OLE DB consumers and providers. These components are not redistributable and must be used solely for developing OLE DB consumers and providers.

Interface Test Tool (ITest)

The Interface Test Tool is a COM interface testing environment that enables you to exercise and explore OLE DB providers through an ad hoc mechanism and to record the results of these actions. ITest works with any OLE DB provider that is registered on your system and can create and manage OLE DB data source objects, sessions, commands, and rowsets.

ITest Spy Tool

The ITest Spy tool enables you to record calls executed by an OLE DB consumer. Spy records all information about each OLE DB call, including the input parameters supplied, the output parameters returned, and any return codes or messages.

To use the OLE DB Spy facility, HookOle must be installed. The HookOle files are in the hookole subdirectory of the MSDA SDK.

Important If HookOle is already installed on your computer you must remove it before installing the version contained in the DA SDK.I If you do not have a previous version of HookOle on your computer, skip these steps and begin the installation process with the section "To install HookOle:" later in this topic.

To remove/unregister a previous version of HookOle

  1. Click Start, point to Settings, and click Control Panel. Double-click the Add/Remove Programs icon.

  2. Click the Install/Uninstall tab. Find HookOle in the list of programs and select it.

  3. Click the Add/Remove button. Follow the instructions to remove HookOle.

If for any reason this fails, manually rename the ole32.dll file to some other name and then rename the ole32r.dll to ole32.dll. This removes HookOle from the operating system.

To install HookOle

  1. Run HookOle setup from the \hookole directory within the DA SDK directory.

  2. Restart your computer. HookOle registration is not complete until your computer is restarted.

Important If you reinstall Windows 95 or Windows NT on your computer, you will have to reinstall HookOle.

The ITest executable files are found in the BIN/OLEDB subdirectory. The following files are components of ITest.

File name | Description
ITNode.dll | Object node support
ITEST.EXE | Interface test application (Interface Test Tool environment shell)
ITESTSPY.EXE | ITest Spy application
ITCallWindow.DLL | Interface test call window
oledbADL.DLL | OLE DB operations library
ITADL.DLL | Base class for API definitions
oledbRowsetWindow.DLL | Interface Test rowset window shell
ITAIS.dll | General execution services

Local Test Manager (LTM)

The Local Test Manager (LTM) is an easy-to-use tool that provides a single point of control for automated testing of OLE DB providers. Once you have created your test modules byusing the OLE DB Test Editor to help generate a .cpp file, you can use LTM to select and run a test module and its cases, variations, and providers on a single local computer. For more information on using LTM for conformance testing see "Conformance Testing in OLE DB 2.0" technical article.

OLE DB Test Editor

The Microsoft OLE DB Test Editor enters the automated test framework code in a .cpp file for use with Local Test Manager. The framework code represents a module with tests and variations that will be run.

File name | Description
TestWizard.EXE | A wizard that assists test designers in creating and modifying tests.
LTM.EXE | The main Local Test Module component. This component runs automated tests.

QuikTest Test Module

The QuikTest module provides a broad range of test coverage. This coverage produces a useful overview of provider functionality but does not test OLE DB providers thoroughly. For information about how to build and run the QuikTest module, see "Unsupported Sample Test Modules" in this topic.

To run the QuikTest, you must specify initialization information for the provider you select. To specify this information, click in the Init String in the Tools/Options dialog box under the Providers tab in LTM.

For data sources that do not require a password, the Init String should have the following format:

NAME=Value;USERID=Value;

For example, to run against the ODBC Provider using the Microsoft Jet OLE_DB_NWind_Jet ODBC data source installed with this SDK, specify the following Init String for a provider:

NAME=OLE_DB_NWind_Jet;USERID=admin;

For data sources that require a password, the Init String should have the following format:

NAME=Value;USERID= Value;PASSWORD=Value;

For example, to run against the ODBC Provider using a SQL Server data source that requires a password, specify an Init String similar to the following:

NAME=MySQLServerDSN;USERID=SSUser;PASSWORD=SSPwd;

File name | Description
QUIKTEST.DLL | QuikTest module

Unsupported Sample Test Modules

The MSDA SDK includes some sample test modules in the \conformance\tests directory. Each test module is named with an abbreviated form of the interface it tests. These tests, provided in source code form, can be compiled and run through the Local Test Manager. These test modules are offered only as examples of test code and are not supported in any way. You may not make any claims of conformance based on these tests, nor may you redistribute them in whole or in part.

Before you can build any of the OLE DB tests except for QuikTest, you must first build the Private Library (privlib.dll), which is used by these tests.

To build the Private Library in Microsoft Developer Studio

  1. Open the workspace of the Private Library makefile. This is in \msdasdk\conformance\tools\PrivLib.

  2. From the Tools menu, choose Options, then click the Directories tab. Add the following directories for include files:

    \msdasdk\conformance\include
    \msdasdk\conformance\tests\proposed\OLEDBTests\include

    and the following directories for library files:

    \msdasdk\conformance\lib
    \msdasdk\lib

  3. Build the Private Library (PrivLib.dll) using the default target.

  4. Copy privlib.dll from \msdasdk\conformance\tools\PrivLib\x86_debug to \msdasdk\bin.

To build the OLE DB tests, including QuikTest, in Microsoft Developer Studio

  1. Open the workspace of the makefile for the test you want to build. The makefiles are found in \msdasdk\conformance\tests\unsupported\tests and in \msdasdk\conformance\tests\proposed\OLEDBTests.

  2. From the Tools menu, choose Options, then click the Directories tab. Add the following directory for include files:

    \msdasdk\conformance\include

    Note that you still need the include and library directories you added when you built the Private Library.

  3. Build the test module using the default target.

To run the OLE DB tests, including QuikTest

  1. Update your environment path. In the Control Panel, double-click the System icon. Click the Environment tab. In the System Variables list, select the Path variable. In the Value box, type <install directory>\bin at the end of the value, separating it from the previous directory by a semicolon.

  2. Following the instructions in the Local Test Module Help file, register, install, and run the test. The initialization information to be used is explained in the "QuikTest Test Module" section, earlier in this topic.


OLE DB Samples

Samples are provided as a quick way to construct and observe OLE DB interfaces. Included with OLE DB are the following:

For information on | See
How to build a simple consumer | Sample Consumer Contents
How to build a consumer capable of issuing queries | Query Demo Contents
A simple way to view and manipulate rowsets | Rowset Viewer Contents
A simple consumer that reads data from one OLE DB rowset and saves it as a new table | Table Copy Contents


Query Demo Contents

This section contains the following information:

  • Query Demo Overview

  • Query Demo Files

  • Building Query Demo

  • Running Query Demo

  • Query Demo Menus and Toolbar

  • Query Demo Example


Query Demo Overview

Query Demo demonstrates multiple, simultaneous connections to OLE DB providers. It enables you to type any command text and browse the results. T

To run the query demo, click Start, point to Programs, point to OLE DB SDK, and click Query Demo Client Sample.

File name | Description
QURYDEMO.EXE | Query Demo sample

Query Demo Features

With Query Demo, you can connect to and retrieve data from multiple OLE DB providers. The only limitations on a session with a provider are imposed by the driver and the amount of memory available on the system. Each session can support multiple commands, which are run in MDI child windows. The command window supports any command text that is valid for the provider and displays the rows and columns of a rowset, if one is returned, in the window. Additionally, built-in commands query for certain schema rowsets; see Query Demo Menus and Toolbar for more on the schema rowsets available through Query Demo.

Query Demo Limitations

The following limitations are declared in QURYDEMO.H.

Description | Limit
Number of child windows | 10
Size of display buffer in bytes | 256
Number of columns in rowset | 35
Number of rows in rowset | 100
Length of data in a column, in bytes | 25
Length of a column name, in bytes | 20


Query Demo Files

The following source files are included with Query Demo. The source files are located in the <install directory>\SAMPLES\QURYDEMO directory; the Query Demo executable file QURYDEMO.EXE is located in the <install directory>\BIN directory.

File name | Description
MAIN.CPP | Main window and application source file. Functions in this file process messages sent to the application window, MDI child windows, the status bar, toolbar, connection dialog, and other interface elements.
NEWQUERY.BMP | Toolbar bitmap to create a new command. Clicking this button performs the same function as choosing New Command from the Command menu.
QUERY.CPP | Main OLE DB implementation file for Query Demo. Functions in this file initialize OLE, display available providers, display available data sources, facilitate connections to data sources, display lists of active connections and commands, open new commands, set up binding structures, create accessors, execute commands and return rowsets, maintain command window data displays, and free OLE objects.
QUERYWDW.ICO | Child (command) window icon.
QURYDEMO.DEF | Module definition file.
QURYDEMO.H | Application header file.
QURYDEMO.ICO | Application icon.
QURYDEMO.MAK | Application NMAKE file.
QURYDEMO.RC and QURYDEMO.RC2 | Query Demo resource files.
QURYVER.H | Version include file.
RESOURCE.H | Resource include file.
RUNQUERY.BMP | Toolbar bitmap to execute a command. Clicking this button performs the same function as choosing Execute Command Text from the Command menu.


Building Query Demo

The file <install directory>\SAMPLES\QURYDEMO\QURYDEMO.MAK is set up for compilation with NMAKE, the Microsoft® Program Maintenance Utility provided with Microsoft Visual Studio™. The following example syntax builds a debug version of QURYDEMO on the Intel® x86 platform based on the Win32® (x86) Application configuration.

NMAKE /f "qurydemo.mak" CFG=" qurydemo - Win32 x86 Debug"

To build QURYDEMO on another platform or with a different configuration, use one of the following configuration options.

Configuration option | Type of file built
"qurydemo - Win32 x86 Debug" | Debug version of QURYDEMO based on Win32 (x86) Application
"qurydemo - Win32 x86 Release" | Retail version of QURYDEMO based on Win32 (x86) Application
"qurydemo - Win32 (ALPHA) axp Debug" | Debug version of QURYDEMO based on Win32 (ALPHA) Application
"qurydemo - Win32 (ALPHA) axp Release" | Retail version of QURYDEMO based on Win32 (ALPHA) Application
"qurydemo - Win32 (PPC) ppc Debug" | Debug version of QURYDEMO based on Win32 (PPC) Application
"qurydemo - Win32 (PPC) ppc Release" | Debug version of QURYDEMO based on Win32 (PPC) Application

For additional information about using NMAKE, see the Microsoft Visual Studio documentation.


Running Query Demo

To run Query Demo, click Start, point to Programs, point to OLE DB SDK, and click Query Demo Client Sample, or run the <install directory>\BIN\QURYDEMO.EXE file from Windows® Explorer.


Query Demo Menus and Toolbar

This section briefly describes the menu and toolbar commands in Query Demo. For more information about the OLE DB interfaces and methods that are executed by many of the menu items, see the OLE DB Programmer’s Reference.

IDBInitialize Menu

The IDBInitialize menu contains the following data source initialization commands.

Menu item | Description
Initialize | Executes IDBInitialize::Initialize with the parameters you supply in the Initialize A Data Source dialog box. Choose a provider from the list and enter the data source name, user ID, and password, as required. Click Initialize to initialize the data source.
Uninitialize | Executes IDBInitialize::Uninitialize on the data source you select from the list. Select the data source you want to uninitialize from the list and click Release. You can also close individual commands by clicking the Close Command button. Click Close when you are done.

Command Menu

The Command menu contains the following query management commands.

Menu Item | Description
Execute Command Text | Executes ICommand::Execute with the command text specified in the command window.
New Command | Executes IDBCreateCommand::CreateCommand and opens a new window for the command.

Schema Menu

The Schema menu retrieves the following schema rowsets. Create a command and choose the schema rowset you want to view from the menu to populate the command window.

Menu Item | Description
CATALOGS | Executes IDBSchemaRowset::GetRowset for the CATALOGS schema rowset.
COLUMNS | Executes IDBSchemaRowset::GetRowset for the COLUMNS schema rowset.
PROCEDURE_ PARAMETERS | Executes IDBSchemaRowset::GetRowset for the PROCEDURE PARAMETERS schema rowset.
PROCEDURES | Executes IDBSchemaRowset::GetRowset for the PROCEDURES schema rowset.
PROVIDER_ TYPES | Executes IDBSchemaRowset::GetRowset for the PROVIDER TYPES schema rowset.
SCHEMATA | Executes IDBSchemaRowset::GetRowset for the SCHEMATA schema rowset.
TABLES | Executes IDBSchemaRowset::GetRowset for the TABLES schema rowset.

Window Menu

The Window menu contains the following commands.

Menu Item | Description
Tile | Arranges the windows as nonoverlapping tiles.
Cascade | Arranges the windows to overlap.
Arrange Icons | Arranges the icons for minimized child windows.
Close All | Closes all windows.

Help Menu

The Help menu contains the following command.

Menu Item | Description
About Qurydemo | Displays version and copyright information for Query Demo.

Toolbar and Status Bar

The toolbar and status bar contain the following items.

Menu Item | Description
Session list | The session list is implemented as a drop-down list on the left side of the toolbar. You can select among multiple active OLE DB sessions from this list.
Command list | The command list is implemented as a drop-down list to the right of the session list on the toolbar. You can select among commands on an active OLE DB session from this list.
New Command button | Click New Command to create a new command. This button executes the same action as choosing New Command from the Command menu.
Execute button | Click Execute to execute the command text on the active command. This button executes the same action as choosing Execute Command Text from the Command menu.
Status messages | During lengthy operations, a status message may appear in the lower-left corner of the status bar. For example, when you execute a command that returns a large rowset, the status bar may display Executing Command Text. When Query Demo is ready for user input, the status bar displays Ready.


Query Demo Example

To initialize a data source, make a query, and release the command and data source, follow these steps:

  1. Start QURYDEMO.EXE.

  2. From the IDBInitialize menu, select Initialize.

  3. Choose MSDASQL as the provider, and type OLE_DB_NWind_Jet for the Name parameter and admin for the UserID. Leave the Password parameter blank. Click Initialize. A command window appears.

    Note If you installed the OLE DB SDK in a directory other than the default directory, you will need to change the OLE_DB_NWind_Jet data source to use your directory.

  4. On the Command Text line, type the following query:
    SELECT * FROM CUSTOMERS
  5. From the Command menu, select Execute Command Text. Query Demo executes the command and returns the rowset.

  6. From the IDBInitialize menu, select Uninitialize. Click Release to close the connection to the data source. Releasing the data source also closes all open queries on that session.


Rowset Viewer Contents

This section contains the following information:

  • Rowset Viewer Overview

  • Rowset Viewer Files

  • Building Rowset Viewer

  • Running Rowset Viewer

  • Rowset Viewer Menus and Toolbar

  • Rowset Viewer Example


Rowset Viewer Overview

Rowset Viewer offers a simple way to view and manipulate OLE DB rowsets, with the added ability to call and manipulate other OLE DB methods from the data source, session, command, rowset, transaction, and notification objects supported by any OLE DB provider.

To run the Rowset Viewer sample, click Start, point to Programs, point to OLE DB SDK, and click Rowset Viewer.

File name | Description
ROWSETVIEWER.EXE | Rowset Viewer sample

Rowset Viewer Features

The Rowset Viewer sample has a separate MDI child window for each connection to a provider. Each MDI child window contains three panes. The Edit pane allows you to edit command text and enter table names to be used with ICommandText and IOpenRowset. The Rowset pane displays rowset data and includes scroll bars implemented from IRowsetScroll. The Notification pane displays all notifications from rowset and data source objects, all method calls, and all input and output parameters. The sample will also display any error information posted by the provider.

Rowset Viewer Limitations

You can call nearly all OLE DB methods from Rowset Viewer. Some methods are not yet implemented; these will appear disabled in the menu. Otherwise, Rowset Viewer is capable of calling all OLE DB methods that are supported by the provider.


Rowset Viewer Files

The following source files are included with the Rowset Viewer sample. The source files are located in the <install directory>\SAMPLES\ROWSETVIEWER directory; the Rowset Viewer executable file ROWSETVIEWER.EXE is located in the <install directory>\BIN directory.

File name | Description
cdialog.cpp | Handles all Connections, Properties, and other common dialogs.
cdialog.h | Header file.
cenum.cpp | Handles all Root Enumerator operations.
cenum.h | Header file.
common.cpp | Common support functions.
common.h | Header file.
ctable.cpp | Rowset helpers for calling rowset methods.
ctable.h | Header file.
datasource.cpp | DataSource, Session, and Command helpers.
datasource.h | Header file.
error.cpp | ErrorInfo routines.
error.h | Header file.
list.h | Linked list implementation.
main.cpp | Main Module: Includes FrameWindow routines, MDI child routines, and most OLE DB method helpers.
main.h | Header file.
makefile | NMAKE file.
property.cpp | Property routines.
property.h | Header file.
resource.h | Header file.
rowsetviewer.dsp | Project file.
rowsetviewer.mak | Make file.
rowsetviewer.rc | Resource file.
rowsetviewer.rc2 | Version resource information.
spy.cpp | IMallocSpy memory leak checking.
spy.h | Header file.
version.h | Version file for Rowset Viewer sample.

The following source files are located in the <install directory>\SAMPLES\ROWSETVIEWER\RES directory.

File name | Description
after.ico | Icon file.
before.ico | Icon file.
change.ico | Icon file.
check.ico | Icon file.
column.ico | Icon file.
delete.ico | Icon file.
insert.ico | Icon file.
menu.bmp | Bitmap file.
normal.ico | Icon file.
readonly.bmp | Bitmap file.
readonly.ico | Icon file.
row.ico | Icon file.
rowsetviewer.ico | Icon file.
stop.ico | Icon file.
table.ico | Icon file.
toolbar.bmp | Bitmap file.
uncheck.ico | Icon file.


Building Rowset Viewer

The file <installdirectory>\SAMPLES\ROWSETVIEWER\ROWSETVIEWER.MAK is set up for compilation with NMAKE, the Microsoft Program Maintenance Utility provided with Microsoft Visual Studio. The following example syntax builds a debug version of ROWSETVIEWER on the Intel x86 platform based on the Win32 (x86) application configuration.

NMAKE /f "rowsetviewer.mak" CFG="RowsetViewer - Win32 x86 Debug"

To build ROWSETVIEWER on another platform or with a different configuration, use one of the following configuration options.

Configuration option | Type of file built
"rowsetviewer - Win32 (ALPHA) axp Debug" | Debug version of ROWSETVIEWER based on Win32 (ALPHA) Application
"rowsetviewer - Win32 (ALPHA) axp Release" | Retail version of ROWSETVIEWER based on Win32 (ALPHA) Application
"rowsetviewer - Win32 x86 Release" | Retail version of ROWSETVIEWER based on Win32 (x86) Application
"rowsetviewer - Win32 x86 Debug" | Debug version of ROWSETVIEWER based on Win32 (x86) Application


Running Rowset Viewer

To run Rowset Viewer, click Start, point to Programs, point to OLE DB SDK, and click Rowset Viewer, or run the <install directory>\BIN\ROWSETVIEWER.EXE file from Windows Explorer.


Rowset Viewer Menus and Toolbar

Four menus are built into Rowset Viewer: the main menu and a separate context menu for each window in the MDI child window. All menu items display an explanation of the functionality in the status bar.

Main Menu

The Main menu displays all OLE DB CoType objects, with all interfaces and methods for the object in the drop-down list. All supported interfaces are enabled, and interfaces that are not supported by the provider are disabled.

Command Menu

Right-click the Command window to display common methods and actions of the command object.

Rowset Menu

Right-click Rowset (data grid) to display common methods and actions of the rowset object.

Notification Menu

Right-click the Notification window to display common methods and actions for filtering and receiving notifications.

Toolbar

Use the toolbar for actions that are used frequently. ToolTips are available to help you determine the action of each item.


Rowset Viewer Example

This example describes how to connect to a provider, execute a command, and use IOpenRowset.

To connect to a provider

  1. Start ROWSETVIEWER.EXE.

  2. From the DataSource menu, choose Full Connect.

  3. Select MSDASQL as the provider and type OLE_DB_NWind_Jet for the DataSource parameter.

  4. Click Connect.

To execute a command

  1. In the top pane of the MDI child window, type the following query:

    SELECT * FROM CUSTOMERS

    2.

  2.
Click the ! button on the toolbar or choose ICommand::Execute from the Command menu.

    3.

To use IOpenRowset

  1.
In the top pane of the MDI child window, type CUSTOMERS.

    2.

  2.
Make sure that the SQL button is up and click the ! button, or choose IOpenRowset from the Session menu.
    3.


Table Copy Sample Contents

Table Copy Contents

This section contains the following information:

  • Table Copy Overview

  • Table Copy Files

  • Building Table Copy

  • Running Table Copy

  • Table Copy Example


Table Copy Sample Overview

Table Copy Overview

The Table Copy sample demonstrates the ability to read and write data in OLE DB by copying a table from one data source to another through the ODBC Provider.

To run the Table Copy sample, click Start, point to Programs, point to OLE DB SDK, and click Table Copy.

File name | Description
TABLECOPY.EXE | Table Copy sample

Table Copy Features

The Table Copy sample exposes a wizard that leads you through the steps of connecting to a source OLE DB provider, selecting the data to copy, connecting to a target OLE DB provider, and copying the table. The wizard provides the ability to select the particular columns of the table to copy, to copy indexes to the new table, and to limit the number of rows or size of BLOB columns copied. You can also show or hide the SQL statements used to create the new table and insert the data.


Table Copy Sample Files

Table Copy Files

The following source files are included with the Table Copy sample. The source files are located in the <install directory>\SAMPLES\TABLECPY directory; the Table Copy executable file TABLECOPY.EXE is located in the <install directory>\BIN directory.

File name | Description
CATALOG.ICO | Catalog icon.
COLUMN.ICO | Column icon.
COLUMNLONG.ICO | BLOB Column icon.
COLUMNREAD.ICO | ReadOnly Column icon.
COMMON.CPP | Contains common routines for handling strings, converting to MBCS or WCHAR, quoting identifiers, and processing different data types. It also contains the static string messages.
COMMON.H | Application header file.
DATASOURCE.CPP | Connects to the data source. Includes functions for instantiating, setting properties, and initializing the OLE DB provider.
DATASOURCE.H | Application header file.
ERROR.CPP | Common error handling routines for the application.
ERROR.H | Application header file.
INDEX.ICO | Index icon.
LIST.H | Application header file.
PRIMARYKEY.ICO | Primary Key icon.
PROGRESS.CPP | Handles display of the Table Copy Progress dialog box.
PROGRESS.H | Application header file.
PROPERTY.CPP | Retrieves properties from the provider. Includes functions for freeing properties and VARIANT elements of property structures.
PROPERTY.H | Application header file.
RESOURCE.H | Resource include file.
SCHEMA.ICO | Schema icon.
SPY.CPP | Contains routines for keeping track of memory allocation and release.
SPY.H | Application header file.
STEP1.CPP | Handles the dialog boxes, buttons, and lists in the first step of the Table Copy sample. Functions in this file call IDBInitialize::Initialize to connect to the provider, call schema functions to retrieve rowsets containing table column metadata information, and process the schema rowsets through IRowset.
STEP2.CPP | Handles the dialog boxes, buttons, and lists in the second step of the Table Copy sample. Functions in this file retrieve the INDEXES schema rowset and process the results, displaying them to the user.
STEP3.CPP | Handles the dialog boxes, buttons, and lists in the third step of the Table Copy sample. Functions in this file connect to the target data source and get the name for the new table from the user.
STEP4.CPP | Handles the dialog boxes, buttons, and lists in the fourth step of the Table Copy sample. Functions in this file present options for the user to control how the new table is created.
SYNONYM.ICO | Synonym icon.
SYSTABLE.ICO | System Table icon.
TABLE.CPP | Main file for copying the table. Includes functions for calling IDBLiteral::GetLiteralInfo to retrieve literal quote characters, creating and executing the SELECT statements, retrieving and mapping data type information, and executing the CREATE TABLE and INSERT TABLE statements.
TABLE.H | Application header file.
TABLE.ICO | Table icon.
TABLE_CO.ICO | Icon for the Table Copy sample.
TABLECOPY.CPP | Provides the main entry point for the Table Copy sample.
TABLECOPY.DSP | Project file.
TABLECOPY.H | Application header file.
TABLECOPY.MAK | Application NMAKE file.
TABLECOPY.RC | Table Copy sample resource files.
TABLECOPY.RC2 | Version file for Table Copy sample.
TYPE.ICO | Type icon.
VERSION.H | Version file for Table Copy sample.
VIEW.ICO | View icon.
WINMAIN.CPP | Main window and application source file. Functions in this file provide support for the windows displayed in the various steps of the Table Copy wizard.
WINMAIN.H | Application header file.
WIZARD.CPP | Contains common routines for supporting the buttons for moving between the various steps of the wizard.
WIZARD.H | Application header file.


Building the Table Copy Sample

Building Table Copy

The file <install directory>\SAMPLES\TABLECPY\TABLECOPY.MAK is set up for compilation with NMAKE, the Microsoft Program Maintenance Utility provided with Microsoft Visual Studio. The following example syntax builds a debug version of TABLECOPY on the Intel x86 platform based on the Win32 (x86) Application configuration.

NMAKE /f "tablecopy.mak" CFG=" tablecopy - Win32 x86 Debug"

To build TABLECOPY on another platform or with a different configuration, use one of the following configuration options.

Configuration option | Type of file built
"tablecopy - Win32 x86 Debug" | Debug version of TABLECOPY based on Win32 (x86) Application
"tablecopy - Win32 x86 Release" | Retail version of TABLECOPY based on Win32 (x86) Application
"tablecopy - Win32 (ALPHA) axp Debug" | Debug version of TABLECOPY based on Win32 (ALPHA) Application
"tablecopy - Win32 (ALPHA) axp Release" | Retail version of TABLECOPY based on Win32 (ALPHA) Application
"tablecopy - Win32 (PPC) ppc Debug" | Debug version of TABLECOPY based on Win32 (PPC) Application
"tablecopy - Win32 (PPC) ppc Release" | Retail version of TABLECOPY based on Win32 (PPC) Application

For additional information about using NMAKE, see the Microsoft Visual Studio documentation.


Running the Table Copy Sample

Running Table Copy

To run the Table Copy sample, click Start, point to Programs, point to OLE DB SDK, and click Table Copy, or run the <install directory>\BIN\TABLECOPY.EXE file from Windows Explorer.


Table Copy Example

To connect to a data source, select the data to be copied, and copy the data using the Table Copy sample, follow these steps:

  1. Start TABLECOPY.EXE.

  2. Click Connect to connect to the source data source and click the Machine Data Source tab.

  3. Select OLE_DB_NWind_Jet as the source data source and click OK.

    Note If you installed the OLE DB SDK in a directory other than the default directory, you will need to change the OLE_DB_NWind_Jet data source to use your directory.

  4. In the Tables list, select the Customers table.

  5. Click Next to move to step 2 of the wizard, then click Next again to move to step 3 of the wizard.

  6. Click Connect to connect to the target data source and click the Machine Data Source tab.

  7. Select OLE_DB_NWind_Jet as the target data source and click OK.

  8. Type NewCustomers as the new table name in the To box.

  9. Click Next to move to step 4 of the wizard.

  10. Under Create, clear the Indexes box.

  11. Click Next again to move to step 5 of the wizard.

  12. Click Finish. The Table Copy sample will create the NewCustomers table and copy the data from the Customers table to the NewCustomers table.


OLE DB Sample Consumer Contents

Sample Consumer Contents

This section contains the following information:

  • Sample Consumer Overview

  • Sample Consumer Files

  • Building the Sample Consumer

  • Running the Sample Consumer


Sample Consumer Overview

The OLE DB sample consumer, SAMPCLNT.EXE, is a learning tool for application developers new to OLE DB. With it, you can learn about building OLE DB applications by displaying the code in the debugger. Although this application uses a simple interface to retrieve and log data, it demonstrates the general procedures that you need to follow to get data from an OLE DB provider that uses IOpenRowset as its only means of exposing data. This consumer uses the sample provider.

To run the sample consumer, click Start, point to Programs, point to OLE DB SDK, and click Sample Client For Text Provider.

File name | Description
SAMPLECLNT.EXE | Sample consumer

Sample Consumer Features

The sample consumer is a console application that interacts with the OLE DB sample provider, SAMPPROV.DLL. SAMPCLNT.EXE reads the data in the rowset exposed by SAMPPROV.DLL and writes it to the log file, SAMPCLNT.OUT. The rowset is created over the entire data source, which is a comma-separated value file. SAMPCLNT.EXE uses the only command recognized by SAMPPROV.DLL, SELECT * FROM CUSTOMER.CSV, to request the data.

Sample Consumer Limitations

Here are some limitations in SAMPCLNT.EXE:

  • The sample consumer exposes a limited amount of OLE DB functionality. The OLE DB interfaces, methods, and data structures used in this application are simple implementations, but they provide a simple framework that application designers can extend.

  • SAMPCLNT.EXE can open only one file: CUSTOMER.CSV.

  • All routines in SAMPCLNT.EXE use the same error-handling facility.


Sample Consumer Files

The following source files are included with the sample consumer. The source files are located in the <install directory>\SAMPLES\SAMPCLNT directory. The sample consumer executable file SAMPCLNT.EXE is located in the <install directory>\BIN directory.

File name | Description
CUSTOMER.CSV | Sample data for sample consumer.
DUMP.CPP | Output routines for SAMPCLNT.EXE.
RESOURCE.H | Resource header file.
SAMPCLNT.CPP | Main implementation file for the sample consumer.
SAMPCLNT.H | Sample consumer header file.
SAMPCLNT.MAK | Sample consumer NMAKE file.
SAMPCLNT.RC, SAMPCLNT.RC2 | Sample consumer resource files.
SAMPVER.H | Version include file.


Building the Sample Consumer

The file <install directory>\SAMPLES\SAMPCLNT\SAMPCLNT.MAK is set up for compilation with NMAKE, the Microsoft Program Maintenance Utility provided with Microsoft Visual Studio. SAMPCLNT must have access to the following OLE DB files:

  • OLEDB.H

  • OLEDBERR.H

  • OLEDB.LIB

Verify that these files are on the path referred to by the INCLUDE environment variable.

The following example syntax builds a debug version of SAMPCLNT on the Intel x86 platform based on the Win32 (x86) Console Application configuration.

NMAKE /f "sampclnt.mak" CFG="sampclnt - Win32 x86 Debug"

To build SAMPCLNT on another platform or with a different configuration, use one of the following configuration options.

Configuration option | Type of file built
"sampclnt - Win32 x86 Debug" | Debug version of SAMPCLNT based on Win32 (x86) Console Application
"sampclnt - Win32 x86 Release" | Retail version of SAMPCLNT based on Win32 (x86) Console Application
"sampclnt - Win32 (ALPHA) axp Debug" | Debug version of SAMPCLNT based on Win32 (ALPHA) Console Application
"sampclnt - Win32 (ALPHA) axp Release" | Retail version of SAMPCLNT based on Win32 (ALPHA) Console Application
"sampclnt - Win32 (PPC) ppc Debug" | Debug version of SAMPCLNT based on Win32 (PPC) Console Application
"sampclnt - Win32 (PPC) ppc Release" | Retail version of SAMPCLNT based on Win32 (PPC) Console Application

For additional information about using NMAKE, see the Microsoft Visual Studio documentation.


Running the Sample Consumer

To run the sample consumer, click Start, point to Programs, point to OLE DB SDK, and click Sample Client For Text Provider, or run the <install directory>\BIN\SAMPCLNT.EXE file from Windows Explorer. The data file, CUSTOMER.CSV, must be in the <install directory>\BIN directory as well. The sample consumer logs the results of its run to the file <install directory>\BIN\SAMPCLNT.OUT; view this file to see the results of the run.


SQLOLEDB Enumerator

Information to come later.


Transaction Retention

Committing or aborting the transaction with the fRetaining flag in ITransaction::Commit or ITransaction::Abort set to TRUE commits or aborts and implicitly starts a new unit of work. If the fRetaining flag is set to FALSE, the transaction is committed or aborted with no new unit of work created—any new work done on the session is outside the scope of a transaction. This is sometimes referred to as auto-commit or implicit mode. Attempting to explicitly commit or abort a transaction when there is no outstanding transaction returns an error.

Commands are always fully functional after a retaining commit or abort. The command also remains active in the case of a nonretaining commit or abort, but the prepared state may be lost.


Rowset Preservation

After a rowset's transaction commits or aborts, there are two possibilities: the full functionality of the rowset is preserved or the rowset enters a zombie state. A preserved rowset is an object with all its capabilities intact. A zombie rowset is an object whose functionality is virtually lost. Specifically, zombie rowsets support only IUnknown operations and the release of the outstanding row and accessor handles. All other operations on the rowset produce the error E_UNEXPECTED.

The behavior of a rowset after a commit is determined by the property DBPROP_COMMITPRESERVE. If this property is VARIANT_FALSE, then the rowset enters a zombie state. However, if the property is VARIANT_TRUE, then the rowset remains fully functional after the commit, with all of its rows, bookmarks, pending changes, and so on still valid.

Similarly, the behavior of a rowset on an abort is determined by the property DBPROP_ABORTPRESERVE. If the property is VARIANT_FALSE, rowsets created with the aborting transaction enter zombie states after an abort. If the property is VARIANT_TRUE, the rowset remains fully functional, with all of its rows, bookmarks, pending changes, and so on still valid. However, all changes made visible to the data source within the aborted transaction are undone.

Preserved rowsets never automatically synchronize with the data source. For example, if the transaction is aborted and DBPROP_ABORTPRESERVE is VARIANT_TRUE, then changes undone in the data source are not undone in the rowset. In delayed update mode, any pending changes are not undone in the rowset. The consumer must call IRowsetRefresh::RefreshVisibleData to synchronize the rowset with the data source.

Even if a rowset is created with preserving properties, it may enter a zombie state if other objects on which it depends are destroyed as the result of an abort. For example, a rowset may enter a zombie state if a table on which it was built was created as part of the same transaction that was aborted.


Fetching Rows While Integrating Indexes and Rowsets

Fetching Rows

Four areas of fetching rows are affected by integrating indexes and rowsets: rowset order, next fetch position, range, and holding rows.


About This Manual

The OLE DB specification is a set of interfaces that exposes data from a variety of data sources using the OLE Component Object Model (COM).

Information in this document is subject to change without notice. Companies, names, and data used in examples herein are fictitious unless otherwise noted. No part of this document may be reproduced or transmitted in any form or by any means, electronic or mechanical, for any purpose, without the express written permission of Microsoft Corporation.

© 1997 Microsoft Corporation. All rights reserved.

FoxPro, Microsoft, Visual Basic, Win32, Windows 95, and Windows NT are registered trademarks and SQL Server is a trademark of Microsoft Corporation in the USA and other countries.

Btrieve is a registered trademark of Btrieve Technologies, Inc.

DB2 is a registered trademark and IMS Client Server/2 is a trademark of International Business Machines Corporation.

dBASE and Paradox are registered trademarks of Borland International, Inc.

DEC is a trademark of Digital Equipment Corporation.

ORACLE is a registered trademark of Oracle Corporation.

UNIX is a registered trademark in the United States and other countries, licensed exclusively through X/Open Company, Ltd.


Organization of This Manual

This manual is divided into the following parts:

  • Part 1 Introduction to OLE DB, providing conceptual information about OLE DB

  • Part 2 OLE DB Reference, containing syntax and semantic information for all OLE DB interfaces

  • Part 3 Appendixes, containing technical details


Document Conventions

This manual uses the following typographic conventions.

Format | Used for
WIN.INI | Uppercase letters indicate file names, SQL statements, macro names, and terms used at the operating-system command level.
#include <io.h> | Monospace font is used for sample command lines and program code.
argument | Italicized words indicate information that the user or the application must provide, or word emphasis.
IRowset | Bold type indicates that syntax must be typed exactly as shown, including method names.


Audience

This manual assumes the following:

  • A working knowledge of the C or C++ programming language

  • Knowledge of COM

In addition, a general knowledge of databases and SQL is helpful.


About the Code Examples

The code examples in this book are designed for illustrative purposes only. Because they are written primarily to demonstrate OLE DB principles, efficiency has sometimes been set aside in the interest of clarity. In addition, whole sections of code have sometimes been omitted for clarity. These include the definitions of non-OLE DB functions and most error handling.


New Interfaces

The following interfaces are not included in the OLE DB Programmer’s Reference.


Conversions Supported from Data Type STR

Convert to type |  | Convert to type |
I1 | X | DBDATE | X
I2 | X | DBTIME | X
I4 | X | DBTIMESTAMP | X
I8 | X | BOOL | X
UI1 | X | BSTR | X
UI2 | X | BYTES | X
UI4 | X | Empty | X
UI8 | X | ERROR |
R4 | X | GUID | X
R8 | X | IDISPATCH |
CY | X | IUNKNOWN |
DECIMAL | X | NULL | X
NUMERIC | X | VARIANT | X
DATE | X | WSTR | X


Conversions Supported from Data Type UI1

Convert to type |  | Convert to type |
I1 | X | DBTIME |
I2 | X | DBTIMESTAMP |
I4 | X | BOOL | X
I8 | X | BSTR | X
UI2 | X | BYTES |
UI4 | X | Empty | X
UI8 | X | ERROR |
R4 | X | GUID |
R8 | X | IDISPATCH |
CY | X | IUNKNOWN |
DECIMAL | X | NULL | X
NUMERIC | X | STR | X
DATE | X | VARIANT | X
DBDATE |  | WSTR | X


Conversions Supported from Data Type UI2

Convert to type |  | Convert to type |
I1 | X | DBTIME |
I2 | X | DBTIMESTAMP |
I4 | X | BOOL | X
I8 | X | BSTR | X
UI1 | X | BYTES |
UI4 | X | Empty | X
UI8 | X | ERROR |
R4 | X | GUID |
R8 | X | IDISPATCH |
CY | X | IUNKNOWN |
DECIMAL | X | NULL | X
NUMERIC | X | STR | X
DATE | X | VARIANT | X
DBDATE |  | WSTR | X


Conversions Supported from Data Type UI4

Convert to type |  | Convert to type |
I1 | X | DBTIME |
I2 | X | DBTIMESTAMP |
I4 | X | BOOL | X
I8 | X | BSTR | X
UI1 | X | BYTES | X
UI2 | X | Empty | X
UI8 | X | ERROR |
R4 | X | GUID |
R8 | X | IDISPATCH |
CY | X | IUNKNOWN |
DECIMAL | X | NULL | X
NUMERIC | X | STR | X
DATE | X | VARIANT | X
DBDATE |  | WSTR | X


Conversions Supported from Data Type UI8

Convert to type |  | Convert to type |
I1 |  | DBTIME |
I2 |  | DBTIMESTAMP |
I4 |  | BOOL |
I8 | X | BSTR |
UI2 |  | BYTES |
UI4 |  | Empty | X
UI8 | X | ERROR |
R4 |  | GUID |
R8 |  | IDISPATCH |
CY |  | IUNKNOWN |
DECIMAL |  | NULL | X
NUMERIC |  | STR |
DATE |  | VARIANT |
DBDATE |  | WSTR |


Conversions Supported from Data Type DBTIMESTAMP

Convert to type |  | Convert to type |
I1 |  | DBDATE | X
I2 |  | DBTIME | X
I4 |  | BOOL |
I8 |  | BSTR | X
UI1 |  | BYTES |
UI2 |  | Empty | X
UI4 |  | ERROR |
UI8 |  | GUID |
R4 |  | IDISPATCH |
R8 |  | IUNKNOWN |
CY |  | NULL | X
DECIMAL |  | STR | X
NUMERIC |  | VARIANT | X
DATE | X | WSTR | X


Conversions Supported from Data Type DBDATE

Convert to type |  | Convert to type |
I1 |  | DBTIME | X
I2 |  | DBTIMESTAMP | X
I4 |  | BOOL |
I8 |  | BSTR | X
UI1 |  | BYTES |
UI2 |  | Empty | X
UI4 |  | ERROR |
UI8 |  | GUID |
R4 |  | IDISPATCH |
R8 |  | IUNKNOWN |
CY |  | NULL | X
DECIMAL |  | STR | X
NUMERIC |  | VARIANT | X
DATE | X | WSTR | X


Conversions Supported from Data Type DBTIME

Convert to type |  | Convert to type |
I1 |  | DBDATE | X
I2 |  | DBTIMESTAMP | X
I4 |  | BOOL |
I8 |  | BSTR | X
UI1 |  | BYTES |
UI2 |  | Empty | X
UI4 |  | ERROR |
UI8 |  | GUID |
R4 |  | IDISPATCH |
R8 |  | IUNKNOWN |
CY |  | NULL | X
DECIMAL |  | STR | X
NUMERIC |  | VARIANT | X
DATE | X | WSTR | X


Conversions Supported from Data Type NUMERIC

Convert to type |  | Convert to type |
I1 | X | DBTIME |
I2 | X | DBTIMESTAMP |
I4 | X | BOOL | X
I8 | X | BSTR | X
UI1 | X | BYTES |
UI2 | X | Empty | X
UI4 | X | ERROR |
UI8 | X | GUID |
R4 | X | IDISPATCH |
R8 | X | IUNKNOWN |
CY | X | NULL | X
DECIMAL | X | STR | X
DATE | X | VARIANT | X
DBDATE |  | WSTR | X


Conversions Supported from Data Type CY

Convert to type |  | Convert to type |
I1 | X | DBTIME |
I2 | X | DBTIMESTAMP |
I4 | X | BOOL | X
I8 | X | BSTR | X
UI1 | X | BYTES |
UI2 | X | Empty | X
UI4 | X | ERROR |
UI8 | X | GUID |
R4 | X | IDISPATCH |
R8 | X | IUNKNOWN |
DECIMAL | X | NULL | X
NUMERIC | X | STR | X
DATE | X | VARIANT | X
DBDATE |  | WSTR | X


Conversions Supported from Data Type I1

Convert to type |  | Convert to type |
I2 | X | DBTIME |
I4 | X | DBTIMESTAMP |
I8 | X | BOOL | X
UI1 | X | BSTR | X
UI2 | X | BYTES |
UI4 | X | Empty | X
UI8 | X | ERROR |
R4 | X | GUID |
R8 | X | IDISPATCH |
CY | X | IUNKNOWN |
DECIMAL | X | NULL | X
NUMERIC | X | STR | X
DATE | X | VARIANT | X
DBDATE |  | WSTR | X


Conversions Supported from Data Type I2

Convert to type |  | Convert to type |
I1 | X | DBTIME |
I4 | X | DBTIMESTAMP |
I8 | X | BOOL | X
UI1 | X | BSTR | X
UI2 | X | BYTES |
UI4 | X | Empty | X
UI8 | X | ERROR |
R4 | X | GUID |
R8 | X | IDISPATCH |
CY | X | IUNKNOWN |
DECIMAL | X | NULL | X
NUMERIC | X | STR | X
DATE | X | VARIANT | X
DBDATE |  | WSTR | X


Conversions Supported from Data Type I4

Convert to type |  | Convert to type |
I1 | X | DBTIME |
I2 | X | DBTIMESTAMP |
I8 | X | BOOL | X
UI1 | X | BSTR | X
UI2 | X | BYTES |
UI4 | X | Empty | X
UI8 | X | ERROR |
R4 | X | GUID |
R8 | X | IDISPATCH |
CY | X | IUNKNOWN |
DECIMAL | X | NULL | X
NUMERIC | X | STR | X
DATE | X | VARIANT | X
DBDATE |  | WSTR | X


Conversions Supported from Data Type I8

Convert to type |  | Convert to type |
I1 |  | DBTIME |
I2 |  | DBTIMESTAMP |
I4 |  | BOOL |
I8 | X | BSTR |
UI2 |  | BYTES |
UI4 |  | Empty | X
UI8 | X | ERROR |
R4 |  | GUID |
R8 |  | IDISPATCH |
CY |  | IUNKNOWN |
DECIMAL |  | NULL | X
NUMERIC |  | STR |
DATE |  | VARIANT |
DBDATE |  | WSTR |


Conversions Supported from Data Type R4

Convert to type |  | Convert to type |
I1 | X | DBTIME |
I2 | X | DBTIMESTAMP |
I4 | X | BOOL | X
I8 | X | BSTR | X
UI1 | X | BYTES |
UI2 | X | Empty | X
UI4 | X | ERROR |
UI8 | X | GUID |
R8 | X | IDISPATCH |
CY | X | IUNKNOWN |
DECIMAL | X | NULL | X
NUMERIC | X | STR | X
DATE | X | VARIANT | X
DBDATE |  | WSTR | X


Conversions Supported from Data Type R8

Convert to type |  | Convert to type |
I1 | X | DBTIME |
I2 | X | DBTIMESTAMP |
I4 | X | BOOL | X
I8 | X | BSTR | X
UI1 | X | BYTES |
UI2 | X | Empty | X
UI4 | X | ERROR |
UI8 | X | GUID |
R4 | X | IDISPATCH |
CY | X | IUNKNOWN |
DECIMAL | X | NULL | X
NUMERIC | X | STR | X
DATE | X | VARIANT | X
DBDATE |  | WSTR | X


Conversions Supported from Data Type WSTR

Convert to type |  | Convert to type |
I1 | X | DBDATE | X
I2 | X | DBTIME | X
I4 | X | DBTIMESTAMP | X
I8 | X | BOOL | X
UI1 | X | BSTR | X
UI2 | X | BYTES | X
UI4 | X | Empty | X
UI8 | X | ERROR |
R4 | X | GUID | X
R8 | X | IDISPATCH |
CY | X | IUNKNOWN |
DECIMAL | X | NULL | X
NUMERIC | X | STR | X
DATE | X | VARIANT | X


Conversions Supported from Data Type GUID

Convert to type | Convert to type |
I1 | DBDATE |
I2 | DBTIME |
I4 | DBTIMESTAMP |
I8 | BOOL |
UI1 | BSTR | X
UI2 | BYTES |
UI4 | Empty | X
UI8 | ERROR |
R4 | IDISPATCH |
R8 | IUNKNOWN |
CY | NULL | X
DECIMAL | STR | X
NUMERIC | VARIANT |
DATE | WSTR | X


Conversions Supported from Data Type ERROR

Convert to type | Convert to type |
I1 | DBDATE |
I2 | DBTIME |
I4 | DBTIMESTAMP |
I8 | BOOL |
UI1 | BSTR |
UI2 | BYTES |
UI4 | Empty |
UI8 | GUID |
R4 | IDISPATCH |
R8 | IUNKNOWN |
CY | NULL |
DECIMAL | STR |
NUMERIC | VARIANT | X
DATE | WSTR |


Conversions Supported from Data Type BOOL

Convert to type |  | Convert to type |
I1 | X | DBDATE |
I2 | X | DBTIME |
I4 | X | DBTIMESTAMP |
I8 | X | BSTR | X
UI1 | X | BYTES |
UI2 | X | Empty | X
UI4 | X | ERROR |
UI8 | X | GUID |
R4 | X | IDISPATCH |
R8 | X | IUNKNOWN |
CY | X | NULL | X
DECIMAL | X | STR | X
NUMERIC | X | VARIANT | X
DATE | X | WSTR | X


Conversions Supported from Data Type DATE

Convert to type |  | Convert to type |
I1 | X | DBTIME | X
I2 | X | DBTIMESTAMP | X
I4 | X | BOOL | X
I8 | X | BSTR | X
UI1 | X | BYTES |
UI2 | X | Empty | X
UI4 | X | ERROR |
UI8 | X | GUID |
R4 | X | IDISPATCH |
R8 | X | IUNKNOWN |
CY | X | NULL | X
DECIMAL | X | STR | X
NUMERIC | X | VARIANT | X
DBDATE | X | WSTR | X


Conversions Supported from Data Type BYTES

Convert to type |  | Convert to type |
I1 |  | DBDATE |
I2 |  | DBTIME |
I4 |  | DBTIMESTAMP |
I8 |  | BOOL |
UI1 |  | BSTR | X
UI2 |  | Empty | X
UI4 | X | ERROR |
UI8 |  | GUID |
R4 |  | IDISPATCH |
R8 |  | IUNKNOWN |
CY |  | NULL | X
DECIMAL |  | STR | X
NUMERIC |  | VARIANT | X
DATE |  | WSTR | X


Conversions Supported from Data Type BSTR

Convert to type |  | Convert to type |
I1 | X | DBDATE | X
I2 | X | DBTIME | X
I4 | X | DBTIMESTAMP | X
I8 | X | BOOL | X
UI1 | X | BYTES | X
UI2 | X | Empty | X
UI4 | X | ERROR |
UI8 | X | GUID | X
R4 | X | IDISPATCH |
R8 | X | IUNKNOWN |
CY | X | NULL | X
DECIMAL | X | STR | X
NUMERIC | X | VARIANT | X
DATE | X | WSTR | X


Conversions Supported from Data Type DECIMAL

Convert to type |  | Convert to type |
I1 | X | DBTIME |
I2 | X | DBTIMESTAMP |
I4 | X | BOOL | X
I8 | X | BSTR | X
UI1 | X | BYTES |
UI2 | X | Empty | X
UI4 | X | ERROR |
UI8 | X | GUID |
R4 | X | IDISPATCH |
R8 | X | IUNKNOWN |
CY | X | NULL | X
NUMERIC | X | STR | X
DATE | X | VARIANT | X
DBDATE |  | WSTR | X


Conversions Supported from Data Type VARIANT

Convert to type |  | Convert to type |
I1 | X | DBDATE | X
I2 | X | DBTIME | X
I4 | X | DBTIMESTAMP | X
I8 | X | BOOL | X
UI1 | X | BSTR | X
UI2 | X | BYTES | X
UI4 | X | Empty | X
UI8 | X | ERROR | X
R4 | X | GUID | X
R8 | X | IDISPATCH | X
CY | X | IUNKNOWN | X
DECIMAL | X | NULL | X
NUMERIC | X | STR | X
DATE | X | WSTR | X


Conversions Supported from Data Type Empty

Convert to type |  | Convert to type |
I1 | X | DBDATE |
I2 | X | DBTIME |
I4 | X | DBTIMESTAMP |
I8 | X | BOOL | X
UI1 | X | BSTR | X
UI2 | X | BYTES |
UI4 | X | ERROR |
UI8 | X | GUID |
R4 | X | IDISPATCH |
R8 | X | IUNKNOWN |
CY | X | NULL | X
DECIMAL | X | STR | X
NUMERIC | X | VARIANT | X
DATE | X | WSTR | X


Conversions Supported from Data Type IDISPATCH

Convert to type | Convert to type |
I1 | DBDATE |
I2 | DBTIME |
I4 | DBTIMESTAMP |
I8 | BOOL |
UI1 | BSTR |
UI2 | BYTES |
UI4 | Empty | X
UI8 | ERROR |
R4 | GUID |
R8 | IUNKNOWN | X
CY | NULL | X
DECIMAL | STR |
NUMERIC | VARIANT | X
DATE | WSTR |


Conversions Supported from Data Type IUNKNOWN

Convert to type | Convert to type |
I1 | DBDATE |
I2 | DBTIME |
I4 | DBTIMESTAMP |
I8 | BOOL |
UI1 | BSTR |
UI2 | BYTES |
UI4 | Empty | X
UI8 | ERROR |
R4 | GUID |
R8 | IDISPATCH | X
CY | NULL | X
DECIMAL | STR |
NUMERIC | VARIANT | X
DATE | WSTR |


Conversions Supported from Data Type NULL

Convert to type | Convert to type |
I1 | DBDATE |
I2 | DBTIME |
I4 | DBTIMESTAMP |
I8 | BOOL |
UI1 | BSTR |
UI2 | BYTES |
UI4 | Empty |
UI8 | ERROR |
R4 | GUID |
R8 | IDISPATCH |
CY | IUNKNOWN |
DECIMAL | STR |
NUMERIC | VARIANT | X
DATE | WSTR |


IRowPosition

IRowPosition allows multiple components to share the concept of a current row over a rowset.

When to Implement

IRowPosition is not implemented by the provider. It is implemented by a common service provider component that works over any rowset.

When to Call

Components create a row position object in order to manage operations involving a current position within a rowset. Components call SetRowPosition in order to specify that the hRow be considered the current row, and GetRowPosition in order to retrieve the hRow specified as the current row. A code example is provided. Changing the current row generates notifications for any listeners of the row position object, but makes no change to the position or state of the rowset itself.

Method | Description
ClearRowPosition | Clears the row position.
GetRowPosition | Retrieves the current row position.
GetRowset | Returns the current underlying Rowset.
Initialize | Sets the Rowset to be used by the row position object.
SetRowPosition | Sets the current row position.


Connecting to a Simple Provider Using ActiveX Data Objects (ADO)

ADO, as an OLE DB consumer, makes requests to a provider to access data sources and retrieve the desired information. In ADO, you supply the name of the provider either as part of a connection string , or in the Provider property of the Connection object. The steps to make the connection depends on whether your Simple Provider is registered as a full OLE DB Provider or not.

  • Simple Provider Registered as a full OLE DB Provider

    In the following example, when the Simple Provider is registered as a full OLE DB provider, the steps are:

    1. Supply the ProgID of your Simple Provider, in this case, SampleOSP_VB, as a parameter to the Provider method of the Connection object.

    2. Open the connection.

    3. Set the Recordset Object's source to the Data Member of your provider's Data Source Object.

    The corresponding code follows:

    con.Provider = "SampleOSP_VB"
con.Open
rs.Open " C:\Oledbsdk\Osptk\Samples\Customer.txt", con
  • Simple Provider Not Registered as a full OLE DB Provider, but it's Data Source Object is Registered

    In the following example, when the Simple Provider is not registered as a full OLE DB provider, but it's Data Source Object is registered, the steps are:

    1. Supply the ProgID of the OLE DB Simple Provider DLL (the mapping layer), in this case, MSDAOSP, as a parameter to the Provider method of the Connection object.

    2. Supply the name of your simple provider's Data Source Object, in this case, SampleOSP_VB.MyDataSource, as a parameter to the Properties method of the Connection object.

    3. Open the connection.

    4. Set the Recordset Object's source to the Data Member of your provider's Data Source Object.

    The corresponding code follows:

    con.Provider = "MSDAOSP"
con.Properties("Data Source") = "SampleOSP_VB.MyDataSource"
con.Open
rs.Open " C:\Oledbsdk\Osptk\Samples\Customer.txt", con


ISourcesRowset::GetSourcesRowset Method

ISourcesRowset::GetSourcesRowset

Returns a rowset of the data sources and enumerators visible from the current enumerator.

HRESULT GetSourcesRowset(
   IUnknown         pUnkOuter,
   REFIID            riid,
   ULONG         cPropertySets,
   DBPROPSET   rgPropertySets[],
   IUnknown **      ppSourcesRowset);

Parameters

pUnkOuter

A pointer to the controlling IUnknown interface if the sources rowset is being created as part of an aggregate. If the rowset is not part of an aggregate, this must be set to a null pointer.

riid

The IID of the interface on which to return a pointer. This interface is conceptually added to the list of required interfaces on the resulting rowset, and the method fails (E_NOINTERFACE) if that interface cannot be supported on the resulting rowset.

cPropertySets

[in]
The number of DBPROPSET structures in rgPropertySets. If this is zero, the provider ignores rgPropertySets.

rgPropertySets

[in/out]
An array of DBPROPSET structures containing properties and values to be set. The properties specified in these structures must belong to the Rowset property group. If the same property is specified more than once in rgPropertySets, then it is provider-specific which value is used. If cPropertySets is 0, this argument is ignored.

For information about the properties in the Rowset property group that are defined by OLE DB, see "Rowset Properties" in Appendix C. For information about the DBPROPSET and DBPROP structures, see "DBPROPSET Structure" and "DBPROP Structure" in Chapter 11.

ppSourcesRowset
A pointer to memory in which to return the requested interface pointer on the rowset. If an error occurs, the returned pointer is null.

Return Code

S_OK
The method succeeded. In all DBPROP structures passed to the method, dwStatus is set to DBPROPSTATUS_OK.

DB_S_ERRORSOCCURRED
The rowset was opened but one or more properties—for which the dwOptions element of the DBPROP structure was DBPROPOPTIONS_OPTIONAL—were not set. The consumer checks dwStatus in the DBPROP structures to determine which properties were not set. The method can fail to set properties for a number of reasons, including:

  • The property was not supported by the provider.

  • The property was not in the Rowset property group.

  • The property set was not supported by the provider.

  • It was not possible to set the property.

  • The colid in the DBPROP structure was invalid.

  • The data type in vValue in the DBPROP structure was not the data type of the property or was not VT_EMPTY.

  • The value in vValue in the DBPROP structure was invalid.

  • The property's value conflicted with an existing property.

  • A property was specified to be applied to all columns, but could not be applied to one or more columns.

E_FAIL
A provider-specific error occurred.

E_INVALIDARG
ppSourcesRowset was a null pointer.

cPropertySets was not 0 and rgPropertySets was a null pointer.

In an element of rgPropertySets, cProperties was not 0 and rgProperties was a null pointer.

E_NOINTERFACE
The rowset did not support the interface specified in riid.

E_OUTOFMEMORY
The provider did not have enough memory to create the Rowset object.

E_UNEXPECTED
The Enumerator object was in an uninitialized state.

DB_E_ABORTLIMITREACHED
The method failed because a resource limit has been reached. For example, a query used to implement the method timed out. No rowset is returned.

DB_E_ERRORSOCCURRED
No rowset was returned because one or more properties—for which the dwOptions element of the DBPROP structure was DBPROPOPTIONS_REQUIRED or an invalid value—were not set. The consumer checks dwStatus in the DBPROP structures to determine which properties were not set. None of the satisfiable properties are remembered. The 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.

DB_E_NOAGGREGATION
pUnkOuter was not a null pointer and the rowset did not support aggregation.

pUnkOuter was non-null and riid was not IID_Unknown.

Comments

GetSourcesRowset returns the following rowset. The rowset is read-only. The columns are returned in the order shown.

Column name | Type indicator | Description
SOURCES_NAME | DBTYPE_WSTR | The name of the data source or enumerator.
SOURCES_PARSENAME | DBTYPE_WSTR | String to pass to IParseDisplayName to obtain a moniker for the data source or enumerator.
SOURCES_DESCRIPTION | DBTYPE_WSTR | The description of the data source or enumerator.
SOURCES_TYPE DBTYPE_UI2 Whether the row describes a data source or an enumerator:
  • DBSOURCETYPE_DATASOURCE

  • DBSOURCETYPE_ENUMERATOR

If a single piece of code is capable of being used both as a data source and as an enumerator, it is listed in the rowset twice, once in each role.
SOURCES_ISPARENT DBTYPE_BOOL If the row describes an enumerator, SOURCES_ISPARENT is VARIANT_TRUE if the enumerator is the parent enumerator; that is, the enumerator whose enumeration contains the enumerator on which ISourcesRowset::GetSourcesRowset was just called. This allows the consumer to go backward through the enumeration. Whether an enumerator is able to enumerate its parent is provider-specific. Otherwise, SOURCES_ISPARENT is VARIANT_FALSE.

If the row describes a data source, SOURCES_ISPARENT is ignored by the consumer.

See Also

IDBProperties::GetPropertyInfo


ISourcesRowset Interface

ISourcesRowset

ISourcesRowset returns a rowset of data sources and enumerators visible from the current enumerator. For more information about enumerators, see "Enumerators" in Chapter 2.

Method | Description
GetSourcesRowset | Returns a rowset of the data sources and enumerators visible from the current enumerator.